From ac9ce4f2e7eac09dab59eedaad16ae13c85daea0 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Tue, 7 Apr 2026 23:16:23 +0200 Subject: [PATCH] feat: add ClaimMappingAdminController for CRUD on mapping rules - ADMIN-only REST endpoints at /api/v1/admin/claim-mappings - Full CRUD: list, get by ID, create, update, delete - OpenAPI annotations for Swagger documentation Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ClaimMappingAdminController.java | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ClaimMappingAdminController.java diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ClaimMappingAdminController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ClaimMappingAdminController.java new file mode 100644 index 00000000..0ac0f318 --- /dev/null +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ClaimMappingAdminController.java @@ -0,0 +1,77 @@ +package com.cameleer3.server.app.controller; + +import com.cameleer3.server.core.rbac.ClaimMappingRepository; +import com.cameleer3.server.core.rbac.ClaimMappingRule; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.net.URI; +import java.util.List; +import java.util.UUID; + +@RestController +@RequestMapping("/api/v1/admin/claim-mappings") +@PreAuthorize("hasRole('ADMIN')") +@Tag(name = "Claim Mapping Admin", description = "Manage OIDC claim-to-role/group mapping rules") +public class ClaimMappingAdminController { + + private final ClaimMappingRepository repository; + + public ClaimMappingAdminController(ClaimMappingRepository repository) { + this.repository = repository; + } + + @GetMapping + @Operation(summary = "List all claim mapping rules") + public List list() { + return repository.findAll(); + } + + @GetMapping("/{id}") + @Operation(summary = "Get a claim mapping rule by ID") + public ResponseEntity get(@PathVariable UUID id) { + return repository.findById(id) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } + + record CreateRuleRequest(String claim, String matchType, String matchValue, + String action, String target, int priority) {} + + @PostMapping + @Operation(summary = "Create a claim mapping rule") + public ResponseEntity create(@RequestBody CreateRuleRequest request) { + UUID id = repository.create( + request.claim(), request.matchType(), request.matchValue(), + request.action(), request.target(), request.priority()); + return repository.findById(id) + .map(rule -> ResponseEntity.created(URI.create("/api/v1/admin/claim-mappings/" + id)).body(rule)) + .orElse(ResponseEntity.internalServerError().build()); + } + + @PutMapping("/{id}") + @Operation(summary = "Update a claim mapping rule") + public ResponseEntity update(@PathVariable UUID id, @RequestBody CreateRuleRequest request) { + if (repository.findById(id).isEmpty()) { + return ResponseEntity.notFound().build(); + } + repository.update(id, request.claim(), request.matchType(), request.matchValue(), + request.action(), request.target(), request.priority()); + return repository.findById(id) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.internalServerError().build()); + } + + @DeleteMapping("/{id}") + @Operation(summary = "Delete a claim mapping rule") + public ResponseEntity delete(@PathVariable UUID id) { + if (repository.findById(id).isEmpty()) { + return ResponseEntity.notFound().build(); + } + repository.delete(id); + return ResponseEntity.noContent().build(); + } +}