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