diff --git a/AUDIT.md b/AUDIT.md index 5acfe1c..7597efb 100644 --- a/AUDIT.md +++ b/AUDIT.md @@ -72,7 +72,7 @@ So static assets, error pages, and login flows do not generate audit rows. For each request the interceptor: -1. Tries `HeaderCurrentUserAdapter.currentUser()` → on failure (e.g. not authenticated), continues with `actorId` / `actorRole` unset rather than failing the HTTP request. +1. Tries `SecurityActorAdapter.currentUser()` → on failure (e.g. not authenticated), continues with `actorId` / `actorRole` unset rather than failing the HTTP request. 2. Sets identity fields: `actorId`, `actorRole`, `principalName`. 3. Sets request metadata: method, URI, query string, resolved handler name. 4. Sets `responseStatus`, `errorType` (from `ex`). @@ -145,7 +145,7 @@ If `from` is after `to`, the service throws `IllegalArgumentException`. | `NURSE` or `HANDLER` | Same pattern using `findAllByHandlerId`. Empty allowed-set → **empty page** (not an error). | | Any other role (e.g. `OTHER`) | **NotAuthorizedException** | -**Note on `Actor` resolution:** `HeaderCurrentUserAdapter` maps Spring authorities to `Role` for `ADMIN`, `HANDLER`, and `CASE_OWNER`; other authenticated users may receive `OTHER` and then **cannot** list audit events even though requests are still **recorded**. The enum also defines `MANAGER`, `DOCTOR`, and `NURSE` for future or alternate identity wiring. +**Note on `Actor` resolution:** `SecurityActorAdapter` maps Spring authorities to `Role` for `ADMIN`, `HANDLER`, and `CASE_OWNER`; other authenticated users may receive `OTHER` and then **cannot** list audit events even though requests are still **recorded**. The enum also defines `MANAGER`, `DOCTOR`, and `NURSE` for future or alternate identity wiring. ### Repository queries used diff --git a/src/main/java/org/example/projektarendehantering/infrastructure/security/HeaderCurrentUserAdapter.java b/src/main/java/org/example/projektarendehantering/infrastructure/security/SecurityActorAdapter.java similarity index 97% rename from src/main/java/org/example/projektarendehantering/infrastructure/security/HeaderCurrentUserAdapter.java rename to src/main/java/org/example/projektarendehantering/infrastructure/security/SecurityActorAdapter.java index 1c83cb3..fb8d7da 100644 --- a/src/main/java/org/example/projektarendehantering/infrastructure/security/HeaderCurrentUserAdapter.java +++ b/src/main/java/org/example/projektarendehantering/infrastructure/security/SecurityActorAdapter.java @@ -14,7 +14,7 @@ * Bridges Spring Security authentication to the application's Actor model. */ @Component -public class HeaderCurrentUserAdapter { +public class SecurityActorAdapter { public Actor currentUser() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); @@ -40,4 +40,3 @@ public Actor currentUser() { return new Actor(userId, role); } } - diff --git a/src/main/java/org/example/projektarendehantering/infrastructure/web/AuditInterceptor.java b/src/main/java/org/example/projektarendehantering/infrastructure/web/AuditInterceptor.java index c2d12d8..edc2648 100644 --- a/src/main/java/org/example/projektarendehantering/infrastructure/web/AuditInterceptor.java +++ b/src/main/java/org/example/projektarendehantering/infrastructure/web/AuditInterceptor.java @@ -5,7 +5,7 @@ import org.example.projektarendehantering.common.Actor; import org.example.projektarendehantering.application.service.AuditService; import org.example.projektarendehantering.infrastructure.persistence.AuditEventEntity; -import org.example.projektarendehantering.infrastructure.security.HeaderCurrentUserAdapter; +import org.example.projektarendehantering.infrastructure.security.SecurityActorAdapter; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; @@ -19,18 +19,18 @@ public class AuditInterceptor implements HandlerInterceptor { private final AuditService auditService; - private final HeaderCurrentUserAdapter currentUserAdapter; + private final SecurityActorAdapter securityActorAdapter; - public AuditInterceptor(AuditService auditService, HeaderCurrentUserAdapter currentUserAdapter) { + public AuditInterceptor(AuditService auditService, SecurityActorAdapter securityActorAdapter) { this.auditService = auditService; - this.currentUserAdapter = currentUserAdapter; + this.securityActorAdapter = securityActorAdapter; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { Actor actor = null; try { - actor = currentUserAdapter.currentUser(); + actor = securityActorAdapter.currentUser(); } catch (RuntimeException ignored) { // If not authenticated (or adapter throws), we still avoid failing the request. } diff --git a/src/main/java/org/example/projektarendehantering/presentation/rest/AuditController.java b/src/main/java/org/example/projektarendehantering/presentation/rest/AuditController.java index 998a69e..c502ab2 100644 --- a/src/main/java/org/example/projektarendehantering/presentation/rest/AuditController.java +++ b/src/main/java/org/example/projektarendehantering/presentation/rest/AuditController.java @@ -1,7 +1,7 @@ package org.example.projektarendehantering.presentation.rest; import org.example.projektarendehantering.application.service.AuditService; -import org.example.projektarendehantering.infrastructure.security.HeaderCurrentUserAdapter; +import org.example.projektarendehantering.infrastructure.security.SecurityActorAdapter; import org.example.projektarendehantering.presentation.dto.AuditEventDTO; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -22,11 +22,11 @@ public class AuditController { private final AuditService auditService; - private final HeaderCurrentUserAdapter currentUserAdapter; + private final SecurityActorAdapter securityActorAdapter; - public AuditController(AuditService auditService, HeaderCurrentUserAdapter currentUserAdapter) { + public AuditController(AuditService auditService, SecurityActorAdapter securityActorAdapter) { this.auditService = auditService; - this.currentUserAdapter = currentUserAdapter; + this.securityActorAdapter = securityActorAdapter; } @GetMapping @@ -43,7 +43,7 @@ public ResponseEntity> list( Sort.by(Sort.Direction.DESC, "occurredAt") ); - return ResponseEntity.ok(auditService.listEvents(currentUserAdapter.currentUser(), from, to, caseId, pageable)); + return ResponseEntity.ok(auditService.listEvents(securityActorAdapter.currentUser(), from, to, caseId, pageable)); } } diff --git a/src/main/java/org/example/projektarendehantering/presentation/rest/CaseController.java b/src/main/java/org/example/projektarendehantering/presentation/rest/CaseController.java index 0b18fe0..c7ed63f 100644 --- a/src/main/java/org/example/projektarendehantering/presentation/rest/CaseController.java +++ b/src/main/java/org/example/projektarendehantering/presentation/rest/CaseController.java @@ -2,7 +2,7 @@ import jakarta.validation.Valid; import org.example.projektarendehantering.application.service.CaseService; -import org.example.projektarendehantering.infrastructure.security.HeaderCurrentUserAdapter; +import org.example.projektarendehantering.infrastructure.security.SecurityActorAdapter; import org.example.projektarendehantering.presentation.dto.CaseAssignmentDTO; import org.example.projektarendehantering.presentation.dto.CaseDTO; import org.springframework.http.ResponseEntity; @@ -16,33 +16,33 @@ public class CaseController { private final CaseService caseService; - private final HeaderCurrentUserAdapter currentUserAdapter; + private final SecurityActorAdapter securityActorAdapter; - public CaseController(CaseService caseService, HeaderCurrentUserAdapter currentUserAdapter) { + public CaseController(CaseService caseService, SecurityActorAdapter securityActorAdapter) { this.caseService = caseService; - this.currentUserAdapter = currentUserAdapter; + this.securityActorAdapter = securityActorAdapter; } @PostMapping public ResponseEntity createCase(@RequestBody @Valid CaseDTO caseDTO) { - CaseDTO created = caseService.createCase(currentUserAdapter.currentUser(), caseDTO); + CaseDTO created = caseService.createCase(securityActorAdapter.currentUser(), caseDTO); return ResponseEntity.ok(created); } @GetMapping("/{id}") public ResponseEntity getCase(@PathVariable UUID id) { - return caseService.getCase(currentUserAdapter.currentUser(), id) + return caseService.getCase(securityActorAdapter.currentUser(), id) .map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()); } @GetMapping public ResponseEntity> getAllCases() { - return ResponseEntity.ok(caseService.getAllCases(currentUserAdapter.currentUser())); + return ResponseEntity.ok(caseService.getAllCases(securityActorAdapter.currentUser())); } @PutMapping("/{id}/assignments") public ResponseEntity assignUsers(@PathVariable UUID id, @RequestBody CaseAssignmentDTO dto) { - return ResponseEntity.ok(caseService.assignUsers(currentUserAdapter.currentUser(), id, dto)); + return ResponseEntity.ok(caseService.assignUsers(securityActorAdapter.currentUser(), id, dto)); } } diff --git a/src/main/java/org/example/projektarendehantering/presentation/rest/EmployeeController.java b/src/main/java/org/example/projektarendehantering/presentation/rest/EmployeeController.java index c7fd0eb..df3c484 100644 --- a/src/main/java/org/example/projektarendehantering/presentation/rest/EmployeeController.java +++ b/src/main/java/org/example/projektarendehantering/presentation/rest/EmployeeController.java @@ -2,7 +2,7 @@ import jakarta.validation.Valid; import org.example.projektarendehantering.application.service.EmployeeService; -import org.example.projektarendehantering.infrastructure.security.HeaderCurrentUserAdapter; +import org.example.projektarendehantering.infrastructure.security.SecurityActorAdapter; import org.example.projektarendehantering.presentation.dto.EmployeeCreateDTO; import org.example.projektarendehantering.presentation.dto.EmployeeDTO; import org.springframework.http.ResponseEntity; @@ -16,28 +16,28 @@ public class EmployeeController { private final EmployeeService employeeService; - private final HeaderCurrentUserAdapter currentUserAdapter; + private final SecurityActorAdapter securityActorAdapter; - public EmployeeController(EmployeeService employeeService, HeaderCurrentUserAdapter currentUserAdapter) { + public EmployeeController(EmployeeService employeeService, SecurityActorAdapter securityActorAdapter) { this.employeeService = employeeService; - this.currentUserAdapter = currentUserAdapter; + this.securityActorAdapter = securityActorAdapter; } @PostMapping public ResponseEntity createEmployee(@RequestBody @Valid EmployeeCreateDTO dto) { - return ResponseEntity.ok(employeeService.createEmployee(currentUserAdapter.currentUser(), dto)); + return ResponseEntity.ok(employeeService.createEmployee(securityActorAdapter.currentUser(), dto)); } @GetMapping("/{id}") public ResponseEntity getEmployee(@PathVariable UUID id) { - return employeeService.getEmployee(currentUserAdapter.currentUser(), id) + return employeeService.getEmployee(securityActorAdapter.currentUser(), id) .map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()); } @GetMapping public ResponseEntity> getAllEmployees() { - return ResponseEntity.ok(employeeService.getAllEmployees(currentUserAdapter.currentUser())); + return ResponseEntity.ok(employeeService.getAllEmployees(securityActorAdapter.currentUser())); } } diff --git a/src/main/java/org/example/projektarendehantering/presentation/rest/PatientController.java b/src/main/java/org/example/projektarendehantering/presentation/rest/PatientController.java index 568b611..01016fc 100644 --- a/src/main/java/org/example/projektarendehantering/presentation/rest/PatientController.java +++ b/src/main/java/org/example/projektarendehantering/presentation/rest/PatientController.java @@ -3,7 +3,7 @@ import jakarta.validation.Valid; import org.example.projektarendehantering.application.service.CaseService; import org.example.projektarendehantering.application.service.PatientService; -import org.example.projektarendehantering.infrastructure.security.HeaderCurrentUserAdapter; +import org.example.projektarendehantering.infrastructure.security.SecurityActorAdapter; import org.example.projektarendehantering.presentation.dto.PatientCreateDTO; import org.example.projektarendehantering.presentation.dto.CaseDTO; import org.example.projektarendehantering.presentation.dto.PatientDTO; @@ -19,12 +19,12 @@ public class PatientController { private final PatientService patientService; private final CaseService caseService; - private final HeaderCurrentUserAdapter currentUserAdapter; + private final SecurityActorAdapter securityActorAdapter; - public PatientController(PatientService patientService, CaseService caseService, HeaderCurrentUserAdapter currentUserAdapter) { + public PatientController(PatientService patientService, CaseService caseService, SecurityActorAdapter securityActorAdapter) { this.patientService = patientService; this.caseService = caseService; - this.currentUserAdapter = currentUserAdapter; + this.securityActorAdapter = securityActorAdapter; } @PostMapping @@ -46,7 +46,7 @@ public ResponseEntity> getAllPatients() { @GetMapping("/{id}/cases") public ResponseEntity> getCasesForPatient(@PathVariable UUID id) { - return ResponseEntity.ok(caseService.getCasesForPatient(currentUserAdapter.currentUser(), id)); + return ResponseEntity.ok(caseService.getCasesForPatient(securityActorAdapter.currentUser(), id)); } } diff --git a/src/main/java/org/example/projektarendehantering/presentation/web/AuditUiController.java b/src/main/java/org/example/projektarendehantering/presentation/web/AuditUiController.java index be8bbbc..196dd46 100644 --- a/src/main/java/org/example/projektarendehantering/presentation/web/AuditUiController.java +++ b/src/main/java/org/example/projektarendehantering/presentation/web/AuditUiController.java @@ -1,7 +1,7 @@ package org.example.projektarendehantering.presentation.web; import org.example.projektarendehantering.application.service.AuditService; -import org.example.projektarendehantering.infrastructure.security.HeaderCurrentUserAdapter; +import org.example.projektarendehantering.infrastructure.security.SecurityActorAdapter; import org.example.projektarendehantering.presentation.dto.AuditEventDTO; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -20,11 +20,11 @@ public class AuditUiController { private final AuditService auditService; - private final HeaderCurrentUserAdapter currentUserAdapter; + private final SecurityActorAdapter securityActorAdapter; - public AuditUiController(AuditService auditService, HeaderCurrentUserAdapter currentUserAdapter) { + public AuditUiController(AuditService auditService, SecurityActorAdapter securityActorAdapter) { this.auditService = auditService; - this.currentUserAdapter = currentUserAdapter; + this.securityActorAdapter = securityActorAdapter; } @GetMapping("/ui/audit") @@ -42,7 +42,7 @@ public String list( Sort.by(Sort.Direction.DESC, "occurredAt") ); - Page events = auditService.listEvents(currentUserAdapter.currentUser(), from, to, caseId, pageable); + Page events = auditService.listEvents(securityActorAdapter.currentUser(), from, to, caseId, pageable); model.addAttribute("events", events); model.addAttribute("from", from); model.addAttribute("to", to); diff --git a/src/main/java/org/example/projektarendehantering/presentation/web/UiController.java b/src/main/java/org/example/projektarendehantering/presentation/web/UiController.java index 611e38f..6e91bed 100644 --- a/src/main/java/org/example/projektarendehantering/presentation/web/UiController.java +++ b/src/main/java/org/example/projektarendehantering/presentation/web/UiController.java @@ -2,7 +2,7 @@ import org.example.projektarendehantering.application.service.CaseService; import org.example.projektarendehantering.application.service.PatientService; -import org.example.projektarendehantering.infrastructure.security.HeaderCurrentUserAdapter; +import org.example.projektarendehantering.infrastructure.security.SecurityActorAdapter; import org.example.projektarendehantering.presentation.dto.CaseDTO; import org.example.projektarendehantering.presentation.dto.CreateCaseForm; import org.springframework.stereotype.Controller; @@ -23,12 +23,12 @@ public class UiController { private final CaseService caseService; private final PatientService patientService; - private final HeaderCurrentUserAdapter currentUserAdapter; + private final SecurityActorAdapter securityActorAdapter; - public UiController(CaseService caseService, PatientService patientService, HeaderCurrentUserAdapter currentUserAdapter) { + public UiController(CaseService caseService, PatientService patientService, SecurityActorAdapter securityActorAdapter) { this.caseService = caseService; this.patientService = patientService; - this.currentUserAdapter = currentUserAdapter; + this.securityActorAdapter = securityActorAdapter; } @PostMapping("/ui/cases/{caseId}/notes") @@ -45,7 +45,7 @@ public String index() { @GetMapping("/ui/cases") public String listCases(Model model) { - model.addAttribute("cases", caseService.getAllCases(currentUserAdapter.currentUser())); + model.addAttribute("cases", caseService.getAllCases(securityActorAdapter.currentUser())); return "cases/list"; } @@ -67,13 +67,13 @@ public String createCase(@Valid @ModelAttribute("createCaseForm") CreateCaseForm caseDTO.setDescription(form.getDescription()); caseDTO.setPatientId(form.getPatientId()); - caseService.createCase(currentUserAdapter.currentUser(), caseDTO); + caseService.createCase(securityActorAdapter.currentUser(), caseDTO); return "redirect:/ui/cases"; } @GetMapping("/ui/cases/{caseId}") public String caseDetail(@PathVariable UUID caseId, Model model) { - caseService.getCase(currentUserAdapter.currentUser(), caseId).ifPresent(c -> model.addAttribute("case", c)); + caseService.getCase(securityActorAdapter.currentUser(), caseId).ifPresent(c -> model.addAttribute("case", c)); return "cases/detail"; } }