fix #493: add effective access matrix

This commit is contained in:
devmrko
2026-06-26 14:14:22 +09:00
parent 210e0bd4d0
commit c8326f954d
12 changed files with 805 additions and 0 deletions

View File

@@ -0,0 +1,296 @@
package com.cloudhandson.vpdbackoffice.service;
import com.cloudhandson.vpdbackoffice.domain.effective.EffectiveMatrixView;
import com.cloudhandson.vpdbackoffice.domain.effective.GroupEffectiveAccessView;
import com.cloudhandson.vpdbackoffice.domain.effective.RoleEffectiveImpactView;
import com.cloudhandson.vpdbackoffice.domain.effective.UserEffectiveAccessView;
import com.cloudhandson.vpdbackoffice.domain.group.AppGroup;
import com.cloudhandson.vpdbackoffice.domain.group.GroupRoleView;
import com.cloudhandson.vpdbackoffice.domain.group.GroupUserView;
import com.cloudhandson.vpdbackoffice.domain.permission.AppRole;
import com.cloudhandson.vpdbackoffice.domain.permission.PermissionView;
import com.cloudhandson.vpdbackoffice.domain.user.AppUser;
import com.cloudhandson.vpdbackoffice.domain.user.UserRoleView;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.stereotype.Service;
@Service
public class EffectiveMatrixService {
private final UserService userService;
private final GroupService groupService;
private final PermissionService permissionService;
public EffectiveMatrixService(
UserService userService,
GroupService groupService,
PermissionService permissionService
) {
this.userService = userService;
this.groupService = groupService;
this.permissionService = permissionService;
}
public EffectiveMatrixView matrix() {
List<AppUser> users = userService.findAll();
List<AppGroup> groups = groupService.findAll();
List<AppRole> roles = permissionService.findRoles();
List<UserRoleView> userRoles = userService.findUserRoles();
List<GroupUserView> groupUsers = groupService.findGroupUsers();
List<GroupRoleView> groupRoles = groupService.findGroupRoles();
List<PermissionView> permissions = permissionService.findPermissionViews();
Map<Long, Set<Long>> directRoleIdsByUser = directRoleIdsByUser(userRoles);
Map<Long, List<String>> directRoleNamesByUser = directRoleNamesByUser(userRoles);
Map<Long, Set<Long>> groupIdsByUser = groupIdsByUser(groupUsers);
Map<Long, List<String>> groupNamesByUser = groupNamesByUser(groupUsers);
Map<Long, List<String>> userNamesByGroup = userNamesByGroup(groupUsers);
Map<Long, Set<Long>> roleIdsByGroup = roleIdsByGroup(groupRoles);
Map<Long, List<String>> roleNamesByGroup = roleNamesByGroup(groupRoles);
Map<Long, List<String>> groupNamesByRole = groupNamesByRole(groupRoles);
Map<Long, List<String>> directUserNamesByRole = directUserNamesByRole(userRoles);
Map<Long, List<PermissionView>> permissionsByRole = permissionsByRole(permissions);
Map<Long, String> roleNameById = roleNameById(roles);
List<UserEffectiveAccessView> userViews = users.stream()
.map(user -> userView(
user,
directRoleIdsByUser,
directRoleNamesByUser,
groupIdsByUser,
groupNamesByUser,
roleIdsByGroup,
roleNameById,
permissionsByRole
))
.toList();
List<GroupEffectiveAccessView> groupViews = groups.stream()
.map(group -> groupView(group, userNamesByGroup, roleIdsByGroup, roleNamesByGroup, permissionsByRole))
.toList();
List<RoleEffectiveImpactView> roleViews = roles.stream()
.map(role -> roleView(role, directUserNamesByRole, groupNamesByRole, groupUsers, groupRoles, permissionsByRole))
.toList();
return new EffectiveMatrixView(userViews, groupViews, roleViews, permissions.size());
}
private UserEffectiveAccessView userView(
AppUser user,
Map<Long, Set<Long>> directRoleIdsByUser,
Map<Long, List<String>> directRoleNamesByUser,
Map<Long, Set<Long>> groupIdsByUser,
Map<Long, List<String>> groupNamesByUser,
Map<Long, Set<Long>> roleIdsByGroup,
Map<Long, String> roleNameById,
Map<Long, List<PermissionView>> permissionsByRole
) {
Set<Long> directRoleIds = directRoleIdsByUser.getOrDefault(user.userId(), Set.of());
Set<Long> inheritedRoleIds = new LinkedHashSet<>();
for (Long groupId : groupIdsByUser.getOrDefault(user.userId(), Set.of())) {
inheritedRoleIds.addAll(roleIdsByGroup.getOrDefault(groupId, Set.of()));
}
Set<Long> effectiveRoleIds = new LinkedHashSet<>(directRoleIds);
effectiveRoleIds.addAll(inheritedRoleIds);
return new UserEffectiveAccessView(
user.userId(),
user.username(),
user.empNo(),
user.deptCode(),
user.active(),
directRoleNamesByUser.getOrDefault(user.userId(), List.of()),
groupNamesByUser.getOrDefault(user.userId(), List.of()),
names(inheritedRoleIds, roleNameById),
names(effectiveRoleIds, roleNameById),
permissionCount(effectiveRoleIds, permissionsByRole),
objectNames(effectiveRoleIds, permissionsByRole)
);
}
private GroupEffectiveAccessView groupView(
AppGroup group,
Map<Long, List<String>> userNamesByGroup,
Map<Long, Set<Long>> roleIdsByGroup,
Map<Long, List<String>> roleNamesByGroup,
Map<Long, List<PermissionView>> permissionsByRole
) {
Set<Long> roleIds = roleIdsByGroup.getOrDefault(group.groupId(), Set.of());
return new GroupEffectiveAccessView(
group.groupId(),
group.groupCode(),
group.groupName(),
group.active(),
userNamesByGroup.getOrDefault(group.groupId(), List.of()),
roleNamesByGroup.getOrDefault(group.groupId(), List.of()),
permissionCount(roleIds, permissionsByRole),
objectNames(roleIds, permissionsByRole)
);
}
private RoleEffectiveImpactView roleView(
AppRole role,
Map<Long, List<String>> directUserNamesByRole,
Map<Long, List<String>> groupNamesByRole,
List<GroupUserView> groupUsers,
List<GroupRoleView> groupRoles,
Map<Long, List<PermissionView>> permissionsByRole
) {
Set<Long> roleIds = Set.of(role.roleId());
Set<String> inheritedUsers = new LinkedHashSet<>();
Set<Long> roleGroupIds = new LinkedHashSet<>();
for (GroupRoleView groupRole : groupRoles) {
if (groupRole.roleId() == role.roleId()) {
roleGroupIds.add(groupRole.groupId());
}
}
for (GroupUserView groupUser : groupUsers) {
if (roleGroupIds.contains(groupUser.groupId())) {
inheritedUsers.add(groupUser.username());
}
}
Set<String> affectedUsers = new LinkedHashSet<>(directUserNamesByRole.getOrDefault(role.roleId(), List.of()));
affectedUsers.addAll(inheritedUsers);
return new RoleEffectiveImpactView(
role.roleId(),
role.roleName(),
role.maxSensitivityLevel(),
directUserNamesByRole.getOrDefault(role.roleId(), List.of()),
groupNamesByRole.getOrDefault(role.roleId(), List.of()),
new ArrayList<>(inheritedUsers),
new ArrayList<>(affectedUsers),
permissionCount(roleIds, permissionsByRole),
objectNames(roleIds, permissionsByRole)
);
}
private Map<Long, Set<Long>> directRoleIdsByUser(List<UserRoleView> userRoles) {
Map<Long, Set<Long>> result = new LinkedHashMap<>();
for (UserRoleView view : userRoles) {
result.computeIfAbsent(view.userId(), key -> new LinkedHashSet<>()).add(view.roleId());
}
return result;
}
private Map<Long, List<String>> directRoleNamesByUser(List<UserRoleView> userRoles) {
Map<Long, Set<String>> result = new LinkedHashMap<>();
for (UserRoleView view : userRoles) {
result.computeIfAbsent(view.userId(), key -> new LinkedHashSet<>()).add(view.roleName());
}
return toListMap(result);
}
private Map<Long, Set<Long>> groupIdsByUser(List<GroupUserView> groupUsers) {
Map<Long, Set<Long>> result = new LinkedHashMap<>();
for (GroupUserView view : groupUsers) {
result.computeIfAbsent(view.userId(), key -> new LinkedHashSet<>()).add(view.groupId());
}
return result;
}
private Map<Long, List<String>> groupNamesByUser(List<GroupUserView> groupUsers) {
Map<Long, Set<String>> result = new LinkedHashMap<>();
for (GroupUserView view : groupUsers) {
result.computeIfAbsent(view.userId(), key -> new LinkedHashSet<>()).add(groupLabel(view.groupCode(), view.groupName()));
}
return toListMap(result);
}
private Map<Long, List<String>> userNamesByGroup(List<GroupUserView> groupUsers) {
Map<Long, Set<String>> result = new LinkedHashMap<>();
for (GroupUserView view : groupUsers) {
result.computeIfAbsent(view.groupId(), key -> new LinkedHashSet<>()).add(view.username());
}
return toListMap(result);
}
private Map<Long, Set<Long>> roleIdsByGroup(List<GroupRoleView> groupRoles) {
Map<Long, Set<Long>> result = new LinkedHashMap<>();
for (GroupRoleView view : groupRoles) {
result.computeIfAbsent(view.groupId(), key -> new LinkedHashSet<>()).add(view.roleId());
}
return result;
}
private Map<Long, List<String>> roleNamesByGroup(List<GroupRoleView> groupRoles) {
Map<Long, Set<String>> result = new LinkedHashMap<>();
for (GroupRoleView view : groupRoles) {
result.computeIfAbsent(view.groupId(), key -> new LinkedHashSet<>()).add(view.roleName());
}
return toListMap(result);
}
private Map<Long, List<String>> groupNamesByRole(List<GroupRoleView> groupRoles) {
Map<Long, Set<String>> result = new LinkedHashMap<>();
for (GroupRoleView view : groupRoles) {
result.computeIfAbsent(view.roleId(), key -> new LinkedHashSet<>()).add(groupLabel(view.groupCode(), view.groupName()));
}
return toListMap(result);
}
private Map<Long, List<String>> directUserNamesByRole(List<UserRoleView> userRoles) {
Map<Long, Set<String>> result = new LinkedHashMap<>();
for (UserRoleView view : userRoles) {
result.computeIfAbsent(view.roleId(), key -> new LinkedHashSet<>()).add(view.username());
}
return toListMap(result);
}
private Map<Long, List<PermissionView>> permissionsByRole(List<PermissionView> permissions) {
Map<Long, List<PermissionView>> result = new LinkedHashMap<>();
for (PermissionView permission : permissions) {
result.computeIfAbsent(permission.roleId(), key -> new ArrayList<>()).add(permission);
}
return result;
}
private Map<Long, String> roleNameById(List<AppRole> roles) {
Map<Long, String> result = new LinkedHashMap<>();
for (AppRole role : roles) {
result.put(role.roleId(), role.roleName());
}
return result;
}
private List<String> names(Set<Long> ids, Map<Long, String> nameById) {
return ids.stream()
.map(nameById::get)
.filter(name -> name != null && !name.isBlank())
.toList();
}
private int permissionCount(Set<Long> roleIds, Map<Long, List<PermissionView>> permissionsByRole) {
return roleIds.stream()
.mapToInt(roleId -> permissionsByRole.getOrDefault(roleId, List.of()).size())
.sum();
}
private List<String> objectNames(Set<Long> roleIds, Map<Long, List<PermissionView>> permissionsByRole) {
Set<String> result = new LinkedHashSet<>();
for (Long roleId : roleIds) {
for (PermissionView permission : permissionsByRole.getOrDefault(roleId, List.of())) {
result.add(permission.objectName() + " / " + permission.permissionEffect());
}
}
return new ArrayList<>(result);
}
private Map<Long, List<String>> toListMap(Map<Long, Set<String>> source) {
Map<Long, List<String>> result = new LinkedHashMap<>();
source.forEach((key, value) -> result.put(key, new ArrayList<>(value)));
return result;
}
private String groupLabel(String groupCode, String groupName) {
return groupCode + " / " + groupName;
}
}