fix: [auth] grant and chekc permission's bug

This commit is contained in:
tjq 2024-02-12 23:42:21 +08:00
parent 919a5c3b35
commit 05c22a5dc5
11 changed files with 185 additions and 93 deletions

View File

@ -18,4 +18,6 @@ public class AuthConstants {
* 前端跳转到指定页面指令
*/
public static final String FE_REDIRECT_KEY = "FE-REDIRECT:";
public static final String TIPS_NO_PERMISSION_TO_SEE = "NO_PERMISSION_TO_SEE";
}

View File

@ -17,7 +17,7 @@ public class HttpServletUtils {
String v = httpServletRequest.getHeader(key);
// 解决 window.localStorage.getItem null 的问题
if (OmsConstant.NULL.equalsIgnoreCase(v)) {
if (OmsConstant.NULL.equalsIgnoreCase(v) || "undefined".equalsIgnoreCase(v)) {
return null;
}

View File

@ -45,17 +45,17 @@ public class PowerJobPermissionServiceImpl implements PowerJobPermissionService
final Role role = Role.of(userRole.getRole());
// 处理全局权限
if (RoleScope.GLOBAL.equals(roleScope)) {
if (RoleScope.GLOBAL.getV() == userRole.getScope()) {
if (Role.ADMIN.equals(role)) {
return true;
}
globalRoles.add(role);
}
if (Objects.equals(userRole.getScope(), RoleScope.NAMESPACE.getV())) {
if (RoleScope.NAMESPACE.getV() == userRole.getScope()) {
namespaceId2Role.put(userRole.getTarget(), role);
}
if (Objects.equals(userRole.getScope(), RoleScope.APP.getV())) {
if (RoleScope.APP.getV() == userRole.getScope()) {
appId2Role.put(userRole.getTarget(), role);
}
}
@ -98,7 +98,7 @@ public class PowerJobPermissionServiceImpl implements PowerJobPermissionService
userRoleDO.setRole(role.getV());
userRoleRepository.saveAndFlush(userRoleDO);
log.info("[PowerJobPermissionService] saveAndFlush userRole successfully: {}", userRoleDO);
log.info("[PowerJobPermissionService] [grantPermission] saveAndFlush userRole successfully: {}", userRoleDO);
}
@Override

View File

@ -22,12 +22,14 @@ public class AppInfoDO {
@GenericGenerator(name = "native", strategy = "native")
private Long id;
/**
* 命名空间ID外键关联
*/
private Long namespaceId;
private String appName;
/**
* 描述
*/
private String title;
/**
* 应用分组密码
*/
@ -40,7 +42,24 @@ public class AppInfoDO {
*/
private String currentServer;
/**
* 命名空间ID外键关联
*/
private Long namespaceId;
/**
* 管理标签
*/
private String tags;
/**
* 扩展字段
*/
private String extra;
private Date gmtCreate;
private Date gmtModified;
private String creator;
private String modifier;
}

View File

@ -1,5 +1,6 @@
package tech.powerjob.server.auth.plugin;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import tech.powerjob.common.response.ResultDTO;
@ -9,12 +10,10 @@ import tech.powerjob.server.auth.PowerJobUser;
import tech.powerjob.server.auth.Role;
import tech.powerjob.server.auth.RoleScope;
import tech.powerjob.server.auth.interceptor.GrantPermissionPlugin;
import tech.powerjob.server.auth.service.permission.PowerJobPermissionService;
import tech.powerjob.server.common.utils.SpringUtils;
import tech.powerjob.server.persistence.remote.model.UserRoleDO;
import tech.powerjob.server.persistence.remote.repository.UserRoleRepository;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;
/**
@ -65,17 +64,12 @@ public abstract class SaveGrantPermissionPlugin implements GrantPermissionPlugin
throw new IllegalArgumentException("[GrantPermission] result success but id not exits, maybe there has some bug, please fix it!!!");
}
UserRoleRepository userRoleRepository = SpringUtils.getBean(UserRoleRepository.class);
UserRoleDO userRoleDO = new UserRoleDO();
PowerJobPermissionService powerJobPermissionService = SpringUtils.getBean(PowerJobPermissionService.class);
userRoleDO.setUserId(powerJobUser.getId());
userRoleDO.setRole(Role.ADMIN.getV());
userRoleDO.setScope(fetchRuleScope().getV());
userRoleDO.setTarget(savedId);
userRoleDO.setGmtCreate(new Date());
userRoleDO.setGmtModified(new Date());
Map<String, Object> extra = Maps.newHashMap();
extra.put("source", "SaveGrantPermissionPlugin");
userRoleRepository.saveAndFlush(userRoleDO);
powerJobPermissionService.grantPermission(fetchRuleScope(), savedId, powerJobUser.getId(), Role.ADMIN, JsonUtils.toJSONString(extra));
}
protected abstract RoleScope fetchRuleScope();

View File

@ -56,9 +56,7 @@ public class WebAuthServiceImpl implements WebAuthService {
return false;
}
powerJobPermissionService.hasPermission(powerJobUser.getId(), roleScope, target, permission);
return false;
return powerJobPermissionService.hasPermission(powerJobUser.getId(), roleScope, target, permission);
}
private void diffGrant(RoleScope roleScope, Long target, Role role, List<Long> uids, Map<Role, List<Long>> originRole2Uids) {

View File

@ -1,7 +1,6 @@
package tech.powerjob.server.web.controller;
import com.google.common.collect.Lists;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
@ -11,24 +10,30 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import tech.powerjob.common.exception.PowerJobException;
import tech.powerjob.common.response.ResultDTO;
import tech.powerjob.common.utils.CommonUtils;
import tech.powerjob.server.auth.LoginUserHolder;
import tech.powerjob.server.auth.Permission;
import tech.powerjob.server.auth.RoleScope;
import tech.powerjob.server.auth.common.AuthConstants;
import tech.powerjob.server.auth.interceptor.ApiPermission;
import tech.powerjob.server.auth.plugin.ModifyOrCreateDynamicPermission;
import tech.powerjob.server.auth.plugin.SaveAppGrantPermissionPlugin;
import tech.powerjob.server.auth.service.WebAuthService;
import tech.powerjob.server.core.service.AppInfoService;
import tech.powerjob.server.persistence.PageResult;
import tech.powerjob.server.persistence.QueryConvertUtils;
import tech.powerjob.server.persistence.remote.model.AppInfoDO;
import tech.powerjob.server.persistence.remote.repository.AppInfoRepository;
import tech.powerjob.server.web.request.AppAssertRequest;
import tech.powerjob.server.web.request.ComponentUserRoleInfo;
import tech.powerjob.server.web.request.ModifyAppInfoRequest;
import tech.powerjob.server.web.request.QueryAppInfoRequest;
import tech.powerjob.server.web.response.AppInfoVO;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
@ -43,6 +48,8 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor
public class AppInfoController {
private final WebAuthService webAuthService;
private final AppInfoService appInfoService;
private final AppInfoRepository appInfoRepository;
@ -50,6 +57,7 @@ public class AppInfoController {
private static final int MAX_APP_NUM = 200;
@PostMapping("/save")
@ApiPermission(name = "App-Save", roleScope = RoleScope.APP, dynamicPermissionPlugin = ModifyOrCreateDynamicPermission.class, grandPermissionPlugin = SaveAppGrantPermissionPlugin.class)
public ResultDTO<AppInfoVO> saveAppInfo(@RequestBody ModifyAppInfoRequest req) {
req.valid();
@ -59,73 +67,73 @@ public class AppInfoController {
if (id == null) {
appInfoDO = new AppInfoDO();
appInfoDO.setGmtCreate(new Date());
}else {
appInfoDO.setCreator(LoginUserHolder.getUserName());
} else {
appInfoDO = appInfoRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("can't find appInfo by id:" + id));
// 对比密码
if (!Objects.equals(req.getOldPassword(), appInfoDO.getPassword())) {
throw new PowerJobException("The password is incorrect.");
// 不允许修改 appName
if (!appInfoDO.getAppName().equalsIgnoreCase(req.getAppName())) {
throw new IllegalArgumentException("NOT_ALLOW_CHANGE_THE_APP_NAME");
}
}
BeanUtils.copyProperties(req, appInfoDO);
appInfoDO.setAppName(req.getAppName());
appInfoDO.setTitle(req.getTitle());
appInfoDO.setPassword(req.getPassword());
appInfoDO.setNamespaceId(req.getNamespaceId());
appInfoDO.setTags(req.getTags());
appInfoDO.setExtra(req.getExtra());
appInfoDO.setGmtModified(new Date());
appInfoDO.setModifier(LoginUserHolder.getUserName());
AppInfoDO savedAppInfo = appInfoRepository.saveAndFlush(appInfoDO);
// 重现授权
webAuthService.processPermissionOnSave(RoleScope.APP, savedAppInfo.getId(), req.getComponentUserRoleInfo());
return ResultDTO.success(convert(Lists.newArrayList(savedAppInfo), false).get(0));
}
@GetMapping("/delete")
@ApiPermission(name = "App-Delete", roleScope = RoleScope.APP, requiredPermission = Permission.SU)
public ResultDTO<Void> deleteAppInfo(Long appId) {
appInfoRepository.deleteById(appId);
return ResultDTO.success(null);
}
@PostMapping("/assert")
public ResultDTO<Long> assertApp(@RequestBody AppAssertRequest request) {
return ResultDTO.success(appInfoService.assertApp(request.getAppName(), request.getPassword()));
}
@GetMapping("/delete")
public ResultDTO<Void> deleteAppInfo(Long appId) {
appInfoRepository.deleteById(appId);
return ResultDTO.success(null);
}
@GetMapping("/list")
public ResultDTO<List<AppInfoVO>> listAppInfo(@RequestParam(required = false) String condition) {
List<AppInfoDO> result;
Pageable limit = PageRequest.of(0, MAX_APP_NUM);
if (StringUtils.isEmpty(condition)) {
result = appInfoRepository.findAll(limit).getContent();
}else {
result = appInfoRepository.findByAppNameLike("%" + condition + "%", limit).getContent();
}
return ResultDTO.success(convert(result, false));
}
@PostMapping("/listByQuery")
public ResultDTO<PageResult<AppInfoVO>> listAppInfoByQuery(QueryAppInfoRequest queryAppInfoRequest) {
@PostMapping("/list")
@ApiPermission(name = "Namespace-List", roleScope = RoleScope.APP, requiredPermission = Permission.NONE)
public ResultDTO<PageResult<AppInfoVO>> listAppInfoByQuery(@RequestBody QueryAppInfoRequest queryAppInfoRequest) {
Pageable pageable = PageRequest.of(queryAppInfoRequest.getIndex(), queryAppInfoRequest.getPageSize());
// TODO: 我有权限的列表
Specification<AppInfoDO> specification = new Specification<AppInfoDO>() {
@Override
public Predicate toPredicate(Root<AppInfoDO> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = Lists.newArrayList();
Specification<AppInfoDO> specification = (root, query, criteriaBuilder) -> {
List<Predicate> predicates = Lists.newArrayList();
Long appId = queryAppInfoRequest.getAppId();
Long namespaceId = queryAppInfoRequest.getNamespaceId();
Long appId = queryAppInfoRequest.getAppId();
Long namespaceId = queryAppInfoRequest.getNamespaceId();
if (appId != null) {
predicates.add(criteriaBuilder.equal(root.get("id"), appId));
}
if (namespaceId != null) {
predicates.add(criteriaBuilder.equal(root.get("namespaceId"), namespaceId));
}
if (StringUtils.isNotEmpty(queryAppInfoRequest.getAppName())) {
predicates.add(criteriaBuilder.like(root.get("appName"), QueryConvertUtils.convertLikeParams(queryAppInfoRequest.getAppName())));
}
return query.where(predicates.toArray(new Predicate[0])).getRestriction();
if (appId != null) {
predicates.add(criteriaBuilder.equal(root.get("id"), appId));
}
if (namespaceId != null) {
predicates.add(criteriaBuilder.equal(root.get("namespaceId"), namespaceId));
}
if (StringUtils.isNotEmpty(queryAppInfoRequest.getAppNameLike())) {
predicates.add(criteriaBuilder.like(root.get("appName"), QueryConvertUtils.convertLikeParams(queryAppInfoRequest.getAppNameLike())));
}
return query.where(predicates.toArray(new Predicate[0])).getRestriction();
};
Page<AppInfoDO> pageAppInfoResult = appInfoRepository.findAll(specification, pageable);
@ -139,27 +147,30 @@ public class AppInfoController {
}
private static List<AppInfoVO> convert(List<AppInfoDO> data, boolean fillDetail) {
private List<AppInfoVO> convert(List<AppInfoDO> data, boolean fillDetail) {
if (CollectionUtils.isEmpty(data)) {
return Lists.newLinkedList();
}
List<AppInfoVO> appInfoVOList = data.stream().map(appInfoDO -> {
return data.stream().map(appInfoDO -> {
AppInfoVO appInfoVO = new AppInfoVO();
BeanUtils.copyProperties(appInfoDO, appInfoVO);
appInfoVO.setGmtCreateStr(CommonUtils.formatTime(appInfoDO.getGmtCreate()));
appInfoVO.setGmtModifiedStr(CommonUtils.formatTime(appInfoDO.getGmtModified()));
if (fillDetail) {
// 人员面板
ComponentUserRoleInfo componentUserRoleInfo = webAuthService.fetchComponentUserRoleInfo(RoleScope.APP, appInfoDO.getId());
appInfoVO.setComponentUserRoleInfo(componentUserRoleInfo);
// 密码
boolean hasPermission = webAuthService.hasPermission(RoleScope.APP, appInfoDO.getId(), Permission.READ);
appInfoVO.setPassword(hasPermission ? appInfoDO.getPassword() : AuthConstants.TIPS_NO_PERMISSION_TO_SEE);
}
return appInfoVO;
}).collect(Collectors.toList());
if (fillDetail) {
// TODO: 补全权限等额外信息
}
return appInfoVOList;
}
@Data
private static class AppInfoVO {
private Long id;
private String appName;
}
}

View File

@ -12,6 +12,7 @@ import tech.powerjob.common.response.ResultDTO;
import tech.powerjob.server.auth.LoginUserHolder;
import tech.powerjob.server.auth.Permission;
import tech.powerjob.server.auth.RoleScope;
import tech.powerjob.server.auth.common.AuthConstants;
import tech.powerjob.server.auth.interceptor.ApiPermission;
import tech.powerjob.server.auth.plugin.ModifyOrCreateDynamicPermission;
import tech.powerjob.server.auth.plugin.SaveNamespaceGrantPermissionPlugin;
@ -105,6 +106,7 @@ public class NamespaceController {
}
@PostMapping("/list")
@ApiPermission(name = "Namespace-List", roleScope = RoleScope.NAMESPACE, requiredPermission = Permission.NONE)
public ResultDTO<PageResult<NamespaceVO>> listNamespace(@RequestBody QueryNamespaceRequest queryNamespaceRequest) {
String codeLike = queryNamespaceRequest.getCodeLike();
@ -155,9 +157,7 @@ public class NamespaceController {
// 有权限用户填充 token
boolean hasPermission = webAuthService.hasPermission(RoleScope.NAMESPACE, namespaceId, Permission.READ);
if (hasPermission) {
namespaceVO.setToken(namespaceDO.getToken());
}
namespaceVO.setToken(hasPermission ? namespaceDO.getToken() : AuthConstants.TIPS_NO_PERMISSION_TO_SEE);
}
private NamespaceDO fetchById(Long id) {

View File

@ -15,14 +15,34 @@ import org.apache.commons.lang3.StringUtils;
public class ModifyAppInfoRequest {
private Long id;
private String oldPassword;
private String appName;
private Long namespaceId;
private String oldPassword;
private String password;
/**
* 描述
*/
private String title;
/**
* 管理标签
*/
private String tags;
/**
* 扩展字段
*/
private String extra;
private ComponentUserRoleInfo componentUserRoleInfo;
public void valid() {
CommonUtils.requireNonNull(appName, "appName can't be empty");
if (StringUtils.containsWhitespace(appName)) {
throw new PowerJobException("appName can't contains white space!");
}
CommonUtils.requireNonNull(password, "password can't be empty");
}
}

View File

@ -22,7 +22,9 @@ public class QueryAppInfoRequest {
/**
* 任务名称
*/
private String appName;
private String appNameLike;
private String tagLike;
/**
* 查询与我相关的任务我有直接权限的

View File

@ -0,0 +1,46 @@
package tech.powerjob.server.web.response;
import lombok.Data;
import tech.powerjob.server.web.request.ComponentUserRoleInfo;
import java.io.Serializable;
import java.util.Date;
/**
* AppInfoVO
*
* @author tjq
* @since 2024/2/12
*/
@Data
public class AppInfoVO implements Serializable {
private Long id;
private String appName;
/**
* 描述
*/
private String title;
private String password;
private String tags;
private String extra;
private ComponentUserRoleInfo componentUserRoleInfo;
private Date gmtCreate;
private String gmtCreateStr;
private Date gmtModified;
private String gmtModifiedStr;
private String creator;
private String modifier;
}