Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AUDIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`).
Expand Down Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -40,4 +40,3 @@ public Actor currentUser() {
return new Actor(userId, role);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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
Expand All @@ -43,7 +43,7 @@ public ResponseEntity<Page<AuditEventDTO>> 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));
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<CaseDTO> 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<CaseDTO> 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<List<CaseDTO>> getAllCases() {
return ResponseEntity.ok(caseService.getAllCases(currentUserAdapter.currentUser()));
return ResponseEntity.ok(caseService.getAllCases(securityActorAdapter.currentUser()));
}

@PutMapping("/{id}/assignments")
public ResponseEntity<CaseDTO> 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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<EmployeeDTO> 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<EmployeeDTO> 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<List<EmployeeDTO>> getAllEmployees() {
return ResponseEntity.ok(employeeService.getAllEmployees(currentUserAdapter.currentUser()));
return ResponseEntity.ok(employeeService.getAllEmployees(securityActorAdapter.currentUser()));
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -46,7 +46,7 @@ public ResponseEntity<List<PatientDTO>> getAllPatients() {

@GetMapping("/{id}/cases")
public ResponseEntity<List<CaseDTO>> getCasesForPatient(@PathVariable UUID id) {
return ResponseEntity.ok(caseService.getCasesForPatient(currentUserAdapter.currentUser(), id));
return ResponseEntity.ok(caseService.getCasesForPatient(securityActorAdapter.currentUser(), id));
}
}

Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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")
Expand All @@ -42,7 +42,7 @@ public String list(
Sort.by(Sort.Direction.DESC, "occurredAt")
);

Page<AuditEventDTO> events = auditService.listEvents(currentUserAdapter.currentUser(), from, to, caseId, pageable);
Page<AuditEventDTO> events = auditService.listEvents(securityActorAdapter.currentUser(), from, to, caseId, pageable);
model.addAttribute("events", events);
model.addAttribute("from", from);
model.addAttribute("to", to);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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")
Expand All @@ -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";
}

Expand All @@ -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";
}
}
Expand Down