From 233387386b23a45ba495b39fccca31aa1c6eae04 Mon Sep 17 00:00:00 2001 From: Antonio Tarricone <110115827+antoniotarricone@users.noreply.github.com> Date: Fri, 15 Sep 2023 17:13:08 +0200 Subject: [PATCH] feat: Azure Key Vault usage. (#62) --- .mvn/wrapper/MavenWrapperDownloader.java | 113 +- dep-sha256.json | 1218 ++++++------ pom.xml | 86 +- .../{ErrorCode.java => AuthErrorCode.java} | 13 +- .../azurekeyvault/bean/CreateKeyRequest.java | 52 + .../azurekeyvault/bean/CreateKeyResponse.java | 34 + .../bean/GetAccessTokenResponse.java | 52 + .../azurekeyvault/bean/GetKeyResponse.java | 34 + .../bean/GetKeyVersionsResponse.java | 34 + .../azurekeyvault/bean/GetKeysResponse.java | 34 + .../mil/auth/azurekeyvault/bean/Key.java | 40 + .../azurekeyvault/bean/KeyAttributes.java | 76 + .../auth/azurekeyvault/bean/KeyDetails.java | 66 + .../azurekeyvault/bean/KeyNameAndVersion.java | 39 + .../auth/azurekeyvault/bean/KeyVersion.java | 24 + .../auth/azurekeyvault/bean/SignRequest.java | 40 + .../auth/azurekeyvault/bean/SignResponse.java | 40 + .../bean/VerifySignatureRequest.java | 46 + .../bean/VerifySignatureResponse.java | 34 + .../azurekeyvault/client/AzureAuthClient.java | 43 + .../client/AzureKeyVaultClient.java | 125 ++ .../service/AzureAuthService.java | 58 + .../azurekeyvault/service/AzureKeyFinder.java | 445 +++++ .../service/AzureKeyVaultService.java | 103 + .../service/AzureTokenSigner.java | 178 ++ .../mil/auth/azurekeyvault/util/KidUtil.java | 106 + .../azurekeyvault/util/SignedJWTFactory.java | 36 + .../swclient/mil/auth/bean/ClaimName.java | 22 + .../pagopa/swclient/mil/auth/bean/Client.java | 11 +- .../swclient/mil/auth/bean/FormParamName.java | 24 + .../mil/auth/bean/GetAccessToken.java | 157 -- .../mil/auth/bean/GetAccessTokenRequest.java | 139 ++ ...Token.java => GetAccessTokenResponse.java} | 24 +- .../swclient/mil/auth/bean/GrantType.java | 1 - .../mil/auth/bean/HeaderParamName.java | 21 + .../mil/auth/bean/JsonPropertyName.java | 30 + .../swclient/mil/auth/bean/KeyPair.java | 11 +- .../swclient/mil/auth/bean/KeyType.java | 1 - .../pagopa/swclient/mil/auth/bean/KeyUse.java | 1 - .../swclient/mil/auth/bean/PublicKey.java | 24 +- .../swclient/mil/auth/bean/PublicKeys.java | 8 +- .../pagopa/swclient/mil/auth/bean/Role.java | 15 +- .../pagopa/swclient/mil/auth/bean/Scope.java | 16 + .../swclient/mil/auth/bean/TokenType.java | 16 + .../pagopa/swclient/mil/auth/bean/User.java | 13 +- .../mil/auth/client/AuthDataRepository.java | 6 +- .../swclient/mil/auth/client/PoyntClient.java | 3 - .../mil/auth/qualifier/ClientCredentials.java | 13 +- .../swclient/mil/auth/qualifier/Password.java | 13 +- .../mil/auth/qualifier/PoyntToken.java | 13 +- .../mil/auth/qualifier/RefreshToken.java | 13 +- .../mil/auth/resource/JwksResource.java | 24 +- .../mil/auth/resource/TokenResource.java | 28 +- .../mil/auth/service/ClientVerifier.java | 47 +- .../swclient/mil/auth/service/KeyFinder.java | 135 +- .../mil/auth/service/KeyPairGenerator.java | 94 - .../mil/auth/service/RedisClient.java | 70 - .../auth/service/RefreshTokenVerifier.java | 55 - .../auth/service/RefreshTokensService.java | 143 +- .../mil/auth/service/RolesFinder.java | 57 +- .../service/TokenByClientSecretService.java | 8 +- .../auth/service/TokenByPasswordService.java | 87 +- .../service/TokenByPoyntTokenService.java | 36 +- .../mil/auth/service/TokenService.java | 130 +- .../mil/auth/service/TokenSigner.java | 35 + .../mil/auth/service/TokenVerifier.java | 215 -- .../swclient/mil/auth/util/AuthError.java | 8 +- .../swclient/mil/auth/util/AuthException.java | 8 +- .../swclient/mil/auth/util/KeyPairUtil.java | 62 - .../mil/auth/util/PasswordVerifier.java | 19 +- .../mil/auth/util/TokenGenerator.java | 82 - .../swclient/mil/auth/util/TokenSigner.java | 50 - .../swclient/mil/auth/util/UniGenerator.java | 9 +- .../constraints/ValidationTarget.java | 10 +- .../validation/constraints/Validator.java | 53 +- .../auth/validation/constraints/Verifier.java | 62 +- src/main/resources/META-INF/openapi.yaml | 6 +- src/main/resources/application.properties | 44 +- .../bean/KeyNameAndVersionTest.java | 39 + .../service/AzureKeyFinderTest.java | 1249 ++++++++++++ .../service/AzureTokenSignerTest.java | 533 +++++ .../mil/auth/client/PoyntClientTest.java | 59 + .../mil/auth/resource/JwksResourceTest.java | 231 +-- .../resource/RefreshTokensResourceTest.java | 660 +++++++ .../auth/resource/RequestValidationTest.java | 1409 ++++++------- .../TokenByClientSecretResourceTest.java | 803 ++++++++ ...ientSecretResourceUnexpectedErrorTest.java | 82 + .../resource/TokenByPasswordResourceTest.java | 483 +++++ .../TokenByPoyntTokenResourceTest.java | 298 +++ .../mil/auth/resource/TokenResourceTest.java | 1755 ----------------- ...kenResourceWithExcOnClientSecrVerTest.java | 107 - .../mil/auth/service/ClientVerifierTest.java | 248 +++ .../mil/auth/service/KeyFinderTest.java | 495 ----- .../service/KeyFinderWithExceptionTest.java | 81 - .../mil/auth/service/RolesFinderTest.java | 241 +++ .../service/TokenByPasswordServiceTest.java | 125 ++ 96 files changed, 8880 insertions(+), 5359 deletions(-) rename src/main/java/it/pagopa/swclient/mil/auth/{ErrorCode.java => AuthErrorCode.java} (90%) create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/CreateKeyRequest.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/CreateKeyResponse.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/GetAccessTokenResponse.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/GetKeyResponse.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/GetKeyVersionsResponse.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/GetKeysResponse.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/Key.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyAttributes.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyDetails.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyNameAndVersion.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyVersion.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/SignRequest.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/SignResponse.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/VerifySignatureRequest.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/VerifySignatureResponse.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/client/AzureAuthClient.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/client/AzureKeyVaultClient.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureAuthService.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureKeyFinder.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureKeyVaultService.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureTokenSigner.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/util/KidUtil.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/util/SignedJWTFactory.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/bean/ClaimName.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/bean/FormParamName.java delete mode 100644 src/main/java/it/pagopa/swclient/mil/auth/bean/GetAccessToken.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/bean/GetAccessTokenRequest.java rename src/main/java/it/pagopa/swclient/mil/auth/bean/{AccessToken.java => GetAccessTokenResponse.java} (59%) create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/bean/HeaderParamName.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/bean/JsonPropertyName.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/bean/Scope.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/bean/TokenType.java delete mode 100644 src/main/java/it/pagopa/swclient/mil/auth/service/KeyPairGenerator.java delete mode 100644 src/main/java/it/pagopa/swclient/mil/auth/service/RedisClient.java delete mode 100644 src/main/java/it/pagopa/swclient/mil/auth/service/RefreshTokenVerifier.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/service/TokenSigner.java delete mode 100644 src/main/java/it/pagopa/swclient/mil/auth/service/TokenVerifier.java delete mode 100644 src/main/java/it/pagopa/swclient/mil/auth/util/KeyPairUtil.java delete mode 100644 src/main/java/it/pagopa/swclient/mil/auth/util/TokenGenerator.java delete mode 100644 src/main/java/it/pagopa/swclient/mil/auth/util/TokenSigner.java create mode 100644 src/test/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyNameAndVersionTest.java create mode 100644 src/test/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureKeyFinderTest.java create mode 100644 src/test/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureTokenSignerTest.java create mode 100644 src/test/java/it/pagopa/swclient/mil/auth/client/PoyntClientTest.java create mode 100644 src/test/java/it/pagopa/swclient/mil/auth/resource/RefreshTokensResourceTest.java create mode 100644 src/test/java/it/pagopa/swclient/mil/auth/resource/TokenByClientSecretResourceTest.java create mode 100644 src/test/java/it/pagopa/swclient/mil/auth/resource/TokenByClientSecretResourceUnexpectedErrorTest.java create mode 100644 src/test/java/it/pagopa/swclient/mil/auth/resource/TokenByPasswordResourceTest.java create mode 100644 src/test/java/it/pagopa/swclient/mil/auth/resource/TokenByPoyntTokenResourceTest.java delete mode 100644 src/test/java/it/pagopa/swclient/mil/auth/resource/TokenResourceTest.java delete mode 100644 src/test/java/it/pagopa/swclient/mil/auth/resource/TokenResourceWithExcOnClientSecrVerTest.java create mode 100644 src/test/java/it/pagopa/swclient/mil/auth/service/ClientVerifierTest.java delete mode 100644 src/test/java/it/pagopa/swclient/mil/auth/service/KeyFinderTest.java delete mode 100644 src/test/java/it/pagopa/swclient/mil/auth/service/KeyFinderWithExceptionTest.java create mode 100644 src/test/java/it/pagopa/swclient/mil/auth/service/RolesFinderTest.java create mode 100644 src/test/java/it/pagopa/swclient/mil/auth/service/TokenByPasswordServiceTest.java diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java index 17083930..05212d56 100644 --- a/.mvn/wrapper/MavenWrapperDownloader.java +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -22,16 +22,15 @@ import java.nio.channels.*; import java.util.Properties; -public class MavenWrapperDownloader -{ +public class MavenWrapperDownloader { private static final String WRAPPER_VERSION = "3.1.1"; /** * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. */ private static final String DEFAULT_DOWNLOAD_URL = - "https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/" + WRAPPER_VERSION - + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + "https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/" + WRAPPER_VERSION + + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; /** * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to use instead of the @@ -49,92 +48,72 @@ public class MavenWrapperDownloader */ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - public static void main( String args[] ) - { - System.out.println( "- Downloader started" ); - File baseDirectory = new File( args[0] ); - System.out.println( "- Using base directory: " + baseDirectory.getAbsolutePath() ); + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); // If the maven-wrapper.properties exists, read it and check if it contains a custom // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File( baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH ); + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); String url = DEFAULT_DOWNLOAD_URL; - if ( mavenWrapperPropertyFile.exists() ) - { + if (mavenWrapperPropertyFile.exists()) { FileInputStream mavenWrapperPropertyFileInputStream = null; - try - { - mavenWrapperPropertyFileInputStream = new FileInputStream( mavenWrapperPropertyFile ); + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load( mavenWrapperPropertyFileInputStream ); - url = mavenWrapperProperties.getProperty( PROPERTY_NAME_WRAPPER_URL, url ); - } - catch ( IOException e ) - { - System.out.println( "- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'" ); - } - finally - { - try - { - if ( mavenWrapperPropertyFileInputStream != null ) - { + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if (mavenWrapperPropertyFileInputStream != null) { mavenWrapperPropertyFileInputStream.close(); } - } - catch ( IOException e ) - { + } catch (IOException e) { // Ignore ... } } } - System.out.println( "- Downloading from: " + url ); + System.out.println("- Downloading from: " + url); - File outputFile = new File( baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH ); - if ( !outputFile.getParentFile().exists() ) - { - if ( !outputFile.getParentFile().mkdirs() ) - { - System.out.println( "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() - + "'" ); + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if (!outputFile.getParentFile().exists()) { + if (!outputFile.getParentFile().mkdirs()) { + System.out.println("- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + + "'"); } } - System.out.println( "- Downloading to: " + outputFile.getAbsolutePath() ); - try - { - downloadFileFromURL( url, outputFile ); - System.out.println( "Done" ); - System.exit( 0 ); - } - catch ( Throwable e ) - { - System.out.println( "- Error downloading" ); + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); e.printStackTrace(); - System.exit( 1 ); + System.exit(1); } } - private static void downloadFileFromURL( String urlString, File destination ) - throws Exception - { - if ( System.getenv( "MVNW_USERNAME" ) != null && System.getenv( "MVNW_PASSWORD" ) != null ) - { - String username = System.getenv( "MVNW_USERNAME" ); - char[] password = System.getenv( "MVNW_PASSWORD" ).toCharArray(); - Authenticator.setDefault( new Authenticator() - { + private static void downloadFileFromURL(String urlString, File destination) + throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { @Override - protected PasswordAuthentication getPasswordAuthentication() - { - return new PasswordAuthentication( username, password ); + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); } - } ); + }); } - URL website = new URL( urlString ); + URL website = new URL(urlString); ReadableByteChannel rbc; - rbc = Channels.newChannel( website.openStream() ); - FileOutputStream fos = new FileOutputStream( destination ); - fos.getChannel().transferFrom( rbc, 0, Long.MAX_VALUE ); + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); fos.close(); rbc.close(); } diff --git a/dep-sha256.json b/dep-sha256.json index eac235e6..2d2a88f1 100644 --- a/dep-sha256.json +++ b/dep-sha256.json @@ -1,32 +1,32 @@ { "dependencies": [ { - "id": "io.quarkus:quarkus-resteasy-reactive:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-resteasy-reactive:jar:3.2.5.Final", "artifactId": "quarkus-resteasy-reactive", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "qp0xZWJhU7869pbMquxpGYc1E33SyltJKSsiyO2R9EY=" + "version": "3.2.5.Final", + "sha256": "xrTeT4fjMZ8-5dOADVlXzM7NZ97syra87r9UKltzCT8=" }, { - "id": "io.quarkus:quarkus-resteasy-reactive-common:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-resteasy-reactive-common:jar:3.2.5.Final", "artifactId": "quarkus-resteasy-reactive-common", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "2WAycgjy6ElCaGsgdkl3sr-6LiEh_7arIZirrwZ_j_k=" + "version": "3.2.5.Final", + "sha256": "s46OwhxGHloV4-514cpau8VaBuzN3roUrUIN2SStoFU=" }, { - "id": "io.quarkus.resteasy.reactive:resteasy-reactive-common:jar:3.1.0.Final", + "id": "io.quarkus.resteasy.reactive:resteasy-reactive-common:jar:3.2.5.Final", "artifactId": "resteasy-reactive-common", "groupId": "io.quarkus.resteasy.reactive", - "version": "3.1.0.Final", - "sha256": "zNhP4qte6Mj088SChE6korUI2JXD1CuGnbNMtVf8_NQ=" + "version": "3.2.5.Final", + "sha256": "_71R3mNvDZclgkoerhQSvncZl32lUDUSjg82AwLobTA=" }, { - "id": "io.quarkus.resteasy.reactive:resteasy-reactive-common-types:jar:3.1.0.Final", + "id": "io.quarkus.resteasy.reactive:resteasy-reactive-common-types:jar:3.2.5.Final", "artifactId": "resteasy-reactive-common-types", "groupId": "io.quarkus.resteasy.reactive", - "version": "3.1.0.Final", - "sha256": "8vPoay6U8P5pXPtG50TQmk4nlC1lVYClLf-6N4DIzdM=" + "version": "3.2.5.Final", + "sha256": "hvg9DqtPLhyan-HFm_UpCnawQdsxOAClahMXaJNUqdI=" }, { "id": "org.reactivestreams:reactive-streams:jar:1.0.4", @@ -43,123 +43,242 @@ "sha256": "tcTuAhmpyCYpGEYkZRN2C9kAHUkpJm8VRPGoSUZmalI=" }, { - "id": "io.quarkus.resteasy.reactive:resteasy-reactive-vertx:jar:3.1.0.Final", + "id": "io.smallrye.common:smallrye-common-annotation:jar:2.1.0", + "artifactId": "smallrye-common-annotation", + "groupId": "io.smallrye.common", + "version": "2.1.0", + "sha256": "ucbZrBiH9kUlnY0Nb_R1W8F9lh4uAug-rW9pmuPW-Mc=" + }, + { + "id": "io.quarkus:quarkus-vertx:jar:3.2.5.Final", + "artifactId": "quarkus-vertx", + "groupId": "io.quarkus", + "version": "3.2.5.Final", + "sha256": "jiuWACfc68OuojVYeIllIjymwGzNPD41aABTy1ZWCIQ=" + }, + { + "id": "io.quarkus:quarkus-netty:jar:3.2.5.Final", + "artifactId": "quarkus-netty", + "groupId": "io.quarkus", + "version": "3.2.5.Final", + "sha256": "5n3-plHgBEsVnB95VzhmExL8xY7V1wcT7jk0F0fAq3Y=" + }, + { + "id": "io.netty:netty-codec:jar:4.1.94.Final", + "artifactId": "netty-codec", + "groupId": "io.netty", + "version": "4.1.94.Final", + "sha256": "kSQ3dq1otNjjnq-57BFeG4-prs0UexLvFbtpFjlJgyg=" + }, + { + "id": "io.netty:netty-codec-http:jar:4.1.94.Final", + "artifactId": "netty-codec-http", + "groupId": "io.netty", + "version": "4.1.94.Final", + "sha256": "GtpFgPaM0XpTT7PAM3CHBzIjp2y3cwTb5aGxnfPVPC8=" + }, + { + "id": "io.netty:netty-codec-http2:jar:4.1.94.Final", + "artifactId": "netty-codec-http2", + "groupId": "io.netty", + "version": "4.1.94.Final", + "sha256": "j70ulavsYVW2DtPJwWAO1OF__j8FPNWkBnfYecCvlh8=" + }, + { + "id": "io.netty:netty-handler:jar:4.1.94.Final", + "artifactId": "netty-handler", + "groupId": "io.netty", + "version": "4.1.94.Final", + "sha256": "jlBxmpq4njPvhcXzbXgODXBWs_dosH0mHYe67XCU6zw=" + }, + { + "id": "io.netty:netty-transport-native-unix-common:jar:4.1.94.Final", + "artifactId": "netty-transport-native-unix-common", + "groupId": "io.netty", + "version": "4.1.94.Final", + "sha256": "J9Df8c10MZAnm-ys-zcv5NRbJm7a-tnxxsAbBNACgOs=" + }, + { + "id": "com.aayushatharva.brotli4j:brotli4j:jar:1.12.0", + "artifactId": "brotli4j", + "groupId": "com.aayushatharva.brotli4j", + "version": "1.12.0", + "sha256": "Ir_4SClHKtV5IJPgdnLABe6g0P_pY3ofmd3XiXD732g=" + }, + { + "id": "com.aayushatharva.brotli4j:service:jar:1.12.0", + "artifactId": "service", + "groupId": "com.aayushatharva.brotli4j", + "version": "1.12.0", + "sha256": "t7rwqDCyaXSRybptDmztgrTGKEKcBGI26zTPxH1zv5k=" + }, + { + "id": "com.aayushatharva.brotli4j:native-osx-aarch64:jar:1.12.0", + "artifactId": "native-osx-aarch64", + "groupId": "com.aayushatharva.brotli4j", + "version": "1.12.0", + "sha256": "ZKfegpUjuDw8ZTUO92x3ZUtYMG4i6xbLLxVm_rC9qpc=" + }, + { + "id": "com.aayushatharva.brotli4j:native-linux-x86_64:jar:1.12.0", + "artifactId": "native-linux-x86_64", + "groupId": "com.aayushatharva.brotli4j", + "version": "1.12.0", + "sha256": "ASDyLPndtKpOKj2AacUMN4wEiHk0HIFTDOW04r5vYPg=" + }, + { + "id": "io.netty:netty-codec-haproxy:jar:4.1.94.Final", + "artifactId": "netty-codec-haproxy", + "groupId": "io.netty", + "version": "4.1.94.Final", + "sha256": "IjOWJtufbHyCSBNFVW2XdlBzaR5et8_pAXESDnEFagQ=" + }, + { + "id": "io.netty:netty-buffer:jar:4.1.94.Final", + "artifactId": "netty-buffer", + "groupId": "io.netty", + "version": "4.1.94.Final", + "sha256": "gGbufEn58p2pbuYvfLE77gIstLaOUUN7M9o7bQE5jxM=" + }, + { + "id": "io.netty:netty-transport:jar:4.1.94.Final", + "artifactId": "netty-transport", + "groupId": "io.netty", + "version": "4.1.94.Final", + "sha256": "p1r6hMo1pQIlmRs55rYngYbmEveioMDpgd5SOqrFFqQ=" + }, + { + "id": "io.quarkus:quarkus-vertx-latebound-mdc-provider:jar:3.2.5.Final", + "artifactId": "quarkus-vertx-latebound-mdc-provider", + "groupId": "io.quarkus", + "version": "3.2.5.Final", + "sha256": "mUKpAabzi_JAH4JyKhri4QbxCXhl001VLWOccYkQqKc=" + }, + { + "id": "io.smallrye:smallrye-fault-tolerance-vertx:jar:6.2.6", + "artifactId": "smallrye-fault-tolerance-vertx", + "groupId": "io.smallrye", + "version": "6.2.6", + "sha256": "UBiidPSiglNqgUWTaiHb3-juPUnQFIZISDpP5tLx0yg=" + }, + { + "id": "io.quarkus.resteasy.reactive:resteasy-reactive-vertx:jar:3.2.5.Final", "artifactId": "resteasy-reactive-vertx", "groupId": "io.quarkus.resteasy.reactive", - "version": "3.1.0.Final", - "sha256": "eoIVAQwIKGe1Nu-GfZp68d5y-15MEvTHmWeUG7oFwoU=" + "version": "3.2.5.Final", + "sha256": "3f7S00QksckmdxdUPis4xfIo3kIryoEDfLlkpvh90iY=" }, { - "id": "io.vertx:vertx-web:jar:4.4.2", + "id": "io.vertx:vertx-web:jar:4.4.4", "artifactId": "vertx-web", "groupId": "io.vertx", - "version": "4.4.2", - "sha256": "meYbrlFjcZCwCrqdpQNFXYLFWfzR5Kj6cV0RvOOoln0=" + "version": "4.4.4", + "sha256": "HOp8PnaLVdvKQxuSDu1mXymtPy1rPa4GJUGhVaJhBHk=" }, { - "id": "io.vertx:vertx-web-common:jar:4.4.2", + "id": "io.vertx:vertx-web-common:jar:4.4.4", "artifactId": "vertx-web-common", "groupId": "io.vertx", - "version": "4.4.2", - "sha256": "uyCJRNiOSRN0jSA4xGBsgswBRvF3ytUZkMoWypI11q8=" + "version": "4.4.4", + "sha256": "yjuyG76g4kyHFXrGx_UsGpIwEFBPQhTjJRuJRUnn2Pc=" }, { - "id": "io.vertx:vertx-auth-common:jar:4.4.2", + "id": "io.vertx:vertx-auth-common:jar:4.4.4", "artifactId": "vertx-auth-common", "groupId": "io.vertx", - "version": "4.4.2", - "sha256": "vFqd0P-vhCQlRZnMHKCuLwOVJ_5Zhh-urpL61S2RYwk=" + "version": "4.4.4", + "sha256": "8nMF4CR3JOySpdkaaVmMPGkntqNRryVHNyjSLS6H6CU=" }, { - "id": "io.vertx:vertx-bridge-common:jar:4.4.2", + "id": "io.vertx:vertx-bridge-common:jar:4.4.4", "artifactId": "vertx-bridge-common", "groupId": "io.vertx", - "version": "4.4.2", - "sha256": "TofB79Az9V2SLeFgE7SwvfzNyZj89y5wcCXy1E0Dn84=" + "version": "4.4.4", + "sha256": "QUA3zerJphcl8LoCjY1vRodLm3kBu3sFNwXH1gWG78M=" }, { - "id": "io.vertx:vertx-core:jar:4.4.2", + "id": "io.vertx:vertx-core:jar:4.4.4", "artifactId": "vertx-core", "groupId": "io.vertx", - "version": "4.4.2", - "sha256": "UzKuUoA_mmpjaSE434BH5k2ommo8jZVVrZ-HjKni3Jc=" + "version": "4.4.4", + "sha256": "5UzIpUxKHelHuZh8vIxkQZ7BM_Ws0kx97fRtd1GhmM4=" }, { - "id": "io.netty:netty-common:jar:4.1.92.Final", + "id": "io.netty:netty-common:jar:4.1.94.Final", "artifactId": "netty-common", "groupId": "io.netty", - "version": "4.1.92.Final", - "sha256": "r4xXdB-1RgbekJCxM7XA-C8qWpQIwa48LY-tuayhVA4=" + "version": "4.1.94.Final", + "sha256": "y42Eo-Y66pDQ16MzoC5QrHUdKwXbVXRdmBte_4k_ZHs=" }, { - "id": "io.netty:netty-handler-proxy:jar:4.1.92.Final", + "id": "io.netty:netty-handler-proxy:jar:4.1.94.Final", "artifactId": "netty-handler-proxy", "groupId": "io.netty", - "version": "4.1.92.Final", - "sha256": "q0Agm9pa4lVmfaaXjrf4tig0JPiSiWtkYeJWSZrQy_o=" + "version": "4.1.94.Final", + "sha256": "N4c4v-uEyBIorl3iCflkyoeEn5pyDJFsd_fLpklLsew=" }, { - "id": "io.netty:netty-codec-socks:jar:4.1.92.Final", + "id": "io.netty:netty-codec-socks:jar:4.1.94.Final", "artifactId": "netty-codec-socks", "groupId": "io.netty", - "version": "4.1.92.Final", - "sha256": "hXSAtt6lJUOFHWONZcycK6hbhbzShieXpzCZDVcLTHU=" + "version": "4.1.94.Final", + "sha256": "HAtO4LYjQC3Vu3j9Smm_gI2iUQUk9ImiSw88xY3tBG4=" }, { - "id": "io.netty:netty-resolver:jar:4.1.92.Final", + "id": "io.netty:netty-resolver:jar:4.1.94.Final", "artifactId": "netty-resolver", "groupId": "io.netty", - "version": "4.1.92.Final", - "sha256": "SUF3OFjz_duEx8bSzGfPDEyZH8PrimCJFtmYLmmXrU0=" + "version": "4.1.94.Final", + "sha256": "vSbpvF6U4tOXSpP9-SFljv9PAzv9TFIIYHdgq1Qphhc=" }, { - "id": "io.netty:netty-resolver-dns:jar:4.1.92.Final", + "id": "io.netty:netty-resolver-dns:jar:4.1.94.Final", "artifactId": "netty-resolver-dns", "groupId": "io.netty", - "version": "4.1.92.Final", - "sha256": "mbZewhWt4pA34x1_iEo_lxL5hssexcGIv-ElHLdTGZ0=" + "version": "4.1.94.Final", + "sha256": "1qCHGtvEfOSwirfCPG4yBHvRl2QfXz_TXRZfrteaieg=" }, { - "id": "io.netty:netty-codec-dns:jar:4.1.92.Final", + "id": "io.netty:netty-codec-dns:jar:4.1.94.Final", "artifactId": "netty-codec-dns", "groupId": "io.netty", - "version": "4.1.92.Final", - "sha256": "PCsL12Eyz8-cP6mpOIIXkOF0vDBRmkRHkwJMrArdTnM=" + "version": "4.1.94.Final", + "sha256": "s0UEi3aSIEgDtJ6xH1IDtS4YqnZH-Ld91jEY_Y1f0qI=" }, { - "id": "io.smallrye.reactive:smallrye-mutiny-vertx-core:jar:3.3.0", + "id": "io.smallrye.reactive:smallrye-mutiny-vertx-core:jar:3.5.0", "artifactId": "smallrye-mutiny-vertx-core", "groupId": "io.smallrye.reactive", - "version": "3.3.0", - "sha256": "ZmMkkW_yRrlkCn0NyGZAB-g5rLmd8p6KE3X7WK_AgAo=" + "version": "3.5.0", + "sha256": "7T1begz0rQTJM5qt_QnqgOQYSzQXh4HcKD_W4nAIieE=" }, { - "id": "io.smallrye.reactive:smallrye-mutiny-vertx-runtime:jar:3.3.0", + "id": "io.smallrye.reactive:smallrye-mutiny-vertx-runtime:jar:3.5.0", "artifactId": "smallrye-mutiny-vertx-runtime", "groupId": "io.smallrye.reactive", - "version": "3.3.0", - "sha256": "d1CSNGZ6vb8Pp7M1OLkTHEDyH8lgE7n1gJWPkLmzQHs=" + "version": "3.5.0", + "sha256": "1lDQvNs3t72RUJHRmERtzsnmDDeQww7bgWDLWV_CPaQ=" }, { - "id": "io.smallrye.reactive:vertx-mutiny-generator:jar:3.3.0", + "id": "io.smallrye.reactive:vertx-mutiny-generator:jar:3.5.0", "artifactId": "vertx-mutiny-generator", "groupId": "io.smallrye.reactive", - "version": "3.3.0", - "sha256": "POOTxlbBumVSxFpHZxEePDzpx6IY3U0vu_4omAKI6wk=" + "version": "3.5.0", + "sha256": "MJpVo-SRQKguzY2viDKPig3Cqljn_Vd0dC9PX5BTKFc=" }, { - "id": "io.vertx:vertx-codegen:jar:4.4.2", + "id": "io.vertx:vertx-codegen:jar:4.4.4", "artifactId": "vertx-codegen", "groupId": "io.vertx", - "version": "4.4.2", - "sha256": "B3GAnHPcg-0IjuBU0UL3GWhFvcVwQ_uzqWZs1zijr3g=" + "version": "4.4.4", + "sha256": "uzo9xfqhGPG1B2GGqM9jU6Ko3cha4uAmoi1mAvcWVOA=" }, { - "id": "io.quarkus.resteasy.reactive:resteasy-reactive:jar:3.1.0.Final", + "id": "io.quarkus.resteasy.reactive:resteasy-reactive:jar:3.2.5.Final", "artifactId": "resteasy-reactive", "groupId": "io.quarkus.resteasy.reactive", - "version": "3.1.0.Final", - "sha256": "e1XyQI3RBA1X2vny402VuMTne7PnPf4tqGsl-gcdujc=" + "version": "3.2.5.Final", + "sha256": "NKRTUYXp9kZFOvL3P7bBCOzeCLO_2JxA1rPjGmibHls=" }, { "id": "jakarta.enterprise:jakarta.enterprise.cdi-api:jar:4.0.1", @@ -211,32 +330,32 @@ "sha256": "9T9XjdDrQXDBlaTiFcWaOKv7QSPcuV3ZAv75KHZJn7s=" }, { - "id": "org.jboss.logging:jboss-logging:jar:3.5.0.Final", + "id": "org.jboss.logging:jboss-logging:jar:3.5.1.Final", "artifactId": "jboss-logging", "groupId": "org.jboss.logging", - "version": "3.5.0.Final", - "sha256": "e7E1sIGVL20y2DN0YZrlIBsFyjv4YqKN0REBbOGbLAc=" + "version": "3.5.1.Final", + "sha256": "S-d0e3VcC73TWOlS_HYMz13MGNO3jfUsiuGmyX86jxI=" }, { - "id": "io.quarkus:quarkus-vertx-http:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-vertx-http:jar:3.2.5.Final", "artifactId": "quarkus-vertx-http", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "t0uMXjBQIRUqvIsVwFHAGIaQG954oBbb7V5cSP9-eXQ=" + "version": "3.2.5.Final", + "sha256": "dYA29NYvdX4oZKCx3ZxmqnqVEtJJ1uR1RZZuhgUOxO8=" }, { - "id": "io.quarkus:quarkus-security-runtime-spi:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-security-runtime-spi:jar:3.2.5.Final", "artifactId": "quarkus-security-runtime-spi", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "_WMHlba9PWrMa_i6KGXwV7Y7hKcWDWd8Ycf8OMMcHtM=" + "version": "3.2.5.Final", + "sha256": "oVFGeY5aEir2o06roKddjHmhNESRRlzG3GARsHMjg7Y=" }, { - "id": "io.quarkus:quarkus-credentials:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-credentials:jar:3.2.5.Final", "artifactId": "quarkus-credentials", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "n5dmYY3K-qJ6OzzVFcXLBwqwRWTWFdjRTaTLVvTl6HI=" + "version": "3.2.5.Final", + "sha256": "4H9l4cqvLB2COqxA2sRWI2DFyJ2S_dJqFyCy6JItuoY=" }, { "id": "io.smallrye.common:smallrye-common-vertx-context:jar:2.1.0", @@ -253,11 +372,11 @@ "sha256": "Fp3rJq6MT9Ae11u619ktSjDSnXxemjynE_DgjfYoh6Y=" }, { - "id": "io.quarkus:quarkus-vertx-http-dev-console-runtime-spi:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-vertx-http-dev-console-runtime-spi:jar:3.2.5.Final", "artifactId": "quarkus-vertx-http-dev-console-runtime-spi", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "93hxnC40LfPg7ZBbooz3H6M9Zax12FbaV8-IvWvpX_A=" + "version": "3.2.5.Final", + "sha256": "jsQOQiQoLkZ-eu6oeME7Fj1argH-m1Q5iFkjq-_A7dg=" }, { "id": "io.quarkus.security:quarkus-security:jar:2.0.2.Final", @@ -267,46 +386,46 @@ "sha256": "-7Nfls8Zn0EMx5xmVOQFjo3VrqiwtSRxIYRHpTXNJdQ=" }, { - "id": "io.smallrye.reactive:smallrye-mutiny-vertx-web:jar:3.3.0", + "id": "io.smallrye.reactive:smallrye-mutiny-vertx-web:jar:3.5.0", "artifactId": "smallrye-mutiny-vertx-web", "groupId": "io.smallrye.reactive", - "version": "3.3.0", - "sha256": "EKfuSiucZKGk3BZ4z_0NoM9SQE4CyrbFdXQqn-AdgNk=" + "version": "3.5.0", + "sha256": "4fFVvsmyGCMuV2-b9koqb4mLKX8qNzPKaet0OoThsR4=" }, { - "id": "io.smallrye.reactive:smallrye-mutiny-vertx-web-common:jar:3.3.0", + "id": "io.smallrye.reactive:smallrye-mutiny-vertx-web-common:jar:3.5.0", "artifactId": "smallrye-mutiny-vertx-web-common", "groupId": "io.smallrye.reactive", - "version": "3.3.0", - "sha256": "PEbrry6llXQlA4W1MwFIp9Lexxa5p8Uk-RWJSV-hOIY=" + "version": "3.5.0", + "sha256": "pGdw676O81JFA8MXaREjh3B_-yfNZ-_11TOrN3GD6_g=" }, { - "id": "io.smallrye.reactive:smallrye-mutiny-vertx-auth-common:jar:3.3.0", + "id": "io.smallrye.reactive:smallrye-mutiny-vertx-auth-common:jar:3.5.0", "artifactId": "smallrye-mutiny-vertx-auth-common", "groupId": "io.smallrye.reactive", - "version": "3.3.0", - "sha256": "ZmLIDTzAANZ3QHQ-MI7tQv44t5iR0KWQsxhVN-wOynI=" + "version": "3.5.0", + "sha256": "NLR3KS_dn8mgpnMR6jXpGtxL6ALwuUfkrPU3xId0aeQ=" }, { - "id": "io.smallrye.reactive:smallrye-mutiny-vertx-bridge-common:jar:3.3.0", + "id": "io.smallrye.reactive:smallrye-mutiny-vertx-bridge-common:jar:3.5.0", "artifactId": "smallrye-mutiny-vertx-bridge-common", "groupId": "io.smallrye.reactive", - "version": "3.3.0", - "sha256": "DaEizb6OUnmZzmYXp_83kHWND5HiYHUQR-hcX6Mwrz0=" + "version": "3.5.0", + "sha256": "OAOM2rPHWiJBgkKRfy0Au7HY5rSP3X1-UWFfTtpxmFc=" }, { - "id": "io.smallrye.reactive:smallrye-mutiny-vertx-uri-template:jar:3.3.0", + "id": "io.smallrye.reactive:smallrye-mutiny-vertx-uri-template:jar:3.5.0", "artifactId": "smallrye-mutiny-vertx-uri-template", "groupId": "io.smallrye.reactive", - "version": "3.3.0", - "sha256": "-8gIy3H_2Eu0HnipACydIHMBgUuLi_zNSRA01Dxjsk4=" + "version": "3.5.0", + "sha256": "HtILt0pG1TeKs0V0Bb6GNZRHQCILIUm9PHlbOMIZkjM=" }, { - "id": "io.vertx:vertx-uri-template:jar:4.4.2", + "id": "io.vertx:vertx-uri-template:jar:4.4.4", "artifactId": "vertx-uri-template", "groupId": "io.vertx", - "version": "4.4.2", - "sha256": "k37tXZko0urphBlCOESHRu2xLlR-fJczUUNCSIb5PUA=" + "version": "4.4.4", + "sha256": "zEkTotCjcLc_JdnriRpa0HbKXesTosRRViPSnupZedU=" }, { "id": "io.github.crac:org-crac:jar:0.1.3", @@ -316,60 +435,81 @@ "sha256": "13oMYo0Tme5oJziFHcoT0GD-9RYBrtrW0e_NMYX5rwk=" }, { - "id": "io.quarkus:quarkus-jsonp:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-jsonp:jar:3.2.5.Final", "artifactId": "quarkus-jsonp", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "NsW73GoAFuGqJF2VwyiVKltHWDvl2B4YHfBaolRY_AI=" + "version": "3.2.5.Final", + "sha256": "FfL7TpKNJUSdUkU1d_GabSoAswd5jlP9BcFBPTLj-s8=" }, { - "id": "org.eclipse.parsson:parsson:jar:1.1.1", + "id": "org.eclipse.parsson:parsson:jar:1.1.2", "artifactId": "parsson", "groupId": "org.eclipse.parsson", - "version": "1.1.1", - "sha256": "TWNoZrAQ7DCv2AEQLqwM1wqzOm5OxtOaYZrgE-pf_9M=" + "version": "1.1.2", + "sha256": "Cg1f75Bt273a5uiU9s9C57lS0klS8d8JrbCiQm9Ja_Y=" }, { - "id": "jakarta.json:jakarta.json-api:jar:2.1.1", + "id": "jakarta.json:jakarta.json-api:jar:2.1.2", "artifactId": "jakarta.json-api", "groupId": "jakarta.json", - "version": "2.1.1", - "sha256": "w8D_55ZVOS9_9APc-b8B5xApm1oEBvboSJ6fJKCPgyc=" + "version": "2.1.2", + "sha256": "8kclB60swS8q7wii96YozRw_hVZoptyyqpow2bkJuZg=" }, { - "id": "io.quarkus:quarkus-resteasy-reactive-jackson:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-resteasy-reactive-jackson:jar:3.2.5.Final", "artifactId": "quarkus-resteasy-reactive-jackson", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "PXs4okE49O3GExTNaEtprFIsR1y5F1iyCFlNmp9S-ts=" + "version": "3.2.5.Final", + "sha256": "2vgvwV1mcpYaaIQJIb38SyCbE_PKCkSWD3SwFerjv_Q=" }, { - "id": "io.quarkus:quarkus-resteasy-reactive-jackson-common:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-resteasy-reactive-jackson-common:jar:3.2.5.Final", "artifactId": "quarkus-resteasy-reactive-jackson-common", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "JDnGxsnHGg65o06U-8rNA08RoQ3eJXfRXWzTBbBlBj0=" + "version": "3.2.5.Final", + "sha256": "8hynriDGmGq9rMe4f7LmeOhIhwzDS_6NfzBsD7OjBas=" + }, + { + "id": "io.quarkus:quarkus-jackson:jar:3.2.5.Final", + "artifactId": "quarkus-jackson", + "groupId": "io.quarkus", + "version": "3.2.5.Final", + "sha256": "YmFk2tdxPvYGW-zFmRRkszKWxo857A22KkGpGyfWZzM=" }, { - "id": "org.mongodb:bson:jar:4.9.1", - "artifactId": "bson", - "groupId": "org.mongodb", - "version": "4.9.1", - "sha256": "RZpeIF-plqVfX1KjbVvwN0XFMaDWxg_j0c8kyhA19lw=" + "id": "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.15.2", + "artifactId": "jackson-datatype-jsr310", + "groupId": "com.fasterxml.jackson.datatype", + "version": "2.15.2", + "sha256": "dXTIGtVwR272qtJvQZKI_UZnM_MxW-4wEvLynJ3ACMg=" + }, + { + "id": "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.15.2", + "artifactId": "jackson-datatype-jdk8", + "groupId": "com.fasterxml.jackson.datatype", + "version": "2.15.2", + "sha256": "W-biBQSx6rekD5jE-YvZLpF31ubiDxg5KOfSB_xmy3g=" }, { - "id": "io.quarkus:quarkus-hibernate-validator:jar:3.1.0.Final", + "id": "com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.15.2", + "artifactId": "jackson-module-parameter-names", + "groupId": "com.fasterxml.jackson.module", + "version": "2.15.2", + "sha256": "L2JOFjc1CPjjoVNfWm6dgChrh6iZ_RKOX97SaGJf6RM=" + }, + { + "id": "io.quarkus:quarkus-hibernate-validator:jar:3.2.5.Final", "artifactId": "quarkus-hibernate-validator", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "n3tBrqzJ0Klm_hhY87yrPIinILeD3smvvOjfq_TzA9o=" + "version": "3.2.5.Final", + "sha256": "djdfqn6rgSp9gPQVn0gKptEIOS3X7MVDI3mBJ0G_L0g=" }, { - "id": "io.quarkus:quarkus-core:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-core:jar:3.2.5.Final", "artifactId": "quarkus-core", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "uGcsTEi8IRHBxSN0bUC5fee1JcUDmBskjhFNCiLDtiY=" + "version": "3.2.5.Final", + "sha256": "khYFK-bJB-aW7vdVf8wdCyeAVH4qMymlvk_dSF2rlRU=" }, { "id": "jakarta.inject:jakarta.inject-api:jar:2.0.1", @@ -386,32 +526,32 @@ "sha256": "zxlEyMicsNEN4f9L4UMpMJ4FzAW0xIx6RBfc2SjO1r4=" }, { - "id": "io.quarkus:quarkus-ide-launcher:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-ide-launcher:jar:3.2.5.Final", "artifactId": "quarkus-ide-launcher", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "ApNfumCqsWUJikh62tSRIsfv4sf-XkAqYMS4XZk2gj0=" + "version": "3.2.5.Final", + "sha256": "oMe-oCXWH2EWEFofs_Dm1s_vdW59xaYaXkhqCEUkU0k=" }, { - "id": "io.quarkus:quarkus-development-mode-spi:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-development-mode-spi:jar:3.2.5.Final", "artifactId": "quarkus-development-mode-spi", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "X0O6AvQQAwmy8xgjZOLgE4hawBgvsIr_19GSe_fmEwU=" + "version": "3.2.5.Final", + "sha256": "n7fuK5tGhH--3j-59OyJS5Wz7mzr1d6CQ2Je6E8qG_Q=" }, { - "id": "io.smallrye.config:smallrye-config:jar:3.2.1", + "id": "io.smallrye.config:smallrye-config:jar:3.3.2", "artifactId": "smallrye-config", "groupId": "io.smallrye.config", - "version": "3.2.1", - "sha256": "OBGFYr_Ptdle7uo6wQ1mMwFNAqcHAeHmIWMco61LzyY=" + "version": "3.3.2", + "sha256": "xVdwSPyi6IBaDK3YjE3qIs3yLIwMUkGHdm3J-hUUBAc=" }, { - "id": "io.smallrye.config:smallrye-config-core:jar:3.2.1", + "id": "io.smallrye.config:smallrye-config-core:jar:3.3.2", "artifactId": "smallrye-config-core", "groupId": "io.smallrye.config", - "version": "3.2.1", - "sha256": "5KtGfWXFQTipZXYELXxyEEqcW6D5KvdYAx8ghJAcwGk=" + "version": "3.3.2", + "sha256": "DW20NHNXgKh4CBhRRLGwRteTqDP0yYFmu8lbRQ-ssWU=" }, { "id": "org.eclipse.microprofile.config:microprofile-config-api:jar:3.0.3", @@ -442,11 +582,11 @@ "sha256": "Jwb_NxWPio3lezngd1CInHpwuNrtGseXdBSYOW0T9AI=" }, { - "id": "io.smallrye.config:smallrye-config-common:jar:3.2.1", + "id": "io.smallrye.config:smallrye-config-common:jar:3.3.2", "artifactId": "smallrye-config-common", "groupId": "io.smallrye.config", - "version": "3.2.1", - "sha256": "KYr0DmE95RsNSH7hMPALpdXu_ExpsHJVCnbA98cdYm4=" + "version": "3.3.2", + "sha256": "gZ9t3lnWAEz9SZw3ySysegFRUqbIBDiuMkVwIUCR80U=" }, { "id": "org.jboss.logmanager:jboss-logmanager-embedded:jar:1.1.1", @@ -484,11 +624,11 @@ "sha256": "J3iFEEtyeiE-94S6sxSZ0jIhB03tKLBIJ-gJ0vVIlpM=" }, { - "id": "org.graalvm.sdk:graal-sdk:jar:22.3.2", + "id": "org.graalvm.sdk:graal-sdk:jar:23.0.1", "artifactId": "graal-sdk", "groupId": "org.graalvm.sdk", - "version": "22.3.2", - "sha256": "gv3rMmA7mxIfO8RVkH1-Pv_LJo6X1y160P0z_dfT6zs=" + "version": "23.0.1", + "sha256": "OcRlWa1kGlvR0rb_UQaqORkpp97CHrshVQL59A4M2bA=" }, { "id": "org.wildfly.common:wildfly-common:jar:1.5.4.Final-format-001", @@ -498,11 +638,11 @@ "sha256": "mIT3kfgV0P7YxRdxr3EWSv1I-W5iHyYAn56-eRwFPxs=" }, { - "id": "io.quarkus:quarkus-bootstrap-runner:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-bootstrap-runner:jar:3.2.5.Final", "artifactId": "quarkus-bootstrap-runner", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "b0HJmZ92FKZ7qq9Zc9b5nZKH9il06JE3WengkLfJDUI=" + "version": "3.2.5.Final", + "sha256": "GcneGkp_0yhfSfLX6f2UQJH-8ki_G75hw-eJ8Z77L9M=" }, { "id": "io.quarkus:quarkus-fs-util:jar:0.0.9", @@ -512,11 +652,11 @@ "sha256": "6go6O0a6DWDu4xhpH22d2k0M1H0ghRK5DYOmLU7NyjY=" }, { - "id": "org.hibernate.validator:hibernate-validator:jar:8.0.0.Final", + "id": "org.hibernate.validator:hibernate-validator:jar:8.0.1.Final", "artifactId": "hibernate-validator", "groupId": "org.hibernate.validator", - "version": "8.0.0.Final", - "sha256": "V6Ms2gWDREsZ0C_XbHtPIwNc24d-C2xz6DQ4AJTiIUU=" + "version": "8.0.1.Final", + "sha256": "jBJEpJgjEJH-cj2WZqk0RO6fk2ByRcaymCncX-V6M1w=" }, { "id": "jakarta.validation:jakarta.validation-api:jar:3.0.2", @@ -547,11 +687,11 @@ "sha256": "kEMtGCgXF7NjuBmXjGoXJDOiZUt364fIY8IvwZwg7O0=" }, { - "id": "io.smallrye.config:smallrye-config-validator:jar:3.2.1", + "id": "io.smallrye.config:smallrye-config-validator:jar:3.3.2", "artifactId": "smallrye-config-validator", "groupId": "io.smallrye.config", - "version": "3.2.1", - "sha256": "PzK_I5gjhBkVpxLwanPVbLuQ3hipXNrdQrWXSNLTxig=" + "version": "3.3.2", + "sha256": "nNTpCpPE6LJGHa5u0zisSKZxZ6BVr6gLeFoLKNnWMP0=" }, { "id": "jakarta.ws.rs:jakarta.ws.rs-api:jar:3.1.0", @@ -561,18 +701,18 @@ "sha256": "azs2KLi0rt2g0kwzVDNemFSX2O88UQuPMCjpINW4Zj0=" }, { - "id": "io.quarkus:quarkus-arc:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-arc:jar:3.2.5.Final", "artifactId": "quarkus-arc", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "Eb-4APo0ixksKasixqRjdp-fgXXEP6WnFlDC0X3g5S0=" + "version": "3.2.5.Final", + "sha256": "dHiSgE0weOzQeu1qVviJ__VDFMY9LWZGEoPyecYPupA=" }, { - "id": "io.quarkus.arc:arc:jar:3.1.0.Final", + "id": "io.quarkus.arc:arc:jar:3.2.5.Final", "artifactId": "arc", "groupId": "io.quarkus.arc", - "version": "3.1.0.Final", - "sha256": "1ZJkU2XVz4R7mutuCnNTouu5I7zi-W6X9qKYHW3RW4Y=" + "version": "3.2.5.Final", + "sha256": "QKNCXasmpl0-Vk2BPLuzTyAJ0LsOBxU0nINp1uT5lqo=" }, { "id": "jakarta.transaction:jakarta.transaction-api:jar:2.0.1", @@ -582,11 +722,11 @@ "sha256": "UMCnx2DBOubAQqzxgrKPAEdBPblbRjb7iHm8_6tbqHU=" }, { - "id": "io.smallrye.reactive:mutiny:jar:2.1.0", + "id": "io.smallrye.reactive:mutiny:jar:2.3.1", "artifactId": "mutiny", "groupId": "io.smallrye.reactive", - "version": "2.1.0", - "sha256": "UnxMJd0bD1xsEQB0u97UKq20qNsXokro0hhCRuSD36A=" + "version": "2.3.1", + "sha256": "__OgFv-GNGHv3UdCYqyo5dHI0eCbreqhj_M-Cs6iwhs=" }, { "id": "org.eclipse.microprofile.context-propagation:microprofile-context-propagation-api:jar:1.3", @@ -596,263 +736,95 @@ "sha256": "aczARIfod3nUlwqlDGc8w0qd8IDBwOjY6rLotG-CXPQ=" }, { - "id": "io.quarkus:quarkus-redis-client:jar:3.1.0.Final", - "artifactId": "quarkus-redis-client", - "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "3hgeh5WC68-vFmh5hoptI0JhBzQlgQ6G9ltpy-2AtEY=" - }, - { - "id": "io.quarkus:quarkus-vertx:jar:3.1.0.Final", - "artifactId": "quarkus-vertx", - "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "ZAUaljhwhT9Q9BnBhfPVLo-7lNEpO4B9PnA9yeObNIA=" - }, - { - "id": "io.quarkus:quarkus-netty:jar:3.1.0.Final", - "artifactId": "quarkus-netty", - "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "ETIoYz60zHwXJj1hPnxd3hJBHIrsL1v2rqb5SRmH7gs=" - }, - { - "id": "io.netty:netty-codec:jar:4.1.92.Final", - "artifactId": "netty-codec", - "groupId": "io.netty", - "version": "4.1.92.Final", - "sha256": "_IqakUDaAcfpGT88-FBAtFMwWfFRoEjUF11EdlWfZ1E=" - }, - { - "id": "io.netty:netty-codec-http:jar:4.1.92.Final", - "artifactId": "netty-codec-http", - "groupId": "io.netty", - "version": "4.1.92.Final", - "sha256": "qwUch9RsMrXp5IbmESQDWNLfpemvhUG2y0dqCd3F5Ug=" - }, - { - "id": "io.netty:netty-codec-http2:jar:4.1.92.Final", - "artifactId": "netty-codec-http2", - "groupId": "io.netty", - "version": "4.1.92.Final", - "sha256": "7MTQhhDyimhI7OSnirpjfghU66bpXbjiCZve8WZ3H8M=" - }, - { - "id": "io.netty:netty-handler:jar:4.1.92.Final", - "artifactId": "netty-handler", - "groupId": "io.netty", - "version": "4.1.92.Final", - "sha256": "0sPBeHnzYZohYwdkSxDa7t2rGwShKH3_UGdKh9oUQ7I=" - }, - { - "id": "io.netty:netty-transport-native-unix-common:jar:4.1.92.Final", - "artifactId": "netty-transport-native-unix-common", - "groupId": "io.netty", - "version": "4.1.92.Final", - "sha256": "s3ubG371z1Ym2S05HeVCLV_VoYFhk0R-57thxwqi2zE=" - }, - { - "id": "com.aayushatharva.brotli4j:brotli4j:jar:1.11.0", - "artifactId": "brotli4j", - "groupId": "com.aayushatharva.brotli4j", - "version": "1.11.0", - "sha256": "XvoDngc9wW0wVKQ7OF1CXBdHzwH1Jn4aSlFO7DqXrD8=" - }, - { - "id": "com.aayushatharva.brotli4j:service:jar:1.11.0", - "artifactId": "service", - "groupId": "com.aayushatharva.brotli4j", - "version": "1.11.0", - "sha256": "8XkfMr5OWeFX_srCcjXP9HIu5iWL4qMZM78JwfN_nTI=" - }, - { - "id": "com.aayushatharva.brotli4j:native-osx-aarch64:jar:1.11.0", - "artifactId": "native-osx-aarch64", - "groupId": "com.aayushatharva.brotli4j", - "version": "1.11.0", - "sha256": "LCxbfByd8uYohfSWydvcfO63dqxSJsJG51luCsRnPgE=" - }, - { - "id": "com.aayushatharva.brotli4j:native-linux-x86_64:jar:1.11.0", - "artifactId": "native-linux-x86_64", - "groupId": "com.aayushatharva.brotli4j", - "version": "1.11.0", - "sha256": "bD-neL_jA9OBHsi6E-qQisZH9ymMf_HGOptXgpF18m0=" - }, - { - "id": "io.netty:netty-codec-haproxy:jar:4.1.92.Final", - "artifactId": "netty-codec-haproxy", - "groupId": "io.netty", - "version": "4.1.92.Final", - "sha256": "Xnm3j6tFiMQE2qZ9Jo7m5F9OMA0ymOAQ5zRZXonLmp0=" - }, - { - "id": "io.netty:netty-buffer:jar:4.1.92.Final", - "artifactId": "netty-buffer", - "groupId": "io.netty", - "version": "4.1.92.Final", - "sha256": "XOvv4hlfpFMYNDY0VMecdnVVFRjKI2tkCJxAPaeWd3A=" - }, - { - "id": "io.netty:netty-transport:jar:4.1.92.Final", - "artifactId": "netty-transport", - "groupId": "io.netty", - "version": "4.1.92.Final", - "sha256": "dHTl4npPkIoAOs5JZZZMzbb4zKICsYDqIPzK_r5Xypk=" - }, - { - "id": "io.smallrye.common:smallrye-common-annotation:jar:2.1.0", - "artifactId": "smallrye-common-annotation", - "groupId": "io.smallrye.common", - "version": "2.1.0", - "sha256": "ucbZrBiH9kUlnY0Nb_R1W8F9lh4uAug-rW9pmuPW-Mc=" - }, - { - "id": "io.quarkus:quarkus-vertx-latebound-mdc-provider:jar:3.1.0.Final", - "artifactId": "quarkus-vertx-latebound-mdc-provider", + "id": "io.quarkus:quarkus-rest-client-reactive-jackson:jar:3.2.5.Final", + "artifactId": "quarkus-rest-client-reactive-jackson", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "fFunrigjJJjjLsTgjJv9jwHbI4PzpVJjubh0Ww9WMmQ=" + "version": "3.2.5.Final", + "sha256": "ToOmdUeXKeqi91dGp_NkSfoNJt0sHfcpNTF5zOvm_X4=" }, { - "id": "io.smallrye:smallrye-fault-tolerance-vertx:jar:6.2.2", - "artifactId": "smallrye-fault-tolerance-vertx", - "groupId": "io.smallrye", - "version": "6.2.2", - "sha256": "laieRRclc0tCyliXP4pqullBSTDu1amSMB1WocmLgQY=" - }, - { - "id": "io.quarkus:quarkus-jackson:jar:3.1.0.Final", - "artifactId": "quarkus-jackson", - "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "MkTlJJPXGkO889caSSkj7Zc-25JueJbl6b1gGWVhoQc=" + "id": "io.quarkus.resteasy.reactive:resteasy-reactive-jackson:jar:3.2.5.Final", + "artifactId": "resteasy-reactive-jackson", + "groupId": "io.quarkus.resteasy.reactive", + "version": "3.2.5.Final", + "sha256": "eSysv820XYlCnizOqCziXqXmXHaOxs8hRsVa-luOxvA=" }, { - "id": "com.fasterxml.jackson.core:jackson-databind:jar:2.15.0", + "id": "com.fasterxml.jackson.core:jackson-databind:jar:2.15.2", "artifactId": "jackson-databind", "groupId": "com.fasterxml.jackson.core", - "version": "2.15.0", - "sha256": "AMWl1a5xrI6NW42mBoQeIlHIBjVZOctdUcTNxrZEoNw=" + "version": "2.15.2", + "sha256": "DrL9rW5Aq4gyp4ybIvWBlt2XBZTo09Wibq2HhHxPOpY=" }, { - "id": "com.fasterxml.jackson.core:jackson-annotations:jar:2.15.0", + "id": "com.fasterxml.jackson.core:jackson-annotations:jar:2.15.2", "artifactId": "jackson-annotations", "groupId": "com.fasterxml.jackson.core", - "version": "2.15.0", - "sha256": "ka3NPc9f2aFkmZNOdTaiPUVmkqAJPj1P1S8TjDk2NIw=" + "version": "2.15.2", + "sha256": "BOIflNz-5LB4-lpfUwR7eFqrpp0Z3jkvYW56f-XTiC8=" }, { - "id": "com.fasterxml.jackson.core:jackson-core:jar:2.15.0", + "id": "com.fasterxml.jackson.core:jackson-core:jar:2.15.2", "artifactId": "jackson-core", "groupId": "com.fasterxml.jackson.core", - "version": "2.15.0", - "sha256": "W0g_aPqd1qo32jfR953VxLlGQjj08GYKJCy2tccklQw=" - }, - { - "id": "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.15.0", - "artifactId": "jackson-datatype-jsr310", - "groupId": "com.fasterxml.jackson.datatype", - "version": "2.15.0", - "sha256": "1d65LVlmio3grI2oGNXZwP37Tbay8dZqd5_jV_MQ1kg=" - }, - { - "id": "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.15.0", - "artifactId": "jackson-datatype-jdk8", - "groupId": "com.fasterxml.jackson.datatype", - "version": "2.15.0", - "sha256": "YIxserorUSOU4zghnMCboNv-hbaDBF2bj0FZksJ0a3E=" - }, - { - "id": "com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.15.0", - "artifactId": "jackson-module-parameter-names", - "groupId": "com.fasterxml.jackson.module", - "version": "2.15.0", - "sha256": "DNhSpOXD5O9emIJphiPVnuGhiz3TzeRSKz2sESuWzVs=" - }, - { - "id": "io.smallrye.reactive:smallrye-mutiny-vertx-redis-client:jar:3.3.0", - "artifactId": "smallrye-mutiny-vertx-redis-client", - "groupId": "io.smallrye.reactive", - "version": "3.3.0", - "sha256": "DVbIA2WZrmiCWw8JQpPxgTSBuFt7uOHNDPbtmnHaf20=" + "version": "2.15.2", + "sha256": "MDyZ6CsfqpGguuXY--tW9-Kt-bUmqQDdcjvxQNYr1LQ=" }, { - "id": "io.vertx:vertx-redis-client:jar:4.4.2", - "artifactId": "vertx-redis-client", - "groupId": "io.vertx", - "version": "4.4.2", - "sha256": "DCdLwk2qWEVjYLuK5D9c-h5cc5-YCsK8ANv_fb6h1a4=" - }, - { - "id": "io.quarkus:quarkus-rest-client-reactive-jackson:jar:3.1.0.Final", - "artifactId": "quarkus-rest-client-reactive-jackson", - "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "2fThhcFyu7inY4lcjK5RlbCGdsccgrncXQnwh1zMJ6A=" - }, - { - "id": "io.quarkus.resteasy.reactive:resteasy-reactive-jackson:jar:3.1.0.Final", - "artifactId": "resteasy-reactive-jackson", - "groupId": "io.quarkus.resteasy.reactive", - "version": "3.1.0.Final", - "sha256": "4YiPmFxMs1E6wz5audzWgfsg5QWRZdZcF3D54rQdMqM=" - }, - { - "id": "io.quarkus:quarkus-rest-client-reactive:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-rest-client-reactive:jar:3.2.5.Final", "artifactId": "quarkus-rest-client-reactive", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "oEYCf4cuN6gOjy9z0bhSiO0EvKY7lar31P_JZ9HjGjU=" + "version": "3.2.5.Final", + "sha256": "Vj4U0vjSgI0kNppDzyA4tn4hxpbzFMAEHx3Tcz4_yN4=" }, { - "id": "io.quarkus:quarkus-jaxrs-client-reactive:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-jaxrs-client-reactive:jar:3.2.5.Final", "artifactId": "quarkus-jaxrs-client-reactive", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "vthh4lVyQq1pMizLOhrJT78bU-LkaUWOUK8PIBYntx4=" + "version": "3.2.5.Final", + "sha256": "oEjzri6fZLbUmvTSJ-NGl54KKRjCEn5LEzoKzMNixEQ=" }, { - "id": "io.quarkus.resteasy.reactive:resteasy-reactive-client:jar:3.1.0.Final", + "id": "io.quarkus.resteasy.reactive:resteasy-reactive-client:jar:3.2.5.Final", "artifactId": "resteasy-reactive-client", "groupId": "io.quarkus.resteasy.reactive", - "version": "3.1.0.Final", - "sha256": "M_cnN2VQ2EUfDrbZdHknKSkg9cQzR3Cs0oiEc1OCcWo=" + "version": "3.2.5.Final", + "sha256": "Xd_kK76-v74OoBURjJipwC_jTuN-hT4ZckF5ERa9bG4=" }, { - "id": "io.vertx:vertx-web-client:jar:4.4.2", + "id": "io.vertx:vertx-web-client:jar:4.4.4", "artifactId": "vertx-web-client", "groupId": "io.vertx", - "version": "4.4.2", - "sha256": "yutnhBLYsB-tO1eNK13rDiz3Hy-Crxvf22y2pMfBHFU=" + "version": "4.4.4", + "sha256": "4TcZx8doI_NbhMPGQ6VhRVaRF8jdLk4Vy2gWQ18dJhc=" }, { - "id": "io.quarkus:quarkus-smallrye-stork:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-smallrye-stork:jar:3.2.5.Final", "artifactId": "quarkus-smallrye-stork", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "wkVQmH5csj_JZ5uv83mLfR8-Y9wYKX5wvRvuG2AYM44=" + "version": "3.2.5.Final", + "sha256": "eoZ-krbYRDC4K62DiKH6X0emmdGeXYVAG7q_aY0TEZk=" }, { - "id": "io.quarkus:quarkus-rest-client-config:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-rest-client-config:jar:3.2.5.Final", "artifactId": "quarkus-rest-client-config", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "eHWjI9b8rGPBy4S7sToV4tdK0JSnyTEn9aTV6l99Hho=" + "version": "3.2.5.Final", + "sha256": "xWsyJLjWX5anT1rMjfdIjK8IVkJp47cXq6lHk3MZ7WY=" }, { - "id": "io.smallrye.stork:stork-api:jar:2.2.0", + "id": "io.smallrye.stork:stork-api:jar:2.2.1", "artifactId": "stork-api", "groupId": "io.smallrye.stork", - "version": "2.2.0", - "sha256": "BMYAdNq7xXhGLidjDIkUOE_Qxce6Ir9kTEisNXzHljA=" + "version": "2.2.1", + "sha256": "PEt8OpJPZR360J0IcxN23Pg7MQlju-QXwOxeAgyN4jA=" }, { - "id": "io.smallrye.stork:stork-core:jar:2.2.0", + "id": "io.smallrye.stork:stork-core:jar:2.2.1", "artifactId": "stork-core", "groupId": "io.smallrye.stork", - "version": "2.2.0", - "sha256": "9SEzjWk-426yyc5QAwC2wrgAeHRyXfjxKp-7DlIDOiw=" + "version": "2.2.1", + "sha256": "AJw3OYT-WZUnHqSGqlmlvS3Y10EpyMix7pq4P-BgdqM=" }, { "id": "org.eclipse.microprofile.rest.client:microprofile-rest-client-api:jar:3.0.1", @@ -861,6 +833,83 @@ "version": "3.0.1", "sha256": "K3Fpi5XIksxlkjX0_-ZjteLxFRzrTncmiyP_KwxjIcs=" }, + { + "id": "io.quarkus:quarkus-cache:jar:3.2.5.Final", + "artifactId": "quarkus-cache", + "groupId": "io.quarkus", + "version": "3.2.5.Final", + "sha256": "J3MXJAuFO1v0PqZU5wiHp_fG8KYBrSvceWIhkFU-i_I=" + }, + { + "id": "io.quarkus:quarkus-caffeine:jar:3.2.5.Final", + "artifactId": "quarkus-caffeine", + "groupId": "io.quarkus", + "version": "3.2.5.Final", + "sha256": "18R__BLCfuJkJEea0bs387w-GaohDvvgqNCm0OKOcB8=" + }, + { + "id": "com.github.ben-manes.caffeine:caffeine:jar:3.1.5", + "artifactId": "caffeine", + "groupId": "com.github.ben-manes.caffeine", + "version": "3.1.5", + "sha256": "AsNP0rp4sZVu9uXemskVrum_pJhYmWb6hyVfuttlNJk=" + }, + { + "id": "com.google.errorprone:error_prone_annotations:jar:2.19.1", + "artifactId": "error_prone_annotations", + "groupId": "com.google.errorprone", + "version": "2.19.1", + "sha256": "rdOZAphQH9y27Fj_8mYIwdKY-13-L4DXSGe_D1xTlmg=" + }, + { + "id": "io.quarkus:quarkus-mutiny:jar:3.2.5.Final", + "artifactId": "quarkus-mutiny", + "groupId": "io.quarkus", + "version": "3.2.5.Final", + "sha256": "Wd-jRe0GjDipeSJHsAQ44U-MtU4QXWQ96gYp57Vsjg8=" + }, + { + "id": "io.quarkus:quarkus-smallrye-context-propagation:jar:3.2.5.Final", + "artifactId": "quarkus-smallrye-context-propagation", + "groupId": "io.quarkus", + "version": "3.2.5.Final", + "sha256": "1R_Mq4gS0cE4gzSoZtntyfWIrEdSQY5aeIEa3UMVLTM=" + }, + { + "id": "io.smallrye:smallrye-context-propagation:jar:2.1.0", + "artifactId": "smallrye-context-propagation", + "groupId": "io.smallrye", + "version": "2.1.0", + "sha256": "mudqxaICUFD5ng1BwVv6eVfYqERYqtM7eaNwrQYnHBo=" + }, + { + "id": "io.smallrye:smallrye-context-propagation-api:jar:2.1.0", + "artifactId": "smallrye-context-propagation-api", + "groupId": "io.smallrye", + "version": "2.1.0", + "sha256": "F2_yFsVyPQ-aGNswPfrtzzuYi077Cn4OVu2Ld1YfLIk=" + }, + { + "id": "io.smallrye:smallrye-context-propagation-storage:jar:2.1.0", + "artifactId": "smallrye-context-propagation-storage", + "groupId": "io.smallrye", + "version": "2.1.0", + "sha256": "BaFJvAxKV73abzKF5Flz1kxg86MCYdYD0a8hLy_Gd1s=" + }, + { + "id": "io.smallrye.reactive:mutiny-smallrye-context-propagation:jar:2.3.1", + "artifactId": "mutiny-smallrye-context-propagation", + "groupId": "io.smallrye.reactive", + "version": "2.3.1", + "sha256": "fRSOupO9hgc6YDllSL-jxCwhoZ-61rmBUDZgBKoAiTA=" + }, + { + "id": "io.quarkus:quarkus-cache-runtime-spi:jar:3.2.5.Final", + "artifactId": "quarkus-cache-runtime-spi", + "groupId": "io.quarkus", + "version": "3.2.5.Final", + "sha256": "X0_BI8LIfBAJuTN57TIjxrW60KD1rbtgZSjb30_VuqU=" + }, { "id": "it.pagopa.swclient.mil:common:jar:2.0.2", "artifactId": "common", @@ -883,25 +932,25 @@ "sha256": "T8z_g4Kq_FiZYsTtsmL2qlleNPHhHmEFfRxqluj8cyM=" }, { - "id": "io.quarkus:quarkus-junit5:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-junit5:jar:3.2.5.Final", "artifactId": "quarkus-junit5", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "gPGsuXew_GznOkdS7GEJdu9a-Iys4foq5wJ5yodGcIU=" + "version": "3.2.5.Final", + "sha256": "6_PDb_nKRTOQ1pNLWAPonGDa9SMUnfrKaT0sfX8pNOU=" }, { - "id": "io.quarkus:quarkus-bootstrap-core:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-bootstrap-core:jar:3.2.5.Final", "artifactId": "quarkus-bootstrap-core", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "AihONrK96gK8lAd8xk6bRHzpR4q_WhjapSH2v3AJHFk=" + "version": "3.2.5.Final", + "sha256": "K_I_PVCD_vE3UY8KM4p8Ubgfap4GupL_PuBSk1LsdLk=" }, { - "id": "io.quarkus:quarkus-bootstrap-app-model:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-bootstrap-app-model:jar:3.2.5.Final", "artifactId": "quarkus-bootstrap-app-model", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "bM4RXT0ELcHgR2ESBPOn-swAjEcjddGBW8UPbAXkXYs=" + "version": "3.2.5.Final", + "sha256": "vZVKZR6lF4rJPD94bjuqGQD0iph2XvgUhaNbsDc3Xco=" }, { "id": "io.smallrye.common:smallrye-common-io:jar:2.1.0", @@ -918,18 +967,18 @@ "sha256": "xZlAELzc4dK9YDpNUMRxkd29eHXRFXsjqqJtM8gv2hM=" }, { - "id": "io.quarkus:quarkus-test-common:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-test-common:jar:3.2.5.Final", "artifactId": "quarkus-test-common", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "Ti7msfJLKRsxj7ViDO8XBFbeYF2_CvuBBJesC8qt_IY=" + "version": "3.2.5.Final", + "sha256": "xSWFAtPhu2rIamUxU6shdhusN1zPo3lmwgqeb6r3oIc=" }, { - "id": "io.quarkus:quarkus-core-deployment:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-core-deployment:jar:3.2.5.Final", "artifactId": "quarkus-core-deployment", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "nYskDjL0L_rlU11UNhyQnpyhZ8-PH-Xuk_sRSubB5KY=" + "version": "3.2.5.Final", + "sha256": "y4nAbS8DDn5FQdq3D8GByl2lar2EgwRXCO02cZjpI_c=" }, { "id": "org.aesh:readline:jar:2.4", @@ -974,25 +1023,25 @@ "sha256": "OfHPF5EzVwHDsCyueyvCEFfsmlWyJAeJy21VKyssYvo=" }, { - "id": "io.quarkus:quarkus-class-change-agent:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-class-change-agent:jar:3.2.5.Final", "artifactId": "quarkus-class-change-agent", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "V7SZ-RzIyvPRaq5d0ZHy1XakXPUQj4A9ddL1w7XudoY=" + "version": "3.2.5.Final", + "sha256": "mwp4L5mlexxTpgGLydNq6GscpBt0SgbnV4JAaoftWi4=" }, { - "id": "io.quarkus:quarkus-devtools-utilities:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-devtools-utilities:jar:3.2.5.Final", "artifactId": "quarkus-devtools-utilities", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "NuKE-zd3fECO4mvpvFzhZc7GDiR9HX9CagbtOg6aFSU=" + "version": "3.2.5.Final", + "sha256": "UJ1TvZIQ8-o8ZOzp06_RqpbiSoy-vnPXe8v11dQ1emg=" }, { - "id": "io.quarkus:quarkus-builder:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-builder:jar:3.2.5.Final", "artifactId": "quarkus-builder", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "Qqf38rj2P5AaIMRKfnc1tMvyB5UmckU4ELLEu8G4QNU=" + "version": "3.2.5.Final", + "sha256": "PjorvRgVq9RuLpALnLiVH0eVSqAiXWf9rvZ_f-74spQ=" }, { "id": "org.junit.platform:junit-platform-launcher:jar:1.9.3", @@ -1002,32 +1051,32 @@ "sha256": "hRXpGAignIya9eNZGFtZkbXna4rc8YW5o9q07I5bqP8=" }, { - "id": "io.quarkus:quarkus-bootstrap-maven-resolver:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-bootstrap-maven-resolver:jar:3.2.5.Final", "artifactId": "quarkus-bootstrap-maven-resolver", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "o0LlJGu7gAZTKiWAlBoYpB6zX5jJw8qdIAwoHfKI3Ww=" + "version": "3.2.5.Final", + "sha256": "JK790wktl8cwJb9Fjkzn9tEtW2NQuH4E80u-gAbQu1I=" }, { - "id": "io.smallrye.beanbag:smallrye-beanbag-maven:jar:1.1.0", + "id": "io.smallrye.beanbag:smallrye-beanbag-maven:jar:1.3.2", "artifactId": "smallrye-beanbag-maven", "groupId": "io.smallrye.beanbag", - "version": "1.1.0", - "sha256": "9srzaK-4FCXyliCQ6T05cSEaMOm4prykDXaAuV2HgUk=" + "version": "1.3.2", + "sha256": "q9qo_vYO1AWg4EY0r4NGpa7yvDQE64toNkQJZBMGpvo=" }, { - "id": "io.smallrye.beanbag:smallrye-beanbag-sisu:jar:1.1.0", + "id": "io.smallrye.beanbag:smallrye-beanbag-sisu:jar:1.3.2", "artifactId": "smallrye-beanbag-sisu", "groupId": "io.smallrye.beanbag", - "version": "1.1.0", - "sha256": "FT2rLdCrWFq-OKRcpoyJthXWgADqlKSuCKNawA-Hf00=" + "version": "1.3.2", + "sha256": "wpt6t29aNrbY-WsmA3jEuXMQob1RoJTR28KUEhKTdco=" }, { - "id": "io.smallrye.beanbag:smallrye-beanbag:jar:1.1.0", + "id": "io.smallrye.beanbag:smallrye-beanbag:jar:1.3.2", "artifactId": "smallrye-beanbag", "groupId": "io.smallrye.beanbag", - "version": "1.1.0", - "sha256": "mt8a7XJGt35rq94C8MgN6DjQk0Dvp-5pwV17d5s73MY=" + "version": "1.3.2", + "sha256": "FC45bawjk-ETX82Adv_WmUD3FiosGpL29KY5pATcCnc=" }, { "id": "javax.inject:javax.inject:jar:1", @@ -1037,81 +1086,88 @@ "sha256": "kcdwRKUMSBY2wy2Rb9ickRinIZU5BFLIEGUID5V95_8=" }, { - "id": "org.apache.maven:maven-artifact:jar:3.9.1", + "id": "org.apache.maven:maven-artifact:jar:3.9.3", "artifactId": "maven-artifact", "groupId": "org.apache.maven", - "version": "3.9.1", - "sha256": "G2c9ds-HdzRMY1DViFM-o5Tdk_6aGyv_MZ2Wyw3edPI=" + "version": "3.9.3", + "sha256": "0ynviuj2VK7KA0BMPO2iTPdH9FmKq-dTnZ7jJbbFujA=" }, { - "id": "org.apache.maven:maven-builder-support:jar:3.9.1", + "id": "org.apache.maven:maven-builder-support:jar:3.9.3", "artifactId": "maven-builder-support", "groupId": "org.apache.maven", - "version": "3.9.1", - "sha256": "_-UV0667ouXb5Vgbjv_sQcsDT0CePcF4T9poC9xGAxs=" + "version": "3.9.3", + "sha256": "ZJ2vA8zPbZ4ed30wtrZgRKgEEpXliQgOUFZ1C-rG0dk=" }, { - "id": "org.apache.maven:maven-model:jar:3.9.1", + "id": "org.apache.maven:maven-model:jar:3.9.3", "artifactId": "maven-model", "groupId": "org.apache.maven", - "version": "3.9.1", - "sha256": "daSBALi0BXpbGpMFam4yk6cwha2SrQ98QUi28mfd8Hg=" + "version": "3.9.3", + "sha256": "DvbJYm1RCNyw9ROoXHXbBnNvMK1qt2IMhfrPyKd3KoM=" }, { - "id": "org.apache.maven:maven-model-builder:jar:3.9.1", + "id": "org.apache.maven:maven-model-builder:jar:3.9.3", "artifactId": "maven-model-builder", "groupId": "org.apache.maven", - "version": "3.9.1", - "sha256": "ODiFpM8aGwz5QbtNLcPTvN0lG0a2jAScJ3Ma9QszIpA=" + "version": "3.9.3", + "sha256": "spxvCUslYhnO3Gdv6ZMtUbuphncioh6xuirwSFMLwws=" }, { - "id": "org.apache.maven:maven-repository-metadata:jar:3.9.1", + "id": "org.apache.maven:maven-repository-metadata:jar:3.9.3", "artifactId": "maven-repository-metadata", "groupId": "org.apache.maven", - "version": "3.9.1", - "sha256": "_kZQXv0VA7pIufdfJU2Z_pCv8mfBP4qq7f3kydg4VNc=" + "version": "3.9.3", + "sha256": "SV_VXOgx8x0t92Q56ev1UwOiVRBZy0XTeWyC-6y33Dg=" }, { - "id": "org.apache.maven:maven-settings:jar:3.9.1", + "id": "org.apache.maven:maven-settings:jar:3.9.3", "artifactId": "maven-settings", "groupId": "org.apache.maven", - "version": "3.9.1", - "sha256": "Jhf0vPV7ewabkzW76AC-mnDznQxGnj_RrGLk-veK2G4=" + "version": "3.9.3", + "sha256": "WXCpHPnJxETSa5o2b-ClNLtIV96FIb73x-Li3nRwJB4=" }, { - "id": "org.apache.maven.resolver:maven-resolver-api:jar:1.9.7", + "id": "org.apache.maven.resolver:maven-resolver-api:jar:1.9.13", "artifactId": "maven-resolver-api", "groupId": "org.apache.maven.resolver", - "version": "1.9.7", - "sha256": "iU6wfqeV83SjA33-6Vg-LSUp1SxkoqH92tiscXSewlI=" + "version": "1.9.13", + "sha256": "osmXDOcq_O7OCYQz5VAcTdMySUOJNToX2eSAcuMtuA8=" }, { - "id": "org.apache.maven.resolver:maven-resolver-impl:jar:1.9.7", + "id": "org.apache.maven.resolver:maven-resolver-impl:jar:1.9.13", "artifactId": "maven-resolver-impl", "groupId": "org.apache.maven.resolver", - "version": "1.9.7", - "sha256": "6dlbsg2tDt7WXo3ilDBlH_8SGOPhH859AqK9IzcuY4s=" + "version": "1.9.13", + "sha256": "YqadJ6k57yn-SisNp9WVQYqmBXx4OYeja4wcc-etkkU=" }, { - "id": "org.apache.maven.resolver:maven-resolver-named-locks:jar:1.9.7", + "id": "org.apache.maven.resolver:maven-resolver-named-locks:jar:1.9.13", "artifactId": "maven-resolver-named-locks", "groupId": "org.apache.maven.resolver", - "version": "1.9.7", - "sha256": "ZFsfbFnTJTXhGEh5yj-dRumDUiX738T1lfHymRwkHnU=" + "version": "1.9.13", + "sha256": "1zscNeb40a9h4r8uT0o6sYZqsG0uGe79hVav7JloNXw=" }, { - "id": "org.apache.maven.resolver:maven-resolver-spi:jar:1.9.7", + "id": "org.apache.maven.resolver:maven-resolver-spi:jar:1.9.13", "artifactId": "maven-resolver-spi", "groupId": "org.apache.maven.resolver", - "version": "1.9.7", - "sha256": "ddn8IobI5C2r1HLql28QZDLnrnPFtg8EDCuP-7oEN20=" + "version": "1.9.13", + "sha256": "u0F-t03Ef9pU8PlKeNyuDbHUdx_NyIsDZNLT4IJmIqE=" }, { - "id": "org.apache.maven.resolver:maven-resolver-util:jar:1.9.7", + "id": "org.apache.maven.resolver:maven-resolver-util:jar:1.9.13", "artifactId": "maven-resolver-util", "groupId": "org.apache.maven.resolver", - "version": "1.9.7", - "sha256": "VRFq5uED5nCv92GqfUfbjTnoVwirY6ddx2QB5g_vPxw=" + "version": "1.9.13", + "sha256": "WXayWxQA41CH1uzrM9d-PxzVDv03clspAnD59oqrvuA=" + }, + { + "id": "org.apache.maven.resolver:maven-resolver-transport-http:jar:1.9.10", + "artifactId": "maven-resolver-transport-http", + "groupId": "org.apache.maven.resolver", + "version": "1.9.10", + "sha256": "DECxNqZ1AAu_UWqQB1j8m284_oAnRYY6454LTSpHxW4=" }, { "id": "org.apache.maven.wagon:wagon-provider-api:jar:3.5.3", @@ -1120,13 +1176,6 @@ "version": "3.5.3", "sha256": "XnIAAziUXtPpb45PV40dBnLhr34ZwOkBQZeuWzGvPvQ=" }, - { - "id": "org.apache.maven.wagon:wagon-http-lightweight:jar:3.5.3", - "artifactId": "wagon-http-lightweight", - "groupId": "org.apache.maven.wagon", - "version": "3.5.3", - "sha256": "mi9pIwcYuGIxG7LUeOC055mr_wB2eSr3wmt5i5kWnCM=" - }, { "id": "org.apache.maven.wagon:wagon-http-shared:jar:3.5.3", "artifactId": "wagon-http-shared", @@ -1149,32 +1198,60 @@ "sha256": "huAlXUyHnGG0gz7X8TEk6LtnnfR967EnMm59t91JoHs=" }, { - "id": "org.sonatype.plexus:plexus-cipher:jar:1.7", + "id": "org.codehaus.plexus:plexus-xml:jar:4.0.0", + "artifactId": "plexus-xml", + "groupId": "org.codehaus.plexus", + "version": "4.0.0", + "sha256": "OEPevd7tK_f5Jk-MidAXvEkw6EMqouZl_eWUmAXgUcc=" + }, + { + "id": "org.apache.maven:maven-xml-impl:jar:4.0.0-alpha-5", + "artifactId": "maven-xml-impl", + "groupId": "org.apache.maven", + "version": "4.0.0-alpha-5", + "sha256": "-n0NBaJil2lH6V9yx-KjMAuO6-Kmsz60CABDzdqhOFQ=" + }, + { + "id": "org.apache.maven:maven-api-xml:jar:4.0.0-alpha-5", + "artifactId": "maven-api-xml", + "groupId": "org.apache.maven", + "version": "4.0.0-alpha-5", + "sha256": "40VWp6viUJGUlCzOubolWH8W9ZZP8WAQL-SSb2fjiVE=" + }, + { + "id": "org.apache.maven:maven-api-meta:jar:4.0.0-alpha-5", + "artifactId": "maven-api-meta", + "groupId": "org.apache.maven", + "version": "4.0.0-alpha-5", + "sha256": "doiV4RrTeryOksHc9u5SrIGvgNR6PWCMzVhj19TdFLI=" + }, + { + "id": "org.codehaus.plexus:plexus-cipher:jar:2.0", "artifactId": "plexus-cipher", - "groupId": "org.sonatype.plexus", - "version": "1.7", - "sha256": "EUhZhh_xD5h7iA1vNOMhUnSvPMkrOnODHITVluN8ZRE=" + "groupId": "org.codehaus.plexus", + "version": "2.0", + "sha256": "mn8bXFqe_9Yerf2HMUUqL3ao55ER-sOR73XqgBvqIDo=" }, { - "id": "org.sonatype.plexus:plexus-sec-dispatcher:jar:1.4", + "id": "org.codehaus.plexus:plexus-sec-dispatcher:jar:2.0", "artifactId": "plexus-sec-dispatcher", - "groupId": "org.sonatype.plexus", - "version": "1.4", - "sha256": "2nPjK1gTLmTa8SJp_Z0BHAswPyNIQPF5kIclpjK2tXw=" + "groupId": "org.codehaus.plexus", + "version": "2.0", + "sha256": "hzE5lgxMeAF23aWAsAOixL-CGIvc5buZI04iTves_Os=" }, { - "id": "org.apache.maven:maven-embedder:jar:3.9.1", + "id": "org.apache.maven:maven-embedder:jar:3.9.3", "artifactId": "maven-embedder", "groupId": "org.apache.maven", - "version": "3.9.1", - "sha256": "4fWlcTtwMsUF7NmhlljA6BSKY81tO6XYZBHW8QpUsYo=" + "version": "3.9.3", + "sha256": "nsvhn7Ez0XY_2BYlDsLAJeBZMjsJbG1GQxSsVJUGaoE=" }, { - "id": "org.apache.maven:maven-core:jar:3.9.1", + "id": "org.apache.maven:maven-core:jar:3.9.3", "artifactId": "maven-core", "groupId": "org.apache.maven", - "version": "3.9.1", - "sha256": "eUlEmkNRQ6idMWSJf3P718IAoMPIuVoK9oyB3T6Peug=" + "version": "3.9.3", + "sha256": "JVcYFg4R_GOKSgEsq2s9q-6lDV_iWjVctQgEv8XvdBc=" }, { "id": "org.codehaus.plexus:plexus-component-annotations:jar:2.1.0", @@ -1184,11 +1261,11 @@ "sha256": "veNhfOm1vPlYQSYEYIAEOvaks7rqQKOxU_Aue7wyrKw=" }, { - "id": "org.apache.maven:maven-plugin-api:jar:3.9.1", + "id": "org.apache.maven:maven-plugin-api:jar:3.9.3", "artifactId": "maven-plugin-api", "groupId": "org.apache.maven", - "version": "3.9.1", - "sha256": "3mGFwQojTCGeCpXotnAsjLsocn_VfAmX2Kl-24n56KM=" + "version": "3.9.3", + "sha256": "lbIa--prJyWbccrFUhVbvpn2jmkxTvJx6dA0GQMgaZE=" }, { "id": "org.apache.maven.shared:maven-shared-utils:jar:3.3.4", @@ -1212,11 +1289,11 @@ "sha256": "Ct3sZw_tzT8RPFyAkdeDKA0j9146y4QbYanNsHk3agg=" }, { - "id": "com.google.guava:guava:jar:31.1-jre", + "id": "com.google.guava:guava:jar:32.0.0-jre", "artifactId": "guava", "groupId": "com.google.guava", - "version": "31.1-jre", - "sha256": "pC7cnKt5Ljn-ObuU8_ymVe0Vf_h6iveOHWulsHxKAKs=" + "version": "32.0.0-jre", + "sha256": "OfNVCwND2NGd1Og70WW1jqM4nS3bnyFI5jkD957NsRQ=" }, { "id": "com.google.guava:failureaccess:jar:1.0.1", @@ -1240,25 +1317,11 @@ "sha256": "Uvd8XsSfeHycQX6-1dbv2ZIvRKIC8hc3bk-UwNdPNUk=" }, { - "id": "org.codehaus.plexus:plexus-sec-dispatcher:jar:2.0", - "artifactId": "plexus-sec-dispatcher", - "groupId": "org.codehaus.plexus", - "version": "2.0", - "sha256": "hzE5lgxMeAF23aWAsAOixL-CGIvc5buZI04iTves_Os=" - }, - { - "id": "org.codehaus.plexus:plexus-cipher:jar:2.0", - "artifactId": "plexus-cipher", - "groupId": "org.codehaus.plexus", - "version": "2.0", - "sha256": "mn8bXFqe_9Yerf2HMUUqL3ao55ER-sOR73XqgBvqIDo=" - }, - { - "id": "commons-cli:commons-cli:jar:1.4", + "id": "commons-cli:commons-cli:jar:1.5.0", "artifactId": "commons-cli", "groupId": "commons-cli", - "version": "1.4", - "sha256": "_Tx8lUWpzbIFHR-RVcT3ax5KxaVzBEBKbu21eP-6cyg=" + "version": "1.5.0", + "sha256": "vIuwH8D60lA4VwbiD5J93P9hc_Yzmzh9yHkjd1JWesY=" }, { "id": "org.eclipse.sisu:org.eclipse.sisu.plexus:jar:0.3.5", @@ -1268,32 +1331,32 @@ "sha256": "fkxhCW1wgm8g96fVXFmlUo56pa0kfuLf5UTk3SX2p4Q=" }, { - "id": "org.apache.maven:maven-settings-builder:jar:3.9.1", + "id": "org.apache.maven:maven-settings-builder:jar:3.9.3", "artifactId": "maven-settings-builder", "groupId": "org.apache.maven", - "version": "3.9.1", - "sha256": "8_gkH_fwkfttwLNG69FyyCD1gliri5YCRp4OMTd_7lU=" + "version": "3.9.3", + "sha256": "tr9gR-kjdGUznNTNfepSfPAka-UYMi7bUIVq9mhxD3E=" }, { - "id": "org.apache.maven:maven-resolver-provider:jar:3.9.1", + "id": "org.apache.maven:maven-resolver-provider:jar:3.9.3", "artifactId": "maven-resolver-provider", "groupId": "org.apache.maven", - "version": "3.9.1", - "sha256": "disdXelEKYX8zDivgJPM9TUaY90N9MH_Y3rK3KNPXrM=" + "version": "3.9.3", + "sha256": "wQMDJiwUMi3PMCoJPYJOM8OpD6dh9O8Zuq-9f1F8XXY=" }, { - "id": "org.apache.maven.resolver:maven-resolver-connector-basic:jar:1.9.7", + "id": "org.apache.maven.resolver:maven-resolver-connector-basic:jar:1.9.13", "artifactId": "maven-resolver-connector-basic", "groupId": "org.apache.maven.resolver", - "version": "1.9.7", - "sha256": "untePGywve-5xOSYcc0w3LWex0m85Z-OFXHN9cKPOWM=" + "version": "1.9.13", + "sha256": "UBE7tCTMqDYBSiS0A_8wIf_-W_O3IzXK9tExgIcvosU=" }, { - "id": "org.apache.maven.resolver:maven-resolver-transport-wagon:jar:1.9.7", + "id": "org.apache.maven.resolver:maven-resolver-transport-wagon:jar:1.9.13", "artifactId": "maven-resolver-transport-wagon", "groupId": "org.apache.maven.resolver", - "version": "1.9.7", - "sha256": "S-wOg7vnvx6N-IsxyMIyTOArnj6YdQgoT4lTBGgieAs=" + "version": "1.9.13", + "sha256": "sKcdLZjbfOSqxQKmqjtGagP1fPCsnXkUSJdHNd3qiGc=" }, { "id": "org.apache.maven.wagon:wagon-http:jar:3.5.3", @@ -1310,32 +1373,32 @@ "sha256": "r8khb6l7eNrSJ7So1NZ7mJe_ETpX-AWY1imThBET4QM=" }, { - "id": "io.quarkus:quarkus-bootstrap-gradle-resolver:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-bootstrap-gradle-resolver:jar:3.2.5.Final", "artifactId": "quarkus-bootstrap-gradle-resolver", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "BGNEr2loSacP_slGPqXUhxtrFxqnExTgu2M506XrgWE=" + "version": "3.2.5.Final", + "sha256": "3EpQTz8B_8chso4sz7sMarX6keOuUKIKDUBqL7HA5eU=" }, { - "id": "io.smallrye:jandex:jar:3.1.1", + "id": "io.smallrye:jandex:jar:3.1.2", "artifactId": "jandex", "groupId": "io.smallrye", - "version": "3.1.1", - "sha256": "FP3vJDxsU0EA1CCc9P8ORZtRIBHGrPesN0wDPnk2VJ8=" + "version": "3.1.2", + "sha256": "3uEvoXh9VSPtGgLWxjsZ5672rFYPfG1wWV2wGqN-BB4=" }, { - "id": "commons-io:commons-io:jar:2.11.0", + "id": "commons-io:commons-io:jar:2.13.0", "artifactId": "commons-io", "groupId": "commons-io", - "version": "2.11.0", - "sha256": "lhsvbYfbrMXVSr9Fq3puJJX4m3VZiWLYxyPOqbwhCQg=" + "version": "2.13.0", + "sha256": "Zx6qOWiNrC_6pGRbPJmAri0OokceSual2hmc0VriNmY=" }, { - "id": "io.quarkus:quarkus-junit5-properties:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-junit5-properties:jar:3.2.5.Final", "artifactId": "quarkus-junit5-properties", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "Z9wmOcN_8Emade_bta40FOODySm8Fhp6v9Uz1oYxPyk=" + "version": "3.2.5.Final", + "sha256": "KaHushfTg7QmZJpVRWZfj3uASnSiyFXN15yDpIKKRHY=" }, { "id": "org.junit.jupiter:junit-jupiter:jar:5.9.3", @@ -1450,11 +1513,11 @@ "sha256": "bJs90UKgncRo4jrTmq1vdaDyuFElEERp8CblKkdORk8=" }, { - "id": "commons-codec:commons-codec:jar:1.15", + "id": "commons-codec:commons-codec:jar:1.16.0", "artifactId": "commons-codec", "groupId": "commons-codec", - "version": "1.15", - "sha256": "s-n21jp5AQm_DQVmEfvtHPaQVYJt7-uYlKcTadJG7WM=" + "version": "1.16.0", + "sha256": "VllfsgsLhbyR0NUD2tULt_G5r8Du1d_6bLslkpAASE0=" }, { "id": "org.apache.httpcomponents:httpmime:jar:4.5.14", @@ -1513,18 +1576,18 @@ "sha256": "2RnZBEhsA3-NGTQS2gyS4iqfokIwudZ6V4VcXDHH6U4=" }, { - "id": "io.quarkus:quarkus-junit5-mockito:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-junit5-mockito:jar:3.2.5.Final", "artifactId": "quarkus-junit5-mockito", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "m_A-_q5hEEwOdKwrQyjXy2sgAycts0vFOXsQYrT7JDU=" + "version": "3.2.5.Final", + "sha256": "_RKPgM4PyifOOSc40y_oySsvX1oIb5BtZA6H96rDgUk=" }, { - "id": "io.quarkus:quarkus-junit5-mockito-config:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-junit5-mockito-config:jar:3.2.5.Final", "artifactId": "quarkus-junit5-mockito-config", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "QEezby3GQjgcnBoeAITtu-vuT5OxsPh69Ba4D3W6oH4=" + "version": "3.2.5.Final", + "sha256": "gRTWwhPkoWqHmqyXfycfmOmEqXghYVcRAP0icTwramE=" }, { "id": "org.mockito:mockito-core:jar:5.3.1", @@ -1555,95 +1618,46 @@ "sha256": "At_QsEOaVZHjW3CO0vVHTrCUj1Or90Y36Vm45O9pv-s=" }, { - "id": "io.quarkus:quarkus-arc-deployment:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-arc-deployment:jar:3.2.5.Final", "artifactId": "quarkus-arc-deployment", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "6bx4ms6M4Mm24qWi-Q0Xj2UpekAJ_SVIpq38iFcrJEc=" + "version": "3.2.5.Final", + "sha256": "1T9TYaejbNDRMLACZ2e4DFfzZeDYmaiP1CgagvLxQ6w=" }, { - "id": "io.quarkus:quarkus-smallrye-context-propagation-spi:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-smallrye-context-propagation-spi:jar:3.2.5.Final", "artifactId": "quarkus-smallrye-context-propagation-spi", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "vMBQ_jnsgFuUGpGaCdu8JZgu55Msdptale9s7ep6TJ8=" + "version": "3.2.5.Final", + "sha256": "TUiSs7BNuIdhJm-Z7K9ihAtJXnVtiCtnjiik6i8qpYY=" }, { - "id": "io.quarkus:quarkus-vertx-http-dev-console-spi:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-vertx-http-dev-console-spi:jar:3.2.5.Final", "artifactId": "quarkus-vertx-http-dev-console-spi", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "FiMGXbIxBzlCZzqjcwvg6S-1Vq4Z3p32BebvqlY4MGY=" + "version": "3.2.5.Final", + "sha256": "cEdAWLhbboSRgQkWXzMK1HhW8sk12chCwLAp5TFbVKc=" }, { - "id": "io.quarkus:quarkus-vertx-http-dev-ui-spi:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-vertx-http-dev-ui-spi:jar:3.2.5.Final", "artifactId": "quarkus-vertx-http-dev-ui-spi", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "diRRF-W-SoGsRVrRSlXC4cOSSrLY65W9Fk0rvQf5q8E=" + "version": "3.2.5.Final", + "sha256": "ZxXUqtfzLpL50MEcFs65_bJC8dYzQEFwipcVPE2002g=" }, { - "id": "io.quarkus.arc:arc-processor:jar:3.1.0.Final", + "id": "io.quarkus.arc:arc-processor:jar:3.2.5.Final", "artifactId": "arc-processor", "groupId": "io.quarkus.arc", - "version": "3.1.0.Final", - "sha256": "dclA1WfYBRlGJL8R9Dwy-wGKMBnIjOMb4Qz_ts3ZuSc=" - }, - { - "id": "org.mockito:mockito-subclass:jar:5.3.1", - "artifactId": "mockito-subclass", - "groupId": "org.mockito", - "version": "5.3.1", - "sha256": "fhxMciD8O89i4H5Aq8rn3DHQF8Rc_qiKxsg2lfLlGEo=" - }, - { - "id": "io.quarkus:quarkus-mutiny:jar:3.1.0.Final", - "artifactId": "quarkus-mutiny", - "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "g1IMlJGeXX_XSkrUEhYeOKA8F1PjLCMicQoetGDztfs=" + "version": "3.2.5.Final", + "sha256": "Vih7RcvsWZanMG5RIpzlRAqo8vb19A1HbXKExdjfHSI=" }, { - "id": "io.quarkus:quarkus-smallrye-context-propagation:jar:3.1.0.Final", - "artifactId": "quarkus-smallrye-context-propagation", - "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "FPjAZf46X79Aqhb5BkPWcEZQcQ0f-pNQG9EpJrRwyyA=" - }, - { - "id": "io.smallrye:smallrye-context-propagation:jar:2.1.0", - "artifactId": "smallrye-context-propagation", - "groupId": "io.smallrye", - "version": "2.1.0", - "sha256": "mudqxaICUFD5ng1BwVv6eVfYqERYqtM7eaNwrQYnHBo=" - }, - { - "id": "io.smallrye:smallrye-context-propagation-api:jar:2.1.0", - "artifactId": "smallrye-context-propagation-api", - "groupId": "io.smallrye", - "version": "2.1.0", - "sha256": "F2_yFsVyPQ-aGNswPfrtzzuYi077Cn4OVu2Ld1YfLIk=" - }, - { - "id": "io.smallrye:smallrye-context-propagation-storage:jar:2.1.0", - "artifactId": "smallrye-context-propagation-storage", - "groupId": "io.smallrye", - "version": "2.1.0", - "sha256": "BaFJvAxKV73abzKF5Flz1kxg86MCYdYD0a8hLy_Gd1s=" - }, - { - "id": "io.smallrye.reactive:mutiny-smallrye-context-propagation:jar:2.1.0", - "artifactId": "mutiny-smallrye-context-propagation", - "groupId": "io.smallrye.reactive", - "version": "2.1.0", - "sha256": "NO4pU_LvoHyBulXKRDwiP3DiNYaJi4q_4eV11oXF82A=" - }, - { - "id": "io.quarkus:quarkus-jacoco:jar:3.1.0.Final", + "id": "io.quarkus:quarkus-jacoco:jar:3.2.5.Final", "artifactId": "quarkus-jacoco", "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "Bkp09d9s7IShUzB_MPI6KnUVIcE9c0tVeTS3WyV79qs=" + "version": "3.2.5.Final", + "sha256": "5bmCo1UbMDpPBdegdkp6TI7n_pFiWpSVLIqmGTTCUrw=" }, { "id": "org.jacoco:org.jacoco.core:jar:0.8.10", @@ -1694,48 +1708,6 @@ "version": "9.5", "sha256": "cu7p-6-53o2UY_IN1YSkjO635RUq1MmHv74X3UgRya4=" }, - { - "id": "io.quarkus:quarkus-logging-json:jar:3.1.0.Final", - "artifactId": "quarkus-logging-json", - "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "yQAoXnMsbCUYbE0TugDvnwEENKKXR0j_hIUtwdHJKhg=" - }, - { - "id": "io.quarkus:quarkus-cache:jar:3.1.0.Final", - "artifactId": "quarkus-cache", - "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "TgbXtA45ZNkAXpDvnuaw5rOKQ-4Jlw8TyDzNCZhaIFA=" - }, - { - "id": "io.quarkus:quarkus-caffeine:jar:3.1.0.Final", - "artifactId": "quarkus-caffeine", - "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "G-2b66CHrBghibti14oDF1GzkDLklnmGgUyR-xwhDew=" - }, - { - "id": "com.github.ben-manes.caffeine:caffeine:jar:3.1.5", - "artifactId": "caffeine", - "groupId": "com.github.ben-manes.caffeine", - "version": "3.1.5", - "sha256": "AsNP0rp4sZVu9uXemskVrum_pJhYmWb6hyVfuttlNJk=" - }, - { - "id": "com.google.errorprone:error_prone_annotations:jar:2.19.1", - "artifactId": "error_prone_annotations", - "groupId": "com.google.errorprone", - "version": "2.19.1", - "sha256": "rdOZAphQH9y27Fj_8mYIwdKY-13-L4DXSGe_D1xTlmg=" - }, - { - "id": "io.quarkus:quarkus-cache-runtime-spi:jar:3.1.0.Final", - "artifactId": "quarkus-cache-runtime-spi", - "groupId": "io.quarkus", - "version": "3.1.0.Final", - "sha256": "CQrxChOQ6CJAt79ApWF_xW7EmsTzzxsHycigjRBD7eQ=" - }, { "id": "org.projectlombok:lombok:jar:1.18.28", "artifactId": "lombok", diff --git a/pom.xml b/pom.xml index d60ab221..8b11863b 100644 --- a/pom.xml +++ b/pom.xml @@ -1,23 +1,24 @@ - + 4.0.0 + it.pagopa.swclient.mil auth 1.7.4 Authorization Microservice for Multi-channel Integration Layer of SW Client Project. + Antonio Tarricone antonio.tarricone@pagopa.it PagoPA S.p.A. - - Anis Lucidi - anis.lucidi@pagopa.it - PagoPA S.p.A. - + 17 ${java.version} @@ -30,10 +31,11 @@ UTF-8 quarkus-bom io.quarkus.platform - 3.1.0.Final + 3.2.5.Final true 1.18.28 1.1.1 + 0.8.10 2.0.2 https://sonarcloud.io:443/ pagopa @@ -42,6 +44,7 @@ 300 target/jacoco-report/jacoco.xml + @@ -53,6 +56,7 @@ + io.quarkus @@ -62,10 +66,6 @@ io.quarkus quarkus-resteasy-reactive-jackson - - org.mongodb - bson - io.quarkus quarkus-hibernate-validator @@ -74,18 +74,10 @@ io.quarkus quarkus-arc - - io.quarkus - quarkus-redis-client - io.quarkus quarkus-rest-client-reactive-jackson - - io.quarkus - quarkus-logging-json - io.quarkus quarkus-cache @@ -122,11 +114,18 @@ + io.quarkus quarkus-jacoco @@ -139,18 +138,21 @@ provided + github https://maven.pkg.github.com/pagopa/mil-common + github https://maven.pkg.github.com/pagopa/depcheck + @@ -224,6 +226,50 @@ false + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + jacoco-check + + check + + test + + ${project.build.directory}/jacoco-quarkus.exec + + + CLASS + + + LINE + COVEREDRATIO + 0.90 + + + BRANCH + COVEREDRATIO + 0.90 + + + + + METHOD + + + COMPLEXITY + TOTALCOUNT + 15 + + + + + + + + @@ -241,7 +287,7 @@ - + validate diff --git a/src/main/java/it/pagopa/swclient/mil/auth/ErrorCode.java b/src/main/java/it/pagopa/swclient/mil/auth/AuthErrorCode.java similarity index 90% rename from src/main/java/it/pagopa/swclient/mil/auth/ErrorCode.java rename to src/main/java/it/pagopa/swclient/mil/auth/AuthErrorCode.java index 58229fe8..21d7d863 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/ErrorCode.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/AuthErrorCode.java @@ -1,5 +1,5 @@ /* - * ErrorCode.java + * AuthErrorCode.java * * 15 mar 2023 */ @@ -7,10 +7,10 @@ /** * Error codes returned by this service. - * + * * @author Antonio Tarricone */ -public final class ErrorCode { +public final class AuthErrorCode { public static final String MODULE_ID = "009"; public static final String CLIENT_ID_MUST_NOT_BE_NULL = MODULE_ID + "000001"; @@ -52,10 +52,13 @@ public final class ErrorCode { public static final String ERROR_GENERATING_KEY_PAIR = MODULE_ID + "000025"; public static final String UNEXPECTED_ERROR = MODULE_ID + "000026"; public static final String ERROR_VERIFING_SECRET = MODULE_ID + "000027"; + public static final String ERROR_STORING_KEY_PAIR = MODULE_ID + "000028"; + public static final String AZURE_ACCESS_TOKEN_IS_NULL = MODULE_ID + "000029"; + public static final String ERROR_FROM_AZURE = MODULE_ID + "00002A"; /** - * + * */ - private ErrorCode() { + private AuthErrorCode() { } } \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/CreateKeyRequest.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/CreateKeyRequest.java new file mode 100644 index 00000000..e783b60a --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/CreateKeyRequest.java @@ -0,0 +1,52 @@ +/* + * CreateKeyRequest.java + * + * 23 lug 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.bean; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author Antonio Tarricone + */ +@RegisterForReflection +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +public class CreateKeyRequest { + /* + * + */ + @JsonProperty("kty") + private String kty; + + /* + * + */ + @JsonProperty("key_size") + private Integer keySize; + + /* + * + */ + @JsonProperty("key_ops") + private String[] keyOps; + + /* + * + */ + @JsonProperty("attributes") + private KeyAttributes attributes; +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/CreateKeyResponse.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/CreateKeyResponse.java new file mode 100644 index 00000000..f5aa606a --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/CreateKeyResponse.java @@ -0,0 +1,34 @@ +/* + * CreateKeyResponse.java + * + * 24 lug 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.bean; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author Antonio Tarricone + */ +@RegisterForReflection +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +public class CreateKeyResponse { + /* + * + */ + @JsonProperty("key") + private KeyDetails key; +} diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/GetAccessTokenResponse.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/GetAccessTokenResponse.java new file mode 100644 index 00000000..466983d0 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/GetAccessTokenResponse.java @@ -0,0 +1,52 @@ +/* + * GetAccessTokenResponse.java + * + * 21 lug 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.bean; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author Antonio Tarricone + */ +@RegisterForReflection +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +public class GetAccessTokenResponse { + /* + * + */ + @JsonProperty("token_type") + private String type; + + /* + * + */ + @JsonProperty("expires_in") + private long expiresIn; + + /* + * + */ + @JsonProperty("ext_expires_in") + private long extExpiresIn; + + /* + * + */ + @JsonProperty("access_token") + private String token; +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/GetKeyResponse.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/GetKeyResponse.java new file mode 100644 index 00000000..84cb5397 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/GetKeyResponse.java @@ -0,0 +1,34 @@ +/* + * GetKeyResponse.java + * + * 24 lug 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.bean; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author Antonio Tarricone + */ +@RegisterForReflection +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +public class GetKeyResponse { + /* + * + */ + @JsonProperty("key") + private KeyDetails key; +} diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/GetKeyVersionsResponse.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/GetKeyVersionsResponse.java new file mode 100644 index 00000000..e45de2f8 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/GetKeyVersionsResponse.java @@ -0,0 +1,34 @@ +/* + * GetKeyVersionsResponse.java + * + * 26 lug 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.bean; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author Antonio Tarricone + */ +@RegisterForReflection +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +public class GetKeyVersionsResponse { + /* + * + */ + @JsonProperty("value") + private KeyVersion[] keys; +} diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/GetKeysResponse.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/GetKeysResponse.java new file mode 100644 index 00000000..1a506310 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/GetKeysResponse.java @@ -0,0 +1,34 @@ +/* + * GetKeysResponse.java + * + * 24 lug 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.bean; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author Antonio Tarricone + */ +@RegisterForReflection +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +public class GetKeysResponse { + /* + * + */ + @JsonProperty("value") + private Key[] keys; +} diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/Key.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/Key.java new file mode 100644 index 00000000..47d41297 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/Key.java @@ -0,0 +1,40 @@ +/* + * Key.java + * + * 23 lug 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.bean; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author Antonio Tarricone + */ +@RegisterForReflection +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Key { + /* + * + */ + @JsonProperty("kid") + private String kid; + + /* + * + */ + @JsonProperty("attributes") + private KeyAttributes attributes; +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyAttributes.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyAttributes.java new file mode 100644 index 00000000..940d4916 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyAttributes.java @@ -0,0 +1,76 @@ +/* + * KeyAttributes.java + * + * 23 lug 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.bean; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author Antonio Tarricone + */ +@RegisterForReflection +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +public class KeyAttributes { + /* + * Creation epoch (seconds from 1/1/1970 00:00:00.000 GMT) + */ + @JsonProperty("created") + private Long created; + + /* + * Expiration epoch (seconds from 1/1/1970 00:00:00.000 GMT) + */ + @JsonProperty("exp") + private Long exp; + + /* + * 'Not before' epoch (seconds from 1/1/1970 00:00:00.000 GMT) + */ + @JsonProperty("nbf") + private Long nbf; + + /* + * + */ + @JsonProperty("updated") + private Long updated; + + /* + * + */ + @JsonProperty("enabled") + private Boolean enabled; + + /* + * + */ + @JsonProperty("recoveryLevel") + private String recoveryLevel; + + /* + * + */ + @JsonProperty("recoverableDays") + private Integer recoverableDays; + + /* + * + */ + @JsonProperty("exportable") + private Boolean exportable; +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyDetails.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyDetails.java new file mode 100644 index 00000000..cfbbf8f3 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyDetails.java @@ -0,0 +1,66 @@ +/* + * Key.java + * + * 23 lug 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.bean; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author Antonio Tarricone + */ +@RegisterForReflection +@NoArgsConstructor +@Getter +@Setter +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +public class KeyDetails extends Key { + /* + * + */ + @JsonProperty("kty") + private String kty; + + /* + * + */ + @JsonProperty("key_ops") + private String[] keyOps; + + /* + * Modulus (Base64 URL-safe). + */ + @JsonProperty("n") + private String modulus; + + /* + * Public exponent (Base64 URL-safe). + */ + @JsonProperty("e") + private String exponent; + + /** + * @param kid + * @param kty + * @param keyOps + * @param modulus + * @param exponent + * @param attributes + */ + public KeyDetails(String kid, String kty, String[] keyOps, String modulus, String exponent, KeyAttributes attributes) { + super(kid, attributes); + this.kty = kty; + this.keyOps = keyOps; + this.modulus = modulus; + this.exponent = exponent; + } +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyNameAndVersion.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyNameAndVersion.java new file mode 100644 index 00000000..ae4bda8c --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyNameAndVersion.java @@ -0,0 +1,39 @@ +/* + * KeyNameAndVersion.java + * + * 27 lug 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.bean; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author Antonio Tarricone + */ +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +public class KeyNameAndVersion { + /* + * + */ + private String name; + + /* + * + */ + private String version; + + /** + * @return + */ + public boolean isValid() { + return name != null && version != null; + } +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyVersion.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyVersion.java new file mode 100644 index 00000000..8b964913 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyVersion.java @@ -0,0 +1,24 @@ +/* + * KeyVersion.java + * + * 1 ago 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.bean; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.NoArgsConstructor; + +/** + * @author Antonio Tarricone + */ +@RegisterForReflection +@NoArgsConstructor +public class KeyVersion extends Key { + /** + * @param kid + * @param attributes + */ + public KeyVersion(String kid, KeyAttributes attributes) { + super(kid, attributes); + } +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/SignRequest.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/SignRequest.java new file mode 100644 index 00000000..90492e11 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/SignRequest.java @@ -0,0 +1,40 @@ +/* + * SignRequest.java + * + * 25 lug 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.bean; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author Antonio Tarricone + */ +@RegisterForReflection +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +public class SignRequest { + /* + * + */ + @JsonProperty("alg") + private String alg; + + /* + * Data to sign (Base64 URL-safe). + */ + @JsonProperty("value") + private String data; +} diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/SignResponse.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/SignResponse.java new file mode 100644 index 00000000..f2a7bc70 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/SignResponse.java @@ -0,0 +1,40 @@ +/* + * SignRequest.java + * + * 25 lug 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.bean; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author Antonio Tarricone + */ +@RegisterForReflection +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +public class SignResponse { + /* + * + */ + @JsonProperty("kid") + private String kid; + + /* + * Signature (Base64 URL-safe). + */ + @JsonProperty("value") + private String signature; +} diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/VerifySignatureRequest.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/VerifySignatureRequest.java new file mode 100644 index 00000000..8b6eeb9c --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/VerifySignatureRequest.java @@ -0,0 +1,46 @@ +/* + * VerifySignatureRequest.java + * + * 25 lug 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.bean; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author Antonio Tarricone + */ +@RegisterForReflection +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +public class VerifySignatureRequest { + /* + * + */ + @JsonProperty("alg") + private String alg; + + /* + * + */ + @JsonProperty("digest") + private String data; + + /* + * + */ + @JsonProperty("value") + private String signature; +} diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/VerifySignatureResponse.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/VerifySignatureResponse.java new file mode 100644 index 00000000..87229d07 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/VerifySignatureResponse.java @@ -0,0 +1,34 @@ +/* + * VerifySignatureResponse.java + * + * 25 lug 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.bean; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * @author Antonio Tarricone + */ +@RegisterForReflection +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +public class VerifySignatureResponse { + /* + * + */ + @JsonProperty("value") + private Boolean ok; +} diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/client/AzureAuthClient.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/client/AzureAuthClient.java new file mode 100644 index 00000000..9803b31b --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/client/AzureAuthClient.java @@ -0,0 +1,43 @@ +/* + * AzureAuthClient.java + * + * 23 lug 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.client; + +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +import io.smallrye.mutiny.Uni; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetAccessTokenResponse; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +/** + * @author Antonio Tarricone + */ +@RegisterRestClient(configKey = "azure-auth-api") +public interface AzureAuthClient { + /** + * @param tenantId + * @param grantType + * @param clientId + * @param clientSecret + * @param scope + * @return + */ + @Path("/{tenantId}/oauth2/v2.0/token") + @POST + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + @Produces(MediaType.APPLICATION_JSON) + Uni getAccessToken( + @PathParam("tenantId") String tenantId, + @FormParam("grant_type") String grantType, + @FormParam("client_id") String clientId, + @FormParam("client_secret") String clientSecret, + @FormParam("scope") String scope); +} diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/client/AzureKeyVaultClient.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/client/AzureKeyVaultClient.java new file mode 100644 index 00000000..7fae5741 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/client/AzureKeyVaultClient.java @@ -0,0 +1,125 @@ +/* + * AzureKeyVaultClient.java + * + * 23 lug 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.client; + +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +import io.quarkus.rest.client.reactive.ClientQueryParam; +import io.smallrye.mutiny.Uni; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.CreateKeyRequest; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.CreateKeyResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeyResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeyVersionsResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeysResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.SignRequest; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.SignResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.VerifySignatureRequest; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.VerifySignatureResponse; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +/** + * @author Antonio Tarricone + */ +@RegisterRestClient(configKey = "azure-key-vault-api") +public interface AzureKeyVaultClient { + /** + * @param authorization + * @param keyName + * @param createKeyRequest + * @return + */ + @Path("/keys/{keyName}/create") + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ClientQueryParam(name = "api-version", value = "${azure-key-vault-api.version}") + Uni createKey( + @HeaderParam("Authorization") String authorization, + @PathParam("keyName") String keyName, + CreateKeyRequest createKeyRequest); + + /** + * @param authorization + * @return + */ + @Path("/keys") + @GET + @Produces(MediaType.APPLICATION_JSON) + @ClientQueryParam(name = "api-version", value = "${azure-key-vault-api.version}") + Uni getKeys( + @HeaderParam("Authorization") String authorization); + + /** + * @param authorization + * @param keyName + * @param keyVersion + * @return + */ + @Path("/keys/{keyName}/{keyVersion}") + @GET + @Produces(MediaType.APPLICATION_JSON) + @ClientQueryParam(name = "api-version", value = "${azure-key-vault-api.version}") + Uni getKey( + @HeaderParam("Authorization") String authorization, + @PathParam("keyName") String keyName, + @PathParam("keyVersion") String keyVersion); + + /** + * @param authorization + * @param keyName + * @return + */ + @Path("/keys/{keyName}/versions") + @GET + @Produces(MediaType.APPLICATION_JSON) + @ClientQueryParam(name = "api-version", value = "${azure-key-vault-api.version}") + Uni getKeyVersions( + @HeaderParam("Authorization") String authorization, + @PathParam("keyName") String keyName); + + /** + * @param authorization + * @param keyName + * @param keyVersion + * @param signRequest + * @return + */ + @Path("/keys/{keyName}/{keyVersion}/sign") + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ClientQueryParam(name = "api-version", value = "${azure-key-vault-api.version}") + Uni sign( + @HeaderParam("Authorization") String authorization, + @PathParam("keyName") String keyName, + @PathParam("keyVersion") String keyVersion, + SignRequest signRequest); + + /** + * @param authorization + * @param keyName + * @param keyVersion + * @param verifySignatureRequest + * @return + */ + @Path("/keys/{keyName}/{keyVersion}/verify") + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @ClientQueryParam(name = "api-version", value = "${azure-key-vault-api.version}") + Uni verifySignature( + @HeaderParam("Authorization") String authorization, + @PathParam("keyName") String keyName, + @PathParam("keyVersion") String keyVersion, + VerifySignatureRequest verifySignatureRequest); +} diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureAuthService.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureAuthService.java new file mode 100644 index 00000000..a4fbaa1f --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureAuthService.java @@ -0,0 +1,58 @@ +/* + * AzureAuthService.java + * + * 1 ago 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.service; + +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.eclipse.microprofile.rest.client.inject.RestClient; + +import io.quarkus.logging.Log; +import io.smallrye.mutiny.Uni; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetAccessTokenResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.client.AzureAuthClient; +import jakarta.enterprise.context.ApplicationScoped; + +/** + * @author Antonio Tarricone + */ +@ApplicationScoped +public class AzureAuthService { + /* + * Grant types. + */ + private static final String CLIENT_CREDENTIALS = "client_credentials"; + /* + * Scope for authentication. + */ + private static final String VAULT = "https://vault.azure.net/.default"; + /* + * + */ + @RestClient + AzureAuthClient client; + /* + * + */ + @ConfigProperty(name = "azure-auth-api.tenant-id") + String tenantId; + /* + * + */ + @ConfigProperty(name = "azure-auth-api.client-id") + String clientId; + /* + * + */ + @ConfigProperty(name = "azure-auth-api.client-secret") + String clientSecret; + + /** + * @return + */ + public Uni getAccessToken() { + Log.debug("Authenticating to Azure AD."); + return client.getAccessToken(tenantId, CLIENT_CREDENTIALS, clientId, clientSecret, VAULT); + } +} diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureKeyFinder.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureKeyFinder.java new file mode 100644 index 00000000..b2733928 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureKeyFinder.java @@ -0,0 +1,445 @@ +/* + * AzureKeyFinder.java + * + * 26 lug 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.service; + +import java.time.Instant; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import io.quarkus.logging.Log; +import io.smallrye.mutiny.Context; +import io.smallrye.mutiny.ItemWithContext; +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; +import it.pagopa.swclient.mil.auth.AuthErrorCode; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.CreateKeyRequest; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetAccessTokenResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeyResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeyVersionsResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeysResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.Key; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.KeyAttributes; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.KeyDetails; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.KeyNameAndVersion; +import it.pagopa.swclient.mil.auth.azurekeyvault.util.KidUtil; +import it.pagopa.swclient.mil.auth.bean.KeyType; +import it.pagopa.swclient.mil.auth.bean.KeyUse; +import it.pagopa.swclient.mil.auth.bean.PublicKey; +import it.pagopa.swclient.mil.auth.bean.PublicKeys; +import it.pagopa.swclient.mil.auth.service.KeyFinder; +import it.pagopa.swclient.mil.auth.util.AuthError; +import it.pagopa.swclient.mil.auth.util.UniGenerator; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +/** + * @author Antonio Tarricone + */ +@ApplicationScoped +public class AzureKeyFinder implements KeyFinder { + /* + * Context keys. + */ + static final String TOKEN = "token"; + /* + * Key types. + */ + private static final String RSA = "RSA"; + /* + * Key operations. + */ + private static final String SIGN = "sign"; + private static final String VERIFY = "verify"; + private static final String[] OPS = new String[] { + SIGN, VERIFY + }; + /* + * Recovery levels. + */ + private static final String PURGEABLE = "Purgeable"; + private static final String CURRENT_KEY_NAME_AND_VERSION = "name_and_version"; + /* + * + */ + @Inject + AzureAuthService authService; + /* + * + */ + @Inject + AzureKeyVaultService keyVaultService; + /* + * + */ + @Inject + KidUtil kidUtil; + /* + * Cryptoperiod of RSA keys in seconds. + */ + @ConfigProperty(name = "cryptoperiod", defaultValue = "86400") + long cryptoperiod; + /* + * Key size (modulus) of RSA keys in bits. + */ + @ConfigProperty(name = "keysize", defaultValue = "4096") + int keysize; + + /** + * @param key + * @return + */ + private boolean isKeyEnabled(Key key) { + if (key.getAttributes().getEnabled() != null && key.getAttributes().getEnabled()) { + Log.debugf("The key [%s] is enabled.", key.getKid()); + return true; + } else { + Log.warnf("The key [%s] is not enabled.", key.getKid()); + return false; + } + } + + /** + * @param key + * @return + */ + private boolean isKeyCreationTimestampCoherent(Key key) { + long now = Instant.now().getEpochSecond(); + if (key.getAttributes().getCreated() != null && key.getAttributes().getCreated() <= now) { + Log.debugf("The creation timestamp of [%s] is valid.", key.getKid()); + return true; + } else { + Log.warnf("The creation timestamp of [%s] is not valid. Found [%s], expected a value less than [%d].", key.getKid(), key.getAttributes().getCreated(), now); + return false; + } + } + + /** + * @param key + * @return + */ + private boolean isKeyNotYetExpired(Key key) { + long now = Instant.now().getEpochSecond(); + if (key.getAttributes().getExp() != null && key.getAttributes().getExp() > now) { + Log.debugf("The key [%s] is not expired.", key.getKid()); + return true; + } else { + Log.warnf("The key [%s] is expired. Found [%s], expected a value greater than [%d].", key.getKid(), key.getAttributes().getExp(), now); + return false; + } + } + + /** + * @param key + * @return + */ + private boolean isKeyNotBeforeMet(Key key) { + long now = Instant.now().getEpochSecond(); + if (key.getAttributes().getNbf() != null && key.getAttributes().getNbf() <= now) { + Log.debugf("The 'not before' timestamp of [%s] is valid.", key.getKid()); + return true; + } else { + Log.warnf("The 'not before' timestamp of [%s] is not valid. Found [%s], expected a value less than [%d].", key.getKid(), key.getAttributes().getNbf(), now); + return false; + } + } + + /** + * @param key + * @return + */ + private boolean isKeyValid(Key key) { + if (key.getAttributes() == null) { + Log.errorf("The key [%s] has null attributes.", key.getKid()); + return false; + } else { + return isKeyEnabled(key) + && isKeyCreationTimestampCoherent(key) + && isKeyNotYetExpired(key) + && isKeyNotBeforeMet(key); + } + } + + /** + * @param key + * @return + */ + private boolean isKeyTypeRsa(KeyDetails key) { + if (Objects.equals(key.getKty(), RSA)) { + Log.debugf("The key type of [%s] is RSA.", key.getKid()); + return true; + } else { + Log.warnf("The key type of [%s] is not RSA. Found [%s].", key.getKid(), key.getKty()); + return false; + } + } + + /** + * @param key + * @return + */ + private boolean isKeySuitableForSignature(KeyDetails key) { + String[] keyOps = key.getKeyOps(); + if (keyOps != null) { + List keyOpList = Arrays.asList(keyOps); + if (keyOpList.contains(SIGN) && keyOpList.contains(VERIFY)) { + Log.debugf("The key [%s] is suitable for signature.", key.getKid()); + return true; + } else { + Log.warnf("The key [%s] is not suitable for signature. Found [%s].", key.getKid(), keyOpList); + return false; + } + } else { + Log.errorf("The key [%s] has null ops.", key.getKid()); + return false; + } + } + + /** + * @param key + * @return + */ + private boolean isKeyValid(KeyDetails key) { + return isKeyValid((Key) key) && isKeyTypeRsa(key) && isKeySuitableForSignature(key); + } + + /** + * @param getKeyResponse + * @return + */ + private boolean isKeyValid(GetKeyResponse getKeyResponse) { + if (getKeyResponse.getKey() != null) { + return isKeyValid(getKeyResponse.getKey()); + } else { + Log.warn("Received null key."); + return false; + } + } + + /** + * @return + */ + private Uni> findPublicKeysWithContext() { + Log.debug("Search for the keys."); + Context context = Context.of(); + return authService.getAccessToken() + .map(x -> { + String t = x.getToken(); + if (t != null) { + Log.debug("Successfully authenticated."); + return t; + } else { + String message = String.format("[%s] Azure access token not valid.", AuthErrorCode.AZURE_ACCESS_TOKEN_IS_NULL); + Log.error(message); + throw new AuthError(AuthErrorCode.AZURE_ACCESS_TOKEN_IS_NULL, message); + } + }) // Getting the access token. + .invoke(token -> context.put(TOKEN, token)) // Storing the access token in the context. + .chain(token -> keyVaultService.getKeys(token)) // Retrieving the list of keys. + .invoke(x -> Log.debugf("Keys retrieved: [%s]", x)) + .map(GetKeysResponse::getKeys) // Getting the list of keys from the response. + .onItem().transformToMulti(keys -> Multi.createFrom().items(Arrays.stream(keys).filter(Objects::nonNull))) // Transforming the list of keys in a stream of events (one event for each key). + .invoke(x -> Log.debugf("Processing of the key: [%s]", x)) + .map(key -> kidUtil.getNameFromAzureKid(key.getKid())) + .filter(keyNameAndVersion -> { + if (keyNameAndVersion.getName() != null) { + Log.debugf("Key name: [%s]", keyNameAndVersion.getName()); + return true; + } else { + Log.warn("Key name is null."); + return false; + } + }) // Filtering the key with invalid kid. + .map(KeyNameAndVersion::getName) + .onItem().transformToUniAndConcatenate(keyName -> keyVaultService + .getKeyVersions( + context.get(TOKEN), + keyName)) // Retrieving the versions of the key. + .invoke(x -> Log.debugf("Versions retrieved: [%s]", x)) + .map(GetKeyVersionsResponse::getKeys) // Getting the list of versions from the response. + .onItem().transformToMultiAndConcatenate(keys -> Multi.createFrom().items(Arrays.stream(keys).filter(Objects::nonNull))) // Transforming the list of versions in a stream of events (one event for each version). + .invoke(x -> Log.debugf("Processing of the version: [%s]", x)) + .filter(version -> { + KeyNameAndVersion keyNameAndVersion = kidUtil.getNameAndVersionFromAzureKid(version.getKid()); + if (keyNameAndVersion.isValid()) { + Log.debugf("Key name and version: [%s]", keyNameAndVersion); + if (isKeyValid(version)) { + context.put(CURRENT_KEY_NAME_AND_VERSION, keyNameAndVersion); + return true; + } else { + return false; + } + } else { + Log.warnf("Invalid key name and version: [%s]", version.getKid()); + return false; + } + }) // Filtering not valid versions. + .onItem().transformToUniAndConcatenate(version -> { + KeyNameAndVersion keyNameAndVersion = (KeyNameAndVersion) context.get(CURRENT_KEY_NAME_AND_VERSION); + return keyVaultService + .getKey( + context.get(TOKEN), + keyNameAndVersion.getName(), + keyNameAndVersion.getVersion()); + }) // Retrieving version details. + .invoke(x -> Log.debugf("Details retrieved: [%s]", x)) + .filter(this::isKeyValid) // Filtering not valid details. + .map(GetKeyResponse::getKey) // Getting the details from the response. + .map(key -> new PublicKey( + key.getExponent(), + KeyUse.sig, + kidUtil.getMyKidFromNameAndVersion(context.get(CURRENT_KEY_NAME_AND_VERSION)), + key.getModulus(), + KeyType.RSA, + key.getAttributes().getExp(), + key.getAttributes().getCreated())) // Generating internal public key object. + .invoke(x -> Log.debugf("Internal public key object: [%s]", x)) + .collect() // Collecting all internal public key objects. + .asList() // Converting the events in an event that is the list of the collected internal public key objects. + .invoke(x -> Log.debugf("Found [%d] valid key/s.", x.size())) + .map(PublicKeys::new) + .invoke(x -> Log.debug(x)) + .map(p -> new ItemWithContext<>(context, p)) + .onFailure(t -> !(t instanceof AuthError)) + .transform(t -> { + String message = String.format("[%s] Error from Azure.", AuthErrorCode.ERROR_FROM_AZURE); + Log.errorf(t, message); + throw new AuthError(AuthErrorCode.ERROR_FROM_AZURE, message); + }); + } + + /** + * Finds all valid public keys. + * + * @return + */ + @Override + public Uni findPublicKeys() { + return findPublicKeysWithContext().map(ItemWithContext::get); + } + + /** + * @param accessToken + * @return + */ + private Uni createKey(String accessToken) { + String keyName = UUID.randomUUID().toString().replace("-", ""); + long now = Instant.now().getEpochSecond(); + KeyAttributes attributes = new KeyAttributes(now, now + cryptoperiod, now, now, true, PURGEABLE, null, false); + CreateKeyRequest createKeyRequest = new CreateKeyRequest(RSA, keysize, OPS, attributes); + return keyVaultService.createKey(accessToken, keyName, createKeyRequest) + .map(resp -> { + if (isKeyValid(resp.getKey())) { + KeyDetails key = resp.getKey(); + KeyNameAndVersion keyNameAndVersion = kidUtil.getNameAndVersionFromAzureKid(key.getKid()); + if (keyNameAndVersion.isValid()) { + return new PublicKey( + key.getExponent(), + KeyUse.sig, + kidUtil.getMyKidFromNameAndVersion(keyNameAndVersion), + key.getModulus(), + KeyType.RSA, + key.getAttributes().getExp(), + key.getAttributes().getCreated()); + } else { + String message = String.format("[%s] Error generating the key pair: kid doesn't contain name and version.", AuthErrorCode.ERROR_GENERATING_KEY_PAIR); + Log.fatal(message); + throw new AuthError(AuthErrorCode.ERROR_GENERATING_KEY_PAIR, message); + } + } else { + String message = String.format("[%s] Error generating the key pair: invalid key pair has been generated.", AuthErrorCode.ERROR_GENERATING_KEY_PAIR); + Log.fatal(message); + throw new AuthError(AuthErrorCode.ERROR_GENERATING_KEY_PAIR, message); + } + }) + .onFailure(t -> !(t instanceof AuthError)) + .transform(t -> { + String message = String.format("[%s] Error generating key pair.", AuthErrorCode.ERROR_GENERATING_KEY_PAIR); + Log.errorf(t, message); + throw new AuthError(AuthErrorCode.ERROR_GENERATING_KEY_PAIR, message); + }); + } + + /** + * Finds the valid public key with the greatest expiration. If there are no valid key a new one is + * generated. + * + * @return + */ + Uni> findValidPublicKeyWithGreatestExpiration() { + Log.debug("Search for a valid key with greatest expiration."); + return findPublicKeysWithContext() + .chain(item -> { + List keys = item.get().getKeys(); + if (keys.isEmpty()) { + /* + * There are no valid key: generating one. + */ + Log.debug("There are no valid key: generating one."); + return createKey(item.context().get(TOKEN)) + .map(p -> new ItemWithContext<>(item.context(), p)); + } else { + /* + * If there are valid keys, search for the key with the greatest expiration. + */ + Log.debug("Search for the key with the greatest expiration."); + keys.sort((x, y) -> { + if (x.getExp() < y.getExp()) { + return 1; + } else if (x.getExp() == y.getExp()) { + return 0; + } else { + return -1; + } + }); + + return UniGenerator.item(new ItemWithContext<>(item.context(), keys.get(0))); + } + }); + } + + /** + * Finds the public key having the given kid. + * + * @param kid + * @return + */ + @Override + public Uni> findPublicKey(String kid) { + Log.debugf("Search for the public key [%s].", kid); + + KeyNameAndVersion keyNameAndVersion = kidUtil.getNameAndVersionFromMyKid(kid); + if (keyNameAndVersion.isValid()) { + return authService.getAccessToken() + .map(GetAccessTokenResponse::getToken) // Getting the access token. + .chain(token -> keyVaultService.getKey(token, keyNameAndVersion.getName(), keyNameAndVersion.getVersion())) + .map(GetKeyResponse::getKey) + .map(k -> { + Log.debugf("Key [%s] found.", kid); + if (isKeyValid(k)) { + return Optional.of(new PublicKey( + k.getExponent(), + KeyUse.sig, + kid, + k.getModulus(), + KeyType.RSA, + k.getAttributes().getExp(), + k.getAttributes().getCreated())); + } else { + Log.warnf("Key [%s] is not valid.", kid); + return Optional.empty(); + } + }); + } else { + Log.warnf("[%s] doesn't contain name and version.", kid); + return UniGenerator.item(Optional.empty()); + } + } +} diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureKeyVaultService.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureKeyVaultService.java new file mode 100644 index 00000000..54444459 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureKeyVaultService.java @@ -0,0 +1,103 @@ +/* + * AzureKeyVaultService.java + * + * 27 lug 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.service; + +import org.eclipse.microprofile.rest.client.inject.RestClient; + +import io.quarkus.logging.Log; +import io.smallrye.mutiny.Uni; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.CreateKeyRequest; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.CreateKeyResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeyResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeyVersionsResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeysResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.SignRequest; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.SignResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.VerifySignatureRequest; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.VerifySignatureResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.client.AzureKeyVaultClient; +import jakarta.enterprise.context.ApplicationScoped; + +/** + * @author Antonio Tarricone + */ +@ApplicationScoped +public class AzureKeyVaultService { + /* + * + */ + private static final String BEARER = "Bearer "; + /* + * + */ + @RestClient + AzureKeyVaultClient client; + + /** + * @param accessToken + * @param keyName + * @param createKeyRequest + * @return + */ + public Uni createKey(String accessToken, String keyName, CreateKeyRequest createKeyRequest) { + Log.debugf("Creating a new key [%s]: [%s]", keyName, createKeyRequest); + return client.createKey(BEARER + accessToken, keyName, createKeyRequest); + } + + /** + * @param accessToken + * @return + */ + public Uni getKeys(String accessToken) { + Log.debug("Retrieving the list of keys."); + return client.getKeys(BEARER + accessToken); + } + + /** + * @param accessToken + * @param keyName + * @param keyVersion + * @return + */ + public Uni getKey(String accessToken, String keyName, String keyVersion) { + Log.debugf("Retrieving details of version [%s] of the key [%s].", keyVersion, keyName); + return client.getKey(BEARER + accessToken, keyName, keyVersion); + } + + /** + * @param accessToken + * @param keyName + * @return + */ + public Uni getKeyVersions(String accessToken, String keyName) { + Log.debugf("Retrieving versions of the key [%s].", keyName); + return client.getKeyVersions(BEARER + accessToken, keyName); + } + + /** + * @param accessToken + * @param keyName + * @param keyVersion + * @param signRequest + * @return + */ + public Uni sign(String accessToken, String keyName, String keyVersion, SignRequest signRequest) { + Log.debugf("Signing data with key [%s/%s]: [%s]", keyName, keyVersion, signRequest); + return client.sign(BEARER + accessToken, keyName, keyVersion, signRequest); + } + + /** + * @param accessToken + * @param keyName + * @param keyVersion + * @param verifySignatureRequest + * @return + */ + public Uni verifySignature(String accessToken, String keyName, String keyVersion, VerifySignatureRequest verifySignatureRequest) { + Log.debugf("Verifing signature with key [%s/%s]: [%s]", keyName, keyVersion, verifySignatureRequest); + return client.verifySignature(BEARER + accessToken, keyName, keyVersion, verifySignatureRequest); + } +} diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureTokenSigner.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureTokenSigner.java new file mode 100644 index 00000000..5c6e0af9 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureTokenSigner.java @@ -0,0 +1,178 @@ +/* + * AzureTokenSigner.java + * + * 1 ago 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.service; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.ParseException; +import java.util.Base64; +import java.util.Objects; + +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.util.Base64URL; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; + +import io.quarkus.logging.Log; +import io.smallrye.mutiny.Uni; +import it.pagopa.swclient.mil.auth.AuthErrorCode; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.SignRequest; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.VerifySignatureRequest; +import it.pagopa.swclient.mil.auth.azurekeyvault.util.SignedJWTFactory; +import it.pagopa.swclient.mil.auth.service.TokenSigner; +import it.pagopa.swclient.mil.auth.util.AuthError; +import it.pagopa.swclient.mil.auth.util.AuthException; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +/** + * + */ +@ApplicationScoped +public class AzureTokenSigner implements TokenSigner { + /* + * + */ + private static final byte[] ID = new byte[] { + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, (byte) 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 + }; + /* + * + */ + @Inject + AzureKeyFinder keyFinder; + /* + * + */ + @Inject + AzureKeyVaultService keyVaultService; + /* + * + */ + @Inject + AzureAuthService authService; + + /** + * @param header + * @param payload + * @return + * @throws NoSuchAlgorithmException + */ + private String getDerDigestInfo(JWSHeader header, JWTClaimsSet payload) throws NoSuchAlgorithmException { + String headerBase64 = Base64.getUrlEncoder().encodeToString(header.toString().getBytes(StandardCharsets.UTF_8)); + String payloadBase64 = Base64.getUrlEncoder().encodeToString(payload.toString().getBytes(StandardCharsets.UTF_8)); + + String stringToSign = headerBase64 + "." + payloadBase64; + byte[] bytesToSign = stringToSign.getBytes(StandardCharsets.UTF_8); + + MessageDigest digest = MessageDigest.getInstance("SHA256"); + digest.update(bytesToSign); + byte[] hash = digest.digest(); + + byte[] derDigestInfo = new byte[ID.length + hash.length]; + System.arraycopy(ID, 0, derDigestInfo, 0, ID.length); + System.arraycopy(hash, 0, derDigestInfo, ID.length, hash.length); + + return Base64.getUrlEncoder().encodeToString(derDigestInfo); + } + + /** + * Signs the given token by means of the valid private key with the greatest expiration. + * + * @param payload + * @return + */ + @Override + public Uni sign(JWTClaimsSet payload) { + Log.debug("Token signing."); + return keyFinder.findValidPublicKeyWithGreatestExpiration() + .chain(item -> { + String kid = item.get().getKid(); + String[] components = kid.split("/"); + String keyName = components[components.length - 2]; + String keyVersion = components[components.length - 1]; + + JWSHeader header = new JWSHeader(JWSAlgorithm.RS256, null, null, null, null, null, null, null, null, null, kid, true, null, null); + + try { + String derDigestInfoBase64 = getDerDigestInfo(header, payload); + + SignRequest req = new SignRequest(JWSAlgorithm.RS256.getName(), derDigestInfoBase64); + + return keyVaultService.sign(item.context().get(AzureKeyFinder.TOKEN), keyName, keyVersion, req) + .map(resp -> { + try { + return SignedJWTFactory.createInstance(header.toBase64URL(), payload.toPayload().toBase64URL(), Base64URL.from(resp.getSignature())); + } catch (ParseException e) { + String message = String.format("[%s] Error generating token.", AuthErrorCode.ERROR_GENERATING_TOKEN); + Log.errorf(e, message); + throw new AuthError(AuthErrorCode.ERROR_GENERATING_TOKEN, message); + } + }); + } catch (NoSuchAlgorithmException e) { + String message = String.format("[%s] Error generating token.", AuthErrorCode.ERROR_GENERATING_TOKEN); + Log.errorf(e, message); + throw new AuthError(AuthErrorCode.ERROR_GENERATING_TOKEN, message); + } + }); + } + + /** + * This class verifies the token signature. + *

+ * If the verification succeeds, the method returns void, otherwise it returns a failure with + * specific error code. + * + * @param token + * @return + */ + @Override + public Uni verify(SignedJWT token) { + Log.debug("Signature verification."); + + String kid = token.getHeader().getKeyID(); + String[] components = kid.split("/"); + String keyName = components[components.length - 2]; + String keyVersion = components[components.length - 1]; + + return authService.getAccessToken() + .invoke(x -> Log.debug(x)) + .map(x -> { + String t = x.getToken(); + if (t != null) { + return t; + } else { + String message = String.format("[%s] Azure access token not valid.", AuthErrorCode.AZURE_ACCESS_TOKEN_IS_NULL); + Log.error(message); + throw new AuthError(AuthErrorCode.AZURE_ACCESS_TOKEN_IS_NULL, message); + } + }) // Getting the access token. + .chain(azureToken -> { + try { + String derDigestInfoBase64 = getDerDigestInfo(token.getHeader(), token.getJWTClaimsSet()); + String signatureBase64 = Base64.getUrlEncoder().encodeToString(token.getSignature().decode()); + VerifySignatureRequest req = new VerifySignatureRequest(JWSAlgorithm.RS256.getName(), derDigestInfoBase64, signatureBase64); + return keyVaultService.verifySignature(azureToken, keyName, keyVersion, req) + .map(resp -> { + if (Objects.equals(resp.getOk(), Boolean.TRUE)) { + Log.debug("Signature has been successfully verified."); + return null; + } else { + String message = String.format("[%s] Wrong signature.", AuthErrorCode.WRONG_SIGNATURE); + Log.warn(message); + throw new AuthException(AuthErrorCode.WRONG_SIGNATURE, message); + } + }); + } catch (NoSuchAlgorithmException | ParseException e) { + String message = String.format("[%s] Error verifing signature.", AuthErrorCode.ERROR_VERIFING_SIGNATURE); + Log.errorf(e, message); + throw new AuthError(AuthErrorCode.ERROR_VERIFING_SIGNATURE, message); + } + }); + } +} diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/util/KidUtil.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/util/KidUtil.java new file mode 100644 index 00000000..83c6f493 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/util/KidUtil.java @@ -0,0 +1,106 @@ +/* + * KidUtil.java + * + * 1 ago 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.util; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.KeyNameAndVersion; +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; + +/** + * + */ +@ApplicationScoped +public class KidUtil { + /* + * + */ + @ConfigProperty(name = "quarkus.rest-client.azure-key-vault-api.url") + String vaultBaseUrl; + + /* + * + */ + private Pattern patternForAzureKidWithNameAndVersion; + + /* + * + */ + private Pattern patternForAzureKidWithName; + + /* + * + */ + private Pattern patternForMyKid; + + /** + * + */ + @PostConstruct + void init() { + String temp = (vaultBaseUrl + "/keys/").replace("//keys", "/keys"); + patternForAzureKidWithNameAndVersion = Pattern.compile("^" + Pattern.quote(temp) + "(?\\w+)\\/(?\\w+)$"); + patternForAzureKidWithName = Pattern.compile("^" + Pattern.quote(temp) + "(?\\w+)$"); + patternForMyKid = Pattern.compile("^(?\\w+)\\/(?\\w+)$"); + } + + /** + * @param kid + * @return + */ + public KeyNameAndVersion getNameAndVersionFromAzureKid(String kid) { + KeyNameAndVersion nameAndVersion = new KeyNameAndVersion(); + if (kid != null) { + Matcher m = patternForAzureKidWithNameAndVersion.matcher(kid); + if (m.find()) { + nameAndVersion.setName(m.group("name")); + nameAndVersion.setVersion(m.group("version")); + } + } + return nameAndVersion; + } + + /** + * @param kid + * @return + */ + public KeyNameAndVersion getNameFromAzureKid(String kid) { + KeyNameAndVersion nameAndVersion = new KeyNameAndVersion(); + if (kid != null) { + Matcher m = patternForAzureKidWithName.matcher(kid); + if (m.find()) { + nameAndVersion.setName(m.group("name")); + } + } + return nameAndVersion; + } + + /** + * @param kid + * @return + */ + public KeyNameAndVersion getNameAndVersionFromMyKid(String kid) { + KeyNameAndVersion nameAndVersion = new KeyNameAndVersion(); + Matcher m = patternForMyKid.matcher(kid); + if (m.find()) { + nameAndVersion.setName(m.group("name")); + nameAndVersion.setVersion(m.group("version")); + } + return nameAndVersion; + } + + /** + * @param nameAndVersion + * @return + */ + public String getMyKidFromNameAndVersion(KeyNameAndVersion nameAndVersion) { + return nameAndVersion.getName() + "/" + nameAndVersion.getVersion(); + } +} diff --git a/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/util/SignedJWTFactory.java b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/util/SignedJWTFactory.java new file mode 100644 index 00000000..5137c401 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/azurekeyvault/util/SignedJWTFactory.java @@ -0,0 +1,36 @@ +/* + * SignedJWTFactory.java + * + * 4 ago 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.util; + +import java.text.ParseException; + +import com.nimbusds.jose.util.Base64URL; +import com.nimbusds.jwt.SignedJWT; + +/** + * To make easier the mocking of the constructor of SignedJWT(Base64URL firstPart, Base64URL + * secondPart, Base64URL thirdPart) for unit testing. + * + * @author Antonio Tarricone + */ +public class SignedJWTFactory { + /** + * + */ + private SignedJWTFactory() { + } + + /** + * @param header + * @param payload + * @param signature + * @return + * @throws ParseException + */ + public static SignedJWT createInstance(Base64URL header, Base64URL payload, Base64URL signature) throws ParseException { + return new SignedJWT(header, payload, signature); + } +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/bean/ClaimName.java b/src/main/java/it/pagopa/swclient/mil/auth/bean/ClaimName.java new file mode 100644 index 00000000..b18789a4 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/bean/ClaimName.java @@ -0,0 +1,22 @@ +/* + * ClaimName.java + * + * 28 ago 2023 + */ +package it.pagopa.swclient.mil.auth.bean; + +/** + * @author Antonio Tarricone + */ +public class ClaimName { + public static final String ACQUIRER_ID = "acquirerId"; + public static final String CHANNEL = "channel"; + public static final String MERCHANT_ID = "merchantId"; + public static final String CLIENT_ID = "clientId"; + public static final String TERMINAL_ID = "terminalId"; + public static final String SCOPE = "scope"; + public static final String GROUPS = "groups"; + + private ClaimName() { + } +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/bean/Client.java b/src/main/java/it/pagopa/swclient/mil/auth/bean/Client.java index 232e9697..f45a094f 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/bean/Client.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/bean/Client.java @@ -9,34 +9,33 @@ import lombok.Getter; /** - * * @author Antonio Tarricone */ @AllArgsConstructor @Getter public class Client { /* - * + * */ private String id; /* - * + * */ private String channel; /* - * + * */ private String salt; /* - * + * */ private String secretHash; /* - * + * */ private String description; } \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/bean/FormParamName.java b/src/main/java/it/pagopa/swclient/mil/auth/bean/FormParamName.java new file mode 100644 index 00000000..3bedb1f7 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/bean/FormParamName.java @@ -0,0 +1,24 @@ +/* + * FormParamName.java + * + * 28 ago 2023 + */ +package it.pagopa.swclient.mil.auth.bean; + +/** + * @author Antonio Tarricone + */ +public class FormParamName { + public static final String GRANT_TYPE = "grant_type"; + public static final String USERNAME = "username"; + public static final String PASSWORD = "password"; + public static final String REFRESH_TOKEN = "refresh_token"; + public static final String EXT_TOKEN = "ext_token"; + public static final String ADD_DATA = "add_data"; + public static final String CLIENT_ID = "client_id"; + public static final String SCOPE = "scope"; + public static final String CLIENT_SECRET = "client_secret"; + + private FormParamName() { + } +} diff --git a/src/main/java/it/pagopa/swclient/mil/auth/bean/GetAccessToken.java b/src/main/java/it/pagopa/swclient/mil/auth/bean/GetAccessToken.java deleted file mode 100644 index 27eaa158..00000000 --- a/src/main/java/it/pagopa/swclient/mil/auth/bean/GetAccessToken.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * GetAccessToken.java - * - * 16 mar 2023 - */ -package it.pagopa.swclient.mil.auth.bean; - -import static it.pagopa.swclient.mil.ErrorCode.ACQUIRER_ID_MUST_MATCH_REGEXP_MSG; -import static it.pagopa.swclient.mil.ErrorCode.CHANNEL_MUST_MATCH_REGEXP_MSG; -import static it.pagopa.swclient.mil.ErrorCode.MERCHANT_ID_MUST_MATCH_REGEXP_MSG; -import static it.pagopa.swclient.mil.ErrorCode.REQUEST_ID_MUST_MATCH_REGEXP_MSG; -import static it.pagopa.swclient.mil.ErrorCode.TERMINAL_ID_MUST_MATCH_REGEXP_MSG; -import static it.pagopa.swclient.mil.ErrorCode.VERSION_MUST_MATCH_REGEXP_MSG; -import static it.pagopa.swclient.mil.ErrorCode.VERSION_SIZE_MUST_BE_AT_MOST_MAX_MSG; -import static it.pagopa.swclient.mil.auth.ErrorCode.ADD_DATA_MUST_MATCH_REGEXP; -import static it.pagopa.swclient.mil.auth.ErrorCode.CLIENT_ID_MUST_MATCH_REGEXP; -import static it.pagopa.swclient.mil.auth.ErrorCode.CLIENT_ID_MUST_NOT_BE_NULL; -import static it.pagopa.swclient.mil.auth.ErrorCode.CLIENT_SECRET_MUST_MATCH_REGEXP; -import static it.pagopa.swclient.mil.auth.ErrorCode.EXT_TOKEN_MUST_MATCH_REGEXP; -import static it.pagopa.swclient.mil.auth.ErrorCode.GRANT_TYPE_MUST_MATCH_REGEXP; -import static it.pagopa.swclient.mil.auth.ErrorCode.GRANT_TYPE_MUST_NOT_BE_NULL; -import static it.pagopa.swclient.mil.auth.ErrorCode.INCONSISTENT_REQUEST; -import static it.pagopa.swclient.mil.auth.ErrorCode.PASSWORD_MUST_MATCH_REGEXP; -import static it.pagopa.swclient.mil.auth.ErrorCode.REFRESH_TOKEN_MUST_MATCH_REGEXP; -import static it.pagopa.swclient.mil.auth.ErrorCode.SCOPE_MUST_MATCH_REGEXP; -import static it.pagopa.swclient.mil.auth.ErrorCode.USERNAME_MUST_MATCH_REGEXP; -import static it.pagopa.swclient.mil.bean.Channel.ATM; -import static it.pagopa.swclient.mil.bean.Channel.POS; - -import io.quarkus.runtime.annotations.RegisterForReflection; -import it.pagopa.swclient.mil.auth.validation.constraints.ValidationTarget; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Pattern; -import jakarta.validation.constraints.Size; -import jakarta.ws.rs.FormParam; -import jakarta.ws.rs.HeaderParam; -import lombok.Getter; -import lombok.NoArgsConstructor; - -/** - * - * @author Antonio Tarricone - */ -@RegisterForReflection -@ValidationTarget(message = "[" + INCONSISTENT_REQUEST + "] Inconsistent request.") -@NoArgsConstructor -@Getter -public class GetAccessToken { - /* - * Request ID - */ - @HeaderParam("RequestId") - @Pattern(regexp = "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$", message = REQUEST_ID_MUST_MATCH_REGEXP_MSG) - private String requestId; - - /* - * Version of the required API - */ - @HeaderParam("Version") - @Size(max = 64, message = VERSION_SIZE_MUST_BE_AT_MOST_MAX_MSG) - @Pattern(regexp = "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", message = VERSION_MUST_MATCH_REGEXP_MSG) - private String version; - - /* - * Acquirer ID assigned by PagoPA - */ - @HeaderParam("AcquirerId") - @Pattern(regexp = "^\\d{1,11}$", message = ACQUIRER_ID_MUST_MATCH_REGEXP_MSG) - private String acquirerId; - - /* - * Channel originating the request - */ - @HeaderParam("Channel") - @Pattern(regexp = "^(" + ATM + "|" + POS + ")$", message = CHANNEL_MUST_MATCH_REGEXP_MSG) - private String channel; - - /* - * Merchant ID originating the transaction. If Channel equals to POS, MerchantId must not be null. - */ - @HeaderParam("MerchantId") - @Pattern(regexp = "^[0-9a-zA-Z]{1,15}$", message = MERCHANT_ID_MUST_MATCH_REGEXP_MSG) - private String merchantId; - - /* - * ID of the terminal originating the transaction. It must be unique per acquirer, channel and - * merchant if present. - */ - @HeaderParam("TerminalId") - @Pattern(regexp = "^[0-9a-zA-Z]{1,8}$", message = TERMINAL_ID_MUST_MATCH_REGEXP_MSG) - private String terminalId; - - /* - * grant_type - */ - @FormParam("grant_type") - @NotNull(message = "[" + GRANT_TYPE_MUST_NOT_BE_NULL + "] grant_type must not be null") - @Pattern(regexp = "^" + GrantType.PASSWORD + "|" + GrantType.REFRESH_TOKEN + "|" + GrantType.POYNT_TOKEN + "|" + GrantType.CLIENT_CREDENTIALS + "$", message = "[" + GRANT_TYPE_MUST_MATCH_REGEXP + "] grant_type must match \"{regexp}\"") - private String grantType; - - /* - * username - */ - @FormParam("username") - @Pattern(regexp = "^[ -~]{1,64}$", message = "[" + USERNAME_MUST_MATCH_REGEXP + "] username must match \"{regexp}\"") - private String username; - - /* - * password - */ - @FormParam("password") - @Pattern(regexp = "^[ -~]{1,64}$", message = "[" + PASSWORD_MUST_MATCH_REGEXP + "] password must match \"{regexp}\"") - private String password; - - /* - * refresh_token - */ - @FormParam("refresh_token") - @Pattern(regexp = "^[a-zA-Z0-9_-]{1,1024}\\.[a-zA-Z0-9_-]{1,1024}\\.[a-zA-Z0-9_-]{1,1024}$", message = "[" + REFRESH_TOKEN_MUST_MATCH_REGEXP + "] refresh_token must match \"{regexp}\"") - private String refreshToken; - - /* - * poynt_token - */ - @FormParam("ext_token") - @Pattern(regexp = "^[ -~]{1,4096}$", message = "[" + EXT_TOKEN_MUST_MATCH_REGEXP + "] ext_token must match \"{regexp}\"") - private String extToken; - - /* - * add_data - */ - @FormParam("add_data") - @Pattern(regexp = "^[ -~]{1,4096}$", message = "[" + ADD_DATA_MUST_MATCH_REGEXP + "] add_data must match \"{regexp}\"") - private String addData; - - /* - * client_id - */ - @FormParam("client_id") - @NotNull(message = "[" + CLIENT_ID_MUST_NOT_BE_NULL + "] client_id must not be null") - @Pattern(regexp = "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$", message = "[" + CLIENT_ID_MUST_MATCH_REGEXP + "] client_id must match \"{regexp}\"") - private String clientId; - - /* - * scope - */ - @FormParam("scope") - @Pattern(regexp = "^offline_access$", message = "[" + SCOPE_MUST_MATCH_REGEXP + "] scope must match \"{regexp}\"") - private String scope; - - /* - * client_secret - */ - @FormParam("client_secret") - @Pattern(regexp = "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$", message = "[" + CLIENT_SECRET_MUST_MATCH_REGEXP + "] client_secret must match \"{regexp}\"") - private String clientSecret; -} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/bean/GetAccessTokenRequest.java b/src/main/java/it/pagopa/swclient/mil/auth/bean/GetAccessTokenRequest.java new file mode 100644 index 00000000..215bbed1 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/bean/GetAccessTokenRequest.java @@ -0,0 +1,139 @@ +/* + * GetAccessTokenRequest.java + * + * 16 mar 2023 + */ +package it.pagopa.swclient.mil.auth.bean; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import it.pagopa.swclient.mil.ErrorCode; +import it.pagopa.swclient.mil.auth.AuthErrorCode; +import it.pagopa.swclient.mil.auth.validation.constraints.ValidationTarget; +import it.pagopa.swclient.mil.bean.Channel; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.HeaderParam; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * @author Antonio Tarricone + */ +@RegisterForReflection +@ValidationTarget(message = "[" + AuthErrorCode.INCONSISTENT_REQUEST + "] Inconsistent request.") +@NoArgsConstructor +@AllArgsConstructor +@Getter +public class GetAccessTokenRequest { + /* + * Request ID + */ + @HeaderParam(HeaderParamName.REQUEST_ID) + @Pattern(regexp = "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$", message = ErrorCode.REQUEST_ID_MUST_MATCH_REGEXP_MSG) + private String requestId; + + /* + * Version of the required API + */ + @HeaderParam(HeaderParamName.VERSION) + @Size(max = 64, message = ErrorCode.VERSION_SIZE_MUST_BE_AT_MOST_MAX_MSG) + @Pattern(regexp = "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", message = ErrorCode.VERSION_MUST_MATCH_REGEXP_MSG) + private String version; + + /* + * Acquirer ID assigned by PagoPA + */ + @HeaderParam(HeaderParamName.ACQUIRER_ID) + @Pattern(regexp = "^\\d{1,11}$", message = ErrorCode.ACQUIRER_ID_MUST_MATCH_REGEXP_MSG) + private String acquirerId; + + /* + * Channel originating the request + */ + @HeaderParam(HeaderParamName.CHANNEL) + @Pattern(regexp = "^(" + Channel.ATM + "|" + Channel.POS + ")$", message = ErrorCode.CHANNEL_MUST_MATCH_REGEXP_MSG) + private String channel; + + /* + * Merchant ID originating the transaction. If Channel equals to POS, MerchantId must not be null. + */ + @HeaderParam(HeaderParamName.MERCHANT_ID) + @Pattern(regexp = "^[0-9a-zA-Z]{1,15}$", message = ErrorCode.MERCHANT_ID_MUST_MATCH_REGEXP_MSG) + private String merchantId; + + /* + * ID of the terminal originating the transaction. It must be unique per acquirer, channel and + * merchant if present. + */ + @HeaderParam(HeaderParamName.TERMINAL_ID) + @Pattern(regexp = "^[0-9a-zA-Z]{1,8}$", message = ErrorCode.TERMINAL_ID_MUST_MATCH_REGEXP_MSG) + private String terminalId; + + /* + * grant_type + */ + @FormParam(FormParamName.GRANT_TYPE) + @NotNull(message = "[" + AuthErrorCode.GRANT_TYPE_MUST_NOT_BE_NULL + "] grant_type must not be null") + @Pattern(regexp = "^" + GrantType.PASSWORD + "|" + GrantType.REFRESH_TOKEN + "|" + GrantType.POYNT_TOKEN + "|" + GrantType.CLIENT_CREDENTIALS + "$", message = "[" + AuthErrorCode.GRANT_TYPE_MUST_MATCH_REGEXP + "] grant_type must match \"{regexp}\"") + private String grantType; + + /* + * username + */ + @FormParam(FormParamName.USERNAME) + @Pattern(regexp = "^[ -~]{1,64}$", message = "[" + AuthErrorCode.USERNAME_MUST_MATCH_REGEXP + "] username must match \"{regexp}\"") + private String username; + + /* + * password + */ + @FormParam(FormParamName.PASSWORD) + @Pattern(regexp = "^[ -~]{1,64}$", message = "[" + AuthErrorCode.PASSWORD_MUST_MATCH_REGEXP + "] password must match \"{regexp}\"") + private String password; + + /* + * refresh_token + */ + @FormParam(FormParamName.REFRESH_TOKEN) + @Pattern(regexp = "^[a-zA-Z0-9_-]{1,1024}\\.[a-zA-Z0-9_-]{1,1024}\\.[a-zA-Z0-9_-]{1,1024}$", message = "[" + AuthErrorCode.REFRESH_TOKEN_MUST_MATCH_REGEXP + "] refresh_token must match \"{regexp}\"") + private String refreshToken; + + /* + * poynt_token + */ + @FormParam(FormParamName.EXT_TOKEN) + @Pattern(regexp = "^[ -~]{1,4096}$", message = "[" + AuthErrorCode.EXT_TOKEN_MUST_MATCH_REGEXP + "] ext_token must match \"{regexp}\"") + private String extToken; + + /* + * add_data + */ + @FormParam(FormParamName.ADD_DATA) + @Pattern(regexp = "^[ -~]{1,4096}$", message = "[" + AuthErrorCode.ADD_DATA_MUST_MATCH_REGEXP + "] add_data must match \"{regexp}\"") + private String addData; + + /* + * client_id + */ + @FormParam(FormParamName.CLIENT_ID) + @NotNull(message = "[" + AuthErrorCode.CLIENT_ID_MUST_NOT_BE_NULL + "] client_id must not be null") + @Pattern(regexp = "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$", message = "[" + AuthErrorCode.CLIENT_ID_MUST_MATCH_REGEXP + "] client_id must match \"{regexp}\"") + private String clientId; + + /* + * scope + */ + @FormParam(FormParamName.SCOPE) + @Pattern(regexp = "^" + Scope.OFFLINE_ACCESS + "$", message = "[" + AuthErrorCode.SCOPE_MUST_MATCH_REGEXP + "] scope must match \"{regexp}\"") + private String scope; + + /* + * client_secret + */ + @FormParam(FormParamName.CLIENT_SECRET) + @Pattern(regexp = "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$", message = "[" + AuthErrorCode.CLIENT_SECRET_MUST_MATCH_REGEXP + "] client_secret must match \"{regexp}\"") + private String clientSecret; +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/bean/AccessToken.java b/src/main/java/it/pagopa/swclient/mil/auth/bean/GetAccessTokenResponse.java similarity index 59% rename from src/main/java/it/pagopa/swclient/mil/auth/bean/AccessToken.java rename to src/main/java/it/pagopa/swclient/mil/auth/bean/GetAccessTokenResponse.java index c4ef77ca..72ba16f2 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/bean/AccessToken.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/bean/GetAccessTokenResponse.java @@ -1,5 +1,5 @@ /* - * AccessToken.java + * GetAccessTokenResponse.java * * 16 mar 2023 */ @@ -13,45 +13,43 @@ import lombok.Getter; /** - * * @author Antonio Tarricone */ @RegisterForReflection @JsonInclude(Include.NON_NULL) @Getter -public class AccessToken { +public class GetAccessTokenResponse { /* * access_token */ - @JsonProperty("access_token") - private String accessTokenProper; + @JsonProperty(JsonPropertyName.ACCESS_TOKEN) + private String accessToken; /* * refresh_token */ - @JsonProperty("refresh_token") + @JsonProperty(JsonPropertyName.REFRESH_TOKEN) private String refreshToken; /* * token_type */ - @JsonProperty("token_type") - private String tokenType = "Bearer"; + @JsonProperty(JsonPropertyName.TOKEN_TYPE) + private String tokenType = TokenType.BEARER; /* * expires_in */ - @JsonProperty("expires_in") + @JsonProperty(JsonPropertyName.EXPIRES_IN) private long expiresIn; /** - * - * @param accessTokenProper + * @param accessToken * @param refreshToken * @param expiresIn */ - public AccessToken(String accessTokenProper, String refreshToken, long expiresIn) { - this.accessTokenProper = accessTokenProper; + public GetAccessTokenResponse(String accessToken, String refreshToken, long expiresIn) { + this.accessToken = accessToken; this.refreshToken = refreshToken; this.expiresIn = expiresIn; } diff --git a/src/main/java/it/pagopa/swclient/mil/auth/bean/GrantType.java b/src/main/java/it/pagopa/swclient/mil/auth/bean/GrantType.java index 14d16e16..8c3e2538 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/bean/GrantType.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/bean/GrantType.java @@ -6,7 +6,6 @@ package it.pagopa.swclient.mil.auth.bean; /** - * * @author Antonio Tarricone */ public class GrantType { diff --git a/src/main/java/it/pagopa/swclient/mil/auth/bean/HeaderParamName.java b/src/main/java/it/pagopa/swclient/mil/auth/bean/HeaderParamName.java new file mode 100644 index 00000000..cc0d641e --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/bean/HeaderParamName.java @@ -0,0 +1,21 @@ +/* + * HeaderParamName.java + * + * 28 ago 2023 + */ +package it.pagopa.swclient.mil.auth.bean; + +/** + * @author Antonio Tarricone + */ +public class HeaderParamName { + public static final String REQUEST_ID = "RequestId"; + public static final String VERSION = "Version"; + public static final String ACQUIRER_ID = "AcquirerId"; + public static final String CHANNEL = "Channel"; + public static final String MERCHANT_ID = "MerchantId"; + public static final String TERMINAL_ID = "TerminalId"; + + private HeaderParamName() { + } +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/bean/JsonPropertyName.java b/src/main/java/it/pagopa/swclient/mil/auth/bean/JsonPropertyName.java new file mode 100644 index 00000000..0fdf5ced --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/bean/JsonPropertyName.java @@ -0,0 +1,30 @@ +/* + * JsonPropertyName.java + * + * 28 ago 2023 + */ +package it.pagopa.swclient.mil.auth.bean; + +/** + * @author Antonio Tarricone + */ +public class JsonPropertyName { + public static final String ACCESS_TOKEN = "access_token"; + public static final String REFRESH_TOKEN = "refresh_token"; + public static final String TOKEN_TYPE = "token_type"; + public static final String EXPIRES_IN = "expires_in"; + + public static final String ERRORS = "errors"; + + public static final String KEYS = "keys"; + public static final String EXPONENT = "e"; + public static final String USE = "use"; + public static final String KID = "kid"; + public static final String MODULUS = "n"; + public static final String TYPE = "kty"; + public static final String EXPIRATION = "exp"; + public static final String ISSUED_AT = "iat"; + + private JsonPropertyName() { + } +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/bean/KeyPair.java b/src/main/java/it/pagopa/swclient/mil/auth/bean/KeyPair.java index a0486a66..f34e9ef7 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/bean/KeyPair.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/bean/KeyPair.java @@ -7,12 +7,12 @@ import io.quarkus.runtime.annotations.RegisterForReflection; import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; /** - * * @author Antonio Tarricone */ @RegisterForReflection @@ -20,6 +20,7 @@ @Setter @NoArgsConstructor @AllArgsConstructor +@EqualsAndHashCode public class KeyPair { /* * Private exponent @@ -60,12 +61,4 @@ public class KeyPair { * Issued at */ private long iat; - - /** - * - * @return - */ - public PublicKey publicKey() { - return new PublicKey(e, use, kid, n, kty, exp, iat); - } } \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/bean/KeyType.java b/src/main/java/it/pagopa/swclient/mil/auth/bean/KeyType.java index 7eec38f0..0a166717 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/bean/KeyType.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/bean/KeyType.java @@ -6,7 +6,6 @@ package it.pagopa.swclient.mil.auth.bean; /** - * * @author Antonio Tarricone */ public enum KeyType { diff --git a/src/main/java/it/pagopa/swclient/mil/auth/bean/KeyUse.java b/src/main/java/it/pagopa/swclient/mil/auth/bean/KeyUse.java index 73495165..8d79741f 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/bean/KeyUse.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/bean/KeyUse.java @@ -6,7 +6,6 @@ package it.pagopa.swclient.mil.auth.bean; /** - * * @author Antonio Tarricone */ public enum KeyUse { diff --git a/src/main/java/it/pagopa/swclient/mil/auth/bean/PublicKey.java b/src/main/java/it/pagopa/swclient/mil/auth/bean/PublicKey.java index 0f0a44b7..c6a4a539 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/bean/PublicKey.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/bean/PublicKey.java @@ -5,12 +5,13 @@ */ package it.pagopa.swclient.mil.auth.bean; +import com.fasterxml.jackson.annotation.JsonProperty; + import io.quarkus.runtime.annotations.RegisterForReflection; import lombok.AllArgsConstructor; import lombok.Data; /** - * * @author Antonio Tarricone */ @RegisterForReflection @@ -20,49 +21,42 @@ public class PublicKey { /* * Public exponent */ + @JsonProperty(JsonPropertyName.EXPONENT) private String e; /* * Public key use */ + @JsonProperty(JsonPropertyName.USE) private KeyUse use; /* * Key ID */ + @JsonProperty(JsonPropertyName.KID) private String kid; /* * Modulus */ + @JsonProperty(JsonPropertyName.MODULUS) private String n; /* * Key type */ + @JsonProperty(JsonPropertyName.TYPE) private KeyType kty; /* * Expiration time */ + @JsonProperty(JsonPropertyName.EXPIRATION) private long exp; /* * Issued at */ + @JsonProperty(JsonPropertyName.ISSUED_AT) private long iat; - - /** - * - * @param publicKey - */ - public PublicKey(PublicKey publicKey) { - this.e = publicKey.e; - this.use = publicKey.use; - this.kid = publicKey.kid; - this.n = publicKey.n; - this.kty = publicKey.kty; - this.exp = publicKey.exp; - this.iat = publicKey.iat; - } } \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/bean/PublicKeys.java b/src/main/java/it/pagopa/swclient/mil/auth/bean/PublicKeys.java index 2ce0dcbf..031b45a8 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/bean/PublicKeys.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/bean/PublicKeys.java @@ -7,24 +7,28 @@ import java.util.List; +import com.fasterxml.jackson.annotation.JsonProperty; + import io.quarkus.runtime.annotations.RegisterForReflection; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.ToString; /** - * * @author Antonio Tarricone */ @RegisterForReflection @AllArgsConstructor +@NoArgsConstructor @Getter @ToString @EqualsAndHashCode public class PublicKeys { /* - * + * */ + @JsonProperty(JsonPropertyName.KEYS) private List keys; } \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/bean/Role.java b/src/main/java/it/pagopa/swclient/mil/auth/bean/Role.java index 5d000dde..8d1edae9 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/bean/Role.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/bean/Role.java @@ -9,41 +9,42 @@ import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.ToString; /** - * * @author Antonio Tarricone */ @AllArgsConstructor @Getter +@ToString public class Role { /* - * + * */ private String acquirerId; /* - * + * */ private String channel; /* - * + * */ private String clientId; /* - * + * */ private String merchantId; /* - * + * */ private String terminalId; /* - * + * */ private List roles; } \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/bean/Scope.java b/src/main/java/it/pagopa/swclient/mil/auth/bean/Scope.java new file mode 100644 index 00000000..7c0b1fa1 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/bean/Scope.java @@ -0,0 +1,16 @@ +/* + * Scope.java + * + * 28 ago 2023 + */ +package it.pagopa.swclient.mil.auth.bean; + +/** + * @author Antonio Tarricone + */ +public class Scope { + public static final String OFFLINE_ACCESS = "offline_access"; + + private Scope() { + } +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/bean/TokenType.java b/src/main/java/it/pagopa/swclient/mil/auth/bean/TokenType.java new file mode 100644 index 00000000..3ae7c30d --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/bean/TokenType.java @@ -0,0 +1,16 @@ +/* + * TokenType.java + * + * 28 ago 2023 + */ +package it.pagopa.swclient.mil.auth.bean; + +/** + * @author antonio.tarricone + */ +public class TokenType { + public static final String BEARER = "Bearer"; + + private TokenType() { + } +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/bean/User.java b/src/main/java/it/pagopa/swclient/mil/auth/bean/User.java index 4f208203..4e4e2ff1 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/bean/User.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/bean/User.java @@ -10,7 +10,6 @@ import lombok.ToString; /** - * * @author Antonio Tarricone */ @AllArgsConstructor @@ -18,32 +17,32 @@ @ToString public class User { /* - * + * */ private String username; /* - * + * */ private String salt; /* - * + * */ private String passwordHash; /* - * + * */ private String acquirerId; /* - * + * */ private String channel; /* - * + * */ private String merchantId; } \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/client/AuthDataRepository.java b/src/main/java/it/pagopa/swclient/mil/auth/client/AuthDataRepository.java index 2943bde4..f67c67a3 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/client/AuthDataRepository.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/client/AuthDataRepository.java @@ -16,13 +16,11 @@ import jakarta.ws.rs.PathParam; /** - * * @author Antonio Tarricone */ @RegisterRestClient(configKey = "auth-data-repository") public interface AuthDataRepository { /** - * * @param clientId * @return */ @@ -31,7 +29,6 @@ public interface AuthDataRepository { Uni getClient(@PathParam("clientId") String clientId); /** - * * @param acquirerId * @param channel * @param merchantId @@ -47,9 +44,8 @@ Uni getRoles( @PathParam("clientId") String clientId, @PathParam("merchantId") String merchantId, @PathParam("terminalId") String terminalId); - + /** - * * @param userHash * @return */ diff --git a/src/main/java/it/pagopa/swclient/mil/auth/client/PoyntClient.java b/src/main/java/it/pagopa/swclient/mil/auth/client/PoyntClient.java index 0ff481c0..0ef1334e 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/client/PoyntClient.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/client/PoyntClient.java @@ -18,13 +18,11 @@ import jakarta.ws.rs.core.Response; /** - * * @author Antonio Tarricone */ @RegisterRestClient(configKey = "poynt-api") public interface PoyntClient { /** - * * @param poyntToken * @param businessId * @return @@ -38,7 +36,6 @@ Uni getBusinessObject( @PathParam("businessId") String businessId); /** - * * @param name * @return */ diff --git a/src/main/java/it/pagopa/swclient/mil/auth/qualifier/ClientCredentials.java b/src/main/java/it/pagopa/swclient/mil/auth/qualifier/ClientCredentials.java index 1687cb39..3d464076 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/qualifier/ClientCredentials.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/qualifier/ClientCredentials.java @@ -5,27 +5,22 @@ */ package it.pagopa.swclient.mil.auth.qualifier; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import jakarta.inject.Qualifier; /** - * * @author Antonio Tarricone */ @Qualifier @Documented -@Retention(RUNTIME) +@Retention(RetentionPolicy.RUNTIME) @Target({ - TYPE, METHOD, FIELD, PARAMETER + ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER }) public @interface ClientCredentials { } \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/qualifier/Password.java b/src/main/java/it/pagopa/swclient/mil/auth/qualifier/Password.java index 5420a1c5..ea299ea9 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/qualifier/Password.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/qualifier/Password.java @@ -5,27 +5,22 @@ */ package it.pagopa.swclient.mil.auth.qualifier; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import jakarta.inject.Qualifier; /** - * * @author Antonio Tarricone */ @Qualifier @Documented -@Retention(RUNTIME) +@Retention(RetentionPolicy.RUNTIME) @Target({ - TYPE, METHOD, FIELD, PARAMETER + ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER }) public @interface Password { } \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/qualifier/PoyntToken.java b/src/main/java/it/pagopa/swclient/mil/auth/qualifier/PoyntToken.java index ce6ce5fc..33a01cc1 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/qualifier/PoyntToken.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/qualifier/PoyntToken.java @@ -5,27 +5,22 @@ */ package it.pagopa.swclient.mil.auth.qualifier; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import jakarta.inject.Qualifier; /** - * * @author Antonio Tarricone */ @Qualifier @Documented -@Retention(RUNTIME) +@Retention(RetentionPolicy.RUNTIME) @Target({ - TYPE, METHOD, FIELD, PARAMETER + ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER }) public @interface PoyntToken { } \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/qualifier/RefreshToken.java b/src/main/java/it/pagopa/swclient/mil/auth/qualifier/RefreshToken.java index 843d00f3..cc2e6dcb 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/qualifier/RefreshToken.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/qualifier/RefreshToken.java @@ -5,27 +5,22 @@ */ package it.pagopa.swclient.mil.auth.qualifier; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import jakarta.inject.Qualifier; /** - * * @author Antonio Tarricone */ @Qualifier @Documented -@Retention(RUNTIME) +@Retention(RetentionPolicy.RUNTIME) @Target({ - TYPE, METHOD, FIELD, PARAMETER + ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER }) public @interface RefreshToken { } \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/resource/JwksResource.java b/src/main/java/it/pagopa/swclient/mil/auth/resource/JwksResource.java index 79e57746..ef0a192a 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/resource/JwksResource.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/resource/JwksResource.java @@ -5,14 +5,13 @@ */ package it.pagopa.swclient.mil.auth.resource; -import static it.pagopa.swclient.mil.auth.ErrorCode.ERROR_SEARCHING_FOR_KEYS; - import java.time.Instant; import java.util.List; import java.util.OptionalLong; import io.quarkus.logging.Log; import io.smallrye.mutiny.Uni; +import it.pagopa.swclient.mil.auth.AuthErrorCode; import it.pagopa.swclient.mil.auth.service.KeyFinder; import it.pagopa.swclient.mil.bean.Errors; import jakarta.inject.Inject; @@ -26,46 +25,43 @@ import jakarta.ws.rs.core.Response.Status; /** - * * @author Antonio Tarricone */ @Path("/.well-known/jwks.json") public class JwksResource { /* - * + * */ - private static final long SKEW = 5 * 60 * 1000L; + private static final long SKEW = 5 * 60L; /* - * + * */ @Inject - KeyFinder keyRetriever; + KeyFinder keyFinder; /** - * * @param t * @return */ private InternalServerErrorException errorOnRetrievingKeys(Throwable t) { - String message = String.format("[%s] Error searching for keys.", ERROR_SEARCHING_FOR_KEYS); + String message = String.format("[%s] Error searching for keys.", AuthErrorCode.ERROR_SEARCHING_FOR_KEYS); Log.errorf(t, message); return new InternalServerErrorException(Response .status(Status.INTERNAL_SERVER_ERROR) - .entity(new Errors(List.of(ERROR_SEARCHING_FOR_KEYS), List.of(message))) + .entity(new Errors(List.of(AuthErrorCode.ERROR_SEARCHING_FOR_KEYS), List.of(message))) .build()); } /** - * * @return */ @GET @Produces(MediaType.APPLICATION_JSON) public Uni get() { Log.debug("get - Input parameters: n/a"); - return keyRetriever.findPublicKeys() // Retrieve keys. - .invoke(l -> Log.debugf("get - Output parameters: %s", l.toString())) + return keyFinder.findPublicKeys() // Retrieve keys. + .invoke(l -> Log.debugf("get - Output parameters: [%s]", l.toString())) .map(l -> { /* * Search the key that expires first to set Cache-Control/max-age @@ -80,7 +76,7 @@ public Uni get() { /* * To be sure that will not be cached keys that will expire in a while, subtract SKEW. */ - maxAge = (minExp.getAsLong() - SKEW - Instant.now().toEpochMilli()) / 1000; // seconds + maxAge = (minExp.getAsLong() - SKEW - Instant.now().getEpochSecond()); } CacheControl cacheControl = new CacheControl(); diff --git a/src/main/java/it/pagopa/swclient/mil/auth/resource/TokenResource.java b/src/main/java/it/pagopa/swclient/mil/auth/resource/TokenResource.java index 119917b1..5eb29e6d 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/resource/TokenResource.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/resource/TokenResource.java @@ -11,9 +11,9 @@ import io.quarkus.logging.Log; import io.smallrye.mutiny.Uni; -import it.pagopa.swclient.mil.auth.ErrorCode; -import it.pagopa.swclient.mil.auth.bean.AccessToken; -import it.pagopa.swclient.mil.auth.bean.GetAccessToken; +import it.pagopa.swclient.mil.auth.AuthErrorCode; +import it.pagopa.swclient.mil.auth.bean.GetAccessTokenRequest; +import it.pagopa.swclient.mil.auth.bean.GetAccessTokenResponse; import it.pagopa.swclient.mil.auth.bean.GrantType; import it.pagopa.swclient.mil.auth.qualifier.ClientCredentials; import it.pagopa.swclient.mil.auth.qualifier.Password; @@ -40,23 +40,16 @@ import jakarta.ws.rs.core.Response.Status; /** - * * @author Antonio Tarricone */ @SuppressWarnings("serial") @Path("/token") public class TokenResource { /* - * - */ - @Inject - @Any - Instance tokenService; - - /* - * + * */ private static Map> qualifiers = new HashMap<>(); + static { qualifiers.put(GrantType.CLIENT_CREDENTIALS, new AnnotationLiteral() { }); @@ -68,6 +61,13 @@ public class TokenResource { }); } + /* + * + */ + @Inject + @Any + Instance tokenService; + /** * Dispatches the request to the right method. * @@ -77,7 +77,7 @@ public class TokenResource { @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Produces(MediaType.APPLICATION_JSON) - public Uni createOrRefreshToken(@Valid @BeanParam GetAccessToken getAccessToken) { + public Uni createOrRefreshToken(@Valid @BeanParam GetAccessTokenRequest getAccessToken) { /* * If the flow reaches this point, the input is validated! */ @@ -88,7 +88,7 @@ public Uni createOrRefreshToken(@Valid @BeanParam GetAccessToken ge .transform(t -> { Log.errorf(t, "Unexpected error."); return new InternalServerErrorException(Response.status(Status.INTERNAL_SERVER_ERROR) - .entity(new Errors(List.of(ErrorCode.UNEXPECTED_ERROR))) + .entity(new Errors(List.of(AuthErrorCode.UNEXPECTED_ERROR))) .build()); }) .onFailure(AuthError.class) diff --git a/src/main/java/it/pagopa/swclient/mil/auth/service/ClientVerifier.java b/src/main/java/it/pagopa/swclient/mil/auth/service/ClientVerifier.java index d806077a..51eb25d4 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/service/ClientVerifier.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/service/ClientVerifier.java @@ -5,12 +5,6 @@ */ package it.pagopa.swclient.mil.auth.service; -import static it.pagopa.swclient.mil.auth.ErrorCode.CLIENT_NOT_FOUND; -import static it.pagopa.swclient.mil.auth.ErrorCode.ERROR_SEARCHING_FOR_CLIENT; -import static it.pagopa.swclient.mil.auth.ErrorCode.ERROR_VERIFING_SECRET; -import static it.pagopa.swclient.mil.auth.ErrorCode.WRONG_CHANNEL; -import static it.pagopa.swclient.mil.auth.ErrorCode.WRONG_SECRET; - import java.security.NoSuchAlgorithmException; import java.util.Objects; @@ -19,6 +13,7 @@ import io.quarkus.cache.CacheResult; import io.quarkus.logging.Log; import io.smallrye.mutiny.Uni; +import it.pagopa.swclient.mil.auth.AuthErrorCode; import it.pagopa.swclient.mil.auth.bean.Client; import it.pagopa.swclient.mil.auth.client.AuthDataRepository; import it.pagopa.swclient.mil.auth.util.AuthError; @@ -29,19 +24,19 @@ import jakarta.ws.rs.core.Response; /** - * * @author Antonio Tarricone */ @ApplicationScoped public class ClientVerifier { /* - * + * */ @RestClient AuthDataRepository repository; /** - * + * Due to caching this method must be public. + * * @param clientId * @return */ @@ -51,36 +46,34 @@ public Uni getClient(String clientId) { } /** - * - * @param cliendId + * @param clientId * @return */ public Uni findClient(String clientId) { - Log.debugf("Search for the client %s.", clientId); + Log.debugf("Search for the client [%s].", clientId); return getClient(clientId) .onFailure().transform(t -> { if (t instanceof WebApplicationException e) { Response r = e.getResponse(); // r cannot be null if (r.getStatus() == 404) { - String message = String.format("[%s] Client %s not found.", CLIENT_NOT_FOUND, clientId); + String message = String.format("[%s] Client [%s] not found.", AuthErrorCode.CLIENT_NOT_FOUND, clientId); Log.warnf(t, message); - return new AuthException(CLIENT_NOT_FOUND, message); + return new AuthException(AuthErrorCode.CLIENT_NOT_FOUND, message); } else { - String message = String.format("[%s] Error searching for the client %s.", ERROR_SEARCHING_FOR_CLIENT, clientId); + String message = String.format("[%s] Error searching for the client [%s].", AuthErrorCode.ERROR_SEARCHING_FOR_CLIENT, clientId); Log.errorf(t, message); - return new AuthError(ERROR_SEARCHING_FOR_CLIENT, message); + return new AuthError(AuthErrorCode.ERROR_SEARCHING_FOR_CLIENT, message); } } else { - String message = String.format("[%s] Error searching for the client %s.", ERROR_SEARCHING_FOR_CLIENT, clientId); + String message = String.format("[%s] Error searching for the client [%s].", AuthErrorCode.ERROR_SEARCHING_FOR_CLIENT, clientId); Log.errorf(t, message); - return new AuthError(ERROR_SEARCHING_FOR_CLIENT, message); + return new AuthError(AuthErrorCode.ERROR_SEARCHING_FOR_CLIENT, message); } }); } /** - * * @param clientEntity * @param expectedChannel */ @@ -91,14 +84,13 @@ private Client verifyChannel(Client clientEntity, String expectedChannel) { Log.debug("Channel has been successfully verified."); return clientEntity; } else { - String message = String.format("[%s] Wrong channel. Expected %s, found %s.", WRONG_CHANNEL, expectedChannel, expectedChannel); + String message = String.format("[%s] Wrong channel. Expected [%s], found [%s].", AuthErrorCode.WRONG_CHANNEL, expectedChannel, foundChannel); Log.warn(message); - throw new AuthException(WRONG_CHANNEL, message); + throw new AuthException(AuthErrorCode.WRONG_CHANNEL, message); } } /** - * * @param clientEntity * @param expectedSecret */ @@ -109,23 +101,22 @@ private Client verifySecret(Client clientEntity, String expectedSecret) { if (foundSecret == null && expectedSecret == null) { Log.debug("Secret is not used."); return clientEntity; - } else if (PasswordVerifier.verify(expectedSecret, clientEntity.getSalt(), foundSecret)) { + } else if (foundSecret != null && expectedSecret != null && PasswordVerifier.verify(expectedSecret, clientEntity.getSalt(), foundSecret)) { Log.debug("Secret is ok."); return clientEntity; } else { - String message = String.format("[%s] Wrong secret.", WRONG_SECRET); + String message = String.format("[%s] Wrong secret.", AuthErrorCode.WRONG_SECRET); Log.warn(message); - throw new AuthException(WRONG_SECRET, message); + throw new AuthException(AuthErrorCode.WRONG_SECRET, message); } } catch (NoSuchAlgorithmException e) { - String message = String.format("[%s] Error verifing secret.", ERROR_VERIFING_SECRET); + String message = String.format("[%s] Error verifing secret.", AuthErrorCode.ERROR_VERIFING_SECRET); Log.error(message); - throw new AuthError(ERROR_VERIFING_SECRET, message); + throw new AuthError(AuthErrorCode.ERROR_VERIFING_SECRET, message); } } /** - * * @param clientId * @param channel * @param secret diff --git a/src/main/java/it/pagopa/swclient/mil/auth/service/KeyFinder.java b/src/main/java/it/pagopa/swclient/mil/auth/service/KeyFinder.java index 9a4a5199..6cfa47e3 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/service/KeyFinder.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/service/KeyFinder.java @@ -1,147 +1,32 @@ /* * KeyFinder.java * - * 22 mar 2023 + * 7 ago 2023 */ package it.pagopa.swclient.mil.auth.service; -import static it.pagopa.swclient.mil.auth.ErrorCode.ERROR_GENERATING_KEY_PAIR; -import static it.pagopa.swclient.mil.auth.util.UniGenerator.error; -import static it.pagopa.swclient.mil.auth.util.UniGenerator.item; - -import java.time.Instant; import java.util.Optional; -import org.eclipse.microprofile.config.inject.ConfigProperty; - -import com.nimbusds.jose.JOSEException; - -import io.quarkus.logging.Log; -import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; -import it.pagopa.swclient.mil.auth.bean.KeyPair; import it.pagopa.swclient.mil.auth.bean.PublicKey; import it.pagopa.swclient.mil.auth.bean.PublicKeys; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; /** - * - * @author Antonio Tarricone + * */ -@ApplicationScoped -public class KeyFinder { - /* - * Access token duration. - */ - @ConfigProperty(name = "access.duration") - long accessDuration; - - /* - * - */ - @Inject - RedisClient redisClient; - - /* - * - */ - @Inject - KeyPairGenerator keyPairGenerator; - - /** - * Returns the valid (not expired yet) key pair with the greatest expiration. If there are no valid - * key pair a new one is generated. - * - * @return - */ - public Uni findKeyPair() { - Log.debug("Search for the key pair with greatest expiration not expired yet."); - return redisClient.keys("*") // Loading kids. - .onItem().transformToMulti(kids -> Multi.createFrom().items(kids.stream())) // Transforming the list of kids in a stream of events (one event for a kid). - .onItem().transformToUniAndMerge(redisClient::get) // For each kid, getting the key pair. - .filter(k -> k.getExp() > Instant.now().toEpochMilli() - accessDuration * 1000) // Filtering expired key pairs or that will expire before the expiration of the access token. - .collect() // Collecting all key pairs. - .asList() // Converting the key pair events in an event that is the list of key pair. - .chain(l -> { - if (l.isEmpty()) { - /* - * There are no valid key pairs: generation of the pair. - */ - Log.debug("There are no valid key pairs: generation of the pair."); - try { - // Generating a new key pair. - KeyPair keyPair = keyPairGenerator.generate(); - - // Adding it to key list. - l.add(keyPair); - - // Key pair storage in Redis. - Log.debug("Key pair storage."); - return redisClient.setex(keyPair.getKid(), keyPair.getExp(), keyPair) - .chain(() -> item(keyPair)); - } catch (JOSEException e) { - String message = String.format("[%s] Error generating the key pair.", ERROR_GENERATING_KEY_PAIR); - Log.fatalf(e, message); - return error(ERROR_GENERATING_KEY_PAIR, message); - } - } else { - /* - * If there are valid key pairs, search for the pair with the greatest expiration. - */ - Log.debug("Search for the pair with the greatest expiration."); - l.sort((x, y) -> { - if (x.getExp() < y.getExp()) { - return 1; - } else if (x.getExp() == y.getExp()) { - return 0; - } else { - return -1; - } - }); - return item(l.get(0)); - } - }); - } - +public interface KeyFinder { /** - * + * Finds all valid public keys. + * * @return */ - public Uni findPublicKeys() { - Log.debug("Search for the public keys."); - return redisClient.keys("*") // Loading kids. - .onItem().transformToMulti(kids -> Multi.createFrom().items(kids.stream())) // Transforming the list of kids in a stream of events (one event for a kid). - .onItem().transformToUniAndMerge(redisClient::get) // For each kid, getting the key pair. - .filter(k -> k.getExp() > Instant.now().toEpochMilli()) // Filtering expired key pairs. - .map(KeyPair::publicKey) // Getting the public key from the key pair. - .collect() // Collecting all public keys. - .asList() // Converting the public key events in an event that is the list of public keys. - .invoke(l -> Log.debugf("Found %d valid key/s.", l.size())) - .map(PublicKeys::new); - } + public Uni findPublicKeys(); /** - * + * Finds the public key having the given kid. + * + * @param kid * @return */ - public Uni> findPublicKey(String kid) { - Log.debugf("Search for the public key %s.", kid); - return redisClient.get(kid) - .map(k -> { - if (k != null) { - long threshold = Instant.now().toEpochMilli(); - if (k.getExp() > threshold) { - Log.debugf("Key %s found. Found expiration %d, threshold %d.", kid, k.getExp(), threshold); - return Optional.of(k.publicKey()); - } else { - Log.warnf("Key %s expired.", kid); - return Optional.empty(); - } - } else { - Log.warnf("Key %s not found.", kid); - return Optional.empty(); - } - }); - } + public Uni> findPublicKey(String kid); } \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/service/KeyPairGenerator.java b/src/main/java/it/pagopa/swclient/mil/auth/service/KeyPairGenerator.java deleted file mode 100644 index a6673391..00000000 --- a/src/main/java/it/pagopa/swclient/mil/auth/service/KeyPairGenerator.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * KeyPairGenerator.java - * - * 22 mar 2023 - */ -package it.pagopa.swclient.mil.auth.service; - -import java.util.Date; -import java.util.UUID; - -import org.eclipse.microprofile.config.inject.ConfigProperty; - -import com.nimbusds.jose.JOSEException; -import com.nimbusds.jose.jwk.RSAKey; -import com.nimbusds.jose.jwk.gen.RSAKeyGenerator; - -import it.pagopa.swclient.mil.auth.bean.KeyPair; -import it.pagopa.swclient.mil.auth.bean.KeyType; -import it.pagopa.swclient.mil.auth.bean.KeyUse; -import jakarta.enterprise.context.ApplicationScoped; - -/** - * - * @author Antonio Tarricone - */ -@ApplicationScoped -public class KeyPairGenerator { - /* - * Cryptoperiod of RSA keys in millis. - */ - @ConfigProperty(name = "cryptoperiod", defaultValue = "86400000") - long cryptoperiod; - - /* - * Key size (modulus) of RSA keys in bits. - */ - @ConfigProperty(name = "keysize", defaultValue = "4096") - int keysize; - - /** - * - * @return - * @throws JOSEException - */ - public KeyPair generate() throws JOSEException { - Date issueTime = new Date(); - Date expirationTime = new Date(issueTime.getTime() + cryptoperiod); - String kid = UUID.randomUUID().toString(); - - RSAKey rsaJwk = new RSAKeyGenerator(keysize) - .keyUse(com.nimbusds.jose.jwk.KeyUse.SIGNATURE) - .keyID(kid) - .issueTime(issueTime) - .expirationTime(expirationTime) - .generate(); - - /* - * Private exponent - */ - String d = rsaJwk.getPrivateExponent().toJSONString().replace("\"", ""); - - /* - * Public exponent - */ - String e = rsaJwk.getPublicExponent().toJSONString().replace("\"", ""); - - /* - * Public key use - */ - KeyUse use = KeyUse.sig; - - /* - * Modulus - */ - String n = rsaJwk.getModulus().toJSONString().replace("\"", ""); - - /* - * Key type - */ - KeyType kty = KeyType.RSA; - - /* - * Expiration time - */ - long exp = expirationTime.getTime(); - - /* - * Issued at - */ - long iat = issueTime.getTime(); - - return new KeyPair(d, e, use, kid, n, kty, exp, iat); - } -} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/service/RedisClient.java b/src/main/java/it/pagopa/swclient/mil/auth/service/RedisClient.java deleted file mode 100644 index 86f98139..00000000 --- a/src/main/java/it/pagopa/swclient/mil/auth/service/RedisClient.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * RedisClient.java - * - * 22 mar 2023 - */ -package it.pagopa.swclient.mil.auth.service; - -import java.util.List; - -import io.quarkus.redis.datasource.ReactiveRedisDataSource; -import io.quarkus.redis.datasource.keys.ReactiveKeyCommands; -import io.quarkus.redis.datasource.value.ReactiveValueCommands; -import io.smallrye.mutiny.Uni; -import it.pagopa.swclient.mil.auth.bean.KeyPair; -import jakarta.enterprise.context.ApplicationScoped; - -/** - * - * @author Antonio Tarricone - */ -@ApplicationScoped -public class RedisClient { - /* - * - */ - private ReactiveKeyCommands keyCommands; - - /* - * - */ - private ReactiveValueCommands valueCommands; - - /** - * - * @param reactive - */ - public RedisClient(ReactiveRedisDataSource reactive) { - valueCommands = reactive.value(String.class, KeyPair.class); - keyCommands = reactive.key(String.class); - } - - /** - * - * @param pattern - * @return - */ - public Uni> keys(String pattern) { - return keyCommands.keys(pattern); - } - - /** - * - * @param kid - * @return - */ - public Uni get(String kid) { - return valueCommands.get(kid); - } - - /** - * - * @param kid - * @param seconds - * @param keyPair - * @return - */ - public Uni setex(String kid, long seconds, KeyPair keyPair) { - return valueCommands.setex(kid, seconds, keyPair); - } -} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/service/RefreshTokenVerifier.java b/src/main/java/it/pagopa/swclient/mil/auth/service/RefreshTokenVerifier.java deleted file mode 100644 index 056ac41b..00000000 --- a/src/main/java/it/pagopa/swclient/mil/auth/service/RefreshTokenVerifier.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * RefreshTokenVerifier.java - * - * 28 apr 2023 - */ -package it.pagopa.swclient.mil.auth.service; - -import static it.pagopa.swclient.mil.auth.ErrorCode.ERROR_PARSING_TOKEN; -import static it.pagopa.swclient.mil.auth.util.UniGenerator.error; - -import java.text.ParseException; - -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; - -import io.quarkus.logging.Log; -import io.smallrye.mutiny.Uni; -import jakarta.enterprise.context.ApplicationScoped; - -/** - * This class verifies the refresh token. - * - * @author Antonio Tarricone - */ -@ApplicationScoped -public class RefreshTokenVerifier extends TokenVerifier { - /** - * - * @param refreshTokenStr - * @return - */ - public Uni verify(String tokenStr) { - try { - SignedJWT token = SignedJWT.parse(tokenStr); - JWTClaimsSet claimsSet = token.getJWTClaimsSet(); - return verifyAlgorithm(token) - .chain(() -> { - return verifyIssueTime(claimsSet); - }) - .chain(() -> { - return verifyExpirationTime(claimsSet); - }) - .chain(() -> { - return verifyScope(claimsSet, "offline_access"); - }) - .chain(o -> { - return verifySignature(token); - }); - } catch (ParseException e) { - String message = String.format("[%s] Error parsing token.", ERROR_PARSING_TOKEN); - Log.errorf(e, message); - return error(ERROR_PARSING_TOKEN, message); - } - } -} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/service/RefreshTokensService.java b/src/main/java/it/pagopa/swclient/mil/auth/service/RefreshTokensService.java index 8ac2f1b2..ba546156 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/service/RefreshTokensService.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/service/RefreshTokensService.java @@ -5,36 +5,157 @@ */ package it.pagopa.swclient.mil.auth.service; +import java.text.ParseException; +import java.util.Date; +import java.util.Objects; + +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; + import io.quarkus.logging.Log; import io.smallrye.mutiny.Uni; -import it.pagopa.swclient.mil.auth.bean.AccessToken; -import it.pagopa.swclient.mil.auth.bean.GetAccessToken; +import it.pagopa.swclient.mil.auth.AuthErrorCode; +import it.pagopa.swclient.mil.auth.bean.ClaimName; +import it.pagopa.swclient.mil.auth.bean.GetAccessTokenRequest; +import it.pagopa.swclient.mil.auth.bean.GetAccessTokenResponse; +import it.pagopa.swclient.mil.auth.bean.Scope; import it.pagopa.swclient.mil.auth.qualifier.RefreshToken; +import it.pagopa.swclient.mil.auth.util.UniGenerator; import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; /** - * * @author Antonio Tarricone */ @ApplicationScoped @RefreshToken public class RefreshTokensService extends TokenService { - /* - * + /** + * This method verifies the token algorithm. + *

+ * If the verification succeeds, the method returns void, otherwise it returns a failure with + * specific error code. + * + * @param token + * @return + */ + private Uni verifyAlgorithm(SignedJWT token) { + Log.debug("Algorithm verification."); + JWSAlgorithm algorithm = token.getHeader().getAlgorithm(); + if (Objects.equals(algorithm, JWSAlgorithm.RS256)) { + Log.debug("Algorithm has been successfully verified."); + return UniGenerator.voidItem(); + } else { + String message = String.format("[%s] Wrong algorithm. Expected [%s], found [%s].", AuthErrorCode.WRONG_ALGORITHM, JWSAlgorithm.RS256, algorithm); + Log.warn(message); + return UniGenerator.exception(AuthErrorCode.WRONG_ALGORITHM, message); + } + } + + /** + * This method verifies the token issue time. + *

+ * If the verification succeeds, the method returns void, otherwise it returns a failure with + * specific error code. + */ + private Uni verifyIssueTime(JWTClaimsSet claimsSet) { + Log.debug("Issue time verification."); + Date issueTime = claimsSet.getIssueTime(); + if (issueTime == null) { + String message = String.format("[%s] Issue time must not be null.", AuthErrorCode.ISSUE_TIME_MUST_NOT_BE_NULL); + Log.warn(message); + return UniGenerator.exception(AuthErrorCode.ISSUE_TIME_MUST_NOT_BE_NULL, message); + } else { + long issueEpoch = issueTime.getTime(); + long now = new Date().getTime(); + if (issueEpoch > now) { + String message = String.format("[%s] Wrong issue time. Found [%d] but now is [%d].", AuthErrorCode.WRONG_ISSUE_TIME, issueEpoch, now); + Log.warn(message); + return UniGenerator.exception(AuthErrorCode.WRONG_ISSUE_TIME, message); + } else { + Log.debug("Issue time has been successfully verified."); + return UniGenerator.voidItem(); + } + } + } + + /** + * This method verifies the token expiration time. + *

+ * If the verification succeeds, the method returns void, otherwise it returns a failure with + * specific error code. + * + * @param claimsSet + * @return + */ + private Uni verifyExpirationTime(JWTClaimsSet claimsSet) { + Log.debug("Expiration time verification."); + Date expirationTime = claimsSet.getExpirationTime(); + if (expirationTime == null) { + String message = String.format("[%s] Expiration time must not be null.", AuthErrorCode.EXPIRATION_TIME_MUST_NOT_BE_NULL); + Log.warn(message); + return UniGenerator.exception(AuthErrorCode.EXPIRATION_TIME_MUST_NOT_BE_NULL, message); + } else if (expirationTime.before(new Date())) { + String message = String.format("[%s] Token expired.", AuthErrorCode.TOKEN_EXPIRED); + Log.warn(message); + return UniGenerator.exception(AuthErrorCode.TOKEN_EXPIRED, message); + } else { + Log.debug("Expiration time has been successfully verified."); + return UniGenerator.voidItem(); + } + } + + /** + * This method verifies the token scope. + *

+ * If the verification succeeds, the method returns void, otherwise it returns a failure with + * specific error code. + * + * @param claimsSet + * @param expectedScope + * @return */ - @Inject - RefreshTokenVerifier refreshTokenVerifier; + private Uni verifyScope(JWTClaimsSet claimsSet, String expectedScope) { + Log.debug("Scope verification."); + Object foundScope = claimsSet.getClaim(ClaimName.SCOPE); + if (Objects.equals(foundScope, expectedScope)) { + Log.debug("Scope has been successfully verified."); + return UniGenerator.voidItem(); + } else { + String message = String.format("[%s] Wrong scope. Expected [%s], found [%s].", AuthErrorCode.WRONG_SCOPE, expectedScope, foundScope); + Log.warn(message); + return UniGenerator.exception(AuthErrorCode.WRONG_SCOPE, message); + } + } + + /** + * @param refreshTokenStr + * @return + */ + private Uni verify(String tokenStr) { + try { + SignedJWT token = SignedJWT.parse(tokenStr); + JWTClaimsSet claimsSet = token.getJWTClaimsSet(); + return verifyAlgorithm(token) + .chain(() -> verifyIssueTime(claimsSet)) + .chain(() -> verifyExpirationTime(claimsSet)) + .chain(() -> verifyScope(claimsSet, Scope.OFFLINE_ACCESS)) + .chain(() -> tokenSigner.verify(token)); + } catch (ParseException e) { + String message = String.format("[%s] Error parsing token.", AuthErrorCode.ERROR_PARSING_TOKEN); + Log.errorf(e, message); + return UniGenerator.error(AuthErrorCode.ERROR_PARSING_TOKEN, message); + } + } /** - * * @param getAccessToken * @return */ @Override - public Uni process(GetAccessToken getAccessToken) { + public Uni process(GetAccessTokenRequest getAccessToken) { Log.debug("Tokens refreshing."); - return refreshTokenVerifier.verify(getAccessToken.getRefreshToken()) + return verify(getAccessToken.getRefreshToken()) .chain(() -> super.process(getAccessToken)); } } \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/service/RolesFinder.java b/src/main/java/it/pagopa/swclient/mil/auth/service/RolesFinder.java index 7dcf1612..901efce0 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/service/RolesFinder.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/service/RolesFinder.java @@ -5,29 +5,29 @@ */ package it.pagopa.swclient.mil.auth.service; -import static it.pagopa.swclient.mil.auth.ErrorCode.ERROR_SEARCHING_FOR_ROLES; -import static it.pagopa.swclient.mil.auth.ErrorCode.ROLES_NOT_FOUND; - import org.eclipse.microprofile.rest.client.inject.RestClient; import io.quarkus.cache.CacheResult; import io.quarkus.logging.Log; import io.smallrye.mutiny.Uni; +import it.pagopa.swclient.mil.auth.AuthErrorCode; import it.pagopa.swclient.mil.auth.bean.Role; import it.pagopa.swclient.mil.auth.client.AuthDataRepository; import it.pagopa.swclient.mil.auth.util.AuthError; import it.pagopa.swclient.mil.auth.util.AuthException; -import it.pagopa.swclient.mil.auth.util.UniGenerator; import jakarta.enterprise.context.ApplicationScoped; import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.core.Response; /** - * * @author Antonio Tarricone */ @ApplicationScoped public class RolesFinder { + /* + * + */ + private static final String NA = "NA"; /* * Role repository. */ @@ -35,16 +35,14 @@ public class RolesFinder { AuthDataRepository repository; /** - * * @param s * @return */ private String replaceNullWithNa(String s) { - return s != null ? s : "NA"; + return s != null ? s : NA; } /** - * * @param acquirerId * @param channel * @param clientId @@ -58,7 +56,6 @@ public Uni getRoles(String acquirerId, String channel, String clientId, St } /** - * * @param acquirerId * @param channel * @param clientId @@ -67,38 +64,33 @@ public Uni getRoles(String acquirerId, String channel, String clientId, St * @return */ private Uni find(String acquirerId, String channel, String clientId, String merchantId, String terminalId) { - Log.debugf("Search for the roles with acquirerId=%s, channel=%s, clientId=%s, merchantId=%s, terminalId=%s.", acquirerId, channel, clientId, merchantId, terminalId); - return getRoles( - replaceNullWithNa(acquirerId), - replaceNullWithNa(channel), - clientId, - replaceNullWithNa(merchantId), - replaceNullWithNa(terminalId)) + Log.debugf("Search (sub) for the roles with acquirerId=[%s], channel=[%s], clientId=[%s], merchantId=[%s], terminalId=[%s].", acquirerId, channel, clientId, merchantId, terminalId); + return getRoles(replaceNullWithNa(acquirerId), replaceNullWithNa(channel), clientId, replaceNullWithNa(merchantId), replaceNullWithNa(terminalId)) + .invoke(role -> Log.debugf("Roles found: [%s]", role)) .onFailure().transform(t -> { if (t instanceof WebApplicationException e) { Response r = e.getResponse(); // r cannot be null if (r.getStatus() == 404) { - String message = String.format("[%s] Roles not found.", ROLES_NOT_FOUND); + String message = String.format("[%s] Roles not found.", AuthErrorCode.ROLES_NOT_FOUND); Log.warn(message); - return new AuthException(ROLES_NOT_FOUND, message); + return new AuthException(AuthErrorCode.ROLES_NOT_FOUND, message); } else { - String message = String.format("[%s] Error searching for the roles.", ERROR_SEARCHING_FOR_ROLES); + String message = String.format("[%s] Error searching for the roles.", AuthErrorCode.ERROR_SEARCHING_FOR_ROLES); Log.errorf(t, message); - return new AuthError(ERROR_SEARCHING_FOR_ROLES, message); + return new AuthError(AuthErrorCode.ERROR_SEARCHING_FOR_ROLES, message); } } else { - String message = String.format("[%s] Error searching for the roles.", ERROR_SEARCHING_FOR_ROLES); + String message = String.format("[%s] Error searching for the roles.", AuthErrorCode.ERROR_SEARCHING_FOR_ROLES); Log.errorf(t, message); - return new AuthError(ERROR_SEARCHING_FOR_ROLES, message); + return new AuthError(AuthErrorCode.ERROR_SEARCHING_FOR_ROLES, message); } - }) - .chain(UniGenerator::item); + }); } /** * Finds roles. - * + * * @param acquirerId * @param channel * @param clientId @@ -107,6 +99,7 @@ private Uni find(String acquirerId, String channel, String clientId, Strin * @return */ public Uni findRoles(String acquirerId, String channel, String clientId, String merchantId, String terminalId) { + Log.debugf("Search (main) for the roles for acquirerId=[%s], channel=[%s], clientId=[%s], merchantId=[%s], terminalId=[%s].", acquirerId, channel, clientId, merchantId, terminalId); return find(acquirerId, channel, clientId, merchantId, terminalId) .onFailure(AuthException.class) .recoverWithUni(t -> { @@ -115,20 +108,28 @@ public Uni findRoles(String acquirerId, String channel, String clientId, S * If there are no roles for acquirer/channel/client/merchant/terminal, search for * acquirer/channel/client/merchant (without terminal). */ - return find(acquirerId, channel, clientId, merchantId, "NA").onFailure(AuthException.class) + return find(acquirerId, channel, clientId, merchantId, NA).onFailure(AuthException.class) .recoverWithUni(tt -> { if (merchantId != null) { /* * If there are no roles for acquirer/channel/client/merchant (without terminal), search for * acquirer/channel/client (without terminal and merchant). */ - return find(acquirerId, channel, clientId, "NA", "NA"); + return find(acquirerId, channel, clientId, NA, NA); } else { return Uni.createFrom().failure(tt); } }); } else { - return Uni.createFrom().failure(t); + if (merchantId != null) { + /* + * If there are no roles for acquirer/channel/client/merchant (without terminal), search for + * acquirer/channel/client (without terminal and merchant). + */ + return find(acquirerId, channel, clientId, NA, NA); + } else { + return Uni.createFrom().failure(t); + } } }); } diff --git a/src/main/java/it/pagopa/swclient/mil/auth/service/TokenByClientSecretService.java b/src/main/java/it/pagopa/swclient/mil/auth/service/TokenByClientSecretService.java index c864380c..ffe15e23 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/service/TokenByClientSecretService.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/service/TokenByClientSecretService.java @@ -7,25 +7,23 @@ import io.quarkus.logging.Log; import io.smallrye.mutiny.Uni; -import it.pagopa.swclient.mil.auth.bean.AccessToken; -import it.pagopa.swclient.mil.auth.bean.GetAccessToken; +import it.pagopa.swclient.mil.auth.bean.GetAccessTokenRequest; +import it.pagopa.swclient.mil.auth.bean.GetAccessTokenResponse; import it.pagopa.swclient.mil.auth.qualifier.ClientCredentials; import jakarta.enterprise.context.ApplicationScoped; /** - * * @author Antonio Tarricone */ @ApplicationScoped @ClientCredentials public class TokenByClientSecretService extends TokenService { /** - * * @param getAccessToken * @return */ @Override - public Uni process(GetAccessToken getAccessToken) { + public Uni process(GetAccessTokenRequest getAccessToken) { Log.debugf("Generation of the token by client secret."); return super.process(getAccessToken); } diff --git a/src/main/java/it/pagopa/swclient/mil/auth/service/TokenByPasswordService.java b/src/main/java/it/pagopa/swclient/mil/auth/service/TokenByPasswordService.java index 268c5731..f0b92b87 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/service/TokenByPasswordService.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/service/TokenByPasswordService.java @@ -5,15 +5,7 @@ */ package it.pagopa.swclient.mil.auth.service; -import static it.pagopa.swclient.mil.auth.ErrorCode.ERROR_SEARCHING_FOR_CREDENTIALS; -import static it.pagopa.swclient.mil.auth.ErrorCode.ERROR_VERIFING_CREDENTIALS; -import static it.pagopa.swclient.mil.auth.ErrorCode.INCONSISTENT_CREDENTIALS; -import static it.pagopa.swclient.mil.auth.ErrorCode.WRONG_CREDENTIALS; -import static it.pagopa.swclient.mil.auth.util.UniGenerator.error; -import static it.pagopa.swclient.mil.auth.util.UniGenerator.exception; -import static it.pagopa.swclient.mil.auth.util.UniGenerator.item; -import static it.pagopa.swclient.mil.auth.util.UniGenerator.voidItem; - +import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; @@ -21,13 +13,12 @@ import org.eclipse.microprofile.rest.client.inject.RestClient; -import com.nimbusds.jose.util.StandardCharset; - import io.quarkus.cache.CacheResult; import io.quarkus.logging.Log; import io.smallrye.mutiny.Uni; -import it.pagopa.swclient.mil.auth.bean.AccessToken; -import it.pagopa.swclient.mil.auth.bean.GetAccessToken; +import it.pagopa.swclient.mil.auth.AuthErrorCode; +import it.pagopa.swclient.mil.auth.bean.GetAccessTokenRequest; +import it.pagopa.swclient.mil.auth.bean.GetAccessTokenResponse; import it.pagopa.swclient.mil.auth.bean.User; import it.pagopa.swclient.mil.auth.client.AuthDataRepository; import it.pagopa.swclient.mil.auth.qualifier.Password; @@ -40,7 +31,6 @@ import jakarta.ws.rs.core.Response; /** - * * @author Antonio Tarricone */ @ApplicationScoped @@ -49,11 +39,15 @@ public class TokenByPasswordService extends TokenService { /* * */ + private static final String ERROR_SEARCHING_FOR_CREDENTIALS_MSG = "[%s] Error searching for the credentials."; + + /* + * + */ @RestClient AuthDataRepository repository; /** - * * @param userHash * @return */ @@ -64,24 +58,22 @@ public Uni getUser(String userHash) { /** * This method finds for resource owner credentials. - * + * * @param getAccessToken * @return */ - private Uni findCredentials(GetAccessToken getAccessToken) { + private Uni findCredentials(GetAccessTokenRequest getAccessToken) { Log.debug("Search for the credentials."); String userHash; try { - userHash = Base64.getEncoder().encodeToString( + userHash = Base64.getUrlEncoder().encodeToString( MessageDigest.getInstance("SHA256").digest( - getAccessToken.getUsername().getBytes(StandardCharset.UTF_8))) - .replace("+", "-") - .replace("/", "_"); + getAccessToken.getUsername().getBytes(StandardCharsets.UTF_8))); } catch (NoSuchAlgorithmException e) { - String message = String.format("[%s] Error searching for the credentials.", ERROR_SEARCHING_FOR_CREDENTIALS); + String message = String.format(ERROR_SEARCHING_FOR_CREDENTIALS_MSG, AuthErrorCode.ERROR_SEARCHING_FOR_CREDENTIALS); Log.errorf(e, message); - return UniGenerator.error(ERROR_SEARCHING_FOR_CREDENTIALS, message); + return UniGenerator.error(AuthErrorCode.ERROR_SEARCHING_FOR_CREDENTIALS, message); } return getUser(userHash) @@ -90,32 +82,32 @@ private Uni findCredentials(GetAccessToken getAccessToken) { Response r = e.getResponse(); // r cannot be null if (r.getStatus() == 404) { - Log.warnf("[%s] Credentials not found.", WRONG_CREDENTIALS); - return new AuthException(WRONG_CREDENTIALS, String.format("[%s] Wrong credentials.", WRONG_CREDENTIALS)); // It's better not to give details... + Log.warnf("[%s] Credentials not found.", AuthErrorCode.WRONG_CREDENTIALS); + return new AuthException(AuthErrorCode.WRONG_CREDENTIALS, String.format("[%s] Wrong credentials.", AuthErrorCode.WRONG_CREDENTIALS)); // It's better not to give details... } else { - String message = String.format("[%s] Error searching for the credentials.", ERROR_SEARCHING_FOR_CREDENTIALS); + String message = String.format(ERROR_SEARCHING_FOR_CREDENTIALS_MSG, AuthErrorCode.ERROR_SEARCHING_FOR_CREDENTIALS); Log.errorf(t, message); - return new AuthError(ERROR_SEARCHING_FOR_CREDENTIALS, message); + return new AuthError(AuthErrorCode.ERROR_SEARCHING_FOR_CREDENTIALS, message); } } else { - String message = String.format("[%s] Error searching for the credentials.", ERROR_SEARCHING_FOR_CREDENTIALS); + String message = String.format(ERROR_SEARCHING_FOR_CREDENTIALS_MSG, AuthErrorCode.ERROR_SEARCHING_FOR_CREDENTIALS); Log.errorf(t, message); - return new AuthError(ERROR_SEARCHING_FOR_CREDENTIALS, message); + return new AuthError(AuthErrorCode.ERROR_SEARCHING_FOR_CREDENTIALS, message); } }); } /** * This method verifies acquirer/channel/merchant/username consistency. - * + *

* If the verification succeeds, the method returns ResourceOwnerCredentialsEntity, otherwise it * returns a failure with specific error code. - * + * * @param credentialsEntity * @param getAccessToken * @return */ - private Uni verifyConsistency(User credentialsEntity, GetAccessToken getAccessToken) { + private Uni verifyConsistency(User credentialsEntity, GetAccessTokenRequest getAccessToken) { Log.debug("Acquirer/channel/merchant consistency verification."); String foundAcquirerId = credentialsEntity.getAcquirerId(); @@ -132,61 +124,60 @@ private Uni verifyConsistency(User credentialsEntity, GetAccessToken getAc if (consistency) { Log.debug("Acquirer/channel/merchant consistency has been successufully verified."); - return item(credentialsEntity); + return UniGenerator.item(credentialsEntity); } else { - Log.warnf("[%s] Acquirer/channel/merchant isn't consistent. Expected %s/%s/%s, found %s/%s/%s.", INCONSISTENT_CREDENTIALS, expectedAcquirerId, expectedChannel, expectedMerchantId, foundAcquirerId, foundChannel, foundMerchantId); - return exception(INCONSISTENT_CREDENTIALS, String.format("[%s] Inconsistent credentials.", INCONSISTENT_CREDENTIALS)); // It's better not to give details... + Log.warnf("[%s] Acquirer/channel/merchant isn't consistent. Expected [%s/%s/%s], found [%s/%s/%s].", AuthErrorCode.INCONSISTENT_CREDENTIALS, expectedAcquirerId, expectedChannel, expectedMerchantId, foundAcquirerId, foundChannel, foundMerchantId); + return UniGenerator.exception(AuthErrorCode.INCONSISTENT_CREDENTIALS, String.format("[%s] Inconsistent credentials.", AuthErrorCode.INCONSISTENT_CREDENTIALS)); // It's better not to give details... } } /** * This method verifies the password. - * + *

* If the verification succeeds, the method returns void, otherwise it returns a failure with * specific error code. - * + * * @param credentialsEntity * @param getAccessToken * @return */ - private Uni verifyPassword(User credentialsEntity, GetAccessToken getAccessToken) { + private Uni verifyPassword(User credentialsEntity, GetAccessTokenRequest getAccessToken) { Log.debug("Password verification."); try { if (PasswordVerifier.verify(getAccessToken.getPassword(), credentialsEntity.getSalt(), credentialsEntity.getPasswordHash())) { Log.debug("Password has been successfully verified."); - return voidItem(); + return UniGenerator.voidItem(); } else { - String message = String.format("[%s] Wrong credentials.", WRONG_CREDENTIALS); + String message = String.format("[%s] Wrong credentials.", AuthErrorCode.WRONG_CREDENTIALS); Log.warn(message); - return exception(WRONG_CREDENTIALS, message); + return UniGenerator.exception(AuthErrorCode.WRONG_CREDENTIALS, message); } } catch (NoSuchAlgorithmException e) { - String message = String.format("[%s] Error verifing credentials.", ERROR_VERIFING_CREDENTIALS); + String message = String.format("[%s] Error verifing credentials.", AuthErrorCode.ERROR_VERIFING_CREDENTIALS); Log.errorf(e, message); - return error(ERROR_VERIFING_CREDENTIALS, message); + return UniGenerator.error(AuthErrorCode.ERROR_VERIFING_CREDENTIALS, message); } } /** * This method verifies credentials. - * + *

* ResourceOwnerCredentialsEntity - * + * * @param getAccessToken */ - private Uni verifyCredentials(GetAccessToken getAccessToken) { + private Uni verifyCredentials(GetAccessTokenRequest getAccessToken) { return findCredentials(getAccessToken) .chain(c -> verifyConsistency(c, getAccessToken)) .chain(c -> verifyPassword(c, getAccessToken)); } /** - * * @param getAccessToken * @return */ @Override - public Uni process(GetAccessToken getAccessToken) { + public Uni process(GetAccessTokenRequest getAccessToken) { Log.debugf("Generation of the token/s by password."); return verifyCredentials(getAccessToken) .chain(() -> super.process(getAccessToken)); diff --git a/src/main/java/it/pagopa/swclient/mil/auth/service/TokenByPoyntTokenService.java b/src/main/java/it/pagopa/swclient/mil/auth/service/TokenByPoyntTokenService.java index 4ccc9138..b3fd64b3 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/service/TokenByPoyntTokenService.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/service/TokenByPoyntTokenService.java @@ -5,34 +5,31 @@ */ package it.pagopa.swclient.mil.auth.service; -import static it.pagopa.swclient.mil.auth.ErrorCode.ERROR_VALIDATING_EXT_TOKEN; -import static it.pagopa.swclient.mil.auth.ErrorCode.EXT_TOKEN_NOT_VALID; -import static it.pagopa.swclient.mil.auth.util.UniGenerator.exception; -import static it.pagopa.swclient.mil.auth.util.UniGenerator.voidItem; - import org.eclipse.microprofile.rest.client.inject.RestClient; import io.quarkus.logging.Log; import io.smallrye.mutiny.Uni; -import it.pagopa.swclient.mil.auth.bean.AccessToken; -import it.pagopa.swclient.mil.auth.bean.GetAccessToken; +import it.pagopa.swclient.mil.auth.AuthErrorCode; +import it.pagopa.swclient.mil.auth.bean.GetAccessTokenRequest; +import it.pagopa.swclient.mil.auth.bean.GetAccessTokenResponse; +import it.pagopa.swclient.mil.auth.bean.TokenType; import it.pagopa.swclient.mil.auth.client.PoyntClient; import it.pagopa.swclient.mil.auth.qualifier.PoyntToken; import it.pagopa.swclient.mil.auth.util.AuthError; import it.pagopa.swclient.mil.auth.util.AuthException; +import it.pagopa.swclient.mil.auth.util.UniGenerator; import jakarta.enterprise.context.ApplicationScoped; import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.core.Response; /** - * * @author Antonio Tarricone */ @ApplicationScoped @PoyntToken public class TokenByPoyntTokenService extends TokenService { /* - * + * */ @RestClient PoyntClient poyntClient; @@ -43,41 +40,40 @@ public class TokenByPoyntTokenService extends TokenService { * @param getAccessToken * @return */ - public Uni verifyPoyntToken(GetAccessToken getAccessToken) { + public Uni verifyPoyntToken(GetAccessTokenRequest getAccessToken) { Log.debug("Poynt token verification."); - return poyntClient.getBusinessObject("Bearer " + getAccessToken.getExtToken(), getAccessToken.getAddData()) + return poyntClient.getBusinessObject(TokenType.BEARER + " " + getAccessToken.getExtToken(), getAccessToken.getAddData()) .onFailure().transform(t -> { if (t instanceof WebApplicationException e) { Response r = e.getResponse(); // r cannot be null - String message = String.format("[%s] Poynt Token not valid. Status: %s", EXT_TOKEN_NOT_VALID, r.getStatus()); + String message = String.format("[%s] Poynt Token not valid. Status: [%s]", AuthErrorCode.EXT_TOKEN_NOT_VALID, r.getStatus()); Log.warnf(e, message); - return new AuthException(EXT_TOKEN_NOT_VALID, message); + return new AuthException(AuthErrorCode.EXT_TOKEN_NOT_VALID, message); } else { - String message = String.format("[%s] Error validating Poynt token.", ERROR_VALIDATING_EXT_TOKEN); + String message = String.format("[%s] Error validating Poynt token.", AuthErrorCode.ERROR_VALIDATING_EXT_TOKEN); Log.errorf(t, message); - return new AuthError(ERROR_VALIDATING_EXT_TOKEN, message); + return new AuthError(AuthErrorCode.ERROR_VALIDATING_EXT_TOKEN, message); } }) .chain(r -> { if (r.getStatus() != 200) { - String message = String.format("[%s] Poynt Token not valid. Status: %s", EXT_TOKEN_NOT_VALID, r.getStatus()); + String message = String.format("[%s] Poynt Token not valid. Status: %s", AuthErrorCode.EXT_TOKEN_NOT_VALID, r.getStatus()); Log.warn(message); - return exception(EXT_TOKEN_NOT_VALID, message); + return UniGenerator.exception(AuthErrorCode.EXT_TOKEN_NOT_VALID, message); } else { Log.debug("Poynt token has been successfully verified."); - return voidItem(); + return UniGenerator.voidItem(); } }); } /** - * * @param getAccessToken * @return */ @Override - public Uni process(GetAccessToken getAccessToken) { + public Uni process(GetAccessTokenRequest getAccessToken) { Log.debugf("Generation of the token/s by Poynt token."); return verifyPoyntToken(getAccessToken) .chain(() -> super.process(getAccessToken)); diff --git a/src/main/java/it/pagopa/swclient/mil/auth/service/TokenService.java b/src/main/java/it/pagopa/swclient/mil/auth/service/TokenService.java index 2ece219a..9044381c 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/service/TokenService.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/service/TokenService.java @@ -5,29 +5,27 @@ */ package it.pagopa.swclient.mil.auth.service; -import static it.pagopa.swclient.mil.auth.ErrorCode.ERROR_GENERATING_TOKEN; -import static it.pagopa.swclient.mil.auth.util.UniGenerator.error; -import static it.pagopa.swclient.mil.auth.util.UniGenerator.item; - -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; +import java.util.Date; import java.util.List; import java.util.Objects; import org.eclipse.microprofile.config.inject.ConfigProperty; -import com.nimbusds.jose.JOSEException; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; import io.quarkus.logging.Log; import io.smallrye.mutiny.Uni; -import it.pagopa.swclient.mil.auth.bean.AccessToken; -import it.pagopa.swclient.mil.auth.bean.GetAccessToken; +import it.pagopa.swclient.mil.auth.bean.ClaimName; +import it.pagopa.swclient.mil.auth.bean.GetAccessTokenRequest; +import it.pagopa.swclient.mil.auth.bean.GetAccessTokenResponse; import it.pagopa.swclient.mil.auth.bean.GrantType; -import it.pagopa.swclient.mil.auth.util.TokenGenerator; +import it.pagopa.swclient.mil.auth.bean.Scope; import jakarta.inject.Inject; /** - * + * This class generates access token string and refresh token string if any and signs them. + * * @author Antonio Tarricone */ public abstract class TokenService { @@ -44,61 +42,101 @@ public abstract class TokenService { long refreshDuration; /* - * + * */ @Inject - KeyFinder keyFinder; + ClientVerifier clientVerifier; /* - * + * */ @Inject - ClientVerifier clientVerifier; + RolesFinder roleFinder; /* - * + * */ @Inject - RolesFinder roleFinder; + TokenSigner tokenSigner; + + /** + * @param strings + * @return + */ + private String concat(List strings) { + if (strings == null) { + return null; + } + StringBuilder buffer = new StringBuilder(); + strings.forEach(x -> { + buffer.append(x); + buffer.append(" "); + }); + return buffer.toString().trim(); + } + + /** + * @param request + * @param duration + * @param roles + * @param scopes + * @return + */ + private Uni generate(GetAccessTokenRequest request, long duration, List roles, List scopes) { + Date now = new Date(); + JWTClaimsSet payload = new JWTClaimsSet.Builder() + .subject(request.getClientId()) + .issueTime(now) + .expirationTime(new Date(now.getTime() + duration * 1000)) + .claim(ClaimName.ACQUIRER_ID, request.getAcquirerId()) + .claim(ClaimName.CHANNEL, request.getChannel()) + .claim(ClaimName.MERCHANT_ID, request.getMerchantId()) + .claim(ClaimName.CLIENT_ID, request.getClientId()) + .claim(ClaimName.TERMINAL_ID, request.getTerminalId()) + .claim(ClaimName.SCOPE, concat(scopes)) + .claim(ClaimName.GROUPS, roles) + .build(); + Log.debug("Token signing."); + return tokenSigner.sign(payload).map(SignedJWT::serialize); + } /** - * This method generates access token string and refresh token string if any, finding the key pair - * to sign them. - * - * @param getAccessToken + * This method generates access token string and refresh token string if any and signs them. + * + * @param request * @param roles * @return */ - private Uni generateToken(GetAccessToken getAccessToken, List roles) { - return keyFinder.findKeyPair() - .chain(k -> { - Log.debug("Access token generation."); - try { - String accessToken = TokenGenerator.generate(getAccessToken.getAcquirerId(), getAccessToken.getChannel(), getAccessToken.getMerchantId(), getAccessToken.getClientId(), getAccessToken.getTerminalId(), accessDuration, roles, null, k); - String refreshToken = null; - if (Objects.equals(getAccessToken.getScope(), "offline_access") || getAccessToken.getGrantType().equals(GrantType.REFRESH_TOKEN)) { - Log.debug("Refresh token generation."); - refreshToken = TokenGenerator.generate(getAccessToken.getAcquirerId(), getAccessToken.getChannel(), getAccessToken.getMerchantId(), getAccessToken.getClientId(), getAccessToken.getTerminalId(), refreshDuration, null, List.of("offline_access"), k); - } - Log.debug("Token/s has/ve been successfully generated."); - return item(new AccessToken(accessToken, refreshToken, accessDuration)); - } catch (NoSuchAlgorithmException | InvalidKeySpecException | JOSEException e) { - String message = String.format("[%s] Error generating token/s.", ERROR_GENERATING_TOKEN); - Log.errorf(e, message); - return error(ERROR_GENERATING_TOKEN, message); - } - }); + private Uni generateToken(GetAccessTokenRequest request, List roles) { + Log.debug("Access token generation."); + if (Objects.equals(request.getScope(), Scope.OFFLINE_ACCESS) || request.getGrantType().equals(GrantType.REFRESH_TOKEN)) { + /* + * With refresh token. + */ + return generate(request, accessDuration, roles, null) + .chain(accessToken -> { + Log.debug("Refresh token generation."); + return generate(request, refreshDuration, null, List.of(Scope.OFFLINE_ACCESS)) + .map(refreshToken -> new GetAccessTokenResponse(accessToken, refreshToken, accessDuration)); + }); + } else { + /* + * Without refresh token. + */ + return generate(request, accessDuration, roles, null) + .map(accessToken -> new GetAccessTokenResponse(accessToken, null, accessDuration)); + } } /** * This method contains all common logic behind the access token generation. - * - * @param getAccessToken + * + * @param request * @return */ - public Uni process(GetAccessToken getAccessToken) { - return clientVerifier.verify(getAccessToken.getClientId(), getAccessToken.getChannel(), getAccessToken.getClientSecret()) - .chain(() -> roleFinder.findRoles(getAccessToken.getAcquirerId(), getAccessToken.getChannel(), getAccessToken.getClientId(), getAccessToken.getMerchantId(), getAccessToken.getTerminalId())) - .chain(roleEntity -> generateToken(getAccessToken, roleEntity.getRoles())); + public Uni process(GetAccessTokenRequest request) { + return clientVerifier.verify(request.getClientId(), request.getChannel(), request.getClientSecret()) + .chain(() -> roleFinder.findRoles(request.getAcquirerId(), request.getChannel(), request.getClientId(), request.getMerchantId(), request.getTerminalId())) + .chain(roleEntity -> generateToken(request, roleEntity.getRoles())); } } \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/service/TokenSigner.java b/src/main/java/it/pagopa/swclient/mil/auth/service/TokenSigner.java new file mode 100644 index 00000000..6352c668 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/service/TokenSigner.java @@ -0,0 +1,35 @@ +/* + * TokenSigner.java + * + * 7 ago 2023 + */ +package it.pagopa.swclient.mil.auth.service; + +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; + +import io.smallrye.mutiny.Uni; + +/** + * + */ +public interface TokenSigner { + /** + * Signs the given token by means of the valid private key with the greatest expiration. + * + * @param payload + * @return + */ + public Uni sign(JWTClaimsSet payload); + + /** + * This class verifies the token signature. + *

+ * If the verification succeeds, the method returns void, otherwise it returns a failure with + * specific error code. + * + * @param token + * @return + */ + public Uni verify(SignedJWT token); +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/service/TokenVerifier.java b/src/main/java/it/pagopa/swclient/mil/auth/service/TokenVerifier.java deleted file mode 100644 index d37e78db..00000000 --- a/src/main/java/it/pagopa/swclient/mil/auth/service/TokenVerifier.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * TokenVerifier.java - * - * 27 apr 2023 - */ -package it.pagopa.swclient.mil.auth.service; - -import static it.pagopa.swclient.mil.auth.ErrorCode.ERROR_SEARCHING_FOR_KEYS; -import static it.pagopa.swclient.mil.auth.ErrorCode.ERROR_VERIFING_SIGNATURE; -import static it.pagopa.swclient.mil.auth.ErrorCode.EXPIRATION_TIME_MUST_NOT_BE_NULL; -import static it.pagopa.swclient.mil.auth.ErrorCode.ISSUE_TIME_MUST_NOT_BE_NULL; -import static it.pagopa.swclient.mil.auth.ErrorCode.KEY_NOT_FOUND; -import static it.pagopa.swclient.mil.auth.ErrorCode.TOKEN_EXPIRED; -import static it.pagopa.swclient.mil.auth.ErrorCode.WRONG_ALGORITHM; -import static it.pagopa.swclient.mil.auth.ErrorCode.WRONG_ISSUE_TIME; -import static it.pagopa.swclient.mil.auth.ErrorCode.WRONG_SCOPE; -import static it.pagopa.swclient.mil.auth.ErrorCode.WRONG_SIGNATURE; -import static it.pagopa.swclient.mil.auth.util.KeyPairUtil.getPublicKey; -import static it.pagopa.swclient.mil.auth.util.UniGenerator.error; -import static it.pagopa.swclient.mil.auth.util.UniGenerator.exception; -import static it.pagopa.swclient.mil.auth.util.UniGenerator.voidItem; - -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; -import java.util.Date; -import java.util.Objects; - -import com.nimbusds.jose.JOSEException; -import com.nimbusds.jose.JWSAlgorithm; -import com.nimbusds.jose.JWSVerifier; -import com.nimbusds.jose.crypto.RSASSAVerifier; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; - -import io.quarkus.logging.Log; -import io.smallrye.mutiny.Uni; -import it.pagopa.swclient.mil.auth.bean.PublicKey; -import it.pagopa.swclient.mil.auth.util.AuthError; -import it.pagopa.swclient.mil.auth.util.AuthException; -import jakarta.inject.Inject; - -/** - * - * @author Antonio Tarricone - */ -public abstract class TokenVerifier { - /* - * - */ - @Inject - KeyFinder keyFinder; - - /** - * This method verifies the token algorithm. - * - * If the verification succeeds, the method returns void, otherwise it returns a failure with - * specific error code. - * - * @param token - * @return - */ - protected Uni verifyAlgorithm(SignedJWT token) { - Log.debug("Algorithm verification."); - JWSAlgorithm algorithm = token.getHeader().getAlgorithm(); - if (Objects.equals(algorithm, JWSAlgorithm.RS256)) { - Log.debug("Algorithm has been successfully verified."); - return voidItem(); - } else { - String message = String.format("[%s] Wrong algorithm. Expected %s, found %s.", WRONG_ALGORITHM, JWSAlgorithm.RS256, algorithm); - Log.warn(message); - return exception(WRONG_ALGORITHM, message); - } - } - - /** - * This method verifies the token issue time. - * - * If the verification succeeds, the method returns void, otherwise it returns a failure with - * specific error code. - */ - protected Uni verifyIssueTime(JWTClaimsSet claimsSet) { - Log.debug("Issue time verification."); - Date issueTime = claimsSet.getIssueTime(); - if (issueTime == null) { - String message = String.format("[%s] Issue time must not be null.", ISSUE_TIME_MUST_NOT_BE_NULL); - Log.warn(message); - return exception(ISSUE_TIME_MUST_NOT_BE_NULL, message); - } else { - long issueEpoch = issueTime.getTime(); - long now = new Date().getTime(); - if (issueEpoch > now) { - String message = String.format("[%s] Wrong issue time. Found %d but now is %d.", WRONG_ISSUE_TIME, issueEpoch, now); - Log.warn(message); - return exception(WRONG_ISSUE_TIME, message); - } else { - Log.debug("Issue time has been successfully verified."); - return voidItem(); - } - } - } - - /** - * This method verifies the token expiration time. - * - * If the verification succeeds, the method returns void, otherwise it returns a failure with - * specific error code. - * - * @param claimsSet - * @return - */ - protected Uni verifyExpirationTime(JWTClaimsSet claimsSet) { - Log.debug("Expiration time verification."); - Date expirationTime = claimsSet.getExpirationTime(); - if (expirationTime == null) { - String message = String.format("[%s] Expiration time must not be null.", EXPIRATION_TIME_MUST_NOT_BE_NULL); - Log.warn(message); - return exception(EXPIRATION_TIME_MUST_NOT_BE_NULL, message); - } else if (expirationTime.before(new Date())) { - String message = String.format("[%s] Token expired.", TOKEN_EXPIRED); - Log.warn(message); - return exception(TOKEN_EXPIRED, message); - } else { - Log.debug("Expiration time has been successfully verified."); - return voidItem(); - } - } - - /** - * This method verifies the token scope. - * - * If the verification succeeds, the method returns void, otherwise it returns a failure with - * specific error code. - * - * @param claimsSet - * @param expectedScope - * @return - */ - protected Uni verifyScope(JWTClaimsSet claimsSet, String expectedScope) { - Log.debug("Scope verification."); - Object foundScope = claimsSet.getClaim("scope"); - if (Objects.equals(foundScope, expectedScope)) { - Log.debug("Scope has been successfully verified."); - return voidItem(); - } else { - String message = String.format("[%s] Wrong scope. Expected %s, found %s.", WRONG_SCOPE, expectedScope, foundScope); - Log.warn(message); - return exception(WRONG_SCOPE, message); - } - } - - /** - * This class verifies the token signature. - * - * If the verification succeeds, the method returns void, otherwise it returns a failure with - * specific error code. - * - * @param token - * @param key - * @return - */ - private Uni verifySignature(SignedJWT token, PublicKey key) { - Log.debug("Signature verification."); - try { - JWSVerifier verifier = new RSASSAVerifier(getPublicKey(key)); - if (token.verify(verifier)) { - Log.debug("Signature has been successfully verified."); - return voidItem(); - } else { - String message = String.format("[%s] Wrong signature.", WRONG_SIGNATURE); - Log.warn(message); - return exception(WRONG_SIGNATURE, message); - } - } catch (JOSEException | NoSuchAlgorithmException | InvalidKeySpecException e) { - String message = String.format("[%s] Error verifing signature.", ERROR_VERIFING_SIGNATURE); - Log.errorf(e, message); - return error(ERROR_VERIFING_SIGNATURE, message); - } - } - - /** - * This method retrieves a public key. - * - * @param kid - * @return - */ - private Uni findPublicKey(String kid) { - Log.debugf("Search for the public key %s.", kid); - return keyFinder.findPublicKey(kid) - .onFailure().transform(t -> { - String message = String.format("[%s] Error searching for the public key.", ERROR_SEARCHING_FOR_KEYS); - Log.errorf(t, message); - return new AuthError(ERROR_SEARCHING_FOR_KEYS, message); - }) - .onItem().transform(op -> op.orElseThrow(() -> { - String message = String.format("[%s] Key %s not found.", KEY_NOT_FOUND, kid); - Log.warn(message); - return new AuthException(KEY_NOT_FOUND, message); - })); - } - - /** - * This method verifies the token signature retrieving the suitable public key. - * - * If the verification succeeds, the method returns void, otherwise it returns a failure with - * specific error code. - * - * @param token - * @return - */ - protected Uni verifySignature(SignedJWT token) { - String kid = token.getHeader().getKeyID(); - return findPublicKey(kid) - .chain(k -> verifySignature(token, k)); - } -} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/util/AuthError.java b/src/main/java/it/pagopa/swclient/mil/auth/util/AuthError.java index 9b14f82c..f44341f4 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/util/AuthError.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/util/AuthError.java @@ -7,23 +7,22 @@ /** * To be used if an application error occurs. - * + * * @author Antonio Tarricone */ public class AuthError extends Error { /* - * + * */ private static final long serialVersionUID = 7785715776960328601L; /* - * + * */ private final String code; /** - * * @param code * @param message */ @@ -33,7 +32,6 @@ public AuthError(String code, String message) { } /** - * * @return */ public String getCode() { diff --git a/src/main/java/it/pagopa/swclient/mil/auth/util/AuthException.java b/src/main/java/it/pagopa/swclient/mil/auth/util/AuthException.java index 0f366772..9acfbf25 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/util/AuthException.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/util/AuthException.java @@ -7,22 +7,21 @@ /** * To be used if a check fails. - * + * * @author Antonio Tarricone */ public class AuthException extends RuntimeException { /* - * + * */ private static final long serialVersionUID = -523911093354154820L; /* - * + * */ private final String code; /** - * * @param code * @param message; */ @@ -32,7 +31,6 @@ public AuthException(String code, String message) { } /** - * * @return */ public String getCode() { diff --git a/src/main/java/it/pagopa/swclient/mil/auth/util/KeyPairUtil.java b/src/main/java/it/pagopa/swclient/mil/auth/util/KeyPairUtil.java deleted file mode 100644 index df4cd5b0..00000000 --- a/src/main/java/it/pagopa/swclient/mil/auth/util/KeyPairUtil.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * KeyPairUtil.java - * - * 27 apr 2023 - */ -package it.pagopa.swclient.mil.auth.util; - -import java.math.BigInteger; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.interfaces.RSAPublicKey; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.RSAPrivateKeySpec; -import java.security.spec.RSAPublicKeySpec; - -import com.nimbusds.jose.util.Base64URL; - -import it.pagopa.swclient.mil.auth.bean.KeyPair; -import it.pagopa.swclient.mil.auth.bean.PublicKey; - -/** - * - * @author Antonio Tarricone - */ -public class KeyPairUtil { - /** - * - */ - private KeyPairUtil() { - } - - /** - * - * @param publicKey - * @return - * @throws NoSuchAlgorithmException - * @throws InvalidKeySpecException - */ - public static RSAPublicKey getPublicKey(PublicKey publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException { - BigInteger modulus = Base64URL.from(publicKey.getN()).decodeToBigInteger(); - BigInteger exponent = Base64URL.from(publicKey.getE()).decodeToBigInteger(); - RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent); - KeyFactory factory = KeyFactory.getInstance("RSA"); - return (RSAPublicKey) (factory.generatePublic(spec)); - } - - /** - * - * @param keyPair - * @return - * @throws NoSuchAlgorithmException - * @throws InvalidKeySpecException - */ - public static PrivateKey getPrivateKey(KeyPair keyPair) throws NoSuchAlgorithmException, InvalidKeySpecException { - BigInteger modulus = Base64URL.from(keyPair.getN()).decodeToBigInteger(); - BigInteger privateExponent = Base64URL.from(keyPair.getD()).decodeToBigInteger(); - RSAPrivateKeySpec spec = new RSAPrivateKeySpec(modulus, privateExponent); - KeyFactory factory = KeyFactory.getInstance("RSA"); - return factory.generatePrivate(spec); - } -} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/util/PasswordVerifier.java b/src/main/java/it/pagopa/swclient/mil/auth/util/PasswordVerifier.java index fc5202e2..264fd891 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/util/PasswordVerifier.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/util/PasswordVerifier.java @@ -12,18 +12,16 @@ import java.util.Base64; /** - * * @author Antonio Tarricone */ public class PasswordVerifier { /** - * + * */ private PasswordVerifier() { } /** - * * @param password * @param salt * @param hash @@ -37,13 +35,12 @@ public static boolean verify(String password, String salt, String hash) throws N } /** - * * @param password * @param salt * @return * @throws NoSuchAlgorithmException */ - private static byte[] hashBytes(String password, String salt) throws NoSuchAlgorithmException { + public static byte[] hashBytes(String password, String salt) throws NoSuchAlgorithmException { byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8); byte[] saltBytes = Base64.getDecoder().decode(salt); @@ -54,16 +51,4 @@ private static byte[] hashBytes(String password, String salt) throws NoSuchAlgor MessageDigest digest = MessageDigest.getInstance("SHA256"); return digest.digest(data); } - - /** - * - * @param password - * @param salt - * @return - * @throws NoSuchAlgorithmException - */ - public static String hash(String password, String salt) throws NoSuchAlgorithmException { - byte[] hashBytes = hashBytes(password, salt); - return Base64.getEncoder().encodeToString(hashBytes); - } } \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/util/TokenGenerator.java b/src/main/java/it/pagopa/swclient/mil/auth/util/TokenGenerator.java deleted file mode 100644 index bd98dc14..00000000 --- a/src/main/java/it/pagopa/swclient/mil/auth/util/TokenGenerator.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * TokenGenerator.java - * - * 28 apr 2023 - */ -package it.pagopa.swclient.mil.auth.util; - -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; -import java.util.Date; -import java.util.List; - -import com.nimbusds.jose.JOSEException; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; - -import io.quarkus.logging.Log; -import it.pagopa.swclient.mil.auth.bean.KeyPair; - -/** - * - * @author Anis Lucidi & Antonio Tarricone - */ -public class TokenGenerator { - /** - * - */ - private TokenGenerator() { - } - - /** - * - * @param strings - * @return - */ - private static String concat(List strings) { - if (strings == null) { - return null; - } - StringBuilder buffer = new StringBuilder(); - strings.forEach(x -> { - buffer.append(x); - buffer.append(" "); - }); - return buffer.toString().trim(); - } - - /** - * - * @param acquirerId - * @param channel - * @param merchantId - * @param clientId - * @param terminalId - * @param duration - * @param roles - * @param scopes - * @param key - * @return - * @throws NoSuchAlgorithmException - * @throws InvalidKeySpecException - * @throws JOSEException - */ - public static String generate(String acquirerId, String channel, String merchantId, String clientId, String terminalId, long duration, List roles, List scopes, KeyPair key) throws NoSuchAlgorithmException, InvalidKeySpecException, JOSEException { - Date now = new Date(); - JWTClaimsSet payload = new JWTClaimsSet.Builder() - .subject(clientId) - .issueTime(now) - .expirationTime(new Date(now.getTime() + duration * 1000)) - .claim("acquirerId", acquirerId) - .claim("channel", channel) - .claim("merchantId", merchantId) - .claim("clientId", clientId) - .claim("terminalId", terminalId) - .claim("scope", concat(scopes)) - .claim("groups", roles) - .build(); - Log.debug("Token signing."); - SignedJWT token = TokenSigner.sign(key, payload); - return token.serialize(); - } -} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/util/TokenSigner.java b/src/main/java/it/pagopa/swclient/mil/auth/util/TokenSigner.java deleted file mode 100644 index bbe58f69..00000000 --- a/src/main/java/it/pagopa/swclient/mil/auth/util/TokenSigner.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * TokenSigner.java - * - * 28 apr 2023 - */ -package it.pagopa.swclient.mil.auth.util; - -import static it.pagopa.swclient.mil.auth.util.KeyPairUtil.getPrivateKey; - -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; - -import com.nimbusds.jose.JOSEException; -import com.nimbusds.jose.JWSAlgorithm; -import com.nimbusds.jose.JWSHeader; -import com.nimbusds.jose.JWSSigner; -import com.nimbusds.jose.crypto.RSASSASigner; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; - -import it.pagopa.swclient.mil.auth.bean.KeyPair; - -/** - * - * @author Antonio Tarricone - */ -public class TokenSigner { - /** - * - */ - private TokenSigner() { - } - - /** - * - * @param key - * @param payload - * @return - * @throws NoSuchAlgorithmException - * @throws InvalidKeySpecException - * @throws JOSEException - */ - public static SignedJWT sign(KeyPair key, JWTClaimsSet payload) throws NoSuchAlgorithmException, InvalidKeySpecException, JOSEException { - JWSHeader header = new JWSHeader(JWSAlgorithm.RS256, null, null, null, null, null, null, null, null, null, key.getKid(), true, null, null); - SignedJWT token = new SignedJWT(header, payload); - JWSSigner signer = new RSASSASigner(getPrivateKey(key)); - token.sign(signer); - return token; - } -} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/util/UniGenerator.java b/src/main/java/it/pagopa/swclient/mil/auth/util/UniGenerator.java index fc48b4d9..dc70d939 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/util/UniGenerator.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/util/UniGenerator.java @@ -8,19 +8,18 @@ import io.smallrye.mutiny.Uni; /** - * * @author Antonio Tarricone */ public class UniGenerator { /** - * + * */ private UniGenerator() { } /** * To be used if a check fails. - * + * * @param * @param code * @param message @@ -32,7 +31,7 @@ public static Uni exception(String code, String message) { /** * To be used if an application error occurs. - * + * * @param * @param code * @param message @@ -43,7 +42,6 @@ public static Uni error(String code, String message) { } /** - * * @param * @param t * @return @@ -53,7 +51,6 @@ public static Uni item(T t) { } /** - * * @return */ public static Uni voidItem() { diff --git a/src/main/java/it/pagopa/swclient/mil/auth/validation/constraints/ValidationTarget.java b/src/main/java/it/pagopa/swclient/mil/auth/validation/constraints/ValidationTarget.java index 2754302d..7441a3e7 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/validation/constraints/ValidationTarget.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/validation/constraints/ValidationTarget.java @@ -5,23 +5,21 @@ */ package it.pagopa.swclient.mil.auth.validation.constraints; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import jakarta.validation.Constraint; import jakarta.validation.Payload; /** - * * @author Antonio Tarricone */ @Documented -@Retention(RUNTIME) -@Target(TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) @Constraint(validatedBy = { Validator.class }) diff --git a/src/main/java/it/pagopa/swclient/mil/auth/validation/constraints/Validator.java b/src/main/java/it/pagopa/swclient/mil/auth/validation/constraints/Validator.java index dce4c8e0..bc191e4f 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/validation/constraints/Validator.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/validation/constraints/Validator.java @@ -5,34 +5,29 @@ */ package it.pagopa.swclient.mil.auth.validation.constraints; -import static it.pagopa.swclient.mil.auth.bean.GrantType.CLIENT_CREDENTIALS; -import static it.pagopa.swclient.mil.auth.bean.GrantType.PASSWORD; -import static it.pagopa.swclient.mil.auth.bean.GrantType.POYNT_TOKEN; -import static it.pagopa.swclient.mil.auth.bean.GrantType.REFRESH_TOKEN; -import static it.pagopa.swclient.mil.bean.Channel.ATM; -import static it.pagopa.swclient.mil.bean.Channel.POS; - import java.util.HashMap; import java.util.Map; import io.quarkus.logging.Log; -import it.pagopa.swclient.mil.auth.bean.GetAccessToken; +import it.pagopa.swclient.mil.auth.bean.GetAccessTokenRequest; +import it.pagopa.swclient.mil.auth.bean.GrantType; +import it.pagopa.swclient.mil.bean.Channel; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; /** - * * @author Antonio Tarricone */ -public class Validator implements ConstraintValidator { +public class Validator implements ConstraintValidator { /* - * + * */ private static final Map VALIDATORS = new HashMap<>(); + static { - VALIDATORS.put(PASSWORD + "/" + POS, new Verifier() { + VALIDATORS.put(GrantType.PASSWORD + "/" + Channel.POS, new Verifier() { @Override - public boolean test(GetAccessToken getAccessToken) { + public boolean test(GetAccessTokenRequest getAccessToken) { return acquirerIdMustNotBeNull(getAccessToken) && merchantIdMustNotBeNull(getAccessToken) && terminalIdMustNotBeNull(getAccessToken) @@ -45,9 +40,9 @@ && usernameMustNotBeNull(getAccessToken) } }); - VALIDATORS.put(REFRESH_TOKEN + "/" + POS, new Verifier() { + VALIDATORS.put(GrantType.REFRESH_TOKEN + "/" + Channel.POS, new Verifier() { @Override - public boolean test(GetAccessToken getAccessToken) { + public boolean test(GetAccessTokenRequest getAccessToken) { return acquirerIdMustNotBeNull(getAccessToken) && merchantIdMustNotBeNull(getAccessToken) && terminalIdMustNotBeNull(getAccessToken) @@ -57,14 +52,14 @@ && addDataMustBeNull(getAccessToken) && refreshTokenMustNotBeNull(getAccessToken) && usernameMustBeNull(getAccessToken) && passwordMustBeNull(getAccessToken) - && scopedMustBeNull(getAccessToken); + && scopeMustBeNull(getAccessToken); } }); - VALIDATORS.put(POYNT_TOKEN + "/" + POS, new Verifier() { + VALIDATORS.put(GrantType.POYNT_TOKEN + "/" + Channel.POS, new Verifier() { @Override - public boolean test(GetAccessToken getAccessToken) { + public boolean test(GetAccessTokenRequest getAccessToken) { return acquirerIdMustNotBeNull(getAccessToken) && merchantIdMustNotBeNull(getAccessToken) && terminalIdMustNotBeNull(getAccessToken) @@ -77,9 +72,9 @@ && usernameMustBeNull(getAccessToken) } }); - VALIDATORS.put(CLIENT_CREDENTIALS + "/" + ATM, new Verifier() { + VALIDATORS.put(GrantType.CLIENT_CREDENTIALS + "/" + Channel.ATM, new Verifier() { @Override - public boolean test(GetAccessToken getAccessToken) { + public boolean test(GetAccessTokenRequest getAccessToken) { return acquirerIdMustNotBeNull(getAccessToken) && merchantIdMustBeNull(getAccessToken) && terminalIdMustNotBeNull(getAccessToken) @@ -89,13 +84,13 @@ && addDataMustBeNull(getAccessToken) && refreshTokenMustBeNull(getAccessToken) && usernameMustBeNull(getAccessToken) && passwordMustBeNull(getAccessToken) - && scopedMustBeNull(getAccessToken); + && scopeMustBeNull(getAccessToken); } }); - VALIDATORS.put(CLIENT_CREDENTIALS + "/" + POS, new Verifier() { + VALIDATORS.put(GrantType.CLIENT_CREDENTIALS + "/" + Channel.POS, new Verifier() { @Override - public boolean test(GetAccessToken getAccessToken) { + public boolean test(GetAccessTokenRequest getAccessToken) { return acquirerIdMustNotBeNull(getAccessToken) && merchantIdMustNotBeNull(getAccessToken) && terminalIdMustNotBeNull(getAccessToken) @@ -105,13 +100,13 @@ && addDataMustBeNull(getAccessToken) && refreshTokenMustBeNull(getAccessToken) && usernameMustBeNull(getAccessToken) && passwordMustBeNull(getAccessToken) - && scopedMustBeNull(getAccessToken); + && scopeMustBeNull(getAccessToken); } }); - VALIDATORS.put(CLIENT_CREDENTIALS + "/null", new Verifier() { + VALIDATORS.put(GrantType.CLIENT_CREDENTIALS + "/null", new Verifier() { @Override - public boolean test(GetAccessToken getAccessToken) { + public boolean test(GetAccessTokenRequest getAccessToken) { return acquirerIdMustBeNull(getAccessToken) && merchantIdMustBeNull(getAccessToken) && terminalIdMustBeNull(getAccessToken) @@ -121,7 +116,7 @@ && addDataMustBeNull(getAccessToken) && refreshTokenMustBeNull(getAccessToken) && usernameMustBeNull(getAccessToken) && passwordMustBeNull(getAccessToken) - && scopedMustBeNull(getAccessToken); + && scopeMustBeNull(getAccessToken); } }); } @@ -130,10 +125,10 @@ && passwordMustBeNull(getAccessToken) * @see jakarta.validation.ConstraintValidator#isValid(Object, ConstraintValidatorContext) */ @Override - public boolean isValid(GetAccessToken getAccessToken, ConstraintValidatorContext context) { + public boolean isValid(GetAccessTokenRequest getAccessToken, ConstraintValidatorContext context) { return VALIDATORS.getOrDefault(getAccessToken.getGrantType() + "/" + getAccessToken.getChannel(), new Verifier() { @Override - public boolean test(GetAccessToken t) { + public boolean test(GetAccessTokenRequest t) { Log.warn("Default validator in use."); return false; } diff --git a/src/main/java/it/pagopa/swclient/mil/auth/validation/constraints/Verifier.java b/src/main/java/it/pagopa/swclient/mil/auth/validation/constraints/Verifier.java index a883c287..6e25d729 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/validation/constraints/Verifier.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/validation/constraints/Verifier.java @@ -8,19 +8,17 @@ import java.util.function.Predicate; import io.quarkus.logging.Log; -import it.pagopa.swclient.mil.auth.bean.GetAccessToken; +import it.pagopa.swclient.mil.auth.bean.GetAccessTokenRequest; /** - * * @author Antonio Tarricone */ -public abstract class Verifier implements Predicate { +public abstract class Verifier implements Predicate { /** - * * @param getAccessToken * @return */ - protected boolean acquirerIdMustBeNull(GetAccessToken getAccessToken) { + protected boolean acquirerIdMustBeNull(GetAccessTokenRequest getAccessToken) { boolean check = getAccessToken.getAcquirerId() == null; if (!check) { Log.warn("AcquirerId must be null."); @@ -29,11 +27,10 @@ protected boolean acquirerIdMustBeNull(GetAccessToken getAccessToken) { } /** - * * @param getAccessToken * @return */ - protected boolean acquirerIdMustNotBeNull(GetAccessToken getAccessToken) { + protected boolean acquirerIdMustNotBeNull(GetAccessTokenRequest getAccessToken) { boolean check = getAccessToken.getAcquirerId() != null; if (!check) { Log.warn("AcquirerId must not be null."); @@ -42,11 +39,10 @@ protected boolean acquirerIdMustNotBeNull(GetAccessToken getAccessToken) { } /** - * * @param getAccessToken * @return */ - protected boolean merchantIdMustBeNull(GetAccessToken getAccessToken) { + protected boolean merchantIdMustBeNull(GetAccessTokenRequest getAccessToken) { boolean check = getAccessToken.getMerchantId() == null; if (!check) { Log.warn("MerchantId must be null."); @@ -55,11 +51,10 @@ protected boolean merchantIdMustBeNull(GetAccessToken getAccessToken) { } /** - * * @param getAccessToken * @return */ - protected boolean merchantIdMustNotBeNull(GetAccessToken getAccessToken) { + protected boolean merchantIdMustNotBeNull(GetAccessTokenRequest getAccessToken) { boolean check = getAccessToken.getMerchantId() != null; if (!check) { Log.warn("MerchantId must not be null."); @@ -68,11 +63,10 @@ protected boolean merchantIdMustNotBeNull(GetAccessToken getAccessToken) { } /** - * * @param getAccessToken * @return */ - protected boolean terminalIdMustBeNull(GetAccessToken getAccessToken) { + protected boolean terminalIdMustBeNull(GetAccessTokenRequest getAccessToken) { boolean check = getAccessToken.getTerminalId() == null; if (!check) { Log.warn("TerminalId must be null."); @@ -81,11 +75,10 @@ protected boolean terminalIdMustBeNull(GetAccessToken getAccessToken) { } /** - * * @param getAccessToken * @return */ - protected boolean terminalIdMustNotBeNull(GetAccessToken getAccessToken) { + protected boolean terminalIdMustNotBeNull(GetAccessTokenRequest getAccessToken) { boolean check = getAccessToken.getTerminalId() != null; if (!check) { Log.warn("TerminalId must not be null."); @@ -94,11 +87,10 @@ protected boolean terminalIdMustNotBeNull(GetAccessToken getAccessToken) { } /** - * * @param getAccessToken * @return */ - protected boolean clientSecretMustBeNull(GetAccessToken getAccessToken) { + protected boolean clientSecretMustBeNull(GetAccessTokenRequest getAccessToken) { boolean check = getAccessToken.getClientSecret() == null; if (!check) { Log.warn("client_secret must be null."); @@ -107,11 +99,10 @@ protected boolean clientSecretMustBeNull(GetAccessToken getAccessToken) { } /** - * * @param getAccessToken * @return */ - protected boolean clientSecretMustNotBeNull(GetAccessToken getAccessToken) { + protected boolean clientSecretMustNotBeNull(GetAccessTokenRequest getAccessToken) { boolean check = getAccessToken.getClientSecret() != null; if (!check) { Log.warn("client_secret must not be null."); @@ -120,11 +111,10 @@ protected boolean clientSecretMustNotBeNull(GetAccessToken getAccessToken) { } /** - * * @param getAccessToken * @return */ - protected boolean extTokenMustBeNull(GetAccessToken getAccessToken) { + protected boolean extTokenMustBeNull(GetAccessTokenRequest getAccessToken) { boolean check = getAccessToken.getExtToken() == null; if (!check) { Log.warn("ext_token must be null."); @@ -133,11 +123,10 @@ protected boolean extTokenMustBeNull(GetAccessToken getAccessToken) { } /** - * * @param getAccessToken * @return */ - protected boolean extTokenMustNotBeNull(GetAccessToken getAccessToken) { + protected boolean extTokenMustNotBeNull(GetAccessTokenRequest getAccessToken) { boolean check = getAccessToken.getExtToken() != null; if (!check) { Log.warn("ext_token must not be null."); @@ -146,11 +135,10 @@ protected boolean extTokenMustNotBeNull(GetAccessToken getAccessToken) { } /** - * * @param getAccessToken * @return */ - protected boolean addDataMustBeNull(GetAccessToken getAccessToken) { + protected boolean addDataMustBeNull(GetAccessTokenRequest getAccessToken) { boolean check = getAccessToken.getAddData() == null; if (!check) { Log.warn("add_data must be null."); @@ -159,11 +147,10 @@ protected boolean addDataMustBeNull(GetAccessToken getAccessToken) { } /** - * * @param getAccessToken * @return */ - protected boolean addDataMustNotBeNull(GetAccessToken getAccessToken) { + protected boolean addDataMustNotBeNull(GetAccessTokenRequest getAccessToken) { boolean check = getAccessToken.getAddData() != null; if (!check) { Log.warn("add_data must not be null."); @@ -172,11 +159,10 @@ protected boolean addDataMustNotBeNull(GetAccessToken getAccessToken) { } /** - * * @param getAccessToken * @return */ - protected boolean refreshTokenMustBeNull(GetAccessToken getAccessToken) { + protected boolean refreshTokenMustBeNull(GetAccessTokenRequest getAccessToken) { boolean check = getAccessToken.getRefreshToken() == null; if (!check) { Log.warn("refresh_token must be null."); @@ -185,11 +171,10 @@ protected boolean refreshTokenMustBeNull(GetAccessToken getAccessToken) { } /** - * * @param getAccessToken * @return */ - protected boolean refreshTokenMustNotBeNull(GetAccessToken getAccessToken) { + protected boolean refreshTokenMustNotBeNull(GetAccessTokenRequest getAccessToken) { boolean check = getAccessToken.getRefreshToken() != null; if (!check) { Log.warn("refresh_token must not be null."); @@ -198,11 +183,10 @@ protected boolean refreshTokenMustNotBeNull(GetAccessToken getAccessToken) { } /** - * * @param getAccessToken * @return */ - protected boolean usernameMustBeNull(GetAccessToken getAccessToken) { + protected boolean usernameMustBeNull(GetAccessTokenRequest getAccessToken) { boolean check = getAccessToken.getUsername() == null; if (!check) { Log.warn("username must be null."); @@ -211,11 +195,10 @@ protected boolean usernameMustBeNull(GetAccessToken getAccessToken) { } /** - * * @param getAccessToken * @return */ - protected boolean usernameMustNotBeNull(GetAccessToken getAccessToken) { + protected boolean usernameMustNotBeNull(GetAccessTokenRequest getAccessToken) { boolean check = getAccessToken.getUsername() != null; if (!check) { Log.warn("username must not be null."); @@ -224,11 +207,10 @@ protected boolean usernameMustNotBeNull(GetAccessToken getAccessToken) { } /** - * * @param getAccessToken * @return */ - protected boolean passwordMustBeNull(GetAccessToken getAccessToken) { + protected boolean passwordMustBeNull(GetAccessTokenRequest getAccessToken) { boolean check = getAccessToken.getPassword() == null; if (!check) { Log.warn("password must be null."); @@ -237,11 +219,10 @@ protected boolean passwordMustBeNull(GetAccessToken getAccessToken) { } /** - * * @param getAccessToken * @return */ - protected boolean passwordMustNotBeNull(GetAccessToken getAccessToken) { + protected boolean passwordMustNotBeNull(GetAccessTokenRequest getAccessToken) { boolean check = getAccessToken.getPassword() != null; if (!check) { Log.warn("password must not be null."); @@ -250,11 +231,10 @@ protected boolean passwordMustNotBeNull(GetAccessToken getAccessToken) { } /** - * * @param getAccessToken * @return */ - protected boolean scopedMustBeNull(GetAccessToken getAccessToken) { + protected boolean scopeMustBeNull(GetAccessTokenRequest getAccessToken) { boolean check = getAccessToken.getScope() == null; if (!check) { Log.warn("scope must be null."); diff --git a/src/main/resources/META-INF/openapi.yaml b/src/main/resources/META-INF/openapi.yaml index 0192ef50..930c3bab 100644 --- a/src/main/resources/META-INF/openapi.yaml +++ b/src/main/resources/META-INF/openapi.yaml @@ -146,9 +146,9 @@ components: example: "28405fHfk73x88D" Modulus: - description: Modulus + description: Modulus (Base64/RFC4648 URL safe) type: string - pattern: "^[a-zA-Z0-9_-]{2,1365}$" + pattern: "^[a-zA-Z0-9_-]{2,1368}$" example: "qjcVEWJTTySeKxHsJSsmVGk2cEvXJ4tBC4uyU5MxYwBAiIWuZb_yDOIjLz7JN8QsJs3QrZtS3vqv18ljW2db6ED90OUo9CVJveSF4eNRozDHOvnHGT0HR-8Wf5GxcNy63zfQLrnfdp5F9TrhMFRMkEA0TCT7PhT3yF6YvwLtQyMciER1_KKnpGomfAkW-UpaF2nHfXiFPrOIHMuNb5BoRR1f0349tqloLgLd7vyMy1jg-BldmEgRV1bcFqjH0Cg3leROjDy9HzdFauRIlSb4VZrqNni2hgaTUHI5Xp7aCwpS9Y_mf19KpxN0_8d-f3UVRlwtI1dryelpdC5jowxia2Pf8UgSZyMs2ZxDf6eU0SH8wHEvMpeFpwmiBD1XcsISoTan0Yv7w_CLo6JOqX6EfogDQZUBzKKlVCZSoSinAz0_7Bj2orgWKQ9sbfgJWgJweKkJLH-bNSRaVcu02boxPnlJeay3wROhSAgtiKWZnsU1_FpPNG0JBFCh_x-VjkuBoREpNEyJM5NvhRCmyObtzocS4eCtAgvmo3EFv_Xa-rp0p5ez4A-_QUb5OsYOswqYbIV1GbtiAfCTOrNbv6K86LaTllZ9WqYrKgDv7KA-604K37k33LHROqcO9Q-bCN8hKzQDWs7M3DFNP6P5iBUUVs-gtWncHvIuUWTth-fBXa8" OfflineAccessScope: @@ -178,7 +178,7 @@ components: example: 234 PublicExponent: - description: Public key exponent + description: Public key exponent (Base64/RFC4648 URL safe) type: string pattern: "^[a-zA-Z0-9_-]{2,1365}$" example: "AQAB" diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 6d834dca..b9c6fa64 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -17,25 +17,21 @@ quarkus.log.console.format=%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{requestId}] [%p] [%c{ %dev.quarkus.log.category."org.jboss.resteasy.reactive.client.logging".level=DEBUG %dev.quarkus.log.console.json=false +%test.quarkus.rest-client.logging.scope=all +%test.quarkus.rest-client.logging.body-limit=32768 +%test.quarkus.log.category."org.jboss.resteasy.reactive.client.logging".level=DEBUG + %test.quarkus.log.level=ERROR %test.quarkus.log.category."it.pagopa.swclient.mil.auth".level=DEBUG -%test.quarkus.log.console.json=false %prod.quarkus.log.level=${auth.quarkus-log-level} %prod.quarkus.log.category."it.pagopa.swclient.mil.auth".level=${auth.app-log-level} -%prod.quarkus.log.console.json=${auth.json-logging} - -# ------------------------------------------------------------------------------ -# Quarkus reactive Redis client configuration -# ------------------------------------------------------------------------------ -%test.quarkus.redis.hosts=redis://localhost:6379 -%prod.quarkus.redis.hosts=${auth.redis-connection-string} # ------------------------------------------------------------------------------ -# Cryptoperiod of RSA keys in millis (86400000ms = 1d) +# Cryptoperiod of RSA keys in seconds (86400s = 1d) # ------------------------------------------------------------------------------ -%dev.cryptoperiod=86400000 -%test.cryptoperiod=86400000 +%dev.cryptoperiod=86400 +%test.cryptoperiod=86400 %prod.cryptoperiod=${auth.cryptoperiod} # ------------------------------------------------------------------------------ @@ -73,4 +69,28 @@ poynt-api.version=1.2 # TTL for the authorization data cache # %test.quarkus.cache.enabled=false -quarkus.cache.caffeine.expire-after-write=1h \ No newline at end of file +quarkus.cache.caffeine.expire-after-write=1h + +# +# Azure Auth API +# +quarkus.rest-client.azure-auth-api.url=https://login.microsoftonline.com + +%dev.azure-auth-api.tenant-id=${AZURE_TENANT_ID} +%dev.azure-auth-api.client-id=${AZURE_CLIENT_ID} +%dev.azure-auth-api.client-secret=${AZURE_CLIENT_SECRET} + +%test.azure-auth-api.tenant-id=dummy +%test.azure-auth-api.client-id=dummy +%test.azure-auth-api.client-secret=dummy + +%prod.azure-auth-api.tenant-id=${azure.tenant.id} +%prod.azure-auth-api.client-id=${azure.client.id} +%prod.azure-auth-api.client-secret=${azure.client.secret} + +# +# Azure Key Vault API +# +%dev.quarkus.rest-client.azure-key-vault-api.url=https://mil-d-appl-kv.vault.azure.net/ +%test.quarkus.rest-client.azure-key-vault-api.url=https://mil-d-appl-kv.vault.azure.net/ +%prod.quarkus.rest-client.azure-key-vault-api.url=${auth.keyvault.url} diff --git a/src/test/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyNameAndVersionTest.java b/src/test/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyNameAndVersionTest.java new file mode 100644 index 00000000..5aa8a959 --- /dev/null +++ b/src/test/java/it/pagopa/swclient/mil/auth/azurekeyvault/bean/KeyNameAndVersionTest.java @@ -0,0 +1,39 @@ +/* + * KeyNameAndVersionTest.java + * + * 14 set 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.bean; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +/** + * @author Antonio Tarricone + */ +@QuarkusTest +class KeyNameAndVersionTest { + @Test + void testIsValid1() { + assertFalse(new KeyNameAndVersion(null, null).isValid()); + } + + @Test + void testIsValid2() { + assertFalse(new KeyNameAndVersion("", null).isValid()); + } + + @Test + void testIsValid3() { + assertFalse(new KeyNameAndVersion(null, "").isValid()); + } + + @Test + void testIsValid4() { + assertTrue(new KeyNameAndVersion("", "").isValid()); + } +} \ No newline at end of file diff --git a/src/test/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureKeyFinderTest.java b/src/test/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureKeyFinderTest.java new file mode 100644 index 00000000..ba7aab97 --- /dev/null +++ b/src/test/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureKeyFinderTest.java @@ -0,0 +1,1249 @@ +/* + * AzureKeyFinderTest.java + * + * 28 lug 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; + +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; + +import io.quarkus.test.InjectMock; +import io.quarkus.test.junit.QuarkusTest; +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.helpers.test.UniAssertSubscriber; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.CreateKeyRequest; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.CreateKeyResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetAccessTokenResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeyResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeyVersionsResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeysResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.Key; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.KeyAttributes; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.KeyDetails; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.KeyVersion; +import it.pagopa.swclient.mil.auth.azurekeyvault.client.AzureAuthClient; +import it.pagopa.swclient.mil.auth.azurekeyvault.client.AzureKeyVaultClient; +import it.pagopa.swclient.mil.auth.bean.KeyType; +import it.pagopa.swclient.mil.auth.bean.KeyUse; +import it.pagopa.swclient.mil.auth.bean.PublicKey; +import it.pagopa.swclient.mil.auth.bean.PublicKeys; +import it.pagopa.swclient.mil.auth.util.AuthError; +import jakarta.inject.Inject; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.Response.Status; + +/** + * @author Antonio Tarricone + */ +@QuarkusTest +@TestInstance(Lifecycle.PER_CLASS) +class AzureKeyFinderTest { + /* + * + */ + private static final String K1 = "0709643f49394529b92c19a68c8e184a"; + private static final String K1_V2 = "82f646ef6bd44b5db8b95d76bb151926"; + private static final String K1_V3 = "4171620a6a8d4f93a1622f95dc7f1ada"; + private static final String K1_V4 = "e400f109f7ab4c2e9a28916d40ed1925"; + private static final String K1_V5 = "9df5a14b79c147358f6a3626ea63b8a8"; + private static final String K1_V7 = "6e1795083aec4467adfd0a5beb05ec06"; + private static final String K1_V8 = "68afc030808c4bb69b15ff5dcdfb7c3c"; + private static final String K1_V9 = "eeb5eb71fb7d46eca705601dd6c09426"; + private static final String K1_V10 = "c47b20cdeba643b59a5b038cb8270c89"; + private static final String K1_V11 = "6581c704deda4979943c3b34468df7c2"; // Valid key version with the greatest expiration. + private static final String K1_V12 = "4a6b1ee949ad40c28f85493328952691"; + private static final String K2 = "10b33e516ee3470cbf5edc9ace919450"; + private static final String K2_V1 = "6e6075cfe78948f79dc7cbe0b58ec14d"; + private static final String K2_V2 = "6e6075cfe78948f79dc7cbe0b58ec14d"; + private static final String K2_V3 = "b59f1231befc4e3cbd5f500a287e290a"; + private static final String K2_V4 = "90630f81e7244cdb9508969fc9aeb372"; + private static final String K2_V5 = "2aef3230ed4147238f3e993f626dc2e7"; + private static final String K2_V7 = "3abba745219d4ce2a45b70321646928f"; + private static final String K2_V8 = "9af4b74a9c4f4cea93abb5574fb5a884"; + private static final String K2_V9 = "fa541b9da1794558982a5c4d9e55a17a"; + private static final String K2_V10 = "a14a9fc70e714ab4b48e6b9baf761b2d"; // Valid key version. + private static final String K3 = "ed2dae1359334729a37e4cafb5bc1e11"; + private static final String K4 = "fc3db9157e92403db23fd12074730a0b"; + /* + * + */ + private static final String KEY_URL = "https://mil-d-appl-kv.vault.azure.net/keys/"; + /* + * + */ + private static final String AZURE_ACCESS_TOKEN = "this_is_the_token"; + private static final String AUTHORIZATION_HDR_VALUE = "Bearer " + AZURE_ACCESS_TOKEN; + /* + * + */ + @InjectMock + @RestClient + AzureAuthClient authClient; + /* + * + */ + @InjectMock + @RestClient + AzureKeyVaultClient keyVaultClient; + /* + * + */ + @Inject + AzureKeyFinder azureKeyFinder; + /* + * + */ + private long now; + /* + * + */ + private Key keyWithKidWithoutName1; + private Key keyWithKidWithoutName2; + private Key keyWithValidKid1; + private Key keyWithValidKid2; + private Key keyWithValidKidWithoutVersions; + + /* + * + */ + private KeyVersion nullVersionK1V1; + private KeyVersion versionWithNullEnabledAttributeK1V2; + private KeyVersion versionWithNullCreationTimestampAttributeK1V3; + private KeyVersion versionWithNullExpiredTimestampAttributeK1V4; + private KeyVersion versionWithNullNotBeforeAttributeK1V5; + private KeyVersion versionWithNullKidK1V6; + private KeyVersion versionWithNullDetailsK1V7; + private KeyVersion versionWithDetailsWithNoRsaKeyTypeK1V8; + private KeyVersion versionWithDetailsWithoutSignOpK1V9; + private KeyVersion versionWithDetailsWithoutSignAndVerifyOpK1V10; + private KeyVersion versionWithValidDetailsWithGreatestExpirationK1V11; + private KeyVersion versionWith500OnGetKeyK1V12; + + private KeyVersion versionWithNullAttributesK2V1; + private KeyVersion versionWithFalseEnabledAttributeK2V2; + private KeyVersion versionWithNotCoherentCreationTimestampAttributeK2V3; + private KeyVersion expiredVersionK2V4; + private KeyVersion versionWithUnmetNotBeforeAttributeK2V5; + private KeyVersion versionWithInvalidKidK2V6; + private KeyVersion versionWithExpiredDetailsK2V7; + private KeyVersion versionWithDetailsWithNullOpsK2V8; + private KeyVersion versionWithDetailsWithoutVerifyOpK2V9; + private KeyVersion versionWithValidDetailsK2V10; + + /* + * + */ + private KeyDetails detailsWithNoRsaKeyTypeK1V8; + private KeyDetails detailsWithoutSignOpK1V9; + private KeyDetails detailsWithoutSignAndVerifyOpK1V10; + private KeyDetails validDetailsWithGreatestExpirationK1V11; + + private KeyDetails expiredDetailsK2V7; + private KeyDetails detailsWithNullOpsK2V8; + private KeyDetails detailsWithoutVerifyOpK2V9; + private KeyDetails validDetailsK2V10; + private KeyDetails detailsWithBadKidK2V11; + + /** + * + */ + @BeforeAll + void setup() { + now = Instant.now().getEpochSecond(); + + /* + * Keys returned by getKeys. + */ + keyWithKidWithoutName1 = new Key( + null, + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + keyWithKidWithoutName2 = new Key( + "", + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + keyWithValidKid1 = new Key( + KEY_URL + K1, + new KeyAttributes( + now - 300, + now + 600, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + keyWithValidKid2 = new Key( + KEY_URL + K2, + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + keyWithValidKidWithoutVersions = new Key( + KEY_URL + K3, + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + /* + * Versions returned by getKeyVersions. + */ + nullVersionK1V1 = null; + + versionWithNullAttributesK2V1 = new KeyVersion( + KEY_URL + K2 + "/" + K2_V1, + null); + + versionWithNullEnabledAttributeK1V2 = new KeyVersion( + KEY_URL + K1 + "/" + K1_V2, + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + null, + "Purgeable", + 0, + Boolean.FALSE)); + + versionWithFalseEnabledAttributeK2V2 = new KeyVersion( + KEY_URL + K2 + "/" + K2_V2, + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.FALSE, + "Purgeable", + 0, + Boolean.FALSE)); + + versionWithNullCreationTimestampAttributeK1V3 = new KeyVersion( + KEY_URL + K1 + "/" + K1_V3, + new KeyAttributes( + null, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + versionWithNotCoherentCreationTimestampAttributeK2V3 = new KeyVersion( + KEY_URL + K2 + "/" + K2_V3, + new KeyAttributes( + now + 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + versionWithNullExpiredTimestampAttributeK1V4 = new KeyVersion( + KEY_URL + K1 + "/" + K1_V4, + new KeyAttributes( + now - 300, + null, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + expiredVersionK2V4 = new KeyVersion( + KEY_URL + K2 + "/" + K2_V4, + new KeyAttributes( + now - 300, + now - 100, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + versionWithNullNotBeforeAttributeK1V5 = new KeyVersion( + KEY_URL + K1 + "/" + K1_V5, + new KeyAttributes( + now - 300, + now + 300, + null, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + versionWithUnmetNotBeforeAttributeK2V5 = new KeyVersion( + KEY_URL + K2 + "/" + K2_V5, + new KeyAttributes( + now - 300, + now + 300, + now + 100, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + versionWithNullKidK1V6 = new KeyVersion( + null, + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + versionWithInvalidKidK2V6 = new KeyVersion( + "", + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + versionWithNullDetailsK1V7 = new KeyVersion( + KEY_URL + K1 + "/" + K1_V7, + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + versionWithExpiredDetailsK2V7 = new KeyVersion( + KEY_URL + K2 + "/" + K2_V7, + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + versionWithDetailsWithNoRsaKeyTypeK1V8 = new KeyVersion( + KEY_URL + K1 + "/" + K1_V8, + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + versionWithDetailsWithNullOpsK2V8 = new KeyVersion( + KEY_URL + K2 + "/" + K2_V8, + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + versionWithDetailsWithoutSignOpK1V9 = new KeyVersion( + KEY_URL + K1 + "/" + K1_V9, + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + versionWithDetailsWithoutVerifyOpK2V9 = new KeyVersion( + KEY_URL + K2 + "/" + K2_V9, + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + versionWithDetailsWithoutSignAndVerifyOpK1V10 = new KeyVersion( + KEY_URL + K1 + "/" + K1_V10, + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + versionWithValidDetailsK2V10 = new KeyVersion( + KEY_URL + K2 + "/" + K2_V10, + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + versionWithValidDetailsWithGreatestExpirationK1V11 = new KeyVersion( + KEY_URL + K1 + "/" + K1_V11, + new KeyAttributes( + now - 300, + now + 600, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + versionWith500OnGetKeyK1V12 = new KeyVersion( + KEY_URL + K1 + "/" + K1_V12, + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + versionWith500OnGetKeyK1V12 = new KeyVersion( + KEY_URL + K1 + "/" + K1_V12, + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + /* + * Details returned by getKey. + */ + expiredDetailsK2V7 = new KeyDetails( + KEY_URL + K2 + "/" + K2_V7, + "RSA", + new String[] { + "sign", "verify" + }, + "this_is_the_modulus", + "this_is_the_exponent", + new KeyAttributes( + now - 300, + now - 100, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + detailsWithNoRsaKeyTypeK1V8 = new KeyDetails( + KEY_URL + K1 + "/" + K1_V8, + "non-RSA", + new String[] { + "sign", "verify" + }, + "this_is_the_modulus", + "this_is_the_exponent", + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + detailsWithNullOpsK2V8 = new KeyDetails( + KEY_URL + K2 + "/" + K2_V8, + "RSA", + null, + "this_is_the_modulus", + "this_is_the_exponent", + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + detailsWithoutSignOpK1V9 = new KeyDetails( + KEY_URL + K1 + "/" + K1_V9, + "RSA", + new String[] { + "verify" + }, + "this_is_the_modulus", + "this_is_the_exponent", + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + detailsWithoutVerifyOpK2V9 = new KeyDetails( + KEY_URL + K2 + "/" + K2_V9, + "RSA", + new String[] { + "sign" + }, + "this_is_the_modulus", + "this_is_the_exponent", + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + detailsWithoutSignAndVerifyOpK1V10 = new KeyDetails( + KEY_URL + K1 + "/" + K1_V10, + "RSA", + new String[] {}, + "this_is_the_modulus", + "this_is_the_exponent", + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + validDetailsK2V10 = new KeyDetails( + KEY_URL + K2 + "/" + K2_V10, + "RSA", + new String[] { + "verify", "sign" + }, + "this_is_the_modulus", + "this_is_the_exponent", + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + validDetailsWithGreatestExpirationK1V11 = new KeyDetails( + KEY_URL + K1 + "/" + K1_V11, + "RSA", + new String[] { + "verify", "sign" + }, + "this_is_the_modulus", + "this_is_the_exponent", + new KeyAttributes( + now - 300, + now + 600, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + detailsWithBadKidK2V11 = new KeyDetails( + "", + "RSA", + new String[] { + "verify", "sign" + }, + "this_is_the_modulus", + "this_is_the_exponent", + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + } + + /** + * + */ + private void mostCommonSetup() { + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(Uni.createFrom().item(new GetAccessTokenResponse("Bearer", 3599, 3599, AZURE_ACCESS_TOKEN))); + + when(keyVaultClient.getKeys(AUTHORIZATION_HDR_VALUE)) + .thenReturn(Uni.createFrom().item(new GetKeysResponse(new Key[]{ + keyWithKidWithoutName1, + keyWithKidWithoutName2, + keyWithValidKid1, + keyWithValidKid2, + keyWithValidKidWithoutVersions + }))); + + when(keyVaultClient.getKeyVersions(AUTHORIZATION_HDR_VALUE, K1)) + .thenReturn(Uni.createFrom().item(new GetKeyVersionsResponse(new KeyVersion[]{ + nullVersionK1V1, + versionWithNullEnabledAttributeK1V2, + versionWithNullCreationTimestampAttributeK1V3, + versionWithNullExpiredTimestampAttributeK1V4, + versionWithNullNotBeforeAttributeK1V5, + versionWithNullKidK1V6, + versionWithNullDetailsK1V7, + versionWithDetailsWithNoRsaKeyTypeK1V8, + versionWithDetailsWithoutSignOpK1V9, + versionWithDetailsWithoutSignAndVerifyOpK1V10, + versionWithValidDetailsWithGreatestExpirationK1V11 + }))); + + when(keyVaultClient.getKeyVersions(AUTHORIZATION_HDR_VALUE, K2)) + .thenReturn(Uni.createFrom().item(new GetKeyVersionsResponse(new KeyVersion[]{ + versionWithNullAttributesK2V1, + versionWithFalseEnabledAttributeK2V2, + versionWithNotCoherentCreationTimestampAttributeK2V3, + expiredVersionK2V4, + versionWithUnmetNotBeforeAttributeK2V5, + versionWithInvalidKidK2V6, + versionWithExpiredDetailsK2V7, + versionWithDetailsWithNullOpsK2V8, + versionWithDetailsWithoutVerifyOpK2V9, + versionWithValidDetailsK2V10 + }))); + + when(keyVaultClient.getKeyVersions(AUTHORIZATION_HDR_VALUE, K3)) + .thenReturn(Uni.createFrom().item(new GetKeyVersionsResponse(new KeyVersion[]{}))); + + when(keyVaultClient.getKeyVersions(AUTHORIZATION_HDR_VALUE, K4)) + .thenReturn(Uni.createFrom().item(new GetKeyVersionsResponse(null))); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, K1, K1_V7)) + .thenReturn(Uni.createFrom().item(new GetKeyResponse(null))); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, K1, K1_V8)) + .thenReturn(Uni.createFrom().item(new GetKeyResponse(detailsWithNoRsaKeyTypeK1V8))); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, K1, K1_V9)) + .thenReturn(Uni.createFrom().item(new GetKeyResponse(detailsWithoutSignOpK1V9))); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, K1, K1_V10)) + .thenReturn(Uni.createFrom().item(new GetKeyResponse(detailsWithoutSignAndVerifyOpK1V10))); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, K1, K1_V11)) + .thenReturn(Uni.createFrom().item(new GetKeyResponse(validDetailsWithGreatestExpirationK1V11))); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, K2, K2_V7)) + .thenReturn(Uni.createFrom().item(new GetKeyResponse(expiredDetailsK2V7))); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, K2, K2_V8)) + .thenReturn(Uni.createFrom().item(new GetKeyResponse(detailsWithNullOpsK2V8))); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, K2, K2_V9)) + .thenReturn(Uni.createFrom().item(new GetKeyResponse(detailsWithoutVerifyOpK2V9))); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, K2, K2_V10)) + .thenReturn(Uni.createFrom().item(new GetKeyResponse(validDetailsK2V10))); + } + + /** + * On get access token a null token string is returned. + */ + @Test + void testFindPublicKeysWithNullAccessToken() { + /* + * Setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(Uni.createFrom().item(new GetAccessTokenResponse("Bearer", 3599, 3599, null))); + + /* + * Test. + */ + azureKeyFinder.findPublicKeys() + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthError.class); + } + + /** + * On get access token a HTTP 401 is returned. + */ + @Test + void testFindPublicKeysWith401OnGetAccessToken() { + /* + * Setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Status.UNAUTHORIZED).build()))); + + /* + * Test. + */ + azureKeyFinder.findPublicKeys() + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthError.class); + } + + /** + * + */ + @Test + void testFindPublicKeysWithSomeWrongKeysAndNoHttpErrors() { + /* + * Setup. + */ + mostCommonSetup(); + + /* + * Test. + */ + PublicKey key1 = new PublicKey( + validDetailsWithGreatestExpirationK1V11.getExponent(), + KeyUse.sig, + K1 + "/" + K1_V11, + validDetailsWithGreatestExpirationK1V11.getModulus(), + KeyType.RSA, + validDetailsWithGreatestExpirationK1V11.getAttributes().getExp(), + validDetailsWithGreatestExpirationK1V11.getAttributes().getCreated()); + + PublicKey key2 = new PublicKey( + validDetailsK2V10.getExponent(), + KeyUse.sig, + K2 + "/" + K2_V10, + validDetailsK2V10.getModulus(), + KeyType.RSA, + validDetailsK2V10.getAttributes().getExp(), + validDetailsK2V10.getAttributes().getCreated()); + + PublicKeys expected = new PublicKeys(List.of(key1, key2)); + + azureKeyFinder.findPublicKeys() + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertCompleted() + .assertItem(expected); + } + + /** + * + */ + @Test + void testFindPublicKeysWithHttpError() { + /* + * Setup. + */ + mostCommonSetup(); + + when(keyVaultClient.getKeyVersions(AUTHORIZATION_HDR_VALUE, K1)) + .thenReturn(Uni.createFrom().item(new GetKeyVersionsResponse(new KeyVersion[] { + nullVersionK1V1, + versionWithNullEnabledAttributeK1V2, + versionWithNullCreationTimestampAttributeK1V3, + versionWithNullExpiredTimestampAttributeK1V4, + versionWithNullNotBeforeAttributeK1V5, + versionWithNullKidK1V6, + versionWithNullDetailsK1V7, + versionWithDetailsWithNoRsaKeyTypeK1V8, + versionWithDetailsWithoutSignOpK1V9, + versionWithDetailsWithoutSignAndVerifyOpK1V10, + versionWithValidDetailsWithGreatestExpirationK1V11, + versionWith500OnGetKeyK1V12 + }))); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, K1, K1_V12)) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Status.UNAUTHORIZED).build()))); + + /* + * Test. + */ + azureKeyFinder.findPublicKeys() + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthError.class); + } + + /** + * + */ + @Test + void testFindPublicKeysWith401OnGetKeys() { + /* + * Setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(Uni.createFrom().item(new GetAccessTokenResponse("Bearer", 3599, 3599, AZURE_ACCESS_TOKEN))); + + when(keyVaultClient.getKeys(AUTHORIZATION_HDR_VALUE)) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Status.UNAUTHORIZED).build()))); + + /* + * Test. + */ + azureKeyFinder.findPublicKeys() + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthError.class); + } + + /** + * + */ + @Test + void testFindValidPublicKeyWithGreatestExpiration() { + /* + * Setup. + */ + mostCommonSetup(); + + /* + * Test. + */ + PublicKey expected = new PublicKey( + validDetailsWithGreatestExpirationK1V11.getExponent(), + KeyUse.sig, + K1 + "/" + K1_V11, + validDetailsWithGreatestExpirationK1V11.getModulus(), + KeyType.RSA, + validDetailsWithGreatestExpirationK1V11.getAttributes().getExp(), + validDetailsWithGreatestExpirationK1V11.getAttributes().getCreated()); + + PublicKey actual = azureKeyFinder.findValidPublicKeyWithGreatestExpiration() + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertCompleted() + .getItem() + .get(); + + assertEquals(expected, actual); + } + + /** + * + */ + @Test + void testFindValidPublicKeyWithGreatestExpiration2() { + /* + * Setup. + */ + mostCommonSetup(); + + KeyDetails validDetails1 = new KeyDetails( + KEY_URL + K1 + "/" + K1_V11, + "RSA", + new String[] { + "verify", "sign" + }, + "this_is_the_modulus", + "this_is_the_exponent", + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + KeyDetails validDetails2 = new KeyDetails( + KEY_URL + K2 + "/" + K2_V10, + "RSA", + new String[] { + "verify", "sign" + }, + "this_is_the_modulus", + "this_is_the_exponent", + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, K1, K1_V11)) + .thenReturn(Uni.createFrom().item(new GetKeyResponse(validDetails1))); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, K2, K2_V10)) + .thenReturn(Uni.createFrom().item(new GetKeyResponse(validDetails2))); + + /* + * Test. + */ + PublicKey expected = new PublicKey( + validDetails1.getExponent(), + KeyUse.sig, + K1 + "/" + K1_V11, + validDetails1.getModulus(), + KeyType.RSA, + validDetails1.getAttributes().getExp(), + validDetails1.getAttributes().getCreated()); + + PublicKey actual = azureKeyFinder.findValidPublicKeyWithGreatestExpiration() + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertCompleted() + .getItem() + .get(); + + assertEquals(expected, actual); + } + + /** + * + */ + @Test + void testFindValidPublicKeyWithGreatestExpiration3() { + /* + * Setup. + */ + mostCommonSetup(); + + KeyDetails validDetails1 = new KeyDetails( + KEY_URL + K1 + "/" + K1_V11, + "RSA", + new String[] { + "verify", "sign" + }, + "this_is_the_modulus", + "this_is_the_exponent", + new KeyAttributes( + now - 300, + now + 300, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + KeyDetails validDetails2 = new KeyDetails( + KEY_URL + K2 + "/" + K2_V10, + "RSA", + new String[] { + "verify", "sign" + }, + "this_is_the_modulus", + "this_is_the_exponent", + new KeyAttributes( + now - 300, + now + 600, + now - 300, + now - 300, + Boolean.TRUE, + "Purgeable", + 0, + Boolean.FALSE)); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, K1, K1_V11)) + .thenReturn(Uni.createFrom().item(new GetKeyResponse(validDetails1))); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, K2, K2_V10)) + .thenReturn(Uni.createFrom().item(new GetKeyResponse(validDetails2))); + + /* + * Test. + */ + PublicKey expected = new PublicKey( + validDetails2.getExponent(), + KeyUse.sig, + K2 + "/" + K2_V10, + validDetails2.getModulus(), + KeyType.RSA, + validDetails2.getAttributes().getExp(), + validDetails2.getAttributes().getCreated()); + + PublicKey actual = azureKeyFinder.findValidPublicKeyWithGreatestExpiration() + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertCompleted() + .getItem() + .get(); + + assertEquals(expected, actual); + } + + /** + * + */ + @Test + void testFindValidPublicKeyWithGreatestExpirationWithNoKeys() { + /* + * Setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(Uni.createFrom().item(new GetAccessTokenResponse("Bearer", 3599, 3599, AZURE_ACCESS_TOKEN))); + + when(keyVaultClient.getKeys(AUTHORIZATION_HDR_VALUE)) + .thenReturn(Uni.createFrom().item(new GetKeysResponse(new Key[]{}))); + + when(keyVaultClient.createKey(eq(AUTHORIZATION_HDR_VALUE), anyString(), any(CreateKeyRequest.class))) + .thenReturn(Uni.createFrom().item(new CreateKeyResponse(validDetailsWithGreatestExpirationK1V11))); + + /* + * Test. + */ + azureKeyFinder.findValidPublicKeyWithGreatestExpiration() + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertCompleted(); + } + + /** + * + */ + @Test + void testFindValidPublicKeyWithGreatestExpirationWithNoKeysAndExpiredKeyIsCreated() { + /* + * Setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(Uni.createFrom().item(new GetAccessTokenResponse("Bearer", 3599, 3599, AZURE_ACCESS_TOKEN))); + + when(keyVaultClient.getKeys(AUTHORIZATION_HDR_VALUE)) + .thenReturn(Uni.createFrom().item(new GetKeysResponse(new Key[]{}))); + + when(keyVaultClient.createKey(eq(AUTHORIZATION_HDR_VALUE), anyString(), any(CreateKeyRequest.class))) + .thenReturn(Uni.createFrom().item(new CreateKeyResponse(expiredDetailsK2V7))); + + /* + * Test. + */ + azureKeyFinder.findValidPublicKeyWithGreatestExpiration() + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthError.class); + } + + /** + * + */ + @Test + void testFindValidPublicKeyWithGreatestExpirationWithNoKeysAndNonRsaKeyIsCreated() { + /* + * Setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(Uni.createFrom().item(new GetAccessTokenResponse("Bearer", 3599, 3599, AZURE_ACCESS_TOKEN))); + + when(keyVaultClient.getKeys(AUTHORIZATION_HDR_VALUE)) + .thenReturn(Uni.createFrom().item(new GetKeysResponse(new Key[]{}))); + + when(keyVaultClient.createKey(eq(AUTHORIZATION_HDR_VALUE), anyString(), any(CreateKeyRequest.class))) + .thenReturn(Uni.createFrom().item(new CreateKeyResponse(detailsWithNoRsaKeyTypeK1V8))); + + /* + * Test. + */ + azureKeyFinder.findValidPublicKeyWithGreatestExpiration() + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthError.class); + } + + /** + * + */ + @Test + void testFindValidPublicKeyWithGreatestExpirationWithNoKeysAndKeyWithBadKidIsCreated() { + /* + * Setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(Uni.createFrom().item(new GetAccessTokenResponse("Bearer", 3599, 3599, AZURE_ACCESS_TOKEN))); + + when(keyVaultClient.getKeys(AUTHORIZATION_HDR_VALUE)) + .thenReturn(Uni.createFrom().item(new GetKeysResponse(new Key[]{}))); + + when(keyVaultClient.createKey(eq(AUTHORIZATION_HDR_VALUE), anyString(), any(CreateKeyRequest.class))) + .thenReturn(Uni.createFrom().item(new CreateKeyResponse(detailsWithBadKidK2V11))); + + /* + * Test. + */ + azureKeyFinder.findValidPublicKeyWithGreatestExpiration() + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthError.class); + } + + /** + * + */ + @Test + void testFindValidPublicKeyWithGreatestExpirationWithNoKeysAndErrorOnCreation() { + /* + * Setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(Uni.createFrom().item(new GetAccessTokenResponse("Bearer", 3599, 3599, AZURE_ACCESS_TOKEN))); + + when(keyVaultClient.getKeys(AUTHORIZATION_HDR_VALUE)) + .thenReturn(Uni.createFrom().item(new GetKeysResponse(new Key[]{}))); + + + when(keyVaultClient.createKey(eq(AUTHORIZATION_HDR_VALUE), anyString(), any(CreateKeyRequest.class))) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Status.UNAUTHORIZED).build()))); + + /* + * Test. + */ + azureKeyFinder.findValidPublicKeyWithGreatestExpiration() + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthError.class); + } + + /** + * + */ + @Test + void findPublicKeyWithBadKid() { + /* + * Test. + */ + azureKeyFinder.findPublicKey("") + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertCompleted() + .assertItem(Optional.empty()); + } + + /** + * + */ + @Test + void findPublicKey() { + /* + * Setup. + */ + mostCommonSetup(); + + /* + * Test. + */ + PublicKey expected = new PublicKey( + validDetailsWithGreatestExpirationK1V11.getExponent(), + KeyUse.sig, + K1 + "/" + K1_V11, + validDetailsWithGreatestExpirationK1V11.getModulus(), + KeyType.RSA, + validDetailsWithGreatestExpirationK1V11.getAttributes().getExp(), + validDetailsWithGreatestExpirationK1V11.getAttributes().getCreated()); + + azureKeyFinder.findPublicKey(K1 + "/" + K1_V11) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertCompleted() + .assertItem(Optional.of(expected)); + } + + /** + * + */ + @Test + void findPublicKeyWithInvalidKey() { + /* + * Setup. + */ + mostCommonSetup(); + + /* + * Test. + */ + azureKeyFinder.findPublicKey(K2 + "/" + K2_V7) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertCompleted() + .assertItem(Optional.empty()); + } +} \ No newline at end of file diff --git a/src/test/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureTokenSignerTest.java b/src/test/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureTokenSignerTest.java new file mode 100644 index 00000000..fede4bbe --- /dev/null +++ b/src/test/java/it/pagopa/swclient/mil/auth/azurekeyvault/service/AzureTokenSignerTest.java @@ -0,0 +1,533 @@ +/* + * AzureTokenSignerTest.java + * + * 2 ago 2023 + */ +package it.pagopa.swclient.mil.auth.azurekeyvault.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +//import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.RSAPrivateKeySpec; +//import java.security.spec.RSAPublicKeySpec; +import java.text.ParseException; +import java.time.Instant; +import java.util.Base64; +import java.util.Date; +import java.util.List; + +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.JWSSigner; +import com.nimbusds.jose.crypto.RSASSASigner; +import com.nimbusds.jose.util.Base64URL; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; + +import io.quarkus.test.InjectMock; +import io.quarkus.test.junit.QuarkusTest; +import io.smallrye.mutiny.Context; +import io.smallrye.mutiny.ItemWithContext; +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.helpers.test.UniAssertSubscriber; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetAccessTokenResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.SignRequest; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.SignResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.VerifySignatureRequest; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.VerifySignatureResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.client.AzureAuthClient; +import it.pagopa.swclient.mil.auth.azurekeyvault.client.AzureKeyVaultClient; +import it.pagopa.swclient.mil.auth.azurekeyvault.util.SignedJWTFactory; +import it.pagopa.swclient.mil.auth.bean.KeyType; +import it.pagopa.swclient.mil.auth.bean.KeyUse; +import it.pagopa.swclient.mil.auth.service.TokenSigner; +import it.pagopa.swclient.mil.auth.util.AuthError; +import it.pagopa.swclient.mil.auth.util.AuthException; +import jakarta.inject.Inject; + +/** + * @author Antonio Tarricone + */ +@QuarkusTest +@TestInstance(Lifecycle.PER_CLASS) +class AzureTokenSignerTest { + /* + * + */ + private static final String KEY_NAME = "KEY_NAME"; + private static final String KEY_VERSION = "KEY_VERSION"; + private static final String KID = KEY_NAME + "/" + KEY_VERSION; + private static final String MODULUS = "AKnFsF5Y16TB9qkmoOyDXG3ulenUWYoW78U7mcGBoYKRpMlswxhc_ZiKcC65vIrCP6hbS5Cx88IbQG2DWH-nE329OLzUbzcdraDLR-7V2BX0nNwmwXxhkd4ofzzjKyhWjV8AkxFpqJPtFG09YCyCpaC8YluVPbHUpWJ1wrOsavdc_YM1W1XuaGvJv4SkilM8vBa81zOLEVhbEE5msHxPNLwVyC_0PIE6OFL9RY4YP1U1q7gjTMmKDc9qgEYkdziMnlxWp_EkKTZOERbEatP0fditFt-zWKlXw0qO4FKFlmj9n5tbB55vaopB71Kv6LcsAY1Q-fgOuoM41HldLppzfDOPwLGyCQF9ODJt1xaKkup6i_BxZum7-QckibwPaj3ODZbYsPuNZ_npQiR6NJZ_q_31YMlyuGdqltawluYLJidw3EzkpTN__bHdio892WbY29PRwbrG486IJ_88qP3lWs1TfzohVa1czUOZwQHqp0ixVBi_SK3jICk-V65DbwzgS5zwBFaqfWO3XVOf6tmWFMZ6ly7wtOnYWoMR15rudsD5xXWwqE-s7IP1lVZuIOdMfLH7-1Pgn-YJuPsBLbZri9_M4KtflYbqnuDckSyFNBynTwoSvSSuBhpkmNgiSQ-WBXHHss5Wy-pr-YjNK7JYppPOHvfHSY96XnJl9SPWcnwx"; + private static final String PRIVATE_EXPONENT = "IlITaUNTFtzaUVA8lIuqxhOHLW3vCv4_ixMVLnwXC0cHteudliGIZ8vGyX9laPTDezS3lkEPSuSI9gqpO6cqRs9Xtr7IW-9NQDYQLO2AoVGh21SfZVZxL2Tm8gdnnGBA9J1wXcMLIBp7uGjBtkXUF2Y2CRcm0XowU_MEASAgQLEFE_8Xn4vSgsXWiIld6F1dFcinxaT9xOul5H-Yeozll4dcwKsCh0pehBJs-wCWXxK6S_-g4JZe29lHJMbu7hjpU7f1_AcIKNEH3d8nzID-5ux49RCz4goasgonua8FXOS23Sh-Jg6WjmwtZj0nEc6c4rVlzzqlBG2a8I0ApJsnlo2RK1E-XftVNip52Bsb9jRKGNjNZP3VOgAdLg-py8HVU3sxn95yJRN6AF7S8a0Jnb6uAzxagmfZqLe1ykswBPJWPP2dyQivb59CMcmHQoOK-up_Tt1P6oIltTCHEg0z79GVatWvikmfrN0tLrMJl8iR_67IDvehkp0r4DoFQNkhKNm5moFGFJWqkWZSpi3OUhPYZNmWPJTf1CxM3li6hNqRuGLCe-M9-gyZ01U9j9sUbV3xaK6kXhDPje2JB-0FkZuU7ewmpmQ5ETuRYrXyQa6b6VyxNwYokvgAGxdQ8leT2jxq_UVoMw-C0JU8tOC1fkXxClfOsSfCKx5WQXIKFrU="; + private static final String PUBLIC_EXPONENT = "AQAB"; + private static final long KEY_DURATION = 5 * 60; + + /* + * + */ + private static final long TOKEN_DURATION = 5 * 60; + private static final String ACQUIRER_ID = "ACQUIRER_ID"; + private static final String CHANNEL = "CHANNEL"; + private static final String MERCHANT_ID = "MERCHANT_ID"; + private static final String CLIENT_ID = "ACQUIRER_ID"; + private static final String TERMINAL_ID = "TERMINAL_ID"; + private static final String SCOPES = "SCOPES"; + private static final List GROUPS = List.of("GROUP_1", "GROUP_2"); + + /* + * + */ + @Inject + TokenSigner tokenSigner; + + /* + * + */ + @InjectMock + AzureKeyFinder keyFinder; + + /* + * + */ + @InjectMock + @RestClient + AzureKeyVaultClient keyVaultClient; + + /* + * + */ + @InjectMock + @RestClient + AzureAuthClient authClient; + + /* + * + */ + private PrivateKey privateKey; + //private PublicKey publicKey; + + /** + * + */ + @BeforeAll + void init() throws NoSuchAlgorithmException, InvalidKeySpecException { + BigInteger modulus = new BigInteger(1, Base64.getUrlDecoder().decode(MODULUS)); + BigInteger privateExponent = new BigInteger(1, Base64.getUrlDecoder().decode(PRIVATE_EXPONENT)); + //BigInteger publicExponent = new BigInteger(1, Base64.getUrlDecoder().decode(PUBLIC_EXPONENT)); + + KeyFactory factory = KeyFactory.getInstance("RSA"); + privateKey = factory.generatePrivate(new RSAPrivateKeySpec(modulus, privateExponent)); + //publicKey = factory.generatePublic(new RSAPublicKeySpec(modulus, publicExponent)); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.azurekeyvault.service.AzureTokenSigner#sign(com.nimbusds.jwt.JWTClaimsSet)}. + * + * @throws JOSEException + */ + @Test + void testSign() throws JOSEException { + /* + * Expected result. + */ + Instant now = Instant.now(); + JWSHeader header = new JWSHeader(JWSAlgorithm.RS256, null, null, null, null, null, null, null, null, null, KID, true, null, null); + JWTClaimsSet payload = new JWTClaimsSet.Builder() + .subject(CLIENT_ID) + .issueTime(new Date(now.toEpochMilli())) + .expirationTime(new Date(now.toEpochMilli() + TOKEN_DURATION * 1000)) + .claim("acquirerId", ACQUIRER_ID) + .claim("channel", CHANNEL) + .claim("merchantId", MERCHANT_ID) + .claim("clientId", CLIENT_ID) + .claim("terminalId", TERMINAL_ID) + .claim("scope", SCOPES) + .claim("groups", GROUPS) + .build(); + SignedJWT expectedToken = new SignedJWT(header, payload); + JWSSigner signer = new RSASSASigner(privateKey); + expectedToken.sign(signer); + String[] components = expectedToken.serialize().split("\\."); + String expectedSignatureBase64 = components[2]; + + /* + * Setup. + */ + when(keyFinder.findValidPublicKeyWithGreatestExpiration()) + .thenReturn( + Uni.createFrom().item( + new ItemWithContext<>( + Context.of().put(AzureKeyFinder.TOKEN, "this_is_the_token"), + new it.pagopa.swclient.mil.auth.bean.PublicKey( + PUBLIC_EXPONENT, + KeyUse.sig, + KID, + MODULUS, + KeyType.RSA, + now.getEpochSecond() + KEY_DURATION, + now.getEpochSecond())))); + + when(keyVaultClient.sign(anyString(), eq(KEY_NAME), eq(KEY_VERSION), any(SignRequest.class))) + .thenReturn(Uni.createFrom().item(new SignResponse(KID, expectedSignatureBase64))); + + /* + * Test. + */ + SignedJWT actualToken = tokenSigner.sign(payload) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertCompleted() + .getItem(); + + components = actualToken.serialize().split("\\."); + String actualSignatureBase64 = components[2]; + + assertEquals(expectedSignatureBase64, actualSignatureBase64); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.azurekeyvault.service.AzureTokenSigner#sign(com.nimbusds.jwt.JWTClaimsSet)}. + * + * @throws JOSEException + */ + @Test + void testSignWithNoSuchAlgorithmException() throws JOSEException { + /* + * Expected result. + */ + Instant now = Instant.now(); + JWSHeader header = new JWSHeader(JWSAlgorithm.RS256, null, null, null, null, null, null, null, null, null, KID, true, null, null); + JWTClaimsSet payload = new JWTClaimsSet.Builder() + .subject(CLIENT_ID) + .issueTime(new Date(now.toEpochMilli())) + .expirationTime(new Date(now.toEpochMilli() + TOKEN_DURATION * 1000)) + .claim("acquirerId", ACQUIRER_ID) + .claim("channel", CHANNEL) + .claim("merchantId", MERCHANT_ID) + .claim("clientId", CLIENT_ID) + .claim("terminalId", TERMINAL_ID) + .claim("scope", SCOPES) + .claim("groups", GROUPS) + .build(); + SignedJWT expectedToken = new SignedJWT(header, payload); + JWSSigner signer = new RSASSASigner(privateKey); + expectedToken.sign(signer); + String[] components = expectedToken.serialize().split("\\."); + String expectedSignatureBase64 = components[2]; + + /* + * Setup. + */ + when(keyFinder.findValidPublicKeyWithGreatestExpiration()) + .thenReturn( + Uni.createFrom().item( + new ItemWithContext<>( + Context.of().put(AzureKeyFinder.TOKEN, "this_is_the_token"), + new it.pagopa.swclient.mil.auth.bean.PublicKey( + PUBLIC_EXPONENT, + KeyUse.sig, + KID, + MODULUS, + KeyType.RSA, + now.getEpochSecond() + KEY_DURATION, + now.getEpochSecond())))); + + when(keyVaultClient.sign(anyString(), eq(KEY_NAME), eq(KEY_VERSION), any(SignRequest.class))) + .thenReturn(Uni.createFrom().item(new SignResponse(KID, expectedSignatureBase64))); + + try (MockedStatic digest = Mockito.mockStatic(MessageDigest.class)) { + digest.when(() -> MessageDigest.getInstance("SHA256")) + .thenThrow(NoSuchAlgorithmException.class); + + /* + * Test. + */ + tokenSigner.sign(payload) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthError.class); + } + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.azurekeyvault.service.AzureTokenSigner#sign(com.nimbusds.jwt.JWTClaimsSet)}. + * + * @throws JOSEException + */ + @Test + void testSignWithParseException() throws JOSEException { + System.out.println("Test AzureTokenSigner.sign(...) with ParseException"); + + /* + * Expected result. + */ + Instant now = Instant.now(); + JWSHeader header = new JWSHeader(JWSAlgorithm.RS256, null, null, null, null, null, null, null, null, null, KID, true, null, null); + JWTClaimsSet payload = new JWTClaimsSet.Builder() + .subject(CLIENT_ID) + .issueTime(new Date(now.toEpochMilli())) + .expirationTime(new Date(now.toEpochMilli() + TOKEN_DURATION * 1000)) + .claim("acquirerId", ACQUIRER_ID) + .claim("channel", CHANNEL) + .claim("merchantId", MERCHANT_ID) + .claim("clientId", CLIENT_ID) + .claim("terminalId", TERMINAL_ID) + .claim("scope", SCOPES) + .claim("groups", GROUPS) + .build(); + SignedJWT expectedToken = new SignedJWT(header, payload); + JWSSigner signer = new RSASSASigner(privateKey); + expectedToken.sign(signer); + String[] components = expectedToken.serialize().split("\\."); + String expectedSignatureBase64 = components[2]; + + /* + * Setup. + */ + when(keyFinder.findValidPublicKeyWithGreatestExpiration()) + .thenReturn( + Uni.createFrom().item( + new ItemWithContext<>( + Context.of().put(AzureKeyFinder.TOKEN, "this_is_the_token"), + new it.pagopa.swclient.mil.auth.bean.PublicKey( + PUBLIC_EXPONENT, + KeyUse.sig, + KID, + MODULUS, + KeyType.RSA, + now.getEpochSecond() + KEY_DURATION, + now.getEpochSecond())))); + + when(keyVaultClient.sign(anyString(), eq(KEY_NAME), eq(KEY_VERSION), any(SignRequest.class))) + .thenReturn(Uni.createFrom().item(new SignResponse(KID, expectedSignatureBase64))); + + try (MockedStatic factory = Mockito.mockStatic(SignedJWTFactory.class)) { + factory.when(() -> SignedJWTFactory.createInstance(any(Base64URL.class), any(Base64URL.class), any(Base64URL.class))) + .thenThrow(new ParseException("synthetic exception", 0)); + + /* + * Test. + */ + tokenSigner.sign(payload) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthError.class); + } + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.azurekeyvault.service.AzureTokenSigner#verify(com.nimbusds.jwt.SignedJWT)}. + * + * @throws JOSEException + */ + @Test + void testVerify() throws JOSEException { + /* + * Expected result. + */ + Instant now = Instant.now(); + JWSHeader header = new JWSHeader(JWSAlgorithm.RS256, null, null, null, null, null, null, null, null, null, KID, true, null, null); + JWTClaimsSet payload = new JWTClaimsSet.Builder() + .subject(CLIENT_ID) + .issueTime(new Date(now.toEpochMilli())) + .expirationTime(new Date(now.toEpochMilli() + TOKEN_DURATION * 1000)) + .claim("acquirerId", ACQUIRER_ID) + .claim("channel", CHANNEL) + .claim("merchantId", MERCHANT_ID) + .claim("clientId", CLIENT_ID) + .claim("terminalId", TERMINAL_ID) + .claim("scope", SCOPES) + .claim("groups", GROUPS) + .build(); + SignedJWT token = new SignedJWT(header, payload); + JWSSigner signer = new RSASSASigner(privateKey); + token.sign(signer); + + /* + * Setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(Uni.createFrom().item(new GetAccessTokenResponse("Bearer", 3599, 3599, "this_is_the_token"))); + + when(keyVaultClient.verifySignature(anyString(), eq(KEY_NAME), eq(KEY_VERSION), any(VerifySignatureRequest.class))) + .thenReturn(Uni.createFrom().item(new VerifySignatureResponse(Boolean.TRUE))); + + /* + * Test. + */ + tokenSigner.verify(token) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertCompleted() + .assertItem(null); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.azurekeyvault.service.AzureTokenSigner#verify(com.nimbusds.jwt.SignedJWT)}. + * + * @throws JOSEException + */ + @Test + void testVerifyWithFailedVerification() throws JOSEException { + /* + * Expected result. + */ + Instant now = Instant.now(); + JWSHeader header = new JWSHeader(JWSAlgorithm.RS256, null, null, null, null, null, null, null, null, null, KID, true, null, null); + JWTClaimsSet payload = new JWTClaimsSet.Builder() + .subject(CLIENT_ID) + .issueTime(new Date(now.toEpochMilli())) + .expirationTime(new Date(now.toEpochMilli() + TOKEN_DURATION * 1000)) + .claim("acquirerId", ACQUIRER_ID) + .claim("channel", CHANNEL) + .claim("merchantId", MERCHANT_ID) + .claim("clientId", CLIENT_ID) + .claim("terminalId", TERMINAL_ID) + .claim("scope", SCOPES) + .claim("groups", GROUPS) + .build(); + SignedJWT token = new SignedJWT(header, payload); + JWSSigner signer = new RSASSASigner(privateKey); + token.sign(signer); + + /* + * Setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(Uni.createFrom().item(new GetAccessTokenResponse("Bearer", 3599, 3599, "this_is_the_token"))); + + when(keyVaultClient.verifySignature(anyString(), eq(KEY_NAME), eq(KEY_VERSION), any(VerifySignatureRequest.class))) + .thenReturn(Uni.createFrom().item(new VerifySignatureResponse(Boolean.FALSE))); + + /* + * Test. + */ + tokenSigner.verify(token) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthException.class); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.azurekeyvault.service.AzureTokenSigner#verify(com.nimbusds.jwt.SignedJWT)}. + * + * @throws JOSEException + */ + @Test + void testVerifyWithNullAccessToken() throws JOSEException { + /* + * Expected result. + */ + Instant now = Instant.now(); + JWSHeader header = new JWSHeader(JWSAlgorithm.RS256, null, null, null, null, null, null, null, null, null, KID, true, null, null); + JWTClaimsSet payload = new JWTClaimsSet.Builder() + .subject(CLIENT_ID) + .issueTime(new Date(now.toEpochMilli())) + .expirationTime(new Date(now.toEpochMilli() + TOKEN_DURATION * 1000)) + .claim("acquirerId", ACQUIRER_ID) + .claim("channel", CHANNEL) + .claim("merchantId", MERCHANT_ID) + .claim("clientId", CLIENT_ID) + .claim("terminalId", TERMINAL_ID) + .claim("scope", SCOPES) + .claim("groups", GROUPS) + .build(); + SignedJWT token = new SignedJWT(header, payload); + JWSSigner signer = new RSASSASigner(privateKey); + token.sign(signer); + + /* + * Setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(Uni.createFrom().item(new GetAccessTokenResponse("Bearer", 3599, 3599, null))); + + when(keyVaultClient.verifySignature(anyString(), eq(KEY_NAME), eq(KEY_VERSION), any(VerifySignatureRequest.class))) + .thenReturn(Uni.createFrom().item(new VerifySignatureResponse(Boolean.TRUE))); + + /* + * Test. + */ + tokenSigner.verify(token) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthError.class); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.azurekeyvault.service.AzureTokenSigner#verify(com.nimbusds.jwt.SignedJWT)}. + * + * @throws JOSEException + */ + @Test + void testVerifyWithNoSuchAlgorithmException() throws JOSEException { + /* + * Expected result. + */ + Instant now = Instant.now(); + JWSHeader header = new JWSHeader(JWSAlgorithm.RS256, null, null, null, null, null, null, null, null, null, KID, true, null, null); + JWTClaimsSet payload = new JWTClaimsSet.Builder() + .subject(CLIENT_ID) + .issueTime(new Date(now.toEpochMilli())) + .expirationTime(new Date(now.toEpochMilli() + TOKEN_DURATION * 1000)) + .claim("acquirerId", ACQUIRER_ID) + .claim("channel", CHANNEL) + .claim("merchantId", MERCHANT_ID) + .claim("clientId", CLIENT_ID) + .claim("terminalId", TERMINAL_ID) + .claim("scope", SCOPES) + .claim("groups", GROUPS) + .build(); + SignedJWT token = new SignedJWT(header, payload); + JWSSigner signer = new RSASSASigner(privateKey); + token.sign(signer); + + /* + * Setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(Uni.createFrom().item(new GetAccessTokenResponse("Bearer", 3599, 3599, "this_is_the_token"))); + + when(keyVaultClient.verifySignature(anyString(), eq(KEY_NAME), eq(KEY_VERSION), any(VerifySignatureRequest.class))) + .thenReturn(Uni.createFrom().item(new VerifySignatureResponse(Boolean.TRUE))); + + try (MockedStatic digest = Mockito.mockStatic(MessageDigest.class)) { + digest.when(() -> MessageDigest.getInstance("SHA256")) + .thenThrow(NoSuchAlgorithmException.class); + + /* + * Test. + */ + tokenSigner.verify(token) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthError.class); + } + } +} diff --git a/src/test/java/it/pagopa/swclient/mil/auth/client/PoyntClientTest.java b/src/test/java/it/pagopa/swclient/mil/auth/client/PoyntClientTest.java new file mode 100644 index 00000000..afaad919 --- /dev/null +++ b/src/test/java/it/pagopa/swclient/mil/auth/client/PoyntClientTest.java @@ -0,0 +1,59 @@ +/** + * + */ +package it.pagopa.swclient.mil.auth.client; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; + +import io.quarkus.test.junit.QuarkusTest; +import io.smallrye.mutiny.Uni; +import jakarta.ws.rs.core.Response; + +/** + * + */ +@QuarkusTest +@TestInstance(Lifecycle.PER_CLASS) +class PoyntClientTest { + /* + * + */ + private PoyntClient client; + + /** + * + */ + @BeforeAll + private void init() { + client = new PoyntClient() { + @Override + public Uni getBusinessObject(String poyntToken, String businessId) { + return null; + } + }; + } + + /** + * + */ + @Test + void testWithParamOk() { + assertNotNull(client.withParam("POYNT-REQUEST-ID")); + } + + /** + * + */ + @Test + void testWithParamKo() { + assertThrows(IllegalArgumentException.class, () -> { + client.withParam("POYNT-REQUEST-ID2"); + }); + } +} diff --git a/src/test/java/it/pagopa/swclient/mil/auth/resource/JwksResourceTest.java b/src/test/java/it/pagopa/swclient/mil/auth/resource/JwksResourceTest.java index 35585fed..7de291a4 100644 --- a/src/test/java/it/pagopa/swclient/mil/auth/resource/JwksResourceTest.java +++ b/src/test/java/it/pagopa/swclient/mil/auth/resource/JwksResourceTest.java @@ -1,37 +1,37 @@ /* * JwksResourceTest.java * - * 23 mar 2023 + * 14 set 2023 */ package it.pagopa.swclient.mil.auth.resource; import static io.restassured.RestAssured.given; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasItem; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; import java.time.Instant; -import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; -import org.mockito.Mockito; - -import com.nimbusds.jose.JOSEException; +import io.quarkus.test.InjectMock; import io.quarkus.test.common.http.TestHTTPEndpoint; import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.junit.mockito.InjectMock; import io.smallrye.mutiny.Uni; -import it.pagopa.swclient.mil.auth.ErrorCode; -import it.pagopa.swclient.mil.auth.bean.KeyPair; -import it.pagopa.swclient.mil.auth.service.KeyPairGenerator; -import it.pagopa.swclient.mil.auth.service.RedisClient; -import jakarta.inject.Inject; +import it.pagopa.swclient.mil.auth.AuthErrorCode; +import it.pagopa.swclient.mil.auth.bean.JsonPropertyName; +import it.pagopa.swclient.mil.auth.bean.KeyType; +import it.pagopa.swclient.mil.auth.bean.KeyUse; +import it.pagopa.swclient.mil.auth.bean.PublicKey; +import it.pagopa.swclient.mil.auth.bean.PublicKeys; +import it.pagopa.swclient.mil.auth.service.KeyFinder; import jakarta.ws.rs.core.MediaType; /** - * + * * @author Antonio Tarricone */ @QuarkusTest @@ -42,190 +42,123 @@ class JwksResourceTest { * */ @InjectMock - RedisClient redisClient; - - /* - * - */ - @Inject - KeyPairGenerator keyPairGenerator; + KeyFinder keyFinder; - /** - * No key found. - */ - @Test() - void noKeyFound() { + @Test + void testOk() { /* - * Setup + * Setup. */ - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item(new ArrayList())); + long now = Instant.now().getEpochSecond(); + long exp = now + 900; + PublicKey publicKey1 = new PublicKey("exp1", KeyUse.sig, "kid1", "mod1", KeyType.RSA, exp, now); + PublicKey publicKey2 = new PublicKey("exp2", KeyUse.sig, "kid2", "mod2", KeyType.RSA, exp + 10, now + 10); + + PublicKeys publicKeys = new PublicKeys(List.of(publicKey1, publicKey2)); + + when(keyFinder.findPublicKeys()) + .thenReturn(Uni.createFrom().item(publicKeys)); /* - * Test + * Test. */ - given() + PublicKeys response = given() .when() .get() .then() + .log() + .everything() .statusCode(200) .contentType(MediaType.APPLICATION_JSON) - .body("keys", empty()); + .header("Cache-Control", containsString("max-age")) + .extract() + .response() + .as(PublicKeys.class); + + assertEquals(publicKeys, response); } - /** - * 1 expired key + 2 valid keys - * - * @throws JOSEException - */ - @Test() - void oneExpiredAndTwoValid() throws JOSEException { + @Test + void testWithExpirationInAWhileOk() { /* - * Setup + * Setup. */ - KeyPair expiredKey = keyPairGenerator.generate(); - expiredKey.setExp(Instant.now().toEpochMilli() - 1000); - - KeyPair validKey0 = keyPairGenerator.generate(); - KeyPair validKey1 = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item(List.of( - expiredKey.getKid(), - validKey0.getKid(), - validKey1.getKid()))); + long now = Instant.now().getEpochSecond(); + long exp = now + 5 * 60; + PublicKey publicKey1 = new PublicKey("exp1", KeyUse.sig, "kid1", "mod1", KeyType.RSA, exp, now); - Mockito - .when(redisClient.get(expiredKey.getKid())) - .thenReturn(Uni.createFrom().item(expiredKey)); + PublicKeys publicKeys = new PublicKeys(List.of(publicKey1)); - Mockito - .when(redisClient.get(validKey0.getKid())) - .thenReturn(Uni.createFrom().item(validKey0)); - - Mockito - .when(redisClient.get(validKey1.getKid())) - .thenReturn(Uni.createFrom().item(validKey1)); + when(keyFinder.findPublicKeys()) + .thenReturn(Uni.createFrom().item(publicKeys)); /* - * Test + * Test. */ - given() + PublicKeys response = given() .when() .get() .then() + .log() + .everything() .statusCode(200) .contentType(MediaType.APPLICATION_JSON) - .body("keys[0].e", equalTo(validKey0.getE())) - .body("keys[0].exp", equalTo(validKey0.getExp())) - .body("keys[0].iat", equalTo(validKey0.getIat())) - .body("keys[0].kid", equalTo(validKey0.getKid())) - .body("keys[0].kty", equalTo(validKey0.getKty().toString())) - .body("keys[0].n", equalTo(validKey0.getN())) - .body("keys[0].use", equalTo(validKey0.getUse().toString())) - .body("keys[1].e", equalTo(validKey1.getE())) - .body("keys[1].exp", equalTo(validKey1.getExp())) - .body("keys[1].iat", equalTo(validKey1.getIat())) - .body("keys[1].kid", equalTo(validKey1.getKid())) - .body("keys[1].kty", equalTo(validKey1.getKty().toString())) - .body("keys[1].n", equalTo(validKey1.getN())) - .body("keys[1].use", equalTo(validKey1.getUse().toString())); - } - - /** - * Failure on ReactiveKeyCommands.keys(String). - */ - @Test() - void failureOnKeys() { - /* - * Setup - */ - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().failure(new Exception("synthetic exception"))); + .header("Cache-Control", containsString("no-cache")) + .extract() + .response() + .as(PublicKeys.class); - /* - * Test - */ - given() - .when() - .get() - .then() - .statusCode(500) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", equalTo(List.of(ErrorCode.ERROR_SEARCHING_FOR_KEYS))); + assertEquals(publicKeys, response); } - /** - * Failure on ReactiveValueCommands.get(String). - * - * @throws JOSEException - */ - @Test() - void failureOnFirstGet() throws JOSEException { + @Test + void testWithoutKeys() { /* - * Setup + * Setup. */ - KeyPair keyPair = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item(List.of(keyPair.getKid()))); + PublicKeys publicKeys = new PublicKeys(List.of()); - Mockito - .when(redisClient.get(keyPair.getKid())) - .thenReturn(Uni.createFrom().failure(new Exception("synthetic exception"))); + when(keyFinder.findPublicKeys()) + .thenReturn(Uni.createFrom().item(publicKeys)); /* - * Test + * Test. */ - given() + PublicKeys response = given() .when() .get() .then() - .statusCode(500) + .log() + .everything() + .statusCode(200) .contentType(MediaType.APPLICATION_JSON) - .body("errors", equalTo(List.of(ErrorCode.ERROR_SEARCHING_FOR_KEYS))); + .header("Cache-Control", containsString("no-cache")) + .extract() + .response() + .as(PublicKeys.class); + + assertEquals(publicKeys, response); } - /** - * Failure on ReactiveValueCommands.get(String). - * - * @throws JOSEException - */ - @Test() - void failureOnSecondGet() throws JOSEException { + @Test + void testWithError() { /* - * Setup + * Setup. */ - KeyPair keyPair1 = keyPairGenerator.generate(); - KeyPair keyPair2 = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item(List.of( - keyPair1.getKid(), - keyPair2.getKid()))); - - Mockito - .when(redisClient.get(keyPair1.getKid())) - .thenReturn(Uni.createFrom().item(keyPair1)); - - Mockito - .when(redisClient.get(keyPair2.getKid())) + when(keyFinder.findPublicKeys()) .thenReturn(Uni.createFrom().failure(new Exception("synthetic exception"))); /* - * Test + * Test. */ given() .when() .get() .then() + .log() + .everything() .statusCode(500) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", equalTo(List.of(ErrorCode.ERROR_SEARCHING_FOR_KEYS))); + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.ERROR_SEARCHING_FOR_KEYS)); } -} \ No newline at end of file +} diff --git a/src/test/java/it/pagopa/swclient/mil/auth/resource/RefreshTokensResourceTest.java b/src/test/java/it/pagopa/swclient/mil/auth/resource/RefreshTokensResourceTest.java new file mode 100644 index 00000000..c6a75c19 --- /dev/null +++ b/src/test/java/it/pagopa/swclient/mil/auth/resource/RefreshTokensResourceTest.java @@ -0,0 +1,660 @@ +/* + * RefreshTokensResourceTest.java + * + * 28 ago 2023 + */ +package it.pagopa.swclient.mil.auth.resource; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.RSAPrivateKeySpec; +import java.time.Instant; +import java.util.Base64; +import java.util.Date; +import java.util.List; + +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.JWSSigner; +import com.nimbusds.jose.crypto.RSASSASigner; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; + +import io.quarkus.test.InjectMock; +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; +import it.pagopa.swclient.mil.auth.AuthErrorCode; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetAccessTokenResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeyResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeyVersionsResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeysResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.Key; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.KeyAttributes; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.KeyDetails; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.KeyVersion; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.SignRequest; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.SignResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.VerifySignatureRequest; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.VerifySignatureResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.client.AzureAuthClient; +import it.pagopa.swclient.mil.auth.azurekeyvault.client.AzureKeyVaultClient; +import it.pagopa.swclient.mil.auth.bean.ClaimName; +import it.pagopa.swclient.mil.auth.bean.Client; +import it.pagopa.swclient.mil.auth.bean.FormParamName; +import it.pagopa.swclient.mil.auth.bean.GrantType; +import it.pagopa.swclient.mil.auth.bean.HeaderParamName; +import it.pagopa.swclient.mil.auth.bean.JsonPropertyName; +import it.pagopa.swclient.mil.auth.bean.Role; +import it.pagopa.swclient.mil.auth.bean.Scope; +import it.pagopa.swclient.mil.auth.bean.TokenType; +import it.pagopa.swclient.mil.auth.client.AuthDataRepository; +import it.pagopa.swclient.mil.auth.util.UniGenerator; +import it.pagopa.swclient.mil.bean.Channel; +import jakarta.ws.rs.core.MediaType; + +/** + * @author Antonio Tarricone + */ +@QuarkusTest +@TestHTTPEndpoint(TokenResource.class) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class RefreshTokensResourceTest { + /* + * + */ + private static final String ACQUIRER_ID = "4585625"; + private static final String MERCHANT_ID = "28405fHfk73x88D"; + private static final String TERMINAL_ID = "12345678"; + private static final String CLIENT_ID = "3965df56-ca9a-49e5-97e8-061433d4a25b"; + private static final String DESCRIPTION = "VAS Layer"; + private static final List ROLES = List.of("NoticePayer", "SlavePos"); + + /* + * + */ + private static final String WRONG_SCOPE = "other_scope"; + private static final String INVALID_REFRESH_TOKEN = "1.1.1"; + + /* + * + */ + private static final long AZURE_TOKEN_DURATION = 3599; + private static final String AZURE_TOKEN = "this_is_the_token"; + private static final String AUTHORIZATION_HDR_VALUE = TokenType.BEARER + " " + AZURE_TOKEN; + + /* + * + */ + private static final String KEY_URL = "https://mil-d-appl-kv.vault.azure.net/keys/"; + private static final String KEY_NAME = "0709643f49394529b92c19a68c8e184a"; + private static final String KEY_VERSION = "6581c704deda4979943c3b34468df7c2"; + private static final String KID = KEY_NAME + "/" + KEY_VERSION; + private static final String KEY_RECOVERY_LEVEL = "Purgeable"; + private static final String KEY_TYPE = "RSA"; + private static final String[] KEY_OPS = new String[] { + "verify", "sign" + }; + private static final String MODULUS = "AKnFsF5Y16TB9qkmoOyDXG3ulenUWYoW78U7mcGBoYKRpMlswxhc_ZiKcC65vIrCP6hbS5Cx88IbQG2DWH-nE329OLzUbzcdraDLR-7V2BX0nNwmwXxhkd4ofzzjKyhWjV8AkxFpqJPtFG09YCyCpaC8YluVPbHUpWJ1wrOsavdc_YM1W1XuaGvJv4SkilM8vBa81zOLEVhbEE5msHxPNLwVyC_0PIE6OFL9RY4YP1U1q7gjTMmKDc9qgEYkdziMnlxWp_EkKTZOERbEatP0fditFt-zWKlXw0qO4FKFlmj9n5tbB55vaopB71Kv6LcsAY1Q-fgOuoM41HldLppzfDOPwLGyCQF9ODJt1xaKkup6i_BxZum7-QckibwPaj3ODZbYsPuNZ_npQiR6NJZ_q_31YMlyuGdqltawluYLJidw3EzkpTN__bHdio892WbY29PRwbrG486IJ_88qP3lWs1TfzohVa1czUOZwQHqp0ixVBi_SK3jICk-V65DbwzgS5zwBFaqfWO3XVOf6tmWFMZ6ly7wtOnYWoMR15rudsD5xXWwqE-s7IP1lVZuIOdMfLH7-1Pgn-YJuPsBLbZri9_M4KtflYbqnuDckSyFNBynTwoSvSSuBhpkmNgiSQ-WBXHHss5Wy-pr-YjNK7JYppPOHvfHSY96XnJl9SPWcnwx"; + private static final String PRIVATE_EXPONENT = "IlITaUNTFtzaUVA8lIuqxhOHLW3vCv4_ixMVLnwXC0cHteudliGIZ8vGyX9laPTDezS3lkEPSuSI9gqpO6cqRs9Xtr7IW-9NQDYQLO2AoVGh21SfZVZxL2Tm8gdnnGBA9J1wXcMLIBp7uGjBtkXUF2Y2CRcm0XowU_MEASAgQLEFE_8Xn4vSgsXWiIld6F1dFcinxaT9xOul5H-Yeozll4dcwKsCh0pehBJs-wCWXxK6S_-g4JZe29lHJMbu7hjpU7f1_AcIKNEH3d8nzID-5ux49RCz4goasgonua8FXOS23Sh-Jg6WjmwtZj0nEc6c4rVlzzqlBG2a8I0ApJsnlo2RK1E-XftVNip52Bsb9jRKGNjNZP3VOgAdLg-py8HVU3sxn95yJRN6AF7S8a0Jnb6uAzxagmfZqLe1ykswBPJWPP2dyQivb59CMcmHQoOK-up_Tt1P6oIltTCHEg0z79GVatWvikmfrN0tLrMJl8iR_67IDvehkp0r4DoFQNkhKNm5moFGFJWqkWZSpi3OUhPYZNmWPJTf1CxM3li6hNqRuGLCe-M9-gyZ01U9j9sUbV3xaK6kXhDPje2JB-0FkZuU7ewmpmQ5ETuRYrXyQa6b6VyxNwYokvgAGxdQ8leT2jxq_UVoMw-C0JU8tOC1fkXxClfOsSfCKx5WQXIKFrU="; + private static final String PUBLIC_EXPONENT = "AQAB"; + private static final String EXPECTED_SIGNATURE = "expected_signature"; + + /* + * + */ + @InjectMock + @RestClient + AuthDataRepository repository; + + /* + * + */ + @InjectMock + @RestClient + AzureKeyVaultClient keyVaultClient; + + /* + * + */ + @InjectMock + @RestClient + AzureAuthClient authClient; + + @Test + void testOk() throws InvalidKeySpecException, NoSuchAlgorithmException, JOSEException { + /* + * Client repository setup. + */ + when(repository.getClient(CLIENT_ID)) + .thenReturn(UniGenerator.item(new Client(CLIENT_ID, Channel.POS, null, null, DESCRIPTION))); + + /* + * Roles repository setup. + */ + when(repository.getRoles(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID)) + .thenReturn(UniGenerator.item(new Role(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID, ROLES))); + + /* + * Azure auth. client setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(UniGenerator.item(new GetAccessTokenResponse(TokenType.BEARER, AZURE_TOKEN_DURATION, AZURE_TOKEN_DURATION, AZURE_TOKEN))); + + /* + * Azure key vault setup. + */ + long now = Instant.now().getEpochSecond(); + KeyAttributes keyAttributes = new KeyAttributes(now - 300, now + 600, now - 300, now - 300, Boolean.TRUE, KEY_RECOVERY_LEVEL, 0, Boolean.FALSE); + + when(keyVaultClient.getKeys(AUTHORIZATION_HDR_VALUE)) + .thenReturn(UniGenerator.item(new GetKeysResponse(new Key[]{ + new Key(KEY_URL + KEY_NAME, keyAttributes) + }))); + + when(keyVaultClient.getKeyVersions(AUTHORIZATION_HDR_VALUE, KEY_NAME)) + .thenReturn(UniGenerator.item(new GetKeyVersionsResponse(new KeyVersion[]{ + new KeyVersion(KEY_URL + KEY_NAME + "/" + KEY_VERSION, keyAttributes) + }))); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, KEY_NAME, KEY_VERSION)) + .thenReturn(UniGenerator.item(new GetKeyResponse(new KeyDetails(KEY_URL + KEY_NAME + "/" + KEY_VERSION, KEY_TYPE, KEY_OPS, MODULUS, PUBLIC_EXPONENT, keyAttributes)))); + + when(keyVaultClient.sign(eq(AUTHORIZATION_HDR_VALUE), eq(KEY_NAME), eq(KEY_VERSION), any(SignRequest.class))) + .thenReturn(UniGenerator.item(new SignResponse(KID, EXPECTED_SIGNATURE))); + + when(keyVaultClient.verifySignature(eq(AUTHORIZATION_HDR_VALUE), eq(KEY_NAME), eq(KEY_VERSION), any(VerifySignatureRequest.class))) + .thenReturn(UniGenerator.item(new VerifySignatureResponse(Boolean.TRUE))); + + /* + * Refresh token. + */ + JWSHeader header = new JWSHeader(JWSAlgorithm.RS256, null, null, null, null, null, null, null, null, null, KID, true, null, null); + + JWTClaimsSet payload = new JWTClaimsSet.Builder() + .subject(CLIENT_ID) + .issueTime(new Date(now * 1000)) + .expirationTime(new Date((now + 15 * 60) * 1000)) + .claim(ClaimName.ACQUIRER_ID, ACQUIRER_ID) + .claim(ClaimName.CHANNEL, Channel.POS) + .claim(ClaimName.MERCHANT_ID, MERCHANT_ID) + .claim(ClaimName.CLIENT_ID, CLIENT_ID) + .claim(ClaimName.TERMINAL_ID, TERMINAL_ID) + .claim(ClaimName.SCOPE, Scope.OFFLINE_ACCESS) + .claim(ClaimName.GROUPS, ROLES) + .build(); + + SignedJWT refreshToken = new SignedJWT(header, payload); + + PrivateKey privateKey = KeyFactory.getInstance("RSA") + .generatePrivate(new RSAPrivateKeySpec( + new BigInteger(1, Base64.getUrlDecoder().decode(MODULUS)), + new BigInteger(1, Base64.getUrlDecoder().decode(PRIVATE_EXPONENT)))); + + JWSSigner signer = new RSASSASigner(privateKey); + refreshToken.sign(signer); + String refreshTokenStr = refreshToken.serialize(); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-300000000000") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.REFRESH_TOKEN) + .formParam(FormParamName.REFRESH_TOKEN, refreshTokenStr) + .when() + .post() + .then() + .statusCode(200) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ACCESS_TOKEN, notNullValue()) + .body(JsonPropertyName.TOKEN_TYPE, equalTo(TokenType.BEARER)) + .body(JsonPropertyName.EXPIRES_IN, notNullValue(Long.class)) + .body(JsonPropertyName.REFRESH_TOKEN, notNullValue()); + } + + @Test + void testExceptionParsingRefreshToken() throws NoSuchAlgorithmException, InvalidKeySpecException, JOSEException { + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-300000000001") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.REFRESH_TOKEN) + .formParam(FormParamName.REFRESH_TOKEN, INVALID_REFRESH_TOKEN) + .when() + .post() + .then() + .statusCode(500) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.ERROR_PARSING_TOKEN)); + } + + @Test + void testWrongAlgorithm() throws NoSuchAlgorithmException, InvalidKeySpecException, JOSEException { + /* + * Refresh token. + */ + long now = Instant.now().getEpochSecond(); + + JWSHeader header = new JWSHeader(JWSAlgorithm.RS384, null, null, null, null, null, null, null, null, null, KID, true, null, null); + + JWTClaimsSet payload = new JWTClaimsSet.Builder() + .subject(CLIENT_ID) + .issueTime(new Date(now * 1000)) + .expirationTime(new Date((now + 15 * 60) * 1000)) + .claim(ClaimName.ACQUIRER_ID, ACQUIRER_ID) + .claim(ClaimName.CHANNEL, Channel.POS) + .claim(ClaimName.MERCHANT_ID, MERCHANT_ID) + .claim(ClaimName.CLIENT_ID, CLIENT_ID) + .claim(ClaimName.TERMINAL_ID, TERMINAL_ID) + .claim(ClaimName.SCOPE, Scope.OFFLINE_ACCESS) + .claim(ClaimName.GROUPS, ROLES) + .build(); + + SignedJWT refreshToken = new SignedJWT(header, payload); + + PrivateKey privateKey = KeyFactory.getInstance("RSA") + .generatePrivate(new RSAPrivateKeySpec( + new BigInteger(1, Base64.getUrlDecoder().decode(MODULUS)), + new BigInteger(1, Base64.getUrlDecoder().decode(PRIVATE_EXPONENT)))); + + JWSSigner signer = new RSASSASigner(privateKey); + refreshToken.sign(signer); + String refreshTokenStr = refreshToken.serialize(); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-300000000002") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.REFRESH_TOKEN) + .formParam(FormParamName.REFRESH_TOKEN, refreshTokenStr) + .when() + .post() + .then() + .statusCode(401) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.WRONG_ALGORITHM)); + } + + @Test + void testNullIssueTime() throws NoSuchAlgorithmException, JOSEException, InvalidKeySpecException { + /* + * Refresh token. + */ + long now = Instant.now().getEpochSecond(); + + JWSHeader header = new JWSHeader(JWSAlgorithm.RS256, null, null, null, null, null, null, null, null, null, KID, true, null, null); + + JWTClaimsSet payload = new JWTClaimsSet.Builder() + .subject(CLIENT_ID) + .expirationTime(new Date((now + 15 * 60) * 1000)) + .claim(ClaimName.ACQUIRER_ID, ACQUIRER_ID) + .claim(ClaimName.CHANNEL, Channel.POS) + .claim(ClaimName.MERCHANT_ID, MERCHANT_ID) + .claim(ClaimName.CLIENT_ID, CLIENT_ID) + .claim(ClaimName.TERMINAL_ID, TERMINAL_ID) + .claim(ClaimName.SCOPE, Scope.OFFLINE_ACCESS) + .claim(ClaimName.GROUPS, ROLES) + .build(); + + SignedJWT refreshToken = new SignedJWT(header, payload); + + PrivateKey privateKey = KeyFactory.getInstance("RSA") + .generatePrivate(new RSAPrivateKeySpec( + new BigInteger(1, Base64.getUrlDecoder().decode(MODULUS)), + new BigInteger(1, Base64.getUrlDecoder().decode(PRIVATE_EXPONENT)))); + + JWSSigner signer = new RSASSASigner(privateKey); + refreshToken.sign(signer); + String refreshTokenStr = refreshToken.serialize(); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-300000000003") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.REFRESH_TOKEN) + .formParam(FormParamName.REFRESH_TOKEN, refreshTokenStr) + .when() + .post() + .then() + .statusCode(401) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.ISSUE_TIME_MUST_NOT_BE_NULL)); + } + + @Test + void testWrongIssueTime() throws NoSuchAlgorithmException, JOSEException, InvalidKeySpecException { + /* + * Refresh token. + */ + long now = Instant.now().getEpochSecond(); + + JWSHeader header = new JWSHeader(JWSAlgorithm.RS256, null, null, null, null, null, null, null, null, null, KID, true, null, null); + + JWTClaimsSet payload = new JWTClaimsSet.Builder() + .subject(CLIENT_ID) + .issueTime(new Date((now + 10 * 60) * 1000)) + .expirationTime(new Date((now + 15 * 60) * 1000)) + .claim(ClaimName.ACQUIRER_ID, ACQUIRER_ID) + .claim(ClaimName.CHANNEL, Channel.POS) + .claim(ClaimName.MERCHANT_ID, MERCHANT_ID) + .claim(ClaimName.CLIENT_ID, CLIENT_ID) + .claim(ClaimName.TERMINAL_ID, TERMINAL_ID) + .claim(ClaimName.SCOPE, Scope.OFFLINE_ACCESS) + .claim(ClaimName.GROUPS, ROLES) + .build(); + + SignedJWT refreshToken = new SignedJWT(header, payload); + + PrivateKey privateKey = KeyFactory.getInstance("RSA") + .generatePrivate(new RSAPrivateKeySpec( + new BigInteger(1, Base64.getUrlDecoder().decode(MODULUS)), + new BigInteger(1, Base64.getUrlDecoder().decode(PRIVATE_EXPONENT)))); + + JWSSigner signer = new RSASSASigner(privateKey); + refreshToken.sign(signer); + String refreshTokenStr = refreshToken.serialize(); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-300000000004") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.REFRESH_TOKEN) + .formParam(FormParamName.REFRESH_TOKEN, refreshTokenStr) + .when() + .post() + .then() + .statusCode(401) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.WRONG_ISSUE_TIME)); + } + + @Test + void testNullExpirationTime() throws NoSuchAlgorithmException, JOSEException, InvalidKeySpecException { + /* + * Refresh token. + */ + long now = Instant.now().getEpochSecond(); + + JWSHeader header = new JWSHeader(JWSAlgorithm.RS256, null, null, null, null, null, null, null, null, null, KID, true, null, null); + + JWTClaimsSet payload = new JWTClaimsSet.Builder() + .subject(CLIENT_ID) + .issueTime(new Date(now * 1000)) + .claim(ClaimName.ACQUIRER_ID, ACQUIRER_ID) + .claim(ClaimName.CHANNEL, Channel.POS) + .claim(ClaimName.MERCHANT_ID, MERCHANT_ID) + .claim(ClaimName.CLIENT_ID, CLIENT_ID) + .claim(ClaimName.TERMINAL_ID, TERMINAL_ID) + .claim(ClaimName.SCOPE, Scope.OFFLINE_ACCESS) + .claim(ClaimName.GROUPS, ROLES) + .build(); + + SignedJWT refreshToken = new SignedJWT(header, payload); + + PrivateKey privateKey = KeyFactory.getInstance("RSA") + .generatePrivate(new RSAPrivateKeySpec( + new BigInteger(1, Base64.getUrlDecoder().decode(MODULUS)), + new BigInteger(1, Base64.getUrlDecoder().decode(PRIVATE_EXPONENT)))); + + JWSSigner signer = new RSASSASigner(privateKey); + refreshToken.sign(signer); + String refreshTokenStr = refreshToken.serialize(); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-300000000005") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.REFRESH_TOKEN) + .formParam(FormParamName.REFRESH_TOKEN, refreshTokenStr) + .when() + .post() + .then() + .statusCode(401) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.EXPIRATION_TIME_MUST_NOT_BE_NULL)); + } + + @Test + void testWrongExpirationTime() throws NoSuchAlgorithmException, JOSEException, InvalidKeySpecException { + /* + * Refresh token. + */ + long now = Instant.now().getEpochSecond(); + + JWSHeader header = new JWSHeader(JWSAlgorithm.RS256, null, null, null, null, null, null, null, null, null, KID, true, null, null); + + JWTClaimsSet payload = new JWTClaimsSet.Builder() + .subject(CLIENT_ID) + .issueTime(new Date(now * 1000)) + .expirationTime(new Date((now - 15 * 60) * 1000)) + .claim(ClaimName.ACQUIRER_ID, ACQUIRER_ID) + .claim(ClaimName.CHANNEL, Channel.POS) + .claim(ClaimName.MERCHANT_ID, MERCHANT_ID) + .claim(ClaimName.CLIENT_ID, CLIENT_ID) + .claim(ClaimName.TERMINAL_ID, TERMINAL_ID) + .claim(ClaimName.SCOPE, Scope.OFFLINE_ACCESS) + .claim(ClaimName.GROUPS, ROLES) + .build(); + + SignedJWT refreshToken = new SignedJWT(header, payload); + + PrivateKey privateKey = KeyFactory.getInstance("RSA") + .generatePrivate(new RSAPrivateKeySpec( + new BigInteger(1, Base64.getUrlDecoder().decode(MODULUS)), + new BigInteger(1, Base64.getUrlDecoder().decode(PRIVATE_EXPONENT)))); + + JWSSigner signer = new RSASSASigner(privateKey); + refreshToken.sign(signer); + String refreshTokenStr = refreshToken.serialize(); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-300000000006") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.REFRESH_TOKEN) + .formParam(FormParamName.REFRESH_TOKEN, refreshTokenStr) + .when() + .post() + .then() + .statusCode(401) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.TOKEN_EXPIRED)); + } + +// @Test +// void testNullAzureAccessToken() { +// } + + @Test + void testWrongScope() throws NoSuchAlgorithmException, InvalidKeySpecException, JOSEException { + /* + * Refresh token. + */ + long now = Instant.now().getEpochSecond(); + + JWSHeader header = new JWSHeader(JWSAlgorithm.RS256, null, null, null, null, null, null, null, null, null, KID, true, null, null); + + JWTClaimsSet payload = new JWTClaimsSet.Builder() + .subject(CLIENT_ID) + .issueTime(new Date(now * 1000)) + .expirationTime(new Date((now + 15 * 60) * 1000)) + .claim(ClaimName.ACQUIRER_ID, ACQUIRER_ID) + .claim(ClaimName.CHANNEL, Channel.POS) + .claim(ClaimName.MERCHANT_ID, MERCHANT_ID) + .claim(ClaimName.CLIENT_ID, CLIENT_ID) + .claim(ClaimName.TERMINAL_ID, TERMINAL_ID) + .claim(ClaimName.SCOPE, WRONG_SCOPE) + .claim(ClaimName.GROUPS, ROLES) + .build(); + + SignedJWT refreshToken = new SignedJWT(header, payload); + + PrivateKey privateKey = KeyFactory.getInstance("RSA") + .generatePrivate(new RSAPrivateKeySpec( + new BigInteger(1, Base64.getUrlDecoder().decode(MODULUS)), + new BigInteger(1, Base64.getUrlDecoder().decode(PRIVATE_EXPONENT)))); + + JWSSigner signer = new RSASSASigner(privateKey); + refreshToken.sign(signer); + String refreshTokenStr = refreshToken.serialize(); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-300000000007") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.REFRESH_TOKEN) + .formParam(FormParamName.REFRESH_TOKEN, refreshTokenStr) + .when() + .post() + .then() + .statusCode(401) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.WRONG_SCOPE)); + } + + @Test + void testWithScope() throws InvalidKeySpecException, NoSuchAlgorithmException, JOSEException { + /* + * Refresh token. + */ + long now = Instant.now().getEpochSecond(); + JWSHeader header = new JWSHeader(JWSAlgorithm.RS256, null, null, null, null, null, null, null, null, null, KID, true, null, null); + + JWTClaimsSet payload = new JWTClaimsSet.Builder() + .subject(CLIENT_ID) + .issueTime(new Date(now * 1000)) + .expirationTime(new Date((now + 15 * 60) * 1000)) + .claim(ClaimName.ACQUIRER_ID, ACQUIRER_ID) + .claim(ClaimName.CHANNEL, Channel.POS) + .claim(ClaimName.MERCHANT_ID, MERCHANT_ID) + .claim(ClaimName.CLIENT_ID, CLIENT_ID) + .claim(ClaimName.TERMINAL_ID, TERMINAL_ID) + .claim(ClaimName.SCOPE, Scope.OFFLINE_ACCESS) + .claim(ClaimName.GROUPS, ROLES) + .build(); + + SignedJWT refreshToken = new SignedJWT(header, payload); + + PrivateKey privateKey = KeyFactory.getInstance("RSA") + .generatePrivate(new RSAPrivateKeySpec( + new BigInteger(1, Base64.getUrlDecoder().decode(MODULUS)), + new BigInteger(1, Base64.getUrlDecoder().decode(PRIVATE_EXPONENT)))); + + JWSSigner signer = new RSASSASigner(privateKey); + refreshToken.sign(signer); + String refreshTokenStr = refreshToken.serialize(); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-300000000008") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.REFRESH_TOKEN) + .formParam(FormParamName.REFRESH_TOKEN, refreshTokenStr) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) + .when() + .post() + .then() + .statusCode(400) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, notNullValue()); + } + +// @Test +// void testUnauthorizedGettingAzureAccessToken() { +// } + +// @Test +// void testNoSuchAlgorithmGettingDerDigestInfo() { +// } + +// @Test +// void testExceptionGettingDerDigestInfo() { +// } + +// @Test +// void testWrongSignature() { +// } + +// @Test +// void testErrorFromSuper() { +// } +} \ No newline at end of file diff --git a/src/test/java/it/pagopa/swclient/mil/auth/resource/RequestValidationTest.java b/src/test/java/it/pagopa/swclient/mil/auth/resource/RequestValidationTest.java index 71d10018..ef8a5123 100644 --- a/src/test/java/it/pagopa/swclient/mil/auth/resource/RequestValidationTest.java +++ b/src/test/java/it/pagopa/swclient/mil/auth/resource/RequestValidationTest.java @@ -13,12 +13,15 @@ import io.quarkus.test.common.http.TestHTTPEndpoint; import io.quarkus.test.junit.QuarkusTest; +import it.pagopa.swclient.mil.auth.bean.FormParamName; import it.pagopa.swclient.mil.auth.bean.GrantType; +import it.pagopa.swclient.mil.auth.bean.HeaderParamName; +import it.pagopa.swclient.mil.auth.bean.JsonPropertyName; +import it.pagopa.swclient.mil.auth.bean.Scope; import it.pagopa.swclient.mil.bean.Channel; import jakarta.ws.rs.core.MediaType; /** - * * @author Antonio Tarricone */ @QuarkusTest @@ -26,7 +29,7 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) class RequestValidationTest { /* - * + * */ private static final String clientId = "5254f087-1214-45cd-94ae-fda53c835197"; private static final String acquirerId = "4585625"; @@ -49,24 +52,24 @@ void clientCredentials1() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000001") - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000001") + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** @@ -79,17 +82,17 @@ void clientCredentials10() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000002") - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000002") + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** @@ -102,23 +105,23 @@ void clientCredentials2() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000003") - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000003") + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** @@ -131,22 +134,22 @@ void clientCredentials3() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000004") - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000004") + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** @@ -159,21 +162,21 @@ void clientCredentials4() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000005") - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000005") + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** @@ -186,22 +189,22 @@ void clientCredentials5() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000006") - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000006") + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** @@ -214,21 +217,21 @@ void clientCredentials6() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000007") - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000007") + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** @@ -241,20 +244,20 @@ void clientCredentials7() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000008") - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000008") + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** @@ -267,19 +270,19 @@ void clientCredentials8() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000009") - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000009") + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** @@ -292,22 +295,22 @@ void clientCredentials9() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000000a") - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000000a") + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void clientCredentialsAtm1() { @@ -316,50 +319,50 @@ void clientCredentialsAtm1() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000000b") - .header("Channel", Channel.ATM) - .header("MerchantId", merchantId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000000b") + .header(HeaderParamName.CHANNEL, Channel.ATM) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void clientCredentialsAtm10() { given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000000c") - .header("Channel", Channel.ATM) - .header("AcquirerId", acquirerId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000000c") + .header(HeaderParamName.CHANNEL, Channel.ATM) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void clientCredentialsAtm2() { @@ -368,211 +371,211 @@ void clientCredentialsAtm2() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000000d") - .header("Channel", Channel.ATM) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000000d") + .header(HeaderParamName.CHANNEL, Channel.ATM) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void clientCredentialsAtm3() { given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000000e") - .header("Channel", Channel.ATM) - .header("AcquirerId", acquirerId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000000e") + .header(HeaderParamName.CHANNEL, Channel.ATM) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void clientCredentialsAtm4() { given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000000f") - .header("Channel", Channel.ATM) - .header("AcquirerId", acquirerId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000000f") + .header(HeaderParamName.CHANNEL, Channel.ATM) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void clientCredentialsAtm5() { given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000010") - .header("Channel", Channel.ATM) - .header("AcquirerId", acquirerId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000010") + .header(HeaderParamName.CHANNEL, Channel.ATM) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void clientCredentialsAtm6() { given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000011") - .header("Channel", Channel.ATM) - .header("AcquirerId", acquirerId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000011") + .header(HeaderParamName.CHANNEL, Channel.ATM) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void clientCredentialsAtm7() { given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000012") - .header("Channel", Channel.ATM) - .header("AcquirerId", acquirerId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000012") + .header(HeaderParamName.CHANNEL, Channel.ATM) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void clientCredentialsAtm8() { given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000013") - .header("Channel", Channel.ATM) - .header("AcquirerId", acquirerId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000013") + .header(HeaderParamName.CHANNEL, Channel.ATM) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void clientCredentialsAtm9() { given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000014") - .header("Channel", Channel.ATM) - .header("AcquirerId", acquirerId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000014") + .header(HeaderParamName.CHANNEL, Channel.ATM) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void clientCredentialsPos1() { @@ -581,22 +584,22 @@ void clientCredentialsPos1() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000015") - .header("Channel", Channel.POS) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000015") + .header(HeaderParamName.CHANNEL, Channel.POS) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** @@ -609,21 +612,21 @@ void clientCredentialsPos10() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000016") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000016") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** @@ -636,23 +639,23 @@ void clientCredentialsPos2() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000018") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000018") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** @@ -665,24 +668,24 @@ void clientCredentialsPos3() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000019") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000019") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** @@ -695,25 +698,25 @@ void clientCredentialsPos4() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000001a") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000001a") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** @@ -726,26 +729,26 @@ void clientCredentialsPos5() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000001b") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000001b") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** @@ -758,25 +761,25 @@ void clientCredentialsPos6() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000001c") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000001c") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** @@ -789,24 +792,24 @@ void clientCredentialsPos7() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000001d") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000001d") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** @@ -819,23 +822,23 @@ void clientCredentialsPos8() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000001e") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000001e") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** @@ -848,26 +851,26 @@ void clientCredentialsPos9() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000001f") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000001f") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void password1() { @@ -876,25 +879,25 @@ void password1() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000020") - .header("Channel", Channel.POS) - .formParam("grant_type", GrantType.PASSWORD) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000020") + .header(HeaderParamName.CHANNEL, Channel.POS) + .formParam(FormParamName.GRANT_TYPE, GrantType.PASSWORD) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void password2() { @@ -903,26 +906,26 @@ void password2() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000021") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .formParam("grant_type", GrantType.PASSWORD) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000021") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .formParam(FormParamName.GRANT_TYPE, GrantType.PASSWORD) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void password3() { @@ -931,27 +934,27 @@ void password3() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000022") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .formParam("grant_type", GrantType.PASSWORD) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000022") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .formParam(FormParamName.GRANT_TYPE, GrantType.PASSWORD) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void password4() { @@ -960,28 +963,28 @@ void password4() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000023") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.PASSWORD) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000023") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.PASSWORD) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void password5() { @@ -990,27 +993,27 @@ void password5() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000024") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.PASSWORD) - .formParam("client_id", clientId) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000024") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.PASSWORD) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void password6() { @@ -1019,26 +1022,26 @@ void password6() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000025") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.PASSWORD) - .formParam("client_id", clientId) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000025") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.PASSWORD) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void password7() { @@ -1047,25 +1050,25 @@ void password7() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000026") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.PASSWORD) - .formParam("client_id", clientId) - .formParam("refresh_token", refreshToken) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000026") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.PASSWORD) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void password8() { @@ -1074,24 +1077,24 @@ void password8() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000027") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.PASSWORD) - .formParam("client_id", clientId) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000027") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.PASSWORD) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void password9() { @@ -1100,25 +1103,25 @@ void password9() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000028") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.PASSWORD) - .formParam("client_id", clientId) - .formParam("username", username) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000028") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.PASSWORD) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void poynt1() { @@ -1127,25 +1130,25 @@ void poynt1() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000029") - .header("Channel", Channel.POS) - .formParam("grant_type", GrantType.POYNT_TOKEN) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000029") + .header(HeaderParamName.CHANNEL, Channel.POS) + .formParam(FormParamName.GRANT_TYPE, GrantType.POYNT_TOKEN) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void poynt2() { @@ -1154,26 +1157,26 @@ void poynt2() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000002a") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .formParam("grant_type", GrantType.POYNT_TOKEN) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000002a") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .formParam(FormParamName.GRANT_TYPE, GrantType.POYNT_TOKEN) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void poynt3() { @@ -1182,27 +1185,27 @@ void poynt3() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000002b") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .formParam("grant_type", GrantType.POYNT_TOKEN) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000002b") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .formParam(FormParamName.GRANT_TYPE, GrantType.POYNT_TOKEN) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void poynt4() { @@ -1211,28 +1214,28 @@ void poynt4() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000002c") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.POYNT_TOKEN) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000002c") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.POYNT_TOKEN) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void poynt5() { @@ -1241,27 +1244,27 @@ void poynt5() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000002d") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.POYNT_TOKEN) - .formParam("client_id", clientId) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000002d") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.POYNT_TOKEN) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void poynt6() { @@ -1270,28 +1273,28 @@ void poynt6() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000002e") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.POYNT_TOKEN) - .formParam("client_id", clientId) - .formParam("ext_token", extToken) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000002e") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.POYNT_TOKEN) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void poynt7() { @@ -1300,29 +1303,29 @@ void poynt7() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000002f") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.POYNT_TOKEN) - .formParam("client_id", clientId) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000002f") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.POYNT_TOKEN) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void poynt8() { @@ -1331,28 +1334,28 @@ void poynt8() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000030") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.POYNT_TOKEN) - .formParam("client_id", clientId) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000030") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.POYNT_TOKEN) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void poynt9() { @@ -1361,27 +1364,27 @@ void poynt9() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000031") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.POYNT_TOKEN) - .formParam("client_id", clientId) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000031") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.POYNT_TOKEN) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void refreshToken1() { @@ -1390,26 +1393,26 @@ void refreshToken1() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000032") - .header("Channel", Channel.POS) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000032") + .header(HeaderParamName.CHANNEL, Channel.POS) + .formParam(FormParamName.GRANT_TYPE, GrantType.REFRESH_TOKEN) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void refreshToken2() { @@ -1418,27 +1421,27 @@ void refreshToken2() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000033") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000033") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .formParam(FormParamName.GRANT_TYPE, GrantType.REFRESH_TOKEN) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void refreshToken3() { @@ -1447,28 +1450,28 @@ void refreshToken3() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000034") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000034") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .formParam(FormParamName.GRANT_TYPE, GrantType.REFRESH_TOKEN) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void refreshToken4() { @@ -1477,29 +1480,29 @@ void refreshToken4() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000035") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("client_id", clientId) - .formParam("client_secret", clientSecret) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000035") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.REFRESH_TOKEN) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.CLIENT_SECRET, clientSecret) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void refreshToken5() { @@ -1508,28 +1511,28 @@ void refreshToken5() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000036") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("client_id", clientId) - .formParam("ext_token", extToken) - .formParam("add_data", addData) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000036") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.REFRESH_TOKEN) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.EXT_TOKEN, extToken) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void refreshToken6() { @@ -1538,27 +1541,27 @@ void refreshToken6() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000037") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("client_id", clientId) - .formParam("add_data", addData) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000037") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.REFRESH_TOKEN) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.ADD_DATA, addData) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void refreshToken7() { @@ -1567,26 +1570,26 @@ void refreshToken7() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000038") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("client_id", clientId) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000038") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.REFRESH_TOKEN) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void refreshToken8() { @@ -1595,27 +1598,27 @@ void refreshToken8() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-100000000039") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("client_id", clientId) - .formParam("refresh_token", refreshToken) - .formParam("username", username) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-100000000039") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.REFRESH_TOKEN) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.USERNAME, username) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void refreshToken9() { @@ -1624,26 +1627,26 @@ void refreshToken9() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000003a") - .header("Channel", Channel.POS) - .header("AcquirerId", acquirerId) - .header("MerchantId", merchantId) - .header("TerminalId", terminalId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("client_id", clientId) - .formParam("refresh_token", refreshToken) - .formParam("password", password) - .formParam("scope", "offline_access") + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000003a") + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.ACQUIRER_ID, acquirerId) + .header(HeaderParamName.MERCHANT_ID, merchantId) + .header(HeaderParamName.TERMINAL_ID, terminalId) + .formParam(FormParamName.GRANT_TYPE, GrantType.REFRESH_TOKEN) + .formParam(FormParamName.CLIENT_ID, clientId) + .formParam(FormParamName.REFRESH_TOKEN, refreshToken) + .formParam(FormParamName.PASSWORD, password) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } /** - * + * */ @Test void withoutValidator() { @@ -1652,14 +1655,14 @@ void withoutValidator() { */ given() .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-10000000003b") - .formParam("grant_type", GrantType.PASSWORD) - .formParam("client_id", clientId) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-10000000003b") + .formParam(FormParamName.GRANT_TYPE, GrantType.PASSWORD) + .formParam(FormParamName.CLIENT_ID, clientId) .when() .post() .then() .statusCode(400) .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); + .body(JsonPropertyName.ERRORS, notNullValue()); } } \ No newline at end of file diff --git a/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenByClientSecretResourceTest.java b/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenByClientSecretResourceTest.java new file mode 100644 index 00000000..f522fbd0 --- /dev/null +++ b/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenByClientSecretResourceTest.java @@ -0,0 +1,803 @@ +/* + * TokenByClientSecretResourceTest.java + * + * 8 ago 2023 + */ +package it.pagopa.swclient.mil.auth.resource; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import java.time.Instant; +import java.util.List; + +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +import io.quarkus.test.InjectMock; +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; +import io.smallrye.mutiny.Uni; +import it.pagopa.swclient.mil.auth.AuthErrorCode; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.CreateKeyRequest; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.CreateKeyResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetAccessTokenResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeyResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeyVersionsResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeysResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.Key; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.KeyAttributes; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.KeyDetails; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.KeyVersion; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.SignRequest; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.SignResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.client.AzureAuthClient; +import it.pagopa.swclient.mil.auth.azurekeyvault.client.AzureKeyVaultClient; +import it.pagopa.swclient.mil.auth.bean.Client; +import it.pagopa.swclient.mil.auth.bean.FormParamName; +import it.pagopa.swclient.mil.auth.bean.GrantType; +import it.pagopa.swclient.mil.auth.bean.HeaderParamName; +import it.pagopa.swclient.mil.auth.bean.JsonPropertyName; +import it.pagopa.swclient.mil.auth.bean.Role; +import it.pagopa.swclient.mil.auth.bean.TokenType; +import it.pagopa.swclient.mil.auth.client.AuthDataRepository; +import it.pagopa.swclient.mil.auth.util.UniGenerator; +import it.pagopa.swclient.mil.bean.Channel; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +/** + * Steps to test: clientVerifier.verify roleFinder.findRoles generateToken tokenSigner.sign + * + * @author Antonio Tarricone + */ +@QuarkusTest +@TestHTTPEndpoint(TokenResource.class) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class TokenByClientSecretResourceTest { + /* + * + */ + private static final String ACQUIRER_ID = "4585625"; + private static final String MERCHANT_ID = "28405fHfk73x88D"; + private static final String TERMINAL_ID = "12345678"; + private static final String CLIENT_ID = "3965df56-ca9a-49e5-97e8-061433d4a25b"; + private static final String SALT = "aGw/h/8Fm9S2aNvlvIaxJyhKP67ZU4FEm6mDVhL3aEVrahXFif9x2BkQ4OY87Z9tWVyWbSB/JeztYVmTshrFWQ=="; + private static final String HASH = "G3oYMwnLVR9+m7WB4/pvoVeHxzsTdeyhndpVoruHzog="; + private static final String SECRET = "5ceef788-4115-43a7-a704-b1bcc9a47c86"; + private static final String DESCRIPTION = "VAS Layer"; + private static final String WRONG_SECRET = "3674f0e7-d717-44cc-a3bc-5f8f41771fea"; + private static final List ROLES = List.of("NoticePayer", "SlavePos"); + + /* + * + */ + private static final long AZURE_TOKEN_DURATION = 3599; + private static final String AZURE_TOKEN = "this_is_the_token"; + private static final String AUTHORIZATION_HDR_VALUE = TokenType.BEARER + " " + AZURE_TOKEN; + + /* + * + */ + private static final String KEY_URL = "https://mil-d-appl-kv.vault.azure.net/keys/"; + private static final String KEY_NAME = "0709643f49394529b92c19a68c8e184a"; + private static final String KEY_VERSION = "6581c704deda4979943c3b34468df7c2"; + private static final String KID = KEY_NAME + "/" + KEY_VERSION; + private static final String KEY_RECOVERY_LEVEL = "Purgeable"; + private static final String KEY_TYPE = "RSA"; + private static final String[] KEY_OPS = new String[] { + "verify", "sign" + }; + private static final String MODULUS = "AKnFsF5Y16TB9qkmoOyDXG3ulenUWYoW78U7mcGBoYKRpMlswxhc_ZiKcC65vIrCP6hbS5Cx88IbQG2DWH-nE329OLzUbzcdraDLR-7V2BX0nNwmwXxhkd4ofzzjKyhWjV8AkxFpqJPtFG09YCyCpaC8YluVPbHUpWJ1wrOsavdc_YM1W1XuaGvJv4SkilM8vBa81zOLEVhbEE5msHxPNLwVyC_0PIE6OFL9RY4YP1U1q7gjTMmKDc9qgEYkdziMnlxWp_EkKTZOERbEatP0fditFt-zWKlXw0qO4FKFlmj9n5tbB55vaopB71Kv6LcsAY1Q-fgOuoM41HldLppzfDOPwLGyCQF9ODJt1xaKkup6i_BxZum7-QckibwPaj3ODZbYsPuNZ_npQiR6NJZ_q_31YMlyuGdqltawluYLJidw3EzkpTN__bHdio892WbY29PRwbrG486IJ_88qP3lWs1TfzohVa1czUOZwQHqp0ixVBi_SK3jICk-V65DbwzgS5zwBFaqfWO3XVOf6tmWFMZ6ly7wtOnYWoMR15rudsD5xXWwqE-s7IP1lVZuIOdMfLH7-1Pgn-YJuPsBLbZri9_M4KtflYbqnuDckSyFNBynTwoSvSSuBhpkmNgiSQ-WBXHHss5Wy-pr-YjNK7JYppPOHvfHSY96XnJl9SPWcnwx"; + private static final String PUBLIC_EXPONENT = "AQAB"; + private static final String EXPECTED_SIGNATURE = "expected_signature"; + + /* + * + */ + @InjectMock + @RestClient + AuthDataRepository repository; + + /* + * + */ + @InjectMock + @RestClient + AzureKeyVaultClient keyVaultClient; + + /* + * + */ + @InjectMock + @RestClient + AzureAuthClient authClient; + + @Test + void testOk() { + /* + * Client repository setup. + */ + when(repository.getClient(CLIENT_ID)) + .thenReturn(UniGenerator.item(new Client(CLIENT_ID, Channel.POS, SALT, HASH, DESCRIPTION))); + + /* + * Roles repository setup. + */ + when(repository.getRoles(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID)) + .thenReturn(UniGenerator.item(new Role(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID, ROLES))); + + /* + * Azure auth. client setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(UniGenerator.item(new GetAccessTokenResponse(JsonPropertyName.TOKEN_TYPE, AZURE_TOKEN_DURATION, AZURE_TOKEN_DURATION, AZURE_TOKEN))); + + /* + * Azure key vault setup. + */ + long now = Instant.now().getEpochSecond(); + KeyAttributes keyAttributes = new KeyAttributes(now - 300, now + 600, now - 300, now - 300, Boolean.TRUE, KEY_RECOVERY_LEVEL, 0, Boolean.FALSE); + + when(keyVaultClient.getKeys(AUTHORIZATION_HDR_VALUE)) + .thenReturn(UniGenerator.item(new GetKeysResponse(new Key[]{ + new Key(KEY_URL + KEY_NAME, keyAttributes) + }))); + + when(keyVaultClient.getKeyVersions(AUTHORIZATION_HDR_VALUE, KEY_NAME)) + .thenReturn(UniGenerator.item(new GetKeyVersionsResponse(new KeyVersion[]{ + new KeyVersion(KEY_URL + KEY_NAME + "/" + KEY_VERSION, keyAttributes) + }))); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, KEY_NAME, KEY_VERSION)) + .thenReturn(UniGenerator.item(new GetKeyResponse(new KeyDetails(KEY_URL + KEY_NAME + "/" + KEY_VERSION, KEY_TYPE, KEY_OPS, MODULUS, PUBLIC_EXPONENT, keyAttributes)))); + + when(keyVaultClient.sign(eq(AUTHORIZATION_HDR_VALUE), eq(KEY_NAME), eq(KEY_VERSION), any(SignRequest.class))) + .thenReturn(UniGenerator.item(new SignResponse(KID, EXPECTED_SIGNATURE))); + + /* + * Test. + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-000000000000") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_SECRET, SECRET) + .when() + .post() + .then() + .log() + .everything() + .statusCode(200) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ACCESS_TOKEN, notNullValue()) + .body(JsonPropertyName.TOKEN_TYPE, equalTo(TokenType.BEARER)) + .body(JsonPropertyName.EXPIRES_IN, notNullValue(Long.class)) + .body(JsonPropertyName.REFRESH_TOKEN, nullValue()); + } + + @Test + void testOkForAtm() { + /* + * Client repository setup. + */ + when(repository.getClient(CLIENT_ID)) + .thenReturn(UniGenerator.item(new Client(CLIENT_ID, Channel.ATM, SALT, HASH, DESCRIPTION))); + + /* + * Roles repository setup. + */ + when(repository.getRoles(ACQUIRER_ID, Channel.ATM, CLIENT_ID, "NA", TERMINAL_ID)) + .thenReturn(UniGenerator.item(new Role(ACQUIRER_ID, Channel.ATM, CLIENT_ID, "NA", TERMINAL_ID, ROLES))); + + /* + * Azure auth. client setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(UniGenerator.item(new GetAccessTokenResponse(JsonPropertyName.TOKEN_TYPE, AZURE_TOKEN_DURATION, AZURE_TOKEN_DURATION, AZURE_TOKEN))); + + /* + * Azure key vault setup. + */ + long now = Instant.now().getEpochSecond(); + KeyAttributes keyAttributes = new KeyAttributes(now - 300, now + 600, now - 300, now - 300, Boolean.TRUE, KEY_RECOVERY_LEVEL, 0, Boolean.FALSE); + + when(keyVaultClient.getKeys(AUTHORIZATION_HDR_VALUE)) + .thenReturn(UniGenerator.item(new GetKeysResponse(new Key[]{ + new Key(KEY_URL + KEY_NAME, keyAttributes) + }))); + + when(keyVaultClient.getKeyVersions(AUTHORIZATION_HDR_VALUE, KEY_NAME)) + .thenReturn(UniGenerator.item(new GetKeyVersionsResponse(new KeyVersion[]{ + new KeyVersion(KEY_URL + KEY_NAME + "/" + KEY_VERSION, keyAttributes) + }))); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, KEY_NAME, KEY_VERSION)) + .thenReturn(UniGenerator.item(new GetKeyResponse(new KeyDetails(KEY_URL + KEY_NAME + "/" + KEY_VERSION, KEY_TYPE, KEY_OPS, MODULUS, PUBLIC_EXPONENT, keyAttributes)))); + + when(keyVaultClient.sign(eq(AUTHORIZATION_HDR_VALUE), eq(KEY_NAME), eq(KEY_VERSION), any(SignRequest.class))) + .thenReturn(UniGenerator.item(new SignResponse(KID, EXPECTED_SIGNATURE))); + + /* + * Test. + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-00000000000C") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.ATM) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_SECRET, SECRET) + .when() + .post() + .then() + .log() + .everything() + .statusCode(200) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ACCESS_TOKEN, notNullValue()) + .body(JsonPropertyName.TOKEN_TYPE, equalTo(TokenType.BEARER)) + .body(JsonPropertyName.EXPIRES_IN, notNullValue(Long.class)) + .body(JsonPropertyName.REFRESH_TOKEN, nullValue()); + } + + @Test + void testOkForPortal() { + /* + * Client repository setup. + */ + when(repository.getClient(CLIENT_ID)) + .thenReturn(UniGenerator.item(new Client(CLIENT_ID, null, SALT, HASH, DESCRIPTION))); + + /* + * Roles repository setup. + */ + when(repository.getRoles("NA", "NA", CLIENT_ID, "NA", "NA")) + .thenReturn(UniGenerator.item(new Role("NA", "NA", CLIENT_ID, "NA", "NA", ROLES))); + + /* + * Azure auth. client setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(UniGenerator.item(new GetAccessTokenResponse(JsonPropertyName.TOKEN_TYPE, AZURE_TOKEN_DURATION, AZURE_TOKEN_DURATION, AZURE_TOKEN))); + + /* + * Azure key vault setup. + */ + long now = Instant.now().getEpochSecond(); + KeyAttributes keyAttributes = new KeyAttributes(now - 300, now + 600, now - 300, now - 300, Boolean.TRUE, KEY_RECOVERY_LEVEL, 0, Boolean.FALSE); + + when(keyVaultClient.getKeys(AUTHORIZATION_HDR_VALUE)) + .thenReturn(UniGenerator.item(new GetKeysResponse(new Key[]{ + new Key(KEY_URL + KEY_NAME, keyAttributes) + }))); + + when(keyVaultClient.getKeyVersions(AUTHORIZATION_HDR_VALUE, KEY_NAME)) + .thenReturn(UniGenerator.item(new GetKeyVersionsResponse(new KeyVersion[]{ + new KeyVersion(KEY_URL + KEY_NAME + "/" + KEY_VERSION, keyAttributes) + }))); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, KEY_NAME, KEY_VERSION)) + .thenReturn(UniGenerator.item(new GetKeyResponse(new KeyDetails(KEY_URL + KEY_NAME + "/" + KEY_VERSION, KEY_TYPE, KEY_OPS, MODULUS, PUBLIC_EXPONENT, keyAttributes)))); + + when(keyVaultClient.sign(eq(AUTHORIZATION_HDR_VALUE), eq(KEY_NAME), eq(KEY_VERSION), any(SignRequest.class))) + .thenReturn(UniGenerator.item(new SignResponse(KID, EXPECTED_SIGNATURE))); + + /* + * Test. + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-00000000000D") + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_SECRET, SECRET) + .when() + .post() + .then() + .log() + .everything() + .statusCode(200) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ACCESS_TOKEN, notNullValue()) + .body(JsonPropertyName.TOKEN_TYPE, equalTo(TokenType.BEARER)) + .body(JsonPropertyName.EXPIRES_IN, notNullValue(Long.class)) + .body(JsonPropertyName.REFRESH_TOKEN, nullValue()); + } + + @Test + void testClientNotFound() { + when(repository.getClient(anyString())) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()))); + + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-000000000001") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_SECRET, SECRET) + .when() + .post() + .then() + .log() + .everything() + .statusCode(401) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.CLIENT_NOT_FOUND)); + } + + @Test + void testWebApplicationExceptionSerchingClient() { + when(repository.getClient(anyString())) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.INTERNAL_SERVER_ERROR).build()))); + + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-000000000002") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_SECRET, SECRET) + .when() + .post() + .then() + .log() + .everything() + .statusCode(500) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.ERROR_SEARCHING_FOR_CLIENT)); + } + + @Test + void testExceptionSearchingClient() { + when(repository.getClient(anyString())) + .thenReturn(Uni.createFrom().failure(new Exception())); + + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-000000000003") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_SECRET, SECRET) + .when() + .post() + .then() + .log() + .everything() + .statusCode(500) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.ERROR_SEARCHING_FOR_CLIENT)); + } + + @Test + void testClientHasWrongChannel() { + when(repository.getClient(CLIENT_ID)) + .thenReturn(UniGenerator.item(new Client(CLIENT_ID, Channel.ATM, SALT, HASH, DESCRIPTION))); + + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-000000000004") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_SECRET, SECRET) + .when() + .post() + .then() + .log() + .everything() + .statusCode(401) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.WRONG_CHANNEL)); + } + + @Test + void testWrongSecret() { + when(repository.getClient(CLIENT_ID)) + .thenReturn(UniGenerator.item(new Client(CLIENT_ID, Channel.POS, SALT, HASH, DESCRIPTION))); + + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-000000000005") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_SECRET, WRONG_SECRET) + .when() + .post() + .then() + .log() + .everything() + .statusCode(401) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.WRONG_SECRET)); + } + + @Test + void testWrongSecretWithNullExpected() { + when(repository.getClient(CLIENT_ID)) + .thenReturn(UniGenerator.item(new Client(CLIENT_ID, Channel.POS, null, null, DESCRIPTION))); + + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-000000000006") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_SECRET, SECRET) + .when() + .post() + .then() + .log() + .everything() + .statusCode(401) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.WRONG_SECRET)); + } + + @Test + void testRolesNotFound() { + when(repository.getClient(CLIENT_ID)) + .thenReturn(UniGenerator.item(new Client(CLIENT_ID, Channel.POS, SALT, HASH, DESCRIPTION))); + + when(repository.getRoles(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID)) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()))); + + when(repository.getRoles(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, "NA")) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()))); + + when(repository.getRoles(ACQUIRER_ID, Channel.POS, CLIENT_ID, "NA", "NA")) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()))); + + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-000000000007") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_SECRET, SECRET) + .when() + .post() + .then() + .log() + .everything() + .statusCode(401) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.ROLES_NOT_FOUND)); + } + + @Test + void testWebApplicationExceptionSearchingRoles() { + when(repository.getClient(CLIENT_ID)) + .thenReturn(UniGenerator.item(new Client(CLIENT_ID, Channel.POS, SALT, HASH, DESCRIPTION))); + + when(repository.getRoles(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID)) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.INTERNAL_SERVER_ERROR).build()))); + + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-000000000008") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_SECRET, SECRET) + .when() + .post() + .then() + .log() + .everything() + .statusCode(500) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.ERROR_SEARCHING_FOR_ROLES)); + } + + @Test + void testExceptionSearchingRoles() { + when(repository.getClient(CLIENT_ID)) + .thenReturn(UniGenerator.item(new Client(CLIENT_ID, Channel.POS, SALT, HASH, DESCRIPTION))); + + when(repository.getRoles(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID)) + .thenReturn(Uni.createFrom().failure(new Exception())); + + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-000000000009") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_SECRET, SECRET) + .when() + .post() + .then() + .log() + .everything() + .statusCode(500) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.ERROR_SEARCHING_FOR_ROLES)); + } + + @Test + void test401OnGetAccessToken() { + /* + * Client repository setup. + */ + when(repository.getClient(CLIENT_ID)) + .thenReturn(UniGenerator.item(new Client(CLIENT_ID, Channel.POS, SALT, HASH, DESCRIPTION))); + + /* + * Roles repository setup. + */ + when(repository.getRoles(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID)) + .thenReturn(UniGenerator.item(new Role(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID, ROLES))); + + /* + * Azure auth. client setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED).build()))); + + /* + * Test. + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-00000000000A") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_SECRET, SECRET) + .when() + .post() + .then() + .log() + .everything() + .statusCode(500) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.ERROR_FROM_AZURE)); + } + + @Test + void test401OnGetKeys() { + /* + * Client repository setup. + */ + when(repository.getClient(CLIENT_ID)) + .thenReturn(UniGenerator.item(new Client(CLIENT_ID, Channel.POS, SALT, HASH, DESCRIPTION))); + + /* + * Roles repository setup. + */ + when(repository.getRoles(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID)) + .thenReturn(UniGenerator.item(new Role(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID, ROLES))); + + /* + * Azure auth. client setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(UniGenerator.item(new GetAccessTokenResponse(JsonPropertyName.TOKEN_TYPE, AZURE_TOKEN_DURATION, AZURE_TOKEN_DURATION, AZURE_TOKEN))); + + /* + * Azure key vault setup. + */ + when(keyVaultClient.getKeys(AUTHORIZATION_HDR_VALUE)) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED).build()))); + + /* + * Test. + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-00000000000A") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_SECRET, SECRET) + .when() + .post() + .then() + .log() + .everything() + .statusCode(500) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.ERROR_FROM_AZURE)); + } + + @Test + void test401WithNullAccessToken() { + /* + * Client repository setup. + */ + when(repository.getClient(CLIENT_ID)) + .thenReturn(UniGenerator.item(new Client(CLIENT_ID, Channel.POS, SALT, HASH, DESCRIPTION))); + + /* + * Roles repository setup. + */ + when(repository.getRoles(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID)) + .thenReturn(UniGenerator.item(new Role(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID, ROLES))); + + /* + * Azure auth. client setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(UniGenerator.item(new GetAccessTokenResponse(JsonPropertyName.TOKEN_TYPE, AZURE_TOKEN_DURATION, AZURE_TOKEN_DURATION, null))); + + /* + * Test. + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-00000000000A") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_SECRET, SECRET) + .when() + .post() + .then() + .log() + .everything() + .statusCode(500) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.AZURE_ACCESS_TOKEN_IS_NULL)); + } + + @Test + void testExpiredKeyOnKeyCreation() { + /* + * Client repository setup. + */ + when(repository.getClient(CLIENT_ID)) + .thenReturn(UniGenerator.item(new Client(CLIENT_ID, Channel.POS, SALT, HASH, DESCRIPTION))); + + /* + * Roles repository setup. + */ + when(repository.getRoles(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID)) + .thenReturn(UniGenerator.item(new Role(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID, ROLES))); + + /* + * Azure auth. client setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(UniGenerator.item(new GetAccessTokenResponse(JsonPropertyName.TOKEN_TYPE, AZURE_TOKEN_DURATION, AZURE_TOKEN_DURATION, AZURE_TOKEN))); + + /* + * Azure key vault setup. + */ + when(keyVaultClient.getKeys(AUTHORIZATION_HDR_VALUE)) + .thenReturn(Uni.createFrom().item(new GetKeysResponse(new Key[]{}))); + + long now = Instant.now().getEpochSecond(); + when(keyVaultClient.createKey(eq(AUTHORIZATION_HDR_VALUE), anyString(), any(CreateKeyRequest.class))) + .thenReturn(Uni.createFrom().item(new CreateKeyResponse(new KeyDetails(KEY_URL + KEY_NAME + "/" + KEY_VERSION, KEY_TYPE, KEY_OPS, MODULUS, PUBLIC_EXPONENT, new KeyAttributes(now - 300, now - 100, now - 300, now - 300, Boolean.TRUE, KEY_RECOVERY_LEVEL, 0, Boolean.FALSE))))); + + /* + * Test. + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-00000000000A") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_SECRET, SECRET) + .when() + .post() + .then() + .log() + .everything() + .statusCode(500) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.ERROR_GENERATING_KEY_PAIR)); + } + + @Test + void testErrorOnKeyCreation() { + /* + * Client repository setup. + */ + when(repository.getClient(CLIENT_ID)) + .thenReturn(UniGenerator.item(new Client(CLIENT_ID, Channel.POS, SALT, HASH, DESCRIPTION))); + + /* + * Roles repository setup. + */ + when(repository.getRoles(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID)) + .thenReturn(UniGenerator.item(new Role(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID, ROLES))); + + /* + * Azure auth. client setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(UniGenerator.item(new GetAccessTokenResponse(JsonPropertyName.TOKEN_TYPE, AZURE_TOKEN_DURATION, AZURE_TOKEN_DURATION, AZURE_TOKEN))); + + /* + * Azure key vault setup. + */ + when(keyVaultClient.getKeys(AUTHORIZATION_HDR_VALUE)) + .thenReturn(Uni.createFrom().item(new GetKeysResponse(new Key[]{}))); + + when(keyVaultClient.createKey(eq(AUTHORIZATION_HDR_VALUE), anyString(), any(CreateKeyRequest.class))) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED).build()))); + + /* + * Test. + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-00000000000B") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_SECRET, SECRET) + .when() + .post() + .then() + .log() + .everything() + .statusCode(500) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.ERROR_GENERATING_KEY_PAIR)); + } +} diff --git a/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenByClientSecretResourceUnexpectedErrorTest.java b/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenByClientSecretResourceUnexpectedErrorTest.java new file mode 100644 index 00000000..ec1aa730 --- /dev/null +++ b/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenByClientSecretResourceUnexpectedErrorTest.java @@ -0,0 +1,82 @@ +/* + * TokenByClientSecretResourceUnexpectedErrorTest.java + * + * 14 set 2023 + */ +package it.pagopa.swclient.mil.auth.resource; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.hasItem; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +import io.quarkus.test.InjectMock; +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; +import io.smallrye.mutiny.Uni; +import it.pagopa.swclient.mil.auth.AuthErrorCode; +import it.pagopa.swclient.mil.auth.bean.FormParamName; +import it.pagopa.swclient.mil.auth.bean.GrantType; +import it.pagopa.swclient.mil.auth.bean.HeaderParamName; +import it.pagopa.swclient.mil.auth.bean.JsonPropertyName; +import it.pagopa.swclient.mil.auth.service.ClientVerifier; +import it.pagopa.swclient.mil.bean.Channel; +import jakarta.ws.rs.core.MediaType; + +/** + * + * @author Antonio Tarricone + */ +@QuarkusTest +@TestHTTPEndpoint(TokenResource.class) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class TokenByClientSecretResourceUnexpectedErrorTest { + /* + * + */ + private static final String ACQUIRER_ID = "4585625"; + private static final String MERCHANT_ID = "28405fHfk73x88D"; + private static final String TERMINAL_ID = "12345678"; + private static final String CLIENT_ID = "3965df56-ca9a-49e5-97e8-061433d4a25b"; + private static final String SECRET = "5ceef788-4115-43a7-a704-b1bcc9a47c86"; + + /* + * + */ + @InjectMock + ClientVerifier clientVerifier; + + @Test + void testUnexpectedError() { + /* + * Client repository setup. + */ + when(clientVerifier.verify(anyString(), anyString(), anyString())) + .thenReturn(Uni.createFrom().failure(new Exception("synthetic exception"))); + + /* + * Test. + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-600000000000") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(FormParamName.CLIENT_SECRET, SECRET) + .when() + .post() + .then() + .log() + .everything() + .statusCode(500) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.UNEXPECTED_ERROR)); + } +} diff --git a/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenByPasswordResourceTest.java b/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenByPasswordResourceTest.java new file mode 100644 index 00000000..f95f2e90 --- /dev/null +++ b/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenByPasswordResourceTest.java @@ -0,0 +1,483 @@ +/* + * TokenByPasswordResourceTest.java + * + * 31 ago 2023 + */ +package it.pagopa.swclient.mil.auth.resource; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.Instant; +import java.util.Base64; +import java.util.List; + +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +import io.quarkus.test.InjectMock; +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; +import io.smallrye.mutiny.Uni; +import it.pagopa.swclient.mil.auth.AuthErrorCode; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetAccessTokenResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeyResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeyVersionsResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeysResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.Key; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.KeyAttributes; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.KeyDetails; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.KeyVersion; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.SignRequest; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.SignResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.client.AzureAuthClient; +import it.pagopa.swclient.mil.auth.azurekeyvault.client.AzureKeyVaultClient; +import it.pagopa.swclient.mil.auth.bean.Client; +import it.pagopa.swclient.mil.auth.bean.FormParamName; +import it.pagopa.swclient.mil.auth.bean.GrantType; +import it.pagopa.swclient.mil.auth.bean.HeaderParamName; +import it.pagopa.swclient.mil.auth.bean.JsonPropertyName; +import it.pagopa.swclient.mil.auth.bean.Role; +import it.pagopa.swclient.mil.auth.bean.Scope; +import it.pagopa.swclient.mil.auth.bean.TokenType; +import it.pagopa.swclient.mil.auth.bean.User; +import it.pagopa.swclient.mil.auth.client.AuthDataRepository; +import it.pagopa.swclient.mil.auth.util.PasswordVerifier; +import it.pagopa.swclient.mil.auth.util.UniGenerator; +import it.pagopa.swclient.mil.bean.Channel; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +/** + * Get user from repository -> Verify Consistency -> Verify Password -> Super + * + * @author Antonio Tarricone + */ +@QuarkusTest +@TestHTTPEndpoint(TokenResource.class) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class TokenByPasswordResourceTest { + /* + * + */ + private static final String ACQUIRER_ID = "4585625"; + private static final String MERCHANT_ID = "28405fHfk73x88D"; + private static final String TERMINAL_ID = "12345678"; + private static final String CLIENT_ID = "3965df56-ca9a-49e5-97e8-061433d4a25b"; + private static final String DESCRIPTION = "VAS Layer"; + private static final List ROLES = List.of("NoticePayer", "SlavePos"); + + /* + * + */ + private static final String ACQUIRER_2_ID = "4585626"; + private static final String MERCHANT_2_ID = "28405fHfk73xkkk"; + + /* + * + */ + private static final long AZURE_TOKEN_DURATION = 3599; + private static final String AZURE_TOKEN = "this_is_the_token"; + private static final String AUTHORIZATION_HDR_VALUE = TokenType.BEARER + " " + AZURE_TOKEN; + + /* + * + */ + private static final String KEY_URL = "https://mil-d-appl-kv.vault.azure.net/keys/"; + private static final String KEY_NAME = "0709643f49394529b92c19a68c8e184a"; + private static final String KEY_VERSION = "6581c704deda4979943c3b34468df7c2"; + private static final String KID = KEY_NAME + "/" + KEY_VERSION; + private static final String KEY_RECOVERY_LEVEL = "Purgeable"; + private static final String KEY_TYPE = "RSA"; + private static final String[] KEY_OPS = new String[] { + "verify", "sign" + }; + private static final String MODULUS = "AKnFsF5Y16TB9qkmoOyDXG3ulenUWYoW78U7mcGBoYKRpMlswxhc_ZiKcC65vIrCP6hbS5Cx88IbQG2DWH-nE329OLzUbzcdraDLR-7V2BX0nNwmwXxhkd4ofzzjKyhWjV8AkxFpqJPtFG09YCyCpaC8YluVPbHUpWJ1wrOsavdc_YM1W1XuaGvJv4SkilM8vBa81zOLEVhbEE5msHxPNLwVyC_0PIE6OFL9RY4YP1U1q7gjTMmKDc9qgEYkdziMnlxWp_EkKTZOERbEatP0fditFt-zWKlXw0qO4FKFlmj9n5tbB55vaopB71Kv6LcsAY1Q-fgOuoM41HldLppzfDOPwLGyCQF9ODJt1xaKkup6i_BxZum7-QckibwPaj3ODZbYsPuNZ_npQiR6NJZ_q_31YMlyuGdqltawluYLJidw3EzkpTN__bHdio892WbY29PRwbrG486IJ_88qP3lWs1TfzohVa1czUOZwQHqp0ixVBi_SK3jICk-V65DbwzgS5zwBFaqfWO3XVOf6tmWFMZ6ly7wtOnYWoMR15rudsD5xXWwqE-s7IP1lVZuIOdMfLH7-1Pgn-YJuPsBLbZri9_M4KtflYbqnuDckSyFNBynTwoSvSSuBhpkmNgiSQ-WBXHHss5Wy-pr-YjNK7JYppPOHvfHSY96XnJl9SPWcnwx"; + private static final String PUBLIC_EXPONENT = "AQAB"; + private static final String EXPECTED_SIGNATURE = "expected_signature"; + + /* + * + */ + private static final String USERNAME = "user"; + private static final String PASSWORD = "password"; + private static final String SALT = "zfN59oSr9RfFiiSASUO1YIcv8bARsj1OAV8tEydQiKC3su5Mlz1TsjbFwvWrGCjXdkDUsbeXGnYZDavJuTKw6Q=="; + + /* + * + */ + private static final String PASSWORD_2 = "password2"; + + /* + * + */ + @InjectMock + @RestClient + AuthDataRepository repository; + + /* + * + */ + @InjectMock + @RestClient + AzureKeyVaultClient keyVaultClient; + + /* + * + */ + @InjectMock + @RestClient + AzureAuthClient authClient; + + @Test + void testOk() throws NoSuchAlgorithmException { + /* + * User respository setup. + */ + String userHash = Base64.getUrlEncoder().encodeToString( + MessageDigest.getInstance("SHA256").digest( + USERNAME.getBytes(StandardCharsets.UTF_8))); + + String passwordHash = Base64.getEncoder().encodeToString(PasswordVerifier.hashBytes(PASSWORD, SALT)); + + when(repository.getUser(userHash)) + .thenReturn(UniGenerator.item(new User(USERNAME, SALT, passwordHash, ACQUIRER_ID, Channel.POS, MERCHANT_ID))); + + /* + * Client repository setup. + */ + when(repository.getClient(CLIENT_ID)) + .thenReturn(UniGenerator.item(new Client(CLIENT_ID, Channel.POS, null, null, DESCRIPTION))); + + /* + * Roles repository setup. + */ + when(repository.getRoles(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID)) + .thenReturn(UniGenerator.item(new Role(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID, ROLES))); + + /* + * Azure auth. client setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(UniGenerator.item(new GetAccessTokenResponse(TokenType.BEARER, AZURE_TOKEN_DURATION, AZURE_TOKEN_DURATION, AZURE_TOKEN))); + + /* + * Azure key vault setup. + */ + long now = Instant.now().getEpochSecond(); + KeyAttributes keyAttributes = new KeyAttributes(now - 300, now + 600, now - 300, now - 300, Boolean.TRUE, KEY_RECOVERY_LEVEL, 0, Boolean.FALSE); + + when(keyVaultClient.getKeys(AUTHORIZATION_HDR_VALUE)) + .thenReturn(UniGenerator.item(new GetKeysResponse(new Key[] { + new Key(KEY_URL + KEY_NAME, keyAttributes) + }))); + + when(keyVaultClient.getKeyVersions(AUTHORIZATION_HDR_VALUE, KEY_NAME)) + .thenReturn(UniGenerator.item(new GetKeyVersionsResponse(new KeyVersion[] { + new KeyVersion(KEY_URL + KEY_NAME + "/" + KEY_VERSION, keyAttributes) + }))); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, KEY_NAME, KEY_VERSION)) + .thenReturn(UniGenerator.item(new GetKeyResponse(new KeyDetails(KEY_URL + KEY_NAME + "/" + KEY_VERSION, KEY_TYPE, KEY_OPS, MODULUS, PUBLIC_EXPONENT, keyAttributes)))); + + when(keyVaultClient.sign(eq(AUTHORIZATION_HDR_VALUE), eq(KEY_NAME), eq(KEY_VERSION), any(SignRequest.class))) + .thenReturn(UniGenerator.item(new SignResponse(KID, EXPECTED_SIGNATURE))); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-400000000000") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.PASSWORD) + .formParam(FormParamName.USERNAME, USERNAME) + .formParam(FormParamName.PASSWORD, PASSWORD) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) + .when() + .post() + .then() + .statusCode(200) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ACCESS_TOKEN, notNullValue()) + .body(JsonPropertyName.TOKEN_TYPE, equalTo(TokenType.BEARER)) + .body(JsonPropertyName.EXPIRES_IN, notNullValue(Long.class)) + .body(JsonPropertyName.REFRESH_TOKEN, notNullValue()); + } + + @Test + void testUserNotFound() throws NoSuchAlgorithmException { + /* + * User respository setup. + */ + String userHash = Base64.getUrlEncoder().encodeToString( + MessageDigest.getInstance("SHA256").digest( + USERNAME.getBytes(StandardCharsets.UTF_8))); + + when(repository.getUser(userHash)) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()))); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-400000000001") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.PASSWORD) + .formParam(FormParamName.USERNAME, USERNAME) + .formParam(FormParamName.PASSWORD, PASSWORD) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) + .when() + .post() + .then() + .statusCode(401) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.WRONG_CREDENTIALS)); + } + + @Test + void testWebApplicationExceptionGettingUser() throws NoSuchAlgorithmException { + /* + * User respository setup. + */ + String userHash = Base64.getUrlEncoder().encodeToString( + MessageDigest.getInstance("SHA256").digest( + USERNAME.getBytes(StandardCharsets.UTF_8))); + + when(repository.getUser(userHash)) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.INTERNAL_SERVER_ERROR).build()))); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-400000000002") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.PASSWORD) + .formParam(FormParamName.USERNAME, USERNAME) + .formParam(FormParamName.PASSWORD, PASSWORD) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) + .when() + .post() + .then() + .statusCode(500) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.ERROR_SEARCHING_FOR_CREDENTIALS)); + } + + @Test + void testExceptionGettingUser() throws NoSuchAlgorithmException { + /* + * User respository setup. + */ + String userHash = Base64.getUrlEncoder().encodeToString( + MessageDigest.getInstance("SHA256").digest( + USERNAME.getBytes(StandardCharsets.UTF_8))); + + when(repository.getUser(userHash)) + .thenReturn(Uni.createFrom().failure(new Exception("synthetic exception"))); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-400000000003") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.PASSWORD) + .formParam(FormParamName.USERNAME, USERNAME) + .formParam(FormParamName.PASSWORD, PASSWORD) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) + .when() + .post() + .then() + .statusCode(500) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.ERROR_SEARCHING_FOR_CREDENTIALS)); + } + + void testNoSuchAlgorithmFindingCredentials() { + // See TokenByPasswordServiceTest.testNoSuchAlgorithmFindingCredentials + } + + @Test + void testInconsistentAcquirerId() throws NoSuchAlgorithmException { + /* + * User respository setup. + */ + String userHash = Base64.getUrlEncoder().encodeToString( + MessageDigest.getInstance("SHA256").digest( + USERNAME.getBytes(StandardCharsets.UTF_8))); + + String passwordHash = Base64.getEncoder().encodeToString(PasswordVerifier.hashBytes(PASSWORD, SALT)); + + when(repository.getUser(userHash)) + .thenReturn(UniGenerator.item(new User(USERNAME, SALT, passwordHash, ACQUIRER_2_ID, Channel.POS, MERCHANT_ID))); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-400000000004") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.PASSWORD) + .formParam(FormParamName.USERNAME, USERNAME) + .formParam(FormParamName.PASSWORD, PASSWORD) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) + .when() + .post() + .then() + .statusCode(401) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.INCONSISTENT_CREDENTIALS)); + } + + @Test + void testInconsistentChannel() throws NoSuchAlgorithmException { + /* + * User respository setup. + */ + String userHash = Base64.getUrlEncoder().encodeToString( + MessageDigest.getInstance("SHA256").digest( + USERNAME.getBytes(StandardCharsets.UTF_8))); + + String passwordHash = Base64.getEncoder().encodeToString(PasswordVerifier.hashBytes(PASSWORD, SALT)); + + when(repository.getUser(userHash)) + .thenReturn(UniGenerator.item(new User(USERNAME, SALT, passwordHash, ACQUIRER_ID, Channel.ATM, MERCHANT_ID))); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-400000000005") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.PASSWORD) + .formParam(FormParamName.USERNAME, USERNAME) + .formParam(FormParamName.PASSWORD, PASSWORD) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) + .when() + .post() + .then() + .statusCode(401) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.INCONSISTENT_CREDENTIALS)); + } + + @Test + void testInconsistentMerchantId() throws NoSuchAlgorithmException { + /* + * User respository setup. + */ + String userHash = Base64.getUrlEncoder().encodeToString( + MessageDigest.getInstance("SHA256").digest( + USERNAME.getBytes(StandardCharsets.UTF_8))); + + String passwordHash = Base64.getEncoder().encodeToString(PasswordVerifier.hashBytes(PASSWORD, SALT)); + + when(repository.getUser(userHash)) + .thenReturn(UniGenerator.item(new User(USERNAME, SALT, passwordHash, ACQUIRER_ID, Channel.POS, MERCHANT_2_ID))); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-400000000006") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.PASSWORD) + .formParam(FormParamName.USERNAME, USERNAME) + .formParam(FormParamName.PASSWORD, PASSWORD) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) + .when() + .post() + .then() + .statusCode(401) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.INCONSISTENT_CREDENTIALS)); + } + + @Test + void testWrongPassword() throws NoSuchAlgorithmException { + /* + * User respository setup. + */ + String userHash = Base64.getUrlEncoder().encodeToString( + MessageDigest.getInstance("SHA256").digest( + USERNAME.getBytes(StandardCharsets.UTF_8))); + + String passwordHash = Base64.getEncoder().encodeToString(PasswordVerifier.hashBytes(PASSWORD_2, SALT)); + + when(repository.getUser(userHash)) + .thenReturn(UniGenerator.item(new User(USERNAME, SALT, passwordHash, ACQUIRER_ID, Channel.POS, MERCHANT_ID))); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-400000000006") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.PASSWORD) + .formParam(FormParamName.USERNAME, USERNAME) + .formParam(FormParamName.PASSWORD, PASSWORD) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) + .when() + .post() + .then() + .statusCode(401) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.WRONG_CREDENTIALS)); + } + +// @Test +// void testErrorFromSuper() { +// } +} \ No newline at end of file diff --git a/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenByPoyntTokenResourceTest.java b/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenByPoyntTokenResourceTest.java new file mode 100644 index 00000000..842931e5 --- /dev/null +++ b/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenByPoyntTokenResourceTest.java @@ -0,0 +1,298 @@ +/* + * TokenByPoyntTokenResourceTest.java + * + * 28 ago 2023 + */ +package it.pagopa.swclient.mil.auth.resource; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import java.time.Instant; +import java.util.List; + +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +import io.quarkus.test.InjectMock; +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; +import io.smallrye.mutiny.Uni; +import it.pagopa.swclient.mil.auth.AuthErrorCode; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetAccessTokenResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeyResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeyVersionsResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.GetKeysResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.Key; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.KeyAttributes; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.KeyDetails; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.KeyVersion; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.SignRequest; +import it.pagopa.swclient.mil.auth.azurekeyvault.bean.SignResponse; +import it.pagopa.swclient.mil.auth.azurekeyvault.client.AzureAuthClient; +import it.pagopa.swclient.mil.auth.azurekeyvault.client.AzureKeyVaultClient; +import it.pagopa.swclient.mil.auth.bean.Client; +import it.pagopa.swclient.mil.auth.bean.FormParamName; +import it.pagopa.swclient.mil.auth.bean.GrantType; +import it.pagopa.swclient.mil.auth.bean.HeaderParamName; +import it.pagopa.swclient.mil.auth.bean.JsonPropertyName; +import it.pagopa.swclient.mil.auth.bean.Role; +import it.pagopa.swclient.mil.auth.bean.Scope; +import it.pagopa.swclient.mil.auth.bean.TokenType; +import it.pagopa.swclient.mil.auth.client.AuthDataRepository; +import it.pagopa.swclient.mil.auth.client.PoyntClient; +import it.pagopa.swclient.mil.auth.util.UniGenerator; +import it.pagopa.swclient.mil.bean.Channel; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.Response.Status; + +/** + * @author Antonio Tarricone + */ +@QuarkusTest +@TestHTTPEndpoint(TokenResource.class) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class TokenByPoyntTokenResourceTest { + /* + * + */ + private static final String ACQUIRER_ID = "4585625"; + private static final String MERCHANT_ID = "28405fHfk73x88D"; + private static final String TERMINAL_ID = "12345678"; + private static final String CLIENT_ID = "3965df56-ca9a-49e5-97e8-061433d4a25b"; + private static final String DESCRIPTION = "VAS Layer"; + private static final List ROLES = List.of("NoticePayer", "SlavePos"); + private static final String EXT_TOKEN = "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJOZXhpIiwicG95bnQuZGlkIjoidXJuOnRpZDo1NTYyYjhlZC1lODljLTMzMmEtYThkYy1jYTA4MTcxMzUxMTAiLCJwb3ludC5kc3QiOiJEIiwicG95bnQub3JnIjoiMGU2Zjc4ODYtMDk1Ni00NDA1LWJjNDgtYzE5ODY4ZDdlZTIyIiwicG95bnQuc2N0IjoiVSIsImlzcyI6Imh0dHBzOlwvXC9zZXJ2aWNlcy1ldS5wb3ludC5uZXQiLCJwb3ludC51cmUiOiJPIiwicG95bnQua2lkIjozOTMyNDI1MjY4MDY5NDA5MjM0LCJwb3ludC5zY3YiOiJOZXhpIiwicG95bnQuc3RyIjoiZDNmZDNmZDMtMTg5ZC00N2M4LThjMzYtYjY4NWRkNjBkOTY0IiwiYXVkIjoidXJuOnRpZDo1NTYyYjhlZC1lODljLTMzMmEtYThkYy1jYTA4MTcxMzUxMTAiLCJwb3ludC51aWQiOjM3MzY1NzQsInBveW50LmJpeiI6IjRiN2ViOTRiLTEwYzktNGYxMS1hMTBlLTcyOTJiMjlhYjExNSIsImV4cCI6MTY4NDU3NTMzNiwiaWF0IjoxNjg0NDg4OTM2LCJqdGkiOiJmNzc5MjQ1OS00ODU1LTQ5YjMtYTZiYS05N2QzNzQ5NDQ2ZGIifQ.niR8AS3OHlmWg1-n3FD4DKoAWlY0nJyEJGBZSBFWHYCl01vjIIFYCmTCyBshZVEtDBKpTG1bWTmVctOCX2ybF5gQ0vBH1H3LFD13Tf73Ps439Ht5_u3Q-jHPf_arXDf2enOs_vKwp8TsdJNPRcxMhYZ91yyiAhbHERVypP2YPszwv5h6mMq_HWNzK9qjrLh8zQCGBEMkFfnSG1xOjzTZLJ4ROPazaDHJ9DSZReC4dY_jRqAlivbXVeLOnN3D4y_GatcHQO1_p_jYE-eXHjLP-wINeAqW57P57HmSe2n67q6UkQf5v5zKVHrJpTFAtHWpDVLxmhPKGurTX45yOvaDZw"; + private static final String ADD_DATA = "4b7eb94b-10c9-4f11-a10e-7292b29ab115"; + + /* + * + */ + private static final long AZURE_TOKEN_DURATION = 3599; + private static final String AZURE_TOKEN = "this_is_the_token"; + private static final String AUTHORIZATION_HDR_VALUE = TokenType.BEARER + " " + AZURE_TOKEN; + + /* + * + */ + private static final String KEY_URL = "https://mil-d-appl-kv.vault.azure.net/keys/"; + private static final String KEY_NAME = "0709643f49394529b92c19a68c8e184a"; + private static final String KEY_VERSION = "6581c704deda4979943c3b34468df7c2"; + private static final String KID = KEY_NAME + "/" + KEY_VERSION; + private static final String KEY_RECOVERY_LEVEL = "Purgeable"; + private static final String KEY_TYPE = "RSA"; + private static final String[] KEY_OPS = new String[] { + "verify", "sign" + }; + private static final String MODULUS = "AKnFsF5Y16TB9qkmoOyDXG3ulenUWYoW78U7mcGBoYKRpMlswxhc_ZiKcC65vIrCP6hbS5Cx88IbQG2DWH-nE329OLzUbzcdraDLR-7V2BX0nNwmwXxhkd4ofzzjKyhWjV8AkxFpqJPtFG09YCyCpaC8YluVPbHUpWJ1wrOsavdc_YM1W1XuaGvJv4SkilM8vBa81zOLEVhbEE5msHxPNLwVyC_0PIE6OFL9RY4YP1U1q7gjTMmKDc9qgEYkdziMnlxWp_EkKTZOERbEatP0fditFt-zWKlXw0qO4FKFlmj9n5tbB55vaopB71Kv6LcsAY1Q-fgOuoM41HldLppzfDOPwLGyCQF9ODJt1xaKkup6i_BxZum7-QckibwPaj3ODZbYsPuNZ_npQiR6NJZ_q_31YMlyuGdqltawluYLJidw3EzkpTN__bHdio892WbY29PRwbrG486IJ_88qP3lWs1TfzohVa1czUOZwQHqp0ixVBi_SK3jICk-V65DbwzgS5zwBFaqfWO3XVOf6tmWFMZ6ly7wtOnYWoMR15rudsD5xXWwqE-s7IP1lVZuIOdMfLH7-1Pgn-YJuPsBLbZri9_M4KtflYbqnuDckSyFNBynTwoSvSSuBhpkmNgiSQ-WBXHHss5Wy-pr-YjNK7JYppPOHvfHSY96XnJl9SPWcnwx"; + private static final String PUBLIC_EXPONENT = "AQAB"; + private static final String EXPECTED_SIGNATURE = "expected_signature"; + + /* + * + */ + @InjectMock + @RestClient + AuthDataRepository repository; + + /* + * + */ + @InjectMock + @RestClient + AzureKeyVaultClient keyVaultClient; + + /* + * + */ + @InjectMock + @RestClient + AzureAuthClient authClient; + + /* + * + */ + @InjectMock + @RestClient + PoyntClient poyntClient; + + @Test + void testOk() { + /* + * Poynt client setup. + */ + when(poyntClient.getBusinessObject(anyString(), anyString())) + .thenReturn(UniGenerator.item(Response.ok().build())); + + /* + * Client repository setup. + */ + when(repository.getClient(CLIENT_ID)) + .thenReturn(UniGenerator.item(new Client(CLIENT_ID, Channel.POS, null, null, DESCRIPTION))); + + /* + * Roles repository setup. + */ + when(repository.getRoles(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID)) + .thenReturn(UniGenerator.item(new Role(ACQUIRER_ID, Channel.POS, CLIENT_ID, MERCHANT_ID, TERMINAL_ID, ROLES))); + + /* + * Azure auth. client setup. + */ + when(authClient.getAccessToken(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn(UniGenerator.item(new GetAccessTokenResponse(JsonPropertyName.TOKEN_TYPE, AZURE_TOKEN_DURATION, AZURE_TOKEN_DURATION, AZURE_TOKEN))); + + /* + * Azure key vault setup. + */ + long now = Instant.now().getEpochSecond(); + KeyAttributes keyAttributes = new KeyAttributes(now - 300, now + 600, now - 300, now - 300, Boolean.TRUE, KEY_RECOVERY_LEVEL, 0, Boolean.FALSE); + + when(keyVaultClient.getKeys(AUTHORIZATION_HDR_VALUE)) + .thenReturn(UniGenerator.item(new GetKeysResponse(new Key[]{ + new Key(KEY_URL + KEY_NAME, keyAttributes) + }))); + + when(keyVaultClient.getKeyVersions(AUTHORIZATION_HDR_VALUE, KEY_NAME)) + .thenReturn(UniGenerator.item(new GetKeyVersionsResponse(new KeyVersion[]{ + new KeyVersion(KEY_URL + KEY_NAME + "/" + KEY_VERSION, keyAttributes) + }))); + + when(keyVaultClient.getKey(AUTHORIZATION_HDR_VALUE, KEY_NAME, KEY_VERSION)) + .thenReturn(UniGenerator.item(new GetKeyResponse(new KeyDetails(KEY_URL + KEY_NAME + "/" + KEY_VERSION, KEY_TYPE, KEY_OPS, MODULUS, PUBLIC_EXPONENT, keyAttributes)))); + + when(keyVaultClient.sign(eq(AUTHORIZATION_HDR_VALUE), eq(KEY_NAME), eq(KEY_VERSION), any(SignRequest.class))) + .thenReturn(UniGenerator.item(new SignResponse(KID, EXPECTED_SIGNATURE))); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-200000000000") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.POYNT_TOKEN) + .formParam(FormParamName.EXT_TOKEN, EXT_TOKEN) + .formParam(FormParamName.ADD_DATA, ADD_DATA) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) + .when() + .post() + .then() + .statusCode(200) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ACCESS_TOKEN, notNullValue()) + .body(JsonPropertyName.TOKEN_TYPE, equalTo(TokenType.BEARER)) + .body(JsonPropertyName.EXPIRES_IN, notNullValue(Long.class)) + .body(JsonPropertyName.REFRESH_TOKEN, notNullValue()); + } + + @Test + void testWebApplicationExceptionGettingBusinessObject() { + /* + * Poynt client setup. + */ + when(poyntClient.getBusinessObject(anyString(), anyString())) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Status.UNAUTHORIZED).build()))); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-200000000001") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.POYNT_TOKEN) + .formParam(FormParamName.EXT_TOKEN, EXT_TOKEN) + .formParam(FormParamName.ADD_DATA, ADD_DATA) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) + .when() + .post() + .then() + .statusCode(401) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.EXT_TOKEN_NOT_VALID)); + } + + @Test + void testExceptionGettingBusinessObject() { + /* + * Poynt client setup. + */ + when(poyntClient.getBusinessObject(anyString(), anyString())) + .thenReturn(Uni.createFrom().failure(new Exception("synthetic exception"))); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-200000000002") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.POYNT_TOKEN) + .formParam(FormParamName.EXT_TOKEN, EXT_TOKEN) + .formParam(FormParamName.ADD_DATA, ADD_DATA) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) + .when() + .post() + .then() + .statusCode(500) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.ERROR_VALIDATING_EXT_TOKEN)); + } + + @Test + void test401GettingBusinessObject() { + /* + * Poynt client setup. + */ + when(poyntClient.getBusinessObject(anyString(), anyString())) + .thenReturn(UniGenerator.item(Response.status(Status.UNAUTHORIZED).build())); + + /* + * Test + */ + given() + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .header(HeaderParamName.REQUEST_ID, "00000000-0000-0000-0000-200000000003") + .header(HeaderParamName.ACQUIRER_ID, ACQUIRER_ID) + .header(HeaderParamName.CHANNEL, Channel.POS) + .header(HeaderParamName.MERCHANT_ID, MERCHANT_ID) + .header(HeaderParamName.TERMINAL_ID, TERMINAL_ID) + .formParam(FormParamName.CLIENT_ID, CLIENT_ID) + .formParam(FormParamName.GRANT_TYPE, GrantType.POYNT_TOKEN) + .formParam(FormParamName.EXT_TOKEN, EXT_TOKEN) + .formParam(FormParamName.ADD_DATA, ADD_DATA) + .formParam(FormParamName.SCOPE, Scope.OFFLINE_ACCESS) + .when() + .post() + .then() + .statusCode(401) + .contentType(MediaType.APPLICATION_JSON) + .body(JsonPropertyName.ERRORS, hasItem(AuthErrorCode.EXT_TOKEN_NOT_VALID)); + } + +// @Test +// void testErrorFromSuper() { +// } +} diff --git a/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenResourceTest.java b/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenResourceTest.java deleted file mode 100644 index 65b30a9d..00000000 --- a/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenResourceTest.java +++ /dev/null @@ -1,1755 +0,0 @@ -/* - * TokenResourceTest.java - * - * 19 mag 2023 - */ -package it.pagopa.swclient.mil.auth.resource; - -import static io.restassured.RestAssured.given; -import static it.pagopa.swclient.mil.auth.util.KeyPairUtil.getPrivateKey; -import static it.pagopa.swclient.mil.auth.util.UniGenerator.item; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; -import static org.mockito.ArgumentMatchers.anyString; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; -import java.text.ParseException; -import java.util.Base64; -import java.util.Date; -import java.util.List; -import java.util.UUID; - -import org.eclipse.microprofile.rest.client.inject.RestClient; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.mockito.Mockito; - -import com.nimbusds.jose.JOSEException; -import com.nimbusds.jose.JWSAlgorithm; -import com.nimbusds.jose.JWSHeader; -import com.nimbusds.jose.JWSSigner; -import com.nimbusds.jose.crypto.RSASSASigner; -import com.nimbusds.jose.util.StandardCharset; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; - -import io.quarkus.test.common.http.TestHTTPEndpoint; -import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.junit.mockito.InjectMock; -import io.smallrye.mutiny.Uni; -import it.pagopa.swclient.mil.auth.bean.Client; -import it.pagopa.swclient.mil.auth.bean.GrantType; -import it.pagopa.swclient.mil.auth.bean.KeyPair; -import it.pagopa.swclient.mil.auth.bean.Role; -import it.pagopa.swclient.mil.auth.bean.User; -import it.pagopa.swclient.mil.auth.client.AuthDataRepository; -import it.pagopa.swclient.mil.auth.client.PoyntClient; -import it.pagopa.swclient.mil.auth.service.KeyFinder; -import it.pagopa.swclient.mil.auth.service.KeyPairGenerator; -import it.pagopa.swclient.mil.auth.service.RedisClient; -import it.pagopa.swclient.mil.auth.util.PasswordVerifier; -import it.pagopa.swclient.mil.auth.util.TokenGenerator; -import it.pagopa.swclient.mil.auth.util.TokenSigner; -import it.pagopa.swclient.mil.bean.Channel; -import jakarta.inject.Inject; -import jakarta.ws.rs.WebApplicationException; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; -import jakarta.ws.rs.core.Response.Status; - -/** - * - * @author Antonio Tarricone - */ -@QuarkusTest -@TestHTTPEndpoint(TokenResource.class) -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -class TokenResourceTest { - /* - * - */ - @InjectMock - @RestClient - AuthDataRepository authDataRepository; - - /* - * - */ - @InjectMock - @RestClient - PoyntClient poyntClient; - - /* - * - */ - @InjectMock - RedisClient redisClient; - - /* - * - */ - @Inject - KeyFinder keyFinder; - - /* - * - */ - @Inject - KeyPairGenerator keyPairGenerator; - - /* - * - */ - String clientId; - String userHash; - - /* - * - */ - private static final String CLIENT_ID_2 = "1e7921f7-677f-4ae0-bd51-2f580fd111e3"; - private static final String ACQUIRER_ID = "4585625"; - private static final String ACQUIRER_ID_2 = "4585626"; - private static final String MERCHANT_ID = "28405fHfk73x88D"; - private static final String TERMINAL_ID = "01234567"; - private static final String EXT_TOKEN = "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJOZXhpIiwicG95bnQuZGlkIjoidXJuOnRpZDo1NTYyYjhlZC1lODljLTMzMmEtYThkYy1jYTA4MTcxMzUxMTAiLCJwb3ludC5kc3QiOiJEIiwicG95bnQub3JnIjoiMGU2Zjc4ODYtMDk1Ni00NDA1LWJjNDgtYzE5ODY4ZDdlZTIyIiwicG95bnQuc2N0IjoiVSIsImlzcyI6Imh0dHBzOlwvXC9zZXJ2aWNlcy1ldS5wb3ludC5uZXQiLCJwb3ludC51cmUiOiJPIiwicG95bnQua2lkIjozOTMyNDI1MjY4MDY5NDA5MjM0LCJwb3ludC5zY3YiOiJOZXhpIiwicG95bnQuc3RyIjoiZDNmZDNmZDMtMTg5ZC00N2M4LThjMzYtYjY4NWRkNjBkOTY0IiwiYXVkIjoidXJuOnRpZDo1NTYyYjhlZC1lODljLTMzMmEtYThkYy1jYTA4MTcxMzUxMTAiLCJwb3ludC51aWQiOjM3MzY1NzQsInBveW50LmJpeiI6IjRiN2ViOTRiLTEwYzktNGYxMS1hMTBlLTcyOTJiMjlhYjExNSIsImV4cCI6MTY4NDU3NTMzNiwiaWF0IjoxNjg0NDg4OTM2LCJqdGkiOiJmNzc5MjQ1OS00ODU1LTQ5YjMtYTZiYS05N2QzNzQ5NDQ2ZGIifQ.niR8AS3OHlmWg1-n3FD4DKoAWlY0nJyEJGBZSBFWHYCl01vjIIFYCmTCyBshZVEtDBKpTG1bWTmVctOCX2ybF5gQ0vBH1H3LFD13Tf73Ps439Ht5_u3Q-jHPf_arXDf2enOs_vKwp8TsdJNPRcxMhYZ91yyiAhbHERVypP2YPszwv5h6mMq_HWNzK9qjrLh8zQCGBEMkFfnSG1xOjzTZLJ4ROPazaDHJ9DSZReC4dY_jRqAlivbXVeLOnN3D4y_GatcHQO1_p_jYE-eXHjLP-wINeAqW57P57HmSe2n67q6UkQf5v5zKVHrJpTFAtHWpDVLxmhPKGurTX45yOvaDZw"; - private static final String ADD_DATA = "4b7eb94b-10c9-4f11-a10e-7292b29ab115"; - private static final String USER_NAME = "mariorossi"; - private static final String PASSWORD = "dF@dkj$S73j#fjd7X!"; - private static final String SALT = "BhPEAxmNsm6JIidDZXl/jwIfuFUFwn/hjfoLnDuYyQEfUMQOrtlOCFljm8IYmN5OmMIh3RddWfNSJEVlRxZjig=="; - private static final String CLIENT_SECRET = "3674f0e7-d717-44cc-a3bc-5f8f41771fea"; - private static final String CLIENT_SECRET_2 = "fe3490ca-e1dc-4f67-8348-d30f2d7d7169"; - - /** - * - * @throws NoSuchAlgorithmException - */ - @BeforeAll - void generateUserHash() throws NoSuchAlgorithmException { - userHash = Base64.getEncoder().encodeToString( - MessageDigest.getInstance("SHA256").digest( - USER_NAME.getBytes(StandardCharset.UTF_8))) - .replace("+", "-") - .replace("/", "_"); - } - - /** - * - */ - @BeforeEach - void generateClientId() { - clientId = UUID.randomUUID().toString(); - } - - /** - * @throws NoSuchAlgorithmException - */ - @Test - void createTokenByClientSecret() throws NoSuchAlgorithmException { - /* - * Setup - */ - setupForCreateTokenByClientSecret(); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000005") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_secret", CLIENT_SECRET) - .when() - .post() - .then() - .statusCode(200) - .contentType(MediaType.APPLICATION_JSON) - .body("access_token", notNullValue()) - .body("token_type", equalTo("Bearer")) - .body("expires_in", notNullValue(Long.class)) - .body("refresh_token", nullValue()); - } - - /** - * @throws NoSuchAlgorithmException - */ - @Test - void createTokenByClientSecretForNodo() throws NoSuchAlgorithmException { - /* - * Setup - */ - Mockito - .when(authDataRepository.getClient(clientId)) - .thenReturn(item(new Client(clientId, null, SALT, PasswordVerifier.hash(CLIENT_SECRET, SALT), "Nodo"))); - - Mockito - .when(authDataRepository.getRoles( - "NA", - "NA", - clientId, - "NA", - "NA")) - .thenReturn(item(new Role("NA", "NA", clientId, "NA", "NA", List.of("Nodo")))); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000006") - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_secret", CLIENT_SECRET) - .when() - .post() - .then() - .statusCode(200) - .contentType(MediaType.APPLICATION_JSON) - .body("access_token", notNullValue()) - .body("token_type", equalTo("Bearer")) - .body("expires_in", notNullValue(Long.class)) - .body("refresh_token", nullValue()); - } - - /** - * @throws NoSuchAlgorithmException - */ - @Test - void createTokenByClientSecretForNodoWithRolesNotFound() throws NoSuchAlgorithmException { - /* - * Setup - */ - Mockito - .when(authDataRepository.getClient(clientId)) - .thenReturn(item(new Client(clientId, null, SALT, PasswordVerifier.hash(CLIENT_SECRET, SALT), "Nodo"))); - - Mockito - .when(authDataRepository.getRoles( - "NA", - "NA", - clientId, - "NA", - "NA")) - .thenReturn(Uni.createFrom().failure(new WebApplicationException(404))); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000022") - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_secret", CLIENT_SECRET) - .when() - .post() - .then() - .statusCode(401) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws NoSuchAlgorithmException - */ - @Test - void createTokenByClientSecretWithClientIdNotFound() throws NoSuchAlgorithmException { - /* - * Setup - */ - setupForCreateTokenByClientSecret(); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-00000000001f") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", CLIENT_ID_2) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_secret", CLIENT_SECRET) - .when() - .post() - .then() - .statusCode(401) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * - */ - @Test - void createTokenByClientSecretWithErrorSearchingClient1() { - /* - * Setup - */ - Mockito - .when(authDataRepository.getClient(clientId)) - .thenReturn(Uni.createFrom().failure(new Exception("synthetic"))); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-00000000001c") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.ATM) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_secret", CLIENT_SECRET) - .when() - .post() - .then() - .statusCode(500) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * - */ - @Test - void createTokenByClientSecretWithErrorSearchingClient2() { - /* - * Setup - */ - Mockito - .when(authDataRepository.getClient(clientId)) - .thenReturn(Uni.createFrom().failure(new WebApplicationException(500))); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000023") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.ATM) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_secret", CLIENT_SECRET) - .when() - .post() - .then() - .statusCode(500) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws NoSuchAlgorithmException - */ - @Test - void createTokenByClientSecretWithErrorSearchingRoles1() throws NoSuchAlgorithmException { - /* - * Setup - */ - Mockito - .when(authDataRepository.getClient(clientId)) - .thenReturn(item(new Client(clientId, Channel.POS, SALT, PasswordVerifier.hash(CLIENT_SECRET, SALT), "VAS Layer"))); - - Mockito - .when(authDataRepository.getRoles( - ACQUIRER_ID, - Channel.POS, - clientId, - MERCHANT_ID, - TERMINAL_ID)) - .thenReturn(Uni.createFrom().failure(new WebApplicationException(500))); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000020") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_secret", CLIENT_SECRET) - .when() - .post() - .then() - .statusCode(500) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws NoSuchAlgorithmException - */ - @Test - void createTokenByClientSecretWithErrorSearchingRoles2() throws NoSuchAlgorithmException { - /* - * Setup - */ - Mockito - .when(authDataRepository.getClient(clientId)) - .thenReturn(item(new Client(clientId, Channel.POS, SALT, PasswordVerifier.hash(CLIENT_SECRET, SALT), "VAS Layer"))); - - Mockito - .when(authDataRepository.getRoles( - ACQUIRER_ID, - Channel.POS, - clientId, - MERCHANT_ID, - TERMINAL_ID)) - .thenReturn(Uni.createFrom().failure(new Exception("synthetic"))); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000021") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_secret", CLIENT_SECRET) - .when() - .post() - .then() - .statusCode(500) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws NoSuchAlgorithmException - */ - @Test - void createTokenByClientSecretWithWrongChannel() throws NoSuchAlgorithmException { - /* - * Setup - */ - setupForCreateTokenByClientSecret(); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-00000000001b") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.ATM) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_secret", CLIENT_SECRET) - .when() - .post() - .then() - .statusCode(401) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws NoSuchAlgorithmException - */ - @Test - void createTokenByClientSecretWithWrongSecret() throws NoSuchAlgorithmException { - /* - * Setup - */ - setupForCreateTokenByClientSecret(); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-00000000001a") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_secret", CLIENT_SECRET_2) - .when() - .post() - .then() - .statusCode(401) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws NoSuchAlgorithmException - */ - @Test - void createTokenByPasswordWithErrorSearchingCredentials1() throws NoSuchAlgorithmException { - /* - * Setup - */ - Mockito - .when(authDataRepository.getClient(clientId)) - .thenReturn(item(new Client(clientId, Channel.POS, null, null, "SmartPOS"))); - - Mockito - .when(authDataRepository.getUser(userHash)) - .thenReturn(Uni.createFrom().failure(new Exception("synthetic"))); - - Mockito - .when(authDataRepository.getRoles( - ACQUIRER_ID, - Channel.POS, - clientId, - MERCHANT_ID, - "NA")) - .thenReturn(item(new Role(ACQUIRER_ID, Channel.POS, clientId, MERCHANT_ID, "NA", List.of("NoticePayer", "SlavePos")))); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000019") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.PASSWORD) - .formParam("username", USER_NAME) - .formParam("password", PASSWORD) - .formParam("scope", "offline_access") - .when() - .post() - .then() - .statusCode(500) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws NoSuchAlgorithmException - */ - @Test - void createTokenByPasswordWithErrorSearchingCredentials2() throws NoSuchAlgorithmException { - /* - * Setup - */ - Mockito - .when(authDataRepository.getClient(clientId)) - .thenReturn(item(new Client(clientId, Channel.POS, null, null, "SmartPOS"))); - - Mockito - .when(authDataRepository.getUser(userHash)) - .thenReturn(Uni.createFrom().failure(new WebApplicationException(500))); - - Mockito - .when(authDataRepository.getRoles( - ACQUIRER_ID, - Channel.POS, - clientId, - MERCHANT_ID, - "NA")) - .thenReturn(item(new Role(ACQUIRER_ID, Channel.POS, clientId, MERCHANT_ID, "NA", List.of("NoticePayer", "SlavePos")))); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000024") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.PASSWORD) - .formParam("username", USER_NAME) - .formParam("password", PASSWORD) - .formParam("scope", "offline_access") - .when() - .post() - .then() - .statusCode(500) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws NoSuchAlgorithmException - */ - @Test - void createTokenByPasswordWithoutConsistency() throws NoSuchAlgorithmException { - /* - * Setup - */ - setupForCreateTokenByPassword(); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000017") - .header("AcquirerId", ACQUIRER_ID_2) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.PASSWORD) - .formParam("username", USER_NAME) - .formParam("password", PASSWORD) - .formParam("scope", "offline_access") - .when() - .post() - .then() - .statusCode(401) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws NoSuchAlgorithmException - */ - @Test - void createTokenByPasswordWithoutRefresh() throws NoSuchAlgorithmException { - /* - * Setup - */ - setupForCreateTokenByPassword(); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000004") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.PASSWORD) - .formParam("username", USER_NAME) - .formParam("password", PASSWORD) - .when() - .post() - .then() - .statusCode(200) - .contentType(MediaType.APPLICATION_JSON) - .body("access_token", notNullValue()) - .body("token_type", equalTo("Bearer")) - .body("expires_in", notNullValue(Long.class)) - .body("refresh_token", nullValue()); - } - - /** - * @throws NoSuchAlgorithmException - */ - @Test - void createTokenByPasswordWithRefresh() throws NoSuchAlgorithmException { - /* - * Setup - */ - setupForCreateTokenByPassword(); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000003") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.PASSWORD) - .formParam("username", USER_NAME) - .formParam("password", PASSWORD) - .formParam("scope", "offline_access") - .when() - .post() - .then() - .statusCode(200) - .contentType(MediaType.APPLICATION_JSON) - .body("access_token", notNullValue()) - .body("token_type", equalTo("Bearer")) - .body("expires_in", notNullValue(Long.class)) - .body("refresh_token", notNullValue()); - } - - /** - * @throws NoSuchAlgorithmException - */ - @Test - void createTokenByPasswordWithWrongPassword() throws NoSuchAlgorithmException { - /* - * Setup - */ - setupForCreateTokenByPassword(); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000016") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.PASSWORD) - .formParam("username", USER_NAME) - .formParam("password", PASSWORD + PASSWORD) - .formParam("scope", "offline_access") - .when() - .post() - .then() - .statusCode(401) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws NoSuchAlgorithmException - */ - @Test - void createTokenByPasswordWithWrongUsername() throws NoSuchAlgorithmException { - /* - * Setup - */ - setupForCreateTokenByPassword(); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000018") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.PASSWORD) - .formParam("username", USER_NAME + USER_NAME) - .formParam("password", PASSWORD) - .formParam("scope", "offline_access") - .when() - .post() - .then() - .statusCode(401) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * - */ - @Test - void createTokenByPoyntTokenWithErrorVerifingToken1() { - /* - * Setup - */ - Mockito - .when(poyntClient.getBusinessObject(anyString(), anyString())) - .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Status.UNAUTHORIZED).build()))); - - Mockito - .when(authDataRepository.getClient(clientId)) - .thenReturn(item(new Client(clientId, Channel.POS, null, null, "SmartPOS"))); - - Mockito - .when(authDataRepository.getRoles( - ACQUIRER_ID, - Channel.POS, - clientId, - MERCHANT_ID, - "NA")) - .thenReturn(item(new Role(ACQUIRER_ID, Channel.POS, clientId, MERCHANT_ID, "NA", List.of("NoticePayer", "SlavePos")))); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000012") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.POYNT_TOKEN) - .formParam("ext_token", EXT_TOKEN) - .formParam("add_data", ADD_DATA) - .formParam("scope", "offline_access") - .when() - .post() - .then() - .statusCode(401) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * - */ - @Test - void createTokenByPoyntTokenWithErrorVerifingToken2() { - /* - * Setup - */ - Mockito - .when(poyntClient.getBusinessObject(anyString(), anyString())) - .thenReturn(Uni.createFrom().failure(new WebApplicationException())); - - Mockito - .when(authDataRepository.getClient(clientId)) - .thenReturn(item(new Client(clientId, Channel.POS, null, null, "SmartPOS"))); - - Mockito - .when(authDataRepository.getRoles( - ACQUIRER_ID, - Channel.POS, - clientId, - MERCHANT_ID, - "NA")) - .thenReturn(item(new Role(ACQUIRER_ID, Channel.POS, clientId, MERCHANT_ID, "NA", List.of("NoticePayer", "SlavePos")))); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000013") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.POYNT_TOKEN) - .formParam("ext_token", EXT_TOKEN) - .formParam("add_data", ADD_DATA) - .formParam("scope", "offline_access") - .when() - .post() - .then() - .statusCode(401) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * - */ - @Test - void createTokenByPoyntTokenWithErrorVerifingToken3() { - /* - * Setup - */ - Mockito - .when(poyntClient.getBusinessObject(anyString(), anyString())) - .thenReturn(Uni.createFrom().failure(new Exception("synthetic exception"))); - - Mockito - .when(authDataRepository.getClient(clientId)) - .thenReturn(item(new Client(clientId, Channel.POS, null, null, "SmartPOS"))); - - Mockito - .when(authDataRepository.getRoles( - ACQUIRER_ID, - Channel.POS, - clientId, - MERCHANT_ID, - "NA")) - .thenReturn(item(new Role(ACQUIRER_ID, Channel.POS, clientId, MERCHANT_ID, "NA", List.of("NoticePayer", "SlavePos")))); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000014") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.POYNT_TOKEN) - .formParam("ext_token", EXT_TOKEN) - .formParam("add_data", ADD_DATA) - .formParam("scope", "offline_access") - .when() - .post() - .then() - .statusCode(500) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * - */ - @Test - void createTokenByPoyntTokenWithErrorVerifingToken4() { - /* - * Setup - */ - Mockito - .when(poyntClient.getBusinessObject(anyString(), anyString())) - .thenReturn(Uni.createFrom().item(Response.serverError().build())); - - Mockito - .when(authDataRepository.getClient(clientId)) - .thenReturn(item(new Client(clientId, Channel.POS, null, null, "SmartPOS"))); - - Mockito - .when(authDataRepository.getRoles( - ACQUIRER_ID, - Channel.POS, - clientId, - MERCHANT_ID, - "NA")) - .thenReturn(item(new Role(ACQUIRER_ID, Channel.POS, clientId, MERCHANT_ID, "NA", List.of("NoticePayer", "SlavePos")))); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000015") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.POYNT_TOKEN) - .formParam("ext_token", EXT_TOKEN) - .formParam("add_data", ADD_DATA) - .formParam("scope", "offline_access") - .when() - .post() - .then() - .statusCode(401) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * - */ - @Test - void createTokenByPoyntTokenWithoutRefresh() { - /* - * Setup - */ - setupForCreateTokenByPoyntToken(); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000002") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.POYNT_TOKEN) - .formParam("ext_token", EXT_TOKEN) - .formParam("add_data", ADD_DATA) - .when() - .post() - .then() - .statusCode(200) - .contentType(MediaType.APPLICATION_JSON) - .body("access_token", notNullValue()) - .body("token_type", equalTo("Bearer")) - .body("expires_in", notNullValue(Long.class)) - .body("refresh_token", nullValue()); - } - - /** - * - */ - @Test - void createTokenByPoyntTokenWithRefresh() { - /* - * Setup - */ - setupForCreateTokenByPoyntToken(); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000001") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.POYNT_TOKEN) - .formParam("ext_token", EXT_TOKEN) - .formParam("add_data", ADD_DATA) - .formParam("scope", "offline_access") - .when() - .post() - .then() - .statusCode(200) - .contentType(MediaType.APPLICATION_JSON) - .body("access_token", notNullValue()) - .body("token_type", equalTo("Bearer")) - .body("expires_in", notNullValue(Long.class)) - .body("refresh_token", notNullValue()); - } - - /** - * @throws JOSEException - * @throws InvalidKeySpecException - * @throws NoSuchAlgorithmException - */ - @Test - void refreshToken() throws JOSEException, NoSuchAlgorithmException, InvalidKeySpecException { - /* - * Setup - */ - KeyPair keyPair = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item( - List.of( - keyPair.getKid()))); - - Mockito - .when(redisClient.get(keyPair.getKid())) - .thenReturn(Uni.createFrom().item(keyPair)); - - Mockito - .when(authDataRepository.getClient(clientId)) - .thenReturn(item(new Client(clientId, Channel.POS, null, null, "SmartPOS"))); - - Mockito - .when(authDataRepository.getRoles( - ACQUIRER_ID, - Channel.POS, - clientId, - MERCHANT_ID, - TERMINAL_ID)) - .thenReturn(Uni.createFrom().failure(new WebApplicationException(404))); - - Mockito - .when(authDataRepository.getRoles( - ACQUIRER_ID, - Channel.POS, - clientId, - MERCHANT_ID, - "NA")) - .thenReturn(item(new Role(ACQUIRER_ID, Channel.POS, clientId, MERCHANT_ID, "NA", List.of("NoticePayer", "SlavePos")))); - - String token = TokenGenerator.generate(ACQUIRER_ID, Channel.POS, MERCHANT_ID, clientId, TERMINAL_ID, 24 * 60 * 60 * 1000, null, List.of("offline_access"), keyPair); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000007") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("refresh_token", token) - .when() - .post() - .then() - .statusCode(200) - .contentType(MediaType.APPLICATION_JSON) - .body("access_token", notNullValue()) - .body("token_type", equalTo("Bearer")) - .body("expires_in", notNullValue(Long.class)) - .body("refresh_token", notNullValue()); - } - - /** - * - */ - @Test - void refreshTokenWithBadToken() { - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000008") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("refresh_token", "a.a.a") - .when() - .post() - .then() - .statusCode(500) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws JOSEException - * @throws ParseException - * @throws InvalidKeySpecException - * @throws NoSuchAlgorithmException - */ - @Test - void refreshTokenWithErrorSearchingPublicKey() throws JOSEException, ParseException, NoSuchAlgorithmException, InvalidKeySpecException { - /* - * Setup - */ - setupForCreateTokenByPoyntToken(); - - KeyPair keyPair = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item( - List.of( - keyPair.getKid()))); - - Mockito - .when(redisClient.get(keyPair.getKid())) - .thenReturn(Uni.createFrom().item(keyPair)); - - String token = TokenGenerator.generate(ACQUIRER_ID, Channel.POS, MERCHANT_ID, clientId, TERMINAL_ID, 24 * 60 * 60 * 1000, null, List.of("offline_access"), keyPair); - - Mockito - .when(keyFinder.findPublicKey(keyPair.getKid())) - .thenReturn(Uni.createFrom().failure(new Exception("synthetic exception"))); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000011") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("refresh_token", token) - .when() - .post() - .then() - .statusCode(500) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws JOSEException - * @throws InvalidKeySpecException - * @throws NoSuchAlgorithmException - */ - @Test - void refreshTokenWithExpiredKey() throws JOSEException, NoSuchAlgorithmException, InvalidKeySpecException { - /* - * Setup - */ - setupForCreateTokenByPoyntToken(); - - KeyPair keyPair = keyPairGenerator.generate(); - keyPair.setExp(keyPair.getExp() - 24 * 60 * 60 * 1000); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item( - List.of( - keyPair.getKid()))); - - Mockito - .when(redisClient.get(keyPair.getKid())) - .thenReturn(Uni.createFrom().item(keyPair)); - - String token = TokenGenerator.generate(ACQUIRER_ID, Channel.POS, MERCHANT_ID, clientId, TERMINAL_ID, 24 * 60 * 60 * 1000, null, List.of("offline_access"), keyPair); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-00000000000a") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("refresh_token", token) - .when() - .post() - .then() - .statusCode(401) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws JOSEException - * @throws ParseException - * @throws InvalidKeySpecException - * @throws NoSuchAlgorithmException - */ - @Test - void refreshTokenWithExpiredToken() throws JOSEException, ParseException, NoSuchAlgorithmException, InvalidKeySpecException { - /* - * Setup - */ - KeyPair keyPair = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item( - List.of( - keyPair.getKid()))); - - Mockito - .when(redisClient.get(keyPair.getKid())) - .thenReturn(Uni.createFrom().item(keyPair)); - - String token = TokenGenerator.generate(ACQUIRER_ID, Channel.POS, MERCHANT_ID, clientId, TERMINAL_ID, -24 * 60 * 60 * 1000, null, List.of("offline_access"), keyPair); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-00000000000b") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("refresh_token", token) - .when() - .post() - .then() - .statusCode(401) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws JOSEException - * @throws InvalidKeySpecException - * @throws NoSuchAlgorithmException - */ - @Test - void refreshTokenWithMissingKey() throws JOSEException, NoSuchAlgorithmException, InvalidKeySpecException { - /* - * Setup - */ - setupForCreateTokenByPoyntToken(); - - KeyPair keyPair = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item(List.of())); - - String token = TokenGenerator.generate(ACQUIRER_ID, Channel.POS, MERCHANT_ID, clientId, TERMINAL_ID, 24 * 60 * 60 * 1000, null, List.of("offline_access"), keyPair); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000009") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("refresh_token", token) - .when() - .post() - .then() - .statusCode(401) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws JOSEException - * @throws ParseException - * @throws InvalidKeySpecException - * @throws NoSuchAlgorithmException - */ - @Test - void refreshTokenWithoutExpiration() throws JOSEException, ParseException, NoSuchAlgorithmException, InvalidKeySpecException { - /* - * Setup - */ - setupForCreateTokenByPoyntToken(); - - KeyPair keyPair = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item( - List.of( - keyPair.getKid()))); - - Mockito - .when(redisClient.get(keyPair.getKid())) - .thenReturn(Uni.createFrom().item(keyPair)); - - Date now = new Date(); - JWTClaimsSet payload = new JWTClaimsSet.Builder() - .subject(clientId) - .issueTime(now) - .claim("acquirerId", ACQUIRER_ID) - .claim("channel", Channel.POS) - .claim("merchantId", MERCHANT_ID) - .claim("clientId", clientId) - .claim("terminalId", TERMINAL_ID) - .claim("scope", "offline_access") - .build(); - SignedJWT token = TokenSigner.sign(keyPair, payload); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-000000000010") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("refresh_token", token.serialize()) - .when() - .post() - .then() - .statusCode(401) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws JOSEException - * @throws ParseException - * @throws InvalidKeySpecException - * @throws NoSuchAlgorithmException - */ - @Test - void refreshTokenWithoutIssueTime() throws JOSEException, ParseException, NoSuchAlgorithmException, InvalidKeySpecException { - /* - * Setup - */ - setupForCreateTokenByPoyntToken(); - - KeyPair keyPair = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item( - List.of( - keyPair.getKid()))); - - Mockito - .when(redisClient.get(keyPair.getKid())) - .thenReturn(Uni.createFrom().item(keyPair)); - - Date now = new Date(); - JWTClaimsSet payload = new JWTClaimsSet.Builder() - .subject(clientId) - .expirationTime(new Date(now.getTime() + 10 * 60 * 1000)) - .claim("acquirerId", ACQUIRER_ID) - .claim("channel", Channel.POS) - .claim("merchantId", MERCHANT_ID) - .claim("clientId", clientId) - .claim("terminalId", TERMINAL_ID) - .claim("scope", "offline_access") - .build(); - SignedJWT token = TokenSigner.sign(keyPair, payload); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-00000000000f") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("refresh_token", token.serialize()) - .when() - .post() - .then() - .statusCode(401) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws JOSEException - * @throws ParseException - * @throws InvalidKeySpecException - * @throws NoSuchAlgorithmException - */ - @Test - void refreshTokenWithoutScope() throws JOSEException, ParseException, NoSuchAlgorithmException, InvalidKeySpecException { - /* - * Setup - */ - setupForCreateTokenByPoyntToken(); - - KeyPair keyPair = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item( - List.of( - keyPair.getKid()))); - - Mockito - .when(redisClient.get(keyPair.getKid())) - .thenReturn(Uni.createFrom().item(keyPair)); - - String token = TokenGenerator.generate(ACQUIRER_ID, Channel.POS, MERCHANT_ID, clientId, TERMINAL_ID, 24 * 60 * 60 * 1000, null, null, keyPair); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-00000000000d") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("refresh_token", token) - .when() - .post() - .then() - .statusCode(401) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws JOSEException - * @throws ParseException - * @throws InvalidKeySpecException - * @throws NoSuchAlgorithmException - */ - @Test - void refreshTokenWithWrongAlgorithm() throws JOSEException, ParseException, NoSuchAlgorithmException, InvalidKeySpecException { - /* - * Setup - */ - KeyPair keyPair = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item( - List.of( - keyPair.getKid()))); - - Mockito - .when(redisClient.get(keyPair.getKid())) - .thenReturn(Uni.createFrom().item(keyPair)); - - Date now = new Date(); - JWTClaimsSet payload = new JWTClaimsSet.Builder() - .subject(clientId) - .issueTime(now) - .expirationTime(new Date(now.getTime() + 24 * 60 * 60 * 1000)) - .claim("acquirerId", ACQUIRER_ID) - .claim("channel", Channel.POS) - .claim("merchantId", MERCHANT_ID) - .claim("clientId", clientId) - .claim("terminalId", TERMINAL_ID) - .claim("scope", "offline_access") - .build(); - - JWSHeader header = new JWSHeader(JWSAlgorithm.RS512, null, null, null, null, null, null, null, null, null, keyPair.getKid(), true, null, null); - SignedJWT token = new SignedJWT(header, payload); - JWSSigner signer = new RSASSASigner(getPrivateKey(keyPair)); - token.sign(signer); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-00000000001d") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("refresh_token", token.serialize()) - .when() - .post() - .then() - .statusCode(401) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws JOSEException - * @throws ParseException - * @throws InvalidKeySpecException - * @throws NoSuchAlgorithmException - */ - @Test - void refreshTokenWithWrongIssueTime() throws JOSEException, ParseException, NoSuchAlgorithmException, InvalidKeySpecException { - /* - * Setup - */ - setupForCreateTokenByPoyntToken(); - - KeyPair keyPair = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item( - List.of( - keyPair.getKid()))); - - Mockito - .when(redisClient.get(keyPair.getKid())) - .thenReturn(Uni.createFrom().item(keyPair)); - - Date now = new Date(); - JWTClaimsSet payload = new JWTClaimsSet.Builder() - .subject(clientId) - .issueTime(new Date(now.getTime() + 5 * 60 * 1000)) - .expirationTime(new Date(now.getTime() + 10 * 60 * 1000)) - .claim("acquirerId", ACQUIRER_ID) - .claim("channel", Channel.POS) - .claim("merchantId", MERCHANT_ID) - .claim("clientId", clientId) - .claim("terminalId", TERMINAL_ID) - .claim("scope", "offline_access") - .build(); - SignedJWT token = TokenSigner.sign(keyPair, payload); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-00000000000e") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("refresh_token", token.serialize()) - .when() - .post() - .then() - .statusCode(401) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws JOSEException - * @throws ParseException - * @throws InvalidKeySpecException - * @throws NoSuchAlgorithmException - */ - @Test - void refreshTokenWithWrongSignature() throws JOSEException, ParseException, NoSuchAlgorithmException, InvalidKeySpecException { - /* - * Setup - */ - KeyPair keyPair = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item( - List.of( - keyPair.getKid()))); - - Mockito - .when(redisClient.get(keyPair.getKid())) - .thenReturn(Uni.createFrom().item(keyPair)); - - KeyPair anotherKey = keyPairGenerator.generate(); - anotherKey.setKid(keyPair.getKid()); - - String token = TokenGenerator.generate(ACQUIRER_ID, Channel.POS, MERCHANT_ID, clientId, TERMINAL_ID, 24 * 60 * 60 * 1000, null, List.of("offline_access"), anotherKey); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-00000000000c") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.REFRESH_TOKEN) - .formParam("refresh_token", token) - .when() - .post() - .then() - .statusCode(401) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } - - /** - * @throws NoSuchAlgorithmException - */ - private void setupForCreateTokenByClientSecret() throws NoSuchAlgorithmException { - Mockito - .when(authDataRepository.getClient(clientId)) - .thenReturn(item(new Client(clientId, Channel.POS, SALT, PasswordVerifier.hash(CLIENT_SECRET, SALT), "VAS Layer"))); - - Mockito - .when(authDataRepository.getClient(CLIENT_ID_2)) - .thenReturn(Uni.createFrom().failure(new WebApplicationException(404))); - - Mockito - .when(authDataRepository.getRoles( - ACQUIRER_ID, - Channel.POS, - clientId, - MERCHANT_ID, - "NA")) - .thenReturn(item(new Role(ACQUIRER_ID, Channel.POS, clientId, MERCHANT_ID, "NA", List.of("NoticePayer", "SlavePos")))); - - Mockito - .when(authDataRepository.getRoles( - ACQUIRER_ID, - Channel.POS, - clientId, - MERCHANT_ID, - TERMINAL_ID)) - .thenReturn(Uni.createFrom().failure(new WebApplicationException(404))); - } - - /** - * @throws NoSuchAlgorithmException - */ - private void setupForCreateTokenByPassword() throws NoSuchAlgorithmException { - Mockito - .when(authDataRepository.getClient(clientId)) - .thenReturn(item(new Client(clientId, Channel.POS, null, null, "SmartPOS"))); - - Mockito - .when(authDataRepository.getUser(anyString())) - .thenReturn(Uni.createFrom().failure(new WebApplicationException(404))); - - Mockito - .when(authDataRepository.getUser(userHash)) - .thenReturn(item(new User(USER_NAME, SALT, PasswordVerifier.hash(PASSWORD, SALT), ACQUIRER_ID, Channel.POS, MERCHANT_ID))); - - Mockito - .when(authDataRepository.getRoles( - ACQUIRER_ID, - Channel.POS, - clientId, - MERCHANT_ID, - TERMINAL_ID)) - .thenReturn(Uni.createFrom().failure(new WebApplicationException(404))); - - Mockito - .when(authDataRepository.getRoles( - ACQUIRER_ID, - Channel.POS, - clientId, - MERCHANT_ID, - "NA")) - .thenReturn(item(new Role(ACQUIRER_ID, Channel.POS, clientId, MERCHANT_ID, "NA", List.of("NoticePayer", "SlavePos")))); - } - - /** - * - */ - private void setupForCreateTokenByPoyntToken() { - Mockito - .when(poyntClient.getBusinessObject(anyString(), anyString())) - .thenReturn(item(Response.ok().build())); - - Mockito - .when(authDataRepository.getClient(clientId)) - .thenReturn(item(new Client(clientId, Channel.POS, null, null, "SmartPOS"))); - - Mockito - .when(authDataRepository.getRoles( - ACQUIRER_ID, - Channel.POS, - clientId, - MERCHANT_ID, - TERMINAL_ID)) - .thenReturn(Uni.createFrom().failure(new WebApplicationException(404))); - - Mockito - .when(authDataRepository.getRoles( - ACQUIRER_ID, - Channel.POS, - clientId, - MERCHANT_ID, - "NA")) - .thenReturn(Uni.createFrom().failure(new WebApplicationException(404))); - - Mockito - .when(authDataRepository.getRoles( - ACQUIRER_ID, - Channel.POS, - clientId, - "NA", - "NA")) - .thenReturn(item(new Role(ACQUIRER_ID, Channel.POS, clientId, "NA", "NA", List.of("NoticePayer", "SlavePos")))); - } -} \ No newline at end of file diff --git a/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenResourceWithExcOnClientSecrVerTest.java b/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenResourceWithExcOnClientSecrVerTest.java deleted file mode 100644 index 2fe98163..00000000 --- a/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenResourceWithExcOnClientSecrVerTest.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * TokenResourceWithExcOnClientSecrVerTest.java - * - * 19 mag 2023 - */ -package it.pagopa.swclient.mil.auth.resource; - -import static io.restassured.RestAssured.given; -import static it.pagopa.swclient.mil.auth.util.UniGenerator.item; -import static org.hamcrest.Matchers.notNullValue; -import static org.mockito.ArgumentMatchers.anyString; - -import java.security.NoSuchAlgorithmException; -import java.util.UUID; - -import org.eclipse.microprofile.rest.client.inject.RestClient; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.mockito.MockedStatic; -import org.mockito.Mockito; - -import io.quarkus.test.common.http.TestHTTPEndpoint; -import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.junit.mockito.InjectMock; -import it.pagopa.swclient.mil.auth.bean.Client; -import it.pagopa.swclient.mil.auth.bean.GrantType; -import it.pagopa.swclient.mil.auth.client.AuthDataRepository; -import it.pagopa.swclient.mil.auth.util.PasswordVerifier; -import it.pagopa.swclient.mil.bean.Channel; -import jakarta.ws.rs.core.MediaType; - -/** - * - * @author Antonio Tarricone - */ -@QuarkusTest -@TestHTTPEndpoint(TokenResource.class) -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -class TokenResourceWithExcOnClientSecrVerTest { - /* - * - */ - @InjectMock - @RestClient - AuthDataRepository authDataRepository; - - /* - * - */ - private String clientId; - - /* - * - */ - private static final String ACQUIRER_ID = "4585625"; - private static final String MERCHANT_ID = "28405fHfk73x88D"; - private static final String TERMINAL_ID = "01234567"; - private static final String SALT = "BhPEAxmNsm6JIidDZXl/jwIfuFUFwn/hjfoLnDuYyQEfUMQOrtlOCFljm8IYmN5OmMIh3RddWfNSJEVlRxZjig=="; - private static final String CLIENT_SECRET = "3674f0e7-d717-44cc-a3bc-5f8f41771fea"; - - /** - * - */ - @BeforeEach - void generateClientId() { - clientId = UUID.randomUUID().toString(); - } - - /** - * @throws NoSuchAlgorithmException - */ - @Test - void createTokenByClientSecretWithExceptionOnSecretVerification() throws NoSuchAlgorithmException { - /* - * Setup - */ - Mockito - .when(authDataRepository.getClient(clientId)) - .thenReturn(item(new Client(clientId, Channel.POS, SALT, PasswordVerifier.hash(CLIENT_SECRET, SALT), "VAS Layer"))); - - MockedStatic passwordVerifier = Mockito.mockStatic(PasswordVerifier.class); - - passwordVerifier.when(() -> PasswordVerifier.verify(anyString(), anyString(), anyString())) - .thenThrow(new NoSuchAlgorithmException("synthetic")); - - /* - * Test - */ - given() - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .header("RequestId", "00000000-0000-0000-0000-200000000001") - .header("AcquirerId", ACQUIRER_ID) - .header("Channel", Channel.POS) - .header("MerchantId", MERCHANT_ID) - .header("TerminalId", TERMINAL_ID) - .formParam("client_id", clientId) - .formParam("grant_type", GrantType.CLIENT_CREDENTIALS) - .formParam("client_secret", CLIENT_SECRET) - .when() - .post() - .then() - .statusCode(500) - .contentType(MediaType.APPLICATION_JSON) - .body("errors", notNullValue()); - } -} \ No newline at end of file diff --git a/src/test/java/it/pagopa/swclient/mil/auth/service/ClientVerifierTest.java b/src/test/java/it/pagopa/swclient/mil/auth/service/ClientVerifierTest.java new file mode 100644 index 00000000..dc580165 --- /dev/null +++ b/src/test/java/it/pagopa/swclient/mil/auth/service/ClientVerifierTest.java @@ -0,0 +1,248 @@ +/* + * ClientVerifierTest.java + * + * 7 ago 2023 + */ +package it.pagopa.swclient.mil.auth.service; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +import java.security.NoSuchAlgorithmException; + +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import io.quarkus.test.InjectMock; +import io.quarkus.test.junit.QuarkusTest; +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.helpers.test.UniAssertSubscriber; +import it.pagopa.swclient.mil.auth.bean.Client; +import it.pagopa.swclient.mil.auth.client.AuthDataRepository; +import it.pagopa.swclient.mil.auth.util.AuthError; +import it.pagopa.swclient.mil.auth.util.AuthException; +import it.pagopa.swclient.mil.auth.util.PasswordVerifier; +import it.pagopa.swclient.mil.auth.util.UniGenerator; +import jakarta.inject.Inject; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Response; + +/** + * @author Antonio Tarricone + */ +@QuarkusTest +class ClientVerifierTest { + /* + * + */ + private static final String ID = "3965df56-ca9a-49e5-97e8-061433d4a25b"; + private static final String CHANNEL = "POS"; + private static final String SALT = "aGw/h/8Fm9S2aNvlvIaxJyhKP67ZU4FEm6mDVhL3aEVrahXFif9x2BkQ4OY87Z9tWVyWbSB/JeztYVmTshrFWQ=="; + private static final String HASH = "G3oYMwnLVR9+m7WB4/pvoVeHxzsTdeyhndpVoruHzog="; + private static final String SECRET = "5ceef788-4115-43a7-a704-b1bcc9a47c86"; + private static final String DESCRIPTION = "VAS Layer"; + private static final String WRONG_CHANNEL = "ATM"; + private static final String WRONG_SECRET = "3674f0e7-d717-44cc-a3bc-5f8f41771fea"; + + /* + * + */ + @InjectMock + @RestClient + AuthDataRepository repository; + + /* + * + */ + @Inject + ClientVerifier verifier; + + /** + * Test method for {@link it.pagopa.swclient.mil.auth.service.ClientVerifier#findClient(java.lang.String)}. + */ + @Test + void testFindClientWithNotFound() { + when(repository.getClient(anyString())) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()))); + + verifier.findClient(ID) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthException.class); + + } + + /** + * Test method for {@link it.pagopa.swclient.mil.auth.service.ClientVerifier#findClient(java.lang.String)}. + */ + @Test + void testFindClientWithError1() { + when(repository.getClient(anyString())) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.INTERNAL_SERVER_ERROR).build()))); + + verifier.findClient(ID) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthError.class); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.service.ClientVerifier#findClient(java.lang.String)}. + */ + @Test + void testFindClientWithError2() { + when(repository.getClient(anyString())) + .thenReturn(Uni.createFrom().failure(new Exception())); + + verifier.findClient(ID) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthError.class); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.service.ClientVerifier#findClient(java.lang.String)}. + */ + @Test + void testFindClientOk() { + Client client = new Client(ID, CHANNEL, SALT, HASH, DESCRIPTION); + + when(repository.getClient(ID)) + .thenReturn(UniGenerator.item(client)); + + verifier.findClient(ID) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertCompleted() + .assertItem(client); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.service.ClientVerifier#verify(java.lang.String, java.lang.String, java.lang.String)}. + */ + @Test + void testVerifyWithWrongChannel() { + Client client = new Client(ID, CHANNEL, SALT, HASH, DESCRIPTION); + + when(repository.getClient(ID)) + .thenReturn(UniGenerator.item(client)); + + verifier.verify(ID, WRONG_CHANNEL, SECRET) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthException.class); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.service.ClientVerifier#verify(java.lang.String, java.lang.String, java.lang.String)}. + */ + @Test + void testVerifyWithWrongSecret() { + Client client = new Client(ID, CHANNEL, SALT, HASH, DESCRIPTION); + + when(repository.getClient(ID)) + .thenReturn(UniGenerator.item(client)); + + verifier.verify(ID, CHANNEL, WRONG_SECRET) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthException.class); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.service.ClientVerifier#verify(java.lang.String, java.lang.String, java.lang.String)}. + */ + @Test + void testVerifyWithNullSecretExpectedButNonNullValueFound() { + Client client = new Client(ID, CHANNEL, SALT, HASH, DESCRIPTION); + + when(repository.getClient(ID)) + .thenReturn(UniGenerator.item(client)); + + verifier.verify(ID, CHANNEL, null) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthException.class); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.service.ClientVerifier#verify(java.lang.String, java.lang.String, java.lang.String)}. + */ + @Test + void testVerifyWithNonNullSecretExpectedButNullValueFound() { + Client client = new Client(ID, CHANNEL, null, null, DESCRIPTION); + + when(repository.getClient(ID)) + .thenReturn(UniGenerator.item(client)); + + verifier.verify(ID, CHANNEL, SECRET) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthException.class); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.service.ClientVerifier#verify(java.lang.String, java.lang.String, java.lang.String)}. + */ + @Test + void testVerifyWithErrorVerifingSecret() { + Client client = new Client(ID, CHANNEL, SALT, HASH, DESCRIPTION); + + when(repository.getClient(ID)) + .thenReturn(UniGenerator.item(client)); + + try (MockedStatic digest = Mockito.mockStatic(PasswordVerifier.class)) { + digest.when(() -> PasswordVerifier.verify(anyString(), anyString(), anyString())) + .thenThrow(NoSuchAlgorithmException.class); + verifier.verify(ID, CHANNEL, SECRET) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthError.class); + } + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.service.ClientVerifier#verify(java.lang.String, java.lang.String, java.lang.String)}. + */ + @Test + void testVerifyOk() { + Client client = new Client(ID, CHANNEL, SALT, HASH, DESCRIPTION); + + when(repository.getClient(ID)) + .thenReturn(UniGenerator.item(client)); + + verifier.verify(ID, CHANNEL, SECRET) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertCompleted() + .assertItem(client); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.service.ClientVerifier#verify(java.lang.String, java.lang.String, java.lang.String)}. + */ + @Test + void testVerifyWithoutSecret() { + Client client = new Client(ID, CHANNEL, null, null, DESCRIPTION); + + when(repository.getClient(ID)) + .thenReturn(UniGenerator.item(client)); + + verifier.verify(ID, CHANNEL, null) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertCompleted() + .assertItem(client); + } +} diff --git a/src/test/java/it/pagopa/swclient/mil/auth/service/KeyFinderTest.java b/src/test/java/it/pagopa/swclient/mil/auth/service/KeyFinderTest.java deleted file mode 100644 index 114c5d2d..00000000 --- a/src/test/java/it/pagopa/swclient/mil/auth/service/KeyFinderTest.java +++ /dev/null @@ -1,495 +0,0 @@ -/* - * KeyFinderTest.java - * - * 23 mar 2023 - */ -package it.pagopa.swclient.mil.auth.service; - -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; - -import java.time.Instant; -import java.util.ArrayList; -import java.util.List; - -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import com.nimbusds.jose.JOSEException; - -import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.junit.mockito.InjectMock; -import io.smallrye.mutiny.Uni; -import io.smallrye.mutiny.helpers.test.UniAssertSubscriber; -import it.pagopa.swclient.mil.auth.bean.KeyPair; -import it.pagopa.swclient.mil.auth.bean.PublicKeys; -import jakarta.inject.Inject; - -/** - * - * @author Antonio Tarricone - */ -@QuarkusTest -class KeyFinderTest { - /* - * - */ - @InjectMock - RedisClient redisClient; - - /* - * - */ - @Inject - KeyFinder keyFinder; - - /* - * - */ - @Inject - KeyPairGenerator keyPairGenerator; - - // getKeyPair -------------------------------------------------------------- - - /** - * Get key pair but there is no one, so it must be generated. - */ - @Test - void getKeyPairWithKeyGeneration() { - /* - * Setup - */ - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item(new ArrayList())); - - Mockito - .when(redisClient.setex(anyString(), anyLong(), any(KeyPair.class))) - .thenReturn(Uni.createFrom().voidItem()); - - /* - * Test - */ - keyFinder.findKeyPair() - .subscribe() - .with( - item -> assertNotNull(item), - fail -> assertNull(fail)); - } - - /** - * Get key pair but there is no one, so it must be generated but Redis fail on set method. - */ - @Test - void getKeyPairWithKeyGenerationAndFailureOnSet() { - /* - * Setup - */ - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item(new ArrayList())); - - Mockito - .when(redisClient.setex(anyString(), anyLong(), any(KeyPair.class))) - .thenReturn(Uni.createFrom().failure(new Exception("synthetic exception"))); - - /* - * Test - */ - keyFinder.findKeyPair() - .subscribe() - .withSubscriber(UniAssertSubscriber.create()) - .assertFailed(); - } - - /** - * Get key pair but there is no one, so it must be generated but Redis fail on expireat method. - */ - @Test - void getKeyPairWithKeyGenerationAndFailureOnExpireat() { - /* - * Setup - */ - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item(new ArrayList())); - - Mockito - .when(redisClient.setex(anyString(), anyLong(), any(KeyPair.class))) - .thenReturn(Uni.createFrom().voidItem()); - - /* - * Test - */ - /* - * Test - */ - keyFinder.findKeyPair() - .subscribe() - .with( - item -> assertNotNull(item), - fail -> assertNull(fail)); - } - - /** - * - * @throws JOSEException - */ - @Test - void getKeyPairWithoutKeyGenerationAndTwoValidKeys() throws JOSEException { - /* - * Setup - */ - KeyPair keyPairThatExpiresLater = keyPairGenerator.generate(); - keyPairThatExpiresLater.setExp(keyPairThatExpiresLater.getExp() + 60000); - - KeyPair keyPair = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item( - List.of( - keyPairThatExpiresLater.getKid(), - keyPair.getKid()))); - - Mockito - .when(redisClient.get(keyPairThatExpiresLater.getKid())) - .thenReturn(Uni.createFrom().item(keyPairThatExpiresLater)); - - Mockito - .when(redisClient.get(keyPair.getKid())) - .thenReturn(Uni.createFrom().item(keyPair)); - - /* - * Test - */ - keyFinder.findKeyPair() - .subscribe() - .withSubscriber(UniAssertSubscriber.create()) - .assertCompleted() - .assertItem(keyPairThatExpiresLater); - } - - /** - * - * @throws JOSEException - */ - @Test - void getKeyPairWithoutKeyGenerationAndTwoValidKeysInvertedOrder() throws JOSEException { - /* - * Setup - */ - KeyPair keyPairThatExpiresLater = keyPairGenerator.generate(); - keyPairThatExpiresLater.setExp(keyPairThatExpiresLater.getExp() + 60000); - - KeyPair keyPair = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item( - List.of( - keyPair.getKid(), - keyPairThatExpiresLater.getKid()))); - - Mockito - .when(redisClient.get(keyPairThatExpiresLater.getKid())) - .thenReturn(Uni.createFrom().item(keyPairThatExpiresLater)); - - Mockito - .when(redisClient.get(keyPair.getKid())) - .thenReturn(Uni.createFrom().item(keyPair)); - - /* - * Test - */ - keyFinder.findKeyPair() - .subscribe() - .withSubscriber(UniAssertSubscriber.create()) - .assertCompleted() - .assertItem(keyPairThatExpiresLater); - } - - /** - * - * @throws JOSEException - */ - @Test - void getKeyPairWithoutKeyGenerationAndTwoValidKeysSameExp() throws JOSEException { - /* - * Setup - */ - KeyPair keyPair1 = keyPairGenerator.generate(); - - KeyPair keyPair2 = keyPairGenerator.generate(); - keyPair2.setExp(keyPair1.getExp()); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item( - List.of( - keyPair1.getKid(), - keyPair2.getKid()))); - - Mockito - .when(redisClient.get(keyPair1.getKid())) - .thenReturn(Uni.createFrom().item(keyPair1)); - - Mockito - .when(redisClient.get(keyPair2.getKid())) - .thenReturn(Uni.createFrom().item(keyPair2)); - - /* - * Test - */ - keyFinder.findKeyPair() - .subscribe() - .withSubscriber(UniAssertSubscriber.create()) - .assertCompleted() - .assertItem(keyPair1); - } - - /** - * - */ - @Test - void getKeyPairWithFailureOnKeys() { - /* - * Setup - */ - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().failure(new Exception("synthetic exception"))); - - /* - * Test - */ - keyFinder.findKeyPair() - .subscribe() - .withSubscriber(UniAssertSubscriber.create()) - .assertFailed(); - } - - /** - * - * @throws JOSEException - */ - @Test - void getKeyPairWithFailureOnFirstGet() throws JOSEException { - /* - * Setup - */ - KeyPair keyPair = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item(List.of( - keyPair.getKid()))); - - Mockito - .when(redisClient.get(keyPair.getKid())) - .thenReturn(Uni.createFrom().failure(new Exception("synthetic exception"))); - - /* - * Test - */ - keyFinder.findKeyPair() - .subscribe() - .withSubscriber(UniAssertSubscriber.create()) - .assertFailed(); - } - - /** - * - * @throws JOSEException - */ - @Test - void getKeyPairWithFailureOnSecondGet() throws JOSEException { - /* - * Setup - */ - KeyPair keyPair1 = keyPairGenerator.generate(); - KeyPair keyPair2 = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item(List.of( - keyPair1.getKid(), - keyPair2.getKid()))); - - Mockito - .when(redisClient.get(keyPair1.getKid())) - .thenReturn(Uni.createFrom().item(keyPair1)); - - Mockito - .when(redisClient.get(keyPair2.getKid())) - .thenReturn(Uni.createFrom().failure(new Exception("synthetic exception"))); - - /* - * Test - */ - keyFinder.findKeyPair() - .subscribe() - .withSubscriber(UniAssertSubscriber.create()) - .assertFailed(); - } - - // getPublicKeys ----------------------------------------------------------- - - /** - * No key found. - */ - @Test() - void getPublicKeysWithNoKeyFound() { - /* - * Setup - */ - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item(new ArrayList())); - - /* - * Test - */ - keyFinder.findPublicKeys() - .subscribe() - .withSubscriber(UniAssertSubscriber.create()) - .assertCompleted() - .assertItem(new PublicKeys(List.of())); - } - - /** - * 1 expired key + 2 valid keys - * - * @throws JOSEException - */ - @Test() - void getPublicKeysWithOneExpiredAndTwoValid() throws JOSEException { - /* - * Setup - */ - KeyPair expiredKey = keyPairGenerator.generate(); - expiredKey.setExp(Instant.now().toEpochMilli() - 1000); - - KeyPair validKey0 = keyPairGenerator.generate(); - KeyPair validKey1 = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item(List.of( - expiredKey.getKid(), - validKey0.getKid(), - validKey1.getKid()))); - - Mockito - .when(redisClient.get(expiredKey.getKid())) - .thenReturn(Uni.createFrom().item(expiredKey)); - - Mockito - .when(redisClient.get(validKey0.getKid())) - .thenReturn(Uni.createFrom().item(validKey0)); - - Mockito - .when(redisClient.get(validKey1.getKid())) - .thenReturn(Uni.createFrom().item(validKey1)); - - /* - * Test - */ - keyFinder.findPublicKeys() - .subscribe() - .withSubscriber(UniAssertSubscriber.create()) - .assertCompleted() - .assertItem(new PublicKeys(List.of(validKey0.publicKey(), validKey1.publicKey()))); - - } - - /** - * Failure on ReactiveKeyCommands.keys(String). - */ - @Test() - void getPublicKeysWithFailureOnKeys() { - /* - * Setup - */ - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().failure(new Exception("synthetic exception"))); - - /* - * Test - */ - keyFinder.findPublicKeys() - .subscribe() - .withSubscriber(UniAssertSubscriber.create()) - .assertFailed(); - - } - - /** - * Failure on ReactiveValueCommands.get(String). - * - * @throws JOSEException - */ - @Test() - void getPublicKeysWithFailureOnFirstGet() throws JOSEException { - /* - * Setup - */ - KeyPair keyPair = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item(List.of(keyPair.getKid()))); - - Mockito - .when(redisClient.get(keyPair.getKid())) - .thenReturn(Uni.createFrom().failure(new Exception("synthetic exception"))); - - /* - * Test - */ - keyFinder.findPublicKeys() - .subscribe() - .withSubscriber(UniAssertSubscriber.create()) - .assertFailed(); - } - - /** - * Failure on ReactiveValueCommands.get(String). - * - * @throws JOSEException - */ - @Test() - void getPublicKeysWithFailureOnSecondGet() throws JOSEException { - /* - * Setup - */ - KeyPair keyPair1 = keyPairGenerator.generate(); - KeyPair keyPair2 = keyPairGenerator.generate(); - - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item(List.of( - keyPair1.getKid(), - keyPair2.getKid()))); - - Mockito - .when(redisClient.get(keyPair1.getKid())) - .thenReturn(Uni.createFrom().item(keyPair1)); - - Mockito - .when(redisClient.get(keyPair2.getKid())) - .thenReturn(Uni.createFrom().failure(new Exception("synthetic exception"))); - - /* - * Test - */ - keyFinder.findPublicKeys() - .subscribe() - .withSubscriber(UniAssertSubscriber.create()) - .assertFailed(); - } -} \ No newline at end of file diff --git a/src/test/java/it/pagopa/swclient/mil/auth/service/KeyFinderWithExceptionTest.java b/src/test/java/it/pagopa/swclient/mil/auth/service/KeyFinderWithExceptionTest.java deleted file mode 100644 index 2393bbb8..00000000 --- a/src/test/java/it/pagopa/swclient/mil/auth/service/KeyFinderWithExceptionTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * KeyFinderTestForException.java - * - * 1 giu 2023 - */ -package it.pagopa.swclient.mil.auth.service; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; - -import java.util.ArrayList; - -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import com.nimbusds.jose.JOSEException; - -import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.junit.mockito.InjectMock; -import io.smallrye.mutiny.Uni; -import io.smallrye.mutiny.helpers.test.UniAssertSubscriber; -import it.pagopa.swclient.mil.auth.bean.KeyPair; -import jakarta.inject.Inject; - -/** - * - * @author Antonio Tarricone - */ -@QuarkusTest -class KeyFinderWithExceptionTest { - /* - * - */ - @InjectMock - RedisClient redisClient; - - /* - * - */ - @Inject - KeyFinder keyFinder; - - /* - * - */ - @InjectMock - KeyPairGenerator keyPairGenerator; - - /** - * Get key pair but there is no one, so it must be generated but during generation an exception - * occurs. - * - * @throws JOSEException - */ - @Test - void getKeyPairWithKeyGenerationAndException() throws JOSEException { - /* - * Setup - */ - Mockito - .when(redisClient.keys("*")) - .thenReturn(Uni.createFrom().item(new ArrayList())); - - Mockito - .when(redisClient.setex(anyString(), anyLong(), any(KeyPair.class))) - .thenReturn(Uni.createFrom().voidItem()); - - Mockito - .when(keyPairGenerator.generate()) - .thenThrow(new JOSEException("synthetic")); - - /* - * Test - */ - keyFinder.findKeyPair() - .subscribe() - .withSubscriber(UniAssertSubscriber.create()) - .assertFailed(); - } -} \ No newline at end of file diff --git a/src/test/java/it/pagopa/swclient/mil/auth/service/RolesFinderTest.java b/src/test/java/it/pagopa/swclient/mil/auth/service/RolesFinderTest.java new file mode 100644 index 00000000..728e4919 --- /dev/null +++ b/src/test/java/it/pagopa/swclient/mil/auth/service/RolesFinderTest.java @@ -0,0 +1,241 @@ +/* + * RolesFinderTest.java + * + * 7 ago 2023 + */ +package it.pagopa.swclient.mil.auth.service; + +import static org.mockito.Mockito.when; + +import java.util.List; + +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.InjectMock; +import io.quarkus.test.junit.QuarkusTest; +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.helpers.test.UniAssertSubscriber; +import it.pagopa.swclient.mil.auth.bean.Role; +import it.pagopa.swclient.mil.auth.client.AuthDataRepository; +import it.pagopa.swclient.mil.auth.util.AuthError; +import it.pagopa.swclient.mil.auth.util.AuthException; +import it.pagopa.swclient.mil.auth.util.UniGenerator; +import jakarta.inject.Inject; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Response; + +/** + * @author Antonio Tarricone + */ +@QuarkusTest +class RolesFinderTest { + /* + * + */ + private static final String ACQUIRER_ID = "4585625"; + private static final String CHANNEL = "POS"; + private static final String CLIENT_ID = "3965df56-ca9a-49e5-97e8-061433d4a25b"; + private static final String MERCHANT_ID = "28405fHfk73x88D"; + private static final String TERMINAL_ID = "12345678"; + private static final List ROLES = List.of("NoticePayer", "SlavePos"); + + /* + * + */ + @InjectMock + @RestClient + AuthDataRepository repository; + + /* + * + */ + @Inject + RolesFinder finder; + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.service.RolesFinder#findRoles(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)}. + */ + @Test + void testFindRolesOk() { + Role role = new Role(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, TERMINAL_ID, ROLES); + + when(repository.getRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, TERMINAL_ID)) + .thenReturn(UniGenerator.item(role)); + + finder.findRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, TERMINAL_ID) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertCompleted() + .assertItem(role); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.service.RolesFinder#findRoles(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)}. + */ + @Test + void testFindRolesNotFound() { + when(repository.getRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, TERMINAL_ID)) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()))); + + when(repository.getRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, "NA")) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()))); + + when(repository.getRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, "NA", "NA")) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()))); + + finder.findRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, TERMINAL_ID) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthException.class); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.service.RolesFinder#findRoles(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)}. + */ + @Test + void testFindRolesNotFoundWithUnknownTerminal() { + when(repository.getRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, "NA")) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()))); + + when(repository.getRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, "NA", "NA")) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()))); + + finder.findRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, null) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthException.class); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.service.RolesFinder#findRoles(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)}. + */ + @Test + void testFindRolesNotFoundWithUnknownMerchant() { + when(repository.getRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, "NA", "NA")) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()))); + + finder.findRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, null, null) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthException.class); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.service.RolesFinder#findRoles(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)}. + */ + @Test + void testFindRolesNotFoundWithUnknownMerchantAndKnownTerminal() { + when(repository.getRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, "NA", TERMINAL_ID)) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()))); + + when(repository.getRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, "NA", "NA")) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()))); + + finder.findRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, null, TERMINAL_ID) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthException.class); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.service.RolesFinder#findRoles(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)}. + */ + @Test + void testFindRolesWithoutSpecificRolesForTerminal() { + Role role = new Role(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, null, ROLES); + + when(repository.getRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, TERMINAL_ID)) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()))); + + when(repository.getRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, "NA")) + .thenReturn(UniGenerator.item(role)); + + finder.findRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, TERMINAL_ID) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertCompleted() + .assertItem(role); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.service.RolesFinder#findRoles(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)}. + */ + @Test + void testFindRolesWithoutSpecificRolesForTerminalButUnkwnownTerminal() { + Role role = new Role(ACQUIRER_ID, CHANNEL, CLIENT_ID, null, null, ROLES); + + when(repository.getRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, "NA")) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()))); + + when(repository.getRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, "NA", "NA")) + .thenReturn(UniGenerator.item(role)); + + finder.findRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, null) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertCompleted() + .assertItem(role); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.service.RolesFinder#findRoles(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)}. + */ + @Test + void testFindRolesWithoutSpecificRolesForMerchant() { + Role role = new Role(ACQUIRER_ID, CHANNEL, CLIENT_ID, null, null, ROLES); + + when(repository.getRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, TERMINAL_ID)) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()))); + + when(repository.getRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, "NA")) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()))); + + when(repository.getRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, "NA", "NA")) + .thenReturn(UniGenerator.item(role)); + + finder.findRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, TERMINAL_ID) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertCompleted() + .assertItem(role); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.service.RolesFinder#findRoles(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)}. + */ + @Test + void testFindRolesWithError1() { + when(repository.getRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, TERMINAL_ID)) + .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.INTERNAL_SERVER_ERROR).build()))); + + finder.findRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, TERMINAL_ID) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthError.class); + } + + /** + * Test method for + * {@link it.pagopa.swclient.mil.auth.service.RolesFinder#findRoles(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)}. + */ + @Test + void testFindRolesWithError2() { + when(repository.getRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, TERMINAL_ID)) + .thenReturn(Uni.createFrom().failure(new Exception())); + + finder.findRoles(ACQUIRER_ID, CHANNEL, CLIENT_ID, MERCHANT_ID, TERMINAL_ID) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthError.class); + } +} \ No newline at end of file diff --git a/src/test/java/it/pagopa/swclient/mil/auth/service/TokenByPasswordServiceTest.java b/src/test/java/it/pagopa/swclient/mil/auth/service/TokenByPasswordServiceTest.java new file mode 100644 index 00000000..25d9fc75 --- /dev/null +++ b/src/test/java/it/pagopa/swclient/mil/auth/service/TokenByPasswordServiceTest.java @@ -0,0 +1,125 @@ +/** + * + */ +package it.pagopa.swclient.mil.auth.service; + +import static org.mockito.Mockito.when; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; + +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import io.quarkus.test.InjectMock; +import io.quarkus.test.junit.QuarkusTest; +import io.smallrye.mutiny.helpers.test.UniAssertSubscriber; +import it.pagopa.swclient.mil.auth.bean.GetAccessTokenRequest; +import it.pagopa.swclient.mil.auth.bean.GrantType; +import it.pagopa.swclient.mil.auth.bean.Scope; +import it.pagopa.swclient.mil.auth.bean.User; +import it.pagopa.swclient.mil.auth.client.AuthDataRepository; +import it.pagopa.swclient.mil.auth.qualifier.Password; +import it.pagopa.swclient.mil.auth.util.AuthError; +import it.pagopa.swclient.mil.auth.util.PasswordVerifier; +import it.pagopa.swclient.mil.auth.util.UniGenerator; +import it.pagopa.swclient.mil.bean.Channel; +import jakarta.enterprise.inject.Any; +import jakarta.enterprise.inject.Instance; +import jakarta.enterprise.util.AnnotationLiteral; +import jakarta.inject.Inject; + +/** + * + * @author Antonio Tarricone + */ +@QuarkusTest +@TestInstance(Lifecycle.PER_CLASS) +class TokenByPasswordServiceTest { + /* + * + */ + private static final String ACQUIRER_ID = "4585625"; + private static final String MERCHANT_ID = "28405fHfk73x88D"; + private static final String TERMINAL_ID = "12345678"; + private static final String CLIENT_ID = "3965df56-ca9a-49e5-97e8-061433d4a25b"; + private static final String USERNAME = "user"; + private static final String PASSWORD = "password"; + private static final String SALT = "zfN59oSr9RfFiiSASUO1YIcv8bARsj1OAV8tEydQiKC3su5Mlz1TsjbFwvWrGCjXdkDUsbeXGnYZDavJuTKw6Q=="; + + /* + * + */ + @Inject + @Any + Instance tokenService; + + /* + * + */ + @InjectMock + @RestClient + AuthDataRepository repository; + + /* + * + */ + @Test + @SuppressWarnings("serial") + void testNoSuchAlgorithmFindingCredentials() { + try (MockedStatic digest = Mockito.mockStatic(MessageDigest.class)) { + digest.when(() -> MessageDigest.getInstance("SHA256")) + .thenThrow(NoSuchAlgorithmException.class); + + /* + * Test. + */ + tokenService.select(new AnnotationLiteral() { + }) + .get().process(new GetAccessTokenRequest("00000000-0000-0000-0000-500000000000", null, ACQUIRER_ID, Channel.POS, MERCHANT_ID, TERMINAL_ID, GrantType.PASSWORD, USERNAME, PASSWORD, null, null, null, CLIENT_ID, Scope.OFFLINE_ACCESS, null)) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthError.class); + } + } + + /* + * + */ + @Test + @SuppressWarnings("serial") + void testNoSuchAlgorithmVerifingPassword() throws NoSuchAlgorithmException { + /* + * User respository setup. + */ + String userHash = Base64.getUrlEncoder().encodeToString( + MessageDigest.getInstance("SHA256").digest( + USERNAME.getBytes(StandardCharsets.UTF_8))); + + String passwordHash = Base64.getEncoder().encodeToString(PasswordVerifier.hashBytes(PASSWORD, SALT)); + + when(repository.getUser(userHash)) + .thenReturn(UniGenerator.item(new User(USERNAME, SALT, passwordHash, ACQUIRER_ID, Channel.POS, MERCHANT_ID))); + + try (MockedStatic passwordVerifier = Mockito.mockStatic(PasswordVerifier.class)) { + passwordVerifier.when(() -> PasswordVerifier.verify(PASSWORD, SALT, passwordHash)) + .thenThrow(NoSuchAlgorithmException.class); + + /* + * Test. + */ + tokenService.select(new AnnotationLiteral() { + }) + .get().process(new GetAccessTokenRequest("00000000-0000-0000-0000-500000000001", null, ACQUIRER_ID, Channel.POS, MERCHANT_ID, TERMINAL_ID, GrantType.PASSWORD, USERNAME, PASSWORD, null, null, null, CLIENT_ID, Scope.OFFLINE_ACCESS, null)) + .subscribe() + .withSubscriber(UniAssertSubscriber.create()) + .assertFailedWith(AuthError.class); + } + } +} \ No newline at end of file