fix:角色授权修改
This commit is contained in:
parent
779052142b
commit
7bc364f367
@ -96,7 +96,8 @@ public class WebMvcConfig implements WebMvcConfigurer {
|
||||
"/api/v1/auth/password-login",
|
||||
"/api/v1/auth/ukey-login",
|
||||
"/api/v1/auth/ukey-login/randoms",
|
||||
"/api/v1/auth/captcha"
|
||||
"/api/v1/auth/captcha",
|
||||
"/api/v1/auth/super-admin/ukeys/issue-sign"
|
||||
);
|
||||
|
||||
registry.addInterceptor(internalApiReplayInterceptor)
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
package com.cisd.tms.modules.auth.controller;
|
||||
|
||||
import com.cisd.tms.common.api.ApiResponse;
|
||||
import com.cisd.tms.common.enums.ErrorCode;
|
||||
import com.cisd.tms.common.exception.BizException;
|
||||
import com.cisd.tms.modules.auth.dto.AdminChangePasswordRequest;
|
||||
import com.cisd.tms.modules.auth.dto.UkeyBindRequest;
|
||||
import com.cisd.tms.modules.auth.enums.AuthLevel;
|
||||
import com.cisd.tms.modules.auth.enums.RoleCode;
|
||||
import com.cisd.tms.modules.auth.security.RequireInternalAuth;
|
||||
@ -24,14 +25,14 @@ import org.springframework.web.bind.annotation.*;
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/auth")
|
||||
@RequiredArgsConstructor
|
||||
@ReplayProtected
|
||||
@Tag(name = "认证管理", description = "角色启用、密码重置与 UKey 绑定接口")
|
||||
public class AuthAdminController {
|
||||
|
||||
private final AuthAdminService authAdminService;
|
||||
|
||||
@PostMapping("/roles/{roleCode}/enable")
|
||||
@Operation(summary = "启用角色", description = "仅允许 KEY_ADMIN FULL 会话启用目标角色。")
|
||||
@Operation(summary = "启用角色", description = "仅允许 SUPER_ADMIN FULL 会话启用目标角色。")
|
||||
@ReplayProtected
|
||||
@RequireInternalAuth(role = RoleCode.SUPER_ADMIN, authLevel = AuthLevel.FULL)
|
||||
@AuditedOperation(module = ModuleCode.AUTH, action = ActionType.ENABLE, summary = "启用角色")
|
||||
public ApiResponse<Void> enableRole(@PathVariable("roleCode") String roleCode, HttpServletRequest request) {
|
||||
@ -44,7 +45,8 @@ public class AuthAdminController {
|
||||
}
|
||||
|
||||
@PostMapping("/roles/{roleCode}/reset-password")
|
||||
@Operation(summary = "重置角色密码", description = "仅允许 KEY_ADMIN FULL 会话重置目标角色密码。")
|
||||
@Operation(summary = "重置角色密码", description = "仅允许 SUPER_ADMIN FULL 会话重置目标角色密码。")
|
||||
@ReplayProtected
|
||||
@RequireInternalAuth(role = RoleCode.SUPER_ADMIN, authLevel = AuthLevel.FULL)
|
||||
@AuditedOperation(module = ModuleCode.AUTH, action = ActionType.RESET, summary = "重置角色密码")
|
||||
public ApiResponse<Void> resetPassword(@PathVariable("roleCode") String roleCode, HttpServletRequest request) {
|
||||
@ -58,6 +60,7 @@ public class AuthAdminController {
|
||||
|
||||
@PostMapping("/roles/{roleCode}/full-accounts/{uid}/change-password")
|
||||
@Operation(summary = "管理员修改 FULL 账户口令", description = "管理员为目标角色指定 UKey 席位账号设置新口令。")
|
||||
@ReplayProtected
|
||||
// @RequireInternalAuth(role = RoleCode.SUPER_ADMIN, authLevel = AuthLevel.FULL)
|
||||
@AuditedOperation(module = ModuleCode.AUTH, action = ActionType.UPDATE, summary = "管理员修改 FULL 账户口令")
|
||||
public ApiResponse<Void> changeFullAccountPassword(
|
||||
@ -79,6 +82,7 @@ public class AuthAdminController {
|
||||
|
||||
@PostMapping("/roles/{roleCode}/limited-accounts/{username}/change-password")
|
||||
@Operation(summary = "管理员修改 LIMITED 账户口令", description = "管理员为目标角色指定独立用户账号设置新口令。")
|
||||
@ReplayProtected
|
||||
// @RequireInternalAuth(role = RoleCode.SUPER_ADMIN, authLevel = AuthLevel.FULL)
|
||||
@AuditedOperation(module = ModuleCode.AUTH, action = ActionType.UPDATE, summary = "管理员修改 LIMITED 账户口令")
|
||||
public ApiResponse<Void> changeLimitedAccountPassword(
|
||||
@ -98,35 +102,31 @@ public class AuthAdminController {
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@PostMapping("/roles/{roleCode}/ukeys/bind")
|
||||
@Operation(summary = "绑定角色 UKey", description = "仅允许 KEY_ADMIN FULL 会话登记目标角色的 UKey 绑定信息。")
|
||||
@RequireInternalAuth(role = RoleCode.SUPER_ADMIN, authLevel = AuthLevel.FULL)
|
||||
@AuditedOperation(module = ModuleCode.AUTH, action = ActionType.BIND, summary = "绑定角色 UKey")
|
||||
public ApiResponse<Void> bindUkey(
|
||||
@PathVariable("roleCode") String roleCode,
|
||||
@Valid @RequestBody UkeyBindRequest request,
|
||||
HttpServletRequest httpRequest
|
||||
@PostMapping("/super-admin/ukeys/issue-sign")
|
||||
@Operation(summary = "签发并绑定超级管理员 UKey", description = "系统初始化后登记超级管理员 UKey,不要求已有登录态。")
|
||||
public ApiResponse<UKeySignResult> issueSuperAdminUkeyBindingSign(
|
||||
@Valid @RequestBody UKeySignDTO request
|
||||
) {
|
||||
authAdminService.bindIssuedUkey(
|
||||
(String) httpRequest.getAttribute(InternalApiAuthInterceptor.ATTR_ROLE_CODE),
|
||||
(String) httpRequest.getAttribute(InternalApiAuthInterceptor.ATTR_AUTH_LEVEL),
|
||||
roleCode,
|
||||
request.getUid(),
|
||||
request.getUkeySerial(),
|
||||
request.getPubKey(),
|
||||
request.getIssuerSignature()
|
||||
);
|
||||
return ApiResponse.success();
|
||||
return ApiResponse.success(authAdminService.issueUkeyBindingSign(
|
||||
null,
|
||||
null,
|
||||
RoleCode.SUPER_ADMIN.getCode(),
|
||||
request
|
||||
));
|
||||
}
|
||||
|
||||
@PostMapping("/roles/{roleCode}/ukeys/issue-sign")
|
||||
@Operation(summary = "生成 UKey 发行签名", description = "按旧绑定流程为目标角色 UKey 材料生成发行签名。")
|
||||
// @RequireInternalAuth(role = RoleCode.SUPER_ADMIN, authLevel = AuthLevel.LIMITED)
|
||||
@Operation(summary = "签发并绑定其他管理员 UKey", description = "仅允许 SUPER_ADMIN FULL 会话登记其他管理员的 UKey。")
|
||||
@ReplayProtected
|
||||
@RequireInternalAuth(role = RoleCode.SUPER_ADMIN, authLevel = AuthLevel.FULL)
|
||||
public ApiResponse<UKeySignResult> issueUkeyBindingSign(
|
||||
@PathVariable("roleCode") String roleCode,
|
||||
@RequestBody UKeySignDTO request,
|
||||
@Valid @RequestBody UKeySignDTO request,
|
||||
HttpServletRequest httpRequest
|
||||
) {
|
||||
if (RoleCode.SUPER_ADMIN.getCode().equals(roleCode)) {
|
||||
throw new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "use super-admin ukey binding endpoint");
|
||||
}
|
||||
return ApiResponse.success(authAdminService.issueUkeyBindingSign(
|
||||
(String) httpRequest.getAttribute(InternalApiAuthInterceptor.ATTR_ROLE_CODE),
|
||||
(String) httpRequest.getAttribute(InternalApiAuthInterceptor.ATTR_AUTH_LEVEL),
|
||||
|
||||
@ -1,57 +0,0 @@
|
||||
package com.cisd.tms.modules.auth.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
@Schema(description = "UKey 绑定请求")
|
||||
public class UkeyBindRequest {
|
||||
|
||||
@NotNull(message = "uid is required")
|
||||
@Schema(description = "角色内固定席位编号", example = "1")
|
||||
private Integer uid;
|
||||
|
||||
@NotBlank(message = "ukeySerial is required")
|
||||
@Schema(description = "UKey 序列号", example = "UK-001")
|
||||
private String ukeySerial;
|
||||
|
||||
@NotBlank(message = "pubKey is required")
|
||||
@Schema(description = "UKey 公钥")
|
||||
private String pubKey;
|
||||
|
||||
@NotBlank(message = "issuerSignature is required")
|
||||
@Schema(description = "认证公钥签名值")
|
||||
private String issuerSignature;
|
||||
|
||||
public Integer getUid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
public void setUid(Integer uid) {
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
public String getUkeySerial() {
|
||||
return ukeySerial;
|
||||
}
|
||||
|
||||
public void setUkeySerial(String ukeySerial) {
|
||||
this.ukeySerial = ukeySerial;
|
||||
}
|
||||
|
||||
public String getPubKey() {
|
||||
return pubKey;
|
||||
}
|
||||
|
||||
public void setPubKey(String pubKey) {
|
||||
this.pubKey = pubKey;
|
||||
}
|
||||
|
||||
public String getIssuerSignature() {
|
||||
return issuerSignature;
|
||||
}
|
||||
|
||||
public void setIssuerSignature(String issuerSignature) {
|
||||
this.issuerSignature = issuerSignature;
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,7 @@ import org.apache.ibatis.annotations.Param;
|
||||
@Mapper
|
||||
public interface RoleUkeyBindingMapper extends BaseMapperX<RoleUkeyBindingEntity> {
|
||||
|
||||
RoleUkeyBindingEntity selectActiveByRoleCodeAndUid(@Param("roleCode") String roleCode, @Param("uid") Integer uid);
|
||||
List<RoleUkeyBindingEntity> selectActiveByRoleCodeAndUid(@Param("roleCode") String roleCode, @Param("uid") Integer uid);
|
||||
|
||||
List<RoleUkeyBindingEntity> selectActiveByRoleCode(@Param("roleCode") String roleCode);
|
||||
}
|
||||
|
||||
@ -2,11 +2,10 @@ package com.cisd.tms.modules.auth.repository;
|
||||
|
||||
import com.cisd.tms.modules.auth.entity.RoleUkeyBindingEntity;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface RoleUkeyBindingRepository {
|
||||
|
||||
Optional<RoleUkeyBindingEntity> findActiveByRoleCodeAndUid(String roleCode, Integer uid);
|
||||
List<RoleUkeyBindingEntity> findActiveByRoleCodeAndUid(String roleCode, Integer uid);
|
||||
|
||||
List<RoleUkeyBindingEntity> findActiveByRoleCode(String roleCode);
|
||||
|
||||
|
||||
@ -4,7 +4,6 @@ import com.cisd.tms.modules.auth.entity.RoleUkeyBindingEntity;
|
||||
import com.cisd.tms.modules.auth.mapper.RoleUkeyBindingMapper;
|
||||
import com.cisd.tms.modules.auth.repository.RoleUkeyBindingRepository;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
@ -17,8 +16,8 @@ public class RoleUkeyBindingRepositoryImpl implements RoleUkeyBindingRepository
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<RoleUkeyBindingEntity> findActiveByRoleCodeAndUid(String roleCode, Integer uid) {
|
||||
return Optional.ofNullable(roleUkeyBindingMapper.selectActiveByRoleCodeAndUid(roleCode, uid));
|
||||
public List<RoleUkeyBindingEntity> findActiveByRoleCodeAndUid(String roleCode, Integer uid) {
|
||||
return roleUkeyBindingMapper.selectActiveByRoleCodeAndUid(roleCode, uid);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -27,26 +27,6 @@ public interface AuthAdminService {
|
||||
String newPassword
|
||||
);
|
||||
|
||||
void bindUkey(
|
||||
String operatorRoleCode,
|
||||
String operatorAuthLevel,
|
||||
String targetRoleCode,
|
||||
Integer uid,
|
||||
String ukeySerial,
|
||||
String ukeyPubkey,
|
||||
String issuerSign
|
||||
);
|
||||
|
||||
void bindIssuedUkey(
|
||||
String operatorRoleCode,
|
||||
String operatorAuthLevel,
|
||||
String targetRoleCode,
|
||||
Integer uid,
|
||||
String ukeySerial,
|
||||
String ukeyPubkey,
|
||||
String issuerSign
|
||||
);
|
||||
|
||||
UKeySignResult issueUkeyBindingSign(
|
||||
String operatorRoleCode,
|
||||
String operatorAuthLevel,
|
||||
|
||||
@ -174,10 +174,7 @@ public class AuthAdminServiceImpl implements AuthAdminService {
|
||||
authUserAccountRepository.update(account);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindUkey(
|
||||
String operatorRoleCode,
|
||||
String operatorAuthLevel,
|
||||
private void bindUkey(
|
||||
String targetRoleCode,
|
||||
Integer uid,
|
||||
String ukeySerial,
|
||||
@ -185,15 +182,11 @@ public class AuthAdminServiceImpl implements AuthAdminService {
|
||||
String issuerSign
|
||||
) {
|
||||
RoleCode targetRole = resolveRoleCode(targetRoleCode);
|
||||
if (uid == null || uid < 1 || uid > targetRole.getRequiredUkeyCount()) {
|
||||
throw new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "uid exceeds role ukey requirement");
|
||||
}
|
||||
validateRoleUid(targetRole, uid);
|
||||
authFullAccountRepository.findByRoleCodeAndUid(targetRoleCode, uid)
|
||||
.orElseThrow(() -> new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "target role full account not found"));
|
||||
|
||||
RoleUkeyBindingEntity binding = roleUkeyBindingRepository
|
||||
.findActiveByRoleCodeAndUid(targetRoleCode, uid)
|
||||
.orElseGet(RoleUkeyBindingEntity::new);
|
||||
RoleUkeyBindingEntity binding = findExistingActiveBinding(targetRoleCode, uid, ukeySerial, ukeyPubkey);
|
||||
if (binding.getId() == null) {
|
||||
binding.setId((long) Math.abs(Objects.hash(targetRoleCode, uid, ukeySerial, TraceIdUtil.newTraceId())));
|
||||
binding.setRoleCode(targetRoleCode);
|
||||
@ -208,19 +201,17 @@ public class AuthAdminServiceImpl implements AuthAdminService {
|
||||
roleUkeyBindingRepository.update(binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindIssuedUkey(
|
||||
String operatorRoleCode,
|
||||
String operatorAuthLevel,
|
||||
String targetRoleCode,
|
||||
private RoleUkeyBindingEntity findExistingActiveBinding(
|
||||
String roleCode,
|
||||
Integer uid,
|
||||
String ukeySerial,
|
||||
String ukeyPubkey,
|
||||
String issuerSign
|
||||
String ukeyPubkey
|
||||
) {
|
||||
resolveRoleCode(targetRoleCode);
|
||||
verifyIssuerSignature(targetRoleCode, ukeyPubkey, uid, issuerSign);
|
||||
bindUkey(operatorRoleCode, operatorAuthLevel, targetRoleCode, uid, ukeySerial, ukeyPubkey, issuerSign);
|
||||
return roleUkeyBindingRepository.findActiveByRoleCodeAndUid(roleCode, uid).stream()
|
||||
.filter(binding -> Objects.equals(binding.getUkeySerial(), ukeySerial)
|
||||
|| Objects.equals(binding.getUkeyPubkey(), ukeyPubkey))
|
||||
.findFirst()
|
||||
.orElseGet(RoleUkeyBindingEntity::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -231,18 +222,28 @@ public class AuthAdminServiceImpl implements AuthAdminService {
|
||||
UKeySignDTO request
|
||||
) {
|
||||
RoleCode targetRole = resolveRoleCode(targetRoleCode);
|
||||
validateRoleUid(targetRole, request.getUid());
|
||||
String authKeyPair = lmkService.exportIkPublicKeyHex();
|
||||
|
||||
UKeySignDTO dto = new UKeySignDTO();
|
||||
dto.setPubKey(request.getPubKey());
|
||||
dto.setRole(targetRole.getCode());
|
||||
dto.setUid(request.getUid());
|
||||
|
||||
String signValue = lmkService.signIk(toIssuePayload(dto, authKeyPair));
|
||||
String signValue = lmkService.signIk(toIssuePayload(dto, authKeyPair, targetRole.getCode()));
|
||||
MasterKeyBackupPacket backupPacket = null;
|
||||
if (request.getUkeySerial() == null || request.getUkeySerial().isBlank()) {
|
||||
throw new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "ukeySerial is required");
|
||||
}
|
||||
if (RoleCode.SUPER_ADMIN == targetRole) {
|
||||
backupPacket = lmkService.buildBackupPacket(request.getUid());
|
||||
}
|
||||
bindUkey(
|
||||
targetRole.getCode(),
|
||||
request.getUid(),
|
||||
request.getUkeySerial(),
|
||||
request.getPubKey(),
|
||||
signValue
|
||||
);
|
||||
return UKeySignResult.builder()
|
||||
.sign(signValue)
|
||||
.backupPacket(backupPacket)
|
||||
@ -295,6 +296,12 @@ public class AuthAdminServiceImpl implements AuthAdminService {
|
||||
.orElseThrow(() -> new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "target role account not found"));
|
||||
}
|
||||
|
||||
private void validateRoleUid(RoleCode targetRole, Integer uid) {
|
||||
if (uid == null || uid < 1 || uid > targetRole.getRequiredUkeyCount()) {
|
||||
throw new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "uid exceeds role ukey requirement");
|
||||
}
|
||||
}
|
||||
|
||||
private RoleCode resolveRoleCode(String roleCode) {
|
||||
return List.of(RoleCode.values()).stream()
|
||||
.filter(item -> item.getCode().equals(roleCode))
|
||||
@ -306,24 +313,9 @@ public class AuthAdminServiceImpl implements AuthAdminService {
|
||||
return LocalDateTime.ofInstant(clock.instant(), ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
private void verifyIssuerSignature(String targetRoleCode, String ukeyPubkey, Integer uid, String issuerSign) {
|
||||
private String toIssuePayload(UKeySignDTO dto, String authKeyPair, String roleCode) {
|
||||
try {
|
||||
UKeySignDTO dto = new UKeySignDTO();
|
||||
dto.setPubKey(ukeyPubkey);
|
||||
dto.setRole(resolveRoleCode(targetRoleCode).getCode());
|
||||
dto.setUid(uid);
|
||||
String authKeyPair = lmkService.exportIkPublicKeyHex();
|
||||
lmkService.verifyIk(toIssuePayload(dto, authKeyPair), issuerSign);
|
||||
} catch (BizException ex) {
|
||||
throw ex;
|
||||
} catch (RuntimeException ex) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey issuer signature verification failed");
|
||||
}
|
||||
}
|
||||
|
||||
private String toIssuePayload(UKeySignDTO dto, String authKeyPair) {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(UKeySignEntity.getInstance(dto, authKeyPair));
|
||||
return objectMapper.writeValueAsString(UKeySignEntity.getInstance(dto, authKeyPair, roleCode));
|
||||
} catch (JsonProcessingException ex) {
|
||||
throw new IllegalStateException("serialize ukey issue payload failed", ex);
|
||||
}
|
||||
|
||||
@ -152,14 +152,8 @@ public class AuthServiceImpl implements AuthService {
|
||||
RoleCode roleCode = RoleCode.valueOf(request.getRoleCode());
|
||||
List<RoleUkeyBindingEntity> activeBindings = roleUkeyBindingRepository.findActiveByRoleCode(roleCode.getCode());
|
||||
validateUkeyCount(roleCode, activeBindings, request.getLoginFactors());
|
||||
Map<Integer, RoleUkeyBindingEntity> bindingsByUid = activeBindings.stream()
|
||||
.collect(Collectors.toMap(RoleUkeyBindingEntity::getUid, item -> item, (left, right) -> left, java.util.LinkedHashMap::new));
|
||||
Set<Integer> requestUids = request.getLoginFactors().stream()
|
||||
.map(UkeyLoginProof::getUid)
|
||||
.collect(Collectors.toSet());
|
||||
if (requestUids.size() != request.getLoginFactors().size() || !bindingsByUid.keySet().containsAll(requestUids)) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey auth info does not match bound role");
|
||||
}
|
||||
Map<Integer, List<RoleUkeyBindingEntity>> bindingsByUid = activeBindings.stream()
|
||||
.collect(Collectors.groupingBy(RoleUkeyBindingEntity::getUid));
|
||||
ukeyLoginRandomService.assertIssued(
|
||||
roleCode.getCode(),
|
||||
request.getLoginFactors().stream().map(UkeyLoginProof::getServerRandom).toList()
|
||||
@ -167,10 +161,10 @@ public class AuthServiceImpl implements AuthService {
|
||||
String authKeyPair = lmkService.exportIkPublicKeyHex();
|
||||
List<String> matchedSerials = new ArrayList<>();
|
||||
for (UkeyLoginProof proof : request.getLoginFactors()) {
|
||||
RoleUkeyBindingEntity binding = bindingsByUid.get(proof.getUid());
|
||||
if (binding == null || !binding.getUkeyPubkey().equals(proof.getPubKey())) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey auth info does not match bound role");
|
||||
}
|
||||
RoleUkeyBindingEntity binding = bindingsByUid.getOrDefault(proof.getUid(), List.of()).stream()
|
||||
.filter(item -> item.getUkeyPubkey().equals(proof.getPubKey()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey auth info does not match bound role"));
|
||||
compatUkeyVerifier.verifyIssuedBinding(buildIssuePayload(request.getRoleCode(), proof, authKeyPair), proof.getIssueSignature());
|
||||
compatUkeyVerifier.verifyLoginSignature(proof.getPubKey(), proof.getLoginPayload(), proof.getLoginSignature());
|
||||
matchedSerials.add(binding.getUkeySerial());
|
||||
@ -503,16 +497,20 @@ public class AuthServiceImpl implements AuthService {
|
||||
}
|
||||
|
||||
List<RoleUkeyBindingEntity> bindings = roleUkeyBindingRepository.findActiveByRoleCode(roleAccount.getRoleCode());
|
||||
if (bindings.size() != requiredUkeyCount) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey count does not satisfy role requirement");
|
||||
}
|
||||
|
||||
Set<String> boundSerials = bindings.stream()
|
||||
.map(RoleUkeyBindingEntity::getUkeySerial)
|
||||
.collect(Collectors.toSet());
|
||||
if (!boundSerials.containsAll(uniqueSerials)) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey verification failed");
|
||||
}
|
||||
long matchedUidCount = bindings.stream()
|
||||
.filter(binding -> uniqueSerials.contains(binding.getUkeySerial()))
|
||||
.map(RoleUkeyBindingEntity::getUid)
|
||||
.distinct()
|
||||
.count();
|
||||
if (matchedUidCount != requiredUkeyCount) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey count does not satisfy role requirement");
|
||||
}
|
||||
return authPolicyService.resolveAuthLevel(AuthMethod.UKEY);
|
||||
}
|
||||
|
||||
@ -524,7 +522,17 @@ public class AuthServiceImpl implements AuthService {
|
||||
if (proofs == null || proofs.size() != authPolicyService.requiredUkeyCount(roleCode)) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey count does not satisfy role requirement");
|
||||
}
|
||||
if (activeBindings.size() != authPolicyService.requiredUkeyCount(roleCode)) {
|
||||
Set<Integer> requestUids = proofs.stream()
|
||||
.map(UkeyLoginProof::getUid)
|
||||
.collect(Collectors.toSet());
|
||||
int requiredCount = authPolicyService.requiredUkeyCount(roleCode);
|
||||
if (requestUids.size() != requiredCount) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey count does not satisfy role requirement");
|
||||
}
|
||||
Set<Integer> activeUids = activeBindings.stream()
|
||||
.map(RoleUkeyBindingEntity::getUid)
|
||||
.collect(Collectors.toSet());
|
||||
if (!activeUids.containsAll(requestUids)) {
|
||||
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey count does not satisfy role requirement");
|
||||
}
|
||||
}
|
||||
@ -532,10 +540,9 @@ public class AuthServiceImpl implements AuthService {
|
||||
private String buildIssuePayload(String roleCode, UkeyLoginProof proof, String authKeyPair) {
|
||||
UKeySignDTO dto = new UKeySignDTO();
|
||||
dto.setPubKey(proof.getPubKey());
|
||||
dto.setRole(RoleCode.valueOf(roleCode).getCode());
|
||||
dto.setUid(proof.getUid());
|
||||
try {
|
||||
return objectMapper.writeValueAsString(UKeySignEntity.getInstance(dto, authKeyPair));
|
||||
return objectMapper.writeValueAsString(UKeySignEntity.getInstance(dto, authKeyPair, RoleCode.valueOf(roleCode).getCode()));
|
||||
} catch (JsonProcessingException ex) {
|
||||
throw new IllegalStateException("serialize ukey issue payload failed", ex);
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package com.cisd.tms.modules.mk.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
@ -10,16 +12,19 @@ public class UKeySignDTO {
|
||||
/**
|
||||
* UKey 公钥
|
||||
*/
|
||||
@NotBlank(message = "pubKey is required")
|
||||
private String pubKey;
|
||||
|
||||
/**
|
||||
* 签名的角色
|
||||
* UKey 序列号。发行签名接口会直接完成绑定,因此必传。
|
||||
*/
|
||||
private String role;
|
||||
@NotBlank(message = "ukeySerial is required")
|
||||
private String ukeySerial;
|
||||
|
||||
/**
|
||||
* 角色内固定席位编号。
|
||||
*/
|
||||
@NotNull(message = "uid is required")
|
||||
private Integer uid;
|
||||
|
||||
}
|
||||
|
||||
@ -27,11 +27,11 @@ public class UKeySignEntity {
|
||||
*/
|
||||
private Integer uid;
|
||||
|
||||
public static UKeySignEntity getInstance(UKeySignDTO uKeySignDTO,String authKeyPair){
|
||||
public static UKeySignEntity getInstance(UKeySignDTO uKeySignDTO, String authKeyPair, String role) {
|
||||
return UKeySignEntity.builder()
|
||||
.pubKey(uKeySignDTO.getPubKey())
|
||||
.authKeyPair(authKeyPair)
|
||||
.role(uKeySignDTO.getRole())
|
||||
.role(role)
|
||||
.uid(uKeySignDTO.getUid())
|
||||
.build();
|
||||
}
|
||||
|
||||
@ -262,7 +262,8 @@ CREATE TABLE IF NOT EXISTS tms_role_ukey_binding (
|
||||
unbound_at DATETIME(3) NULL,
|
||||
create_time DATETIME(3) NOT NULL,
|
||||
update_time DATETIME(3) NOT NULL,
|
||||
UNIQUE KEY uk_tms_role_ukey_binding_role_uid (role_code, uid),
|
||||
UNIQUE KEY uk_tms_role_ukey_binding_role_uid_serial (role_code, uid, ukey_serial),
|
||||
KEY idx_tms_role_ukey_binding_role_uid_status (role_code, uid, status),
|
||||
KEY idx_tms_role_ukey_binding_role_status (role_code, status)
|
||||
);
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
WHERE role_code = #{roleCode}
|
||||
AND uid = #{uid}
|
||||
AND status = 'ACTIVE'
|
||||
LIMIT 1
|
||||
ORDER BY bound_at DESC, id DESC
|
||||
</select>
|
||||
|
||||
<select id="selectActiveByRoleCode" parameterType="string" resultMap="RoleUkeyBindingResultMap">
|
||||
|
||||
@ -267,34 +267,6 @@ class AuthControllerTest {
|
||||
Mockito.verify(authAdminService).enableRole("KEY_ADMIN", "FULL", "AUDIT_ADMIN");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBindUkeyThroughAdminEndpoint() throws Exception {
|
||||
AuthService authService = Mockito.mock(AuthService.class);
|
||||
AuthAdminService authAdminService = Mockito.mock(AuthAdminService.class);
|
||||
|
||||
MockMvc mockMvc = MockMvcBuilders
|
||||
.standaloneSetup(new AuthController(authService), new AuthAdminController(authAdminService))
|
||||
.setControllerAdvice(new GlobalExceptionHandler())
|
||||
.build();
|
||||
|
||||
mockMvc.perform(post("/api/v1/auth/roles/SUPER_ADMIN/ukeys/bind")
|
||||
.requestAttr(InternalApiAuthInterceptor.ATTR_ROLE_CODE, "KEY_ADMIN")
|
||||
.requestAttr(InternalApiAuthInterceptor.ATTR_AUTH_LEVEL, "FULL")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"uid": 1,
|
||||
"ukeySerial": "UK-NEW",
|
||||
"pubKey": "PUB-NEW",
|
||||
"issuerSignature": "SIG-NEW"
|
||||
}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("\"success\":true")));
|
||||
|
||||
Mockito.verify(authAdminService).bindIssuedUkey("KEY_ADMIN", "FULL", "SUPER_ADMIN", 1, "UK-NEW", "PUB-NEW", "SIG-NEW");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldChangeFullAccountPasswordThroughAdminEndpoint() throws Exception {
|
||||
AuthService authService = Mockito.mock(AuthService.class);
|
||||
@ -348,15 +320,15 @@ class AuthControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldIssueUkeyBindingSignThroughAdminEndpoint() throws Exception {
|
||||
void shouldIssueSuperAdminUkeyBindingSignWithoutLoginContext() throws Exception {
|
||||
AuthService authService = Mockito.mock(AuthService.class);
|
||||
AuthAdminService authAdminService = Mockito.mock(AuthAdminService.class);
|
||||
MasterKeyBackupPacket packet = new MasterKeyBackupPacket();
|
||||
packet.setPacketIndex(1);
|
||||
UKeySignResult response = UKeySignResult.builder().sign("ISSUE-SIGN-001").backupPacket(packet).build();
|
||||
Mockito.when(authAdminService.issueUkeyBindingSign(
|
||||
ArgumentMatchers.eq("KEY_ADMIN"),
|
||||
ArgumentMatchers.eq("FULL"),
|
||||
ArgumentMatchers.isNull(),
|
||||
ArgumentMatchers.isNull(),
|
||||
ArgumentMatchers.eq("SUPER_ADMIN"),
|
||||
ArgumentMatchers.any()
|
||||
)).thenReturn(response);
|
||||
@ -366,14 +338,12 @@ class AuthControllerTest {
|
||||
.setControllerAdvice(new GlobalExceptionHandler())
|
||||
.build();
|
||||
|
||||
mockMvc.perform(post("/api/v1/auth/roles/SUPER_ADMIN/ukeys/issue-sign")
|
||||
.requestAttr(InternalApiAuthInterceptor.ATTR_ROLE_CODE, "KEY_ADMIN")
|
||||
.requestAttr(InternalApiAuthInterceptor.ATTR_AUTH_LEVEL, "FULL")
|
||||
mockMvc.perform(post("/api/v1/auth/super-admin/ukeys/issue-sign")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"pubKey": "PUB-NEW",
|
||||
"role": "SUPER_ADMIN",
|
||||
"ukeySerial": "UK-NEW",
|
||||
"uid": 1,
|
||||
"extra": "EXTRA-001"
|
||||
}
|
||||
@ -382,4 +352,36 @@ class AuthControllerTest {
|
||||
.andExpect(content().string(containsString("\"sign\":\"ISSUE-SIGN-001\"")))
|
||||
.andExpect(content().string(containsString("\"packetIndex\":1")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldIssueNormalAdminUkeyBindingSignThroughAdminEndpoint() throws Exception {
|
||||
AuthService authService = Mockito.mock(AuthService.class);
|
||||
AuthAdminService authAdminService = Mockito.mock(AuthAdminService.class);
|
||||
UKeySignResult response = UKeySignResult.builder().sign("ISSUE-SIGN-KEY-001").build();
|
||||
Mockito.when(authAdminService.issueUkeyBindingSign(
|
||||
ArgumentMatchers.eq("SUPER_ADMIN"),
|
||||
ArgumentMatchers.eq("FULL"),
|
||||
ArgumentMatchers.eq("KEY_ADMIN"),
|
||||
ArgumentMatchers.any()
|
||||
)).thenReturn(response);
|
||||
|
||||
MockMvc mockMvc = MockMvcBuilders
|
||||
.standaloneSetup(new AuthController(authService), new AuthAdminController(authAdminService))
|
||||
.setControllerAdvice(new GlobalExceptionHandler())
|
||||
.build();
|
||||
|
||||
mockMvc.perform(post("/api/v1/auth/roles/KEY_ADMIN/ukeys/issue-sign")
|
||||
.requestAttr(InternalApiAuthInterceptor.ATTR_ROLE_CODE, "SUPER_ADMIN")
|
||||
.requestAttr(InternalApiAuthInterceptor.ATTR_AUTH_LEVEL, "FULL")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"pubKey": "PUB-KEY",
|
||||
"ukeySerial": "UK-KEY",
|
||||
"uid": 1
|
||||
}
|
||||
"""))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("\"sign\":\"ISSUE-SIGN-KEY-001\"")));
|
||||
}
|
||||
}
|
||||
|
||||
@ -277,11 +277,15 @@ class AuthAdminServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReplaceActiveBindingInSameUidSeat() {
|
||||
void shouldUpdateActiveBindingWhenIssueSignUsesSameSerial() {
|
||||
InMemoryRoleUkeyBindingRepository bindings = new InMemoryRoleUkeyBindingRepository();
|
||||
bindings.save(binding(RoleCode.SUPER_ADMIN, 1, "UK-OLD", "PUB-OLD", "SIG-OLD"));
|
||||
LmkService lmkService = Mockito.mock(LmkService.class);
|
||||
Mockito.when(lmkService.exportIkPublicKeyHex()).thenReturn("IK-PUB-001");
|
||||
Mockito.when(lmkService.signIk(org.mockito.ArgumentMatchers.anyString())).thenReturn("SIG-NEW");
|
||||
com.cisd.tms.modules.mk.dto.MasterKeyBackupPacket packet = new com.cisd.tms.modules.mk.dto.MasterKeyBackupPacket();
|
||||
packet.setPacketIndex(1);
|
||||
Mockito.when(lmkService.buildBackupPacket(1)).thenReturn(packet);
|
||||
|
||||
AuthAdminService service = new AuthAdminServiceImpl(
|
||||
new InMemoryRoleAccountRepository(),
|
||||
@ -295,19 +299,25 @@ class AuthAdminServiceTest {
|
||||
new FixedSaltSupplier("salt-001")
|
||||
);
|
||||
|
||||
service.bindIssuedUkey(
|
||||
RoleCode.KEY_ADMIN.getCode(),
|
||||
AuthLevel.FULL.name(),
|
||||
UKeySignDTO dto = new UKeySignDTO();
|
||||
dto.setPubKey("PUB-NEW");
|
||||
dto.setUkeySerial("UK-OLD");
|
||||
dto.setUid(1);
|
||||
|
||||
service.issueUkeyBindingSign(
|
||||
null,
|
||||
null,
|
||||
RoleCode.SUPER_ADMIN.getCode(),
|
||||
1,
|
||||
"UK-NEW",
|
||||
"PUB-NEW",
|
||||
"SIG-NEW"
|
||||
dto
|
||||
);
|
||||
|
||||
RoleUkeyBindingEntity updated = bindings.findActiveByRoleCodeAndUid(RoleCode.SUPER_ADMIN.getCode(), 1).orElseThrow();
|
||||
Assertions.assertEquals("UK-NEW", updated.getUkeySerial());
|
||||
RoleUkeyBindingEntity updated = bindings.findActiveByRoleCodeAndUid(RoleCode.SUPER_ADMIN.getCode(), 1).stream()
|
||||
.filter(binding -> "UK-OLD".equals(binding.getUkeySerial()))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
Assertions.assertEquals("UK-OLD", updated.getUkeySerial());
|
||||
Assertions.assertEquals("PUB-NEW", updated.getUkeyPubkey());
|
||||
Assertions.assertEquals("SIG-NEW", updated.getIssuerSign());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -321,7 +331,7 @@ class AuthAdminServiceTest {
|
||||
|
||||
AuthAdminService service = new AuthAdminServiceImpl(
|
||||
new InMemoryRoleAccountRepository(),
|
||||
fullAccountRepository(),
|
||||
fullAccountRepository(binding(RoleCode.SUPER_ADMIN, 1, "UK-001", "PUB-OLD", "SIG-OLD")),
|
||||
new InMemoryAuthUserAccountRepository(),
|
||||
new InMemoryRoleUkeyBindingRepository(),
|
||||
new FakePasswordHasher(),
|
||||
@ -332,6 +342,7 @@ class AuthAdminServiceTest {
|
||||
);
|
||||
UKeySignDTO dto = new UKeySignDTO();
|
||||
dto.setPubKey("PUB-001");
|
||||
dto.setUkeySerial("UK-001");
|
||||
dto.setUid(1);
|
||||
|
||||
UKeySignResult result = service.issueUkeyBindingSign(RoleCode.KEY_ADMIN.getCode(), AuthLevel.FULL.name(), RoleCode.SUPER_ADMIN.getCode(), dto);
|
||||
@ -341,6 +352,90 @@ class AuthAdminServiceTest {
|
||||
Assertions.assertEquals(1, result.getBackupPacket().getPacketIndex());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldIssueAndBindNormalRoleUkeyInOneStep() {
|
||||
InMemoryRoleUkeyBindingRepository bindings = new InMemoryRoleUkeyBindingRepository();
|
||||
LmkService lmkService = Mockito.mock(LmkService.class);
|
||||
Mockito.when(lmkService.exportIkPublicKeyHex()).thenReturn("IK-PUB-001");
|
||||
Mockito.when(lmkService.signIk(org.mockito.ArgumentMatchers.anyString())).thenReturn("ISSUE-SIGN-KEY-001");
|
||||
|
||||
AuthAdminService service = new AuthAdminServiceImpl(
|
||||
new InMemoryRoleAccountRepository(),
|
||||
fullAccountRepository(binding(RoleCode.KEY_ADMIN, 1, "UK-KEY-001", "PUB-OLD", "SIG-OLD")),
|
||||
new InMemoryAuthUserAccountRepository(),
|
||||
bindings,
|
||||
new FakePasswordHasher(),
|
||||
lmkService,
|
||||
new com.fasterxml.jackson.databind.ObjectMapper(),
|
||||
FIXED_CLOCK,
|
||||
new FixedSaltSupplier("salt-001")
|
||||
);
|
||||
UKeySignDTO dto = new UKeySignDTO();
|
||||
dto.setPubKey("PUB-KEY-001");
|
||||
dto.setUkeySerial("UK-KEY-001");
|
||||
dto.setUid(1);
|
||||
|
||||
UKeySignResult result = service.issueUkeyBindingSign(
|
||||
RoleCode.SUPER_ADMIN.getCode(),
|
||||
AuthLevel.FULL.name(),
|
||||
RoleCode.KEY_ADMIN.getCode(),
|
||||
dto
|
||||
);
|
||||
|
||||
Assertions.assertEquals("ISSUE-SIGN-KEY-001", result.getSign());
|
||||
Assertions.assertNull(result.getBackupPacket());
|
||||
RoleUkeyBindingEntity binding = bindings.findActiveByRoleCodeAndUid(RoleCode.KEY_ADMIN.getCode(), 1).stream()
|
||||
.filter(item -> "UK-KEY-001".equals(item.getUkeySerial()))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
Assertions.assertEquals("PUB-KEY-001", binding.getUkeyPubkey());
|
||||
Assertions.assertEquals("ISSUE-SIGN-KEY-001", binding.getIssuerSign());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldIssueSuperAdminBackupUkeyWithoutReplacingExistingBindingForSameUid() {
|
||||
InMemoryRoleUkeyBindingRepository bindings = new InMemoryRoleUkeyBindingRepository();
|
||||
bindings.save(binding(RoleCode.SUPER_ADMIN, 1, "UK-PRIMARY", "PUB-PRIMARY", "SIG-PRIMARY"));
|
||||
LmkService lmkService = Mockito.mock(LmkService.class);
|
||||
Mockito.when(lmkService.exportIkPublicKeyHex()).thenReturn("IK-PUB-001");
|
||||
Mockito.when(lmkService.signIk(org.mockito.ArgumentMatchers.anyString())).thenReturn("ISSUE-SIGN-BACKUP");
|
||||
com.cisd.tms.modules.mk.dto.MasterKeyBackupPacket packet = new com.cisd.tms.modules.mk.dto.MasterKeyBackupPacket();
|
||||
packet.setPacketIndex(1);
|
||||
Mockito.when(lmkService.buildBackupPacket(1)).thenReturn(packet);
|
||||
|
||||
AuthAdminService service = new AuthAdminServiceImpl(
|
||||
new InMemoryRoleAccountRepository(),
|
||||
fullAccountRepository(binding(RoleCode.SUPER_ADMIN, 1, "UK-PRIMARY", "PUB-PRIMARY", "SIG-PRIMARY")),
|
||||
new InMemoryAuthUserAccountRepository(),
|
||||
bindings,
|
||||
new FakePasswordHasher(),
|
||||
lmkService,
|
||||
new com.fasterxml.jackson.databind.ObjectMapper(),
|
||||
FIXED_CLOCK,
|
||||
new FixedSaltSupplier("salt-001")
|
||||
);
|
||||
UKeySignDTO dto = new UKeySignDTO();
|
||||
dto.setPubKey("PUB-BACKUP");
|
||||
dto.setUkeySerial("UK-BACKUP");
|
||||
dto.setUid(1);
|
||||
|
||||
UKeySignResult result = service.issueUkeyBindingSign(
|
||||
RoleCode.KEY_ADMIN.getCode(),
|
||||
AuthLevel.FULL.name(),
|
||||
RoleCode.SUPER_ADMIN.getCode(),
|
||||
dto
|
||||
);
|
||||
|
||||
Assertions.assertEquals("ISSUE-SIGN-BACKUP", result.getSign());
|
||||
List<RoleUkeyBindingEntity> activeUidBindings = bindings.findActiveByRoleCodeAndUid(RoleCode.SUPER_ADMIN.getCode(), 1);
|
||||
Assertions.assertEquals(2, activeUidBindings.size());
|
||||
Assertions.assertTrue(activeUidBindings.stream().anyMatch(binding -> "UK-PRIMARY".equals(binding.getUkeySerial())
|
||||
&& "PUB-PRIMARY".equals(binding.getUkeyPubkey())));
|
||||
Assertions.assertTrue(activeUidBindings.stream().anyMatch(binding -> "UK-BACKUP".equals(binding.getUkeySerial())
|
||||
&& "PUB-BACKUP".equals(binding.getUkeyPubkey())
|
||||
&& "ISSUE-SIGN-BACKUP".equals(binding.getIssuerSign())));
|
||||
}
|
||||
|
||||
private static RoleAccountEntity role(RoleCode roleCode, RoleAccountStatus status) {
|
||||
RoleAccountEntity entity = new RoleAccountEntity();
|
||||
entity.setId((long) roleCode.ordinal() + 100);
|
||||
@ -504,11 +599,12 @@ class AuthAdminServiceTest {
|
||||
private final Map<String, List<RoleUkeyBindingEntity>> store = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public Optional<RoleUkeyBindingEntity> findActiveByRoleCodeAndUid(String roleCode, Integer uid) {
|
||||
public List<RoleUkeyBindingEntity> findActiveByRoleCodeAndUid(String roleCode, Integer uid) {
|
||||
return store.getOrDefault(roleCode, List.of()).stream()
|
||||
.filter(entity -> uid.equals(entity.getUid()))
|
||||
.filter(entity -> "ACTIVE".equals(entity.getStatus()))
|
||||
.findFirst();
|
||||
.sorted(Comparator.comparing(RoleUkeyBindingEntity::getBoundAt).reversed())
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -694,6 +694,68 @@ class AuthServiceTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLoginSuperAdminWithBackupUkeysBoundToSameUidSeats() {
|
||||
InMemoryRoleAccountRepository roleAccounts = new InMemoryRoleAccountRepository();
|
||||
InMemoryAuthUserAccountRepository userAccounts = new InMemoryAuthUserAccountRepository();
|
||||
InMemoryAuthFullAccountRepository fullAccounts = new InMemoryAuthFullAccountRepository();
|
||||
InMemoryAuthSessionRepository sessions = new InMemoryAuthSessionRepository();
|
||||
InMemoryRoleUkeyBindingRepository ukeyBindings = new InMemoryRoleUkeyBindingRepository();
|
||||
roleAccounts.save(activeRole(RoleCode.SUPER_ADMIN));
|
||||
fullAccounts.save(activeFullAccount(RoleCode.SUPER_ADMIN, 1, "super-admin-full-01", "11111111", "SALT-S1", 0, null));
|
||||
fullAccounts.save(activeFullAccount(RoleCode.SUPER_ADMIN, 2, "super-admin-full-02", "22222222", "SALT-S2", 0, null));
|
||||
ukeyBindings.save(activeBinding(RoleCode.SUPER_ADMIN, 1, "UK-1", "PUB-1"));
|
||||
ukeyBindings.save(activeBinding(RoleCode.SUPER_ADMIN, 1, "UK-1-BACKUP", "PUB-1-BACKUP"));
|
||||
ukeyBindings.save(activeBinding(RoleCode.SUPER_ADMIN, 2, "UK-2", "PUB-2"));
|
||||
ukeyBindings.save(activeBinding(RoleCode.SUPER_ADMIN, 2, "UK-2-BACKUP", "PUB-2-BACKUP"));
|
||||
|
||||
LmkService lmkService = org.mockito.Mockito.mock(LmkService.class);
|
||||
UkeyLoginRandomService randomService = org.mockito.Mockito.mock(UkeyLoginRandomService.class);
|
||||
CompatUkeyVerifier verifier = org.mockito.Mockito.mock(CompatUkeyVerifier.class);
|
||||
org.mockito.Mockito.when(lmkService.getMasterKeyStatus()).thenReturn(MasterKeyStatus.NORMAL.getDetail("MAC-001"));
|
||||
org.mockito.Mockito.when(lmkService.exportIkPublicKeyHex()).thenReturn("IK-PUB-001");
|
||||
|
||||
AuthService service = new AuthServiceImpl(
|
||||
roleAccounts,
|
||||
new InMemoryLegacyAuthUserRepository(),
|
||||
userAccounts,
|
||||
fullAccounts,
|
||||
sessions,
|
||||
ukeyBindings,
|
||||
new FakePasswordHasher(),
|
||||
lmkService,
|
||||
randomService,
|
||||
verifier,
|
||||
new InMemoryCaptchaService(),
|
||||
new ObjectMapper(),
|
||||
FIXED_CLOCK,
|
||||
() -> "token-full-super-backup-001",
|
||||
() -> "salt-full-super-backup-001",
|
||||
new AuthPolicyServiceImpl()
|
||||
);
|
||||
|
||||
UkeyLoginRequest request = new UkeyLoginRequest();
|
||||
request.setRoleCode(RoleCode.SUPER_ADMIN.getCode());
|
||||
request.setLoginFactors(List.of(
|
||||
proofWithPassword("PUB-1-BACKUP", 1, "11111111", "RB-1", "ISSUE-1B", "LOGIN-DATA-1B", "LOGIN-SIGN-1B"),
|
||||
proofWithPassword("PUB-2-BACKUP", 2, "22222222", "RB-2", "ISSUE-2B", "LOGIN-DATA-2B", "LOGIN-SIGN-2B")
|
||||
));
|
||||
|
||||
LoginResponse response = service.ukeyLogin(request);
|
||||
|
||||
Assertions.assertEquals("token-full-super-backup-001", response.getToken());
|
||||
Assertions.assertEquals(AuthLevel.FULL.name(), response.getAuthLevel());
|
||||
org.mockito.Mockito.verify(randomService).assertIssued(RoleCode.SUPER_ADMIN.getCode(), List.of("RB-1", "RB-2"));
|
||||
org.mockito.Mockito.verify(verifier).verifyIssuedBinding(
|
||||
"{\"pubKey\":\"PUB-1-BACKUP\",\"authKeyPair\":\"IK-PUB-001\",\"role\":\"SUPER_ADMIN\",\"uid\":1}",
|
||||
"ISSUE-1B"
|
||||
);
|
||||
org.mockito.Mockito.verify(verifier).verifyIssuedBinding(
|
||||
"{\"pubKey\":\"PUB-2-BACKUP\",\"authKeyPair\":\"IK-PUB-001\",\"role\":\"SUPER_ADMIN\",\"uid\":2}",
|
||||
"ISSUE-2B"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLockOnlyMappedFullAccountDuringSuperAdminUkeyLoginWhenPasswordIsWrong() {
|
||||
InMemoryRoleAccountRepository roleAccounts = new InMemoryRoleAccountRepository();
|
||||
@ -1096,11 +1158,12 @@ class AuthServiceTest {
|
||||
private final Map<String, List<RoleUkeyBindingEntity>> store = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public Optional<RoleUkeyBindingEntity> findActiveByRoleCodeAndUid(String roleCode, Integer uid) {
|
||||
public List<RoleUkeyBindingEntity> findActiveByRoleCodeAndUid(String roleCode, Integer uid) {
|
||||
return store.getOrDefault(roleCode, List.of()).stream()
|
||||
.filter(entity -> uid.equals(entity.getUid()))
|
||||
.filter(entity -> "ACTIVE".equals(entity.getStatus()))
|
||||
.findFirst();
|
||||
.sorted(Comparator.comparing(RoleUkeyBindingEntity::getBoundAt).reversed())
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -41,7 +41,15 @@ class ReplayProtectedEndpointsTest {
|
||||
String.class, com.cisd.tms.modules.auth.dto.ChangePasswordRequest.class, jakarta.servlet.http.HttpServletRequest.class));
|
||||
Assertions.assertNotNull(annotation(DeviceController.class, "restart", jakarta.servlet.http.HttpServletRequest.class));
|
||||
|
||||
Assertions.assertNotNull(AnnotatedElementUtils.findMergedAnnotation(AuthAdminController.class, ReplayProtected.class));
|
||||
Assertions.assertNull(AnnotatedElementUtils.findMergedAnnotation(AuthAdminController.class, ReplayProtected.class));
|
||||
Assertions.assertNotNull(effectiveAnnotation(AuthAdminController.class, "enableRole",
|
||||
String.class, jakarta.servlet.http.HttpServletRequest.class));
|
||||
Assertions.assertNotNull(effectiveAnnotation(AuthAdminController.class, "resetPassword",
|
||||
String.class, jakarta.servlet.http.HttpServletRequest.class));
|
||||
Assertions.assertNotNull(effectiveAnnotation(AuthAdminController.class, "issueUkeyBindingSign",
|
||||
String.class, com.cisd.tms.modules.mk.dto.UKeySignDTO.class, jakarta.servlet.http.HttpServletRequest.class));
|
||||
Assertions.assertNull(effectiveAnnotation(AuthAdminController.class, "issueSuperAdminUkeyBindingSign",
|
||||
com.cisd.tms.modules.mk.dto.UKeySignDTO.class));
|
||||
Assertions.assertNotNull(AnnotatedElementUtils.findMergedAnnotation(TimeConfigController.class, ReplayProtected.class));
|
||||
Assertions.assertNotNull(AnnotatedElementUtils.findMergedAnnotation(CryptoCardController.class, ReplayProtected.class));
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user