From d0f16fe6a951ca3cf3bbad5eb53d33e4c08e566e Mon Sep 17 00:00:00 2001 From: LarissaASLeite Date: Tue, 7 May 2024 09:37:20 +0200 Subject: [PATCH 01/18] P4ADEV-24P4ADEV-248-creation-api-get-token-selfcare --- Dockerfile | 4 + build.gradle | 18 +- helm/values.yaml | 3 + .../configuration/AuthErrorManagerConfig.java | 21 ++ .../configuration/ServiceExceptionConfig.java | 27 +++ .../payhub/auth/constants/AuthConstants.java | 14 ++ .../auth/controller/AuthController.java | 13 ++ .../auth/controller/AuthControllerImpl.java | 19 ++ .../auth/exception/InvalidTokenException.java | 18 ++ .../auth/exception/TokenExpiredException.java | 18 ++ .../payhub/auth/service/AuthService.java | 6 + .../payhub/auth/service/AuthServiceImpl.java | 34 ++++ .../payhub/auth/utils/JWTValidator.java | 41 ++++ .../payhub/common/web/dto/ErrorDTO.java | 21 ++ .../common/web/exception/ClientException.java | 25 +++ .../web/exception/ClientExceptionNoBody.java | 20 ++ .../exception/ClientExceptionWithBody.java | 22 ++ .../common/web/exception/ErrorManager.java | 77 +++++++ .../web/exception/ServiceException.java | 19 ++ .../exception/ServiceExceptionHandler.java | 42 ++++ .../exception/ValidationExceptionHandler.java | 62 ++++++ src/main/resources/application.yml | 7 + src/main/resources/logback-spring.xml | 75 +++++++ .../auth/PayhubAuthApplicationTests.java | 13 -- .../auth/controller/AuthControllerTest.java | 32 +++ .../common/exception/ErrorManagerTest.java | 189 ++++++++++++++++++ .../ServiceExceptionHandlerTest.java | 73 +++++++ .../ValidationExceptionHandlerTest.java | 89 +++++++++ .../payhub/common/utils/MemoryAppender.java | 27 +++ 29 files changed, 1010 insertions(+), 19 deletions(-) create mode 100644 src/main/java/it/gov/pagopa/payhub/auth/configuration/AuthErrorManagerConfig.java create mode 100644 src/main/java/it/gov/pagopa/payhub/auth/configuration/ServiceExceptionConfig.java create mode 100644 src/main/java/it/gov/pagopa/payhub/auth/constants/AuthConstants.java create mode 100644 src/main/java/it/gov/pagopa/payhub/auth/controller/AuthController.java create mode 100644 src/main/java/it/gov/pagopa/payhub/auth/controller/AuthControllerImpl.java create mode 100644 src/main/java/it/gov/pagopa/payhub/auth/exception/InvalidTokenException.java create mode 100644 src/main/java/it/gov/pagopa/payhub/auth/exception/TokenExpiredException.java create mode 100644 src/main/java/it/gov/pagopa/payhub/auth/service/AuthService.java create mode 100644 src/main/java/it/gov/pagopa/payhub/auth/service/AuthServiceImpl.java create mode 100644 src/main/java/it/gov/pagopa/payhub/auth/utils/JWTValidator.java create mode 100644 src/main/java/it/gov/pagopa/payhub/common/web/dto/ErrorDTO.java create mode 100644 src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientException.java create mode 100644 src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientExceptionNoBody.java create mode 100644 src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientExceptionWithBody.java create mode 100644 src/main/java/it/gov/pagopa/payhub/common/web/exception/ErrorManager.java create mode 100644 src/main/java/it/gov/pagopa/payhub/common/web/exception/ServiceException.java create mode 100644 src/main/java/it/gov/pagopa/payhub/common/web/exception/ServiceExceptionHandler.java create mode 100644 src/main/java/it/gov/pagopa/payhub/common/web/exception/ValidationExceptionHandler.java create mode 100644 src/main/resources/logback-spring.xml delete mode 100644 src/test/java/it/gov/pagopa/payhub/auth/PayhubAuthApplicationTests.java create mode 100644 src/test/java/it/gov/pagopa/payhub/auth/controller/AuthControllerTest.java create mode 100644 src/test/java/it/gov/pagopa/payhub/common/exception/ErrorManagerTest.java create mode 100644 src/test/java/it/gov/pagopa/payhub/common/exception/ServiceExceptionHandlerTest.java create mode 100644 src/test/java/it/gov/pagopa/payhub/common/exception/ValidationExceptionHandlerTest.java create mode 100644 src/test/java/it/gov/pagopa/payhub/common/utils/MemoryAppender.java diff --git a/Dockerfile b/Dockerfile index 145ccfd1..2fcb3891 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,4 +20,8 @@ COPY --from=buildtime /build/build/libs/*.jar /app/app.jar # The agent is enabled at runtime via JAVA_TOOL_OPTIONS. ADD https://github.com/microsoft/ApplicationInsights-Java/releases/download/3.5.2/applicationinsights-agent-3.5.2.jar /app/applicationinsights-agent.jar +RUN chown -R nobody:nobody /app +EXPOSE 8080 +USER 65534 # user nobody + ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app/app.jar"] diff --git a/build.gradle b/build.gradle index 9413cf18..513c9129 100644 --- a/build.gradle +++ b/build.gradle @@ -50,17 +50,22 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0' implementation 'org.codehaus.janino:janino:3.1.12' - // Security fixes + // Security fixes implementation 'org.yaml:snakeyaml:2.0' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testImplementation 'org.junit.jupiter:junit-jupiter-api' - testImplementation 'org.junit.jupiter:junit-jupiter-engine' - testImplementation 'org.mockito:mockito-core' + // validation token jwt + implementation 'com.auth0:java-jwt:4.4.0' + implementation 'com.auth0:jwks-rsa:0.22.1' + + // TESTS + testImplementation 'org.springframework.boot:spring-boot-starter-test:3.2.5' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.0-M1' + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.11.0-M1' + testImplementation 'org.mockito:mockito-core:5.11.0' + testImplementation 'org.projectlombok:lombok:1.18.28' compileOnly 'org.projectlombok:lombok:1.18.32' @@ -75,6 +80,7 @@ tasks.named('test') { processResources { expand(project.properties) + exclude 'logback-spring.xml' } springBoot { diff --git a/helm/values.yaml b/helm/values.yaml index 81e5aad2..02ef0956 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -44,6 +44,9 @@ microservice-chart: securityContext: allowPrivilegeEscalation: false + runAsNonRoot: true + runAsUser: 65534 + runAsGroup: 65534 externalConfigMapFiles: create: true diff --git a/src/main/java/it/gov/pagopa/payhub/auth/configuration/AuthErrorManagerConfig.java b/src/main/java/it/gov/pagopa/payhub/auth/configuration/AuthErrorManagerConfig.java new file mode 100644 index 00000000..274b9939 --- /dev/null +++ b/src/main/java/it/gov/pagopa/payhub/auth/configuration/AuthErrorManagerConfig.java @@ -0,0 +1,21 @@ +package it.gov.pagopa.payhub.auth.configuration; + +import it.gov.pagopa.payhub.auth.constants.AuthConstants; +import it.gov.pagopa.payhub.common.web.dto.ErrorDTO; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class AuthErrorManagerConfig { + @Bean + ErrorDTO defaultErrorDTO() { + return new ErrorDTO( + AuthConstants.ExceptionCode.GENERIC_ERROR, + "A generic error occurred" + ); + } + @Bean + ErrorDTO templateValidationErrorDTO(){ + return new ErrorDTO(AuthConstants.ExceptionCode.INVALID_REQUEST, null); + } +} diff --git a/src/main/java/it/gov/pagopa/payhub/auth/configuration/ServiceExceptionConfig.java b/src/main/java/it/gov/pagopa/payhub/auth/configuration/ServiceExceptionConfig.java new file mode 100644 index 00000000..641d5b8f --- /dev/null +++ b/src/main/java/it/gov/pagopa/payhub/auth/configuration/ServiceExceptionConfig.java @@ -0,0 +1,27 @@ +package it.gov.pagopa.payhub.auth.configuration; + +import it.gov.pagopa.payhub.auth.exception.InvalidTokenException; +import it.gov.pagopa.payhub.auth.exception.TokenExpiredException; +import it.gov.pagopa.payhub.common.web.exception.ServiceException; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class ServiceExceptionConfig { + + @Bean + public Map, HttpStatus> serviceExceptionMapper() { + Map, HttpStatus> exceptionMap = new HashMap<>(); + + //Unauthorized + exceptionMap.put(TokenExpiredException.class, HttpStatus.UNAUTHORIZED); + exceptionMap.put(InvalidTokenException.class, HttpStatus.UNAUTHORIZED); + + return exceptionMap; + } + +} diff --git a/src/main/java/it/gov/pagopa/payhub/auth/constants/AuthConstants.java b/src/main/java/it/gov/pagopa/payhub/auth/constants/AuthConstants.java new file mode 100644 index 00000000..c1afc8ee --- /dev/null +++ b/src/main/java/it/gov/pagopa/payhub/auth/constants/AuthConstants.java @@ -0,0 +1,14 @@ +package it.gov.pagopa.payhub.auth.constants; + +public class AuthConstants { + private AuthConstants() {} + public static final class ExceptionCode { + + public static final String TOKEN_DATE_EXPIRED = "AUTH_TOKEN_EXPIRED_DATE"; + public static final String GENERIC_ERROR = "AUTH_GENERIC_ERROR"; + public static final String INVALID_REQUEST = "AUTH_INVALID_REQUEST"; + public static final String INVALID_TOKEN = "AUTH_INVALID_TOKEN"; + + private ExceptionCode() {} + } +} diff --git a/src/main/java/it/gov/pagopa/payhub/auth/controller/AuthController.java b/src/main/java/it/gov/pagopa/payhub/auth/controller/AuthController.java new file mode 100644 index 00000000..52e0ddca --- /dev/null +++ b/src/main/java/it/gov/pagopa/payhub/auth/controller/AuthController.java @@ -0,0 +1,13 @@ +package it.gov.pagopa.payhub.auth.controller; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/payhub/") +interface AuthController { + + @PostMapping("auth") + @ResponseStatus(code = HttpStatus.OK) + void authToken(@RequestParam String token); +} \ No newline at end of file diff --git a/src/main/java/it/gov/pagopa/payhub/auth/controller/AuthControllerImpl.java b/src/main/java/it/gov/pagopa/payhub/auth/controller/AuthControllerImpl.java new file mode 100644 index 00000000..e111eeed --- /dev/null +++ b/src/main/java/it/gov/pagopa/payhub/auth/controller/AuthControllerImpl.java @@ -0,0 +1,19 @@ +package it.gov.pagopa.payhub.auth.controller; + +import it.gov.pagopa.payhub.auth.service.AuthService; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class AuthControllerImpl implements AuthController{ + + private final AuthService authService; + + public AuthControllerImpl(AuthService authService) { + this.authService = authService; + } + + @Override + public void authToken(String token) { + authService.authToken(token); + } +} diff --git a/src/main/java/it/gov/pagopa/payhub/auth/exception/InvalidTokenException.java b/src/main/java/it/gov/pagopa/payhub/auth/exception/InvalidTokenException.java new file mode 100644 index 00000000..6a8f158b --- /dev/null +++ b/src/main/java/it/gov/pagopa/payhub/auth/exception/InvalidTokenException.java @@ -0,0 +1,18 @@ +package it.gov.pagopa.payhub.auth.exception; + +import it.gov.pagopa.payhub.auth.constants.AuthConstants; +import it.gov.pagopa.payhub.common.web.exception.ServiceException; + +public class InvalidTokenException extends ServiceException { + public InvalidTokenException(String message) { + this(AuthConstants.ExceptionCode.INVALID_TOKEN, message); + } + + public InvalidTokenException(String code, String message) { + this(code, message, false, null); + } + + public InvalidTokenException(String code, String message, boolean printStackTrace, Throwable ex) { + super(code, message,printStackTrace, ex); + } +} diff --git a/src/main/java/it/gov/pagopa/payhub/auth/exception/TokenExpiredException.java b/src/main/java/it/gov/pagopa/payhub/auth/exception/TokenExpiredException.java new file mode 100644 index 00000000..189d925e --- /dev/null +++ b/src/main/java/it/gov/pagopa/payhub/auth/exception/TokenExpiredException.java @@ -0,0 +1,18 @@ +package it.gov.pagopa.payhub.auth.exception; + +import it.gov.pagopa.payhub.auth.constants.AuthConstants; +import it.gov.pagopa.payhub.common.web.exception.ServiceException; + +public class TokenExpiredException extends ServiceException { + public TokenExpiredException(String message) { + this(AuthConstants.ExceptionCode.TOKEN_DATE_EXPIRED, message); + } + + public TokenExpiredException(String code, String message) { + this(code, message, false, null); + } + + public TokenExpiredException(String code, String message, boolean printStackTrace, Throwable ex) { + super(code, message,printStackTrace, ex); + } +} diff --git a/src/main/java/it/gov/pagopa/payhub/auth/service/AuthService.java b/src/main/java/it/gov/pagopa/payhub/auth/service/AuthService.java new file mode 100644 index 00000000..61d281ad --- /dev/null +++ b/src/main/java/it/gov/pagopa/payhub/auth/service/AuthService.java @@ -0,0 +1,6 @@ +package it.gov.pagopa.payhub.auth.service; + +public interface AuthService { + + void authToken(String token); +} diff --git a/src/main/java/it/gov/pagopa/payhub/auth/service/AuthServiceImpl.java b/src/main/java/it/gov/pagopa/payhub/auth/service/AuthServiceImpl.java new file mode 100644 index 00000000..1047ee59 --- /dev/null +++ b/src/main/java/it/gov/pagopa/payhub/auth/service/AuthServiceImpl.java @@ -0,0 +1,34 @@ +package it.gov.pagopa.payhub.auth.service; + +import com.auth0.jwt.interfaces.Claim; +import it.gov.pagopa.payhub.auth.exception.InvalidTokenException; +import it.gov.pagopa.payhub.auth.utils.JWTValidator; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.Map; + +@Slf4j +@Service +public class AuthServiceImpl implements AuthService{ + private final String audience; + private final String issuer; + private final String urlJwkProvider; + + public AuthServiceImpl(@Value("${auth.token.audience:}")String audience, + @Value("${auth.token.issuer:}")String issuer, + @Value("${auth.token.jwk:}")String urlJwkProvider) { + this.audience = audience; + this.issuer = issuer; + this.urlJwkProvider = urlJwkProvider; + } + + @Override + public void authToken(String token) { + Map data = JWTValidator.validate(token, urlJwkProvider); + if (!(data.get("aud").asString().equals(audience) && data.get("iss").asString().equals(issuer))){ + throw new InvalidTokenException("Invalid audience or issuer in the token"); + } + } +} diff --git a/src/main/java/it/gov/pagopa/payhub/auth/utils/JWTValidator.java b/src/main/java/it/gov/pagopa/payhub/auth/utils/JWTValidator.java new file mode 100644 index 00000000..490d5ca8 --- /dev/null +++ b/src/main/java/it/gov/pagopa/payhub/auth/utils/JWTValidator.java @@ -0,0 +1,41 @@ +package it.gov.pagopa.payhub.auth.utils; + +import com.auth0.jwk.Jwk; +import com.auth0.jwk.JwkException; +import com.auth0.jwk.JwkProvider; +import com.auth0.jwk.UrlJwkProvider; +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTVerificationException; +import com.auth0.jwt.interfaces.Claim; +import com.auth0.jwt.interfaces.DecodedJWT; +import it.gov.pagopa.payhub.auth.exception.InvalidTokenException; +import it.gov.pagopa.payhub.auth.exception.TokenExpiredException; + +import java.security.interfaces.RSAPublicKey; +import java.util.Map; + +public class JWTValidator { + + private JWTValidator() {} + + public static Map validate(String token, String urlJwkProvider) { + try { + DecodedJWT jwt = JWT.decode(token); + + JwkProvider provider = new UrlJwkProvider(urlJwkProvider); + Jwk jwk = provider.get(jwt.getKeyId()); + Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null); + JWTVerifier verifier = JWT.require(algorithm).build(); + verifier.verify(token); + + return jwt.getClaims(); + + } catch (com.auth0.jwt.exceptions.TokenExpiredException e){ + throw new TokenExpiredException(e.getMessage()); + } catch (JwkException | JWTVerificationException ex) { + throw new InvalidTokenException("The token is not valid"); + } + } +} diff --git a/src/main/java/it/gov/pagopa/payhub/common/web/dto/ErrorDTO.java b/src/main/java/it/gov/pagopa/payhub/common/web/dto/ErrorDTO.java new file mode 100644 index 00000000..e1f06819 --- /dev/null +++ b/src/main/java/it/gov/pagopa/payhub/common/web/dto/ErrorDTO.java @@ -0,0 +1,21 @@ +package it.gov.pagopa.payhub.common.web.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@AllArgsConstructor +@NoArgsConstructor +@Data +@EqualsAndHashCode +public class ErrorDTO { + + @NotBlank + private String code; + @NotBlank + private String message; +} diff --git a/src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientException.java b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientException.java new file mode 100644 index 00000000..fb8fa41f --- /dev/null +++ b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientException.java @@ -0,0 +1,25 @@ +package it.gov.pagopa.payhub.common.web.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class ClientException extends RuntimeException { + private final HttpStatus httpStatus; + private final boolean printStackTrace; + + public ClientException(HttpStatus httpStatus, String message) { + this(httpStatus, message, null); + } + + public ClientException(HttpStatus httpStatus, String message, Throwable ex) { + this(httpStatus, message, false, ex); + } + + public ClientException( + HttpStatus httpStatus, String message, boolean printStackTrace, Throwable ex) { + super(message, ex); + this.httpStatus = httpStatus; + this.printStackTrace = printStackTrace; + } +} diff --git a/src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientExceptionNoBody.java b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientExceptionNoBody.java new file mode 100644 index 00000000..58c3648e --- /dev/null +++ b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientExceptionNoBody.java @@ -0,0 +1,20 @@ +package it.gov.pagopa.payhub.common.web.exception; + +import org.springframework.http.HttpStatus; + +public class ClientExceptionNoBody extends ClientException{ + + public ClientExceptionNoBody(HttpStatus httpStatus, String message) { + super(httpStatus, message); + } + + public ClientExceptionNoBody(HttpStatus httpStatus, String message, Throwable ex) { + super(httpStatus, message, ex); + } + + public ClientExceptionNoBody(HttpStatus httpStatus, String message, boolean printStackTrace, + Throwable ex) { + super(httpStatus, message, printStackTrace, ex); + } +} + diff --git a/src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientExceptionWithBody.java b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientExceptionWithBody.java new file mode 100644 index 00000000..c21d31c0 --- /dev/null +++ b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientExceptionWithBody.java @@ -0,0 +1,22 @@ +package it.gov.pagopa.payhub.common.web.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class ClientExceptionWithBody extends ClientException{ + private final String code; + + public ClientExceptionWithBody(HttpStatus httpStatus, String code, String message){ + this(httpStatus, code, message, null); + } + + public ClientExceptionWithBody(HttpStatus httpStatus, String code, String message, Throwable ex){ + this(httpStatus, code, message, false, ex); + } + + public ClientExceptionWithBody(HttpStatus httpStatus, String code, String message, boolean printStackTrace, Throwable ex){ + super(httpStatus, message, printStackTrace, ex); + this.code = code; + } +} diff --git a/src/main/java/it/gov/pagopa/payhub/common/web/exception/ErrorManager.java b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ErrorManager.java new file mode 100644 index 00000000..91643bdd --- /dev/null +++ b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ErrorManager.java @@ -0,0 +1,77 @@ +package it.gov.pagopa.payhub.common.web.exception; + +import it.gov.pagopa.payhub.common.web.dto.ErrorDTO; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.lang.Nullable; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.Optional; + +@RestControllerAdvice +@Slf4j +public class ErrorManager { + private final ErrorDTO defaultErrorDTO; + + public ErrorManager(@Nullable ErrorDTO defaultErrorDTO) { + this.defaultErrorDTO = Optional.ofNullable(defaultErrorDTO) + .orElse(new ErrorDTO("Error", "Something gone wrong")); + } + @ExceptionHandler(RuntimeException.class) + protected ResponseEntity handleException(RuntimeException error, HttpServletRequest request) { + logClientException(error, request); + + if(error instanceof ClientExceptionNoBody clientExceptionNoBody){ + return ResponseEntity.status(clientExceptionNoBody.getHttpStatus()).build(); + } + else { + ErrorDTO errorDTO; + HttpStatus httpStatus; + if (error instanceof ClientExceptionWithBody clientExceptionWithBody){ + httpStatus=clientExceptionWithBody.getHttpStatus(); + errorDTO = new ErrorDTO(clientExceptionWithBody.getCode(), error.getMessage()); + } + else { + httpStatus=HttpStatus.INTERNAL_SERVER_ERROR; + errorDTO = defaultErrorDTO; + } + return ResponseEntity.status(httpStatus) + .contentType(MediaType.APPLICATION_JSON) + .body(errorDTO); + } + } + public static void logClientException(RuntimeException error, HttpServletRequest request) { + Throwable unwrappedException = error.getCause() instanceof ServiceException + ? error.getCause() + : error; + + String clientExceptionMessage = ""; + if(error instanceof ClientException clientException) { + clientExceptionMessage = ": HttpStatus %s - %s%s".formatted( + clientException.getHttpStatus(), + (clientException instanceof ClientExceptionWithBody clientExceptionWithBody) ? clientExceptionWithBody.getCode() + ": " : "", + clientException.getMessage() + ); + } + + if(!(error instanceof ClientException clientException) || clientException.isPrintStackTrace() || unwrappedException.getCause() != null){ + log.error("Something went wrong handling request {}{}", getRequestDetails(request), clientExceptionMessage, unwrappedException); + } else { + log.info("A {} occurred handling request {}{} at {}", + unwrappedException.getClass().getSimpleName() , + getRequestDetails(request), + clientExceptionMessage, + unwrappedException.getStackTrace().length > 0 ? unwrappedException.getStackTrace()[0] : "UNKNOWN"); + } + } + + + public static String getRequestDetails(HttpServletRequest request) { + return "%s %s".formatted(request.getMethod(), request.getRequestURI()); + } + +} diff --git a/src/main/java/it/gov/pagopa/payhub/common/web/exception/ServiceException.java b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ServiceException.java new file mode 100644 index 00000000..e4911b5d --- /dev/null +++ b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ServiceException.java @@ -0,0 +1,19 @@ +package it.gov.pagopa.payhub.common.web.exception; + +import lombok.Getter; + +@Getter +public class ServiceException extends RuntimeException { + private final String code; + private final boolean printStackTrace; + + public ServiceException(String code, String message) { + this(code, message, false, null); + } + + public ServiceException(String code, String message, boolean printStackTrace, Throwable ex) { + super(message, ex); + this.code = code; + this.printStackTrace = printStackTrace; + } +} diff --git a/src/main/java/it/gov/pagopa/payhub/common/web/exception/ServiceExceptionHandler.java b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ServiceExceptionHandler.java new file mode 100644 index 00000000..9f00cf1b --- /dev/null +++ b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ServiceExceptionHandler.java @@ -0,0 +1,42 @@ +package it.gov.pagopa.payhub.common.web.exception; + +import it.gov.pagopa.payhub.common.web.dto.ErrorDTO; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.Map; + +@RestControllerAdvice +@Slf4j +@Order(Ordered.HIGHEST_PRECEDENCE) +public class ServiceExceptionHandler { + private final ErrorManager errorManager; + private final Map, HttpStatus> transcodeMap; + + public ServiceExceptionHandler(ErrorManager errorManager, Map, HttpStatus> transcodeMap) { + this.errorManager = errorManager; + this.transcodeMap = transcodeMap; + } + + @ExceptionHandler(ServiceException.class) + protected ResponseEntity handleException(ServiceException error, HttpServletRequest request) { + return errorManager.handleException(transcodeException(error), request); + } + + private ClientException transcodeException(ServiceException error) { + HttpStatus httpStatus = transcodeMap.get(error.getClass()); + + if (httpStatus == null) { + log.warn("Unhandled exception: {}", error.getClass().getName()); + httpStatus = HttpStatus.INTERNAL_SERVER_ERROR; + } + + return new ClientExceptionWithBody(httpStatus, error.getCode(), error.getMessage(), error.isPrintStackTrace(), error); + } +} \ No newline at end of file diff --git a/src/main/java/it/gov/pagopa/payhub/common/web/exception/ValidationExceptionHandler.java b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ValidationExceptionHandler.java new file mode 100644 index 00000000..0a32641b --- /dev/null +++ b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ValidationExceptionHandler.java @@ -0,0 +1,62 @@ +package it.gov.pagopa.payhub.common.web.exception; + +import it.gov.pagopa.payhub.common.web.dto.ErrorDTO; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.lang.Nullable; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingRequestHeaderException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.Optional; +import java.util.stream.Collectors; + +@RestControllerAdvice +@Slf4j +@Order(Ordered.HIGHEST_PRECEDENCE) +public class ValidationExceptionHandler { + + private final ErrorDTO templateValidationErrorDTO; + + public ValidationExceptionHandler(@Nullable ErrorDTO templateValidationErrorDTO) { + this.templateValidationErrorDTO = Optional.ofNullable(templateValidationErrorDTO) + .orElse(new ErrorDTO("INVALID_REQUEST", "Invalid request")); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorDTO handleValidationExceptions( + MethodArgumentNotValidException ex, HttpServletRequest request) { + + String message = ex.getBindingResult().getAllErrors().stream() + .map(error -> { + String fieldName = ((FieldError) error).getField(); + String errorMessage = error.getDefaultMessage(); + return String.format("[%s]: %s", fieldName, errorMessage); + }).collect(Collectors.joining("; ")); + + log.info("A MethodArgumentNotValidException occurred handling request {}: HttpStatus 400 - {}", + ErrorManager.getRequestDetails(request), message); + log.debug("Something went wrong while validating http request", ex); + return new ErrorDTO(templateValidationErrorDTO.getCode(), message); + } + + @ExceptionHandler(MissingRequestHeaderException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorDTO handleMissingRequestHeaderExceptions( + MissingRequestHeaderException ex, HttpServletRequest request) { + + String message = ex.getMessage(); + + log.info("A MissingRequestHeaderException occurred handling request {}: HttpStatus 400 - {}", + ErrorManager.getRequestDetails(request), message); + log.debug("Something went wrong handling request", ex); + return new ErrorDTO(templateValidationErrorDTO.getCode(), message); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 83a2167d..6c788dcb 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -5,6 +5,13 @@ spring: jmx.enabled: true +auth: + token: + issuer: "\${AUTH_TOKEN_ISS:https://dev.selfcare.pagopa.it}" + audience: "\${AUTH_TOKEN_AUD:dev.piattaformaunitaria.pagopa.it}" + jwk: "\${AUTH_TOKEN_JWK:https://dev.selfcare.pagopa.it}" + + management: endpoint: health: diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 00000000..f863ecd6 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + ${CONSOLE_LOG_PATTERN} + + + + + + %msg%n + + + + + true + 20000 + 0 + + + + + true + 20000 + 0 + + + + + + + + + + true + 20000 + 0 + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/it/gov/pagopa/payhub/auth/PayhubAuthApplicationTests.java b/src/test/java/it/gov/pagopa/payhub/auth/PayhubAuthApplicationTests.java deleted file mode 100644 index f7a69da9..00000000 --- a/src/test/java/it/gov/pagopa/payhub/auth/PayhubAuthApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package it.gov.pagopa.payhub.auth; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class PayhubAuthApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/src/test/java/it/gov/pagopa/payhub/auth/controller/AuthControllerTest.java b/src/test/java/it/gov/pagopa/payhub/auth/controller/AuthControllerTest.java new file mode 100644 index 00000000..2a714e44 --- /dev/null +++ b/src/test/java/it/gov/pagopa/payhub/auth/controller/AuthControllerTest.java @@ -0,0 +1,32 @@ +package it.gov.pagopa.payhub.auth.controller; + +import it.gov.pagopa.payhub.auth.service.AuthService; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +import static org.mockito.Mockito.doNothing; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(AuthControllerImpl.class) +public class AuthControllerTest { + @Autowired + private MockMvc mockMvc; + + @MockBean + AuthService authServiceMock; + + @Test + void authToken() throws Exception { + doNothing().when(authServiceMock).authToken(""); + + MvcResult result = mockMvc.perform( + get("payhub/auth/") + .param("token", "token") + ).andExpect(status().is2xxSuccessful()).andReturn(); + } +} diff --git a/src/test/java/it/gov/pagopa/payhub/common/exception/ErrorManagerTest.java b/src/test/java/it/gov/pagopa/payhub/common/exception/ErrorManagerTest.java new file mode 100644 index 00000000..0bdab2b8 --- /dev/null +++ b/src/test/java/it/gov/pagopa/payhub/common/exception/ErrorManagerTest.java @@ -0,0 +1,189 @@ +package it.gov.pagopa.payhub.common.exception; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.classic.spi.StackTraceElementProxy; +import it.gov.pagopa.payhub.common.utils.MemoryAppender; +import it.gov.pagopa.payhub.common.web.exception.ClientException; +import it.gov.pagopa.payhub.common.web.exception.ClientExceptionNoBody; +import it.gov.pagopa.payhub.common.web.exception.ClientExceptionWithBody; +import it.gov.pagopa.payhub.common.web.exception.ErrorManager; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.regex.Pattern; + +@ExtendWith(SpringExtension.class) +@WebMvcTest(value = {ErrorManagerTest.TestController.class}, excludeAutoConfiguration = SecurityAutoConfiguration.class) +@ContextConfiguration(classes = {ErrorManagerTest.TestController.class, ErrorManager.class}) +class ErrorManagerTest { + + public static final String EXPECTED_DEFAULT_ERROR = "{\"code\":\"Error\",\"message\":\"Something gone wrong\"}"; + @Autowired + private MockMvc mockMvc; + + private static MemoryAppender memoryAppender; + + @SpyBean + private TestController testControllerSpy; + + @RestController + @Slf4j + static class TestController { + + @GetMapping("/test") + String testEndpoint() { + return "OK"; + } + } + + @BeforeAll + static void configureMemoryAppender(){ + memoryAppender = new MemoryAppender(); + memoryAppender.setContext((LoggerContext) LoggerFactory.getILoggerFactory()); + memoryAppender.start(); + } + + @BeforeEach + void clearAndAppendMemoryAppender(){ + memoryAppender.reset(); + + ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ErrorManager.class.getName()); + logger.setLevel(ch.qos.logback.classic.Level.INFO); + logger.addAppender(memoryAppender); + } + + @Test + void handleExceptionClientExceptionNoBody() throws Exception { + Mockito.doThrow(new ClientExceptionNoBody(HttpStatus.BAD_REQUEST, "NOTFOUND ClientExceptionNoBody")) + .when(testControllerSpy).testEndpoint(); + + mockMvc.perform(MockMvcRequestBuilders.get("/test") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isBadRequest()); + + checkStackTraceSuppressedLog(memoryAppender, + "A ClientExceptionNoBody occurred handling request GET /test: HttpStatus 400 BAD_REQUEST - NOTFOUND ClientExceptionNoBody at it.gov.pagopa.common.web.exception.ErrorManagerTest\\$TestController.testEndpoint\\(ErrorManagerTest.java:[0-9]+\\)"); + + } + + @Test + void handleExceptionClientExceptionWithBody() throws Exception { + Mockito.doThrow(new ClientExceptionWithBody(HttpStatus.BAD_REQUEST, "Error","Error ClientExceptionWithBody")) + .when(testControllerSpy).testEndpoint(); + + String errorClientExceptionWithBody= "{\"code\":\"Error\",\"message\":\"Error ClientExceptionWithBody\"}"; + + mockMvc.perform(MockMvcRequestBuilders.get("/test") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isBadRequest()) + .andExpect(MockMvcResultMatchers.content().json(errorClientExceptionWithBody)); + + Mockito.doThrow(new ClientExceptionWithBody(HttpStatus.BAD_REQUEST, "Error","Error ClientExceptionWithBody", new Throwable())) + .when(testControllerSpy).testEndpoint(); + + String errorClientExceptionWithBodyWithStatusAndTitleAndMessageAndThrowable= "{\"code\":\"Error\",\"message\":\"Error ClientExceptionWithBody\"}"; + + mockMvc.perform(MockMvcRequestBuilders.get("/test") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isBadRequest()) + .andExpect(MockMvcResultMatchers.content().json(errorClientExceptionWithBodyWithStatusAndTitleAndMessageAndThrowable)); + + } + + @Test + void handleExceptionClientExceptionTest() throws Exception { + + Mockito.doThrow(ClientException.class) + .when(testControllerSpy).testEndpoint(); + + mockMvc.perform(MockMvcRequestBuilders.get("/test") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isInternalServerError()) + .andExpect(MockMvcResultMatchers.content().json(EXPECTED_DEFAULT_ERROR)); + + checkStackTraceSuppressedLog(memoryAppender, "A ClientException occurred handling request GET /test: HttpStatus null - null at UNKNOWN"); + memoryAppender.reset(); + + Mockito.doThrow(new ClientException(HttpStatus.BAD_REQUEST, "ClientException with httpStatus and message")) + .when(testControllerSpy).testEndpoint(); + + mockMvc.perform(MockMvcRequestBuilders.get("/test") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isInternalServerError()) + .andExpect(MockMvcResultMatchers.content().json(EXPECTED_DEFAULT_ERROR)); + + checkStackTraceSuppressedLog(memoryAppender, "A ClientException occurred handling request GET /test: HttpStatus 400 BAD_REQUEST - ClientException with httpStatus and message at it.gov.pagopa.common.web.exception.ErrorManagerTest\\$TestController.testEndpoint\\(ErrorManagerTest.java:[0-9]+\\)"); + memoryAppender.reset(); + + + Mockito.doThrow(new ClientException(HttpStatus.BAD_REQUEST, "ClientException with httpStatus, message and throwable", new Throwable())) + .when(testControllerSpy).testEndpoint(); + + mockMvc.perform(MockMvcRequestBuilders.get("/test") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isInternalServerError()) + .andExpect(MockMvcResultMatchers.content().json(EXPECTED_DEFAULT_ERROR)); + + checkLog(memoryAppender, + "Something went wrong handling request GET /test: HttpStatus 400 BAD_REQUEST - ClientException with httpStatus, message and throwable", + "it.gov.pagopa.common.web.exception.ClientException: ClientException with httpStatus, message and throwable", + "it.gov.pagopa.common.web.exception.ErrorManagerTest$TestController.testEndpoint" + ); + } + + @Test + void handleExceptionRuntimeException() throws Exception { + Mockito.doThrow(RuntimeException.class) + .when(testControllerSpy).testEndpoint(); + + mockMvc.perform(MockMvcRequestBuilders.get("/test") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isInternalServerError()) + .andExpect(MockMvcResultMatchers.content().json(EXPECTED_DEFAULT_ERROR)); + } + + public static void checkStackTraceSuppressedLog(MemoryAppender memoryAppender, String expectedLoggedMessage) { + String loggedMessage = memoryAppender.getLoggedEvents().get(0).getFormattedMessage(); + Assertions.assertTrue(Pattern.matches(expectedLoggedMessage, loggedMessage), + "Unexpected logged message: " + loggedMessage); + } + + + public static void checkLog(MemoryAppender memoryAppender, String expectedLoggedMessageRegexp, String expectedLoggedExceptionMessage, String expectedLoggedExceptionOccurrencePosition) { + ILoggingEvent loggedEvent = memoryAppender.getLoggedEvents().get(0); + IThrowableProxy loggedException = loggedEvent.getThrowableProxy(); + StackTraceElementProxy loggedExceptionOccurrenceStackTrace = loggedException.getStackTraceElementProxyArray()[0]; + + String loggedMessage = loggedEvent.getFormattedMessage(); + Assertions.assertTrue(Pattern.matches(expectedLoggedMessageRegexp, + loggedEvent.getFormattedMessage()), + "Unexpected logged message: " + loggedMessage); + + Assertions.assertEquals(expectedLoggedExceptionMessage, + loggedException.getClassName() + ": " + loggedException.getMessage()); + + Assertions.assertEquals(expectedLoggedExceptionOccurrencePosition, + loggedExceptionOccurrenceStackTrace.getStackTraceElement().getClassName() + "." + loggedExceptionOccurrenceStackTrace.getStackTraceElement().getMethodName()); + } +} diff --git a/src/test/java/it/gov/pagopa/payhub/common/exception/ServiceExceptionHandlerTest.java b/src/test/java/it/gov/pagopa/payhub/common/exception/ServiceExceptionHandlerTest.java new file mode 100644 index 00000000..0e18f142 --- /dev/null +++ b/src/test/java/it/gov/pagopa/payhub/common/exception/ServiceExceptionHandlerTest.java @@ -0,0 +1,73 @@ +package it.gov.pagopa.payhub.common.exception; + + +import ch.qos.logback.classic.LoggerContext; +import it.gov.pagopa.payhub.common.utils.MemoryAppender; +import it.gov.pagopa.payhub.common.web.exception.ErrorManager; +import it.gov.pagopa.payhub.common.web.exception.ServiceException; +import it.gov.pagopa.payhub.common.web.exception.ServiceExceptionHandler; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@ExtendWith(SpringExtension.class) +@WebMvcTest(value = { + ServiceExceptionHandlerTest.TestController.class}, excludeAutoConfiguration = SecurityAutoConfiguration.class) +@ContextConfiguration(classes = {ServiceExceptionHandler.class, + ServiceExceptionHandlerTest.TestController.class, ErrorManager.class}) +class ServiceExceptionHandlerTest { + @Autowired + private MockMvc mockMvc; + private static MemoryAppender memoryAppender; + + @BeforeAll + static void configureMemoryAppender(){ + memoryAppender = new MemoryAppender(); + memoryAppender.setContext((LoggerContext) LoggerFactory.getILoggerFactory()); + memoryAppender.start(); + } + + @BeforeEach + void clearAndAppendMemoryAppender(){ + memoryAppender.reset(); + + ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ErrorManager.class.getName()); + logger.setLevel(ch.qos.logback.classic.Level.INFO); + logger.addAppender(memoryAppender); + } + + @RestController + @Slf4j + static class TestController { + @GetMapping("/test") + String test() { + throw new ServiceException("DUMMY_CODE", "DUMMY_MESSAGE"); + } + } + + @Test + void testSimpleException() throws Exception{ + mockMvc.perform(MockMvcRequestBuilders.get("/test") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isInternalServerError()) + .andExpect(MockMvcResultMatchers.content().json("{\"code\":\"DUMMY_CODE\",\"message\":\"DUMMY_MESSAGE\"}", false)); + + + ErrorManagerTest.checkStackTraceSuppressedLog(memoryAppender, "A ServiceException occurred handling request GET /test: HttpStatus 500 INTERNAL_SERVER_ERROR - DUMMY_CODE: DUMMY_MESSAGE at it.gov.pagopa.common.web.exception.ServiceExceptionHandlerTest\\$TestController.test\\(ServiceExceptionHandlerTest.java:[0-9]+\\)"); + } + +} diff --git a/src/test/java/it/gov/pagopa/payhub/common/exception/ValidationExceptionHandlerTest.java b/src/test/java/it/gov/pagopa/payhub/common/exception/ValidationExceptionHandlerTest.java new file mode 100644 index 00000000..24a665ae --- /dev/null +++ b/src/test/java/it/gov/pagopa/payhub/common/exception/ValidationExceptionHandlerTest.java @@ -0,0 +1,89 @@ +package it.gov.pagopa.payhub.common.exception; + +import com.fasterxml.jackson.databind.ObjectMapper; +import it.gov.pagopa.payhub.common.web.exception.ValidationExceptionHandler; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.http.MediaType; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; +@ExtendWith({SpringExtension.class, MockitoExtension.class}) +@WebMvcTest(value = {ValidationExceptionHandlerTest.TestController.class}, excludeAutoConfiguration = SecurityAutoConfiguration.class) +@ContextConfiguration(classes = { + ValidationExceptionHandlerTest.TestController.class, + ValidationExceptionHandler.class}) +class ValidationExceptionHandlerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + ObjectMapper objectMapper; + + @SpyBean + private TestController testControllerSpy; + + @RestController + @Slf4j + static class TestController { + + @PutMapping("/test") + String testEndpoint(@RequestBody @Valid ValidationDTO body, @RequestHeader("data") String data) { + return "OK"; + } + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + static class ValidationDTO { + @NotBlank(message = "The field is mandatory!") + private String data; + } + + private final ValidationDTO VALIDATION_DTO = new ValidationDTO("data"); + + @Test + void handleMethodArgumentNotValidException() throws Exception { + + mockMvc.perform(MockMvcRequestBuilders.put("/test") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(new ValidationDTO(""))) + .header("data", "data") + .accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isBadRequest()) + .andExpect(MockMvcResultMatchers.jsonPath("$.code").value("INVALID_REQUEST")) + .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("[data]: The field is mandatory!")); + } + + @Test + void handleMissingRequestHeaderException() throws Exception { + + mockMvc.perform(MockMvcRequestBuilders.put("/test") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(VALIDATION_DTO)) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isBadRequest()) + .andExpect(MockMvcResultMatchers.jsonPath("$.code").value("INVALID_REQUEST")) + .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("Required request header 'data' for method parameter type String is not present")); + + } +} \ No newline at end of file diff --git a/src/test/java/it/gov/pagopa/payhub/common/utils/MemoryAppender.java b/src/test/java/it/gov/pagopa/payhub/common/utils/MemoryAppender.java new file mode 100644 index 00000000..d22369a6 --- /dev/null +++ b/src/test/java/it/gov/pagopa/payhub/common/utils/MemoryAppender.java @@ -0,0 +1,27 @@ +package it.gov.pagopa.payhub.common.utils; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; + +import java.util.Collections; +import java.util.List; + +public class MemoryAppender extends ListAppender { + public void reset() { + this.list.clear(); + } + + public boolean contains(ch.qos.logback.classic.Level level, String string) { + return this.list.stream() + .anyMatch(event -> event.toString().contains(string) + && event.getLevel().equals(level)); + } + + public int getSize() { + return this.list.size(); + } + + public List getLoggedEvents() { + return Collections.unmodifiableList(this.list); + } +} From 87a43f8f146a90d73e26a9293b7c8f10643b0dd0 Mon Sep 17 00:00:00 2001 From: LarissaASLeite Date: Thu, 9 May 2024 18:13:26 +0200 Subject: [PATCH 02/18] P4ADEV-24P4ADEV-248-creation-api-get-token-selfcare --- build.gradle | 15 +- helm/Chart.lock | 6 + helm/charts/microservice-chart-5.9.0.tgz | Bin 0 -> 10179 bytes .../auth/controller/AuthController.java | 4 +- .../payhub/auth/service/AuthServiceImpl.java | 10 +- .../payhub/auth/utils/JWTValidator.java | 14 +- .../common/web/exception/ClientException.java | 25 --- .../web/exception/ClientExceptionNoBody.java | 20 --- .../exception/ClientExceptionWithBody.java | 22 --- .../common/web/exception/ErrorManager.java | 66 ++++---- .../exception/ServiceExceptionHandler.java | 42 ----- .../exception/ValidationExceptionHandler.java | 27 +--- .../auth/controller/AuthControllerTest.java | 35 +++- .../payhub/auth/service/AuthServiceTest.java | 149 ++++++++++++++++++ .../common/exception/ErrorManagerTest.java | 110 ++++--------- .../ServiceExceptionHandlerTest.java | 73 --------- .../ValidationExceptionHandlerTest.java | 36 +---- 17 files changed, 279 insertions(+), 375 deletions(-) create mode 100644 helm/Chart.lock create mode 100644 helm/charts/microservice-chart-5.9.0.tgz delete mode 100644 src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientException.java delete mode 100644 src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientExceptionNoBody.java delete mode 100644 src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientExceptionWithBody.java delete mode 100644 src/main/java/it/gov/pagopa/payhub/common/web/exception/ServiceExceptionHandler.java create mode 100644 src/test/java/it/gov/pagopa/payhub/auth/service/AuthServiceTest.java delete mode 100644 src/test/java/it/gov/pagopa/payhub/common/exception/ServiceExceptionHandlerTest.java diff --git a/build.gradle b/build.gradle index 513c9129..45c0f4cb 100644 --- a/build.gradle +++ b/build.gradle @@ -59,13 +59,16 @@ dependencies { // validation token jwt implementation 'com.auth0:java-jwt:4.4.0' implementation 'com.auth0:jwks-rsa:0.22.1' + implementation 'com.nimbusds:nimbus-jose-jwt:9.38-rc5' // TESTS - testImplementation 'org.springframework.boot:spring-boot-starter-test:3.2.5' - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.0-M1' - testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.11.0-M1' - testImplementation 'org.mockito:mockito-core:5.11.0' - testImplementation 'org.projectlombok:lombok:1.18.28' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.junit.jupiter:junit-jupiter-api' + testImplementation 'org.junit.jupiter:junit-jupiter-engine' + testImplementation 'org.mockito:mockito-core' + testImplementation 'org.projectlombok:lombok' + testImplementation 'org.wiremock:wiremock-standalone:3.5.4' + compileOnly 'org.projectlombok:lombok:1.18.32' @@ -74,7 +77,7 @@ dependencies { } -tasks.named('test') { +test { useJUnitPlatform() } diff --git a/helm/Chart.lock b/helm/Chart.lock new file mode 100644 index 00000000..36f672e1 --- /dev/null +++ b/helm/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: microservice-chart + repository: https://pagopa.github.io/aks-microservice-chart-blueprint + version: 5.9.0 +digest: sha256:b614dd4be4c439e182fe5e7102e959fcda019413ddb2430be77c7a080bb13de1 +generated: "2024-05-08T12:16:12.9457229+02:00" diff --git a/helm/charts/microservice-chart-5.9.0.tgz b/helm/charts/microservice-chart-5.9.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..2bbfa7b1186ff697e2ba85fe777676c363c8551c GIT binary patch literal 10179 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 xxjfvS()^Or=-3Mpdo data = JWTValidator.validate(token, urlJwkProvider); - if (!(data.get("aud").asString().equals(audience) && data.get("iss").asString().equals(issuer))){ + Map data = jwtValidator.validate(token, urlJwkProvider); + if (!(data.get("aud").equals(audience) && data.get("iss").equals(issuer))){ throw new InvalidTokenException("Invalid audience or issuer in the token"); } } diff --git a/src/main/java/it/gov/pagopa/payhub/auth/utils/JWTValidator.java b/src/main/java/it/gov/pagopa/payhub/auth/utils/JWTValidator.java index 490d5ca8..bd09cad0 100644 --- a/src/main/java/it/gov/pagopa/payhub/auth/utils/JWTValidator.java +++ b/src/main/java/it/gov/pagopa/payhub/auth/utils/JWTValidator.java @@ -8,19 +8,21 @@ import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTVerificationException; -import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT; +import it.gov.pagopa.payhub.auth.constants.AuthConstants; import it.gov.pagopa.payhub.auth.exception.InvalidTokenException; import it.gov.pagopa.payhub.auth.exception.TokenExpiredException; +import org.springframework.stereotype.Component; import java.security.interfaces.RSAPublicKey; +import java.util.HashMap; import java.util.Map; +@Component public class JWTValidator { - private JWTValidator() {} - public static Map validate(String token, String urlJwkProvider) { + public Map validate(String token, String urlJwkProvider) { try { DecodedJWT jwt = JWT.decode(token); @@ -30,12 +32,14 @@ public static Map validate(String token, String urlJwkProvider) { JWTVerifier verifier = JWT.require(algorithm).build(); verifier.verify(token); - return jwt.getClaims(); + Map claimsMap = new HashMap<>(); + jwt.getClaims().forEach((key, value) -> claimsMap.put(key, value.asString())); + return claimsMap; } catch (com.auth0.jwt.exceptions.TokenExpiredException e){ throw new TokenExpiredException(e.getMessage()); } catch (JwkException | JWTVerificationException ex) { - throw new InvalidTokenException("The token is not valid"); + throw new InvalidTokenException(AuthConstants.ExceptionCode.INVALID_TOKEN, "The token is not valid", true, ex); } } } diff --git a/src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientException.java b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientException.java deleted file mode 100644 index fb8fa41f..00000000 --- a/src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientException.java +++ /dev/null @@ -1,25 +0,0 @@ -package it.gov.pagopa.payhub.common.web.exception; - -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@Getter -public class ClientException extends RuntimeException { - private final HttpStatus httpStatus; - private final boolean printStackTrace; - - public ClientException(HttpStatus httpStatus, String message) { - this(httpStatus, message, null); - } - - public ClientException(HttpStatus httpStatus, String message, Throwable ex) { - this(httpStatus, message, false, ex); - } - - public ClientException( - HttpStatus httpStatus, String message, boolean printStackTrace, Throwable ex) { - super(message, ex); - this.httpStatus = httpStatus; - this.printStackTrace = printStackTrace; - } -} diff --git a/src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientExceptionNoBody.java b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientExceptionNoBody.java deleted file mode 100644 index 58c3648e..00000000 --- a/src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientExceptionNoBody.java +++ /dev/null @@ -1,20 +0,0 @@ -package it.gov.pagopa.payhub.common.web.exception; - -import org.springframework.http.HttpStatus; - -public class ClientExceptionNoBody extends ClientException{ - - public ClientExceptionNoBody(HttpStatus httpStatus, String message) { - super(httpStatus, message); - } - - public ClientExceptionNoBody(HttpStatus httpStatus, String message, Throwable ex) { - super(httpStatus, message, ex); - } - - public ClientExceptionNoBody(HttpStatus httpStatus, String message, boolean printStackTrace, - Throwable ex) { - super(httpStatus, message, printStackTrace, ex); - } -} - diff --git a/src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientExceptionWithBody.java b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientExceptionWithBody.java deleted file mode 100644 index c21d31c0..00000000 --- a/src/main/java/it/gov/pagopa/payhub/common/web/exception/ClientExceptionWithBody.java +++ /dev/null @@ -1,22 +0,0 @@ -package it.gov.pagopa.payhub.common.web.exception; - -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@Getter -public class ClientExceptionWithBody extends ClientException{ - private final String code; - - public ClientExceptionWithBody(HttpStatus httpStatus, String code, String message){ - this(httpStatus, code, message, null); - } - - public ClientExceptionWithBody(HttpStatus httpStatus, String code, String message, Throwable ex){ - this(httpStatus, code, message, false, ex); - } - - public ClientExceptionWithBody(HttpStatus httpStatus, String code, String message, boolean printStackTrace, Throwable ex){ - super(httpStatus, message, printStackTrace, ex); - this.code = code; - } -} diff --git a/src/main/java/it/gov/pagopa/payhub/common/web/exception/ErrorManager.java b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ErrorManager.java index 91643bdd..92ca6310 100644 --- a/src/main/java/it/gov/pagopa/payhub/common/web/exception/ErrorManager.java +++ b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ErrorManager.java @@ -10,66 +10,58 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; +import java.util.Map; import java.util.Optional; @RestControllerAdvice @Slf4j public class ErrorManager { private final ErrorDTO defaultErrorDTO; + private final Map, HttpStatus> transcodeMap; - public ErrorManager(@Nullable ErrorDTO defaultErrorDTO) { + + public ErrorManager(@Nullable ErrorDTO defaultErrorDTO, Map, HttpStatus> transcodeMap) { this.defaultErrorDTO = Optional.ofNullable(defaultErrorDTO) .orElse(new ErrorDTO("Error", "Something gone wrong")); + this.transcodeMap = transcodeMap; } @ExceptionHandler(RuntimeException.class) protected ResponseEntity handleException(RuntimeException error, HttpServletRequest request) { - logClientException(error, request); + logException(error, request); - if(error instanceof ClientExceptionNoBody clientExceptionNoBody){ - return ResponseEntity.status(clientExceptionNoBody.getHttpStatus()).build(); - } - else { - ErrorDTO errorDTO; - HttpStatus httpStatus; - if (error instanceof ClientExceptionWithBody clientExceptionWithBody){ - httpStatus=clientExceptionWithBody.getHttpStatus(); - errorDTO = new ErrorDTO(clientExceptionWithBody.getCode(), error.getMessage()); - } - else { - httpStatus=HttpStatus.INTERNAL_SERVER_ERROR; + HttpStatus httpStatus; + ErrorDTO errorDTO; + + if (error instanceof ServiceException serviceException) { + httpStatus = transcodeMap.get(error.getClass()); + if (httpStatus == null) { + log.warn("Unhandled exception: {}", error.getClass().getName()); + httpStatus = HttpStatus.INTERNAL_SERVER_ERROR; errorDTO = defaultErrorDTO; + }else { + errorDTO = new ErrorDTO(serviceException.getCode(), error.getMessage()); } - return ResponseEntity.status(httpStatus) - .contentType(MediaType.APPLICATION_JSON) - .body(errorDTO); + } else { + httpStatus = HttpStatus.INTERNAL_SERVER_ERROR; + errorDTO = defaultErrorDTO; } - } - public static void logClientException(RuntimeException error, HttpServletRequest request) { - Throwable unwrappedException = error.getCause() instanceof ServiceException - ? error.getCause() - : error; - String clientExceptionMessage = ""; - if(error instanceof ClientException clientException) { - clientExceptionMessage = ": HttpStatus %s - %s%s".formatted( - clientException.getHttpStatus(), - (clientException instanceof ClientExceptionWithBody clientExceptionWithBody) ? clientExceptionWithBody.getCode() + ": " : "", - clientException.getMessage() - ); - } + return ResponseEntity.status(httpStatus) + .contentType(MediaType.APPLICATION_JSON) + .body(errorDTO); + } - if(!(error instanceof ClientException clientException) || clientException.isPrintStackTrace() || unwrappedException.getCause() != null){ - log.error("Something went wrong handling request {}{}", getRequestDetails(request), clientExceptionMessage, unwrappedException); + public static void logException(RuntimeException error, HttpServletRequest request) { + if(!(error instanceof ServiceException serviceException) || serviceException.isPrintStackTrace() || error.getCause() != null){ + log.error("Something went wrong handling request {}", getRequestDetails(request), error); } else { - log.info("A {} occurred handling request {}{} at {}", - unwrappedException.getClass().getSimpleName() , + log.info("A {} occurred handling request {} at {}", + error.getClass().getSimpleName() , getRequestDetails(request), - clientExceptionMessage, - unwrappedException.getStackTrace().length > 0 ? unwrappedException.getStackTrace()[0] : "UNKNOWN"); + error.getStackTrace().length > 0 ? error.getStackTrace()[0] : "UNKNOWN"); } } - public static String getRequestDetails(HttpServletRequest request) { return "%s %s".formatted(request.getMethod(), request.getRequestURI()); } diff --git a/src/main/java/it/gov/pagopa/payhub/common/web/exception/ServiceExceptionHandler.java b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ServiceExceptionHandler.java deleted file mode 100644 index 9f00cf1b..00000000 --- a/src/main/java/it/gov/pagopa/payhub/common/web/exception/ServiceExceptionHandler.java +++ /dev/null @@ -1,42 +0,0 @@ -package it.gov.pagopa.payhub.common.web.exception; - -import it.gov.pagopa.payhub.common.web.dto.ErrorDTO; -import jakarta.servlet.http.HttpServletRequest; -import lombok.extern.slf4j.Slf4j; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RestControllerAdvice; - -import java.util.Map; - -@RestControllerAdvice -@Slf4j -@Order(Ordered.HIGHEST_PRECEDENCE) -public class ServiceExceptionHandler { - private final ErrorManager errorManager; - private final Map, HttpStatus> transcodeMap; - - public ServiceExceptionHandler(ErrorManager errorManager, Map, HttpStatus> transcodeMap) { - this.errorManager = errorManager; - this.transcodeMap = transcodeMap; - } - - @ExceptionHandler(ServiceException.class) - protected ResponseEntity handleException(ServiceException error, HttpServletRequest request) { - return errorManager.handleException(transcodeException(error), request); - } - - private ClientException transcodeException(ServiceException error) { - HttpStatus httpStatus = transcodeMap.get(error.getClass()); - - if (httpStatus == null) { - log.warn("Unhandled exception: {}", error.getClass().getName()); - httpStatus = HttpStatus.INTERNAL_SERVER_ERROR; - } - - return new ClientExceptionWithBody(httpStatus, error.getCode(), error.getMessage(), error.isPrintStackTrace(), error); - } -} \ No newline at end of file diff --git a/src/main/java/it/gov/pagopa/payhub/common/web/exception/ValidationExceptionHandler.java b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ValidationExceptionHandler.java index 0a32641b..889effe5 100644 --- a/src/main/java/it/gov/pagopa/payhub/common/web/exception/ValidationExceptionHandler.java +++ b/src/main/java/it/gov/pagopa/payhub/common/web/exception/ValidationExceptionHandler.java @@ -10,6 +10,7 @@ import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingRequestHeaderException; +import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; @@ -29,32 +30,14 @@ public ValidationExceptionHandler(@Nullable ErrorDTO templateValidationErrorDTO) .orElse(new ErrorDTO("INVALID_REQUEST", "Invalid request")); } - @ExceptionHandler(MethodArgumentNotValidException.class) + @ExceptionHandler(MissingServletRequestParameterException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) - public ErrorDTO handleValidationExceptions( - MethodArgumentNotValidException ex, HttpServletRequest request) { - - String message = ex.getBindingResult().getAllErrors().stream() - .map(error -> { - String fieldName = ((FieldError) error).getField(); - String errorMessage = error.getDefaultMessage(); - return String.format("[%s]: %s", fieldName, errorMessage); - }).collect(Collectors.joining("; ")); - - log.info("A MethodArgumentNotValidException occurred handling request {}: HttpStatus 400 - {}", - ErrorManager.getRequestDetails(request), message); - log.debug("Something went wrong while validating http request", ex); - return new ErrorDTO(templateValidationErrorDTO.getCode(), message); - } - - @ExceptionHandler(MissingRequestHeaderException.class) - @ResponseStatus(HttpStatus.BAD_REQUEST) - public ErrorDTO handleMissingRequestHeaderExceptions( - MissingRequestHeaderException ex, HttpServletRequest request) { + public ErrorDTO handleMissingServletRequestParameterException( + MissingServletRequestParameterException ex, HttpServletRequest request) { String message = ex.getMessage(); - log.info("A MissingRequestHeaderException occurred handling request {}: HttpStatus 400 - {}", + log.info("A MissingServletRequestParameterException occurred handling request {}: HttpStatus 400 - {}", ErrorManager.getRequestDetails(request), message); log.debug("Something went wrong handling request", ex); return new ErrorDTO(templateValidationErrorDTO.getCode(), message); diff --git a/src/test/java/it/gov/pagopa/payhub/auth/controller/AuthControllerTest.java b/src/test/java/it/gov/pagopa/payhub/auth/controller/AuthControllerTest.java index 2a714e44..629cc367 100644 --- a/src/test/java/it/gov/pagopa/payhub/auth/controller/AuthControllerTest.java +++ b/src/test/java/it/gov/pagopa/payhub/auth/controller/AuthControllerTest.java @@ -1,32 +1,61 @@ package it.gov.pagopa.payhub.auth.controller; +import com.fasterxml.jackson.databind.ObjectMapper; +import it.gov.pagopa.payhub.auth.configuration.AuthErrorManagerConfig; +import it.gov.pagopa.payhub.auth.constants.AuthConstants; import it.gov.pagopa.payhub.auth.service.AuthService; +import it.gov.pagopa.payhub.common.web.dto.ErrorDTO; +import it.gov.pagopa.payhub.common.web.exception.ValidationExceptionHandler; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(AuthControllerImpl.class) -public class AuthControllerTest { +@Import({AuthErrorManagerConfig.class, ValidationExceptionHandler.class}) +class AuthControllerTest { @Autowired private MockMvc mockMvc; + @Autowired + private ObjectMapper objectMapper; + @MockBean AuthService authServiceMock; @Test void authToken() throws Exception { - doNothing().when(authServiceMock).authToken(""); + doNothing().when(authServiceMock).authToken("token"); MvcResult result = mockMvc.perform( - get("payhub/auth/") + post("/payhub/auth") .param("token", "token") ).andExpect(status().is2xxSuccessful()).andReturn(); + + Assertions.assertNotNull(result); + } + + @Test + void authToken1() throws Exception { + doNothing().when(authServiceMock).authToken("token"); + + MvcResult result = mockMvc.perform( + post("/payhub/auth") + ).andExpect(status().isBadRequest()).andReturn(); + + ErrorDTO actual = objectMapper.readValue(result.getResponse().getContentAsString(), + ErrorDTO.class); + assertEquals(AuthConstants.ExceptionCode.INVALID_REQUEST, actual.getCode()); } } diff --git a/src/test/java/it/gov/pagopa/payhub/auth/service/AuthServiceTest.java b/src/test/java/it/gov/pagopa/payhub/auth/service/AuthServiceTest.java new file mode 100644 index 00000000..88c63658 --- /dev/null +++ b/src/test/java/it/gov/pagopa/payhub/auth/service/AuthServiceTest.java @@ -0,0 +1,149 @@ +package it.gov.pagopa.payhub.auth.service; + +import com.auth0.jwk.Jwk; +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jose.jwk.RSAKey; +import it.gov.pagopa.payhub.auth.constants.AuthConstants; +import it.gov.pagopa.payhub.auth.exception.InvalidTokenException; +import it.gov.pagopa.payhub.auth.utils.JWTValidator; +import org.json.JSONObject; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +public class AuthServiceTest { + + private AuthService authService; + private WireMockServer wireMockServer; + private static final String AUD = "AUD"; + private static final String ISS = "ISS"; + @Mock + private JWTValidator jwtValidator; + @Mock + private Jwk jwk; + private String setUp() throws Exception { + KeyPair keyPair = generateKeyPair(); + RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); + String token = generateToken(keyPair); + wireMockServer = new WireMockServer(wireMockConfig().dynamicPort()); + wireMockServer.start(); + + JWK jwk = new RSAKey.Builder(rsaPublicKey) + .keyID("my-key-id") + .build(); + JWKSet jwkSet = new JWKSet(jwk); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("keys", jwkSet.toJSONObject().get("keys")); + + WireMock.configureFor("localhost", wireMockServer.port()); + stubFor(get(urlEqualTo("/jwks/.well-known/jwks.json")) + .willReturn(aResponse() + .withHeader("Content-Type", "application/json") + .withBody(String.valueOf(jwkSet)))); + + authService = new AuthServiceImpl(AUD, ISS, getUrlJwkProvider(), jwtValidator); + + return token; + } + + @AfterEach + void clean(){ + wireMockServer.stop(); + } + @Test + void authToken() throws Exception { + String token = setUp(); + Map claimsMap = createJWKClaims(ISS, AUD); + + String wireMockUrl = getUrlJwkProvider(); + when(jwtValidator.validate(token, wireMockUrl)).thenReturn(claimsMap); + + authService.authToken(token); + Mockito.verify(jwtValidator, times(1)).validate(token, wireMockUrl); + } + + @Test + void authTokenWrongIss() throws Exception { + String token = setUp(); + Map claimsMap = createJWKClaims("ISS_FAKE", AUD); + + String wireMockUrl = getUrlJwkProvider(); + when(jwtValidator.validate(token, wireMockUrl)).thenReturn(claimsMap); + + InvalidTokenException result = + assertThrows(InvalidTokenException.class, () -> + authService.authToken(token)); + + assertEquals(AuthConstants.ExceptionCode.INVALID_TOKEN, result.getCode()); + } + + @Test + void authTokenWrongAud() throws Exception { + String token = setUp(); + Map claimsMap = createJWKClaims(ISS, "AUD_FAKE"); + + String wireMockUrl = getUrlJwkProvider(); + when(jwtValidator.validate(token, wireMockUrl)).thenReturn(claimsMap); + + InvalidTokenException result = + assertThrows(InvalidTokenException.class, () -> + authService.authToken(token)); + + assertEquals(AuthConstants.ExceptionCode.INVALID_TOKEN, result.getCode()); + } + + public static String generateToken(KeyPair keyPair) { + return JWT.create() + .withIssuer(ISS) + .withAudience(AUD) + .withKeyId("my-key-id") + .withJWTId("my-jwt-id") + .withExpiresAt(new Date(System.currentTimeMillis() + 3600000)) + .sign(Algorithm.RSA256((RSAPrivateKey) keyPair.getPrivate())); + } + + private String getUrlJwkProvider() { + return "http://localhost:" + wireMockServer.port() + "/jwks"; + } + + private static KeyPair generateKeyPair() throws Exception { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(2048); + return keyPairGenerator.generateKeyPair(); + } + + private Map createJWKClaims (String iss, String aud){ + Map claims = new HashMap<>(); + claims.put("iss", iss); + claims.put("aud", aud); + claims.put("exp", "1715267318"); + claims.put("jti", "my-key-id"); + return claims; + } +} diff --git a/src/test/java/it/gov/pagopa/payhub/common/exception/ErrorManagerTest.java b/src/test/java/it/gov/pagopa/payhub/common/exception/ErrorManagerTest.java index 0bdab2b8..671d5192 100644 --- a/src/test/java/it/gov/pagopa/payhub/common/exception/ErrorManagerTest.java +++ b/src/test/java/it/gov/pagopa/payhub/common/exception/ErrorManagerTest.java @@ -1,14 +1,11 @@ package it.gov.pagopa.payhub.common.exception; import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.IThrowableProxy; -import ch.qos.logback.classic.spi.StackTraceElementProxy; +import it.gov.pagopa.payhub.auth.configuration.ServiceExceptionConfig; +import it.gov.pagopa.payhub.auth.exception.InvalidTokenException; import it.gov.pagopa.payhub.common.utils.MemoryAppender; -import it.gov.pagopa.payhub.common.web.exception.ClientException; -import it.gov.pagopa.payhub.common.web.exception.ClientExceptionNoBody; -import it.gov.pagopa.payhub.common.web.exception.ClientExceptionWithBody; import it.gov.pagopa.payhub.common.web.exception.ErrorManager; +import it.gov.pagopa.payhub.common.web.exception.ServiceException; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -21,7 +18,6 @@ import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.SpyBean; -import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -35,7 +31,7 @@ @ExtendWith(SpringExtension.class) @WebMvcTest(value = {ErrorManagerTest.TestController.class}, excludeAutoConfiguration = SecurityAutoConfiguration.class) -@ContextConfiguration(classes = {ErrorManagerTest.TestController.class, ErrorManager.class}) +@ContextConfiguration(classes = {ErrorManagerTest.TestController.class, ErrorManager.class, ServiceExceptionConfig.class}) class ErrorManagerTest { public static final String EXPECTED_DEFAULT_ERROR = "{\"code\":\"Error\",\"message\":\"Something gone wrong\"}"; @@ -74,82 +70,47 @@ void clearAndAppendMemoryAppender(){ } @Test - void handleExceptionClientExceptionNoBody() throws Exception { - Mockito.doThrow(new ClientExceptionNoBody(HttpStatus.BAD_REQUEST, "NOTFOUND ClientExceptionNoBody")) - .when(testControllerSpy).testEndpoint(); - - mockMvc.perform(MockMvcRequestBuilders.get("/test") - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(MockMvcResultMatchers.status().isBadRequest()); + void handleExceptionInvalidToken() throws Exception { + Mockito.doThrow(new InvalidTokenException("401", "InvalidTokenError")) + .when(testControllerSpy).testEndpoint(); - checkStackTraceSuppressedLog(memoryAppender, - "A ClientExceptionNoBody occurred handling request GET /test: HttpStatus 400 BAD_REQUEST - NOTFOUND ClientExceptionNoBody at it.gov.pagopa.common.web.exception.ErrorManagerTest\\$TestController.testEndpoint\\(ErrorManagerTest.java:[0-9]+\\)"); + String errorInvalidTokenException = "{\"code\":\"401\",\"message\":\"InvalidTokenError\"}"; + mockMvc.perform(MockMvcRequestBuilders.get("/test") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isUnauthorized()) + .andExpect(MockMvcResultMatchers.content().json(errorInvalidTokenException)); } - @Test - void handleExceptionClientExceptionWithBody() throws Exception { - Mockito.doThrow(new ClientExceptionWithBody(HttpStatus.BAD_REQUEST, "Error","Error ClientExceptionWithBody")) + void handleExceptionInvalidTokenWithStackTrace() throws Exception { + Mockito.doThrow(new InvalidTokenException("401", "InvalidTokenError", true, new Throwable())) .when(testControllerSpy).testEndpoint(); - String errorClientExceptionWithBody= "{\"code\":\"Error\",\"message\":\"Error ClientExceptionWithBody\"}"; + String errorInvalidTokenExceptionStatusAndTitleAndMessageAndThrowable= "{\"code\":\"401\",\"message\":\"InvalidTokenError\"}"; mockMvc.perform(MockMvcRequestBuilders.get("/test") .contentType(MediaType.APPLICATION_JSON)) - .andExpect(MockMvcResultMatchers.status().isBadRequest()) - .andExpect(MockMvcResultMatchers.content().json(errorClientExceptionWithBody)); + .andExpect(MockMvcResultMatchers.status().isUnauthorized()) + .andExpect(MockMvcResultMatchers.content().json(errorInvalidTokenExceptionStatusAndTitleAndMessageAndThrowable)); - Mockito.doThrow(new ClientExceptionWithBody(HttpStatus.BAD_REQUEST, "Error","Error ClientExceptionWithBody", new Throwable())) - .when(testControllerSpy).testEndpoint(); - - String errorClientExceptionWithBodyWithStatusAndTitleAndMessageAndThrowable= "{\"code\":\"Error\",\"message\":\"Error ClientExceptionWithBody\"}"; - - mockMvc.perform(MockMvcRequestBuilders.get("/test") - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(MockMvcResultMatchers.status().isBadRequest()) - .andExpect(MockMvcResultMatchers.content().json(errorClientExceptionWithBodyWithStatusAndTitleAndMessageAndThrowable)); + checkLogException(memoryAppender, "Something went wrong handling request GET /test"); + memoryAppender.reset(); } @Test - void handleExceptionClientExceptionTest() throws Exception { + void handleServiceExceptionWithoutHTTPStatus() throws Exception { - Mockito.doThrow(ClientException.class) - .when(testControllerSpy).testEndpoint(); + Mockito.doThrow(ServiceException.class) + .when(testControllerSpy).testEndpoint(); mockMvc.perform(MockMvcRequestBuilders.get("/test") - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(MockMvcResultMatchers.status().isInternalServerError()) - .andExpect(MockMvcResultMatchers.content().json(EXPECTED_DEFAULT_ERROR)); + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().isInternalServerError()) + .andExpect(MockMvcResultMatchers.content().json(EXPECTED_DEFAULT_ERROR)); - checkStackTraceSuppressedLog(memoryAppender, "A ClientException occurred handling request GET /test: HttpStatus null - null at UNKNOWN"); + checkLogException(memoryAppender, "A ServiceException occurred handling request GET /test at UNKNOWN"); memoryAppender.reset(); - - Mockito.doThrow(new ClientException(HttpStatus.BAD_REQUEST, "ClientException with httpStatus and message")) - .when(testControllerSpy).testEndpoint(); - - mockMvc.perform(MockMvcRequestBuilders.get("/test") - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(MockMvcResultMatchers.status().isInternalServerError()) - .andExpect(MockMvcResultMatchers.content().json(EXPECTED_DEFAULT_ERROR)); - - checkStackTraceSuppressedLog(memoryAppender, "A ClientException occurred handling request GET /test: HttpStatus 400 BAD_REQUEST - ClientException with httpStatus and message at it.gov.pagopa.common.web.exception.ErrorManagerTest\\$TestController.testEndpoint\\(ErrorManagerTest.java:[0-9]+\\)"); - memoryAppender.reset(); - - - Mockito.doThrow(new ClientException(HttpStatus.BAD_REQUEST, "ClientException with httpStatus, message and throwable", new Throwable())) - .when(testControllerSpy).testEndpoint(); - - mockMvc.perform(MockMvcRequestBuilders.get("/test") - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(MockMvcResultMatchers.status().isInternalServerError()) - .andExpect(MockMvcResultMatchers.content().json(EXPECTED_DEFAULT_ERROR)); - - checkLog(memoryAppender, - "Something went wrong handling request GET /test: HttpStatus 400 BAD_REQUEST - ClientException with httpStatus, message and throwable", - "it.gov.pagopa.common.web.exception.ClientException: ClientException with httpStatus, message and throwable", - "it.gov.pagopa.common.web.exception.ErrorManagerTest$TestController.testEndpoint" - ); } @Test @@ -163,27 +124,10 @@ void handleExceptionRuntimeException() throws Exception { .andExpect(MockMvcResultMatchers.content().json(EXPECTED_DEFAULT_ERROR)); } - public static void checkStackTraceSuppressedLog(MemoryAppender memoryAppender, String expectedLoggedMessage) { + public static void checkLogException(MemoryAppender memoryAppender, String expectedLoggedMessage) { String loggedMessage = memoryAppender.getLoggedEvents().get(0).getFormattedMessage(); Assertions.assertTrue(Pattern.matches(expectedLoggedMessage, loggedMessage), "Unexpected logged message: " + loggedMessage); } - - public static void checkLog(MemoryAppender memoryAppender, String expectedLoggedMessageRegexp, String expectedLoggedExceptionMessage, String expectedLoggedExceptionOccurrencePosition) { - ILoggingEvent loggedEvent = memoryAppender.getLoggedEvents().get(0); - IThrowableProxy loggedException = loggedEvent.getThrowableProxy(); - StackTraceElementProxy loggedExceptionOccurrenceStackTrace = loggedException.getStackTraceElementProxyArray()[0]; - - String loggedMessage = loggedEvent.getFormattedMessage(); - Assertions.assertTrue(Pattern.matches(expectedLoggedMessageRegexp, - loggedEvent.getFormattedMessage()), - "Unexpected logged message: " + loggedMessage); - - Assertions.assertEquals(expectedLoggedExceptionMessage, - loggedException.getClassName() + ": " + loggedException.getMessage()); - - Assertions.assertEquals(expectedLoggedExceptionOccurrencePosition, - loggedExceptionOccurrenceStackTrace.getStackTraceElement().getClassName() + "." + loggedExceptionOccurrenceStackTrace.getStackTraceElement().getMethodName()); - } } diff --git a/src/test/java/it/gov/pagopa/payhub/common/exception/ServiceExceptionHandlerTest.java b/src/test/java/it/gov/pagopa/payhub/common/exception/ServiceExceptionHandlerTest.java deleted file mode 100644 index 0e18f142..00000000 --- a/src/test/java/it/gov/pagopa/payhub/common/exception/ServiceExceptionHandlerTest.java +++ /dev/null @@ -1,73 +0,0 @@ -package it.gov.pagopa.payhub.common.exception; - - -import ch.qos.logback.classic.LoggerContext; -import it.gov.pagopa.payhub.common.utils.MemoryAppender; -import it.gov.pagopa.payhub.common.web.exception.ErrorManager; -import it.gov.pagopa.payhub.common.web.exception.ServiceException; -import it.gov.pagopa.payhub.common.web.exception.ServiceExceptionHandler; -import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.http.MediaType; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import org.springframework.test.web.servlet.result.MockMvcResultMatchers; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -@ExtendWith(SpringExtension.class) -@WebMvcTest(value = { - ServiceExceptionHandlerTest.TestController.class}, excludeAutoConfiguration = SecurityAutoConfiguration.class) -@ContextConfiguration(classes = {ServiceExceptionHandler.class, - ServiceExceptionHandlerTest.TestController.class, ErrorManager.class}) -class ServiceExceptionHandlerTest { - @Autowired - private MockMvc mockMvc; - private static MemoryAppender memoryAppender; - - @BeforeAll - static void configureMemoryAppender(){ - memoryAppender = new MemoryAppender(); - memoryAppender.setContext((LoggerContext) LoggerFactory.getILoggerFactory()); - memoryAppender.start(); - } - - @BeforeEach - void clearAndAppendMemoryAppender(){ - memoryAppender.reset(); - - ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ErrorManager.class.getName()); - logger.setLevel(ch.qos.logback.classic.Level.INFO); - logger.addAppender(memoryAppender); - } - - @RestController - @Slf4j - static class TestController { - @GetMapping("/test") - String test() { - throw new ServiceException("DUMMY_CODE", "DUMMY_MESSAGE"); - } - } - - @Test - void testSimpleException() throws Exception{ - mockMvc.perform(MockMvcRequestBuilders.get("/test") - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(MockMvcResultMatchers.status().isInternalServerError()) - .andExpect(MockMvcResultMatchers.content().json("{\"code\":\"DUMMY_CODE\",\"message\":\"DUMMY_MESSAGE\"}", false)); - - - ErrorManagerTest.checkStackTraceSuppressedLog(memoryAppender, "A ServiceException occurred handling request GET /test: HttpStatus 500 INTERNAL_SERVER_ERROR - DUMMY_CODE: DUMMY_MESSAGE at it.gov.pagopa.common.web.exception.ServiceExceptionHandlerTest\\$TestController.test\\(ServiceExceptionHandlerTest.java:[0-9]+\\)"); - } - -} diff --git a/src/test/java/it/gov/pagopa/payhub/common/exception/ValidationExceptionHandlerTest.java b/src/test/java/it/gov/pagopa/payhub/common/exception/ValidationExceptionHandlerTest.java index 24a665ae..84374d12 100644 --- a/src/test/java/it/gov/pagopa/payhub/common/exception/ValidationExceptionHandlerTest.java +++ b/src/test/java/it/gov/pagopa/payhub/common/exception/ValidationExceptionHandlerTest.java @@ -21,10 +21,8 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + @ExtendWith({SpringExtension.class, MockitoExtension.class}) @WebMvcTest(value = {ValidationExceptionHandlerTest.TestController.class}, excludeAutoConfiguration = SecurityAutoConfiguration.class) @ContextConfiguration(classes = { @@ -46,44 +44,20 @@ class ValidationExceptionHandlerTest { static class TestController { @PutMapping("/test") - String testEndpoint(@RequestBody @Valid ValidationDTO body, @RequestHeader("data") String data) { + String testEndpoint(@RequestParam("data") String data) { return "OK"; } } - @Data - @AllArgsConstructor - @NoArgsConstructor - static class ValidationDTO { - @NotBlank(message = "The field is mandatory!") - private String data; - } - - private final ValidationDTO VALIDATION_DTO = new ValidationDTO("data"); - - @Test - void handleMethodArgumentNotValidException() throws Exception { - - mockMvc.perform(MockMvcRequestBuilders.put("/test") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(new ValidationDTO(""))) - .header("data", "data") - .accept(MediaType.APPLICATION_JSON)) - .andExpect(MockMvcResultMatchers.status().isBadRequest()) - .andExpect(MockMvcResultMatchers.jsonPath("$.code").value("INVALID_REQUEST")) - .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("[data]: The field is mandatory!")); - } - @Test - void handleMissingRequestHeaderException() throws Exception { + void handleMissingServletRequestParameterException() throws Exception { mockMvc.perform(MockMvcRequestBuilders.put("/test") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(VALIDATION_DTO)) .accept(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.status().isBadRequest()) .andExpect(MockMvcResultMatchers.jsonPath("$.code").value("INVALID_REQUEST")) - .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("Required request header 'data' for method parameter type String is not present")); + .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("Required request parameter 'data' for method parameter type String is not present")); } } \ No newline at end of file From e2fc299ab3428676f47945df20a49012df1504d5 Mon Sep 17 00:00:00 2001 From: LarissaASLeite Date: Thu, 9 May 2024 18:38:28 +0200 Subject: [PATCH 03/18] P4ADEV-24P4ADEV-248-creation-api-get-token-selfcare --- build.gradle | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 45c0f4cb..6f47212b 100644 --- a/build.gradle +++ b/build.gradle @@ -78,7 +78,14 @@ dependencies { test { - useJUnitPlatform() + finalizedBy jacocoTestReport +} + +jacocoTestReport { + dependsOn test + reports { + xml.required = false + } } processResources { From 6001bb928a3c13621e88c1962bc9688d802de4d5 Mon Sep 17 00:00:00 2001 From: LarissaASLeite Date: Thu, 9 May 2024 18:45:17 +0200 Subject: [PATCH 04/18] P4ADEV-24P4ADEV-248-creation-api-get-token-selfcare --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 6f47212b..5ec46262 100644 --- a/build.gradle +++ b/build.gradle @@ -78,6 +78,7 @@ dependencies { test { + useJUnitPlatform() finalizedBy jacocoTestReport } From 8023cf5ad7437ebfe2ad4804210ca09322532221 Mon Sep 17 00:00:00 2001 From: LarissaASLeite Date: Thu, 9 May 2024 19:29:39 +0200 Subject: [PATCH 05/18] P4ADEV-24P4ADEV-248-creation-api-get-token-selfcare --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5ec46262..a30887b6 100644 --- a/build.gradle +++ b/build.gradle @@ -85,7 +85,7 @@ test { jacocoTestReport { dependsOn test reports { - xml.required = false + xml.enabled true } } From 3173ff3986bd8ddee7f1e3580412bf543cb9981b Mon Sep 17 00:00:00 2001 From: LarissaASLeite Date: Thu, 9 May 2024 19:46:15 +0200 Subject: [PATCH 06/18] P4ADEV-24P4ADEV-248-creation-api-get-token-selfcare --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a30887b6..5ec46262 100644 --- a/build.gradle +++ b/build.gradle @@ -85,7 +85,7 @@ test { jacocoTestReport { dependsOn test reports { - xml.enabled true + xml.required = false } } From bf891640328e66a2085fe867ef15e78f618b4257 Mon Sep 17 00:00:00 2001 From: LarissaASLeite Date: Fri, 10 May 2024 09:35:32 +0200 Subject: [PATCH 07/18] P4ADEV-24P4ADEV-248-creation-api-get-token-selfcare tests --- build.gradle | 5 +- .../payhub/auth/service/AuthServiceTest.java | 85 +++++-------------- .../payhub/auth/utils/JWTValidatorTest.java | 65 ++++++++++++++ .../payhub/auth/utils/JWTValidatorUtils.java | 72 ++++++++++++++++ 4 files changed, 160 insertions(+), 67 deletions(-) create mode 100644 src/test/java/it/gov/pagopa/payhub/auth/utils/JWTValidatorTest.java create mode 100644 src/test/java/it/gov/pagopa/payhub/auth/utils/JWTValidatorUtils.java diff --git a/build.gradle b/build.gradle index 5ec46262..939facec 100644 --- a/build.gradle +++ b/build.gradle @@ -85,10 +85,13 @@ test { jacocoTestReport { dependsOn test reports { - xml.required = false + html.enabled = true + xml.enabled = true + csv.enabled = false } } + processResources { expand(project.properties) exclude 'logback-spring.xml' diff --git a/src/test/java/it/gov/pagopa/payhub/auth/service/AuthServiceTest.java b/src/test/java/it/gov/pagopa/payhub/auth/service/AuthServiceTest.java index 88c63658..412e3dce 100644 --- a/src/test/java/it/gov/pagopa/payhub/auth/service/AuthServiceTest.java +++ b/src/test/java/it/gov/pagopa/payhub/auth/service/AuthServiceTest.java @@ -1,34 +1,22 @@ package it.gov.pagopa.payhub.auth.service; -import com.auth0.jwk.Jwk; -import com.auth0.jwt.JWT; -import com.auth0.jwt.algorithms.Algorithm; import com.github.tomakehurst.wiremock.WireMockServer; -import com.github.tomakehurst.wiremock.client.WireMock; -import com.nimbusds.jose.jwk.JWK; -import com.nimbusds.jose.jwk.JWKSet; -import com.nimbusds.jose.jwk.RSAKey; import it.gov.pagopa.payhub.auth.constants.AuthConstants; import it.gov.pagopa.payhub.auth.exception.InvalidTokenException; import it.gov.pagopa.payhub.auth.utils.JWTValidator; -import org.json.JSONObject; +import it.gov.pagopa.payhub.auth.utils.JWTValidatorUtils; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.Mockito; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.interfaces.RSAPrivateKey; -import java.security.interfaces.RSAPublicKey; import java.util.Date; import java.util.HashMap; import java.util.Map; -import static com.github.tomakehurst.wiremock.client.WireMock.*; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -36,40 +24,25 @@ import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) public class AuthServiceTest { - private AuthService authService; - private WireMockServer wireMockServer; + public static final Date EXPIRES_AT = new Date(System.currentTimeMillis() + 3600000); private static final String AUD = "AUD"; private static final String ISS = "ISS"; + + private AuthService authService; + private WireMockServer wireMockServer; + private JWTValidatorUtils utils; + @Mock private JWTValidator jwtValidator; - @Mock - private Jwk jwk; - private String setUp() throws Exception { - KeyPair keyPair = generateKeyPair(); - RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); - String token = generateToken(keyPair); + + @BeforeEach + void setup(){ wireMockServer = new WireMockServer(wireMockConfig().dynamicPort()); wireMockServer.start(); - - JWK jwk = new RSAKey.Builder(rsaPublicKey) - .keyID("my-key-id") - .build(); - JWKSet jwkSet = new JWKSet(jwk); - JSONObject jsonObject = new JSONObject(); - jsonObject.put("keys", jwkSet.toJSONObject().get("keys")); - - WireMock.configureFor("localhost", wireMockServer.port()); - stubFor(get(urlEqualTo("/jwks/.well-known/jwks.json")) - .willReturn(aResponse() - .withHeader("Content-Type", "application/json") - .withBody(String.valueOf(jwkSet)))); - - authService = new AuthServiceImpl(AUD, ISS, getUrlJwkProvider(), jwtValidator); - - return token; + utils = new JWTValidatorUtils(wireMockServer); + authService = new AuthServiceImpl(AUD, ISS, utils.getUrlJwkProvider(), jwtValidator); } @AfterEach @@ -78,10 +51,10 @@ void clean(){ } @Test void authToken() throws Exception { - String token = setUp(); + String token = utils.generateJWK(EXPIRES_AT); Map claimsMap = createJWKClaims(ISS, AUD); - String wireMockUrl = getUrlJwkProvider(); + String wireMockUrl = utils.getUrlJwkProvider(); when(jwtValidator.validate(token, wireMockUrl)).thenReturn(claimsMap); authService.authToken(token); @@ -90,10 +63,10 @@ void authToken() throws Exception { @Test void authTokenWrongIss() throws Exception { - String token = setUp(); + String token = utils.generateJWK(EXPIRES_AT); Map claimsMap = createJWKClaims("ISS_FAKE", AUD); - String wireMockUrl = getUrlJwkProvider(); + String wireMockUrl = utils.getUrlJwkProvider(); when(jwtValidator.validate(token, wireMockUrl)).thenReturn(claimsMap); InvalidTokenException result = @@ -105,10 +78,10 @@ void authTokenWrongIss() throws Exception { @Test void authTokenWrongAud() throws Exception { - String token = setUp(); + String token = utils.generateJWK(EXPIRES_AT); Map claimsMap = createJWKClaims(ISS, "AUD_FAKE"); - String wireMockUrl = getUrlJwkProvider(); + String wireMockUrl = utils.getUrlJwkProvider(); when(jwtValidator.validate(token, wireMockUrl)).thenReturn(claimsMap); InvalidTokenException result = @@ -118,26 +91,6 @@ void authTokenWrongAud() throws Exception { assertEquals(AuthConstants.ExceptionCode.INVALID_TOKEN, result.getCode()); } - public static String generateToken(KeyPair keyPair) { - return JWT.create() - .withIssuer(ISS) - .withAudience(AUD) - .withKeyId("my-key-id") - .withJWTId("my-jwt-id") - .withExpiresAt(new Date(System.currentTimeMillis() + 3600000)) - .sign(Algorithm.RSA256((RSAPrivateKey) keyPair.getPrivate())); - } - - private String getUrlJwkProvider() { - return "http://localhost:" + wireMockServer.port() + "/jwks"; - } - - private static KeyPair generateKeyPair() throws Exception { - KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); - keyPairGenerator.initialize(2048); - return keyPairGenerator.generateKeyPair(); - } - private Map createJWKClaims (String iss, String aud){ Map claims = new HashMap<>(); claims.put("iss", iss); diff --git a/src/test/java/it/gov/pagopa/payhub/auth/utils/JWTValidatorTest.java b/src/test/java/it/gov/pagopa/payhub/auth/utils/JWTValidatorTest.java new file mode 100644 index 00000000..ef4c35f5 --- /dev/null +++ b/src/test/java/it/gov/pagopa/payhub/auth/utils/JWTValidatorTest.java @@ -0,0 +1,65 @@ +package it.gov.pagopa.payhub.auth.utils; + +import com.github.tomakehurst.wiremock.WireMockServer; +import it.gov.pagopa.payhub.auth.exception.InvalidTokenException; +import it.gov.pagopa.payhub.auth.exception.TokenExpiredException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.Date; +import java.util.Map; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ExtendWith(SpringExtension.class) +class JWTValidatorTest { + + private JWTValidator jwtValidator; + private WireMockServer wireMockServer; + private JWTValidatorUtils utils; + + @BeforeEach + void setup(){ + wireMockServer = new WireMockServer(wireMockConfig().dynamicPort()); + wireMockServer.start(); + utils = new JWTValidatorUtils(wireMockServer); + jwtValidator = new JWTValidator(); + } + + @AfterEach + void clean(){ + wireMockServer.stop(); + } + + @Test + void validateToken() throws Exception { + String token = utils.generateJWK(new Date(System.currentTimeMillis() + 3600000)); + + String urlJwkProvider = utils.getUrlJwkProvider(); + + Map claimsMap = jwtValidator.validate(token, urlJwkProvider); + + assertNotNull(claimsMap); + } + + @Test + void validate_ExpiredToken_ThrowsTokenExpiredException() throws Exception { + String expiredToken = utils.generateJWK(new Date(System.currentTimeMillis() - 3600000)); + String urlJwkProvider = utils.getUrlJwkProvider(); + + assertThrows(TokenExpiredException.class, () -> jwtValidator.validate(expiredToken, urlJwkProvider)); + } + + @Test + void validate_InvalidToken_ThrowsInvalidTokenException() { + String invalidToken = "your_invalid_token_here"; + String urlJwkProvider = "your_jwk_provider_url_here"; + + assertThrows(InvalidTokenException.class, () -> jwtValidator.validate(invalidToken, urlJwkProvider)); + } +} diff --git a/src/test/java/it/gov/pagopa/payhub/auth/utils/JWTValidatorUtils.java b/src/test/java/it/gov/pagopa/payhub/auth/utils/JWTValidatorUtils.java new file mode 100644 index 00000000..f794bd2e --- /dev/null +++ b/src/test/java/it/gov/pagopa/payhub/auth/utils/JWTValidatorUtils.java @@ -0,0 +1,72 @@ +package it.gov.pagopa.payhub.auth.utils; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jose.jwk.RSAKey; +import org.json.JSONObject; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.util.Date; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; + +public class JWTValidatorUtils { + + private final WireMockServer wireMockServer; + + private static final String AUD = "AUD"; + private static final String ISS = "ISS"; + + public JWTValidatorUtils(WireMockServer wireMockServer) { + this.wireMockServer = wireMockServer; + } + + public String generateJWK(Date expiresAt) throws Exception { + KeyPair keyPair = generateKeyPair(); + RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); + + String token = generateToken(keyPair, expiresAt); + + JWK jwk = new RSAKey.Builder(rsaPublicKey) + .keyID("my-key-id") + .build(); + JWKSet jwkSet = new JWKSet(jwk); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("keys", jwkSet.toJSONObject().get("keys")); + + WireMock.configureFor("localhost", wireMockServer.port()); + stubFor(get(urlEqualTo("/jwks/.well-known/jwks.json")) + .willReturn(aResponse() + .withHeader("Content-Type", "application/json") + .withBody(String.valueOf(jwkSet)))); + + return token; + } + + public static String generateToken(KeyPair keyPair, Date expiresAt) { + return JWT.create() + .withIssuer(ISS) + .withAudience(AUD) + .withKeyId("my-key-id") + .withJWTId("my-jwt-id") + .withExpiresAt(expiresAt) + .sign(Algorithm.RSA256((RSAPrivateKey) keyPair.getPrivate())); + } + + public String getUrlJwkProvider() { + return "http://localhost:" + wireMockServer.port() + "/jwks"; + } + + private static KeyPair generateKeyPair() throws Exception { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(2048); + return keyPairGenerator.generateKeyPair(); + } +} From 53b8193d4b378fb9eab3d5e1de1a8e2ec798567f Mon Sep 17 00:00:00 2001 From: LarissaASLeite Date: Fri, 10 May 2024 09:48:08 +0200 Subject: [PATCH 08/18] P4ADEV-24P4ADEV-248-creation-api-get-token-selfcare tests --- build.gradle | 5 +---- .../it/gov/pagopa/payhub/auth/service/AuthServiceImpl.java | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 939facec..511a36df 100644 --- a/build.gradle +++ b/build.gradle @@ -85,13 +85,10 @@ test { jacocoTestReport { dependsOn test reports { - html.enabled = true - xml.enabled = true - csv.enabled = false + xml.required = true } } - processResources { expand(project.properties) exclude 'logback-spring.xml' diff --git a/src/main/java/it/gov/pagopa/payhub/auth/service/AuthServiceImpl.java b/src/main/java/it/gov/pagopa/payhub/auth/service/AuthServiceImpl.java index 0c3bdf4a..87b49761 100644 --- a/src/main/java/it/gov/pagopa/payhub/auth/service/AuthServiceImpl.java +++ b/src/main/java/it/gov/pagopa/payhub/auth/service/AuthServiceImpl.java @@ -32,5 +32,6 @@ public void authToken(String token) { if (!(data.get("aud").equals(audience) && data.get("iss").equals(issuer))){ throw new InvalidTokenException("Invalid audience or issuer in the token"); } + log.info("Token validated successfully"); } } From 4c1e1755782beda2df3a04ab92431ba40a310a57 Mon Sep 17 00:00:00 2001 From: LarissaASLeite Date: Fri, 10 May 2024 12:10:26 +0200 Subject: [PATCH 09/18] P4ADEV-24P4ADEV-248-creation-api-get-token-selfcare java_tool_options --- Dockerfile | 2 +- helm/values-dev.yaml | 2 +- helm/values-prod.yaml | 2 +- helm/values-uat.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2fcb3891..aa9d4ef5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,4 +24,4 @@ RUN chown -R nobody:nobody /app EXPOSE 8080 USER 65534 # user nobody -ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app/app.jar"] +ENTRYPOINT ["java","-jar","/app/app.jar"] diff --git a/helm/values-dev.yaml b/helm/values-dev.yaml index 504eff27..0c18bd60 100644 --- a/helm/values-dev.yaml +++ b/helm/values-dev.yaml @@ -29,7 +29,7 @@ microservice-chart: envConfig: ENV: "DEV" - JAVA_OPTS: "-Djava.util.concurrent.ForkJoinPool.common.parallelism=7 -agentlib:jdwp=transport=dt_socket,server=y,address=8001,suspend=n -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=3002 -Dcom.sun.management.jmxremote.rmi.port=3003 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false" + JAVA_TOOL_OPTIONS: "-Xms128m -Xmx4g -Djava.util.concurrent.ForkJoinPool.common.parallelism=7 -Dio.netty.eventLoopThreads=100 -javaagent:/app/applicationinsights-agent.jar -Dapplicationinsights.configuration.file=/mnt/file-config-external/appinsights-config/applicationinsights.json -agentlib:jdwp=transport=dt_socket,server=y,address=8001,suspend=n -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=3002 -Dcom.sun.management.jmxremote.rmi.port=3003 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false" keyvault: diff --git a/helm/values-prod.yaml b/helm/values-prod.yaml index 172dc94a..95572911 100644 --- a/helm/values-prod.yaml +++ b/helm/values-prod.yaml @@ -29,7 +29,7 @@ microservice-chart: envConfig: ENV: "PROD" - JAVA_OPTS: "-Djava.util.concurrent.ForkJoinPool.common.parallelism=7 -agentlib:jdwp=transport=dt_socket,server=y,address=8001,suspend=n -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=3002 -Dcom.sun.management.jmxremote.rmi.port=3003 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false" + JAVA_TOOL_OPTIONS: "-Xms128m -Xmx4g -Djava.util.concurrent.ForkJoinPool.common.parallelism=7 -Dio.netty.eventLoopThreads=100 -javaagent:/app/applicationinsights-agent.jar -Dapplicationinsights.configuration.file=/mnt/file-config-external/appinsights-config/applicationinsights.json -agentlib:jdwp=transport=dt_socket,server=y,address=8001,suspend=n -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=3002 -Dcom.sun.management.jmxremote.rmi.port=3003 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false" keyvault: diff --git a/helm/values-uat.yaml b/helm/values-uat.yaml index ed3f2cf0..b56ed042 100644 --- a/helm/values-uat.yaml +++ b/helm/values-uat.yaml @@ -29,7 +29,7 @@ microservice-chart: envConfig: ENV: "UAT" - JAVA_OPTS: "-Djava.util.concurrent.ForkJoinPool.common.parallelism=7 -agentlib:jdwp=transport=dt_socket,server=y,address=8001,suspend=n -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=3002 -Dcom.sun.management.jmxremote.rmi.port=3003 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false" + JAVA_TOOL_OPTIONS: "-Xms128m -Xmx4g -Djava.util.concurrent.ForkJoinPool.common.parallelism=7 -Dio.netty.eventLoopThreads=100 -javaagent:/app/applicationinsights-agent.jar -Dapplicationinsights.configuration.file=/mnt/file-config-external/appinsights-config/applicationinsights.json -agentlib:jdwp=transport=dt_socket,server=y,address=8001,suspend=n -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=3002 -Dcom.sun.management.jmxremote.rmi.port=3003 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false" keyvault: From 2444844728066f9bd6737cfd4b58a3e4a4c2c9b6 Mon Sep 17 00:00:00 2001 From: LarissaASLeite Date: Fri, 10 May 2024 15:09:31 +0200 Subject: [PATCH 10/18] P4ADEV-24P4ADEV-248-creation-api-get-token-selfcare --- .gitignore | 1 + build.gradle | 1 + helm/values-dev.yaml | 3 +-- helm/values-prod.yaml | 2 +- helm/values-uat.yaml | 2 +- .../it/gov/pagopa/payhub/auth/service/AuthServiceImpl.java | 3 ++- .../java/it/gov/pagopa/payhub/auth/utils/JWTValidator.java | 1 + 7 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index c2065bc2..70b813ca 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ build/ !gradle/wrapper/gradle-wrapper.jar !**/src/main/**/build/ !**/src/test/**/build/ +helm/charts/ ### STS ### .apt_generated diff --git a/build.gradle b/build.gradle index 511a36df..80b08ce5 100644 --- a/build.gradle +++ b/build.gradle @@ -60,6 +60,7 @@ dependencies { implementation 'com.auth0:java-jwt:4.4.0' implementation 'com.auth0:jwks-rsa:0.22.1' implementation 'com.nimbusds:nimbus-jose-jwt:9.38-rc5' + implementation 'io.jsonwebtoken:jjwt:0.12.5' // TESTS testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/helm/values-dev.yaml b/helm/values-dev.yaml index 0c18bd60..3a2c1ffc 100644 --- a/helm/values-dev.yaml +++ b/helm/values-dev.yaml @@ -29,8 +29,7 @@ microservice-chart: envConfig: ENV: "DEV" - JAVA_TOOL_OPTIONS: "-Xms128m -Xmx4g -Djava.util.concurrent.ForkJoinPool.common.parallelism=7 -Dio.netty.eventLoopThreads=100 -javaagent:/app/applicationinsights-agent.jar -Dapplicationinsights.configuration.file=/mnt/file-config-external/appinsights-config/applicationinsights.json -agentlib:jdwp=transport=dt_socket,server=y,address=8001,suspend=n -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=3002 -Dcom.sun.management.jmxremote.rmi.port=3003 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false" - + JAVA_TOOL_OPTIONS: "-Xms128m -Xmx4g -Djava.util.concurrent.ForkJoinPool.common.parallelism=7 -javaagent:/app/applicationinsights-agent.jar -Dapplicationinsights.configuration.file=/mnt/file-config-external/appinsights-config/applicationinsights.json -agentlib:jdwp=transport=dt_socket,server=y,address=8001,suspend=n -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=3002 -Dcom.sun.management.jmxremote.rmi.port=3003 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false" keyvault: name: "p4pa-d-payhub-kv" diff --git a/helm/values-prod.yaml b/helm/values-prod.yaml index 95572911..e6e9e0a4 100644 --- a/helm/values-prod.yaml +++ b/helm/values-prod.yaml @@ -29,7 +29,7 @@ microservice-chart: envConfig: ENV: "PROD" - JAVA_TOOL_OPTIONS: "-Xms128m -Xmx4g -Djava.util.concurrent.ForkJoinPool.common.parallelism=7 -Dio.netty.eventLoopThreads=100 -javaagent:/app/applicationinsights-agent.jar -Dapplicationinsights.configuration.file=/mnt/file-config-external/appinsights-config/applicationinsights.json -agentlib:jdwp=transport=dt_socket,server=y,address=8001,suspend=n -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=3002 -Dcom.sun.management.jmxremote.rmi.port=3003 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false" + JAVA_TOOL_OPTIONS: "-Xms128m -Xmx4g -Djava.util.concurrent.ForkJoinPool.common.parallelism=7 -javaagent:/app/applicationinsights-agent.jar -Dapplicationinsights.configuration.file=/mnt/file-config-external/appinsights-config/applicationinsights.json -agentlib:jdwp=transport=dt_socket,server=y,address=8001,suspend=n -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=3002 -Dcom.sun.management.jmxremote.rmi.port=3003 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false" keyvault: diff --git a/helm/values-uat.yaml b/helm/values-uat.yaml index b56ed042..0aef9a73 100644 --- a/helm/values-uat.yaml +++ b/helm/values-uat.yaml @@ -29,7 +29,7 @@ microservice-chart: envConfig: ENV: "UAT" - JAVA_TOOL_OPTIONS: "-Xms128m -Xmx4g -Djava.util.concurrent.ForkJoinPool.common.parallelism=7 -Dio.netty.eventLoopThreads=100 -javaagent:/app/applicationinsights-agent.jar -Dapplicationinsights.configuration.file=/mnt/file-config-external/appinsights-config/applicationinsights.json -agentlib:jdwp=transport=dt_socket,server=y,address=8001,suspend=n -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=3002 -Dcom.sun.management.jmxremote.rmi.port=3003 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false" + JAVA_TOOL_OPTIONS: "-Xms128m -Xmx4g -Djava.util.concurrent.ForkJoinPool.common.parallelism=7 -javaagent:/app/applicationinsights-agent.jar -Dapplicationinsights.configuration.file=/mnt/file-config-external/appinsights-config/applicationinsights.json -agentlib:jdwp=transport=dt_socket,server=y,address=8001,suspend=n -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=3002 -Dcom.sun.management.jmxremote.rmi.port=3003 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false" keyvault: diff --git a/src/main/java/it/gov/pagopa/payhub/auth/service/AuthServiceImpl.java b/src/main/java/it/gov/pagopa/payhub/auth/service/AuthServiceImpl.java index 87b49761..a3136bec 100644 --- a/src/main/java/it/gov/pagopa/payhub/auth/service/AuthServiceImpl.java +++ b/src/main/java/it/gov/pagopa/payhub/auth/service/AuthServiceImpl.java @@ -1,5 +1,6 @@ package it.gov.pagopa.payhub.auth.service; +import io.jsonwebtoken.Claims; import it.gov.pagopa.payhub.auth.exception.InvalidTokenException; import it.gov.pagopa.payhub.auth.utils.JWTValidator; import lombok.extern.slf4j.Slf4j; @@ -29,7 +30,7 @@ public AuthServiceImpl(@Value("${auth.token.audience:}")String audience, @Override public void authToken(String token) { Map data = jwtValidator.validate(token, urlJwkProvider); - if (!(data.get("aud").equals(audience) && data.get("iss").equals(issuer))){ + if (!(data.get(Claims.AUDIENCE).equals(audience) && data.get(Claims.ISSUER).equals(issuer))){ throw new InvalidTokenException("Invalid audience or issuer in the token"); } log.info("Token validated successfully"); diff --git a/src/main/java/it/gov/pagopa/payhub/auth/utils/JWTValidator.java b/src/main/java/it/gov/pagopa/payhub/auth/utils/JWTValidator.java index bd09cad0..5d450cb5 100644 --- a/src/main/java/it/gov/pagopa/payhub/auth/utils/JWTValidator.java +++ b/src/main/java/it/gov/pagopa/payhub/auth/utils/JWTValidator.java @@ -34,6 +34,7 @@ public Map validate(String token, String urlJwkProvider) { Map claimsMap = new HashMap<>(); jwt.getClaims().forEach((key, value) -> claimsMap.put(key, value.asString())); + return claimsMap; } catch (com.auth0.jwt.exceptions.TokenExpiredException e){ From 90e0f5606b9d1cbf458abbea2986c9cbf034fef9 Mon Sep 17 00:00:00 2001 From: LarissaASLeite Date: Fri, 10 May 2024 15:13:10 +0200 Subject: [PATCH 11/18] P4ADEV-24P4ADEV-248-creation-api-get-token-selfcare --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 70b813ca..cee3a891 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,8 @@ build/ !gradle/wrapper/gradle-wrapper.jar !**/src/main/**/build/ !**/src/test/**/build/ -helm/charts/ +charts/ +*.tgz ### STS ### .apt_generated From 8da92cd67e157ceeed80f66dfe6819d912068479 Mon Sep 17 00:00:00 2001 From: LarissaASLeite Date: Fri, 10 May 2024 15:19:33 +0200 Subject: [PATCH 12/18] Revert "P4ADEV-24P4ADEV-248-creation-api-get-token-selfcare" This reverts commit 90e0f5606b9d1cbf458abbea2986c9cbf034fef9. --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index cee3a891..70b813ca 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,7 @@ build/ !gradle/wrapper/gradle-wrapper.jar !**/src/main/**/build/ !**/src/test/**/build/ -charts/ -*.tgz +helm/charts/ ### STS ### .apt_generated From 700794e6a5069c9a5dd12948656222b9b8eb44f2 Mon Sep 17 00:00:00 2001 From: LarissaASLeite Date: Fri, 10 May 2024 15:20:33 +0200 Subject: [PATCH 13/18] P4ADEV-24P4ADEV-248-creation-api-get-token-selfcare --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 70b813ca..c2065bc2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ build/ !gradle/wrapper/gradle-wrapper.jar !**/src/main/**/build/ !**/src/test/**/build/ -helm/charts/ ### STS ### .apt_generated From 92576bd2de2482a8a08f119486e35f91af18271a Mon Sep 17 00:00:00 2001 From: LarissaASLeite Date: Fri, 10 May 2024 15:28:22 +0200 Subject: [PATCH 14/18] P4ADEV-24P4ADEV-248-creation-api-get-token-selfcare --- helm/charts/microservice-chart-5.9.0.tgz | Bin 10179 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 helm/charts/microservice-chart-5.9.0.tgz diff --git a/helm/charts/microservice-chart-5.9.0.tgz b/helm/charts/microservice-chart-5.9.0.tgz deleted file mode 100644 index 2bbfa7b1186ff697e2ba85fe777676c363c8551c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10179 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 xxjfvS()^Or=-3Mpdo Date: Fri, 10 May 2024 15:28:41 +0200 Subject: [PATCH 15/18] P4ADEV-24P4ADEV-248-creation-api-get-token-selfcare --- helm/charts/microservice-chart-5.9.0.tgz | Bin 0 -> 10179 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 helm/charts/microservice-chart-5.9.0.tgz diff --git a/helm/charts/microservice-chart-5.9.0.tgz b/helm/charts/microservice-chart-5.9.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..2bbfa7b1186ff697e2ba85fe777676c363c8551c GIT binary patch literal 10179 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 xxjfvS()^Or=-3Mpdo Date: Fri, 10 May 2024 15:29:52 +0200 Subject: [PATCH 16/18] P4ADEV-24P4ADEV-248-creation-api-get-token-selfcare --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c2065bc2..da11cd6a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ build/ !gradle/wrapper/gradle-wrapper.jar !**/src/main/**/build/ !**/src/test/**/build/ +*.tgz ### STS ### .apt_generated From e71c885977cf90e363bdc9a6be64452afd17c17b Mon Sep 17 00:00:00 2001 From: LarissaASLeite Date: Fri, 10 May 2024 15:34:55 +0200 Subject: [PATCH 17/18] P4ADEV-24P4ADEV-248-creation-api-get-token-selfcare --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index da11cd6a..d7bf4b45 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ build/ !**/src/main/**/build/ !**/src/test/**/build/ *.tgz +helm/charts ### STS ### .apt_generated From cd7aade64827942b61d5c45b10fd356ddda531ba Mon Sep 17 00:00:00 2001 From: LarissaASLeite Date: Fri, 10 May 2024 15:37:08 +0200 Subject: [PATCH 18/18] P4ADEV-24P4ADEV-248-creation-api-get-token-selfcare --- helm/charts/microservice-chart-5.9.0.tgz | Bin 10179 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 helm/charts/microservice-chart-5.9.0.tgz diff --git a/helm/charts/microservice-chart-5.9.0.tgz b/helm/charts/microservice-chart-5.9.0.tgz deleted file mode 100644 index 2bbfa7b1186ff697e2ba85fe777676c363c8551c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10179 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 xxjfvS()^Or=-3Mpdo