Skip to content

Commit

Permalink
add method to check authenticated user against request
Browse files Browse the repository at this point in the history
  • Loading branch information
petretiandrea committed Nov 21, 2024
1 parent 3db7bbc commit fd98434
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -25,8 +26,7 @@ public Mono<ResponseEntity<Void>> activate(
Mono<ActivationReqDto> 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());
}
}
Original file line number Diff line number Diff line change
@@ -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 <T> 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 <T> Mono<T> verifySubjectRequest(Mono<T> requestBody, Function<T, String> 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 <T> 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 <T> Mono<T> verifyRequestBody(Mono<T> requestBody, BiPredicate<T, Authentication> 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."))
)
);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;


Expand Down Expand Up @@ -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() {
Expand All @@ -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));
}
}
9 changes: 7 additions & 2 deletions src/test/java/it/gov/pagopa/rtp/activator/utils/Users.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 { }
}

0 comments on commit fd98434

Please sign in to comment.