WangHan
2024-09-12 d5855a4926926698b740bc6c7ba489de47adb68b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
package tech.powerjob.server.auth.service.permission;
 
import com.google.common.collect.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import tech.powerjob.server.auth.Permission;
import tech.powerjob.server.auth.Role;
import tech.powerjob.server.auth.RoleScope;
import tech.powerjob.server.persistence.remote.model.AppInfoDO;
import tech.powerjob.server.persistence.remote.model.UserRoleDO;
import tech.powerjob.server.persistence.remote.repository.AppInfoRepository;
import tech.powerjob.server.persistence.remote.repository.UserRoleRepository;
 
import javax.annotation.Resource;
import java.util.*;
 
/**
 * PowerJobPermissionService
 *
 * @author tjq
 * @since 2024/2/11
 */
@Slf4j
@Service
public class PowerJobPermissionServiceImpl implements PowerJobPermissionService {
 
    @Resource
    private AppInfoRepository appInfoRepository;
    @Resource
    private UserRoleRepository userRoleRepository;
 
    @Override
    public boolean hasPermission(Long userId, RoleScope roleScope, Long target, Permission requiredPermission) {
        final List<UserRoleDO> userRoleList = Optional.ofNullable(userRoleRepository.findAllByUserId(userId)).orElse(Collections.emptyList());
 
        Multimap<Long, Role> appId2Role = ArrayListMultimap.create();
        Multimap<Long, Role> namespaceId2Role = ArrayListMultimap.create();
 
        List<Role> globalRoles = Lists.newArrayList();
 
        for (UserRoleDO userRole : userRoleList) {
            final Role role = Role.of(userRole.getRole());
 
            // 处理全局权限
            if (RoleScope.GLOBAL.getV() == userRole.getScope()) {
                if (Role.ADMIN.equals(role)) {
                    return true;
                }
                globalRoles.add(role);
            }
 
            if (RoleScope.NAMESPACE.getV() == userRole.getScope()) {
                namespaceId2Role.put(userRole.getTarget(), role);
            }
            if (RoleScope.APP.getV() == userRole.getScope()) {
                appId2Role.put(userRole.getTarget(), role);
            }
        }
 
        // 前置判断需要的权限(新增场景还没有 appId or namespaceId)
        if (requiredPermission == Permission.NONE) {
            return true;
        }
 
        // 检验全局穿透权限
        for (Role role : globalRoles) {
            if (role.getPermissions().contains(requiredPermission)) {
                return true;
            }
        }
 
        // 无超级管理员权限,校验普通权限
        if (RoleScope.APP.equals(roleScope)) {
            return checkAppPermission(target, requiredPermission, appId2Role, namespaceId2Role);
        }
 
        if (RoleScope.NAMESPACE.equals(roleScope)) {
            return checkNamespacePermission(target, requiredPermission, namespaceId2Role);
        }
 
        return false;
    }
 
    @Override
    public void grantRole(RoleScope roleScope, Long target, Long userId, Role role, String extra) {
 
        UserRoleDO userRoleDO = new UserRoleDO();
        userRoleDO.setGmtCreate(new Date());
        userRoleDO.setGmtModified(new Date());
        userRoleDO.setExtra(extra);
 
        userRoleDO.setScope(roleScope.getV());
        userRoleDO.setTarget(target);
        userRoleDO.setUserId(userId);
        userRoleDO.setRole(role.getV());
 
        userRoleRepository.saveAndFlush(userRoleDO);
        log.info("[PowerJobPermissionService] [grantPermission] saveAndFlush userRole successfully: {}", userRoleDO);
    }
 
    @Override
    public void retrieveRole(RoleScope roleScope, Long target, Long userId, Role role) {
        List<UserRoleDO> originUserRole = userRoleRepository.findAllByScopeAndTargetAndRoleAndUserId(roleScope.getV(), target, role.getV(), userId);
        log.info("[PowerJobPermissionService] [retrievePermission] origin rule: {}", originUserRole);
        Optional.ofNullable(originUserRole).orElse(Collections.emptyList()).forEach(r -> {
            userRoleRepository.deleteById(r.getId());
            log.info("[PowerJobPermissionService] [retrievePermission] delete UserRole: {}", r);
        });
    }
 
    @Override
    public Map<Role, Set<Long>> fetchUserWithPermissions(RoleScope roleScope, Long target) {
        List<UserRoleDO> permissionUserList = userRoleRepository.findAllByScopeAndTarget(roleScope.getV(), target);
        Map<Role, Set<Long>> ret = Maps.newHashMap();
        Optional.ofNullable(permissionUserList).orElse(Collections.emptyList()).forEach(userRoleDO -> {
            Role role = Role.of(userRoleDO.getRole());
            Set<Long> userIds = ret.computeIfAbsent(role, ignore -> Sets.newHashSet());
            userIds.add(userRoleDO.getUserId());
        });
 
        return ret;
    }
 
    @Override
    public Map<Role, List<Long>> fetchUserHadPermissionTargets(RoleScope roleScope, Long userId) {
 
        Map<Role, List<Long>> ret = Maps.newHashMap();
        List<UserRoleDO> userRoleDOList = userRoleRepository.findAllByUserIdAndScope(userId, roleScope.getV());
 
        Optional.ofNullable(userRoleDOList).orElse(Collections.emptyList()).forEach(r -> {
            Role role = Role.of(r.getRole());
            List<Long> targetIds = ret.computeIfAbsent(role, ignore -> Lists.newArrayList());
            targetIds.add(r.getTarget());
        });
 
        return ret;
    }
 
    private boolean checkAppPermission(Long targetId, Permission requiredPermission, Multimap<Long, Role> appId2Role, Multimap<Long, Role> namespaceId2Role) {
 
        final Collection<Role> appRoles = appId2Role.get(targetId);
        for (Role role : appRoles) {
            if (role.getPermissions().contains(requiredPermission)) {
                return true;
            }
        }
 
        // 校验 namespace 穿透权限
        Optional<AppInfoDO> appInfoOpt = appInfoRepository.findById(targetId);
        if (!appInfoOpt.isPresent()) {
            throw new IllegalArgumentException("can't find appInfo by appId in permission check: " + targetId);
        }
        Long namespaceId = Optional.ofNullable(appInfoOpt.get().getNamespaceId()).orElse(-1L);
        Collection<Role> namespaceRoles = namespaceId2Role.get(namespaceId);
        for (Role role : namespaceRoles) {
            if (role.getPermissions().contains(requiredPermission)) {
                return true;
            }
        }
 
        return false;
    }
 
    private boolean checkNamespacePermission(Long targetId, Permission requiredPermission, Multimap<Long, Role> namespaceId2Role) {
        Collection<Role> namespaceRoles = namespaceId2Role.get(targetId);
        for (Role role : namespaceRoles) {
            if (role.getPermissions().contains(requiredPermission)) {
                return true;
            }
        }
 
        return false;
    }
 
}