-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
300 additions
and
6 deletions.
There are no files selected for viewing
28 changes: 28 additions & 0 deletions
28
src/main/java/it/gov/pagopa/payhub/auth/mapper/A2ALegacyClaims2UserInfoMapper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package it.gov.pagopa.payhub.auth.mapper; | ||
|
||
import it.gov.pagopa.payhub.auth.dto.IamUserInfoDTO; | ||
import it.gov.pagopa.payhub.auth.dto.IamUserOrganizationRolesDTO; | ||
import it.gov.pagopa.payhub.auth.utils.Constants; | ||
import org.springframework.stereotype.Service; | ||
|
||
import java.util.Collections; | ||
import java.util.UUID; | ||
|
||
@Service | ||
public class A2ALegacyClaims2UserInfoMapper { | ||
|
||
public IamUserInfoDTO map(String subject) { | ||
return IamUserInfoDTO.builder() | ||
.systemUser(true) | ||
.issuer(subject) | ||
.userId(UUID.randomUUID().toString()) | ||
.name(subject) | ||
.familyName(subject) | ||
.fiscalCode(subject) | ||
.organizationAccess(IamUserOrganizationRolesDTO.builder() | ||
.organizationIpaCode(subject) | ||
.roles(Collections.singletonList(Constants.ROLE_ADMIN)) | ||
.build()) | ||
.build(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
src/main/java/it/gov/pagopa/payhub/auth/service/a2a/legacy/A2AClientLegacyPropConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package it.gov.pagopa.payhub.auth.service.a2a.legacy; | ||
|
||
import lombok.Getter; | ||
import lombok.Setter; | ||
import org.springframework.boot.context.properties.ConfigurationProperties; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.util.Map; | ||
|
||
@Getter | ||
@Setter | ||
@Component | ||
@ConfigurationProperties(prefix = "m2m.legacy.public") | ||
public class A2AClientLegacyPropConfig { | ||
private Map<String, String> secrets; | ||
} |
41 changes: 41 additions & 0 deletions
41
...n/java/it/gov/pagopa/payhub/auth/service/a2a/legacy/A2ALegacySecretsRetrieverService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package it.gov.pagopa.payhub.auth.service.a2a.legacy; | ||
|
||
import io.jsonwebtoken.io.Decoders; | ||
import it.gov.pagopa.payhub.auth.exception.custom.InvalidTokenException; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.stereotype.Service; | ||
|
||
import java.security.KeyFactory; | ||
import java.security.PublicKey; | ||
import java.security.spec.X509EncodedKeySpec; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
@Service | ||
@Slf4j | ||
public class A2ALegacySecretsRetrieverService { | ||
private final A2AClientLegacyPropConfig a2AClientLegacyPropConfig; | ||
|
||
public A2ALegacySecretsRetrieverService(A2AClientLegacyPropConfig a2AClientLegacyPropConfig) { | ||
this.a2AClientLegacyPropConfig = a2AClientLegacyPropConfig; | ||
} | ||
|
||
public Map<String, PublicKey> envToMapByPrefix(String prefix) { | ||
Map<String, PublicKey> secretMap = new HashMap<>(); | ||
a2AClientLegacyPropConfig.getSecrets() | ||
.forEach((key, value) -> | ||
secretMap.put(key, getPublicKeyFromString(value)) | ||
); | ||
return secretMap; | ||
} | ||
|
||
private PublicKey getPublicKeyFromString(String encodedKey) { | ||
try { | ||
X509EncodedKeySpec publicKeyX509 = new X509EncodedKeySpec(Decoders.BASE64.decode(encodedKey)); | ||
KeyFactory kf = KeyFactory.getInstance("RSA"); | ||
return kf.generatePublic(publicKeyX509); | ||
} catch (Exception e){ | ||
throw new InvalidTokenException("invalid public key"); | ||
} | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
src/main/java/it/gov/pagopa/payhub/auth/service/a2a/legacy/JWTLegacyHandlerService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package it.gov.pagopa.payhub.auth.service.a2a.legacy; | ||
|
||
import com.auth0.jwt.RegisteredClaims; | ||
import com.auth0.jwt.interfaces.Claim; | ||
import it.gov.pagopa.payhub.auth.dto.IamUserInfoDTO; | ||
import it.gov.pagopa.payhub.auth.mapper.A2ALegacyClaims2UserInfoMapper; | ||
import it.gov.pagopa.payhub.auth.service.TokenStoreService; | ||
import it.gov.pagopa.payhub.model.generated.AccessToken; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.apache.commons.lang3.tuple.Pair; | ||
import org.springframework.stereotype.Service; | ||
|
||
import java.util.Map; | ||
|
||
@Service | ||
@Slf4j | ||
public class JWTLegacyHandlerService { | ||
private final ValidateJWTLegacyService validateJWTLegacyService; | ||
private final TokenStoreService tokenStoreService; | ||
private final A2ALegacyClaims2UserInfoMapper a2ALegacyClaims2UserInfoMapper; | ||
|
||
public JWTLegacyHandlerService(ValidateJWTLegacyService validateJWTLegacyService, TokenStoreService tokenStoreService, A2ALegacyClaims2UserInfoMapper a2ALegacyClaims2UserInfoMapper) { | ||
this.validateJWTLegacyService = validateJWTLegacyService; | ||
this.tokenStoreService = tokenStoreService; | ||
this.a2ALegacyClaims2UserInfoMapper = a2ALegacyClaims2UserInfoMapper; | ||
} | ||
|
||
public void handleLegacyToken(String token) { | ||
Pair<String, Map<String, Claim>> claims = validateJWTLegacyService.validate(token); | ||
AccessToken accessToken = AccessToken.builder() | ||
.accessToken(token) | ||
.tokenType("bearer") | ||
.expiresIn(claims.getRight().get(RegisteredClaims.EXPIRES_AT).asInt()) | ||
.build(); | ||
IamUserInfoDTO iamUser = a2ALegacyClaims2UserInfoMapper.map(claims.getLeft()); | ||
tokenStoreService.save(accessToken.getAccessToken(), iamUser); | ||
} | ||
} |
88 changes: 88 additions & 0 deletions
88
src/main/java/it/gov/pagopa/payhub/auth/service/a2a/legacy/ValidateJWTLegacyService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package it.gov.pagopa.payhub.auth.service.a2a.legacy; | ||
|
||
import com.auth0.jwt.JWT; | ||
import com.auth0.jwt.JWTVerifier; | ||
import com.auth0.jwt.RegisteredClaims; | ||
import com.auth0.jwt.algorithms.Algorithm; | ||
import com.auth0.jwt.interfaces.Claim; | ||
import com.auth0.jwt.interfaces.DecodedJWT; | ||
import it.gov.pagopa.payhub.auth.exception.custom.InvalidTokenException; | ||
import org.apache.commons.lang3.StringUtils; | ||
import org.apache.commons.lang3.tuple.ImmutablePair; | ||
import org.apache.commons.lang3.tuple.Pair; | ||
import org.springframework.stereotype.Service; | ||
|
||
import java.security.PublicKey; | ||
import java.security.interfaces.RSAPublicKey; | ||
import java.time.Instant; | ||
import java.util.Map; | ||
|
||
@Service | ||
public class ValidateJWTLegacyService { | ||
private static final String ENV_M2M_LEGACY_SECRET_PREFIX = "m2m.legacy.public"; | ||
private static final String TOKEN_TYPE_A2A = "a2a"; | ||
|
||
private final A2ALegacySecretsRetrieverService a2ALegacySecretsRetrieverService; | ||
private final Map<String, PublicKey> clientApplicationsPublicKey; | ||
|
||
public ValidateJWTLegacyService(A2ALegacySecretsRetrieverService a2ALegacySecretsRetrieverService) { | ||
this.a2ALegacySecretsRetrieverService = a2ALegacySecretsRetrieverService; | ||
this.clientApplicationsPublicKey = a2ALegacySecretsRetrieverService.envToMapByPrefix(ENV_M2M_LEGACY_SECRET_PREFIX); | ||
} | ||
|
||
public Pair<String, Map<String, Claim>> validate(String token) { | ||
verifyEnvMap(); | ||
Pair<String, Map<String, Claim>> claims = this.clientApplicationsPublicKey.keySet().stream() | ||
.map(key -> new ImmutablePair<>(key, this.clientApplicationsPublicKey.get(key))) | ||
.map(pair -> validateLegacyToken(pair.getLeft(), token, pair.getRight())) | ||
.findFirst() | ||
.orElseThrow(() -> new InvalidTokenException("Invalid token for A2A call")); | ||
isA2AAuthToken(claims.getRight()); | ||
validateClaims(claims.getRight()); | ||
validateSubject(claims.getLeft()); | ||
return claims; | ||
} | ||
|
||
private void verifyEnvMap() { | ||
if (this.clientApplicationsPublicKey.isEmpty()){ | ||
throw new InvalidTokenException("The token is not present"); | ||
} | ||
} | ||
|
||
public void isA2AAuthToken(Map<String, Claim> claims){ | ||
if (TOKEN_TYPE_A2A.equals(claims.getOrDefault("type", null))) | ||
throw new InvalidTokenException("Invalid token type"); | ||
} | ||
|
||
private void validateClaims(Map<String, Claim> claims) { | ||
if (claims.get(RegisteredClaims.ISSUED_AT).asInstant().isBefore(Instant.now().minusSeconds(3600 * 24))) { | ||
throw new InvalidTokenException("Invalid field iat"); | ||
} | ||
if (claims.get(RegisteredClaims.EXPIRES_AT).asInstant().isAfter(Instant.now().plusSeconds(3600 * 24))) { | ||
throw new InvalidTokenException("Invalid field exp"); | ||
} | ||
if (StringUtils.isBlank(claims.get(RegisteredClaims.JWT_ID).asString())) { | ||
throw new InvalidTokenException("Invalid field jti"); | ||
} | ||
} | ||
|
||
private void validateSubject(String subject) { | ||
if (StringUtils.isBlank(subject)) { | ||
throw new InvalidTokenException("Invalid subject"); | ||
} | ||
} | ||
|
||
private Pair<String, Map<String, Claim>> validateLegacyToken(String app, String token, PublicKey publicKey) { | ||
try{ | ||
DecodedJWT jwt = JWT.decode(token); | ||
|
||
Algorithm algorithm = Algorithm.RSA512((RSAPublicKey) publicKey); | ||
JWTVerifier verifier = JWT.require(algorithm).build(); | ||
verifier.verify(jwt); | ||
|
||
return new ImmutablePair<>(app, jwt.getClaims()); | ||
} catch (Exception e) { | ||
return null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
.../it/gov/pagopa/payhub/auth/service/a2a/retrieve/A2ALegacySecretsRetrieverServiceTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package it.gov.pagopa.payhub.auth.service.a2a.retrieve; | ||
|
||
import io.jsonwebtoken.SignatureAlgorithm; | ||
import io.jsonwebtoken.io.Encoders; | ||
import io.jsonwebtoken.security.Keys; | ||
import it.gov.pagopa.payhub.auth.service.a2a.legacy.A2AClientLegacyPropConfig; | ||
import it.gov.pagopa.payhub.auth.service.a2a.legacy.A2ALegacySecretsRetrieverService; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.mockito.Mock; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
|
||
import java.security.KeyPair; | ||
import java.security.PublicKey; | ||
import java.util.Map; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
import static org.mockito.Mockito.when; | ||
|
||
@ExtendWith(MockitoExtension.class) | ||
class A2ALegacySecretsRetrieverServiceTest { | ||
|
||
@Mock | ||
private A2AClientLegacyPropConfig a2aClientLegacyPropConfigMock; | ||
|
||
private A2ALegacySecretsRetrieverService service; | ||
|
||
private KeyPair keyPair; | ||
|
||
@BeforeEach | ||
public void setUp() { | ||
service = new A2ALegacySecretsRetrieverService(a2aClientLegacyPropConfigMock); | ||
keyPair = Keys.keyPairFor(SignatureAlgorithm.RS512); | ||
} | ||
|
||
@Test | ||
public void givenEnvPropWhenEnvToMapByPrefixThenGetKey() { | ||
//Given | ||
String secretKeyPrefix = "m2m.legacy.public"; | ||
PublicKey publicKey = keyPair.getPublic(); | ||
String publicKeyEncoded = Encoders.BASE64.encode(keyPair.getPublic().getEncoded()); | ||
Map<String, String> mockSettings = Map.of("acme", publicKeyEncoded); | ||
when(a2aClientLegacyPropConfigMock.getSecrets()).thenReturn(mockSettings); | ||
//When | ||
|
||
Map<String, PublicKey> result = service.envToMapByPrefix(secretKeyPrefix); | ||
|
||
//Then | ||
assertNotNull(result); | ||
assertEquals(publicKey, result.get("acme")); | ||
} | ||
|
||
@Test | ||
public void WhenEnvToMapByPrefixThenGetEmptyMap() { | ||
//When | ||
Map<String, PublicKey> result = service.envToMapByPrefix("nonExistent"); | ||
//Then | ||
assertEquals(0, result.size()); | ||
} | ||
} |