diff --git a/src/main/java/it/gov/pagopa/rtp/activator/controller/ActivationAPIControllerImpl.java b/src/main/java/it/gov/pagopa/rtp/activator/controller/ActivationAPIControllerImpl.java index 7ee2e15..c14d83a 100644 --- a/src/main/java/it/gov/pagopa/rtp/activator/controller/ActivationAPIControllerImpl.java +++ b/src/main/java/it/gov/pagopa/rtp/activator/controller/ActivationAPIControllerImpl.java @@ -1,18 +1,19 @@ package it.gov.pagopa.rtp.activator.controller; -import java.net.URI; -import java.util.UUID; - +import it.gov.pagopa.rtp.activator.controller.generated.CreateApi; +import it.gov.pagopa.rtp.activator.model.generated.ActivationReqDto; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ServerWebExchange; - -import it.gov.pagopa.rtp.activator.controller.generated.CreateApi; -import it.gov.pagopa.rtp.activator.model.generated.ActivationReqDto; import reactor.core.publisher.Mono; +import java.net.URI; +import java.util.UUID; + +import static it.gov.pagopa.rtp.activator.utils.Authorizations.verifySubjectRequest; + @RestController @Validated public class ActivationAPIControllerImpl implements CreateApi { @@ -25,8 +26,7 @@ public Mono> activate( Mono activationReqDto, ServerWebExchange exchange ) { - return activationReqDto.flatMap( - request -> Mono.just(ResponseEntity.created(URI.create("http://localhost")).build()) - ); + return verifySubjectRequest(activationReqDto, it -> it.getPayer().getRtpSpId()) + .map(request -> ResponseEntity.created(URI.create("http://localhost")).build()); } } diff --git a/src/main/java/it/gov/pagopa/rtp/activator/utils/Authorizations.java b/src/main/java/it/gov/pagopa/rtp/activator/utils/Authorizations.java new file mode 100644 index 0000000..4997d9a --- /dev/null +++ b/src/main/java/it/gov/pagopa/rtp/activator/utils/Authorizations.java @@ -0,0 +1,48 @@ +package it.gov.pagopa.rtp.activator.utils; + +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.ReactiveSecurityContextHolder; +import reactor.core.publisher.Mono; + +import java.util.function.BiPredicate; +import java.util.function.Function; + +public final class Authorizations { + + private Authorizations(){} + + /** + * Verifies that the subject in the request matches the authenticated user's subject. + * It uses the provided {@code extractSubject} function to extract the subject from the request object, + * and compares it with the authenticated user's name. + * + * @param The type of the request body. + * @param requestBody A {@link Mono} containing the request body that needs to be verified. + * @param extractSubject A function that extracts the subject (e.g., user identifier) from the request body. + * @return A {@link Mono} containing the request body if the subjects match, or an error if they don't. + */ + public static Mono verifySubjectRequest(Mono requestBody, Function extractSubject) { + return verifyRequestBody(requestBody, (request, auth) -> extractSubject.apply(request).equals(auth.getName())); + } + + /** + * Verifies that the request body passes a custom verification function that involves the authenticated user. + * This method takes a {@link Mono} of the request body and checks the provided {@code verify} predicate to ensure + * the request meets the security requirements. If the predicate fails, an {@link AccessDeniedException} is thrown. + * + * @param The type of the request body. + * @param requestBody A {@link Mono} containing the request body that needs to be verified. + * @param verify A {@link BiPredicate} that performs a custom verification on the request body and the authenticated user. + * @return A {@link Mono} containing the request body if the verification succeeds. + */ + public static Mono verifyRequestBody(Mono requestBody, BiPredicate verify) { + return ReactiveSecurityContextHolder.getContext().flatMap(securityContext -> + requestBody.flatMap(request -> verify.test(request, securityContext.getAuthentication()) ? + Mono.just(request) : + Mono.error(new AccessDeniedException("Authenticated user doesn't have permission to perform this action.")) + ) + ); + } + +} diff --git a/src/test/java/it/gov/pagopa/rtp/activator/controller/ActivationAPIControllerImplTest.java b/src/test/java/it/gov/pagopa/rtp/activator/controller/ActivationAPIControllerImplTest.java index 8e754e7..46178e9 100644 --- a/src/test/java/it/gov/pagopa/rtp/activator/controller/ActivationAPIControllerImplTest.java +++ b/src/test/java/it/gov/pagopa/rtp/activator/controller/ActivationAPIControllerImplTest.java @@ -19,6 +19,7 @@ import java.util.UUID; +import static it.gov.pagopa.rtp.activator.utils.Users.SERVICE_PROVIDER_ID; import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity; @@ -53,6 +54,18 @@ void shouldCreateNewActivation() { .expectHeader().exists(HttpHeaders.LOCATION); } + @Test + @WithMockUser(value = "another", roles = Users.ACTIVATION_WRITE_ROLE) + void authorizedUserShouldNotActivateForAnotherServiceProvider() { + web.post() + .uri("/activations") + .header("RequestId", UUID.randomUUID().toString()) + .header("Version", "v1") + .bodyValue(generateActivationRequest()) + .exchange() + .expectStatus().isEqualTo(HttpStatus.FORBIDDEN); + } + @Test @WithMockUser void userWithoutEnoughPermissionShouldNotCreateNewActivation() { @@ -66,6 +79,6 @@ void userWithoutEnoughPermissionShouldNotCreateNewActivation() { } private ActivationReqDto generateActivationRequest() { - return new ActivationReqDto(new PayerDto("RSSMRA85T10A562S", "134")); + return new ActivationReqDto(new PayerDto("RSSMRA85T10A562S", SERVICE_PROVIDER_ID)); } } \ No newline at end of file diff --git a/src/test/java/it/gov/pagopa/rtp/activator/utils/Users.java b/src/test/java/it/gov/pagopa/rtp/activator/utils/Users.java index bdabc68..3ef1898 100644 --- a/src/test/java/it/gov/pagopa/rtp/activator/utils/Users.java +++ b/src/test/java/it/gov/pagopa/rtp/activator/utils/Users.java @@ -7,11 +7,16 @@ public class Users { + public static final String SERVICE_PROVIDER_ID = "1234"; + + public static final String ACTIVATION_WRITE_ROLE = "write_rtp_activations"; + public static final String ACTIVATION_READ_ROLE = "read_rtp_activations"; + @Retention(RetentionPolicy.RUNTIME) - @WithMockUser(value = "writer", roles = "write_rtp_activations") + @WithMockUser(value = SERVICE_PROVIDER_ID, roles = ACTIVATION_WRITE_ROLE) public @interface RtpWriter { } @Retention(RetentionPolicy.RUNTIME) - @WithMockUser(value = "reader", roles = "read_rtp_activations") + @WithMockUser(value = SERVICE_PROVIDER_ID, roles = ACTIVATION_READ_ROLE) public @interface RtpReader { } }