build: 增加shiro相关配置
This commit is contained in:
parent
3db8b599c9
commit
64d3aa2ad6
|
@ -23,7 +23,7 @@ jobs:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 17
|
java-version: 17
|
||||||
- name: Build with Maven
|
- name: Build with Maven
|
||||||
run: mvn -B package --file pom.xml
|
run: mvn -B package -DskipAntRunForJenkins --file backend/pom.xml
|
||||||
- name: Upload coverage reports to Codecov
|
- name: Upload coverage reports to Codecov
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v3
|
||||||
with:
|
with:
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
-- set innodb lock wait timeout
|
-- set innodb lock wait timeout
|
||||||
SET SESSION innodb_lock_wait_timeout = 7200;
|
SET SESSION innodb_lock_wait_timeout = 7200;
|
||||||
|
|
||||||
|
insert into user(id, name, email, password, status, create_time, update_time, language, last_workspace_id, phone, source, last_project_id, create_user)
|
||||||
|
VALUES ('admin', 'Administrator', 'admin@metersphere.io', MD5('metersphere'), '1', UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, NULL, NUll, '', 'LOCAL', NULL, 'admin');
|
||||||
|
|
||||||
-- set innodb lock wait timeout to default
|
-- set innodb lock wait timeout to default
|
||||||
SET SESSION innodb_lock_wait_timeout = DEFAULT;
|
SET SESSION innodb_lock_wait_timeout = DEFAULT;
|
|
@ -1,5 +1,6 @@
|
||||||
package io.metersphere.sdk.config;
|
package io.metersphere.sdk.config;
|
||||||
|
|
||||||
|
import io.metersphere.sdk.util.CommonBeanFactory;
|
||||||
import io.metersphere.sdk.util.Translator;
|
import io.metersphere.sdk.util.Translator;
|
||||||
import jakarta.validation.Validator;
|
import jakarta.validation.Validator;
|
||||||
import org.hibernate.validator.HibernateValidator;
|
import org.hibernate.validator.HibernateValidator;
|
||||||
|
@ -38,4 +39,10 @@ public class I18nConfig {
|
||||||
return localValidatorFactoryBean.getValidator();
|
return localValidatorFactoryBean.getValidator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public CommonBeanFactory commonBeanFactory() {
|
||||||
|
return new CommonBeanFactory();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package io.metersphere.sdk.config;
|
||||||
|
|
||||||
|
|
||||||
|
import io.metersphere.sdk.constants.SessionConstants;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.session.web.http.HeaderHttpSessionIdResolver;
|
||||||
|
@Configuration
|
||||||
|
public class SessionConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public HeaderHttpSessionIdResolver sessionIdResolver() {
|
||||||
|
return new HeaderHttpSessionIdResolver(SessionConstants.HEADER_TOKEN);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
package io.metersphere.sdk.config;
|
||||||
|
|
||||||
|
|
||||||
|
import io.metersphere.sdk.security.CsrfFilter;
|
||||||
|
import io.metersphere.sdk.security.MsPermissionAnnotationMethodInterceptor;
|
||||||
|
import io.metersphere.sdk.security.realm.LocalRealm;
|
||||||
|
import io.metersphere.sdk.util.FilterChainUtils;
|
||||||
|
import jakarta.servlet.DispatcherType;
|
||||||
|
import jakarta.servlet.Filter;
|
||||||
|
import org.apache.shiro.aop.AnnotationResolver;
|
||||||
|
import org.apache.shiro.authz.aop.*;
|
||||||
|
import org.apache.shiro.cache.CacheManager;
|
||||||
|
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
|
||||||
|
import org.apache.shiro.realm.Realm;
|
||||||
|
import org.apache.shiro.session.mgt.SessionManager;
|
||||||
|
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
|
||||||
|
import org.apache.shiro.spring.aop.SpringAnnotationResolver;
|
||||||
|
import org.apache.shiro.spring.security.interceptor.AopAllianceAnnotationsAuthorizingMethodInterceptor;
|
||||||
|
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
|
||||||
|
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
|
||||||
|
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
|
||||||
|
import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
|
||||||
|
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
|
||||||
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.DependsOn;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class ShiroConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager sessionManager) {
|
||||||
|
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
|
||||||
|
shiroFilterFactoryBean.setLoginUrl("/");
|
||||||
|
shiroFilterFactoryBean.setSecurityManager(sessionManager);
|
||||||
|
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
|
||||||
|
shiroFilterFactoryBean.setSuccessUrl("/");
|
||||||
|
|
||||||
|
// shiroFilterFactoryBean.getFilters().put("apikey", new ApiKeyFilter());
|
||||||
|
shiroFilterFactoryBean.getFilters().put("csrf", new CsrfFilter());
|
||||||
|
Map<String, String> filterChainDefinitionMap = shiroFilterFactoryBean.getFilterChainDefinitionMap();
|
||||||
|
|
||||||
|
filterChainDefinitionMap.putAll(FilterChainUtils.loadBaseFilterChain());
|
||||||
|
// todo ignore csrf
|
||||||
|
// filterChainDefinitionMap.putAll(FilterChainUtils.ignoreCsrfFilter());
|
||||||
|
|
||||||
|
filterChainDefinitionMap.put("/**", "csrf, authc");
|
||||||
|
// todo
|
||||||
|
// filterChainDefinitionMap.put("/**", "apikey, csrf, authc");
|
||||||
|
return shiroFilterFactoryBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean(name = "shiroFilter")
|
||||||
|
public FilterRegistrationBean<Filter> shiroFilter(ShiroFilterFactoryBean shiroFilterFactoryBean) throws Exception {
|
||||||
|
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
|
||||||
|
registration.setFilter((Filter) Objects.requireNonNull(shiroFilterFactoryBean.getObject()));
|
||||||
|
registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
|
||||||
|
return registration;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MemoryConstrainedCacheManager memoryConstrainedCacheManager() {
|
||||||
|
return new MemoryConstrainedCacheManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SessionManager sessionManager() {
|
||||||
|
return new ServletContainerSessionManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* securityManager 不用直接注入 Realm,可能会导致事务失效
|
||||||
|
* 解决方法见 handleContextRefresh
|
||||||
|
*/
|
||||||
|
@Bean(name = "securityManager")
|
||||||
|
public DefaultWebSecurityManager securityManager(SessionManager sessionManager, CacheManager cacheManager, Realm localRealm) {
|
||||||
|
DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
|
||||||
|
dwsm.setSessionManager(sessionManager);
|
||||||
|
dwsm.setCacheManager(cacheManager);
|
||||||
|
dwsm.setRealm(localRealm);
|
||||||
|
return dwsm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@DependsOn("lifecycleBeanPostProcessor")
|
||||||
|
public LocalRealm localRealm() {
|
||||||
|
return new LocalRealm();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean(name = "lifecycleBeanPostProcessor")
|
||||||
|
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
|
||||||
|
return new LifecycleBeanPostProcessor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@DependsOn({"lifecycleBeanPostProcessor"})
|
||||||
|
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
|
||||||
|
DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
|
||||||
|
daap.setProxyTargetClass(true);
|
||||||
|
return daap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultWebSecurityManager sessionManager) {
|
||||||
|
AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
|
||||||
|
aasa.setSecurityManager(sessionManager);
|
||||||
|
AopAllianceAnnotationsAuthorizingMethodInterceptor advice = new AopAllianceAnnotationsAuthorizingMethodInterceptor();
|
||||||
|
List<AuthorizingAnnotationMethodInterceptor> interceptors = new ArrayList<>(5);
|
||||||
|
|
||||||
|
AnnotationResolver resolver = new SpringAnnotationResolver();
|
||||||
|
interceptors.add(new RoleAnnotationMethodInterceptor(resolver));
|
||||||
|
interceptors.add(new MsPermissionAnnotationMethodInterceptor(resolver));
|
||||||
|
interceptors.add(new AuthenticatedAnnotationMethodInterceptor(resolver));
|
||||||
|
interceptors.add(new UserAnnotationMethodInterceptor(resolver));
|
||||||
|
interceptors.add(new GuestAnnotationMethodInterceptor(resolver));
|
||||||
|
advice.setMethodInterceptors(interceptors);
|
||||||
|
aasa.setAdvice(advice);
|
||||||
|
return aasa;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package io.metersphere.sdk.constants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统内置用户组常量
|
||||||
|
*/
|
||||||
|
public class UserRoleConstants {
|
||||||
|
public static final String SUPER_ROLE = "super_role";
|
||||||
|
public static final String ADMIN = "admin";
|
||||||
|
public static final String ORG_ADMIN = "org_admin";
|
||||||
|
public static final String ORG_MEMBER = "org_member";
|
||||||
|
public static final String WS_ADMIN = "ws_admin";
|
||||||
|
public static final String WS_MEMBER = "ws_member";
|
||||||
|
public static final String PROJECT_ADMIN = "project_admin";
|
||||||
|
public static final String PROJECT_MEMBER = "project_member";
|
||||||
|
public static final String READ_ONLY = "read_only";
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package io.metersphere.sdk.constants;
|
||||||
|
|
||||||
|
public class UserRoleType {
|
||||||
|
public static final String SYSTEM = "SYSTEM";
|
||||||
|
public static final String ORGANIZATION = "ORGANIZATION";
|
||||||
|
public static final String WORKSPACE = "WORKSPACE";
|
||||||
|
public static final String PROJECT = "PROJECT";
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package io.metersphere.sdk.constants;
|
||||||
|
|
||||||
|
public enum UserSource {
|
||||||
|
LOCAL, LDAP, CAS, OIDC, OAuth2
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package io.metersphere.sdk.constants;
|
||||||
|
|
||||||
|
public class UserStatus {
|
||||||
|
public static final String NORMAL = "1";
|
||||||
|
public static final String DISABLED = "0";
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
package io.metersphere.sdk.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import io.metersphere.sdk.constants.UserSource;
|
||||||
|
import io.metersphere.sdk.controller.handler.ResultHolder;
|
||||||
|
import io.metersphere.sdk.dto.LoginRequest;
|
||||||
|
import io.metersphere.sdk.dto.SessionUser;
|
||||||
|
import io.metersphere.sdk.dto.UserDTO;
|
||||||
|
import io.metersphere.sdk.exception.MSException;
|
||||||
|
import io.metersphere.sdk.service.BaseUserService;
|
||||||
|
import io.metersphere.sdk.util.RsaKey;
|
||||||
|
import io.metersphere.sdk.util.RsaUtil;
|
||||||
|
import io.metersphere.sdk.util.SessionUtils;
|
||||||
|
import io.metersphere.sdk.util.Translator;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.reflect.MethodUtils;
|
||||||
|
import org.apache.shiro.SecurityUtils;
|
||||||
|
import org.springframework.context.i18n.LocaleContextHolder;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping
|
||||||
|
public class LoginController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private BaseUserService baseUserService;
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping(value = "/is-login")
|
||||||
|
public ResultHolder isLogin() throws Exception {
|
||||||
|
RsaKey rsaKey = RsaUtil.getRsaKey();
|
||||||
|
SessionUser user = SessionUtils.getUser();
|
||||||
|
if (user != null) {
|
||||||
|
UserDTO userDTO = baseUserService.getUserDTO((String) MethodUtils.invokeMethod(user, "getId"));
|
||||||
|
if (StringUtils.isBlank(userDTO.getLanguage())) {
|
||||||
|
userDTO.setLanguage(LocaleContextHolder.getLocale().toString());
|
||||||
|
}
|
||||||
|
// todo 跳转用户
|
||||||
|
// baseUserService.autoSwitch(userDTO);
|
||||||
|
SessionUser sessionUser = SessionUser.fromUser(userDTO, SessionUtils.getSessionId());
|
||||||
|
SessionUtils.putUser(sessionUser);
|
||||||
|
// 用户只有工作空间权限
|
||||||
|
if (StringUtils.isBlank(sessionUser.getLastProjectId())) {
|
||||||
|
sessionUser.setLastProjectId("no_such_project");
|
||||||
|
}
|
||||||
|
return ResultHolder.success(sessionUser);
|
||||||
|
}
|
||||||
|
MSException.throwException(rsaKey.getPublicKey());
|
||||||
|
//
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/signin")
|
||||||
|
public ResultHolder login(@RequestBody LoginRequest request) {
|
||||||
|
SessionUser sessionUser = SessionUtils.getUser();
|
||||||
|
if (sessionUser != null) {
|
||||||
|
if (!StringUtils.equals(sessionUser.getId(), request.getUsername())) {
|
||||||
|
MSException.throwException(Translator.get("please_logout_current_user"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SecurityUtils.getSubject().getSession().setAttribute("authenticate", UserSource.LOCAL.name());
|
||||||
|
ResultHolder result = baseUserService.login(request);
|
||||||
|
// todo 登录是否提示修改密码
|
||||||
|
// boolean changePassword = baseUserService.checkWhetherChangePasswordOrNot(request);
|
||||||
|
// result.setMessage(BooleanUtils.toStringTrueFalse(changePassword));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "/signout")
|
||||||
|
public void logout(HttpServletResponse response) throws Exception {
|
||||||
|
SecurityUtils.getSubject().logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,6 +3,10 @@ package io.metersphere.sdk.controller.handler;
|
||||||
import io.metersphere.sdk.controller.handler.result.IResultCode;
|
import io.metersphere.sdk.controller.handler.result.IResultCode;
|
||||||
import io.metersphere.sdk.controller.handler.result.MsHttpResultCode;
|
import io.metersphere.sdk.controller.handler.result.MsHttpResultCode;
|
||||||
import io.metersphere.sdk.exception.MSException;
|
import io.metersphere.sdk.exception.MSException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.apache.shiro.ShiroException;
|
||||||
|
import org.apache.shiro.authz.UnauthorizedException;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.validation.FieldError;
|
import org.springframework.validation.FieldError;
|
||||||
|
@ -22,6 +26,7 @@ public class RestControllerExceptionHandler {
|
||||||
* 处理数据校验异常
|
* 处理数据校验异常
|
||||||
* 返回具体字段的校验信息
|
* 返回具体字段的校验信息
|
||||||
* http 状态码返回 400
|
* http 状态码返回 400
|
||||||
|
*
|
||||||
* @param ex
|
* @param ex
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
@ -41,6 +46,7 @@ public class RestControllerExceptionHandler {
|
||||||
/**
|
/**
|
||||||
* 根据 MSException 中的 errorCode
|
* 根据 MSException 中的 errorCode
|
||||||
* 设置对应的 Http 状态码,以及业务状态码和错误提示
|
* 设置对应的 Http 状态码,以及业务状态码和错误提示
|
||||||
|
*
|
||||||
* @param e
|
* @param e
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
@ -64,4 +70,20 @@ public class RestControllerExceptionHandler {
|
||||||
.body(new ResultHolder(errorCode.getCode(), errorCode.getMessage(), e.getMessage()));
|
.body(new ResultHolder(errorCode.getCode(), errorCode.getMessage(), e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*=========== Shiro 异常拦截==============*/
|
||||||
|
@ExceptionHandler(ShiroException.class)
|
||||||
|
public ResultHolder exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) {
|
||||||
|
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||||
|
return ResultHolder.error(HttpStatus.UNAUTHORIZED.value(), exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=========== Shiro 异常拦截==============*/
|
||||||
|
@ExceptionHandler(UnauthorizedException.class)
|
||||||
|
public ResultHolder unauthorizedExceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) {
|
||||||
|
response.setStatus(HttpStatus.FORBIDDEN.value());
|
||||||
|
return ResultHolder.error(HttpStatus.FORBIDDEN.value(), exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package io.metersphere.sdk.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class GroupResource implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
private String name;
|
||||||
|
private Boolean license = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统设置、工作空间、项目类型 公用的权限模块
|
||||||
|
* e.g. 个人信息
|
||||||
|
*/
|
||||||
|
private boolean global = false;
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package io.metersphere.sdk.dto;
|
||||||
|
|
||||||
|
import io.metersphere.sdk.util.RsaKey;
|
||||||
|
import io.metersphere.sdk.util.RsaUtil;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class LoginRequest {
|
||||||
|
@NotBlank(message = "{user_name_is_null}")
|
||||||
|
private String username;
|
||||||
|
@NotBlank(message = "{password_is_null}")
|
||||||
|
private String password;
|
||||||
|
private String authenticate;
|
||||||
|
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
try {
|
||||||
|
RsaKey rsaKey = RsaUtil.getRsaKey();
|
||||||
|
return RsaUtil.privateDecrypt(username, rsaKey.getPrivateKey());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
try {
|
||||||
|
RsaKey rsaKey = RsaUtil.getRsaKey();
|
||||||
|
return RsaUtil.privateDecrypt(password, rsaKey.getPrivateKey());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package io.metersphere.sdk.dto;
|
||||||
|
|
||||||
|
import io.metersphere.sdk.util.CodingUtil;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
public class SessionUser extends UserDTO implements Serializable {
|
||||||
|
public static final String secret = "9a9rdqPlTqhpZzkq";
|
||||||
|
public static final String iv = "1Av7hf9PgHusUHRm";
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -7149638440406959033L;
|
||||||
|
private String csrfToken;
|
||||||
|
private String sessionId;
|
||||||
|
private String loginUrl; // 三方登录地址,如果有值,前端跳转到该地址
|
||||||
|
|
||||||
|
private SessionUser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SessionUser fromUser(UserDTO user, String sessionId) {
|
||||||
|
SessionUser sessionUser = new SessionUser();
|
||||||
|
BeanUtils.copyProperties(user, sessionUser);
|
||||||
|
|
||||||
|
List<String> infos = Arrays.asList(user.getId(), RandomStringUtils.randomAlphabetic(6), sessionId, StringUtils.EMPTY + System.currentTimeMillis());
|
||||||
|
sessionUser.csrfToken = CodingUtil.aesEncrypt(StringUtils.join(infos, "|"), secret, iv);
|
||||||
|
sessionUser.sessionId = sessionId;
|
||||||
|
return sessionUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,14 +1,23 @@
|
||||||
package io.metersphere.sdk.dto;
|
package io.metersphere.sdk.dto;
|
||||||
|
|
||||||
import io.metersphere.system.domain.User;
|
import io.metersphere.system.domain.User;
|
||||||
|
import io.metersphere.system.domain.UserRole;
|
||||||
|
import io.metersphere.system.domain.UserRoleRelation;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
public class UserDTO extends User {
|
public class UserDTO extends User {
|
||||||
|
|
||||||
|
private List<UserRole> userRoles = new ArrayList<>();
|
||||||
|
private List<UserRoleRelation> userRoleRelations = new ArrayList<>();
|
||||||
|
private List<UserRoleResourceDTO> userRolePermissions = new ArrayList<>();
|
||||||
|
|
||||||
@Schema(title = "其他平台对接信息", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
@Schema(title = "其他平台对接信息", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||||
private byte[] platformInfo;
|
private byte[] platformInfo;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package io.metersphere.sdk.dto;
|
||||||
|
|
||||||
|
import io.metersphere.system.domain.UserRole;
|
||||||
|
import io.metersphere.system.domain.UserRoleRelation;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class UserRolePermissionDTO {
|
||||||
|
List<UserRoleResourceDTO> list = new ArrayList<>();
|
||||||
|
List<UserRole> userRoles = new ArrayList<>();
|
||||||
|
List<UserRoleRelation> userRoleRelations = new ArrayList<>();
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package io.metersphere.sdk.dto;
|
||||||
|
|
||||||
|
import io.metersphere.system.domain.UserRole;
|
||||||
|
import io.metersphere.system.domain.UserRolePermission;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class UserRoleResourceDTO implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private GroupResource resource;
|
||||||
|
private List<UserRolePermission> permissions;
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
private UserRole userRole;
|
||||||
|
private List<UserRolePermission> userRolePermissions;
|
||||||
|
}
|
|
@ -7,16 +7,16 @@ public class MSException extends RuntimeException {
|
||||||
|
|
||||||
protected IResultCode errorCode;
|
protected IResultCode errorCode;
|
||||||
|
|
||||||
protected MSException(String message) {
|
public MSException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected MSException(IResultCode errorCode, String message) {
|
public MSException(IResultCode errorCode, String message) {
|
||||||
super(message);
|
super(message);
|
||||||
this.errorCode = errorCode;
|
this.errorCode = errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected MSException(IResultCode errorCode, Throwable t) {
|
public MSException(IResultCode errorCode, Throwable t) {
|
||||||
super(t);
|
super(t);
|
||||||
this.errorCode = errorCode;
|
this.errorCode = errorCode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package io.metersphere.sdk.mapper;
|
||||||
|
|
||||||
|
import io.metersphere.project.domain.Project;
|
||||||
|
|
||||||
|
public interface BaseProjectMapper {
|
||||||
|
Project selectOne();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="io.metersphere.sdk.mapper.BaseProjectMapper">
|
||||||
|
<select id="selectOne" resultType="io.metersphere.project.domain.Project">
|
||||||
|
SELECT *
|
||||||
|
FROM project
|
||||||
|
LIMIT 1
|
||||||
|
</select>
|
||||||
|
</mapper>
|
|
@ -14,4 +14,6 @@ public interface BaseUserMapper {
|
||||||
void insert(User user);
|
void insert(User user);
|
||||||
|
|
||||||
void batchSave(@Param("users") List<User> users);
|
void batchSave(@Param("users") List<User> users);
|
||||||
|
|
||||||
|
boolean isSuperUser(String userId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
<mapper namespace="io.metersphere.sdk.mapper.BaseUserMapper">
|
<mapper namespace="io.metersphere.sdk.mapper.BaseUserMapper">
|
||||||
|
|
||||||
<select id="selectById" resultType="io.metersphere.sdk.dto.UserDTO">
|
<select id="selectById" resultType="io.metersphere.sdk.dto.UserDTO">
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM user
|
FROM user
|
||||||
|
@ -15,19 +14,29 @@
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<insert id="insert">
|
<insert id="insert">
|
||||||
INSERT INTO user(id, name, email, password, status, create_time, update_time, language, last_workspace_id, phone,
|
INSERT INTO user(id, name, email, password, status, create_time, update_time, language, last_workspace_id,
|
||||||
|
phone,
|
||||||
source, last_project_id, create_user)
|
source, last_project_id, create_user)
|
||||||
VALUES (#{id}, #{name}, #{email}, #{password}, #{status}, #{createTime}, #{updateTime}, #{language},
|
VALUES (#{id}, #{name}, #{email}, #{password}, #{status}, #{createTime}, #{updateTime}, #{language},
|
||||||
#{lastWorkspaceId}, #{phone}, #{source}, #{lastProjectId}, #{createUser})
|
#{lastWorkspaceId}, #{phone}, #{source}, #{lastProjectId}, #{createUser})
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
<insert id="batchSave">
|
<insert id="batchSave">
|
||||||
INSERT INTO user(id, name, email, password, status, create_time, update_time, language, last_workspace_id, phone,
|
INSERT INTO user(id, name, email, password, status, create_time, update_time, language, last_workspace_id,
|
||||||
|
phone,
|
||||||
source, last_project_id, create_user)
|
source, last_project_id, create_user)
|
||||||
VALUES
|
VALUES
|
||||||
<foreach collection="users" item="user" separator=",">
|
<foreach collection="users" item="user" separator=",">
|
||||||
(#{user.id}, #{user.name}, #{user.email}, #{user.password}, #{user.status}, #{user.createTime}, #{user.updateTime}, #{user.language},
|
(#{user.id}, #{user.name}, #{user.email}, #{user.password}, #{user.status}, #{user.createTime},
|
||||||
|
#{user.updateTime}, #{user.language},
|
||||||
#{user.lastWorkspaceId}, #{user.phone}, #{user.source}, #{user.lastProjectId}, #{user.createUser})
|
#{user.lastWorkspaceId}, #{user.phone}, #{user.source}, #{user.lastProjectId}, #{user.createUser})
|
||||||
</foreach>
|
</foreach>
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
<select id="isSuperUser" resultType="java.lang.Boolean">
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM user_role_relation
|
||||||
|
WHERE user_id = #{userId}
|
||||||
|
AND role_id = 'super_group'
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
|
@ -0,0 +1,90 @@
|
||||||
|
package io.metersphere.sdk.security;
|
||||||
|
|
||||||
|
|
||||||
|
import io.metersphere.sdk.constants.SessionConstants;
|
||||||
|
import io.metersphere.sdk.dto.SessionUser;
|
||||||
|
import io.metersphere.sdk.util.CodingUtil;
|
||||||
|
import io.metersphere.sdk.util.CommonBeanFactory;
|
||||||
|
import io.metersphere.sdk.util.SessionUtils;
|
||||||
|
import jakarta.servlet.ServletRequest;
|
||||||
|
import jakarta.servlet.ServletResponse;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.shiro.SecurityUtils;
|
||||||
|
import org.apache.shiro.web.filter.authc.AnonymousFilter;
|
||||||
|
import org.apache.shiro.web.util.WebUtils;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
|
||||||
|
public class CsrfFilter extends AnonymousFilter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) {
|
||||||
|
HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
|
||||||
|
|
||||||
|
if (!SecurityUtils.getSubject().isAuthenticated()) {
|
||||||
|
((HttpServletResponse) response).setHeader(SessionConstants.AUTHENTICATION_STATUS, SessionConstants.AUTHENTICATION_INVALID);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 错误页面不需要 csrf
|
||||||
|
if (WebUtils.toHttp(request).getRequestURI().equals("/error")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// todo api 过来的请求
|
||||||
|
// if (ApiKeyHandler.isApiKeyCall(WebUtils.toHttp(request))) {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// websocket 不需要csrf
|
||||||
|
String websocketKey = httpServletRequest.getHeader("Sec-WebSocket-Key");
|
||||||
|
if (StringUtils.isNotBlank(websocketKey)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求头取出的token value
|
||||||
|
String csrfToken = httpServletRequest.getHeader(SessionConstants.CSRF_TOKEN);
|
||||||
|
String xAuthToken = httpServletRequest.getHeader(SessionConstants.HEADER_TOKEN);
|
||||||
|
// 校验 token
|
||||||
|
validateToken(csrfToken, xAuthToken);
|
||||||
|
// 校验 referer
|
||||||
|
validateReferer(httpServletRequest);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateReferer(HttpServletRequest request) {
|
||||||
|
Environment env = CommonBeanFactory.getBean(Environment.class);
|
||||||
|
String domains = env.getProperty("referer.urls");
|
||||||
|
if (StringUtils.isBlank(domains)) {
|
||||||
|
// 没有配置不校验
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] split = StringUtils.split(domains, ",");
|
||||||
|
String referer = request.getHeader(HttpHeaders.REFERER);
|
||||||
|
if (split != null) {
|
||||||
|
if (!ArrayUtils.contains(split, referer)) {
|
||||||
|
throw new RuntimeException("csrf error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateToken(String csrfToken, String xAuthToken) {
|
||||||
|
if (StringUtils.isBlank(csrfToken)) {
|
||||||
|
throw new RuntimeException("csrf token is empty");
|
||||||
|
}
|
||||||
|
csrfToken = CodingUtil.aesDecrypt(csrfToken, SessionUser.secret, SessionUser.iv);
|
||||||
|
|
||||||
|
String[] signatureArray = StringUtils.split(StringUtils.trimToNull(csrfToken), "|");
|
||||||
|
if (signatureArray.length != 4) {
|
||||||
|
throw new RuntimeException("invalid token");
|
||||||
|
}
|
||||||
|
if (!StringUtils.equals(SessionUtils.getUserId(), signatureArray[0])) {
|
||||||
|
throw new RuntimeException("Please check csrf token.");
|
||||||
|
}
|
||||||
|
if (!StringUtils.equals(SessionUtils.getSessionId(), signatureArray[2]) &&
|
||||||
|
!StringUtils.equals(xAuthToken, signatureArray[2])) {
|
||||||
|
throw new RuntimeException("Please check csrf token.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package io.metersphere.sdk.security;
|
||||||
|
|
||||||
|
import io.metersphere.project.domain.Project;
|
||||||
|
import io.metersphere.sdk.util.SessionUtils;
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||||
|
import org.apache.commons.lang3.reflect.MethodUtils;
|
||||||
|
import org.apache.shiro.aop.AnnotationResolver;
|
||||||
|
import org.apache.shiro.aop.MethodInvocation;
|
||||||
|
import org.apache.shiro.authz.AuthorizationException;
|
||||||
|
import org.apache.shiro.authz.aop.PermissionAnnotationMethodInterceptor;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Parameter;
|
||||||
|
|
||||||
|
public class MsPermissionAnnotationMethodInterceptor extends PermissionAnnotationMethodInterceptor {
|
||||||
|
|
||||||
|
|
||||||
|
public MsPermissionAnnotationMethodInterceptor(AnnotationResolver resolver) {
|
||||||
|
super(resolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void assertAuthorized(MethodInvocation mi) throws AuthorizationException {
|
||||||
|
String projectId = null;
|
||||||
|
String workspaceId = null;
|
||||||
|
Object[] arguments = mi.getArguments();
|
||||||
|
if (ArrayUtils.isNotEmpty(arguments)) {
|
||||||
|
Parameter[] parameters = mi.getMethod().getParameters();
|
||||||
|
for (int i = 0; i < arguments.length; i++) {
|
||||||
|
Object argument = arguments[i];
|
||||||
|
if (argument instanceof String) {
|
||||||
|
if (StringUtils.equals(parameters[i].getName(), "projectId")) {
|
||||||
|
projectId = (String) argument;
|
||||||
|
}
|
||||||
|
if (StringUtils.equals(parameters[i].getName(), "workspaceId")) {
|
||||||
|
workspaceId = (String) argument;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
if (StringUtils.isEmpty(projectId) && isExistField(argument, "projectId")) {
|
||||||
|
projectId = (String) MethodUtils.invokeMethod(argument, "getProjectId");
|
||||||
|
}
|
||||||
|
if (StringUtils.equals(parameters[i].getName(), "project") && argument instanceof Project) {
|
||||||
|
projectId = ((Project) argument).getId();
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(workspaceId) && isExistField(argument, "workspaceId")) {
|
||||||
|
workspaceId = (String) MethodUtils.invokeMethod(argument, "getWorkspaceId");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
SessionUtils.setCurrentWorkspaceId(workspaceId);
|
||||||
|
SessionUtils.setCurrentProjectId(projectId);
|
||||||
|
super.assertAuthorized(mi);
|
||||||
|
} finally {
|
||||||
|
SessionUtils.clearCurrentWorkspaceId();
|
||||||
|
SessionUtils.clearCurrentProjectId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean isExistField(Object obj, String fieldName) {
|
||||||
|
if (obj == null || StringUtils.isEmpty(fieldName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//获取这个类的所有属性
|
||||||
|
Field[] fields = FieldUtils.getAllFields(obj.getClass());
|
||||||
|
boolean flag = false;
|
||||||
|
//循环遍历所有的fields
|
||||||
|
for (Field field : fields) {
|
||||||
|
if (field.getName().equals(fieldName)) {
|
||||||
|
flag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package io.metersphere.sdk.security;
|
||||||
|
|
||||||
|
import io.metersphere.sdk.constants.SessionConstants;
|
||||||
|
import io.metersphere.sdk.dto.SessionUser;
|
||||||
|
import io.metersphere.sdk.util.CodingUtil;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class SSOSessionHandler {
|
||||||
|
|
||||||
|
public static String random = UUID.randomUUID() + UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
public static String validate(HttpServletRequest request) {
|
||||||
|
try {
|
||||||
|
String v = request.getHeader(SessionConstants.CSRF_TOKEN);
|
||||||
|
if (StringUtils.isNotBlank(v)) {
|
||||||
|
return validate(v);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// LogUtil.error("failed to validate", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String validate(String csrfToken) {
|
||||||
|
csrfToken = CodingUtil.aesDecrypt(csrfToken, SessionUser.secret, SessionUser.iv);
|
||||||
|
String[] signatureArray = StringUtils.split(StringUtils.trimToNull(csrfToken), "|");
|
||||||
|
if (signatureArray.length != 4) {
|
||||||
|
throw new RuntimeException("invalid token");
|
||||||
|
}
|
||||||
|
return signatureArray[0];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
package io.metersphere.sdk.security.realm;
|
||||||
|
|
||||||
|
|
||||||
|
import io.metersphere.sdk.constants.SessionConstants;
|
||||||
|
import io.metersphere.sdk.constants.UserSource;
|
||||||
|
import io.metersphere.sdk.dto.SessionUser;
|
||||||
|
import io.metersphere.sdk.dto.UserDTO;
|
||||||
|
import io.metersphere.sdk.service.BaseUserService;
|
||||||
|
import io.metersphere.sdk.util.SessionUtils;
|
||||||
|
import io.metersphere.sdk.util.Translator;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.shiro.SecurityUtils;
|
||||||
|
import org.apache.shiro.authc.*;
|
||||||
|
import org.apache.shiro.authz.AuthorizationInfo;
|
||||||
|
import org.apache.shiro.realm.AuthorizingRealm;
|
||||||
|
import org.apache.shiro.session.Session;
|
||||||
|
import org.apache.shiro.subject.PrincipalCollection;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义Realm 注入service 可能会导致在 service的aop 失效,例如@Transactional,
|
||||||
|
* 解决方法:
|
||||||
|
* <p>
|
||||||
|
* 1. 这里改成注入mapper,这样mapper 中的事务失效<br/>
|
||||||
|
* 2. 这里仍然注入service,在配置ShiroConfig 的时候不去set realm, 等到spring 初始化完成之后
|
||||||
|
* set realm
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class LocalRealm extends AuthorizingRealm {
|
||||||
|
|
||||||
|
private Logger logger = LoggerFactory.getLogger(LocalRealm.class);
|
||||||
|
@Resource
|
||||||
|
private BaseUserService baseUserService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "LOCAL";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色认证
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录认证
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
|
||||||
|
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
|
||||||
|
Session session = SecurityUtils.getSubject().getSession();
|
||||||
|
String login = (String) session.getAttribute("authenticate");
|
||||||
|
|
||||||
|
String userId = token.getUsername();
|
||||||
|
String password = String.valueOf(token.getPassword());
|
||||||
|
|
||||||
|
if (StringUtils.equals(login, UserSource.LOCAL.name())) {
|
||||||
|
return loginLocalMode(userId, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
UserDTO user = getUserWithOutAuthenticate(userId);
|
||||||
|
userId = user.getId();
|
||||||
|
SessionUser sessionUser = SessionUser.fromUser(user, (String) session.getId());
|
||||||
|
session.setAttribute(SessionConstants.ATTR_USER, sessionUser);
|
||||||
|
return new SimpleAuthenticationInfo(userId, password, getName());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPermitted(PrincipalCollection principals, String permission) {
|
||||||
|
return SessionUtils.hasPermission(SessionUtils.getCurrentWorkspaceId(), SessionUtils.getCurrentProjectId(), permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserDTO getUserWithOutAuthenticate(String userId) {
|
||||||
|
UserDTO user = baseUserService.getUserDTO(userId);
|
||||||
|
String msg;
|
||||||
|
if (user == null) {
|
||||||
|
user = baseUserService.getUserDTOByEmail(userId);
|
||||||
|
if (user == null) {
|
||||||
|
msg = "The user does not exist: " + userId;
|
||||||
|
logger.warn(msg);
|
||||||
|
throw new UnknownAccountException(Translator.get("password_is_incorrect"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AuthenticationInfo loginLocalMode(String userId, String password) {
|
||||||
|
UserDTO user = baseUserService.getUserDTO(userId);
|
||||||
|
String msg;
|
||||||
|
if (user == null) {
|
||||||
|
user = baseUserService.getUserDTOByEmail(userId, UserSource.LOCAL.name());
|
||||||
|
if (user == null) {
|
||||||
|
msg = "The user does not exist: " + userId;
|
||||||
|
logger.warn(msg);
|
||||||
|
throw new UnknownAccountException(Translator.get("password_is_incorrect"));
|
||||||
|
}
|
||||||
|
userId = user.getId();
|
||||||
|
}
|
||||||
|
// 密码验证
|
||||||
|
if (!baseUserService.checkUserPassword(userId, password)) {
|
||||||
|
throw new IncorrectCredentialsException(Translator.get("password_is_incorrect"));
|
||||||
|
}
|
||||||
|
SessionUser sessionUser = SessionUser.fromUser(user, SessionUtils.getSessionId());
|
||||||
|
SessionUtils.putUser(sessionUser);
|
||||||
|
return new SimpleAuthenticationInfo(userId, password, getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,8 +1,399 @@
|
||||||
package io.metersphere.sdk.service;
|
package io.metersphere.sdk.service;
|
||||||
|
|
||||||
|
import io.metersphere.project.domain.Project;
|
||||||
|
import io.metersphere.project.domain.ProjectExample;
|
||||||
|
import io.metersphere.project.mapper.ProjectMapper;
|
||||||
|
import io.metersphere.sdk.constants.UserRoleConstants;
|
||||||
|
import io.metersphere.sdk.constants.UserRoleType;
|
||||||
|
import io.metersphere.sdk.constants.UserSource;
|
||||||
|
import io.metersphere.sdk.constants.UserStatus;
|
||||||
|
import io.metersphere.sdk.controller.handler.ResultHolder;
|
||||||
|
import io.metersphere.sdk.dto.*;
|
||||||
|
import io.metersphere.sdk.exception.MSException;
|
||||||
|
import io.metersphere.sdk.mapper.BaseProjectMapper;
|
||||||
|
import io.metersphere.sdk.mapper.BaseUserMapper;
|
||||||
|
import io.metersphere.sdk.util.CodingUtil;
|
||||||
|
import io.metersphere.sdk.util.SessionUtils;
|
||||||
|
import io.metersphere.sdk.util.Translator;
|
||||||
|
import io.metersphere.system.domain.*;
|
||||||
|
import io.metersphere.system.mapper.UserMapper;
|
||||||
|
import io.metersphere.system.mapper.UserRoleMapper;
|
||||||
|
import io.metersphere.system.mapper.UserRolePermissionMapper;
|
||||||
|
import io.metersphere.system.mapper.UserRoleRelationMapper;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.shiro.SecurityUtils;
|
||||||
|
import org.apache.shiro.authc.*;
|
||||||
|
import org.apache.shiro.authz.UnauthorizedException;
|
||||||
|
import org.apache.shiro.subject.Subject;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public class BaseUserService {
|
public class BaseUserService {
|
||||||
|
@Resource
|
||||||
|
private BaseUserMapper baseUserMapper;
|
||||||
|
@Resource
|
||||||
|
private UserMapper userMapper;
|
||||||
|
@Resource
|
||||||
|
private UserRolePermissionMapper userRolePermissionMapper;
|
||||||
|
@Resource
|
||||||
|
private UserRoleMapper userRoleMapper;
|
||||||
|
@Resource
|
||||||
|
private UserRoleRelationMapper userRoleRelationMapper;
|
||||||
|
@Resource
|
||||||
|
private ProjectMapper projectMapper;
|
||||||
|
@Resource
|
||||||
|
private BaseProjectMapper baseProjectMapper;
|
||||||
|
|
||||||
|
|
||||||
|
public UserDTO getUserDTO(String userId) {
|
||||||
|
UserDTO userDTO = baseUserMapper.selectById(userId);
|
||||||
|
if (userDTO == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (StringUtils.equals(userDTO.getStatus(), UserStatus.DISABLED)) {
|
||||||
|
throw new DisabledAccountException();
|
||||||
|
}
|
||||||
|
UserRolePermissionDTO dto = getUserRolePermission(userId);
|
||||||
|
userDTO.setUserRoleRelations(dto.getUserRoleRelations());
|
||||||
|
userDTO.setUserRoles(dto.getUserRoles());
|
||||||
|
userDTO.setUserRolePermissions(dto.getList());
|
||||||
|
return userDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultHolder login(LoginRequest request) {
|
||||||
|
String login = (String) SecurityUtils.getSubject().getSession().getAttribute("authenticate");
|
||||||
|
String username = StringUtils.trim(request.getUsername());
|
||||||
|
String password = StringUtils.EMPTY;
|
||||||
|
if (!StringUtils.equals(login, UserSource.LDAP.name())) {
|
||||||
|
password = StringUtils.trim(request.getPassword());
|
||||||
|
}
|
||||||
|
|
||||||
|
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
|
||||||
|
Subject subject = SecurityUtils.getSubject();
|
||||||
|
try {
|
||||||
|
subject.login(token);
|
||||||
|
if (subject.isAuthenticated()) {
|
||||||
|
SessionUser sessionUser = SessionUtils.getUser();
|
||||||
|
autoSwitch(sessionUser);
|
||||||
|
// 放入session中
|
||||||
|
SessionUtils.putUser(sessionUser);
|
||||||
|
return ResultHolder.success(sessionUser);
|
||||||
|
} else {
|
||||||
|
throw new MSException(Translator.get("login_fail"));
|
||||||
|
}
|
||||||
|
} catch (ExcessiveAttemptsException e) {
|
||||||
|
throw new ExcessiveAttemptsException(Translator.get("excessive_attempts"));
|
||||||
|
} catch (LockedAccountException e) {
|
||||||
|
throw new LockedAccountException(Translator.get("user_locked"));
|
||||||
|
} catch (DisabledAccountException e) {
|
||||||
|
throw new DisabledAccountException(Translator.get("user_has_been_disabled"));
|
||||||
|
} catch (ExpiredCredentialsException e) {
|
||||||
|
throw new ExpiredCredentialsException(Translator.get("user_expires"));
|
||||||
|
} catch (AuthenticationException e) {
|
||||||
|
throw new AuthenticationException(e.getMessage());
|
||||||
|
} catch (UnauthorizedException e) {
|
||||||
|
throw new UnauthorizedException(Translator.get("not_authorized") + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void autoSwitch(UserDTO user) {
|
||||||
|
// 用户有 last_project_id 权限
|
||||||
|
if (hasLastProjectPermission(user)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 用户有 last_workspace_id 权限
|
||||||
|
if (hasLastWorkspacePermission(user)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 判断其他权限
|
||||||
|
checkNewWorkspaceAndProject(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkNewWorkspaceAndProject(UserDTO user) {
|
||||||
|
List<UserRoleRelation> userRoleRelations = user.getUserRoleRelations();
|
||||||
|
List<String> projectRoleIds = user.getUserRoles()
|
||||||
|
.stream().filter(ug -> StringUtils.equals(ug.getType(), UserRoleType.PROJECT))
|
||||||
|
.map(UserRole::getId)
|
||||||
|
.toList();
|
||||||
|
List<UserRoleRelation> project = userRoleRelations.stream().filter(ug -> projectRoleIds.contains(ug.getRoleId()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (CollectionUtils.isEmpty(project)) {
|
||||||
|
List<String> workspaceIds = user.getUserRoles()
|
||||||
|
.stream()
|
||||||
|
.filter(ug -> StringUtils.equals(ug.getType(), UserRoleType.WORKSPACE))
|
||||||
|
.map(UserRole::getId)
|
||||||
|
.toList();
|
||||||
|
List<UserRoleRelation> workspaces = userRoleRelations.stream().filter(ug -> workspaceIds.contains(ug.getRoleId()))
|
||||||
|
.toList();
|
||||||
|
if (workspaces.size() > 0) {
|
||||||
|
String wsId = workspaces.get(0).getSourceId();
|
||||||
|
switchUserResource("workspace", wsId, user);
|
||||||
|
} else {
|
||||||
|
List<String> superRoleIds = user.getUserRoles()
|
||||||
|
.stream()
|
||||||
|
.map(UserRole::getId)
|
||||||
|
.filter(id -> StringUtils.equals(id, UserRoleConstants.SUPER_ROLE))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (CollectionUtils.isNotEmpty(superRoleIds)) {
|
||||||
|
Project p = baseProjectMapper.selectOne();
|
||||||
|
if (p != null) {
|
||||||
|
switchSuperUserResource(p.getId(), p.getWorkspaceId(), user);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 用户登录之后没有项目和工作空间的权限就把值清空
|
||||||
|
user.setLastWorkspaceId(StringUtils.EMPTY);
|
||||||
|
user.setLastProjectId(StringUtils.EMPTY);
|
||||||
|
updateUser(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
UserRoleRelation userRoleRelation = project.stream().filter(p -> StringUtils.isNotBlank(p.getSourceId()))
|
||||||
|
.toList().get(0);
|
||||||
|
String projectId = userRoleRelation.getSourceId();
|
||||||
|
Project p = projectMapper.selectByPrimaryKey(projectId);
|
||||||
|
String wsId = p.getWorkspaceId();
|
||||||
|
user.setId(user.getId());
|
||||||
|
user.setLastProjectId(projectId);
|
||||||
|
user.setLastWorkspaceId(wsId);
|
||||||
|
updateUser(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasLastProjectPermission(UserDTO user) {
|
||||||
|
if (StringUtils.isNotBlank(user.getLastProjectId())) {
|
||||||
|
List<UserRoleRelation> userRoleRelations = user.getUserRoleRelations().stream()
|
||||||
|
.filter(ug -> StringUtils.equals(user.getLastProjectId(), ug.getSourceId()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (CollectionUtils.isNotEmpty(userRoleRelations)) {
|
||||||
|
Project project = projectMapper.selectByPrimaryKey(user.getLastProjectId());
|
||||||
|
if (StringUtils.equals(project.getWorkspaceId(), user.getLastWorkspaceId())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// last_project_id 和 last_workspace_id 对应不上了
|
||||||
|
user.setLastWorkspaceId(project.getWorkspaceId());
|
||||||
|
updateUser(user);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return baseUserMapper.isSuperUser(user.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasLastWorkspacePermission(UserDTO user) {
|
||||||
|
if (StringUtils.isNotBlank(user.getLastWorkspaceId())) {
|
||||||
|
List<UserRoleRelation> userRoleRelations = user.getUserRoleRelations().stream()
|
||||||
|
.filter(ug -> StringUtils.equals(user.getLastWorkspaceId(), ug.getSourceId()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (CollectionUtils.isNotEmpty(userRoleRelations)) {
|
||||||
|
ProjectExample example = new ProjectExample();
|
||||||
|
example.createCriteria().andWorkspaceIdEqualTo(user.getLastWorkspaceId());
|
||||||
|
List<Project> projects = projectMapper.selectByExample(example);
|
||||||
|
// 工作空间下没有项目
|
||||||
|
if (CollectionUtils.isEmpty(projects)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 工作空间下有项目,选中有权限的项目
|
||||||
|
List<String> projectIds = projects.stream()
|
||||||
|
.map(Project::getId)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
List<UserRoleRelation> roleRelations = user.getUserRoleRelations();
|
||||||
|
List<String> projectRoleIds = user.getUserRoles()
|
||||||
|
.stream().filter(ug -> StringUtils.equals(ug.getType(), UserRoleType.PROJECT))
|
||||||
|
.map(UserRole::getId)
|
||||||
|
.toList();
|
||||||
|
List<String> projectIdsWithPermission = roleRelations.stream().filter(ug -> projectRoleIds.contains(ug.getRoleId()))
|
||||||
|
.map(UserRoleRelation::getSourceId)
|
||||||
|
.filter(StringUtils::isNotBlank)
|
||||||
|
.filter(projectIds::contains)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
List<String> intersection = projectIds.stream().filter(projectIdsWithPermission::contains).collect(Collectors.toList());
|
||||||
|
// 当前工作空间下的所有项目都没有权限
|
||||||
|
if (CollectionUtils.isEmpty(intersection)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Project project = projects.stream().filter(p -> StringUtils.equals(intersection.get(0), p.getId())).findFirst().get();
|
||||||
|
String wsId = project.getWorkspaceId();
|
||||||
|
user.setId(user.getId());
|
||||||
|
user.setLastProjectId(project.getId());
|
||||||
|
user.setLastWorkspaceId(wsId);
|
||||||
|
updateUser(user);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return baseUserMapper.isSuperUser(user.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void switchUserResource(String sign, String sourceId, UserDTO sessionUser) {
|
||||||
|
// 获取最新UserDTO
|
||||||
|
UserDTO user = getUserDTO(sessionUser.getId());
|
||||||
|
User newUser = new User();
|
||||||
|
boolean isSuper = baseUserMapper.isSuperUser(sessionUser.getId());
|
||||||
|
if (StringUtils.equals("workspace", sign)) {
|
||||||
|
user.setLastWorkspaceId(sourceId);
|
||||||
|
sessionUser.setLastWorkspaceId(sourceId);
|
||||||
|
List<Project> projects = getProjectListByWsAndUserId(sessionUser.getId(), sourceId);
|
||||||
|
if (CollectionUtils.isNotEmpty(projects)) {
|
||||||
|
user.setLastProjectId(projects.get(0).getId());
|
||||||
|
} else {
|
||||||
|
if (isSuper) {
|
||||||
|
ProjectExample example = new ProjectExample();
|
||||||
|
example.createCriteria().andWorkspaceIdEqualTo(sourceId);
|
||||||
|
List<Project> allWsProject = projectMapper.selectByExample(example);
|
||||||
|
if (CollectionUtils.isNotEmpty(allWsProject)) {
|
||||||
|
user.setLastProjectId(allWsProject.get(0).getId());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
user.setLastProjectId(StringUtils.EMPTY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BeanUtils.copyProperties(user, newUser);
|
||||||
|
// 切换工作空间或组织之后更新 session 里的 user
|
||||||
|
SessionUtils.putUser(SessionUser.fromUser(user, SessionUtils.getSessionId()));
|
||||||
|
userMapper.updateByPrimaryKeySelective(newUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchSuperUserResource(String projectId, String workspaceId, UserDTO sessionUser) {
|
||||||
|
// 获取最新UserDTO
|
||||||
|
UserDTO user = getUserDTO(sessionUser.getId());
|
||||||
|
User newUser = new User();
|
||||||
|
user.setLastWorkspaceId(workspaceId);
|
||||||
|
sessionUser.setLastWorkspaceId(workspaceId);
|
||||||
|
user.setLastProjectId(projectId);
|
||||||
|
BeanUtils.copyProperties(user, newUser);
|
||||||
|
// 切换工作空间或组织之后更新 session 里的 user
|
||||||
|
SessionUtils.putUser(SessionUser.fromUser(user, SessionUtils.getSessionId()));
|
||||||
|
userMapper.updateByPrimaryKeySelective(newUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateUser(User user) {
|
||||||
|
// todo 提取重复代码
|
||||||
|
if (StringUtils.isNotBlank(user.getEmail())) {
|
||||||
|
UserExample example = new UserExample();
|
||||||
|
UserExample.Criteria criteria = example.createCriteria();
|
||||||
|
criteria.andEmailEqualTo(user.getEmail());
|
||||||
|
criteria.andIdNotEqualTo(user.getId());
|
||||||
|
if (userMapper.countByExample(example) > 0) {
|
||||||
|
MSException.throwException(Translator.get("user_email_already_exists"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
user.setPassword(null);
|
||||||
|
user.setUpdateTime(System.currentTimeMillis());
|
||||||
|
// 变更前
|
||||||
|
User userFromDB = userMapper.selectByPrimaryKey(user.getId());
|
||||||
|
// last workspace id 变了
|
||||||
|
if (user.getLastWorkspaceId() != null && !StringUtils.equals(user.getLastWorkspaceId(), userFromDB.getLastWorkspaceId())) {
|
||||||
|
List<Project> projects = getProjectListByWsAndUserId(user.getId(), user.getLastWorkspaceId());
|
||||||
|
if (projects.size() > 0) {
|
||||||
|
// 如果传入的 last_project_id 是 last_workspace_id 下面的
|
||||||
|
boolean present = projects.stream().anyMatch(p -> StringUtils.equals(p.getId(), user.getLastProjectId()));
|
||||||
|
if (!present) {
|
||||||
|
user.setLastProjectId(projects.get(0).getId());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
user.setLastProjectId(StringUtils.EMPTY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 执行变更
|
||||||
|
userMapper.updateByPrimaryKeySelective(user);
|
||||||
|
if (StringUtils.equals(user.getStatus(), UserStatus.DISABLED)) {
|
||||||
|
SessionUtils.kickOutUser(user.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Project> getProjectListByWsAndUserId(String userId, String workspaceId) {
|
||||||
|
ProjectExample projectExample = new ProjectExample();
|
||||||
|
projectExample.createCriteria().andWorkspaceIdEqualTo(workspaceId);
|
||||||
|
List<Project> projects = projectMapper.selectByExample(projectExample);
|
||||||
|
|
||||||
|
UserRoleRelationExample userRoleRelationExample = new UserRoleRelationExample();
|
||||||
|
userRoleRelationExample.createCriteria().andUserIdEqualTo(userId);
|
||||||
|
List<UserRoleRelation> userRoleRelations = userRoleRelationMapper.selectByExample(userRoleRelationExample);
|
||||||
|
List<Project> projectList = new ArrayList<>();
|
||||||
|
userRoleRelations.forEach(userRoleRelation -> projects.forEach(project -> {
|
||||||
|
if (StringUtils.equals(userRoleRelation.getSourceId(), project.getId())) {
|
||||||
|
if (!projectList.contains(project)) {
|
||||||
|
projectList.add(project);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return projectList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public UserDTO getUserDTOByEmail(String email, String... source) {
|
||||||
|
UserExample example = new UserExample();
|
||||||
|
UserExample.Criteria criteria = example.createCriteria();
|
||||||
|
criteria.andEmailEqualTo(email);
|
||||||
|
|
||||||
|
if (!CollectionUtils.isEmpty(Arrays.asList(source))) {
|
||||||
|
criteria.andSourceIn(Arrays.asList(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<User> users = userMapper.selectByExample(example);
|
||||||
|
|
||||||
|
if (users == null || users.size() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getUserDTO(users.get(0).getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean checkUserPassword(String userId, String password) {
|
||||||
|
if (StringUtils.isBlank(userId)) {
|
||||||
|
MSException.throwException(Translator.get("user_name_is_null"));
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(password)) {
|
||||||
|
MSException.throwException(Translator.get("password_is_null"));
|
||||||
|
}
|
||||||
|
UserExample example = new UserExample();
|
||||||
|
example.createCriteria().andIdEqualTo(userId).andPasswordEqualTo(CodingUtil.md5(password));
|
||||||
|
return userMapper.countByExample(example) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserRolePermissionDTO getUserRolePermission(String userId) {
|
||||||
|
UserRolePermissionDTO permissionDTO = new UserRolePermissionDTO();
|
||||||
|
List<UserRoleResourceDTO> list = new ArrayList<>();
|
||||||
|
UserRoleRelationExample userRoleRelationExample = new UserRoleRelationExample();
|
||||||
|
userRoleRelationExample.createCriteria().andUserIdEqualTo(userId);
|
||||||
|
List<UserRoleRelation> userRoleRelations = userRoleRelationMapper.selectByExample(userRoleRelationExample);
|
||||||
|
if (CollectionUtils.isEmpty(userRoleRelations)) {
|
||||||
|
return permissionDTO;
|
||||||
|
}
|
||||||
|
permissionDTO.setUserRoleRelations(userRoleRelations);
|
||||||
|
List<String> roleList = userRoleRelations.stream().map(UserRoleRelation::getRoleId).collect(Collectors.toList());
|
||||||
|
UserRoleExample userRoleExample = new UserRoleExample();
|
||||||
|
userRoleExample.createCriteria().andIdIn(roleList);
|
||||||
|
List<UserRole> userRoles = userRoleMapper.selectByExample(userRoleExample);
|
||||||
|
permissionDTO.setUserRoles(userRoles);
|
||||||
|
for (UserRole gp : userRoles) {
|
||||||
|
UserRoleResourceDTO dto = new UserRoleResourceDTO();
|
||||||
|
dto.setUserRole(gp);
|
||||||
|
UserRolePermissionExample userRolePermissionExample = new UserRolePermissionExample();
|
||||||
|
userRolePermissionExample.createCriteria().andRoleIdEqualTo(gp.getId());
|
||||||
|
List<UserRolePermission> userRolePermissions = userRolePermissionMapper.selectByExample(userRolePermissionExample);
|
||||||
|
dto.setUserRolePermissions(userRolePermissions);
|
||||||
|
list.add(dto);
|
||||||
|
}
|
||||||
|
permissionDTO.setList(list);
|
||||||
|
return permissionDTO;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
package io.metersphere.sdk.util;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class CommonBeanFactory implements ApplicationContextAware {
|
||||||
|
private static ApplicationContext context;
|
||||||
|
|
||||||
|
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
|
||||||
|
context = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object getBean(String beanName) {
|
||||||
|
try {
|
||||||
|
return context != null && !StringUtils.isBlank(beanName) ? context.getBean(beanName) : null;
|
||||||
|
} catch (BeansException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T getBean(Class<T> className) {
|
||||||
|
try {
|
||||||
|
return context != null && className != null ? context.getBean(className) : null;
|
||||||
|
} catch (BeansException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Map<String, T> getBeansOfType(Class<T> className) {
|
||||||
|
return context.getBeansOfType(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object invoke(String beanName, Function<Class, Method> methodFunction, Object... args) {
|
||||||
|
Object bean = getBean(beanName);
|
||||||
|
try {
|
||||||
|
Class<?> clazz = bean.getClass();
|
||||||
|
return methodFunction.apply(clazz).invoke(bean, args);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// LogUtil.error(e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
package io.metersphere.sdk.util;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class FilterChainUtils {
|
||||||
|
|
||||||
|
public static Map<String, String> loadBaseFilterChain() {
|
||||||
|
Map<String, String> filterChainDefinitionMap = new HashMap<>();
|
||||||
|
filterChainDefinitionMap.put("/*.html", "anon");
|
||||||
|
filterChainDefinitionMap.put("/signin", "anon");
|
||||||
|
filterChainDefinitionMap.put("/ldap/signin", "anon");
|
||||||
|
filterChainDefinitionMap.put("/ldap/open", "anon");
|
||||||
|
filterChainDefinitionMap.put("/signout", "anon");
|
||||||
|
filterChainDefinitionMap.put("/is-login", "anon");
|
||||||
|
filterChainDefinitionMap.put("/css/**", "anon");
|
||||||
|
filterChainDefinitionMap.put("/js/**", "anon");
|
||||||
|
filterChainDefinitionMap.put("/img/**", "anon");
|
||||||
|
filterChainDefinitionMap.put("/assets/**", "anon");
|
||||||
|
filterChainDefinitionMap.put("/fonts/**", "anon");
|
||||||
|
filterChainDefinitionMap.put("/display/info", "anon");
|
||||||
|
filterChainDefinitionMap.put("/favicon.ico", "anon");
|
||||||
|
filterChainDefinitionMap.put("/display/file/**", "anon");
|
||||||
|
filterChainDefinitionMap.put("/jmeter/download/**", "anon");
|
||||||
|
filterChainDefinitionMap.put("/jmeter/ping", "anon");
|
||||||
|
filterChainDefinitionMap.put("/jmeter/ready/**", "anon");
|
||||||
|
filterChainDefinitionMap.put("/authsource/list/allenable", "anon");
|
||||||
|
filterChainDefinitionMap.put("/sso/callback/**", "anon");
|
||||||
|
filterChainDefinitionMap.put("/license/validate", "anon");
|
||||||
|
|
||||||
|
// for swagger
|
||||||
|
filterChainDefinitionMap.put("/swagger-ui.html", "anon");
|
||||||
|
filterChainDefinitionMap.put("/swagger-ui/**", "anon");
|
||||||
|
filterChainDefinitionMap.put("/v3/api-docs/**", "anon");
|
||||||
|
|
||||||
|
filterChainDefinitionMap.put("/403", "anon");
|
||||||
|
filterChainDefinitionMap.put("/anonymous/**", "anon");
|
||||||
|
|
||||||
|
//分享相关接口
|
||||||
|
|
||||||
|
filterChainDefinitionMap.put("/system/theme", "anon");
|
||||||
|
filterChainDefinitionMap.put("/system/save/baseurl/**", "anon");
|
||||||
|
filterChainDefinitionMap.put("/system/timeout", "anon");
|
||||||
|
filterChainDefinitionMap.put("/file/metadata/info/**", "anon");
|
||||||
|
// consul
|
||||||
|
filterChainDefinitionMap.put("/v1/catalog/**", "anon");
|
||||||
|
filterChainDefinitionMap.put("/v1/agent/**", "anon");
|
||||||
|
filterChainDefinitionMap.put("/v1/health/**", "anon");
|
||||||
|
//mock接口
|
||||||
|
filterChainDefinitionMap.put("/mock/**", "anon");
|
||||||
|
filterChainDefinitionMap.put("/ws/**", "anon");
|
||||||
|
//
|
||||||
|
filterChainDefinitionMap.put("/performance/update/cache", "anon");
|
||||||
|
// websocket
|
||||||
|
filterChainDefinitionMap.put("/websocket/**", "csrf");
|
||||||
|
|
||||||
|
return filterChainDefinitionMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, String> ignoreCsrfFilter() {
|
||||||
|
Map<String, String> filterChainDefinitionMap = new HashMap<>();
|
||||||
|
filterChainDefinitionMap.put("/", "apikey, authc"); // 跳转到 / 不用校验 csrf
|
||||||
|
filterChainDefinitionMap.put("/language", "apikey, authc");// 跳转到 /language 不用校验 csrf
|
||||||
|
filterChainDefinitionMap.put("/mock", "apikey, authc"); // 跳转到 /mock接口 不用校验 csrf
|
||||||
|
return filterChainDefinitionMap;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package io.metersphere.sdk.util;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
public class RsaKey implements Serializable {
|
||||||
|
|
||||||
|
//公钥
|
||||||
|
private String publicKey;
|
||||||
|
|
||||||
|
//私钥
|
||||||
|
private String privateKey;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,236 @@
|
||||||
|
package io.metersphere.sdk.util;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
|
||||||
|
public class RsaUtil {
|
||||||
|
|
||||||
|
public static final String CHARSET = StandardCharsets.UTF_8.name();
|
||||||
|
public static final String RSA_ALGORITHM = "RSA";
|
||||||
|
|
||||||
|
private static RsaKey rsaKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建RSA 公钥-私钥
|
||||||
|
*/
|
||||||
|
public static RsaKey getRsaKey() throws NoSuchAlgorithmException {
|
||||||
|
if (rsaKey == null) {
|
||||||
|
rsaKey = createKeys();
|
||||||
|
}
|
||||||
|
return rsaKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setRsaKey(RsaKey rsaKey) throws NoSuchAlgorithmException {
|
||||||
|
// 放到缓存里
|
||||||
|
RsaUtil.rsaKey = rsaKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RsaKey createKeys() throws NoSuchAlgorithmException {
|
||||||
|
return createKeys(1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建RSA 公钥-私钥
|
||||||
|
*/
|
||||||
|
public static RsaKey createKeys(int keySize) throws NoSuchAlgorithmException {
|
||||||
|
//为RSA算法创建一个KeyPairGenerator对象
|
||||||
|
KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM);
|
||||||
|
|
||||||
|
//初始化KeyPairGenerator对象,密钥长度
|
||||||
|
kpg.initialize(keySize);
|
||||||
|
//生成密匙对
|
||||||
|
KeyPair keyPair = kpg.generateKeyPair();
|
||||||
|
|
||||||
|
//得到公钥
|
||||||
|
Key publicKey = keyPair.getPublic();
|
||||||
|
|
||||||
|
String publicKeyStr = new String(Base64.encodeBase64(publicKey.getEncoded()));
|
||||||
|
|
||||||
|
//得到私钥
|
||||||
|
Key privateKey = keyPair.getPrivate();
|
||||||
|
String privateKeyStr = new String(Base64.encodeBase64(privateKey.getEncoded()));
|
||||||
|
|
||||||
|
RsaKey rsaKey = new RsaKey();
|
||||||
|
rsaKey.setPublicKey(publicKeyStr);
|
||||||
|
rsaKey.setPrivateKey(privateKeyStr);
|
||||||
|
|
||||||
|
return rsaKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公钥加密
|
||||||
|
*
|
||||||
|
* @param originalText 原文
|
||||||
|
* @param publicKey 公钥
|
||||||
|
*/
|
||||||
|
public static String publicEncrypt(String originalText, String publicKey) throws NoSuchAlgorithmException {
|
||||||
|
RSAPublicKey rsaPublicKey = getPublicKey(publicKey);
|
||||||
|
return publicEncrypt(originalText, rsaPublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公钥解密
|
||||||
|
*
|
||||||
|
* @param cipherText 密文
|
||||||
|
* @param publicKey 公钥
|
||||||
|
*/
|
||||||
|
public static String publicDecrypt(String cipherText, String publicKey) throws NoSuchAlgorithmException {
|
||||||
|
RSAPublicKey rsaPublicKey = getPublicKey(publicKey);
|
||||||
|
return publicDecrypt(cipherText, rsaPublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私钥加密
|
||||||
|
*
|
||||||
|
* @param originalText 原文
|
||||||
|
* @param privateKey 私钥
|
||||||
|
*/
|
||||||
|
public static String privateEncrypt(String originalText, String privateKey) throws NoSuchAlgorithmException {
|
||||||
|
RSAPrivateKey rsaPrivateKey = getPrivateKey(privateKey);
|
||||||
|
return privateEncrypt(originalText, rsaPrivateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私钥解密
|
||||||
|
*
|
||||||
|
* @param cipherText 密文
|
||||||
|
* @param privateKey 私钥
|
||||||
|
*/
|
||||||
|
public static String privateDecrypt(String cipherText, String privateKey) throws NoSuchAlgorithmException {
|
||||||
|
RSAPrivateKey rsaPrivateKey = getPrivateKey(privateKey);
|
||||||
|
return privateDecrypt(cipherText, rsaPrivateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 得到公钥
|
||||||
|
*
|
||||||
|
* @param publicKey 密钥字符串(经过base64编码)
|
||||||
|
*/
|
||||||
|
private static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException {
|
||||||
|
//通过X509编码的Key指令获得公钥对象
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
|
||||||
|
|
||||||
|
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
|
||||||
|
RSAPublicKey key = null;
|
||||||
|
try {
|
||||||
|
key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
|
||||||
|
} catch (InvalidKeySpecException e) {
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公钥加密
|
||||||
|
*
|
||||||
|
* @param originalText 原文
|
||||||
|
* @param publicKey 公钥
|
||||||
|
*/
|
||||||
|
private static String publicEncrypt(String originalText, RSAPublicKey publicKey) {
|
||||||
|
try {
|
||||||
|
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||||
|
return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, originalText.getBytes(CHARSET), publicKey.getModulus().bitLength()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("加密字符串[" + originalText + "]时遇到异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 得到私钥
|
||||||
|
*
|
||||||
|
* @param privateKey 密钥字符串(经过base64编码)
|
||||||
|
*/
|
||||||
|
private static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException {
|
||||||
|
//通过PKCS#8编码的Key指令获得私钥对象
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
|
||||||
|
|
||||||
|
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
|
||||||
|
RSAPrivateKey key = null;
|
||||||
|
try {
|
||||||
|
key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
|
||||||
|
} catch (InvalidKeySpecException e) {
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私钥解密
|
||||||
|
*
|
||||||
|
* @param cipherText 密文
|
||||||
|
* @param privateKey 私钥
|
||||||
|
*/
|
||||||
|
|
||||||
|
private static String privateDecrypt(String cipherText, RSAPrivateKey privateKey) {
|
||||||
|
try {
|
||||||
|
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||||
|
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(cipherText), privateKey.getModulus().bitLength()), CHARSET);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("解密字符串[" + cipherText + "]时遇到异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String privateEncrypt(String originalText, RSAPrivateKey privateKey) {
|
||||||
|
try {
|
||||||
|
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
|
||||||
|
return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, originalText.getBytes(CHARSET), privateKey.getModulus().bitLength()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("加密字符串[" + originalText + "]时遇到异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static String publicDecrypt(String cipherText, RSAPublicKey publicKey) {
|
||||||
|
try {
|
||||||
|
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, publicKey);
|
||||||
|
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(cipherText), publicKey.getModulus().bitLength()), CHARSET);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("解密字符串[" + cipherText + "]时遇到异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize) {
|
||||||
|
int maxBlock;
|
||||||
|
if (opmode == Cipher.DECRYPT_MODE) {
|
||||||
|
maxBlock = keySize / 8;
|
||||||
|
} else {
|
||||||
|
maxBlock = keySize / 8 - 11;
|
||||||
|
}
|
||||||
|
int offSet = 0;
|
||||||
|
byte[] buff;
|
||||||
|
int i = 0;
|
||||||
|
try (
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream()
|
||||||
|
) {
|
||||||
|
while (datas.length > offSet) {
|
||||||
|
if (datas.length - offSet > maxBlock) {
|
||||||
|
buff = cipher.doFinal(datas, offSet, maxBlock);
|
||||||
|
} else {
|
||||||
|
buff = cipher.doFinal(datas, offSet, datas.length - offSet);
|
||||||
|
}
|
||||||
|
out.write(buff, 0, buff.length);
|
||||||
|
i++;
|
||||||
|
offSet = i * maxBlock;
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,202 @@
|
||||||
|
package io.metersphere.sdk.util;
|
||||||
|
|
||||||
|
import io.metersphere.sdk.constants.UserRoleConstants;
|
||||||
|
import io.metersphere.sdk.dto.SessionUser;
|
||||||
|
import io.metersphere.system.domain.UserRole;
|
||||||
|
import io.metersphere.system.domain.UserRolePermission;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.apache.commons.collections4.MapUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.shiro.SecurityUtils;
|
||||||
|
import org.apache.shiro.session.Session;
|
||||||
|
import org.apache.shiro.subject.Subject;
|
||||||
|
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||||
|
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static io.metersphere.sdk.constants.SessionConstants.ATTR_USER;
|
||||||
|
|
||||||
|
public class SessionUtils {
|
||||||
|
|
||||||
|
private static final ThreadLocal<String> projectId = new ThreadLocal<>();
|
||||||
|
private static final ThreadLocal<String> workspaceId = new ThreadLocal<>();
|
||||||
|
|
||||||
|
public static String getUserId() {
|
||||||
|
SessionUser user = getUser();
|
||||||
|
return user == null ? null : user.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SessionUser getUser() {
|
||||||
|
try {
|
||||||
|
Subject subject = SecurityUtils.getSubject();
|
||||||
|
Session session = subject.getSession();
|
||||||
|
return (SessionUser) session.getAttribute(ATTR_USER);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.warn("后台获取在线用户失败: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getSessionId() {
|
||||||
|
return (String) SecurityUtils.getSubject().getSession().getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 踢除用户
|
||||||
|
*
|
||||||
|
* @param username
|
||||||
|
*/
|
||||||
|
public static void kickOutUser(String username) {
|
||||||
|
// redis session
|
||||||
|
RedisIndexedSessionRepository sessionRepository = CommonBeanFactory.getBean(RedisIndexedSessionRepository.class);
|
||||||
|
if (sessionRepository == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Map<String, ?> users = sessionRepository.findByPrincipalName(username);
|
||||||
|
if (MapUtils.isNotEmpty(users)) {
|
||||||
|
users.keySet().forEach(k -> {
|
||||||
|
sessionRepository.deleteById(k);
|
||||||
|
sessionRepository.getSessionRedisOperations().delete("spring:session:sessions:" + k);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
public static void putUser(SessionUser sessionUser) {
|
||||||
|
SecurityUtils.getSubject().getSession().setAttribute(ATTR_USER, sessionUser);
|
||||||
|
SecurityUtils.getSubject().getSession().setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, sessionUser.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限验证时从 controller 参数列表中找到 workspaceId 传入
|
||||||
|
*/
|
||||||
|
public static void setCurrentWorkspaceId(String workspaceId) {
|
||||||
|
SessionUtils.workspaceId.set(workspaceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限验证时从 controller 参数列表中找到 projectId 传入
|
||||||
|
*/
|
||||||
|
public static void setCurrentProjectId(String projectId) {
|
||||||
|
SessionUtils.projectId.set(projectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getCurrentWorkspaceId() {
|
||||||
|
if (StringUtils.isNotEmpty(workspaceId.get())) {
|
||||||
|
return workspaceId.get();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
|
||||||
|
LogUtils.debug("WORKSPACE: {}", request.getHeader("WORKSPACE"));
|
||||||
|
if (request.getHeader("WORKSPACE") != null) {
|
||||||
|
return request.getHeader("WORKSPACE");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return getUser().getLastWorkspaceId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getCurrentProjectId() {
|
||||||
|
if (StringUtils.isNotEmpty(projectId.get())) {
|
||||||
|
return projectId.get();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
|
||||||
|
LogUtils.debug("PROJECT: {}", request.getHeader("PROJECT"));
|
||||||
|
if (request.getHeader("PROJECT") != null) {
|
||||||
|
return request.getHeader("PROJECT");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return getUser().getLastProjectId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getHttpHeader(String headerName) {
|
||||||
|
if (StringUtils.isBlank(headerName)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
|
||||||
|
return request.getHeader(headerName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasPermission(String workspaceId, String projectId, String permission) {
|
||||||
|
Map<String, List<UserRolePermission>> userRolePermissions = new HashMap<>();
|
||||||
|
Map<String, UserRole> group = new HashMap<>();
|
||||||
|
SessionUser user = Objects.requireNonNull(SessionUtils.getUser());
|
||||||
|
user.getUserRoleRelations().forEach(ug -> user.getUserRolePermissions().forEach(gp -> {
|
||||||
|
if (StringUtils.equals(gp.getUserRole().getId(), ug.getRoleId())) {
|
||||||
|
userRolePermissions.put(ug.getId(), gp.getUserRolePermissions());
|
||||||
|
group.put(ug.getId(), gp.getUserRole());
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
long count = user.getUserRoles()
|
||||||
|
.stream()
|
||||||
|
.filter(g -> StringUtils.equals(g.getId(), UserRoleConstants.SUPER_ROLE))
|
||||||
|
.count();
|
||||||
|
|
||||||
|
if (count > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Set<String> currentProjectPermissions = getCurrentProjectPermissions(userRolePermissions, projectId, group, user);
|
||||||
|
if (currentProjectPermissions.contains(permission)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> currentWorkspacePermissions = getCurrentWorkspacePermissions(userRolePermissions, workspaceId, group, user);
|
||||||
|
if (currentWorkspacePermissions.contains(permission)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> systemPermissions = getSystemPermissions(userRolePermissions, group, user);
|
||||||
|
return systemPermissions.contains(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<String> getSystemPermissions(Map<String, List<UserRolePermission>> userRolePermissions, Map<String, UserRole> group, SessionUser user) {
|
||||||
|
return user.getUserRoleRelations().stream()
|
||||||
|
.filter(ug -> group.get(ug.getId()) != null && StringUtils.equals(group.get(ug.getId()).getType(), "SYSTEM"))
|
||||||
|
.filter(ug -> StringUtils.equals(ug.getSourceId(), "system") || StringUtils.equals(ug.getSourceId(), "'adminSourceId'"))
|
||||||
|
.flatMap(ug -> userRolePermissions.get(ug.getId()).stream())
|
||||||
|
.map(UserRolePermission::getPermissionId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<String> getCurrentWorkspacePermissions(Map<String, List<UserRolePermission>> userRolePermissions, String workspaceId, Map<String, UserRole> group, SessionUser user) {
|
||||||
|
return user.getUserRoleRelations().stream()
|
||||||
|
.filter(ug -> group.get(ug.getId()) != null && StringUtils.equals(group.get(ug.getId()).getType(), "WORKSPACE"))
|
||||||
|
.filter(ug -> StringUtils.equals(ug.getSourceId(), workspaceId))
|
||||||
|
.flatMap(ug -> userRolePermissions.get(ug.getId()).stream())
|
||||||
|
.map(UserRolePermission::getPermissionId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<String> getCurrentProjectPermissions(Map<String, List<UserRolePermission>> userRolePermissions, String projectId, Map<String, UserRole> group, SessionUser user) {
|
||||||
|
return user.getUserRoleRelations().stream()
|
||||||
|
.filter(ug -> group.get(ug.getId()) != null && StringUtils.equals(group.get(ug.getId()).getType(), "PROJECT"))
|
||||||
|
.filter(ug -> StringUtils.equals(ug.getSourceId(), projectId))
|
||||||
|
.flatMap(ug -> userRolePermissions.get(ug.getId()).stream())
|
||||||
|
.map(UserRolePermission::getPermissionId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clearCurrentWorkspaceId() {
|
||||||
|
workspaceId.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clearCurrentProjectId() {
|
||||||
|
projectId.remove();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
package io.metersphere.api.controller;
|
package io.metersphere.api.controller;
|
||||||
|
|
||||||
|
import com.jayway.jsonpath.JsonPath;
|
||||||
import io.metersphere.api.domain.ApiDefinition;
|
import io.metersphere.api.domain.ApiDefinition;
|
||||||
import io.metersphere.api.dto.ApiDefinitionDTO;
|
import io.metersphere.api.dto.ApiDefinitionDTO;
|
||||||
import io.metersphere.api.dto.ApiDefinitionListRequest;
|
import io.metersphere.api.dto.ApiDefinitionListRequest;
|
||||||
|
import io.metersphere.sdk.constants.SessionConstants;
|
||||||
import io.metersphere.sdk.controller.handler.ResultHolder;
|
import io.metersphere.sdk.controller.handler.ResultHolder;
|
||||||
import io.metersphere.sdk.util.JSON;
|
import io.metersphere.sdk.util.JSON;
|
||||||
import io.metersphere.sdk.util.LogUtils;
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
|
@ -25,7 +27,7 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||||
|
|
||||||
|
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
|
@ -35,12 +37,29 @@ public class ApiDefinitionControllerTests {
|
||||||
@Resource
|
@Resource
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
private final static String prefix = "/api/definition";
|
private final static String prefix = "/api/definition";
|
||||||
|
private static String sessionId;
|
||||||
|
private static String csrfToken;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() {
|
public void init() {
|
||||||
LogUtils.info("init base api test");
|
LogUtils.info("init base api test");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(0)
|
||||||
|
public void login() throws Exception {
|
||||||
|
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/signin")
|
||||||
|
.content("{\"username\":\"admin\",\"password\":\"metersphere\"}")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andReturn();
|
||||||
|
sessionId = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.sessionId");
|
||||||
|
csrfToken = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.csrfToken");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1)
|
@Order(1)
|
||||||
public void testCreate() throws Exception {
|
public void testCreate() throws Exception {
|
||||||
|
@ -71,9 +90,11 @@ public class ApiDefinitionControllerTests {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.multipart(prefix + "/add")
|
mockMvc.perform(MockMvcRequestBuilders.multipart(prefix + "/add")
|
||||||
.file(file)
|
.file(file)
|
||||||
.contentType(MediaType.APPLICATION_JSON_VALUE)
|
.contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JsonUtils.toJSONString(request)))
|
.content(JsonUtils.toJSONString(request)))
|
||||||
.andExpect(MockMvcResultMatchers.status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON))
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(jsonPath("$.data.id").value("test-api-id"));
|
.andExpect(jsonPath("$.data.id").value("test-api-id"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,8 +114,10 @@ public class ApiDefinitionControllerTests {
|
||||||
|
|
||||||
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.multipart(prefix + "/page")
|
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.multipart(prefix + "/page")
|
||||||
.contentType(MediaType.APPLICATION_JSON_VALUE)
|
.contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||||
.content(JsonUtils.toJSONString(request)))
|
.content(JsonUtils.toJSONString(request))
|
||||||
.andExpect(MockMvcResultMatchers.status().isOk())
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken))
|
||||||
|
.andExpect(status().isOk())
|
||||||
.andReturn();
|
.andReturn();
|
||||||
|
|
||||||
MockHttpServletResponse mockResponse = mvcResult.getResponse();
|
MockHttpServletResponse mockResponse = mvcResult.getResponse();
|
||||||
|
@ -125,8 +148,10 @@ public class ApiDefinitionControllerTests {
|
||||||
request.setPageSize(20);
|
request.setPageSize(20);
|
||||||
mockMvc.perform(MockMvcRequestBuilders.multipart(prefix + "/page")
|
mockMvc.perform(MockMvcRequestBuilders.multipart(prefix + "/page")
|
||||||
.contentType(MediaType.APPLICATION_JSON_VALUE)
|
.contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||||
.content(JsonUtils.toJSONString(request)))
|
.content(JsonUtils.toJSONString(request))
|
||||||
.andExpect(MockMvcResultMatchers.status().isBadRequest());
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken))
|
||||||
|
.andExpect(status().isBadRequest());
|
||||||
|
|
||||||
//pageSize为空
|
//pageSize为空
|
||||||
request = new ApiDefinitionListRequest();
|
request = new ApiDefinitionListRequest();
|
||||||
|
@ -134,8 +159,10 @@ public class ApiDefinitionControllerTests {
|
||||||
request.setProjectId("test-project-id");
|
request.setProjectId("test-project-id");
|
||||||
mockMvc.perform(MockMvcRequestBuilders.multipart(prefix + "/page")
|
mockMvc.perform(MockMvcRequestBuilders.multipart(prefix + "/page")
|
||||||
.contentType(MediaType.APPLICATION_JSON_VALUE)
|
.contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||||
.content(JsonUtils.toJSONString(request)))
|
.content(JsonUtils.toJSONString(request))
|
||||||
.andExpect(MockMvcResultMatchers.status().isBadRequest());
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken))
|
||||||
|
.andExpect(status().isBadRequest());
|
||||||
|
|
||||||
//current为空
|
//current为空
|
||||||
request = new ApiDefinitionListRequest();
|
request = new ApiDefinitionListRequest();
|
||||||
|
@ -143,8 +170,10 @@ public class ApiDefinitionControllerTests {
|
||||||
request.setProjectId("test-project-id");
|
request.setProjectId("test-project-id");
|
||||||
mockMvc.perform(MockMvcRequestBuilders.multipart(prefix + "/page")
|
mockMvc.perform(MockMvcRequestBuilders.multipart(prefix + "/page")
|
||||||
.contentType(MediaType.APPLICATION_JSON_VALUE)
|
.contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||||
.content(JsonUtils.toJSONString(request)))
|
.content(JsonUtils.toJSONString(request))
|
||||||
.andExpect(MockMvcResultMatchers.status().isBadRequest());
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken))
|
||||||
|
.andExpect(status().isBadRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
package io.metersphere.functional.controller;
|
package io.metersphere.functional.controller;
|
||||||
|
|
||||||
|
import com.jayway.jsonpath.JsonPath;
|
||||||
import io.metersphere.functional.domain.CaseReview;
|
import io.metersphere.functional.domain.CaseReview;
|
||||||
|
import io.metersphere.sdk.constants.SessionConstants;
|
||||||
import io.metersphere.sdk.util.JSON;
|
import io.metersphere.sdk.util.JSON;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.junit.jupiter.api.MethodOrderer;
|
import org.junit.jupiter.api.MethodOrderer;
|
||||||
|
@ -11,6 +13,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
|
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
|
||||||
|
@ -25,6 +28,23 @@ public class CaseReviewControllerTests {
|
||||||
@Resource
|
@Resource
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
|
private static String sessionId;
|
||||||
|
private static String csrfToken;
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(0)
|
||||||
|
public void login() throws Exception {
|
||||||
|
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/signin")
|
||||||
|
.content("{\"username\":\"admin\",\"password\":\"metersphere\"}")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andReturn();
|
||||||
|
sessionId = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.sessionId");
|
||||||
|
csrfToken = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.csrfToken");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1)
|
@Order(1)
|
||||||
public void testAddCaseReview() throws Exception {
|
public void testAddCaseReview() throws Exception {
|
||||||
|
@ -40,6 +60,8 @@ public class CaseReviewControllerTests {
|
||||||
caseReview.setReviewPassRule("SINGLE");
|
caseReview.setReviewPassRule("SINGLE");
|
||||||
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/case/review/add")
|
mockMvc.perform(MockMvcRequestBuilders.post("/case/review/add")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(caseReview))
|
.content(JSON.toJSONString(caseReview))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
|
@ -53,6 +75,8 @@ public class CaseReviewControllerTests {
|
||||||
caseReview.setName("test");
|
caseReview.setName("test");
|
||||||
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/case/review/add")
|
mockMvc.perform(MockMvcRequestBuilders.post("/case/review/add")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(caseReview))
|
.content(JSON.toJSONString(caseReview))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isBadRequest())
|
.andExpect(status().isBadRequest())
|
||||||
|
@ -65,6 +89,8 @@ public class CaseReviewControllerTests {
|
||||||
CaseReview caseReview = new CaseReview();
|
CaseReview caseReview = new CaseReview();
|
||||||
caseReview.setId("case-review-id");
|
caseReview.setId("case-review-id");
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/case/review/delete")
|
mockMvc.perform(MockMvcRequestBuilders.post("/case/review/delete")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(caseReview))
|
.content(JSON.toJSONString(caseReview))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isBadRequest())
|
.andExpect(status().isBadRequest())
|
||||||
|
@ -74,7 +100,9 @@ public class CaseReviewControllerTests {
|
||||||
@Test
|
@Test
|
||||||
@Order(4)
|
@Order(4)
|
||||||
public void testGetCaseReview() throws Exception {
|
public void testGetCaseReview() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/case/review/get/case-review-id"))
|
mockMvc.perform(MockMvcRequestBuilders.get("/case/review/get/case-review-id")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken))
|
||||||
.andExpect(status().isOk());
|
.andExpect(status().isOk());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
package io.metersphere.issue.controller;
|
package io.metersphere.issue.controller;
|
||||||
|
|
||||||
|
import com.jayway.jsonpath.JsonPath;
|
||||||
import io.metersphere.issue.domain.Issue;
|
import io.metersphere.issue.domain.Issue;
|
||||||
|
import io.metersphere.sdk.constants.SessionConstants;
|
||||||
import io.metersphere.sdk.util.JSON;
|
import io.metersphere.sdk.util.JSON;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import org.junit.jupiter.api.MethodOrderer;
|
||||||
|
import org.junit.jupiter.api.Order;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.TestMethodOrder;
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
@ -18,11 +24,29 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
|
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
@AutoConfigureMockMvc
|
@AutoConfigureMockMvc
|
||||||
class IssueControllerTest {
|
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||||
|
public class IssueControllerTest {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
|
private static String sessionId;
|
||||||
|
private static String csrfToken;
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(0)
|
||||||
|
public void login() throws Exception {
|
||||||
|
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/signin")
|
||||||
|
.content("{\"username\":\"admin\",\"password\":\"metersphere\"}")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andReturn();
|
||||||
|
sessionId = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.sessionId");
|
||||||
|
csrfToken = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.csrfToken");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void listAll() {
|
void listAll() {
|
||||||
}
|
}
|
||||||
|
@ -45,6 +69,8 @@ class IssueControllerTest {
|
||||||
|
|
||||||
mockMvc.perform(
|
mockMvc.perform(
|
||||||
MockMvcRequestBuilders.post("/issue/add")
|
MockMvcRequestBuilders.post("/issue/add")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(issue))
|
.content(JSON.toJSONString(issue))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
// 检查状态
|
// 检查状态
|
||||||
|
@ -58,6 +84,8 @@ class IssueControllerTest {
|
||||||
// 缺陷已存在校验
|
// 缺陷已存在校验
|
||||||
mockMvc.perform(
|
mockMvc.perform(
|
||||||
MockMvcRequestBuilders.post("/issue/add")
|
MockMvcRequestBuilders.post("/issue/add")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(issue))
|
.content(JSON.toJSONString(issue))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
// 检查失败状态码
|
// 检查失败状态码
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package io.metersphere.project.controller;
|
package io.metersphere.project.controller;
|
||||||
|
|
||||||
|
import com.jayway.jsonpath.JsonPath;
|
||||||
import io.metersphere.project.domain.ProjectApplication;
|
import io.metersphere.project.domain.ProjectApplication;
|
||||||
|
import io.metersphere.sdk.constants.SessionConstants;
|
||||||
import io.metersphere.sdk.util.JSON;
|
import io.metersphere.sdk.util.JSON;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.junit.jupiter.api.MethodOrderer;
|
import org.junit.jupiter.api.MethodOrderer;
|
||||||
|
@ -11,9 +13,9 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
|
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,6 +25,22 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
public class ProjectApplicationControllerTests {
|
public class ProjectApplicationControllerTests {
|
||||||
@Resource
|
@Resource
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
private static String sessionId;
|
||||||
|
private static String csrfToken;
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(0)
|
||||||
|
public void login() throws Exception {
|
||||||
|
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/signin")
|
||||||
|
.content("{\"username\":\"admin\",\"password\":\"metersphere\"}")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andReturn();
|
||||||
|
sessionId = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.sessionId");
|
||||||
|
csrfToken = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.csrfToken");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1)
|
@Order(1)
|
||||||
|
@ -32,6 +50,8 @@ public class ProjectApplicationControllerTests {
|
||||||
projectApplication.setType("1");
|
projectApplication.setType("1");
|
||||||
projectApplication.setTypeValue("1");
|
projectApplication.setTypeValue("1");
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/project/application/save")
|
mockMvc.perform(MockMvcRequestBuilders.post("/project/application/save")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(projectApplication))
|
.content(JSON.toJSONString(projectApplication))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
|
@ -47,6 +67,8 @@ public class ProjectApplicationControllerTests {
|
||||||
projectApplication.setType("1");
|
projectApplication.setType("1");
|
||||||
projectApplication.setTypeValue("2");
|
projectApplication.setTypeValue("2");
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/project/application/update")
|
mockMvc.perform(MockMvcRequestBuilders.post("/project/application/update")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(projectApplication))
|
.content(JSON.toJSONString(projectApplication))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
|
@ -57,7 +79,9 @@ public class ProjectApplicationControllerTests {
|
||||||
@Test
|
@Test
|
||||||
@Order(3)
|
@Order(3)
|
||||||
public void testListApp() throws Exception {
|
public void testListApp() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/project/application/list/1"))
|
mockMvc.perform(MockMvcRequestBuilders.get("/project/application/list/1")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(jsonPath("$.data[0].typeValue").value("2"));
|
.andExpect(jsonPath("$.data[0].typeValue").value("2"));
|
||||||
|
|
|
@ -2,6 +2,7 @@ package io.metersphere.project.controller;
|
||||||
|
|
||||||
import com.jayway.jsonpath.JsonPath;
|
import com.jayway.jsonpath.JsonPath;
|
||||||
import io.metersphere.project.domain.Project;
|
import io.metersphere.project.domain.Project;
|
||||||
|
import io.metersphere.sdk.constants.SessionConstants;
|
||||||
import io.metersphere.sdk.util.JSON;
|
import io.metersphere.sdk.util.JSON;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.junit.jupiter.api.MethodOrderer;
|
import org.junit.jupiter.api.MethodOrderer;
|
||||||
|
@ -12,6 +13,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
|
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
|
||||||
|
@ -27,6 +29,22 @@ public class ProjectControllerTests {
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
private static String projectId;
|
private static String projectId;
|
||||||
|
private static String sessionId;
|
||||||
|
private static String csrfToken;
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(0)
|
||||||
|
public void login() throws Exception {
|
||||||
|
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/signin")
|
||||||
|
.content("{\"username\":\"admin\",\"password\":\"metersphere\"}")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andReturn();
|
||||||
|
sessionId = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.sessionId");
|
||||||
|
csrfToken = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.csrfToken");
|
||||||
|
}
|
||||||
|
|
||||||
// 添加项目
|
// 添加项目
|
||||||
@Test
|
@Test
|
||||||
|
@ -38,6 +56,8 @@ public class ProjectControllerTests {
|
||||||
project.setWorkspaceId("default");
|
project.setWorkspaceId("default");
|
||||||
|
|
||||||
var result = mockMvc.perform(MockMvcRequestBuilders.post("/project/add")
|
var result = mockMvc.perform(MockMvcRequestBuilders.post("/project/add")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(project))
|
.content(JSON.toJSONString(project))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
|
@ -56,6 +76,8 @@ public class ProjectControllerTests {
|
||||||
project.setWorkspaceId("default");
|
project.setWorkspaceId("default");
|
||||||
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/project/edit")
|
mockMvc.perform(MockMvcRequestBuilders.post("/project/edit")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(project))
|
.content(JSON.toJSONString(project))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
|
@ -66,7 +88,9 @@ public class ProjectControllerTests {
|
||||||
@Test
|
@Test
|
||||||
@Order(3)
|
@Order(3)
|
||||||
public void testSelectAll() throws Exception {
|
public void testSelectAll() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/project/list-all"))
|
mockMvc.perform(MockMvcRequestBuilders.get("/project/list-all")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||||
// .andExpect(jsonPath("$.person.name").value("Jason"))
|
// .andExpect(jsonPath("$.person.name").value("Jason"))
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package io.metersphere.system.controller;
|
package io.metersphere.system.controller;
|
||||||
|
|
||||||
|
import com.jayway.jsonpath.JsonPath;
|
||||||
|
import io.metersphere.sdk.constants.SessionConstants;
|
||||||
import io.metersphere.sdk.dto.UserDTO;
|
import io.metersphere.sdk.dto.UserDTO;
|
||||||
import io.metersphere.sdk.util.JSON;
|
import io.metersphere.sdk.util.JSON;
|
||||||
import io.metersphere.system.domain.User;
|
import io.metersphere.system.domain.User;
|
||||||
|
@ -12,6 +14,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -25,13 +28,28 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
public class UserControllerTests {
|
public class UserControllerTests {
|
||||||
@Resource
|
@Resource
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
private static String sessionId;
|
||||||
|
private static String csrfToken;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(0)
|
||||||
|
public void login() throws Exception {
|
||||||
|
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/signin")
|
||||||
|
.content("{\"username\":\"admin\",\"password\":\"metersphere\"}")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andReturn();
|
||||||
|
sessionId = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.sessionId");
|
||||||
|
csrfToken = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.csrfToken");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1)
|
@Order(1)
|
||||||
public void testAddUser() throws Exception {
|
public void testAddUser() throws Exception {
|
||||||
UserDTO user = new UserDTO();
|
UserDTO user = new UserDTO();
|
||||||
user.setId("admin");
|
user.setId("user");
|
||||||
user.setName("admin");
|
user.setName("user");
|
||||||
user.setSource("LOCAL");
|
user.setSource("LOCAL");
|
||||||
user.setEmail("bin@fit2cloud.com");
|
user.setEmail("bin@fit2cloud.com");
|
||||||
user.setStatus("enabled");
|
user.setStatus("enabled");
|
||||||
|
@ -39,6 +57,8 @@ public class UserControllerTests {
|
||||||
user.setSeleniumServer("http://localhost:4444");
|
user.setSeleniumServer("http://localhost:4444");
|
||||||
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/user/add")
|
mockMvc.perform(MockMvcRequestBuilders.post("/user/add")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(user))
|
.content(JSON.toJSONString(user))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
|
@ -49,11 +69,13 @@ public class UserControllerTests {
|
||||||
@Order(2)
|
@Order(2)
|
||||||
public void testAddUserFailed() throws Exception {
|
public void testAddUserFailed() throws Exception {
|
||||||
UserDTO user = new UserDTO();
|
UserDTO user = new UserDTO();
|
||||||
user.setId("admin2");
|
user.setId("user2");
|
||||||
|
|
||||||
user.setSeleniumServer("http://localhost:4444");
|
user.setSeleniumServer("http://localhost:4444");
|
||||||
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/user/add")
|
mockMvc.perform(MockMvcRequestBuilders.post("/user/add")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(user))
|
.content(JSON.toJSONString(user))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isBadRequest())
|
.andExpect(status().isBadRequest())
|
||||||
|
@ -63,10 +85,12 @@ public class UserControllerTests {
|
||||||
@Test
|
@Test
|
||||||
@Order(3)
|
@Order(3)
|
||||||
public void testGetUser() throws Exception {
|
public void testGetUser() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/user/get/admin"))
|
mockMvc.perform(MockMvcRequestBuilders.get("/user/get/user")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(jsonPath("$.data.id").value("admin"));
|
.andExpect(jsonPath("$.data.id").value("user"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -74,11 +98,13 @@ public class UserControllerTests {
|
||||||
@Order(4)
|
@Order(4)
|
||||||
public void testUpdateUser() throws Exception {
|
public void testUpdateUser() throws Exception {
|
||||||
UserDTO user = new UserDTO();
|
UserDTO user = new UserDTO();
|
||||||
user.setId("admin");
|
user.setId("user");
|
||||||
user.setName("Administrator");
|
user.setName("useristrator");
|
||||||
|
|
||||||
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/user/update")
|
mockMvc.perform(MockMvcRequestBuilders.post("/user/update")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(user))
|
.content(JSON.toJSONString(user))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
|
@ -89,10 +115,12 @@ public class UserControllerTests {
|
||||||
@Order(5)
|
@Order(5)
|
||||||
public void testDeleteUser() throws Exception {
|
public void testDeleteUser() throws Exception {
|
||||||
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/user/delete/admin"))
|
mockMvc.perform(MockMvcRequestBuilders.get("/user/delete/user")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(jsonPath("$.data.id").value("admin"));
|
.andExpect(jsonPath("$.data.id").value("user"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -111,15 +139,19 @@ public class UserControllerTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/user/batch-add3")
|
mockMvc.perform(MockMvcRequestBuilders.post("/user/batch-add3")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(users))
|
.content(JSON.toJSONString(users))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
|
||||||
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/user/count")
|
// mockMvc.perform(MockMvcRequestBuilders.get("/user/count")
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
// .header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
.andExpect(status().isOk())
|
// .header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
// .contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(jsonPath("$.data").value(size));
|
// .andExpect(status().isOk())
|
||||||
|
// .andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||||
|
// .andExpect(jsonPath("$.data").value(size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package io.metersphere.plan.controller;
|
package io.metersphere.plan.controller;
|
||||||
|
|
||||||
|
import com.jayway.jsonpath.JsonPath;
|
||||||
import io.metersphere.plan.dto.TestPlanApiCaseDTO;
|
import io.metersphere.plan.dto.TestPlanApiCaseDTO;
|
||||||
|
import io.metersphere.sdk.constants.SessionConstants;
|
||||||
import io.metersphere.sdk.util.JSON;
|
import io.metersphere.sdk.util.JSON;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.junit.jupiter.api.MethodOrderer;
|
import org.junit.jupiter.api.MethodOrderer;
|
||||||
|
@ -13,12 +15,14 @@ import org.springframework.http.MediaType;
|
||||||
import org.springframework.test.context.jdbc.Sql;
|
import org.springframework.test.context.jdbc.Sql;
|
||||||
import org.springframework.test.context.jdbc.SqlConfig;
|
import org.springframework.test.context.jdbc.SqlConfig;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
|
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
|
||||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
|
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
|
@ -35,6 +39,22 @@ public class TestPlanApiCaseControllerTests {
|
||||||
|
|
||||||
public static final String PARAM_TEST_ID = "test-plan-api-case-id";
|
public static final String PARAM_TEST_ID = "test-plan-api-case-id";
|
||||||
|
|
||||||
|
private static String sessionId;
|
||||||
|
private static String csrfToken;
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(0)
|
||||||
|
public void login() throws Exception {
|
||||||
|
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/signin")
|
||||||
|
.content("{\"username\":\"admin\",\"password\":\"metersphere\"}")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andReturn();
|
||||||
|
sessionId = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.sessionId");
|
||||||
|
csrfToken = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.csrfToken");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1)
|
@Order(1)
|
||||||
|
@ -45,6 +65,8 @@ public class TestPlanApiCaseControllerTests {
|
||||||
testPlanApiCaseDTO.setApiCaseId(UUID.randomUUID().toString());
|
testPlanApiCaseDTO.setApiCaseId(UUID.randomUUID().toString());
|
||||||
testPlanApiCaseDTO.setPos(10001L);
|
testPlanApiCaseDTO.setPos(10001L);
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post(REQ_PREFIX + "/add")
|
mockMvc.perform(MockMvcRequestBuilders.post(REQ_PREFIX + "/add")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(testPlanApiCaseDTO))
|
.content(JSON.toJSONString(testPlanApiCaseDTO))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
|
@ -54,7 +76,9 @@ public class TestPlanApiCaseControllerTests {
|
||||||
@Test
|
@Test
|
||||||
@Order(2)
|
@Order(2)
|
||||||
public void testGet() throws Exception {
|
public void testGet() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get(REQ_PREFIX + "/get/" + PARAM_TEST_ID))
|
mockMvc.perform(MockMvcRequestBuilders.get(REQ_PREFIX + "/get/" + PARAM_TEST_ID)
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andDo(MockMvcResultHandlers.print());
|
.andDo(MockMvcResultHandlers.print());
|
||||||
}
|
}
|
||||||
|
@ -66,11 +90,15 @@ public class TestPlanApiCaseControllerTests {
|
||||||
testPlanApiCaseDTO.setId(PARAM_TEST_ID);
|
testPlanApiCaseDTO.setId(PARAM_TEST_ID);
|
||||||
testPlanApiCaseDTO.setPos(15001L);
|
testPlanApiCaseDTO.setPos(15001L);
|
||||||
mockMvc.perform(MockMvcRequestBuilders.put(REQ_PREFIX + "/update")
|
mockMvc.perform(MockMvcRequestBuilders.put(REQ_PREFIX + "/update")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(testPlanApiCaseDTO))
|
.content(JSON.toJSONString(testPlanApiCaseDTO))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andDo(MockMvcResultHandlers.print());
|
.andDo(MockMvcResultHandlers.print());
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get(REQ_PREFIX + "/get/" + PARAM_TEST_ID))
|
mockMvc.perform(MockMvcRequestBuilders.get(REQ_PREFIX + "/get/" + PARAM_TEST_ID)
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken))
|
||||||
.andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$.data.pos").value(15001L))
|
.andExpect(status().isOk()).andExpect(MockMvcResultMatchers.jsonPath("$.data.pos").value(15001L))
|
||||||
.andDo(MockMvcResultHandlers.print());
|
.andDo(MockMvcResultHandlers.print());
|
||||||
}
|
}
|
||||||
|
@ -78,7 +106,9 @@ public class TestPlanApiCaseControllerTests {
|
||||||
@Test
|
@Test
|
||||||
@Order(4)
|
@Order(4)
|
||||||
public void testDelete() throws Exception {
|
public void testDelete() throws Exception {
|
||||||
mockMvc.perform(MockMvcRequestBuilders.delete(REQ_PREFIX + "/delete/" + PARAM_TEST_ID))
|
mockMvc.perform(MockMvcRequestBuilders.delete(REQ_PREFIX + "/delete/" + PARAM_TEST_ID)
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andDo(MockMvcResultHandlers.print());
|
.andDo(MockMvcResultHandlers.print());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package io.metersphere.plan.controller;
|
package io.metersphere.plan.controller;
|
||||||
|
|
||||||
|
import com.jayway.jsonpath.JsonPath;
|
||||||
import io.metersphere.plan.domain.TestPlan;
|
import io.metersphere.plan.domain.TestPlan;
|
||||||
import io.metersphere.plan.dto.TestPlanDTO;
|
import io.metersphere.plan.dto.TestPlanDTO;
|
||||||
|
import io.metersphere.sdk.constants.SessionConstants;
|
||||||
import io.metersphere.sdk.util.JSON;
|
import io.metersphere.sdk.util.JSON;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.junit.jupiter.api.MethodOrderer;
|
import org.junit.jupiter.api.MethodOrderer;
|
||||||
|
@ -12,6 +14,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -29,6 +32,10 @@ public class TestPlanControllerTests {
|
||||||
@Resource
|
@Resource
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
|
private static String sessionId;
|
||||||
|
private static String csrfToken;
|
||||||
|
|
||||||
|
|
||||||
private TestPlanDTO getSimpleTestPlan() {
|
private TestPlanDTO getSimpleTestPlan() {
|
||||||
TestPlanDTO testPlan = new TestPlanDTO();
|
TestPlanDTO testPlan = new TestPlanDTO();
|
||||||
testPlan.setId("test");
|
testPlan.setId("test");
|
||||||
|
@ -42,6 +49,20 @@ public class TestPlanControllerTests {
|
||||||
return testPlan;
|
return testPlan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(0)
|
||||||
|
public void login() throws Exception {
|
||||||
|
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/signin")
|
||||||
|
.content("{\"username\":\"admin\",\"password\":\"metersphere\"}")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andReturn();
|
||||||
|
sessionId = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.sessionId");
|
||||||
|
csrfToken = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.csrfToken");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1)
|
@Order(1)
|
||||||
public void testAdd1() throws Exception {
|
public void testAdd1() throws Exception {
|
||||||
|
@ -60,6 +81,8 @@ public class TestPlanControllerTests {
|
||||||
testPlan.setPrincipals(participantList);
|
testPlan.setPrincipals(participantList);
|
||||||
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add")
|
mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(testPlan))
|
.content(JSON.toJSONString(testPlan))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
|
@ -79,6 +102,8 @@ public class TestPlanControllerTests {
|
||||||
testPlan.setFollowers(followerList);
|
testPlan.setFollowers(followerList);
|
||||||
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add")
|
mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(testPlan))
|
.content(JSON.toJSONString(testPlan))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
|
@ -96,6 +121,8 @@ public class TestPlanControllerTests {
|
||||||
testPlan.setPrincipals(participantList);
|
testPlan.setPrincipals(participantList);
|
||||||
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add")
|
mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(testPlan))
|
.content(JSON.toJSONString(testPlan))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
|
@ -108,6 +135,8 @@ public class TestPlanControllerTests {
|
||||||
TestPlanDTO testPlan = this.getSimpleTestPlan();
|
TestPlanDTO testPlan = this.getSimpleTestPlan();
|
||||||
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add")
|
mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(testPlan))
|
.content(JSON.toJSONString(testPlan))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
|
@ -121,6 +150,8 @@ public class TestPlanControllerTests {
|
||||||
testPlan.setName("test");
|
testPlan.setName("test");
|
||||||
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add")
|
mockMvc.perform(MockMvcRequestBuilders.post("/test-plan/add")
|
||||||
|
.header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||||
|
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||||
.content(JSON.toJSONString(testPlan))
|
.content(JSON.toJSONString(testPlan))
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isBadRequest())
|
.andExpect(status().isBadRequest())
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
lombok.addLombokGeneratedAnnotation = true
|
7
pom.xml
7
pom.xml
|
@ -219,13 +219,12 @@
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
<version>${jacoco.version}</version>
|
<version>${jacoco.version}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<includes>
|
|
||||||
<include>io/metersphere/*/controller/*Controller.*</include>
|
|
||||||
</includes>
|
|
||||||
<excludes>
|
<excludes>
|
||||||
<exclude>io/metersphere/*/service/**</exclude>
|
|
||||||
<exclude>io/metersphere/*/mapper/**</exclude>
|
<exclude>io/metersphere/*/mapper/**</exclude>
|
||||||
<exclude>io/metersphere/*/domain/**</exclude>
|
<exclude>io/metersphere/*/domain/**</exclude>
|
||||||
|
<exclude>io/metersphere/**/dto/**</exclude>
|
||||||
|
<exclude>io/metersphere/**/config/**</exclude>
|
||||||
|
<exclude>io/metersphere/**/constants/**</exclude>
|
||||||
</excludes>
|
</excludes>
|
||||||
</configuration>
|
</configuration>
|
||||||
<executions>
|
<executions>
|
||||||
|
|
Loading…
Reference in New Issue