diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/common/exception/GlobalExceptionHandler.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/common/exception/GlobalExceptionHandler.java index 1105ad5..f39299d 100644 --- a/eeg_backend/src/main/java/at/mueller/eeg/backend/common/exception/GlobalExceptionHandler.java +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/common/exception/GlobalExceptionHandler.java @@ -1,7 +1,10 @@ package at.mueller.eeg.backend.common.exception; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.DisabledException; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -10,6 +13,8 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class GlobalExceptionHandler { + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + @ExceptionHandler(BadCredentialsException.class) public ResponseEntity handleBadCredentials(BadCredentialsException ex) { return ResponseEntity @@ -24,11 +29,27 @@ public class GlobalExceptionHandler { .body(new ErrorResponse("Dein Account wurde noch nicht vom Admin freigeschaltet. Bitte habe etwas Geduld.", HttpStatus.FORBIDDEN.value())); } - @ExceptionHandler(Exception.class) - public ResponseEntity handleGeneric(Exception ex) { + @ExceptionHandler(AccessDeniedException.class) + public ResponseEntity handleAccessDenied(AccessDeniedException ex) { return ResponseEntity - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .body(new ErrorResponse("Ein unerwarteter Fehler ist aufgetreten: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value())); + .status(HttpStatus.FORBIDDEN) + .body(new ErrorResponse("Keine Berechtigung für diese Aktion.", HttpStatus.FORBIDDEN.value())); + } + + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity handleIllegalArgument(IllegalArgumentException ex) { + log.warn("Validation error: {}", ex.getMessage()); + return ResponseEntity + .status(HttpStatus.BAD_REQUEST) + .body(new ErrorResponse(ex.getMessage(), HttpStatus.BAD_REQUEST.value())); + } + + @ExceptionHandler(IllegalStateException.class) + public ResponseEntity handleIllegalState(IllegalStateException ex) { + log.warn("Business rule violation: {}", ex.getMessage()); + return ResponseEntity + .status(HttpStatus.CONFLICT) + .body(new ErrorResponse(ex.getMessage(), HttpStatus.CONFLICT.value())); } @ExceptionHandler(AtNumberAlreadyExistsException.class) @@ -37,4 +58,12 @@ public class GlobalExceptionHandler { .status(HttpStatus.CONFLICT) .body(new ErrorResponse("Zählerpunkt existiert bereits", HttpStatus.CONFLICT.value())); } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleGeneric(Exception ex) { + log.error("Unexpected error: ", ex); + return ResponseEntity + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new ErrorResponse("Ein unerwarteter Fehler ist aufgetreten.", HttpStatus.INTERNAL_SERVER_ERROR.value())); + } } \ No newline at end of file diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/api/MembershipController.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/api/MembershipController.java index 8b231a1..b96beba 100644 --- a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/api/MembershipController.java +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/api/MembershipController.java @@ -26,13 +26,17 @@ public class MembershipController { // --- USER ENDPUNKTE --- @GetMapping("/metering-points/{id}/eligible-communities") + @PreAuthorize("hasRole('MEMBER')") public ResponseEntity> getEligibleCommunities(@PathVariable UUID id) { return ResponseEntity.ok(membershipService.getEligibleCommunities(id)); } @PostMapping("/memberships/request") - public ResponseEntity requestMembership(@Valid @RequestBody MembershipRequest request) { - UUID membershipId = membershipService.requestMembership(request); + @PreAuthorize("hasRole('MEMBER')") + public ResponseEntity requestMembership( + @CurrentUserId String userId, + @Valid @RequestBody MembershipRequest request) { + UUID membershipId = membershipService.requestMembership(UUID.fromString(userId), request); return ResponseEntity.status(HttpStatus.CREATED).body(membershipId); } diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/service/MembershipService.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/service/MembershipService.java index 393059f..527598b 100644 --- a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/service/MembershipService.java +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/service/MembershipService.java @@ -54,10 +54,14 @@ public class MembershipService { } @Transactional - public UUID requestMembership(MembershipRequest request) { + public UUID requestMembership(UUID userId, MembershipRequest request) { MeteringPoint point = meteringPointRepository.findById(request.meteringPointId()) .orElseThrow(() -> new IllegalArgumentException("Zählpunkt nicht gefunden")); + if (!point.getUserId().equals(userId)) { + throw new org.springframework.security.access.AccessDeniedException("Keine Berechtigung für diesen Zählpunkt."); + } + EnergyCommunity community = energyCommunityRepository.findById(request.energyCommunityId()) .orElseThrow(() -> new IllegalArgumentException("Energiegemeinschaft nicht gefunden")); diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/service/MeteringPointService.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/service/MeteringPointService.java index 0a132b4..b79660d 100644 --- a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/service/MeteringPointService.java +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/service/MeteringPointService.java @@ -88,7 +88,7 @@ public class MeteringPointService { .orElseThrow(() -> new IllegalArgumentException("Zählpunkt nicht gefunden")); if (!point.getUserId().equals(userId)) { - throw new SecurityException("Keine Berechtigung zum Löschen dieses Zählpunkts"); + throw new org.springframework.security.access.AccessDeniedException("Keine Berechtigung zum Löschen dieses Zählpunkts"); } if (point.getMakoState() == MakoState.ACTIVE) { diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/config/SecurityConfig.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/config/SecurityConfig.java index 53e961a..d8e9656 100644 --- a/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/config/SecurityConfig.java +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/iam/config/SecurityConfig.java @@ -63,7 +63,7 @@ public class SecurityConfig { "/api/mako/admin/**" ).hasRole("ADMIN") .requestMatchers("/api/**").authenticated() - .anyRequest().permitAll() + .anyRequest().denyAll() ) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter())) diff --git a/eeg_backend/src/test/java/at/mueller/eeg/backend/community/service/MembershipServiceTest.java b/eeg_backend/src/test/java/at/mueller/eeg/backend/community/service/MembershipServiceTest.java index 0dbb8ca..4d18e49 100644 --- a/eeg_backend/src/test/java/at/mueller/eeg/backend/community/service/MembershipServiceTest.java +++ b/eeg_backend/src/test/java/at/mueller/eeg/backend/community/service/MembershipServiceTest.java @@ -140,7 +140,7 @@ class MembershipServiceTest { when(membershipRepository.save(any())).thenReturn(testMembership); MembershipRequest request = new MembershipRequest(testMeteringPointId, testCommunityId, 1); - UUID result = membershipService.requestMembership(request); + UUID result = membershipService.requestMembership(testUserId, request); assertEquals(testMembershipId, result); verify(membershipRepository).save(any()); @@ -154,7 +154,7 @@ class MembershipServiceTest { MembershipRequest request = new MembershipRequest(testMeteringPointId, testCommunityId, 1); - assertThrows(IllegalStateException.class, () -> membershipService.requestMembership(request)); + assertThrows(IllegalStateException.class, () -> membershipService.requestMembership(testUserId, request)); } @Test @@ -165,7 +165,7 @@ class MembershipServiceTest { MembershipRequest request = new MembershipRequest(testMeteringPointId, testCommunityId, 1); - assertThrows(IllegalStateException.class, () -> membershipService.requestMembership(request)); + assertThrows(IllegalStateException.class, () -> membershipService.requestMembership(testUserId, request)); } @Test @@ -179,7 +179,7 @@ class MembershipServiceTest { MembershipRequest request = new MembershipRequest(testMeteringPointId, testCommunityId, 1); - assertThrows(IllegalStateException.class, () -> membershipService.requestMembership(request)); + assertThrows(IllegalStateException.class, () -> membershipService.requestMembership(testUserId, request)); } @Test diff --git a/eeg_backend/src/test/java/at/mueller/eeg/backend/community/service/MeteringPointServiceTest.java b/eeg_backend/src/test/java/at/mueller/eeg/backend/community/service/MeteringPointServiceTest.java index 27d6a64..6afe155 100644 --- a/eeg_backend/src/test/java/at/mueller/eeg/backend/community/service/MeteringPointServiceTest.java +++ b/eeg_backend/src/test/java/at/mueller/eeg/backend/community/service/MeteringPointServiceTest.java @@ -104,7 +104,7 @@ class MeteringPointServiceTest { UUID otherUserId = UUID.randomUUID(); when(meteringPointRepository.findById(testPointId)).thenReturn(Optional.of(testMeteringPoint)); - assertThrows(SecurityException.class, () -> + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> meteringPointService.deleteOwnMeteringPoint(otherUserId, testPointId)); }