Skip to content

Commit

Permalink
feat: Tokenization of fiscal code with PDV if any. (#82)
Browse files Browse the repository at this point in the history
  • Loading branch information
antoniotarricone authored May 16, 2024
1 parent badec5d commit 09342d2
Show file tree
Hide file tree
Showing 10 changed files with 481 additions and 298 deletions.
390 changes: 195 additions & 195 deletions dep-sha256.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,20 @@
<maven.compiler.target>${java.version}</maven.compiler.target>
<maven.compiler.source>${java.version}</maven.compiler.source>
<!-- Plug-ins version -->
<compiler-plugin.version>3.12.1</compiler-plugin.version>
<compiler-plugin.version>3.13.0</compiler-plugin.version>
<surefire-plugin.version>3.2.5</surefire-plugin.version>
<sonar-plugin.version>3.11.0.3922</sonar-plugin.version>
<jacoco-maven-plugin.version>0.8.10</jacoco-maven-plugin.version>
<jacoco-maven-plugin.version>0.8.12</jacoco-maven-plugin.version>
<depcheck-plugin.version>1.2.1</depcheck-plugin.version>
<!-- Encodings -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Quarkus version -->
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.10.0</quarkus.platform.version>
<quarkus.platform.version>3.10.1</quarkus.platform.version>
<!-- Other dependecies version -->
<common.version>2.2.1</common.version>
<common.version>2.3.0</common.version>
<lombok.version>1.18.32</lombok.version>
<otel-exporter-azure.version>3.8.3.0</otel-exporter-azure.version>
<!-- Sonar config -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.Objects;

import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.rest.client.inject.RestClient;

import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jwt.JWTClaimsSet;
Expand All @@ -24,6 +25,7 @@
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 it.pagopa.swclient.mil.pdv.client.Tokenizer;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

Expand All @@ -48,6 +50,7 @@ public class RefreshTokensService extends TokenService {
* @param clientVerifier
* @param roleFinder
* @param tokenSigner
* @paran tokenizer
*/
@Inject
RefreshTokensService(@ConfigProperty(name = "access.duration") long accessDuration,
Expand All @@ -56,8 +59,9 @@ public class RefreshTokensService extends TokenService {
@ConfigProperty(name = "token-audience", defaultValue = "mil.pagopa.it") String audience,
ClientVerifier clientVerifier,
RolesFinder roleFinder,
TokenSigner tokenSigner) {
super(accessDuration, refreshDuration, baseUrl, audience, clientVerifier, roleFinder, tokenSigner);
TokenSigner tokenSigner,
@RestClient Tokenizer tokenizer) {
super(accessDuration, refreshDuration, baseUrl, audience, clientVerifier, roleFinder, tokenSigner, tokenizer);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
package it.pagopa.swclient.mil.auth.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.bean.GetAccessTokenRequest;
import it.pagopa.swclient.mil.auth.bean.GetAccessTokenResponse;
import it.pagopa.swclient.mil.auth.qualifier.ClientCredentials;
import it.pagopa.swclient.mil.pdv.client.Tokenizer;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

Expand All @@ -37,6 +39,7 @@ public class TokenByClientSecretService extends TokenService {
* @param clientVerifier
* @param roleFinder
* @param tokenSigner
* @param tokenizer
*/
@Inject
TokenByClientSecretService(@ConfigProperty(name = "access.duration") long accessDuration,
Expand All @@ -45,8 +48,9 @@ public class TokenByClientSecretService extends TokenService {
@ConfigProperty(name = "token-audience", defaultValue = "mil.pagopa.it") String audience,
ClientVerifier clientVerifier,
RolesFinder roleFinder,
TokenSigner tokenSigner) {
super(accessDuration, refreshDuration, baseUrl, audience, clientVerifier, roleFinder, tokenSigner);
TokenSigner tokenSigner,
@RestClient Tokenizer tokenizer) {
super(accessDuration, refreshDuration, baseUrl, audience, clientVerifier, roleFinder, tokenSigner, tokenizer);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.util.Objects;

import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.rest.client.inject.RestClient;

import io.quarkus.cache.CacheResult;
import io.quarkus.logging.Log;
Expand All @@ -25,6 +26,7 @@
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 it.pagopa.swclient.mil.pdv.client.Tokenizer;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.WebApplicationException;
Expand Down Expand Up @@ -63,6 +65,7 @@ public class TokenByPasswordService extends TokenService {
* @param roleFinder
* @param tokenSigner
* @param repository
* @param tokenizer
*/
@Inject
TokenByPasswordService(@ConfigProperty(name = "access.duration") long accessDuration,
Expand All @@ -72,8 +75,9 @@ public class TokenByPasswordService extends TokenService {
ClientVerifier clientVerifier,
RolesFinder roleFinder,
TokenSigner tokenSigner,
AuthDataRepository repository) {
super(accessDuration, refreshDuration, baseUrl, audience, clientVerifier, roleFinder, tokenSigner);
AuthDataRepository repository,
@RestClient Tokenizer tokenizer) {
super(accessDuration, refreshDuration, baseUrl, audience, clientVerifier, roleFinder, tokenSigner, tokenizer);
this.repository = repository;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
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 it.pagopa.swclient.mil.pdv.client.Tokenizer;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.WebApplicationException;
Expand Down Expand Up @@ -52,6 +53,7 @@ public class TokenByPoyntTokenService extends TokenService {
* @param roleFinder
* @param tokenSigner
* @param poyntClient
* @param tokenizer
*/
@Inject
TokenByPoyntTokenService(
Expand All @@ -62,8 +64,9 @@ public class TokenByPoyntTokenService extends TokenService {
ClientVerifier clientVerifier,
RolesFinder roleFinder,
TokenSigner tokenSigner,
@RestClient PoyntClient poyntClient) {
super(accessDuration, refreshDuration, baseUrl, audience, clientVerifier, roleFinder, tokenSigner);
@RestClient PoyntClient poyntClient,
@RestClient Tokenizer tokenizer) {
super(accessDuration, refreshDuration, baseUrl, audience, clientVerifier, roleFinder, tokenSigner, tokenizer);
this.poyntClient = poyntClient;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import it.pagopa.swclient.mil.auth.bean.GetAccessTokenResponse;
import it.pagopa.swclient.mil.auth.bean.GrantType;
import it.pagopa.swclient.mil.auth.bean.Scope;
import it.pagopa.swclient.mil.pdv.bean.PersonalData;
import it.pagopa.swclient.mil.pdv.bean.Token;
import it.pagopa.swclient.mil.pdv.client.Tokenizer;

/**
* This class generates access token string and refresh token string if any and signs them.
Expand Down Expand Up @@ -61,6 +64,11 @@ public abstract class TokenService {
*/
protected TokenSigner tokenSigner;

/*
*
*/
private Tokenizer tokenizer;

/**
*
*/
Expand All @@ -76,6 +84,7 @@ public abstract class TokenService {
* @param clientVerifier
* @param roleFinder
* @param tokenSigner
* @param tokenizer
*/
TokenService(
long accessDuration,
Expand All @@ -84,14 +93,16 @@ public abstract class TokenService {
String audience,
ClientVerifier clientVerifier,
RolesFinder roleFinder,
TokenSigner tokenSigner) {
TokenSigner tokenSigner,
Tokenizer tokenizer) {
this.accessDuration = accessDuration;
this.refreshDuration = refreshDuration;
this.baseUrl = baseUrl;
this.audience = audience;
this.clientVerifier = clientVerifier;
this.roleFinder = roleFinder;
this.tokenSigner = tokenSigner;
this.tokenizer = tokenizer;
}

/**
Expand All @@ -104,15 +115,36 @@ private String concat(List<String> strings) {
}
return String.join(" ", strings);
}

/**
*
* @param request
* @param duration
* @param roles
* @param scopes
* @return
*/
private Uni<String> generate(GetAccessTokenRequest request, long duration, List<String> roles, List<String> scopes) {
String fiscalCode = request.getFiscalCode();
if (fiscalCode == null) {
return generate(request, duration, roles, scopes, null);
} else {
return tokenizer.tokenize(new PersonalData(fiscalCode))
.map(Token::getValue)
.chain(fiscalCodeToken -> generate(request, duration, roles, scopes, fiscalCodeToken));
}
}

/**
*
* @param request
* @param duration
* @param roles
* @param scopes
* @param fiscalCodeToken
* @return
*/
private Uni<String> generate(GetAccessTokenRequest request, long duration, List<String> roles, List<String> scopes, String fiscalCodeToken) {
Date now = new Date();
JWTClaimsSet payload = new JWTClaimsSet.Builder()
.subject(request.getClientId())
Expand All @@ -125,7 +157,7 @@ private Uni<String> generate(GetAccessTokenRequest request, long duration, List<
.claim(ClaimName.TERMINAL_ID, request.getTerminalId())
.claim(ClaimName.SCOPE, concat(scopes))
.claim(ClaimName.GROUPS, roles)
.claim(ClaimName.FISCAL_CODE, request.getFiscalCode())
.claim(ClaimName.FISCAL_CODE, fiscalCodeToken)
.issuer(baseUrl)
.audience(audience)
.build();
Expand Down
9 changes: 9 additions & 0 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,12 @@ quarkus.otel.azure.applicationinsights.connection.string=InstrumentationKey=dumm

%prod.quarkus.opentelemetry.tracer.exporter.azure.enabled=true
%prod.quarkus.otel.azure.applicationinsights.connection.string=${application-insights.connection-string}

# ------------------------------------------------------------------------------
# PDV Tokenizer
# ------------------------------------------------------------------------------
quarkus.rest-client.pdv-api.url=http://dummy
pdv-api.api-key=dummy

%prod.quarkus.rest-client.pdv-api.url=${pdv.url}
%prod.pdv-api.api-key=${pdv.api-key}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@
import it.pagopa.swclient.mil.auth.bean.TokenType;
import it.pagopa.swclient.mil.auth.util.UniGenerator;
import it.pagopa.swclient.mil.bean.Channel;
import it.pagopa.swclient.mil.pdv.bean.PersonalData;
import it.pagopa.swclient.mil.pdv.bean.Token;
import it.pagopa.swclient.mil.pdv.client.Tokenizer;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
Expand Down Expand Up @@ -130,6 +133,13 @@ class TokenByClientSecretResourceTest {
@InjectMock
@RestClient
AzureAuthClient authClient;

/*
*
*/
@InjectMock
@RestClient
Tokenizer tokenizer;

/**
*
Expand Down Expand Up @@ -206,6 +216,81 @@ void testOk() {
.body(JsonPropertyName.EXPIRES_IN, notNullValue(Long.class))
.body(JsonPropertyName.REFRESH_TOKEN, nullValue());
}

@Test
void testOkWithFiscalCode() {
/*
* Client repository setup.
*/
when(repository.getClient(AZURE_TOKEN, CLIENT_ID))
.thenReturn(UniGenerator.item(new Client(CLIENT_ID, Channel.POS, SALT, HASH, DESCRIPTION)));

/*
* Roles repository setup.
*/
when(repository.getRoles(AZURE_TOKEN, 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()))
.thenReturn(UniGenerator.item(new GetAccessTokenResponse(TokenType.BEARER, Instant.now().getEpochSecond() + AZURE_TOKEN_DURATION, "", "", AZURE_TOKEN)));

/*
* PDV tokenizer.
*/
when(tokenizer.tokenize(any(PersonalData.class)))
.thenReturn(UniGenerator.item(new Token("4b39a715-672b-4bf2-a7b1-76de90133334")));

/*
* 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(AZURE_TOKEN))
.thenReturn(UniGenerator.item(new GetKeysResponse(new BasicKey[] {
new BasicKey(keyUrl + KEY_NAME, keyAttributes)
})));

when(keyVaultClient.getKeyVersions(AZURE_TOKEN, KEY_NAME))
.thenReturn(UniGenerator.item(new GetKeysResponse(new BasicKey[] {
new BasicKey(keyUrl + KEY_NAME + "/" + KEY_VERSION, keyAttributes)
})));

when(keyVaultClient.getKey(AZURE_TOKEN, KEY_NAME, KEY_VERSION))
.thenReturn(UniGenerator.item(new DetailedKey(new KeyDetails(keyUrl + KEY_NAME + "/" + KEY_VERSION, KEY_TYPE, KEY_OPS, MODULUS, PUBLIC_EXPONENT), keyAttributes)));

when(keyVaultClient.sign(eq(AZURE_TOKEN), 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)
.formParam(FormParamName.FISCAL_CODE, "CHCZLN73D08A662B")
.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() {
Expand Down
Loading

0 comments on commit 09342d2

Please sign in to comment.