Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: P4ADEV-1341 retrieve pdnd access token #4

Merged
merged 37 commits into from
Nov 27, 2024
Merged
Changes from 11 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
6dda65d
P4ADEV-1341 retrieve access token
antocalo Nov 21, 2024
b28d4c3
P4ADEV-1341 edit helm variables
antocalo Nov 21, 2024
3c14c08
P4ADEV-1341 edit helm variables
antocalo Nov 21, 2024
3729434
P4ADEV-1341 add PdndClientImplTest
antocalo Nov 21, 2024
7f98030
P4ADEV-1341 add CertUtilsTest
antocalo Nov 21, 2024
84691ee
P4ADEV-1341 add PdndUtilsTest
antocalo Nov 21, 2024
096811b
P4ADEV-1341 change generic exception
antocalo Nov 21, 2024
b3dd116
P4ADEV-1341 fix issues
antocalo Nov 21, 2024
878d5e2
P4ADEV-1341 refactor
antocalo Nov 22, 2024
717efe0
P4ADEV-1341 introduced cache token
antocalo Nov 22, 2024
3020f16
P4ADEV-1341 add JWTUtilsTest
antocalo Nov 22, 2024
9f5c044
P4ADEV-1341 add excludes to codereview
antocalo Nov 22, 2024
9516cd0
P4ADEV-1341 add unitTest
antocalo Nov 22, 2024
7bc0622
P4ADEV-1341 add PdndServiceTest
antocalo Nov 22, 2024
19c2570
P4ADEV-1341_retrievePDNDAccessToken
LarissaASLeite Nov 22, 2024
94f943c
P4ADEV-1341 fix PdndServiceTest
antocalo Nov 22, 2024
73ca6c7
Merge remote-tracking branch 'origin/P4ADEV-1341_retrievePDNDAccessTo…
antocalo Nov 22, 2024
b441102
P4ADEV-1341 fix PdndServiceTest
antocalo Nov 22, 2024
08fde8a
P4ADEV-1341 fix PdndServiceTest
antocalo Nov 22, 2024
cf54c2f
P4ADEV-1341 fix PdndServiceTest and Dockerfile
antocalo Nov 22, 2024
3ff7eca
P4ADEV-1341 fix Dockerfile
antocalo Nov 22, 2024
4b5d6fa
P4ADEV-1341 add PdndClientImplTest
antocalo Nov 22, 2024
0504483
P4ADEV-1341 add RestTemplateConfig and resolve pr requests
antocalo Nov 25, 2024
37eeb5a
P4ADEV-1341 fix import
antocalo Nov 25, 2024
126b044
P4ADEV-1341 edit timeout restTemplate
antocalo Nov 25, 2024
727ac19
P4ADEV-1341 refactor config
antocalo Nov 26, 2024
be5c7c2
P4ADEV-1341 refactor config
antocalo Nov 26, 2024
edfa8db
P4ADEV-1341 add RestTemplateConfig
antocalo Nov 26, 2024
1b95770
P4ADEV-1341 fix test
antocalo Nov 26, 2024
cde681c
P4ADEV-1341 upgrade jdk version
antocalo Nov 26, 2024
2858518
P4ADEV-1341 refactor config
antocalo Nov 26, 2024
78dd618
P4ADEV-1341 upgrade jdk version
antocalo Nov 26, 2024
c30e1dc
P4ADEV-1341 resolve isseus
antocalo Nov 26, 2024
5f328f6
P4ADEV-1341 - fix issues
antocalo Nov 27, 2024
7dfdc61
P4ADEV-1341 rename class
antocalo Nov 27, 2024
c5127af
P4ADEV-1341 fix
antocalo Nov 27, 2024
3306841
P4ADEV-1341 - rename ENV variable
antocalo Nov 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 40 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -31,6 +31,10 @@ repositories {
val springDocOpenApiVersion = "2.6.0"
val openApiToolsVersion = "0.2.6"
val findbugsVersion = "3.0.2"
val javaJwtVersion = "4.4.0"
val jwksRsaVersion = "0.22.1"
val nimbusJoseJwtVersion = "9.47"
val jjwtVersion = "0.12.6"

dependencies {
implementation("org.springframework.boot:spring-boot-starter")
@@ -45,6 +49,12 @@ dependencies {
compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")

// validation token jwt
implementation("com.auth0:java-jwt:$javaJwtVersion")
implementation("com.auth0:jwks-rsa:$jwksRsaVersion")
implementation("com.nimbusds:nimbus-jose-jwt:$nimbusJoseJwtVersion")
implementation("io.jsonwebtoken:jjwt-api:$jjwtVersion")

// Testing
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.security:spring-security-test")
@@ -84,21 +94,24 @@ configurations {
}

tasks.compileJava {
dependsOn("openApiGenerate")
dependsOn("openApiGeneratePayhub","openApiGeneratePdndClient")
}


configure<SourceSetContainer> {
named("main") {
java.srcDir("$projectDir/build/generated/src/main/java")
java.srcDir("$projectDir/build/generated/pdnd-client/src/main/java")
}
}

springBoot {
mainClass.value("it.gov.pagopa.payhub.pdnd.PayhubPdndApplication")
}

openApiGenerate {
tasks.register<org.openapitools.generator.gradle.plugin.tasks.GenerateTask>("openApiGeneratePayhub") {
group = "openapi"
description = "description"

generatorName.set("spring")
inputSpec.set("$rootDir/openapi/p4pa-pdnd.openapi.yaml")
outputDir.set("$projectDir/build/generated")
@@ -112,6 +125,29 @@ openApiGenerate {
"useTags" to "true",
"generateConstructorWithAllArgs" to "false",
"generatedConstructorWithRequiredArgs" to "false",
"additionalModelTypeAnnotations" to "@lombok.Data @lombok.Builder @lombok.AllArgsConstructor @lombok.RequiredArgsConstructor"
"additionalModelTypeAnnotations" to "@lombok.Data @lombok.Builder @lombok.AllArgsConstructor @lombok.RequiredArgsConstructor",
"serializationLibrary" to "jackson"
))
}

tasks.register<org.openapitools.generator.gradle.plugin.tasks.GenerateTask>("openApiGeneratePdndClient") {
group = "openapi"
description = "description"

generatorName.set("java")
inputSpec.set("$rootDir/src/main/resources/pdnd/pdnd-v1.yaml")
outputDir.set("$projectDir/build/generated/pdnd-client")
apiPackage.set("it.gov.pagopa.common.pdnd.generated.api")
modelPackage.set("it.gov.pagopa.common.pdnd.generated.dto")
modelNameSuffix.set("DTO")
configOptions.set(mapOf(
"swaggerAnnotations" to "false",
"openApiNullable" to "false",
"dateLibrary" to "java17",
"useSpringBoot3" to "true",
"useJakartaEe" to "true",
"serializationLibrary" to "jackson",
"generateSupportingFiles" to "true"
))
library.set("resttemplate")
}
4 changes: 4 additions & 0 deletions gradle.lockfile
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@
# This file is expected to be part of source control.
ch.qos.logback:logback-classic:1.5.11=compileClasspath
ch.qos.logback:logback-core:1.5.11=compileClasspath
com.auth0:java-jwt:4.4.0=compileClasspath
com.auth0:jwks-rsa:0.22.1=compileClasspath
com.fasterxml.jackson.core:jackson-annotations:2.17.2=compileClasspath
com.fasterxml.jackson.core:jackson-core:2.17.2=compileClasspath
com.fasterxml.jackson.core:jackson-databind:2.17.2=compileClasspath
@@ -12,6 +14,8 @@ com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.2=compileClasspath
com.fasterxml.jackson.module:jackson-module-parameter-names:2.17.2=compileClasspath
com.fasterxml.jackson:jackson-bom:2.17.2=compileClasspath
com.google.code.findbugs:jsr305:3.0.2=compileClasspath
com.nimbusds:nimbus-jose-jwt:9.47=compileClasspath
io.jsonwebtoken:jjwt-api:0.12.6=compileClasspath
io.micrometer:micrometer-commons:1.13.6=compileClasspath
io.micrometer:micrometer-core:1.13.6=compileClasspath
io.micrometer:micrometer-jakarta9:1.13.6=compileClasspath
6 changes: 6 additions & 0 deletions helm/values-dev.yaml
Original file line number Diff line number Diff line change
@@ -31,6 +31,12 @@ microservice-chart:
ENV: "DEV"
JAVA_TOOL_OPTIONS: "-Xms128m -Xmx4g -Djava.util.concurrent.ForkJoinPool.common.parallelism=7 -javaagent:/app/applicationinsights-agent.jar -Dapplicationinsights.configuration.file=/mnt/file-config-external/appinsights-config/applicationinsights.json -agentlib:jdwp=transport=dt_socket,server=y,address=8001,suspend=n -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=3002 -Dcom.sun.management.jmxremote.rmi.port=3003 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false"

PDND_BASE_URL: https://auth.uat.interop.pagopa.it
PDND_ACCESS_TOKEN_AUDIENCE: auth.uat.interop.pagopa.it/client-assertion
PAGOPA_PDND_CONFIGURATION_CLIENT_ID: 890b7ca9-b402-4dce-9e8d-9a333d22d76d
PAGOPA_PDND_CONFIGURATION_KID: jxOpPRxM6oFcnnKtICqeW5l7fbxLr45IAsJ8Q9s-fK8
PAGOPA_PDND_CONFIGURATION_PURPOSE_ID: 87520bd5-207a-4616-85d9-10d7bb3e88b8

keyvault:
name: "p4pa-d-payhub-kv"
tenantId: "7788edaf-0346-4068-9d79-c868aed15b3d"
7 changes: 7 additions & 0 deletions helm/values-prod.yaml
Original file line number Diff line number Diff line change
@@ -31,6 +31,13 @@ microservice-chart:
ENV: "PROD"
JAVA_TOOL_OPTIONS: "-Xms128m -Xmx4g -Djava.util.concurrent.ForkJoinPool.common.parallelism=7 -javaagent:/app/applicationinsights-agent.jar -Dapplicationinsights.configuration.file=/mnt/file-config-external/appinsights-config/applicationinsights.json -agentlib:jdwp=transport=dt_socket,server=y,address=8001,suspend=n -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=3002 -Dcom.sun.management.jmxremote.rmi.port=3003 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false"

#TODO edit with real env when prod is ready P4ADEV-1518
PDND_BASE_URL: https://auth.uat.interop.pagopa.it
PDND_ACCESS_TOKEN_AUDIENCE: auth.uat.interop.pagopa.it/client-assertion
PAGOPA_PDND_CONFIGURATION_CLIENT_ID: 890b7ca9-b402-4dce-9e8d-9a333d22d76d
PAGOPA_PDND_CONFIGURATION_KID: jxOpPRxM6oFcnnKtICqeW5l7fbxLr45IAsJ8Q9s-fK8
PAGOPA_PDND_CONFIGURATION_PURPOSE_ID: 87520bd5-207a-4616-85d9-10d7bb3e88b8

keyvault:
name: "p4pa-p-payhub-kv"
tenantId: "7788edaf-0346-4068-9d79-c868aed15b3d"
6 changes: 6 additions & 0 deletions helm/values-uat.yaml
Original file line number Diff line number Diff line change
@@ -31,6 +31,12 @@ microservice-chart:
ENV: "UAT"
JAVA_TOOL_OPTIONS: "-Xms128m -Xmx4g -Djava.util.concurrent.ForkJoinPool.common.parallelism=7 -javaagent:/app/applicationinsights-agent.jar -Dapplicationinsights.configuration.file=/mnt/file-config-external/appinsights-config/applicationinsights.json -agentlib:jdwp=transport=dt_socket,server=y,address=8001,suspend=n -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=3002 -Dcom.sun.management.jmxremote.rmi.port=3003 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false"

PDND_BASE_URL: https://auth.uat.interop.pagopa.it
PDND_ACCESS_TOKEN_AUDIENCE: auth.uat.interop.pagopa.it/client-assertion
PAGOPA_PDND_CONFIGURATION_CLIENT_ID: 685e6542-8d1b-4837-a555-130e92c9dc6c
PAGOPA_PDND_CONFIGURATION_KID: y80rvmuzGPyfMw0n6v5K-yWsyUVYXiICG2zzNPAJg64
PAGOPA_PDND_CONFIGURATION_PURPOSE_ID: 87520bd5-207a-4616-85d9-10d7bb3e88b8

keyvault:
name: "p4pa-u-payhub-kv"
tenantId: "7788edaf-0346-4068-9d79-c868aed15b3d"
3 changes: 3 additions & 0 deletions helm/values.yaml
Original file line number Diff line number Diff line change
@@ -65,6 +65,9 @@ microservice-chart:
envSecret:
APPLICATIONINSIGHTS_CONNECTION_STRING: appinsights-connection-string

PDND_PRIVATE_KEY: piattaforma-unitaria-interop-priv
PDND_PUBLIC_KEY: piattaforma-unitaria-interop-pub

# nodeSelector: {}

# tolerations: []
10 changes: 10 additions & 0 deletions src/main/java/it/gov/pagopa/payhub/pdnd/config/AnprConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package it.gov.pagopa.payhub.pdnd.config;

import it.gov.pagopa.payhub.pdnd.model.PdndGenericConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "app.pdnd.anpr.config")
public class AnprConfig extends PdndGenericConfig {
}
14 changes: 14 additions & 0 deletions src/main/java/it/gov/pagopa/payhub/pdnd/config/PdndConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package it.gov.pagopa.payhub.pdnd.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "app.pdnd.config")
@Data
public class PdndConfig {
private String audience;
private String key;
private String publicKey;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package it.gov.pagopa.payhub.pdnd.connector.pdnd.client;

import it.gov.pagopa.common.pdnd.generated.dto.ClientCredentialsResponseDTO;

public interface PdndClient {
ClientCredentialsResponseDTO getAccessToken(String clientId, String assertions);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package it.gov.pagopa.payhub.pdnd.connector.pdnd.client;

import it.gov.pagopa.common.pdnd.generated.ApiClient;
import it.gov.pagopa.common.pdnd.generated.api.AuthApi;
import it.gov.pagopa.common.pdnd.generated.dto.ClientCredentialsResponseDTO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class PdndClientImpl implements PdndClient {

private static final String CLIENT_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
private static final String GRANT_TYPE = "client_credentials";
private final AuthApi authApi;

public PdndClientImpl(RestTemplateBuilder restTemplateBuilder,
@Value("${app.pdnd.base-url}") String pdndBaseUrl) {
RestTemplate restTemplate = restTemplateBuilder.build();

ApiClient apiClient = new ApiClient(restTemplate);
apiClient.setBasePath(pdndBaseUrl);
authApi = new AuthApi(apiClient);
}

@Override
public ClientCredentialsResponseDTO getAccessToken(String clientId, String assertions) {
return authApi.createToken(assertions, CLIENT_ASSERTION_TYPE, GRANT_TYPE, clientId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package it.gov.pagopa.payhub.pdnd.connector.pdnd.service;

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JOSEObjectType;
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.gov.pagopa.payhub.pdnd.config.PdndConfig;
import it.gov.pagopa.payhub.pdnd.model.PdndGenericConfig;
import it.gov.pagopa.payhub.pdnd.utils.CertUtils;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Date;
import java.util.UUID;
import org.springframework.stereotype.Service;

@Service
public class PdndClientAssertionBuilderService {

private final PdndConfig pdndConfig;

public PdndClientAssertionBuilderService(PdndConfig pdndConfig) {
this.pdndConfig = pdndConfig;
}

public String buildPdndClientAssertion(PdndGenericConfig pdndGenericConfig)
throws InvalidKeySpecException, NoSuchAlgorithmException, IOException, JOSEException {
JWTClaimsSet claims = buildPdndClientAssertionClaims(pdndGenericConfig.getClientId(), pdndGenericConfig.getPurposeId());
return signPdndJWT(pdndGenericConfig.getKid(), claims);
}

private JWTClaimsSet buildPdndClientAssertionClaims(String clientId, String purposeId) {
long now = System.currentTimeMillis() / 1000;
return new JWTClaimsSet.Builder()
.issuer(clientId)
.subject(clientId)
.audience(pdndConfig.getAudience())
.claim("purposeId",purposeId)
.issueTime(new Date(now * 1000))
.expirationTime(new Date((now + 300) * 1000))
.jwtID(UUID.randomUUID().toString())
.build();
}

private String signPdndJWT(String kid, JWTClaimsSet claims)
throws InvalidKeySpecException, NoSuchAlgorithmException, IOException, JOSEException {
JWSSigner signer = new RSASSASigner(CertUtils.pemKey2PrivateKey(pdndConfig.getKey()));
SignedJWT signedJWT = new SignedJWT(
new JWSHeader.Builder(JWSAlgorithm.RS256)
.type(JOSEObjectType.JWT)
.keyID(kid)
.build(),
claims
);
signedJWT.sign(signer);
return signedJWT.serialize();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package it.gov.pagopa.payhub.pdnd.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class PdndGenericConfig {
private String clientId;
private String kid;
private String purposeId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package it.gov.pagopa.payhub.pdnd.service;

import com.nimbusds.jose.JOSEException;
import it.gov.pagopa.payhub.pdnd.connector.pdnd.client.PdndClientImpl;
import it.gov.pagopa.payhub.pdnd.connector.pdnd.service.PdndClientAssertionBuilderService;
import it.gov.pagopa.payhub.pdnd.model.PdndGenericConfig;
import it.gov.pagopa.payhub.pdnd.utils.JWTUtils;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.concurrent.ConcurrentHashMap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class PdndService {

private final PdndClientImpl pdndClientImpl;
private final PdndClientAssertionBuilderService pdndClientAssertionBuilderService;
private final ConcurrentHashMap<PdndGenericConfig, String> jwtCache = new ConcurrentHashMap<>();

public PdndService(PdndClientImpl pdndClientImpl,
PdndClientAssertionBuilderService pdndClientAssertionBuilderService) {
this.pdndClientImpl = pdndClientImpl;
this.pdndClientAssertionBuilderService = pdndClientAssertionBuilderService;
}

public String generateToken(PdndGenericConfig pdndGenericConfig) {
return jwtCache.compute(pdndGenericConfig, (key, existingJwt) -> {
log.debug("Check cache for token exists and not expired");
if(existingJwt == null || JWTUtils.isJWTExpired(existingJwt)) {
try {
log.debug("Token not present or expired, generate new one");
String clientAssertion = pdndClientAssertionBuilderService.buildPdndClientAssertion(key);
return pdndClientImpl.getAccessToken(key.getClientId(), clientAssertion).getAccessToken();
} catch (InvalidKeySpecException | NoSuchAlgorithmException | IOException | JOSEException e) {
throw new RuntimeException(e);
}
}
log.debug("Token is present in cache");
return existingJwt;
});
}
}
33 changes: 33 additions & 0 deletions src/main/java/it/gov/pagopa/payhub/pdnd/utils/CertUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package it.gov.pagopa.payhub.pdnd.utils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

public class CertUtils {
private CertUtils(){}

public static RSAPrivateKey pemKey2PrivateKey(String privateKey) throws InvalidKeySpecException, NoSuchAlgorithmException, IOException {
String keyStringFormat = extractInlinePemBody(privateKey);
try(
InputStream is = new ByteArrayInputStream(Base64.getDecoder().decode(keyStringFormat))
) {
PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(is.readAllBytes());
KeyFactory kf = KeyFactory.getInstance("RSA");
return (RSAPrivateKey) kf.generatePrivate(encodedKeySpec);
}
}

public static String extractInlinePemBody(String target) {
return target
.replaceAll("^-----BEGIN[A-Z|\\s]+-----", "")
.replaceAll("\\s+", "")
.replaceAll("-----END[A-Z|\\s]+-----$", "");
}
}
21 changes: 21 additions & 0 deletions src/main/java/it/gov/pagopa/payhub/pdnd/utils/JWTUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package it.gov.pagopa.payhub.pdnd.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Date;

public class JWTUtils {
private JWTUtils() {
}

public static boolean isJWTExpired(String token) {
try {
DecodedJWT decodedJWT = JWT.decode(token);
Date expiresAt = decodedJWT.getExpiresAt();
return expiresAt.before(new Date());
} catch (JWTDecodeException e) {
throw new JWTDecodeException(e.getMessage());
}
}
}
Loading