feat: replace manual Logto role check with @PreAuthorize in TenantController
Remove LogtoManagementClient dependency from TenantController; gate
listAll and create with @PreAuthorize("hasRole('platform-admin')"),
relying on the JWT roles claim already mapped by JwtAuthenticationConverter.
Update TenantControllerTest to supply the platform-admin role via jwt()
on all POST requests that expect 201/409.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,11 @@
|
|||||||
package net.siegeln.cameleer.saas.tenant;
|
package net.siegeln.cameleer.saas.tenant;
|
||||||
|
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import net.siegeln.cameleer.saas.identity.LogtoManagementClient;
|
|
||||||
import net.siegeln.cameleer.saas.tenant.dto.CreateTenantRequest;
|
import net.siegeln.cameleer.saas.tenant.dto.CreateTenantRequest;
|
||||||
import net.siegeln.cameleer.saas.tenant.dto.TenantResponse;
|
import net.siegeln.cameleer.saas.tenant.dto.TenantResponse;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
@@ -22,30 +22,24 @@ import java.util.UUID;
|
|||||||
public class TenantController {
|
public class TenantController {
|
||||||
|
|
||||||
private final TenantService tenantService;
|
private final TenantService tenantService;
|
||||||
private final LogtoManagementClient logtoClient;
|
|
||||||
|
|
||||||
public TenantController(TenantService tenantService, LogtoManagementClient logtoClient) {
|
public TenantController(TenantService tenantService) {
|
||||||
this.tenantService = tenantService;
|
this.tenantService = tenantService;
|
||||||
this.logtoClient = logtoClient;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public ResponseEntity<List<TenantResponse>> listAll(Authentication authentication) {
|
@PreAuthorize("hasRole('platform-admin')")
|
||||||
String userId = authentication.getName();
|
public ResponseEntity<List<TenantResponse>> listAll() {
|
||||||
List<String> roles = logtoClient.getUserRoles(userId);
|
|
||||||
if (!roles.contains("platform-admin")) {
|
|
||||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
|
|
||||||
}
|
|
||||||
List<TenantResponse> tenants = tenantService.findAll().stream()
|
List<TenantResponse> tenants = tenantService.findAll().stream()
|
||||||
.map(this::toResponse).toList();
|
.map(this::toResponse).toList();
|
||||||
return ResponseEntity.ok(tenants);
|
return ResponseEntity.ok(tenants);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
|
@PreAuthorize("hasRole('platform-admin')")
|
||||||
public ResponseEntity<TenantResponse> create(@Valid @RequestBody CreateTenantRequest request,
|
public ResponseEntity<TenantResponse> create(@Valid @RequestBody CreateTenantRequest request,
|
||||||
Authentication authentication) {
|
Authentication authentication) {
|
||||||
try {
|
try {
|
||||||
// Extract actor ID from JWT subject (Logto OIDC: sub may be a non-UUID string)
|
|
||||||
String sub = authentication.getName();
|
String sub = authentication.getName();
|
||||||
UUID actorId;
|
UUID actorId;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ class TenantControllerTest {
|
|||||||
mockMvc.perform(post("/api/tenants")
|
mockMvc.perform(post("/api/tenants")
|
||||||
.with(jwt().jwt(j -> j
|
.with(jwt().jwt(j -> j
|
||||||
.claim("sub", "test-user")
|
.claim("sub", "test-user")
|
||||||
.claim("organization_id", "test-org")))
|
.claim("organization_id", "test-org")
|
||||||
|
.claim("roles", java.util.List.of("platform-admin"))))
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(request)))
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
.andExpect(status().isCreated())
|
.andExpect(status().isCreated())
|
||||||
@@ -53,13 +54,17 @@ class TenantControllerTest {
|
|||||||
var request = new CreateTenantRequest("First", slug, null);
|
var request = new CreateTenantRequest("First", slug, null);
|
||||||
|
|
||||||
mockMvc.perform(post("/api/tenants")
|
mockMvc.perform(post("/api/tenants")
|
||||||
.with(jwt().jwt(j -> j.claim("sub", "test-user")))
|
.with(jwt().jwt(j -> j
|
||||||
|
.claim("sub", "test-user")
|
||||||
|
.claim("roles", java.util.List.of("platform-admin"))))
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(request)))
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
.andExpect(status().isCreated());
|
.andExpect(status().isCreated());
|
||||||
|
|
||||||
mockMvc.perform(post("/api/tenants")
|
mockMvc.perform(post("/api/tenants")
|
||||||
.with(jwt().jwt(j -> j.claim("sub", "test-user")))
|
.with(jwt().jwt(j -> j
|
||||||
|
.claim("sub", "test-user")
|
||||||
|
.claim("roles", java.util.List.of("platform-admin"))))
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(request)))
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
.andExpect(status().isConflict());
|
.andExpect(status().isConflict());
|
||||||
@@ -81,7 +86,9 @@ class TenantControllerTest {
|
|||||||
var request = new CreateTenantRequest("Get Test", slug, null);
|
var request = new CreateTenantRequest("Get Test", slug, null);
|
||||||
|
|
||||||
var createResult = mockMvc.perform(post("/api/tenants")
|
var createResult = mockMvc.perform(post("/api/tenants")
|
||||||
.with(jwt().jwt(j -> j.claim("sub", "test-user")))
|
.with(jwt().jwt(j -> j
|
||||||
|
.claim("sub", "test-user")
|
||||||
|
.claim("roles", java.util.List.of("platform-admin"))))
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(request)))
|
.content(objectMapper.writeValueAsString(request)))
|
||||||
.andExpect(status().isCreated())
|
.andExpect(status().isCreated())
|
||||||
|
|||||||
Reference in New Issue
Block a user