fix #492: add token context selection

This commit is contained in:
devmrko
2026-06-26 13:53:10 +09:00
parent cd7860e253
commit 210e0bd4d0
14 changed files with 383 additions and 12 deletions

View File

@@ -2,17 +2,27 @@ package com.cloudhandson.vpdbackoffice.service;
import com.cloudhandson.vpdbackoffice.config.BackofficeProperties;
import com.cloudhandson.vpdbackoffice.domain.audit.AuditEvent;
import com.cloudhandson.vpdbackoffice.domain.group.GroupRoleView;
import com.cloudhandson.vpdbackoffice.domain.group.GroupUserView;
import com.cloudhandson.vpdbackoffice.domain.token.BearerTokenRecord;
import com.cloudhandson.vpdbackoffice.domain.token.IssuedToken;
import com.cloudhandson.vpdbackoffice.domain.token.TokenContextView;
import com.cloudhandson.vpdbackoffice.domain.token.TokenIssueCommand;
import com.cloudhandson.vpdbackoffice.domain.user.AppUser;
import com.cloudhandson.vpdbackoffice.domain.user.UserRoleView;
import com.cloudhandson.vpdbackoffice.mapper.BearerTokenMapper;
import com.cloudhandson.vpdbackoffice.mapper.GroupMapper;
import com.cloudhandson.vpdbackoffice.mapper.UserMapper;
import java.time.Clock;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
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;
import org.springframework.transaction.annotation.Transactional;
@@ -21,6 +31,7 @@ public class BearerTokenService {
private final BearerTokenMapper tokenMapper;
private final UserMapper userMapper;
private final GroupMapper groupMapper;
private final AuditService auditService;
private final TokenGenerator tokenGenerator;
private final TokenHasher tokenHasher;
@@ -30,6 +41,7 @@ public class BearerTokenService {
public BearerTokenService(
BearerTokenMapper tokenMapper,
UserMapper userMapper,
GroupMapper groupMapper,
AuditService auditService,
TokenGenerator tokenGenerator,
TokenHasher tokenHasher,
@@ -38,6 +50,7 @@ public class BearerTokenService {
) {
this.tokenMapper = tokenMapper;
this.userMapper = userMapper;
this.groupMapper = groupMapper;
this.auditService = auditService;
this.tokenGenerator = tokenGenerator;
this.tokenHasher = tokenHasher;
@@ -49,6 +62,31 @@ public class BearerTokenService {
return tokenMapper.findAll();
}
public List<TokenContextView> findTokenContextOptions() {
List<BearerTokenRecord> tokens = tokenMapper.findAll();
List<GroupUserView> groupUsers = groupMapper.findGroupUsers();
Map<Long, List<String>> directRolesByUser = directRolesByUser();
Map<Long, List<String>> groupsByUser = groupsByUser(groupUsers);
Map<Long, List<String>> inheritedRolesByUser = inheritedRolesByUser(groupUsers);
LocalDateTime now = LocalDateTime.now(clock.withZone(ZoneId.systemDefault()));
return tokens.stream()
.map(token -> new TokenContextView(
token.keyId(),
token.userId(),
token.username(),
token.keyPrefix(),
token.expiresAt(),
token.revokedAt(),
token.description(),
token.active(now),
directRolesByUser.getOrDefault(token.userId(), List.of()),
groupsByUser.getOrDefault(token.userId(), List.of()),
inheritedRolesByUser.getOrDefault(token.userId(), List.of())
))
.toList();
}
public BearerTokenRecord findById(long keyId) {
return tokenMapper.findById(keyId);
}
@@ -68,6 +106,45 @@ public class BearerTokenService {
return hash.equalsIgnoreCase(record.keyHash());
}
private Map<Long, List<String>> directRolesByUser() {
Map<Long, Set<String>> grouped = new LinkedHashMap<>();
for (UserRoleView view : userMapper.findUserRoles()) {
grouped.computeIfAbsent(view.userId(), key -> new LinkedHashSet<>()).add(view.roleName());
}
return toListMap(grouped);
}
private Map<Long, List<String>> groupsByUser(List<GroupUserView> groupUsers) {
Map<Long, Set<String>> grouped = new LinkedHashMap<>();
for (GroupUserView view : groupUsers) {
grouped.computeIfAbsent(view.userId(), key -> new LinkedHashSet<>())
.add(view.groupCode() + " / " + view.groupName());
}
return toListMap(grouped);
}
private Map<Long, List<String>> inheritedRolesByUser(List<GroupUserView> groupUsers) {
Map<Long, Set<String>> rolesByGroup = new LinkedHashMap<>();
for (GroupRoleView view : groupMapper.findGroupRoles()) {
rolesByGroup.computeIfAbsent(view.groupId(), key -> new LinkedHashSet<>()).add(view.roleName());
}
Map<Long, Set<String>> rolesByUser = new LinkedHashMap<>();
for (GroupUserView view : groupUsers) {
Set<String> roles = rolesByGroup.getOrDefault(view.groupId(), Set.of());
if (!roles.isEmpty()) {
rolesByUser.computeIfAbsent(view.userId(), key -> new LinkedHashSet<>()).addAll(roles);
}
}
return toListMap(rolesByUser);
}
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;
}
@Transactional
public IssuedToken issueToken(TokenIssueCommand command) {
AppUser user = userMapper.findById(command.userId());