feat(audit): add SOC 2 audit logging to vendor admin, auth policy, email connector, and certificate operations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -102,15 +102,15 @@ public class CertificateController {
|
||||
}
|
||||
|
||||
@PostMapping("/activate")
|
||||
public ResponseEntity<Void> activate() {
|
||||
certificateService.activate();
|
||||
public ResponseEntity<Void> activate(@AuthenticationPrincipal Jwt jwt) {
|
||||
certificateService.activate(resolveActorId(jwt));
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@PostMapping("/restore")
|
||||
public ResponseEntity<Void> restore() {
|
||||
public ResponseEntity<Void> restore(@AuthenticationPrincipal Jwt jwt) {
|
||||
try {
|
||||
certificateService.restore();
|
||||
certificateService.restore(resolveActorId(jwt));
|
||||
return ResponseEntity.noContent().build();
|
||||
} catch (IllegalStateException e) {
|
||||
return ResponseEntity.badRequest().body(null);
|
||||
@@ -118,8 +118,8 @@ public class CertificateController {
|
||||
}
|
||||
|
||||
@DeleteMapping("/staged")
|
||||
public ResponseEntity<Void> discardStaged() {
|
||||
certificateService.discardStaged();
|
||||
public ResponseEntity<Void> discardStaged(@AuthenticationPrincipal Jwt jwt) {
|
||||
certificateService.discardStaged(resolveActorId(jwt));
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package io.cameleer.saas.certificate;
|
||||
|
||||
import io.cameleer.saas.audit.AuditAction;
|
||||
import io.cameleer.saas.audit.AuditService;
|
||||
import io.cameleer.saas.tenant.TenantRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -8,6 +10,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@@ -18,13 +21,16 @@ public class CertificateService {
|
||||
private final CertificateManager certManager;
|
||||
private final CertificateRepository certRepository;
|
||||
private final TenantRepository tenantRepository;
|
||||
private final AuditService auditService;
|
||||
|
||||
public CertificateService(CertificateManager certManager,
|
||||
CertificateRepository certRepository,
|
||||
TenantRepository tenantRepository) {
|
||||
TenantRepository tenantRepository,
|
||||
AuditService auditService) {
|
||||
this.certManager = certManager;
|
||||
this.certRepository = certRepository;
|
||||
this.tenantRepository = tenantRepository;
|
||||
this.auditService = auditService;
|
||||
}
|
||||
|
||||
public record CertificateOverview(
|
||||
@@ -61,12 +67,17 @@ public class CertificateService {
|
||||
entity.setUploadedBy(actorId);
|
||||
certRepository.save(entity);
|
||||
|
||||
auditService.log(actorId, null, null, AuditAction.CERTIFICATE_STAGED, entity.getFingerprint(),
|
||||
null, null, "SUCCESS",
|
||||
Map.of("subject", result.info().subject(), "issuer", result.info().issuer(),
|
||||
"hasCa", result.info().hasCaBundle()));
|
||||
|
||||
log.info("Certificate staged by actor {}: subject={}", actorId, result.info().subject());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void activate() {
|
||||
public void activate(UUID actorId) {
|
||||
var staged = certRepository.findByStatus(CertificateEntity.Status.STAGED)
|
||||
.orElseThrow(() -> new IllegalStateException("No staged certificate to activate"));
|
||||
|
||||
@@ -86,11 +97,14 @@ public class CertificateService {
|
||||
staged.setActivatedAt(Instant.now());
|
||||
certRepository.save(staged);
|
||||
|
||||
auditService.log(actorId, null, null, AuditAction.CERTIFICATE_ACTIVATED, staged.getFingerprint(),
|
||||
null, null, "SUCCESS", Map.of("subject", staged.getSubject()));
|
||||
|
||||
log.info("Certificate activated: subject={}", staged.getSubject());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void restore() {
|
||||
public void restore(UUID actorId) {
|
||||
var archived = certRepository.findByStatus(CertificateEntity.Status.ARCHIVED)
|
||||
.orElseThrow(() -> new IllegalStateException("No archived certificate to restore"));
|
||||
|
||||
@@ -115,13 +129,18 @@ public class CertificateService {
|
||||
certRepository.save(active);
|
||||
}
|
||||
|
||||
auditService.log(actorId, null, null, AuditAction.CERTIFICATE_RESTORED, archived.getFingerprint(),
|
||||
null, null, "SUCCESS", Map.of("subject", archived.getSubject()));
|
||||
|
||||
log.info("Certificate restored from archive: subject={}", archived.getSubject());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void discardStaged() {
|
||||
public void discardStaged(UUID actorId) {
|
||||
certManager.discardStaged();
|
||||
certRepository.findByStatus(CertificateEntity.Status.STAGED).ifPresent(certRepository::delete);
|
||||
auditService.log(actorId, null, null, AuditAction.CERTIFICATE_DISCARDED, "staged",
|
||||
null, null, "SUCCESS", null);
|
||||
log.info("Staged certificate discarded");
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@@ -15,6 +17,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/vendor/email-connector")
|
||||
@@ -76,7 +79,8 @@ public class EmailConnectorController {
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<?> save(@Valid @RequestBody SmtpConfigRequest request) {
|
||||
public ResponseEntity<?> save(@AuthenticationPrincipal Jwt jwt,
|
||||
@Valid @RequestBody SmtpConfigRequest request) {
|
||||
// Resolve password: use provided value, or fall back to existing password from Logto
|
||||
String password = request.password();
|
||||
if (password == null || password.isBlank()) {
|
||||
@@ -98,13 +102,13 @@ public class EmailConnectorController {
|
||||
return ResponseEntity.badRequest().body(Map.of("message", e.getMessage()));
|
||||
}
|
||||
|
||||
var status = emailConnectorService.saveSmtpConnector(smtp, request.registrationEnabled());
|
||||
var status = emailConnectorService.saveSmtpConnector(smtp, request.registrationEnabled(), resolveActorId(jwt));
|
||||
return ResponseEntity.ok(EmailConnectorResponse.from(status));
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
public ResponseEntity<Void> delete() {
|
||||
emailConnectorService.deleteEmailConnector();
|
||||
public ResponseEntity<Void> delete(@AuthenticationPrincipal Jwt jwt) {
|
||||
emailConnectorService.deleteEmailConnector(resolveActorId(jwt));
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@@ -119,13 +123,22 @@ public class EmailConnectorController {
|
||||
}
|
||||
|
||||
@PostMapping("/registration")
|
||||
public ResponseEntity<Void> toggleRegistration(@RequestBody Map<String, Boolean> body) {
|
||||
public ResponseEntity<Void> toggleRegistration(@AuthenticationPrincipal Jwt jwt,
|
||||
@RequestBody Map<String, Boolean> body) {
|
||||
boolean enabled = body.getOrDefault("enabled", false);
|
||||
var existing = emailConnectorService.getEmailConnector();
|
||||
if (existing == null && enabled) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
emailConnectorService.setRegistrationEnabled(enabled);
|
||||
emailConnectorService.setRegistrationEnabled(enabled, resolveActorId(jwt));
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
private UUID resolveActorId(Jwt jwt) {
|
||||
try {
|
||||
return UUID.fromString(jwt.getSubject());
|
||||
} catch (Exception e) {
|
||||
return UUID.nameUUIDFromBytes(jwt.getSubject().getBytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package io.cameleer.saas.vendor;
|
||||
|
||||
import io.cameleer.saas.audit.AuditAction;
|
||||
import io.cameleer.saas.audit.AuditService;
|
||||
import io.cameleer.saas.identity.LogtoManagementClient;
|
||||
import io.cameleer.saas.provisioning.ProvisioningProperties;
|
||||
import org.slf4j.Logger;
|
||||
@@ -14,6 +16,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class EmailConnectorService {
|
||||
@@ -23,10 +26,14 @@ public class EmailConnectorService {
|
||||
|
||||
private final LogtoManagementClient logtoClient;
|
||||
private final ProvisioningProperties provisioningProps;
|
||||
private final AuditService auditService;
|
||||
|
||||
public EmailConnectorService(LogtoManagementClient logtoClient, ProvisioningProperties provisioningProps) {
|
||||
public EmailConnectorService(LogtoManagementClient logtoClient,
|
||||
ProvisioningProperties provisioningProps,
|
||||
AuditService auditService) {
|
||||
this.logtoClient = logtoClient;
|
||||
this.provisioningProps = provisioningProps;
|
||||
this.auditService = auditService;
|
||||
}
|
||||
|
||||
public record SmtpConfig(String host, int port, String username, String password, String fromEmail) {}
|
||||
@@ -120,7 +127,7 @@ public class EmailConnectorService {
|
||||
}
|
||||
|
||||
/** Create or update the SMTP email connector. Returns the connector status. */
|
||||
public EmailConnectorStatus saveSmtpConnector(SmtpConfig smtp, Boolean registrationEnabled) {
|
||||
public EmailConnectorStatus saveSmtpConnector(SmtpConfig smtp, Boolean registrationEnabled, UUID actorId) {
|
||||
var connectorConfig = buildSmtpConfig(smtp);
|
||||
|
||||
// Check if an email connector already exists
|
||||
@@ -133,20 +140,24 @@ public class EmailConnectorService {
|
||||
log.info("Created SMTP email connector: {}", result != null ? result.get("id") : "unknown");
|
||||
}
|
||||
|
||||
auditService.log(actorId, null, null, AuditAction.EMAIL_CONNECTOR_SAVED, null, null, null, "SUCCESS",
|
||||
Map.of("host", smtp.host(), "port", smtp.port(), "fromEmail", smtp.fromEmail()));
|
||||
|
||||
// Handle registration toggle
|
||||
boolean enableReg = registrationEnabled != null ? registrationEnabled : (existing == null);
|
||||
setRegistrationEnabled(enableReg);
|
||||
setRegistrationEnabled(enableReg, actorId);
|
||||
|
||||
return getEmailConnector();
|
||||
}
|
||||
|
||||
/** Delete the email connector and disable registration. */
|
||||
public void deleteEmailConnector() {
|
||||
public void deleteEmailConnector(UUID actorId) {
|
||||
var existing = getEmailConnector();
|
||||
if (existing != null) {
|
||||
logtoClient.deleteConnector(existing.connectorId());
|
||||
setRegistrationEnabled(false);
|
||||
log.info("Deleted email connector: {}", existing.connectorId());
|
||||
auditService.log(actorId, null, null, AuditAction.EMAIL_CONNECTOR_DELETED, null, null, null, "SUCCESS", null);
|
||||
setRegistrationEnabled(false, actorId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +180,7 @@ public class EmailConnectorService {
|
||||
}
|
||||
|
||||
/** Set registration mode on the Logto sign-in experience. */
|
||||
public void setRegistrationEnabled(boolean enabled) {
|
||||
public void setRegistrationEnabled(boolean enabled, UUID actorId) {
|
||||
if (enabled) {
|
||||
logtoClient.updateSignInExperience(Map.of(
|
||||
"signInMode", "SignInAndRegister",
|
||||
@@ -201,6 +212,8 @@ public class EmailConnectorService {
|
||||
)
|
||||
));
|
||||
}
|
||||
auditService.log(actorId, null, null, AuditAction.REGISTRATION_TOGGLED, null, null, null, "SUCCESS",
|
||||
Map.of("enabled", enabled));
|
||||
}
|
||||
|
||||
/** Check if registration is currently enabled in Logto. */
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/vendor/admins")
|
||||
@@ -27,27 +28,38 @@ public class VendorAdminController {
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public CreateAdminResponse createAdmin(@RequestBody CreateAdminRequest request) {
|
||||
return vendorAdminService.createAdmin(request);
|
||||
public CreateAdminResponse createAdmin(@AuthenticationPrincipal Jwt jwt,
|
||||
@RequestBody CreateAdminRequest request) {
|
||||
return vendorAdminService.createAdmin(request, resolveActorId(jwt));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{userId}")
|
||||
public ResponseEntity<Void> removeAdmin(@AuthenticationPrincipal Jwt jwt,
|
||||
@PathVariable String userId) {
|
||||
vendorAdminService.removeAdmin(userId, jwt.getSubject());
|
||||
vendorAdminService.removeAdmin(userId, jwt.getSubject(), resolveActorId(jwt));
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@PostMapping("/{userId}/reset-password")
|
||||
public ResponseEntity<Void> resetPassword(@PathVariable String userId,
|
||||
public ResponseEntity<Void> resetPassword(@AuthenticationPrincipal Jwt jwt,
|
||||
@PathVariable String userId,
|
||||
@RequestBody Map<String, String> body) {
|
||||
vendorAdminService.resetAdminPassword(userId, body.get("password"));
|
||||
vendorAdminService.resetAdminPassword(userId, body.get("password"), resolveActorId(jwt));
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{userId}/mfa")
|
||||
public ResponseEntity<Void> resetMfa(@PathVariable String userId) {
|
||||
vendorAdminService.resetAdminMfa(userId);
|
||||
public ResponseEntity<Void> resetMfa(@AuthenticationPrincipal Jwt jwt,
|
||||
@PathVariable String userId) {
|
||||
vendorAdminService.resetAdminMfa(userId, resolveActorId(jwt));
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
private UUID resolveActorId(Jwt jwt) {
|
||||
try {
|
||||
return UUID.fromString(jwt.getSubject());
|
||||
} catch (Exception e) {
|
||||
return UUID.nameUUIDFromBytes(jwt.getSubject().getBytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package io.cameleer.saas.vendor;
|
||||
|
||||
import io.cameleer.saas.account.AccountService;
|
||||
import io.cameleer.saas.audit.AuditAction;
|
||||
import io.cameleer.saas.audit.AuditService;
|
||||
import io.cameleer.saas.identity.LogtoManagementClient;
|
||||
import io.cameleer.saas.notification.PasswordResetNotificationService;
|
||||
import org.slf4j.Logger;
|
||||
@@ -11,6 +13,7 @@ import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class VendorAdminService {
|
||||
@@ -22,15 +25,18 @@ public class VendorAdminService {
|
||||
private final AccountService accountService;
|
||||
private final EmailConnectorService emailConnectorService;
|
||||
private final PasswordResetNotificationService passwordNotificationService;
|
||||
private final AuditService auditService;
|
||||
|
||||
public VendorAdminService(LogtoManagementClient logtoClient,
|
||||
AccountService accountService,
|
||||
EmailConnectorService emailConnectorService,
|
||||
PasswordResetNotificationService passwordNotificationService) {
|
||||
PasswordResetNotificationService passwordNotificationService,
|
||||
AuditService auditService) {
|
||||
this.logtoClient = logtoClient;
|
||||
this.accountService = accountService;
|
||||
this.emailConnectorService = emailConnectorService;
|
||||
this.passwordNotificationService = passwordNotificationService;
|
||||
this.auditService = auditService;
|
||||
}
|
||||
|
||||
// --- Records ---
|
||||
@@ -62,7 +68,7 @@ public class VendorAdminService {
|
||||
.toList();
|
||||
}
|
||||
|
||||
public CreateAdminResponse createAdmin(CreateAdminRequest request) {
|
||||
public CreateAdminResponse createAdmin(CreateAdminRequest request, UUID actorId) {
|
||||
if (request.email() == null || request.email().isBlank() || !request.email().contains("@")) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Valid email address required");
|
||||
}
|
||||
@@ -97,24 +103,28 @@ public class VendorAdminService {
|
||||
|
||||
logtoClient.assignGlobalRole(userId, roleId);
|
||||
log.info("Assigned vendor role to user {}", userId);
|
||||
auditService.log(actorId, null, null, AuditAction.ADMIN_CREATED, userId, null, null, "SUCCESS",
|
||||
Map.of("email", request.email(), "invited", invited));
|
||||
|
||||
return new CreateAdminResponse(invited, invited ? null : tempPassword);
|
||||
}
|
||||
|
||||
public void removeAdmin(String userId, String requesterId) {
|
||||
public void removeAdmin(String userId, String requesterId, UUID actorId) {
|
||||
if (userId.equals(requesterId)) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Cannot remove yourself as administrator");
|
||||
}
|
||||
String roleId = getVendorRoleId();
|
||||
logtoClient.revokeGlobalRole(userId, roleId);
|
||||
log.info("Revoked vendor role from user {}", userId);
|
||||
auditService.log(actorId, null, null, AuditAction.ADMIN_REMOVED, userId, null, null, "SUCCESS", null);
|
||||
}
|
||||
|
||||
public void resetAdminPassword(String userId, String newPassword) {
|
||||
public void resetAdminPassword(String userId, String newPassword, UUID actorId) {
|
||||
verifyIsVendorAdmin(userId);
|
||||
accountService.validatePassword(newPassword);
|
||||
logtoClient.updateUserPassword(userId, newPassword);
|
||||
log.info("Reset password for vendor admin {}", userId);
|
||||
auditService.log(actorId, null, null, AuditAction.ADMIN_PASSWORD_RESET, userId, null, null, "SUCCESS", null);
|
||||
|
||||
// Send notification email
|
||||
try {
|
||||
@@ -130,10 +140,11 @@ public class VendorAdminService {
|
||||
}
|
||||
}
|
||||
|
||||
public void resetAdminMfa(String userId) {
|
||||
public void resetAdminMfa(String userId, UUID actorId) {
|
||||
verifyIsVendorAdmin(userId);
|
||||
logtoClient.deleteAllMfaVerifications(userId);
|
||||
log.info("Reset MFA for vendor admin {}", userId);
|
||||
auditService.log(actorId, null, null, AuditAction.ADMIN_MFA_RESET, userId, null, null, "SUCCESS", null);
|
||||
}
|
||||
|
||||
private void verifyIsVendorAdmin(String userId) {
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
package io.cameleer.saas.vendor;
|
||||
|
||||
import io.cameleer.saas.audit.AuditAction;
|
||||
import io.cameleer.saas.audit.AuditService;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/vendor/auth-policy")
|
||||
@@ -15,9 +22,12 @@ public class VendorAuthPolicyController {
|
||||
private static final Set<String> VALID_PASSKEY_MODES = Set.of("optional", "preferred", "required");
|
||||
|
||||
private final VendorAuthPolicyRepository repository;
|
||||
private final AuditService auditService;
|
||||
|
||||
public VendorAuthPolicyController(VendorAuthPolicyRepository repository) {
|
||||
public VendorAuthPolicyController(VendorAuthPolicyRepository repository,
|
||||
AuditService auditService) {
|
||||
this.repository = repository;
|
||||
this.auditService = auditService;
|
||||
}
|
||||
|
||||
public record AuthPolicyResponse(String mfaMode, boolean passkeyEnabled, String passkeyMode) {
|
||||
@@ -34,9 +44,14 @@ public class VendorAuthPolicyController {
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
public ResponseEntity<AuthPolicyResponse> updatePolicy(@RequestBody AuthPolicyUpdateRequest request) {
|
||||
public ResponseEntity<AuthPolicyResponse> updatePolicy(@AuthenticationPrincipal Jwt jwt,
|
||||
@RequestBody AuthPolicyUpdateRequest request) {
|
||||
var policy = repository.getPolicy();
|
||||
|
||||
String mfaMode_old = policy.getMfaMode();
|
||||
boolean passkeyEnabled_old = policy.isPasskeyEnabled();
|
||||
String passkeyMode_old = policy.getPasskeyMode();
|
||||
|
||||
if (request.mfaMode() != null) {
|
||||
if (!VALID_MFA_MODES.contains(request.mfaMode())) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
@@ -54,6 +69,25 @@ public class VendorAuthPolicyController {
|
||||
}
|
||||
|
||||
repository.save(policy);
|
||||
|
||||
var changes = new HashMap<String, Object>();
|
||||
changes.put("mfaMode_old", mfaMode_old);
|
||||
changes.put("mfaMode_new", policy.getMfaMode());
|
||||
changes.put("passkeyEnabled_old", passkeyEnabled_old);
|
||||
changes.put("passkeyEnabled_new", policy.isPasskeyEnabled());
|
||||
changes.put("passkeyMode_old", passkeyMode_old);
|
||||
changes.put("passkeyMode_new", policy.getPasskeyMode());
|
||||
auditService.log(resolveActorId(jwt), null, null, AuditAction.PLATFORM_AUTH_POLICY_UPDATED,
|
||||
null, null, null, "SUCCESS", changes);
|
||||
|
||||
return ResponseEntity.ok(AuthPolicyResponse.from(policy));
|
||||
}
|
||||
|
||||
private UUID resolveActorId(Jwt jwt) {
|
||||
try {
|
||||
return UUID.fromString(jwt.getSubject());
|
||||
} catch (Exception e) {
|
||||
return UUID.nameUUIDFromBytes(jwt.getSubject().getBytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user