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 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); String v = httpServletRequest.getHeader(key);
// 解决 window.localStorage.getItem null 的问题 // 解决 window.localStorage.getItem null 的问题
if (OmsConstant.NULL.equalsIgnoreCase(v)) { if (OmsConstant.NULL.equalsIgnoreCase(v) || "undefined".equalsIgnoreCase(v)) {
return null; return null;
} }

View File

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

View File

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

View File

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

View File

@ -56,9 +56,7 @@ public class WebAuthServiceImpl implements WebAuthService {
return false; return false;
} }
powerJobPermissionService.hasPermission(powerJobUser.getId(), roleScope, target, permission); return powerJobPermissionService.hasPermission(powerJobUser.getId(), roleScope, target, permission);
return false;
} }
private void diffGrant(RoleScope roleScope, Long target, Role role, List<Long> uids, Map<Role, List<Long>> originRole2Uids) { 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; package tech.powerjob.server.web.controller;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import lombok.Data;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils; 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.data.jpa.domain.Specification;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import tech.powerjob.common.exception.PowerJobException;
import tech.powerjob.common.response.ResultDTO; 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.core.service.AppInfoService;
import tech.powerjob.server.persistence.PageResult; import tech.powerjob.server.persistence.PageResult;
import tech.powerjob.server.persistence.QueryConvertUtils; import tech.powerjob.server.persistence.QueryConvertUtils;
import tech.powerjob.server.persistence.remote.model.AppInfoDO; import tech.powerjob.server.persistence.remote.model.AppInfoDO;
import tech.powerjob.server.persistence.remote.repository.AppInfoRepository; import tech.powerjob.server.persistence.remote.repository.AppInfoRepository;
import tech.powerjob.server.web.request.AppAssertRequest; 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.ModifyAppInfoRequest;
import tech.powerjob.server.web.request.QueryAppInfoRequest; 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.Predicate;
import javax.persistence.criteria.Root;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -43,6 +48,8 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor @RequiredArgsConstructor
public class AppInfoController { public class AppInfoController {
private final WebAuthService webAuthService;
private final AppInfoService appInfoService; private final AppInfoService appInfoService;
private final AppInfoRepository appInfoRepository; private final AppInfoRepository appInfoRepository;
@ -50,6 +57,7 @@ public class AppInfoController {
private static final int MAX_APP_NUM = 200; private static final int MAX_APP_NUM = 200;
@PostMapping("/save") @PostMapping("/save")
@ApiPermission(name = "App-Save", roleScope = RoleScope.APP, dynamicPermissionPlugin = ModifyOrCreateDynamicPermission.class, grandPermissionPlugin = SaveAppGrantPermissionPlugin.class)
public ResultDTO<AppInfoVO> saveAppInfo(@RequestBody ModifyAppInfoRequest req) { public ResultDTO<AppInfoVO> saveAppInfo(@RequestBody ModifyAppInfoRequest req) {
req.valid(); req.valid();
@ -59,73 +67,73 @@ public class AppInfoController {
if (id == null) { if (id == null) {
appInfoDO = new AppInfoDO(); appInfoDO = new AppInfoDO();
appInfoDO.setGmtCreate(new Date()); appInfoDO.setGmtCreate(new Date());
}else { appInfoDO.setCreator(LoginUserHolder.getUserName());
} else {
appInfoDO = appInfoRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("can't find appInfo by id:" + id)); appInfoDO = appInfoRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("can't find appInfo by id:" + id));
// 对比密码 // 不允许修改 appName
if (!Objects.equals(req.getOldPassword(), appInfoDO.getPassword())) { if (!appInfoDO.getAppName().equalsIgnoreCase(req.getAppName())) {
throw new PowerJobException("The password is incorrect."); 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.setGmtModified(new Date());
appInfoDO.setModifier(LoginUserHolder.getUserName());
AppInfoDO savedAppInfo = appInfoRepository.saveAndFlush(appInfoDO); AppInfoDO savedAppInfo = appInfoRepository.saveAndFlush(appInfoDO);
// 重现授权
webAuthService.processPermissionOnSave(RoleScope.APP, savedAppInfo.getId(), req.getComponentUserRoleInfo());
return ResultDTO.success(convert(Lists.newArrayList(savedAppInfo), false).get(0)); 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") @PostMapping("/assert")
public ResultDTO<Long> assertApp(@RequestBody AppAssertRequest request) { public ResultDTO<Long> assertApp(@RequestBody AppAssertRequest request) {
return ResultDTO.success(appInfoService.assertApp(request.getAppName(), request.getPassword())); return ResultDTO.success(appInfoService.assertApp(request.getAppName(), request.getPassword()));
} }
@GetMapping("/delete") @PostMapping("/list")
public ResultDTO<Void> deleteAppInfo(Long appId) { @ApiPermission(name = "Namespace-List", roleScope = RoleScope.APP, requiredPermission = Permission.NONE)
appInfoRepository.deleteById(appId); public ResultDTO<PageResult<AppInfoVO>> listAppInfoByQuery(@RequestBody QueryAppInfoRequest queryAppInfoRequest) {
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) {
Pageable pageable = PageRequest.of(queryAppInfoRequest.getIndex(), queryAppInfoRequest.getPageSize()); Pageable pageable = PageRequest.of(queryAppInfoRequest.getIndex(), queryAppInfoRequest.getPageSize());
// TODO: 我有权限的列表 // TODO: 我有权限的列表
Specification<AppInfoDO> specification = new Specification<AppInfoDO>() { Specification<AppInfoDO> specification = (root, query, criteriaBuilder) -> {
@Override List<Predicate> predicates = Lists.newArrayList();
public Predicate toPredicate(Root<AppInfoDO> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = Lists.newArrayList();
Long appId = queryAppInfoRequest.getAppId(); Long appId = queryAppInfoRequest.getAppId();
Long namespaceId = queryAppInfoRequest.getNamespaceId(); Long namespaceId = queryAppInfoRequest.getNamespaceId();
if (appId != null) { if (appId != null) {
predicates.add(criteriaBuilder.equal(root.get("id"), appId)); 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 (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); 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)) { if (CollectionUtils.isEmpty(data)) {
return Lists.newLinkedList(); return Lists.newLinkedList();
} }
List<AppInfoVO> appInfoVOList = data.stream().map(appInfoDO -> {
return data.stream().map(appInfoDO -> {
AppInfoVO appInfoVO = new AppInfoVO(); AppInfoVO appInfoVO = new AppInfoVO();
BeanUtils.copyProperties(appInfoDO, 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; return appInfoVO;
}).collect(Collectors.toList()); }).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.LoginUserHolder;
import tech.powerjob.server.auth.Permission; import tech.powerjob.server.auth.Permission;
import tech.powerjob.server.auth.RoleScope; 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.interceptor.ApiPermission;
import tech.powerjob.server.auth.plugin.ModifyOrCreateDynamicPermission; import tech.powerjob.server.auth.plugin.ModifyOrCreateDynamicPermission;
import tech.powerjob.server.auth.plugin.SaveNamespaceGrantPermissionPlugin; import tech.powerjob.server.auth.plugin.SaveNamespaceGrantPermissionPlugin;
@ -105,6 +106,7 @@ public class NamespaceController {
} }
@PostMapping("/list") @PostMapping("/list")
@ApiPermission(name = "Namespace-List", roleScope = RoleScope.NAMESPACE, requiredPermission = Permission.NONE)
public ResultDTO<PageResult<NamespaceVO>> listNamespace(@RequestBody QueryNamespaceRequest queryNamespaceRequest) { public ResultDTO<PageResult<NamespaceVO>> listNamespace(@RequestBody QueryNamespaceRequest queryNamespaceRequest) {
String codeLike = queryNamespaceRequest.getCodeLike(); String codeLike = queryNamespaceRequest.getCodeLike();
@ -155,9 +157,7 @@ public class NamespaceController {
// 有权限用户填充 token // 有权限用户填充 token
boolean hasPermission = webAuthService.hasPermission(RoleScope.NAMESPACE, namespaceId, Permission.READ); boolean hasPermission = webAuthService.hasPermission(RoleScope.NAMESPACE, namespaceId, Permission.READ);
if (hasPermission) { namespaceVO.setToken(hasPermission ? namespaceDO.getToken() : AuthConstants.TIPS_NO_PERMISSION_TO_SEE);
namespaceVO.setToken(namespaceDO.getToken());
}
} }
private NamespaceDO fetchById(Long id) { private NamespaceDO fetchById(Long id) {

View File

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