From 43a1058f3390c2da6baa772daed11a2d59a64009 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Mon, 27 Apr 2026 09:01:23 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20code=20review=20findings=20=E2=80=94=20a?= =?UTF-8?q?uth-settings=20HTTP=20method,=20authorization,=20redirect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change auth-settings endpoint from PUT to PATCH (matches partial update semantics and frontend hook) - Add @PreAuthorize("SCOPE_tenant:manage") to updateAuthSettings endpoint - Consolidate MFA/passkey 403 redirect handling in API client Co-Authored-By: Claude Opus 4.6 (1M context) --- .../cameleer/saas/portal/TenantPortalController.java | 4 +++- ui/src/api/client.ts | 12 +++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/siegeln/cameleer/saas/portal/TenantPortalController.java b/src/main/java/net/siegeln/cameleer/saas/portal/TenantPortalController.java index c864aec..6cbafb6 100644 --- a/src/main/java/net/siegeln/cameleer/saas/portal/TenantPortalController.java +++ b/src/main/java/net/siegeln/cameleer/saas/portal/TenantPortalController.java @@ -5,6 +5,7 @@ import net.siegeln.cameleer.saas.certificate.TenantCaCertService; import net.siegeln.cameleer.saas.config.TenantContext; import net.siegeln.cameleer.saas.tenant.TenantService; 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; @@ -239,7 +240,8 @@ public class TenantPortalController { return ResponseEntity.ok(portalService.getAuthSettings()); } - @PutMapping("/auth-settings") + @PreAuthorize("hasAuthority('SCOPE_tenant:manage')") + @PatchMapping("/auth-settings") public ResponseEntity updateAuthSettings(@RequestBody Map updates) { portalService.updateTenantSettings(updates); return ResponseEntity.ok().build(); diff --git a/ui/src/api/client.ts b/ui/src/api/client.ts index d4c4f71..7441e71 100644 --- a/ui/src/api/client.ts +++ b/ui/src/api/client.ts @@ -65,13 +65,11 @@ async function apiFetch(path: string, options: RequestInit = {}): Promise if (response.status === 403) { const errorHeader = response.headers.get('X-Cameleer-Error'); - if (errorHeader === 'APP_MFA_REQUIRED') { - window.location.href = '/platform/tenant/settings?mfa=required'; - throw new ApiError(403, '{"message":"MFA enrollment required"}'); - } - if (errorHeader === 'APP_PASSKEY_REQUIRED') { - window.location.href = '/platform/tenant/settings?passkey=required'; - throw new ApiError(403, '{"message":"Passkey enrollment required"}'); + if (errorHeader === 'APP_MFA_REQUIRED' || errorHeader === 'APP_PASSKEY_REQUIRED') { + const param = errorHeader === 'APP_PASSKEY_REQUIRED' ? 'passkey=required' : 'mfa=required'; + window.location.href = `/platform/tenant/settings?${param}`; + const msg = errorHeader === 'APP_PASSKEY_REQUIRED' ? 'Passkey enrollment required' : 'MFA enrollment required'; + throw new ApiError(403, JSON.stringify({ message: msg })); } }