From 8c7358f2114673018f11b743f4901455ace94493 Mon Sep 17 00:00:00 2001 From: Priyanka Sharma Date: Tue, 7 Jan 2025 16:59:56 +0530 Subject: [PATCH 01/15] feat: boilerplate for java tdk --- packages/java/.env.example | 2 + packages/java/pom.xml | 102 +++++++++++ .../authProvider/helper/ProjectTokenUtil.java | 22 +++ .../types/ProjectScopedToken.java | 19 ++ .../authProvider/types/SignPayload.java | 42 +++++ .../com/affinidi/common/EnvironmentUtil.java | 165 ++++++++++++++++++ .../java/com/affinidi/common/VaultUtil.java | 69 ++++++++ .../affinidi/common/EnvironmentUtilTest.java | 119 +++++++++++++ .../com/affinidi/common/VaultUtilTest.java | 124 +++++++++++++ 9 files changed, 664 insertions(+) create mode 100644 packages/java/.env.example create mode 100644 packages/java/pom.xml create mode 100644 packages/java/src/main/java/com/affinidi/authProvider/helper/ProjectTokenUtil.java create mode 100644 packages/java/src/main/java/com/affinidi/authProvider/types/ProjectScopedToken.java create mode 100644 packages/java/src/main/java/com/affinidi/authProvider/types/SignPayload.java create mode 100644 packages/java/src/main/java/com/affinidi/common/EnvironmentUtil.java create mode 100644 packages/java/src/main/java/com/affinidi/common/VaultUtil.java create mode 100644 packages/java/src/test/java/com/affinidi/common/EnvironmentUtilTest.java create mode 100644 packages/java/src/test/java/com/affinidi/common/VaultUtilTest.java diff --git a/packages/java/.env.example b/packages/java/.env.example new file mode 100644 index 000000000..4c79f59c8 --- /dev/null +++ b/packages/java/.env.example @@ -0,0 +1,2 @@ +AFFINIDI_TDK_ENVIRONMENT= +NEXT_PUBLIC_AFFINIDI_TDK_ENVIRONMENT= diff --git a/packages/java/pom.xml b/packages/java/pom.xml new file mode 100644 index 000000000..baabee2fb --- /dev/null +++ b/packages/java/pom.xml @@ -0,0 +1,102 @@ + + + + 4.0.0 + + com.affinidi + java-tdk + 1.0 + + java-tdk + https://github.com/affinidi/affinidi-tdk + + + UTF-8 + 1.8 + 1.8 + + + + + org.junit.jupiter + junit-jupiter-api + 5.11.0 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.11.0 + test + + + io.github.cdimascio + dotenv-java + 2.2.0 + + + org.glassfish.jersey.core + jersey-common + 3.1.7 + + + org.mockito + mockito-core + 3.12.4 + test + + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + + org.apache.maven.plugins + maven-surefire-plugin + + + maven-failsafe-plugin + 3.5.0 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + diff --git a/packages/java/src/main/java/com/affinidi/authProvider/helper/ProjectTokenUtil.java b/packages/java/src/main/java/com/affinidi/authProvider/helper/ProjectTokenUtil.java new file mode 100644 index 000000000..4514ba3dd --- /dev/null +++ b/packages/java/src/main/java/com/affinidi/authProvider/helper/ProjectTokenUtil.java @@ -0,0 +1,22 @@ +package com.affinidi.authProvider.helper; + +import com.affinidi.authProvider.types.ProjectScopedToken; +import com.affinidi.authProvider.types.SignPayload; + +public class ProjectTokenUtil { + + public SignPayload signPayload(String tokenId, String audience, String privateKey, String passphrase, String keyId){ + return null; + + } + + public SignPayload getUserAccessToken(String tokenId, String audience, String privateKey, String passphrase, String keyId){ + return null; + } + + public ProjectScopedToken fetchProjectScopedToken(String apiGatewayUrl, String projectId, String tokenId, + String audience, String privateKey, String passphrase, String keyId){ + return null; + } + +} diff --git a/packages/java/src/main/java/com/affinidi/authProvider/types/ProjectScopedToken.java b/packages/java/src/main/java/com/affinidi/authProvider/types/ProjectScopedToken.java new file mode 100644 index 000000000..a67e7c4ff --- /dev/null +++ b/packages/java/src/main/java/com/affinidi/authProvider/types/ProjectScopedToken.java @@ -0,0 +1,19 @@ +package com.affinidi.authProvider.types; + +public class ProjectScopedToken extends SignPayload{ + private String apiGatewayUrl; + public String getApiGatewayUrl() { + return apiGatewayUrl; + } + public void setApiGatewayUrl(String apiGatewayUrl) { + this.apiGatewayUrl = apiGatewayUrl; + } + public String getProjectId() { + return projectId; + } + public void setProjectId(String projectId) { + this.projectId = projectId; + } + private String projectId; + +} diff --git a/packages/java/src/main/java/com/affinidi/authProvider/types/SignPayload.java b/packages/java/src/main/java/com/affinidi/authProvider/types/SignPayload.java new file mode 100644 index 000000000..5e97dffcc --- /dev/null +++ b/packages/java/src/main/java/com/affinidi/authProvider/types/SignPayload.java @@ -0,0 +1,42 @@ +package com.affinidi.authProvider.types; + +public class SignPayload { + private String tokenId; + private String audience; + private String privateKey; + private String passphrase; + private String keyId; + + public String getTokenId() { + return tokenId; + } + public void setTokenId(String tokenId) { + this.tokenId = tokenId; + } + public String getAudience() { + return audience; + } + public void setAudience(String audience) { + this.audience = audience; + } + public String getPrivateKey() { + return privateKey; + } + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + } + public String getPassphrase() { + return passphrase; + } + public void setPassphrase(String passphrase) { + this.passphrase = passphrase; + } + public String getKeyId() { + return keyId; + } + public void setKeyId(String keyId) { + this.keyId = keyId; + } + + +} diff --git a/packages/java/src/main/java/com/affinidi/common/EnvironmentUtil.java b/packages/java/src/main/java/com/affinidi/common/EnvironmentUtil.java new file mode 100644 index 000000000..1456b7718 --- /dev/null +++ b/packages/java/src/main/java/com/affinidi/common/EnvironmentUtil.java @@ -0,0 +1,165 @@ + +package com.affinidi.common; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; +import io.github.cdimascio.dotenv.Dotenv; + +/** +* This class provides utility functions required to access environment specific +* configurations. The environment can be specified in the .env file at the project +* base using AFFINIDI_TDK_PROPERTY_NAME or NEXT_PUBLIC_AFFINIDI_TDK_ENVIRONMENT as +* dev, local or prod. +* +* +* @author Priyanka +* +*/ + +public class EnvironmentUtil { + + private static Dotenv properties; + private static Logger logger = Logger.getLogger(EnvironmentUtil.class.getName()); + + static { + properties = Dotenv.load(); + } + + + /** + * Returns the environment name as configured in the .env file + * In case no configuration is found, the default return is + * production environment + * + * @return String + */ + public String getConfiguredEnvironment() { + String configuredEnvironment = null; + if (properties != null) { + configuredEnvironment = properties.get(Environment.AFFINIDI_TDK_PROPERTY_NAME); + if (configuredEnvironment == null) { + configuredEnvironment = properties.get(Environment.NEXT_AFFINIDI_TDK_PROPERTY_NAME); + } + } + if (configuredEnvironment == null) { + logger.severe("Could not find environment details for "+configuredEnvironment+". Defaulting to production"); + configuredEnvironment = Environment.PRODUCTION.environmentName; + } + return configuredEnvironment; + } + + + /** + * Return the default region name + * + * @return String + */ + public String getDefaultRegion() { + return Environment.DEFAULT_REGION; + } + + + /** + * Returns the IOT url string for the configured environment + * + * @return String + */ + public String getIotUrlForEnvironment() { + return getEnvironmentDetail().iotUrl; + } + + /** + * Returns the elements auth token url string for the configured environment + * + * @return String + */ + public String getElementAuthTokenUrlForEnvironment() { + return getEnvironmentDetail().elementsAuthTokenUrl; + } + + /** + * Returns the vault URL for the configured environment + * + * @return String + */ + public String getVaultUrlForEnvironment() { + return getEnvironmentDetail().vaultUrl; + } + + /** + * @return String + */ + public String getApiGatewayUrlForEnvironment() { + return getEnvironmentDetail().apiGatewayUrl; + } + + /** + * Returns the APi gateway URL for the configured environment + * + * @return String + */ + private Environment getEnvironmentDetail() { + + String envName = getConfiguredEnvironment(); + Environment envDetail = (envName != null) ? Environment.getEnvSpecificDetails(envName) : null; + + if (envDetail == null) { + envDetail = Environment.getEnvSpecificDetails(Environment.PRODUCTION.environmentName); + logger.severe("Could not find environment details for the specified name " + envName); + } + return envDetail; + } +} + +enum Environment { + + LOCAL("LOCAL", Environment.LOCAL_IOT_URL, Environment.LOCAL_APIGATEWAY_URL, Environment.LOCAL_ELEMENTS_AUTH_TOKEN_URL, Environment.LOCAL_VAULT_URL), + DEVELOPMENT("DEV", Environment.DEV_IOT_URL, Environment.DEV_APIGATEWAY_URL, Environment.DEV_ELEMENTS_AUTH_TOKEN_URL, Environment.DEV_VAULT_URL), + PRODUCTION("PROD", Environment.PROD_IOT_URL, Environment.PROD_APIGATEWAY_URL, Environment.PROD_ELEMENTS_AUTH_TOKEN_URL, Environment.PROD_VAULT_URL),; + + static Environment getEnvSpecificDetails(String environmentName) { + return EnvironmentMap.get(environmentName.toUpperCase()); + } + + public final String environmentName; + public final String iotUrl; + public final String elementsAuthTokenUrl; + public final String vaultUrl; + public final String apiGatewayUrl; + + private Environment(String environmentName, String iotUrl, String apiGatewayUrl, String elementsAuthTokenUrl, + String vaultUrl) { + this.environmentName = environmentName; + this.iotUrl = iotUrl; + this.apiGatewayUrl = apiGatewayUrl; + this.elementsAuthTokenUrl = elementsAuthTokenUrl; + this.vaultUrl = vaultUrl; + } + + private static final Map EnvironmentMap = new HashMap<>(); + static { + for (Environment env : values()) { + EnvironmentMap.put(env.environmentName, env); + } + } + + static final String AFFINIDI_TDK_PROPERTY_NAME = "AFFINIDI_TDK_ENVIRONMENT"; + static final String NEXT_AFFINIDI_TDK_PROPERTY_NAME = "NEXT_PUBLIC_AFFINIDI_TDK_ENVIRONMENT"; + static final String DEFAULT_REGION = "ap-southeast-1"; + + static final String LOCAL_IOT_URL = "a3sq1vuw0cw9an-ats.iot.ap-southeast-1.amazonaws.com"; + static final String LOCAL_APIGATEWAY_URL = "https://apse1.dev.api.affinidi.io"; + static final String LOCAL_ELEMENTS_AUTH_TOKEN_URL = "https://apse1.dev.auth.developer.affinidi.io/auth/oauth2/token"; + static final String LOCAL_VAULT_URL = "http://localhost:3001"; + + static final String DEV_IOT_URL = "a3sq1vuw0cw9an-ats.iot.ap-southeast-1.amazonaws.com"; + static final String DEV_APIGATEWAY_URL = "https://apse1.dev.api.affinidi.io"; + static final String DEV_ELEMENTS_AUTH_TOKEN_URL = "https://apse1.dev.auth.developer.affinidi.io/auth/oauth2/token"; + static final String DEV_VAULT_URL = "https://vault.dev.affinidi.com"; + + static final String PROD_IOT_URL = "a13pfgsvt8xhx-ats.iot.ap-southeast-1.amazonaws.com"; + static final String PROD_APIGATEWAY_URL = "https://apse1.api.affinidi.io"; + static final String PROD_ELEMENTS_AUTH_TOKEN_URL = "https://apse1.auth.developer.affinidi.io/auth/oauth2/token"; + static final String PROD_VAULT_URL = "https://vault.affinidi.com"; + +} diff --git a/packages/java/src/main/java/com/affinidi/common/VaultUtil.java b/packages/java/src/main/java/com/affinidi/common/VaultUtil.java new file mode 100644 index 000000000..697141138 --- /dev/null +++ b/packages/java/src/main/java/com/affinidi/common/VaultUtil.java @@ -0,0 +1,69 @@ +package com.affinidi.common; + +import java.net.URI; +import java.util.logging.Logger; +import jakarta.ws.rs.core.UriBuilder; + +/** +* This class provides utility functions required to create vault specific links. +* Refer {@EnvironmentUtil} for more information +* +* +* @author Priyanka +* +*/ +public class VaultUtil { + + static final String VAULT_SHARE_PATH = "/login"; + static final String VAULT_CLAIM_PATH = "/claim"; + static final String SHARE_REQUEST_PARAM = "request"; + static final String SHARE_CLIENT_PARAM = "clientId"; + static final String CLAIM_CREDENTIAL_URI_PARAM = "credential_offer_uri"; + + private static Logger logger = Logger.getLogger(VaultUtil.class.getName()); + EnvironmentUtil environmentUtil = new EnvironmentUtil(); + + /** + * Returns the credential offer claim link specific to the configured environment + * + * @param credentialOfferUri + * @return String + */ + public String buildClaimLink(String credentialOfferUri) { + String webVaultUrl = environmentUtil.getVaultUrlForEnvironment(); + + if(credentialOfferUri == null || credentialOfferUri.equals("")){ + logger.severe("Invalid Credential Offer URI passed to utility "+credentialOfferUri+". Returning vault url"); + return webVaultUrl; + } + + webVaultUrl = String.join("", webVaultUrl, VAULT_CLAIM_PATH); + URI claimUri = UriBuilder.fromUri(webVaultUrl).queryParam(CLAIM_CREDENTIAL_URI_PARAM, credentialOfferUri).build(); + return claimUri.toString(); + } + + /** + * Returns the vault share link given request and a client id, + * specific to the configured environment + * + * @param request + * @param clientId + * @return String + */ + public String buildShareLink(String request, String clientId){ + + String webVaultUrl = environmentUtil.getVaultUrlForEnvironment(); + webVaultUrl = String.join("", webVaultUrl, VAULT_SHARE_PATH); + + URI shareUri = UriBuilder.fromUri(webVaultUrl). + queryParam(SHARE_REQUEST_PARAM, request).queryParam(SHARE_CLIENT_PARAM, clientId).build(); + return shareUri.toString(); + + } + + + + +} + + diff --git a/packages/java/src/test/java/com/affinidi/common/EnvironmentUtilTest.java b/packages/java/src/test/java/com/affinidi/common/EnvironmentUtilTest.java new file mode 100644 index 000000000..a67fe3a45 --- /dev/null +++ b/packages/java/src/test/java/com/affinidi/common/EnvironmentUtilTest.java @@ -0,0 +1,119 @@ +package com.affinidi.common; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; + +import static org.mockito.Mockito.doReturn; + +public class EnvironmentUtilTest { + + @Spy + EnvironmentUtil spyEnvironmentUtil; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void testGetVaultUrlForEnvironment_InLocal(){ + + //Test the function call for local environment without reading the configured property + doReturn("LOCAL").when(spyEnvironmentUtil).getConfiguredEnvironment(); + + assertEquals(Environment.LOCAL_VAULT_URL, spyEnvironmentUtil.getVaultUrlForEnvironment()); + } + @Test + void testGetVaultUrlForEnvironment_InDev(){ + + //Test the function call for local environment without reading the configured property + doReturn("DEV").when(spyEnvironmentUtil).getConfiguredEnvironment(); + + assertEquals(Environment.DEV_VAULT_URL, spyEnvironmentUtil.getVaultUrlForEnvironment()); + } + @Test + void testGetVaultUrlForEnvironment_InProd(){ + + //Test the function call for local environment without reading the configured property + doReturn("PROD").when(spyEnvironmentUtil).getConfiguredEnvironment(); + + assertEquals(Environment.PROD_VAULT_URL, spyEnvironmentUtil.getVaultUrlForEnvironment()); + } + + @Test + void testGetIotaUrlForEnvironment_InLocal(){ + + //Test the function call for local environment without reading the configured property + doReturn("LOCAL").when(spyEnvironmentUtil).getConfiguredEnvironment(); + + assertEquals(Environment.LOCAL_IOT_URL, spyEnvironmentUtil.getIotUrlForEnvironment()); + } + @Test + void testGetIotaUrlForEnvironment_InDev(){ + //Test the function call for local environment without reading the configured property + doReturn("DEV").when(spyEnvironmentUtil).getConfiguredEnvironment(); + + assertEquals(Environment.DEV_IOT_URL, spyEnvironmentUtil.getIotUrlForEnvironment()); + } + @Test + void testGetIotaUrlForEnvironment_InProd(){ + + //Test the function call for local environment without reading the configured property + doReturn("PROD").when(spyEnvironmentUtil).getConfiguredEnvironment(); + + assertEquals(Environment.PROD_IOT_URL, spyEnvironmentUtil.getIotUrlForEnvironment()); + } + + @Test + void testGetElementAuthTokenUrlForEnvironment_InLocal(){ + + //Test the function call for local environment without reading the configured property + doReturn("LOCAL").when(spyEnvironmentUtil).getConfiguredEnvironment(); + + assertEquals(Environment.LOCAL_ELEMENTS_AUTH_TOKEN_URL, spyEnvironmentUtil.getElementAuthTokenUrlForEnvironment()); + } + @Test + void testGetElementAuthTokenUrlForEnvironment_InDev(){ + + //Test the function call for local environment without reading the configured property + doReturn("DEV").when(spyEnvironmentUtil).getConfiguredEnvironment(); + + assertEquals(Environment.DEV_ELEMENTS_AUTH_TOKEN_URL, spyEnvironmentUtil.getElementAuthTokenUrlForEnvironment()); + } + @Test + void testGetElementAuthTokenUrlForEnvironment_InProd(){ + + //Test the function call for local environment without reading the configured property + doReturn("PROD").when(spyEnvironmentUtil).getConfiguredEnvironment(); + + assertEquals(Environment.PROD_ELEMENTS_AUTH_TOKEN_URL, spyEnvironmentUtil.getElementAuthTokenUrlForEnvironment()); + } + @Test + void testGetApiGatewayUrlForEnvironment_InLocal(){ + + //Test the function call for local environment without reading the configured property + doReturn("LOCAL").when(spyEnvironmentUtil).getConfiguredEnvironment(); + + assertEquals(Environment.LOCAL_APIGATEWAY_URL, spyEnvironmentUtil.getApiGatewayUrlForEnvironment()); + } + @Test + void testGetApiGatewayUrlForEnvironment_InDev(){ + + //Test the function call for local environment without reading the configured property + doReturn("DEV").when(spyEnvironmentUtil).getConfiguredEnvironment(); + + assertEquals(Environment.DEV_APIGATEWAY_URL, spyEnvironmentUtil.getApiGatewayUrlForEnvironment()); + } + @Test + void testGetApiGatewayUrlForEnvironment_InProd(){ + + //Test the function call for local environment without reading the configured property + doReturn("PROD").when(spyEnvironmentUtil).getConfiguredEnvironment(); + + assertEquals(Environment.PROD_APIGATEWAY_URL, spyEnvironmentUtil.getApiGatewayUrlForEnvironment()); + } +} diff --git a/packages/java/src/test/java/com/affinidi/common/VaultUtilTest.java b/packages/java/src/test/java/com/affinidi/common/VaultUtilTest.java new file mode 100644 index 000000000..edbe0940e --- /dev/null +++ b/packages/java/src/test/java/com/affinidi/common/VaultUtilTest.java @@ -0,0 +1,124 @@ +package com.affinidi.common; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +public class VaultUtilTest { + + @Mock + EnvironmentUtil environmentUtil; + + @InjectMocks + VaultUtil vaultUtil = new VaultUtil(); + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void testBuildShareLink_InLocal(){ + + Mockito.when(environmentUtil.getVaultUrlForEnvironment()).thenReturn(Environment.LOCAL_VAULT_URL); + + String testRequestId = "request1234"; + String testClientId = "clientabcde"; + String shareLink = vaultUtil.buildShareLink(testRequestId, testClientId); + + String expectedValue = Environment.LOCAL_VAULT_URL + VaultUtil.VAULT_SHARE_PATH + + "?"+VaultUtil.SHARE_REQUEST_PARAM+"="+testRequestId + + "&"+VaultUtil.SHARE_CLIENT_PARAM+"="+testClientId; + + assertEquals(expectedValue, shareLink); + } + + @Test + void testBuildShareLink_InDev(){ + + Mockito.when(environmentUtil.getVaultUrlForEnvironment()).thenReturn(Environment.DEV_VAULT_URL); + + String testRequestId = "request1234"; + String testClientId = "clientabcde"; + String shareLink = vaultUtil.buildShareLink(testRequestId, testClientId); + + String expectedValue = Environment.DEV_VAULT_URL + VaultUtil.VAULT_SHARE_PATH + + "?"+VaultUtil.SHARE_REQUEST_PARAM+"="+testRequestId + + "&"+VaultUtil.SHARE_CLIENT_PARAM+"="+testClientId; + + assertEquals(expectedValue, shareLink); + } + + @Test + void testBuildShareLink_InProd(){ + + Mockito.when(environmentUtil.getVaultUrlForEnvironment()).thenReturn(Environment.PROD_VAULT_URL); + + String testRequestId = "request1234"; + String testClientId = "clientabcde"; + String shareLink = vaultUtil.buildShareLink(testRequestId, testClientId); + + String expectedValue = Environment.PROD_VAULT_URL + VaultUtil.VAULT_SHARE_PATH + + "?"+VaultUtil.SHARE_REQUEST_PARAM+"="+testRequestId + + "&"+VaultUtil.SHARE_CLIENT_PARAM+"="+testClientId; + + assertEquals(expectedValue, shareLink); + } + + @Test + void testBuildClaimLink_InLocal(){ + + Mockito.when(environmentUtil.getVaultUrlForEnvironment()).thenReturn(Environment.LOCAL_VAULT_URL); + + String testCredentialOfferUri = "https://00112233-xxx-yyyy-aaaaa-abcd2345678.apse1.issuance.affinidi.io/offers/cb3817e6-4b16-4f19-a658-8eb096541bcc"; + + String claimLink = vaultUtil.buildClaimLink(testCredentialOfferUri); + + String expectedValue = Environment.LOCAL_VAULT_URL + VaultUtil.VAULT_CLAIM_PATH + + "?"+VaultUtil.CLAIM_CREDENTIAL_URI_PARAM+"="+URLEncoder.encode(testCredentialOfferUri, StandardCharsets.UTF_8); + + assertEquals(expectedValue, claimLink); + } + + + @Test + void testBuildClaimLink_InDev(){ + + Mockito.when(environmentUtil.getVaultUrlForEnvironment()).thenReturn(Environment.DEV_VAULT_URL); + + String testCredentialOfferUri = "https://00112233-xxx-yyyy-aaaaa-abcd2345678.apse1.issuance.affinidi.io/offers/cb3817e6-4b16-4f19-a658-8eb096541bcc"; + + String claimLink = vaultUtil.buildClaimLink(testCredentialOfferUri); + + String expectedValue = Environment.DEV_VAULT_URL + VaultUtil.VAULT_CLAIM_PATH + + "?"+VaultUtil.CLAIM_CREDENTIAL_URI_PARAM+"="+URLEncoder.encode(testCredentialOfferUri, StandardCharsets.UTF_8); + + assertEquals(expectedValue, claimLink); + } + + + @Test + void testBuildClaimLink_InProd(){ + + Mockito.when(environmentUtil.getVaultUrlForEnvironment()).thenReturn(Environment.PROD_VAULT_URL); + + String testCredentialOfferUri = "https://00112233-xxx-yyyy-aaaaa-abcd2345678.apse1.issuance.affinidi.io/offers/cb3817e6-4b16-4f19-a658-8eb096541bcc"; + + String claimLink = vaultUtil.buildClaimLink(testCredentialOfferUri); + + String expectedValue = Environment.PROD_VAULT_URL + VaultUtil.VAULT_CLAIM_PATH + + "?"+VaultUtil.CLAIM_CREDENTIAL_URI_PARAM+"="+URLEncoder.encode(testCredentialOfferUri, StandardCharsets.UTF_8); + + assertEquals(expectedValue, claimLink); + } + + +} From 4155b7adfecf6b524456aa566d2b53efe8f14e2b Mon Sep 17 00:00:00 2001 From: Priyanka Sharma Date: Thu, 9 Jan 2025 12:14:04 +0800 Subject: [PATCH 02/15] feat: segregating commons and auth-provider --- .../java/{ => auth.provider}/.env.example | 0 packages/java/auth.provider/pom.xml | 118 ++++++++++++++++++ .../helper}/ProjectScopedToken.java | 4 +- .../authProvider/helper/ProjectTokenUtil.java | 5 +- .../tdk/authProvider/helper}/SignPayload.java | 2 +- .../helper/ProjectTokenUtilTest.java | 18 +++ packages/java/common/.env.example | 2 + packages/java/{ => common}/pom.xml | 30 +++-- .../affinidi/tdk}/common/EnvironmentUtil.java | 2 +- .../com/affinidi/tdk}/common/VaultUtil.java | 2 +- .../tdk}/common/EnvironmentUtilTest.java | 2 +- .../affinidi/tdk}/common/VaultUtilTest.java | 2 +- 12 files changed, 169 insertions(+), 18 deletions(-) rename packages/java/{ => auth.provider}/.env.example (100%) create mode 100644 packages/java/auth.provider/pom.xml rename packages/java/{src/main/java/com/affinidi/authProvider/types => auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper}/ProjectScopedToken.java (90%) rename packages/java/{src/main/java/com/affinidi => auth.provider/src/main/java/com/affinidi/tdk}/authProvider/helper/ProjectTokenUtil.java (79%) rename packages/java/{src/main/java/com/affinidi/authProvider/types => auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper}/SignPayload.java (95%) create mode 100644 packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/ProjectTokenUtilTest.java create mode 100644 packages/java/common/.env.example rename packages/java/{ => common}/pom.xml (81%) rename packages/java/{src/main/java/com/affinidi => common/src/main/java/com/affinidi/tdk}/common/EnvironmentUtil.java (99%) rename packages/java/{src/main/java/com/affinidi => common/src/main/java/com/affinidi/tdk}/common/VaultUtil.java (98%) rename packages/java/{src/test/java/com/affinidi => common/src/test/java/com/affinidi/tdk}/common/EnvironmentUtilTest.java (99%) rename packages/java/{src/test/java/com/affinidi => common/src/test/java/com/affinidi/tdk}/common/VaultUtilTest.java (99%) diff --git a/packages/java/.env.example b/packages/java/auth.provider/.env.example similarity index 100% rename from packages/java/.env.example rename to packages/java/auth.provider/.env.example diff --git a/packages/java/auth.provider/pom.xml b/packages/java/auth.provider/pom.xml new file mode 100644 index 000000000..23dd5dbb3 --- /dev/null +++ b/packages/java/auth.provider/pom.xml @@ -0,0 +1,118 @@ + + + + 4.0.0 + jar + com.affinidi.tdk + auth.provider + 1.0 + auth.provider + https://github.com/affinidi/affinidi-tdk + + + Apache-2.0 + https://github.com/affinidi/affinidi-tdk/blob/main/LICENSE + repo + + + + + + Affinidi + ... + Affinidi + https://affinidi.com + + + + + UTF-8 + 1.8 + ${java.version} + ${java.version} + + + + + org.junit.jupiter + junit-jupiter-api + 5.11.0 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.11.0 + test + + + io.github.cdimascio + dotenv-java + 2.2.0 + + + org.glassfish.jersey.core + jersey-common + 3.1.7 + + + org.mockito + mockito-core + 3.12.4 + test + + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + + org.apache.maven.plugins + maven-surefire-plugin + + + maven-failsafe-plugin + 3.5.0 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + diff --git a/packages/java/src/main/java/com/affinidi/authProvider/types/ProjectScopedToken.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/ProjectScopedToken.java similarity index 90% rename from packages/java/src/main/java/com/affinidi/authProvider/types/ProjectScopedToken.java rename to packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/ProjectScopedToken.java index a67e7c4ff..1c083e51d 100644 --- a/packages/java/src/main/java/com/affinidi/authProvider/types/ProjectScopedToken.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/ProjectScopedToken.java @@ -1,4 +1,5 @@ -package com.affinidi.authProvider.types; +package com.affinidi.tdk.authProvider.helper; + public class ProjectScopedToken extends SignPayload{ private String apiGatewayUrl; @@ -15,5 +16,4 @@ public void setProjectId(String projectId) { this.projectId = projectId; } private String projectId; - } diff --git a/packages/java/src/main/java/com/affinidi/authProvider/helper/ProjectTokenUtil.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/ProjectTokenUtil.java similarity index 79% rename from packages/java/src/main/java/com/affinidi/authProvider/helper/ProjectTokenUtil.java rename to packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/ProjectTokenUtil.java index 4514ba3dd..e72faf0bd 100644 --- a/packages/java/src/main/java/com/affinidi/authProvider/helper/ProjectTokenUtil.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/ProjectTokenUtil.java @@ -1,7 +1,4 @@ -package com.affinidi.authProvider.helper; - -import com.affinidi.authProvider.types.ProjectScopedToken; -import com.affinidi.authProvider.types.SignPayload; +package com.affinidi.tdk.authProvider.helper; public class ProjectTokenUtil { diff --git a/packages/java/src/main/java/com/affinidi/authProvider/types/SignPayload.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/SignPayload.java similarity index 95% rename from packages/java/src/main/java/com/affinidi/authProvider/types/SignPayload.java rename to packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/SignPayload.java index 5e97dffcc..7997e2d2e 100644 --- a/packages/java/src/main/java/com/affinidi/authProvider/types/SignPayload.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/SignPayload.java @@ -1,4 +1,4 @@ -package com.affinidi.authProvider.types; +package com.affinidi.tdk.authProvider.helper; public class SignPayload { private String tokenId; diff --git a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/ProjectTokenUtilTest.java b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/ProjectTokenUtilTest.java new file mode 100644 index 000000000..176e66f5e --- /dev/null +++ b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/ProjectTokenUtilTest.java @@ -0,0 +1,18 @@ +package com.affinidi.tdk.authProvider.helper; + + +import org.junit.jupiter.api.BeforeEach; + +public class ProjectTokenUtilTest { + + + + @BeforeEach + void setUp() { + + } + + + + +} diff --git a/packages/java/common/.env.example b/packages/java/common/.env.example new file mode 100644 index 000000000..4c79f59c8 --- /dev/null +++ b/packages/java/common/.env.example @@ -0,0 +1,2 @@ +AFFINIDI_TDK_ENVIRONMENT= +NEXT_PUBLIC_AFFINIDI_TDK_ENVIRONMENT= diff --git a/packages/java/pom.xml b/packages/java/common/pom.xml similarity index 81% rename from packages/java/pom.xml rename to packages/java/common/pom.xml index baabee2fb..a93811cb9 100644 --- a/packages/java/pom.xml +++ b/packages/java/common/pom.xml @@ -3,18 +3,34 @@ 4.0.0 - - com.affinidi - java-tdk + jar + com.affinidi.tdk + common 1.0 - - java-tdk + common https://github.com/affinidi/affinidi-tdk + + + Apache-2.0 + https://github.com/affinidi/affinidi-tdk/blob/main/LICENSE + repo + + + + + + Affinidi + ... + Affinidi + https://affinidi.com + + UTF-8 - 1.8 - 1.8 + 1.8 + ${java.version} + ${java.version} diff --git a/packages/java/src/main/java/com/affinidi/common/EnvironmentUtil.java b/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java similarity index 99% rename from packages/java/src/main/java/com/affinidi/common/EnvironmentUtil.java rename to packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java index 1456b7718..444f72f64 100644 --- a/packages/java/src/main/java/com/affinidi/common/EnvironmentUtil.java +++ b/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java @@ -1,5 +1,5 @@ -package com.affinidi.common; +package com.affinidi.tdk.common; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; diff --git a/packages/java/src/main/java/com/affinidi/common/VaultUtil.java b/packages/java/common/src/main/java/com/affinidi/tdk/common/VaultUtil.java similarity index 98% rename from packages/java/src/main/java/com/affinidi/common/VaultUtil.java rename to packages/java/common/src/main/java/com/affinidi/tdk/common/VaultUtil.java index 697141138..8cb09d125 100644 --- a/packages/java/src/main/java/com/affinidi/common/VaultUtil.java +++ b/packages/java/common/src/main/java/com/affinidi/tdk/common/VaultUtil.java @@ -1,4 +1,4 @@ -package com.affinidi.common; +package com.affinidi.tdk.common; import java.net.URI; import java.util.logging.Logger; diff --git a/packages/java/src/test/java/com/affinidi/common/EnvironmentUtilTest.java b/packages/java/common/src/test/java/com/affinidi/tdk/common/EnvironmentUtilTest.java similarity index 99% rename from packages/java/src/test/java/com/affinidi/common/EnvironmentUtilTest.java rename to packages/java/common/src/test/java/com/affinidi/tdk/common/EnvironmentUtilTest.java index a67fe3a45..68ad08ff5 100644 --- a/packages/java/src/test/java/com/affinidi/common/EnvironmentUtilTest.java +++ b/packages/java/common/src/test/java/com/affinidi/tdk/common/EnvironmentUtilTest.java @@ -1,4 +1,4 @@ -package com.affinidi.common; +package com.affinidi.tdk.common; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/packages/java/src/test/java/com/affinidi/common/VaultUtilTest.java b/packages/java/common/src/test/java/com/affinidi/tdk/common/VaultUtilTest.java similarity index 99% rename from packages/java/src/test/java/com/affinidi/common/VaultUtilTest.java rename to packages/java/common/src/test/java/com/affinidi/tdk/common/VaultUtilTest.java index edbe0940e..2f0885a1a 100644 --- a/packages/java/src/test/java/com/affinidi/common/VaultUtilTest.java +++ b/packages/java/common/src/test/java/com/affinidi/tdk/common/VaultUtilTest.java @@ -1,4 +1,4 @@ -package com.affinidi.common; +package com.affinidi.tdk.common; import static org.junit.jupiter.api.Assertions.assertEquals; From cb391e416adb6e47871186c7406207c010b61b63 Mon Sep 17 00:00:00 2001 From: Priyanka Sharma Date: Fri, 10 Jan 2025 18:36:53 +0800 Subject: [PATCH 03/15] feat: Adding AuthProvider --- packages/java/auth.provider/pom.xml | 10 ++ .../tdk/authProvider/AuthProvider.java | 134 ++++++++++++++++++ .../tdk/authProvider/helper/JwtUtil.java | 21 +++ .../helper/ProjectScopedToken.java | 19 --- .../authProvider/helper/ProjectTokenUtil.java | 19 --- .../tdk/authProvider/helper/SignPayload.java | 42 ------ .../AuthProvider$Configurations.class | Bin 0 -> 1892 bytes .../tdk/authProvider/AuthProvider.class | Bin 0 -> 3043 bytes 8 files changed, 165 insertions(+), 80 deletions(-) create mode 100644 packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java create mode 100644 packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java delete mode 100644 packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/ProjectScopedToken.java delete mode 100644 packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/ProjectTokenUtil.java delete mode 100644 packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/SignPayload.java create mode 100644 packages/java/auth.provider/target/classes/com/affinidi/tdk/authProvider/AuthProvider$Configurations.class create mode 100644 packages/java/auth.provider/target/classes/com/affinidi/tdk/authProvider/AuthProvider.class diff --git a/packages/java/auth.provider/pom.xml b/packages/java/auth.provider/pom.xml index 23dd5dbb3..df13241ee 100644 --- a/packages/java/auth.provider/pom.xml +++ b/packages/java/auth.provider/pom.xml @@ -62,6 +62,16 @@ 3.12.4 test + + com.affinidi.tdk + common + 1.0 + + + io.jsonwebtoken + jjwt-api + 0.12.6 + diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java new file mode 100644 index 000000000..93e3ddb13 --- /dev/null +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java @@ -0,0 +1,134 @@ +package com.affinidi.tdk.authProvider; + +import com.affinidi.tdk.common.EnvironmentUtil; + +public class AuthProvider { + + private final String projectId; + private final String tokenId; + private final String privateKey; + private final String keyId; + private final String passphrase; + private final String apiGatewayUrl; + private final String tokenEndPoint; + private String publicKey; + private String projectScopeToken; + + AuthProvider(Configurations configurations){ + this.projectId = configurations.projectId; + this.tokenId = configurations.tokenId; + this.privateKey = configurations.privateKey; + this.keyId = configurations.keyId; + this.passphrase = configurations.passphrase; + EnvironmentUtil eUtil = new EnvironmentUtil(); + this.apiGatewayUrl = eUtil.getApiGatewayUrlForEnvironment(); + this.tokenEndPoint = eUtil.getElementAuthTokenUrlForEnvironment(); + } + + public boolean shouldRefreshToken(){ + // TODO Implement shouldRefreshToken + return false; + } + + public String fetchProjectScopedToken(){ + boolean tokenFetchRequired = shouldRefreshToken(); + if(tokenFetchRequired){ + this.projectScopeToken = fetchProjectScopedToken(apiGatewayUrl, projectId, tokenId, apiGatewayUrl, privateKey, passphrase, keyId); + } + return this.projectScopeToken; + } + + public String getUserAccessToken(String audience){ + // TODO Implement getUserAccessToken + return null; + } + + private String fetchProjectScopedToken(String apiGatewayUrl, String projectId, String tokenId, + String audience, String privateKey, String passphrase, String keyId){ + + // TODO Implement fetchProjectScopedToken + return null; + } + + public static class Configurations{ + private String projectId; + private String tokenId; + private String privateKey; + private String keyId; + private String passphrase; + + public Configurations projectId(String projectId){ + this.projectId = projectId; + return this; + } + public Configurations tokenId(String tokenId){ + this.tokenId = tokenId; + return this; + } + public Configurations privateKey(String privateKey){ + this.privateKey = privateKey; + return this; + } + public Configurations keyId(String keyId){ + this.keyId = keyId; + return this; + } + public Configurations passphrase(String passphrase){ + this.passphrase = passphrase; + return this; + } + + public AuthProvider build() throws Exception{ + if(this.projectId == null || this.privateKey == null || this.tokenId == null){ + throw new Exception("Cannot create Auth provider without projectId, privateKey and toeknId"); + } + return new AuthProvider(this); + } + + } + + public String getProjectId() { + return projectId; + } + + public String getTokenId() { + return tokenId; + } + + public String getPrivateKey() { + return privateKey; + } + + public String getKeyId() { + return keyId; + } + + public String getPassphrase() { + return passphrase; + } + + + public String getApiGatewayUrl() { + return apiGatewayUrl; + } + + public String getTokenEndPoint() { + return tokenEndPoint; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getProjectScopeToken() { + return projectScopeToken; + } + + public void setProjectScopeToken(String projectScopeToken) { + this.projectScopeToken = projectScopeToken; + } +} diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java new file mode 100644 index 000000000..b9a0dcb92 --- /dev/null +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java @@ -0,0 +1,21 @@ +package com.affinidi.tdk.authProvider.helper; + + + +public class JwtUtil { + + public String signPayload(String tokenId, String audience, String privateKey, String passphrase, String keyId){ + + return null; + + } + + public static String fetchPublicKey(String apiGatewayUrl){ + return null; + } + + public static boolean validateToken(String token, String publicKey){ + return false; + } + +} diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/ProjectScopedToken.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/ProjectScopedToken.java deleted file mode 100644 index 1c083e51d..000000000 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/ProjectScopedToken.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.affinidi.tdk.authProvider.helper; - - -public class ProjectScopedToken extends SignPayload{ - private String apiGatewayUrl; - public String getApiGatewayUrl() { - return apiGatewayUrl; - } - public void setApiGatewayUrl(String apiGatewayUrl) { - this.apiGatewayUrl = apiGatewayUrl; - } - public String getProjectId() { - return projectId; - } - public void setProjectId(String projectId) { - this.projectId = projectId; - } - private String projectId; -} diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/ProjectTokenUtil.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/ProjectTokenUtil.java deleted file mode 100644 index e72faf0bd..000000000 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/ProjectTokenUtil.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.affinidi.tdk.authProvider.helper; - -public class ProjectTokenUtil { - - public SignPayload signPayload(String tokenId, String audience, String privateKey, String passphrase, String keyId){ - return null; - - } - - public SignPayload getUserAccessToken(String tokenId, String audience, String privateKey, String passphrase, String keyId){ - return null; - } - - public ProjectScopedToken fetchProjectScopedToken(String apiGatewayUrl, String projectId, String tokenId, - String audience, String privateKey, String passphrase, String keyId){ - return null; - } - -} diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/SignPayload.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/SignPayload.java deleted file mode 100644 index 7997e2d2e..000000000 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/SignPayload.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.affinidi.tdk.authProvider.helper; - -public class SignPayload { - private String tokenId; - private String audience; - private String privateKey; - private String passphrase; - private String keyId; - - public String getTokenId() { - return tokenId; - } - public void setTokenId(String tokenId) { - this.tokenId = tokenId; - } - public String getAudience() { - return audience; - } - public void setAudience(String audience) { - this.audience = audience; - } - public String getPrivateKey() { - return privateKey; - } - public void setPrivateKey(String privateKey) { - this.privateKey = privateKey; - } - public String getPassphrase() { - return passphrase; - } - public void setPassphrase(String passphrase) { - this.passphrase = passphrase; - } - public String getKeyId() { - return keyId; - } - public void setKeyId(String keyId) { - this.keyId = keyId; - } - - -} diff --git a/packages/java/auth.provider/target/classes/com/affinidi/tdk/authProvider/AuthProvider$Configurations.class b/packages/java/auth.provider/target/classes/com/affinidi/tdk/authProvider/AuthProvider$Configurations.class new file mode 100644 index 0000000000000000000000000000000000000000..582b949c83bcec063b46649152f30552267bdffc GIT binary patch literal 1892 zcmbtTYflqF6g|_HKGx-JK}575Xj_o-6cEK=0|{Edh?4Mex*h1!vfFI8DE~?m4T&WB z4HJKq@y@i^+Aayom$`Qy=iHfl&iwrK^&5aWEQR1>STgLR6yM)BEi-4P+*~2WD{lU^ zV;`G2;iOg@CZlQF+BXj>4tGu4Du)nY=sM)bJXPe@L25gDC=8b&QgUpWZR8kwGWGo( z*DBE=*idyFYa>3l=IqRz|EUwhULtEzn1R39dCV2;u?$* zF!bqYLr`Y7bcCweZ5`oic1K5~n%&i*AxKTiR?H%GZZiHamFe=@+6P0FYQo)T=&cL$ zXD5s?hP5=eEZdD4jv!@X(f~11RM{c+-gNVJ#gz+Hi6jRnQjrWrQ6~g_B^ujlmZHcgwk1!|@kt%)qA{oz=h02MJSq$H zpqC<95;tj-;OA7#M=_Kf|AJ`pBc(n_xTcn$(a{w}(N{fIqac8~tE?RIChKn@TP4Vg zOUUGdC0W>;Y@mf~jmXw7A(LH`WD#$&!4|R&B71oWnQXlz)4a)sTF5quZ0iy-sh=df zhZyy9nED)`*@%)NM=p>u#gm^AJx4c%fpcgS`hTGP3?q9zqo;WA{S1jcl6qWfE#uH$BKr$L6B)rNPjbA xp#d`E2^naHEH*%9Jt2e5kfjF5oF`-;LQS8ixB2LG3lufgfCLs1`3*etWWWFb literal 0 HcmV?d00001 diff --git a/packages/java/auth.provider/target/classes/com/affinidi/tdk/authProvider/AuthProvider.class b/packages/java/auth.provider/target/classes/com/affinidi/tdk/authProvider/AuthProvider.class new file mode 100644 index 0000000000000000000000000000000000000000..f6d52cdbc8d2dbeea21c9c808f481b885bd506de GIT binary patch literal 3043 zcmcgtTT>iG6#jaFU09pja!W!)l5ia$8wlni1Vx01fe^WA5*3Hp-etlvv(C<UPyZaA+2bg&ofgY2Xp?A0yFks*@MaR7IF~?|!0mr+UE(}02-KUFrHaMAYXU3(Db}+k zZ-Br>i8{nN-X4k9!Vbq-eMuq_sIN%$h1CM@C{Vf7@d zuSp~kPhdh|@JyNLV9#~Z^Ul8Qxz3(){0-mECozsTe!MPm12+YFa>}0#b78^tLKuPe zq2cJvZ%e$w!Hr`|Vw!FBk*)c>VqZ*XO%-C-X^R~AjD$p7pn1u5)cw-lw({1^?L6@^ zwd7i6e$(`9-d7H5{T;g~F!~ZqqjqSrPLWMu>Lu{iOrm8A0Alemj_d9^IDi2DNH4V}G%|MTPp6nG%f z#P^ue(_?`%T2-HwY){b;5|m}*nr~XW%Vy!|dWj($Mdi)vo~+E0fr#WH9C9m{GPZIo z;)x_fmE!5yMq6!-H;r0%G63soPlN1O6&iyrq&mfZn!HD*PKKd&b~F!}zn@Apa^A8? zQ5-1*I-(Mx5!bE*I=Sge-tCMI5ukFMTysmFr50@Nht}|y;VKV(^1A3a%A3nm9*Wwc z>GV*=B?_>HK0OUc@o1t|if53PG;Fk_siGx~4=t&YwWO{j8RP70 zG8Ds`^fYLefgIFQxDst>r{6_%(9rC}D7xrNdXA>ddAx%Qc#Gcb4T8Wd-liRegE`U( zdasq%3uIMr7gd5DB`N5wSj~_8)E?vdq(@tmfwp2zP}kk06HfPQDrAi0gz*gTO?Q8f zLBlW(DT71G`j9dX*8L>Y^rxaMm9avrm7fSzqdlc4E8sF+#}%^HhcOIjubqMAiq|?i ze;=#lca7er=>EhQN4<`XN-Q@>ax5{yw!>MBgzUwRYZAjI+o3mUid1ZPBOlTXc zfi`4=$)Rhf>7@N@LNN&f51d_$zhv|Vub5T=ftY9sLVs{v&ou4WS{;?ByE=`>I)vad z>W(kt!$@#%HFzr${4f&SUk$b*!H*)rMm0DW34R<2PFI6>Bf%d!i<<`t(a;%2(P#&;#*fzQy(MVUAdl sf#|jhQ~#UDKr+#RtW*c`Ed}!Q1%dGNjW&{yS*+8#zH@G5`Po literal 0 HcmV?d00001 From 096eea747a9938737ab7dbac51027e008c6f92c5 Mon Sep 17 00:00:00 2001 From: Priyanka Sharma Date: Fri, 10 Jan 2025 19:29:52 +0800 Subject: [PATCH 04/15] feat: merge main --- packages/java/auth.provider/.env.example | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/java/auth.provider/.env.example b/packages/java/auth.provider/.env.example index 4c79f59c8..98534ba29 100644 --- a/packages/java/auth.provider/.env.example +++ b/packages/java/auth.provider/.env.example @@ -1,2 +1,9 @@ AFFINIDI_TDK_ENVIRONMENT= NEXT_PUBLIC_AFFINIDI_TDK_ENVIRONMENT= + +PROJECT_ID= +KEY_ID= +TOKEN_ID= +PASSPHRASE= +PRIVATE_KEY= + From 800593e8222cfa27e22ed690121f57fbbc359330 Mon Sep 17 00:00:00 2001 From: Priyanka Sharma Date: Mon, 13 Jan 2025 15:33:08 +0800 Subject: [PATCH 05/15] feat: Added Jwt utility functions --- packages/java/auth.provider/pom.xml | 29 ++-- .../tdk/authProvider/AuthProvider.java | 26 +++- .../tdk/authProvider/helper/JwtUtil.java | 129 +++++++++++++++++- .../tdk/authProvider/type/IotaJwtOutput.java | 20 +++ .../tdk/authProvider/AuthProviderTest.java | 27 ++++ .../tdk/authProvider/helper/JwtUtilTest.java | 39 ++++++ .../helper/ProjectTokenUtilTest.java | 18 --- .../AuthProvider$Configurations.class | Bin 1892 -> 0 bytes .../tdk/authProvider/AuthProvider.class | Bin 3043 -> 0 bytes 9 files changed, 251 insertions(+), 37 deletions(-) create mode 100644 packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/type/IotaJwtOutput.java create mode 100644 packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/AuthProviderTest.java create mode 100644 packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/JwtUtilTest.java delete mode 100644 packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/ProjectTokenUtilTest.java delete mode 100644 packages/java/auth.provider/target/classes/com/affinidi/tdk/authProvider/AuthProvider$Configurations.class delete mode 100644 packages/java/auth.provider/target/classes/com/affinidi/tdk/authProvider/AuthProvider.class diff --git a/packages/java/auth.provider/pom.xml b/packages/java/auth.provider/pom.xml index df13241ee..0185b73f6 100644 --- a/packages/java/auth.provider/pom.xml +++ b/packages/java/auth.provider/pom.xml @@ -31,48 +31,59 @@ 1.8 ${java.version} ${java.version} + 5.4.1 + 5.11.0 + 2.2.0 + 3.1.7 + 1.0 + 0.12.6 + 3.12.4 org.junit.jupiter - junit-jupiter-api - 5.11.0 + junit-jupiter-api + ${junit-version} test org.junit.jupiter junit-jupiter-engine - 5.11.0 + ${junit-version} test io.github.cdimascio dotenv-java - 2.2.0 + ${cdimascio-version} org.glassfish.jersey.core jersey-common - 3.1.7 + ${jersey-version} org.mockito mockito-core - 3.12.4 + ${mockito-version} test com.affinidi.tdk common - 1.0 + ${affinidi-common-version} io.jsonwebtoken jjwt-api - 0.12.6 + ${jsonwebtoken-version} + + + org.apache.httpcomponents.client5 + httpclient5 + ${apache-http-version} - diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java index 93e3ddb13..aba3326b3 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java @@ -1,7 +1,12 @@ package com.affinidi.tdk.authProvider; +import java.util.UUID; + +import com.affinidi.tdk.authProvider.helper.JwtUtil; +import com.affinidi.tdk.authProvider.type.IotaJwtOutput; import com.affinidi.tdk.common.EnvironmentUtil; + public class AuthProvider { private final String projectId; @@ -25,13 +30,16 @@ public class AuthProvider { this.tokenEndPoint = eUtil.getElementAuthTokenUrlForEnvironment(); } - public boolean shouldRefreshToken(){ - // TODO Implement shouldRefreshToken - return false; + public boolean shouldRefreshToken() { + if(this.projectScopeToken == null){ + return true; + } + return !(JwtUtil.validProjectTokenPresent(this.projectScopeToken, this.apiGatewayUrl)); } public String fetchProjectScopedToken(){ boolean tokenFetchRequired = shouldRefreshToken(); + if(tokenFetchRequired){ this.projectScopeToken = fetchProjectScopedToken(apiGatewayUrl, projectId, tokenId, apiGatewayUrl, privateKey, passphrase, keyId); } @@ -50,6 +58,16 @@ private String fetchProjectScopedToken(String apiGatewayUrl, String projectId, S return null; } + public IotaJwtOutput signIotaJwt(String iotaConfigId, String did, String iotaSessionId) throws Exception { + + String iotaTokenId = "token/"+tokenId; + String iotaSessionID = (iotaSessionId != null) ? iotaSessionId : UUID.randomUUID().toString(); + + String iotaJwt = JwtUtil.signIotaPayload(iotaTokenId, did, privateKey, passphrase, keyId, projectId, iotaConfigId, iotaSessionID); + + return new IotaJwtOutput(iotaSessionID, iotaJwt); + } + public static class Configurations{ private String projectId; private String tokenId; @@ -84,7 +102,6 @@ public AuthProvider build() throws Exception{ } return new AuthProvider(this); } - } public String getProjectId() { @@ -107,7 +124,6 @@ public String getPassphrase() { return passphrase; } - public String getApiGatewayUrl() { return apiGatewayUrl; } diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java index b9a0dcb92..1a7fc8319 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java @@ -1,21 +1,140 @@ package com.affinidi.tdk.authProvider.helper; +import java.security.AlgorithmParameters; +import java.security.Key; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.logging.Logger; +import javax.crypto.Cipher; +import javax.crypto.EncryptedPrivateKeyInfo; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.MalformedJwtException; public class JwtUtil { + private static Logger logger = Logger.getLogger(JwtUtil.class.getName()); - public String signPayload(String tokenId, String audience, String privateKey, String passphrase, String keyId){ - - return null; + public static String signPayload(String tokenId, String audience, String privateKeyString, String passphrase, String keyId) throws Exception{ + + long issueTimeInSeconds = System.currentTimeMillis() / 1000; + + Map claims = new HashMap<>(); + claims.put("iss", tokenId); + claims.put("sub", tokenId); + claims.put("aud", audience); + claims.put("jti", UUID.randomUUID().toString()); + claims.put("exp", issueTimeInSeconds + (5 * 60)); + claims.put("iat", issueTimeInSeconds); + + return generateJwt(derivePrivateKey(privateKeyString, passphrase), claims); + } + public static String signIotaPayload(String tokenId, String audience, String privateKeyString, String passphrase, + String keyId, String projectId, String iotaConfigId, String iotaSessionId) throws Exception{ + long issueTimeInSeconds = System.currentTimeMillis() / 1000; + + Map claims = new HashMap<>(); + claims.put("iss", tokenId); + claims.put("sub", tokenId); + claims.put("kid", tokenId); + claims.put("aud", audience); + claims.put("jti", UUID.randomUUID().toString()); + claims.put("exp", issueTimeInSeconds + (5 * 60)); + claims.put("iat", issueTimeInSeconds); + claims.put("project_id", projectId); + claims.put("iota_configuration_id", iotaConfigId); + claims.put("iota_session_id", iotaSessionId); + claims.put("scope", "iota_channel"); + + return generateJwt(derivePrivateKey(privateKeyString, passphrase), claims); } - public static String fetchPublicKey(String apiGatewayUrl){ + public static PublicKey fetchPublicKey(String apiGatewayUrl){ + // TODO Implement fetchPublicKey to call the apiGateway + // and extract public key from the received jwk + return null; } - public static boolean validateToken(String token, String publicKey){ + public static boolean validProjectTokenPresent(String token, String apiGatewayUrl){ + PublicKey publicKey = fetchPublicKey(apiGatewayUrl); + try{ + Jwts.parser().verifyWith(publicKey).build().parse(token); + }catch(ExpiredJwtException ejException){ + return true; + }catch(MalformedJwtException mjException){ + return true; + }catch(Exception eException){ + return true; + } return false; } + private static PrivateKey derivePrivateKey(String privateKeyString, String passphrase) throws Exception{ + + PrivateKey privateKey = null; + try{ + if (!passphrase.isEmpty()) { + privateKey = getPrivateKeyFromString(privateKeyString); + } else { + privateKey = getEncryptedPrivateKeyFromString(privateKeyString, passphrase); + } + }catch(Exception exception){ + logger.severe("extractPrivateKey : Issue in retrieving private key from the string passed"); + throw new Exception("Could not derive private key out of the configurations. Exception : "+exception.toString()); + } + + return privateKey; + } + + private static String generateJwt(PrivateKey privateKey, Map claims){ + return Jwts.builder() + .claims(claims) + .signWith(privateKey, Jwts.SIG.RS256) + .compact(); + } + + private static PrivateKey getPrivateKeyFromString(String privateKeyPEM) throws Exception { + String privateKeyContent = privateKeyPEM + .replace("-----BEGIN PRIVATE KEY-----", "") + .replace("-----END PRIVATE KEY-----", "") + .replaceAll("\\s", ""); + + byte[] encoded = Base64.getDecoder().decode(privateKeyContent); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded); + return keyFactory.generatePrivate(keySpec); + } + + private static PrivateKey getEncryptedPrivateKeyFromString(String encryptedPrivateKeyPEM, String password) + throws Exception { + String privateKeyContent = encryptedPrivateKeyPEM + .replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "") + .replace("-----END ENCRYPTED PRIVATE KEY-----", ""); + + byte[] encodedKey = Base64.getDecoder().decode(privateKeyContent); + EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(encodedKey); + Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName()); + PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray()); + SecretKeyFactory secFac = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName()); + Key pbeKey = secFac.generateSecret(pbeKeySpec); + AlgorithmParameters algParams = encryptedPrivateKeyInfo.getAlgParameters(); + cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams); + PKCS8EncodedKeySpec pkcs8KeySpec = encryptedPrivateKeyInfo.getKeySpec(cipher); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + + return keyFactory.generatePrivate(pkcs8KeySpec); + } + + + } diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/type/IotaJwtOutput.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/type/IotaJwtOutput.java new file mode 100644 index 000000000..da31b2092 --- /dev/null +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/type/IotaJwtOutput.java @@ -0,0 +1,20 @@ +package com.affinidi.tdk.authProvider.type; + +public class IotaJwtOutput { + private final String iotaSessionId; + private final String iotaJwt; + + public IotaJwtOutput(String iotaSessionId, String iotaJwt){ + this.iotaJwt = iotaJwt; + this.iotaSessionId = iotaSessionId; + } + + public String getIotaSessionId() { + return iotaSessionId; + } + public String getIotaJwt() { + return iotaJwt; + } + + +} diff --git a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/AuthProviderTest.java b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/AuthProviderTest.java new file mode 100644 index 000000000..b0d721ead --- /dev/null +++ b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/AuthProviderTest.java @@ -0,0 +1,27 @@ +package com.affinidi.tdk.authProvider; + +import org.junit.jupiter.api.Test; + +public class AuthProviderTest { + + @Test + void testFetchProjectScopedToken() { + + } + + @Test + void testGetUserAccessToken() { + + } + + @Test + void testShouldRefreshToken() { + + } + + @Test + void testSignIotaJwt() { + + } + +} diff --git a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/JwtUtilTest.java b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/JwtUtilTest.java new file mode 100644 index 000000000..736c10d2d --- /dev/null +++ b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/JwtUtilTest.java @@ -0,0 +1,39 @@ +package com.affinidi.tdk.authProvider.helper; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class JwtUtilTest { + + + + @BeforeEach + void setUp() { + + } + + @Test + void testFetchPublicKey() { + + } + + @Test + void testSignIotaPayload() { + + } + + @Test + void testSignPayload() { + + } + + @Test + void testValidProjectTokenPresent() { + + } + + + + +} diff --git a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/ProjectTokenUtilTest.java b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/ProjectTokenUtilTest.java deleted file mode 100644 index 176e66f5e..000000000 --- a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/ProjectTokenUtilTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.affinidi.tdk.authProvider.helper; - - -import org.junit.jupiter.api.BeforeEach; - -public class ProjectTokenUtilTest { - - - - @BeforeEach - void setUp() { - - } - - - - -} diff --git a/packages/java/auth.provider/target/classes/com/affinidi/tdk/authProvider/AuthProvider$Configurations.class b/packages/java/auth.provider/target/classes/com/affinidi/tdk/authProvider/AuthProvider$Configurations.class deleted file mode 100644 index 582b949c83bcec063b46649152f30552267bdffc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1892 zcmbtTYflqF6g|_HKGx-JK}575Xj_o-6cEK=0|{Edh?4Mex*h1!vfFI8DE~?m4T&WB z4HJKq@y@i^+Aayom$`Qy=iHfl&iwrK^&5aWEQR1>STgLR6yM)BEi-4P+*~2WD{lU^ zV;`G2;iOg@CZlQF+BXj>4tGu4Du)nY=sM)bJXPe@L25gDC=8b&QgUpWZR8kwGWGo( z*DBE=*idyFYa>3l=IqRz|EUwhULtEzn1R39dCV2;u?$* zF!bqYLr`Y7bcCweZ5`oic1K5~n%&i*AxKTiR?H%GZZiHamFe=@+6P0FYQo)T=&cL$ zXD5s?hP5=eEZdD4jv!@X(f~11RM{c+-gNVJ#gz+Hi6jRnQjrWrQ6~g_B^ujlmZHcgwk1!|@kt%)qA{oz=h02MJSq$H zpqC<95;tj-;OA7#M=_Kf|AJ`pBc(n_xTcn$(a{w}(N{fIqac8~tE?RIChKn@TP4Vg zOUUGdC0W>;Y@mf~jmXw7A(LH`WD#$&!4|R&B71oWnQXlz)4a)sTF5quZ0iy-sh=df zhZyy9nED)`*@%)NM=p>u#gm^AJx4c%fpcgS`hTGP3?q9zqo;WA{S1jcl6qWfE#uH$BKr$L6B)rNPjbA xp#d`E2^naHEH*%9Jt2e5kfjF5oF`-;LQS8ixB2LG3lufgfCLs1`3*etWWWFb diff --git a/packages/java/auth.provider/target/classes/com/affinidi/tdk/authProvider/AuthProvider.class b/packages/java/auth.provider/target/classes/com/affinidi/tdk/authProvider/AuthProvider.class deleted file mode 100644 index f6d52cdbc8d2dbeea21c9c808f481b885bd506de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3043 zcmcgtTT>iG6#jaFU09pja!W!)l5ia$8wlni1Vx01fe^WA5*3Hp-etlvv(C<UPyZaA+2bg&ofgY2Xp?A0yFks*@MaR7IF~?|!0mr+UE(}02-KUFrHaMAYXU3(Db}+k zZ-Br>i8{nN-X4k9!Vbq-eMuq_sIN%$h1CM@C{Vf7@d zuSp~kPhdh|@JyNLV9#~Z^Ul8Qxz3(){0-mECozsTe!MPm12+YFa>}0#b78^tLKuPe zq2cJvZ%e$w!Hr`|Vw!FBk*)c>VqZ*XO%-C-X^R~AjD$p7pn1u5)cw-lw({1^?L6@^ zwd7i6e$(`9-d7H5{T;g~F!~ZqqjqSrPLWMu>Lu{iOrm8A0Alemj_d9^IDi2DNH4V}G%|MTPp6nG%f z#P^ue(_?`%T2-HwY){b;5|m}*nr~XW%Vy!|dWj($Mdi)vo~+E0fr#WH9C9m{GPZIo z;)x_fmE!5yMq6!-H;r0%G63soPlN1O6&iyrq&mfZn!HD*PKKd&b~F!}zn@Apa^A8? zQ5-1*I-(Mx5!bE*I=Sge-tCMI5ukFMTysmFr50@Nht}|y;VKV(^1A3a%A3nm9*Wwc z>GV*=B?_>HK0OUc@o1t|if53PG;Fk_siGx~4=t&YwWO{j8RP70 zG8Ds`^fYLefgIFQxDst>r{6_%(9rC}D7xrNdXA>ddAx%Qc#Gcb4T8Wd-liRegE`U( zdasq%3uIMr7gd5DB`N5wSj~_8)E?vdq(@tmfwp2zP}kk06HfPQDrAi0gz*gTO?Q8f zLBlW(DT71G`j9dX*8L>Y^rxaMm9avrm7fSzqdlc4E8sF+#}%^HhcOIjubqMAiq|?i ze;=#lca7er=>EhQN4<`XN-Q@>ax5{yw!>MBgzUwRYZAjI+o3mUid1ZPBOlTXc zfi`4=$)Rhf>7@N@LNN&f51d_$zhv|Vub5T=ftY9sLVs{v&ou4WS{;?ByE=`>I)vad z>W(kt!$@#%HFzr${4f&SUk$b*!H*)rMm0DW34R<2PFI6>Bf%d!i<<`t(a;%2(P#&;#*fzQy(MVUAdl sf#|jhQ~#UDKr+#RtW*c`Ed}!Q1%dGNjW&{yS*+8#zH@G5`Po From 1cfa195af97bb0c6e891d6a510fa1157b6d3e786 Mon Sep 17 00:00:00 2001 From: Priyanka Sharma Date: Wed, 15 Jan 2025 16:53:03 +0800 Subject: [PATCH 06/15] feat: Implementation of AuthProvider and JwtUtil functions --- packages/java/auth.provider/.env.example | 3 +- packages/java/auth.provider/README.md | 108 +++++++ packages/java/auth.provider/pom.xml | 18 ++ .../tdk/authProvider/AuthProvider.java | 219 +++++++++++-- .../exception/InvalidPrivateKeyException.java | 9 + .../exception/InvalidPublicKeyException.java | 8 + .../exception/JwtGenerationException.java | 9 + .../helper/AuthProviderConstants.java | 15 + .../tdk/authProvider/helper/JwtUtil.java | 299 +++++++++++++++--- .../{type => types}/IotaJwtOutput.java | 10 +- .../tdk/authProvider/helper/JwtUtilTest.java | 3 - .../affinidi/tdk/common/EnvironmentUtil.java | 7 + 12 files changed, 638 insertions(+), 70 deletions(-) create mode 100644 packages/java/auth.provider/README.md create mode 100644 packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/InvalidPrivateKeyException.java create mode 100644 packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/InvalidPublicKeyException.java create mode 100644 packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/JwtGenerationException.java create mode 100644 packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/AuthProviderConstants.java rename packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/{type => types}/IotaJwtOutput.java (69%) diff --git a/packages/java/auth.provider/.env.example b/packages/java/auth.provider/.env.example index 98534ba29..69103468e 100644 --- a/packages/java/auth.provider/.env.example +++ b/packages/java/auth.provider/.env.example @@ -1,6 +1,7 @@ +# Mandatory value required to derive the environment AFFINIDI_TDK_ENVIRONMENT= -NEXT_PUBLIC_AFFINIDI_TDK_ENVIRONMENT= +# Optional values needed to generate auth token. These values can also be directly passsed to the AuthProvider build fuunction PROJECT_ID= KEY_ID= TOKEN_ID= diff --git a/packages/java/auth.provider/README.md b/packages/java/auth.provider/README.md new file mode 100644 index 000000000..1b5e8cb33 --- /dev/null +++ b/packages/java/auth.provider/README.md @@ -0,0 +1,108 @@ +# auth.provider + +AuthProvider package + +- API version: 1.0.0 + +For more information, please visit [https://github.com/affinidi/affinidi-tdk](https://github.com/affinidi/affinidi-tdk) + +## Requirements + +Building the package requires: + +1. Java 1.8+ +2. Maven (3.8.3+) + +## Installation + +To install the API client library to your local Maven repository, simply execute: + +```shell +mvn clean install +``` + +To deploy it to a remote Maven repository instead, configure the settings of the repository and execute: + +```shell +mvn clean deploy +``` + +### Maven users + +Add this dependency to your project's POM: + +```xml + + com.affinidi.tdk + auth.provider + 1.0.0 + +``` + +### Others + +At first generate the JAR by executing: + +```shell +mvn clean package +``` + +## Getting Started + +Configure .env file at your project base with the following values (Refer .env.example) + +AFFINIDI_TDK_ENVIRONMENT= (local,dev or prod) + +PROJECT_ID= +KEY_ID= +TOKEN_ID= +PASSPHRASE= +PRIVATE_KEY= + +```java + +// Import classes: +import com.affinidi.tdk.authProvider.helper.JwtUtil; +import com.affinidi.tdk.authProvider.helper.AuthProvider; + +public class AuthProviderConsumer { + public static void main(String arg[]) { + try{ + // Create an authprovider from the values configured in the environment file + AuthProvider authProviderFromEnvFile = new AuthProvider.Configurations().buildWithEnv(); + String projectToken = authProviderFromEnvFile.fetchProjectScopedToken(); + System.out.println(projectToken); + + boolean isExistingProjectScopeTokenValid = JwtUtil.validProjectTokenPresent(projectToken, authProviderFromEnvFile.apiGatewayUrl); + System.out.println(isExistingProjectScopeTokenValid); + + + // Alternatively you can create an auth provider by explicitly passing the configurations + AuthProvider authProviderWithPassedValues = new AuthProvider.Configurations() + .keyId("") + .projectId("") + .passphrase("") + .projectId("") + .tokenId("") + .build(); + String projectToken = authProvider.fetchProjectScopedToken(); + System.out.println(projectToken); + + + }catch(Exception e){ + e.printStackTrace(); + } + } +} + +``` + +## Documentation for API Endpoints + + +## Recommendation + + +## Author + +info@affinidi.com diff --git a/packages/java/auth.provider/pom.xml b/packages/java/auth.provider/pom.xml index 0185b73f6..06375c2f2 100644 --- a/packages/java/auth.provider/pom.xml +++ b/packages/java/auth.provider/pom.xml @@ -38,6 +38,8 @@ 1.0 0.12.6 3.12.4 + 2.10.1 + 2.0.16 @@ -79,11 +81,27 @@ jjwt-api ${jsonwebtoken-version} + + io.jsonwebtoken + jjwt-impl + ${jsonwebtoken-version} + + + io.jsonwebtoken + jjwt-jackson + ${jsonwebtoken-version} + org.apache.httpcomponents.client5 httpclient5 ${apache-http-version} + + com.google.code.gson + gson + ${gson-version} + + diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java index aba3326b3..e3a91dee5 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java @@ -1,14 +1,35 @@ package com.affinidi.tdk.authProvider; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; +import org.apache.hc.client5.http.classic.methods.HttpPost; +import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.NameValuePair; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.hc.core5.http.message.BasicNameValuePair; + +import com.affinidi.tdk.authProvider.helper.AuthProviderConstants; import com.affinidi.tdk.authProvider.helper.JwtUtil; -import com.affinidi.tdk.authProvider.type.IotaJwtOutput; +import com.affinidi.tdk.authProvider.types.IotaJwtOutput; import com.affinidi.tdk.common.EnvironmentUtil; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; - +/** +* This class provides +* +* +* @author Priyanka +* +*/ public class AuthProvider { - + private final String projectId; private final String tokenId; private final String privateKey; @@ -30,6 +51,17 @@ public class AuthProvider { this.tokenEndPoint = eUtil.getElementAuthTokenUrlForEnvironment(); } + + /** + * This method identifies if the current AuthProvider has a valid existing + * projectScopeToken or not. This helps to reuse the valid tokens without always + * generating a new. + * + * The validation involves verifying token's signature against the + * public (verification) key. + * + * @return boolean + */ public boolean shouldRefreshToken() { if(this.projectScopeToken == null){ return true; @@ -37,27 +69,119 @@ public boolean shouldRefreshToken() { return !(JwtUtil.validProjectTokenPresent(this.projectScopeToken, this.apiGatewayUrl)); } - public String fetchProjectScopedToken(){ + + /** + * @return String + * @throws Exception + */ + public String fetchProjectScopedToken() throws Exception{ boolean tokenFetchRequired = shouldRefreshToken(); if(tokenFetchRequired){ - this.projectScopeToken = fetchProjectScopedToken(apiGatewayUrl, projectId, tokenId, apiGatewayUrl, privateKey, passphrase, keyId); + this.projectScopeToken = getProjectScopedToken(); } return this.projectScopeToken; } - public String getUserAccessToken(String audience){ - // TODO Implement getUserAccessToken - return null; + + /** + * @return String + * @throws Exception + */ + public String getUserAccessToken() throws Exception{ + + String signedToken = JwtUtil.signPayload(this.tokenId, this.tokenEndPoint, this.privateKey, this.passphrase, this.keyId); + if(signedToken == null){ + return null; + } + final HttpPost httpPost = new HttpPost(this.getTokenEndPoint()); + httpPost.setHeader(AuthProviderConstants.contentTypeHeader, "application/x-www-form-urlencoded"); + + final List params = new ArrayList(); + params.add(new BasicNameValuePair("grant_type", "client_credentials")); + params.add(new BasicNameValuePair("scope", "openid")); + params.add(new BasicNameValuePair("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); + params.add(new BasicNameValuePair("client_assertion", signedToken)); + params.add(new BasicNameValuePair("client_id", this.tokenId)); + httpPost.setEntity(new UrlEncodedFormEntity(params)); + + CloseableHttpClient client = HttpClients.createDefault(); + String userAccessToken = null; + try{ + userAccessToken = client.execute(httpPost, + response -> { + System.out.println(response.getCode()); + if(response.getCode() >=200 && response.getCode() < 300){ + HttpEntity responseEntity = response.getEntity(); + + JsonElement responseAsJson = new Gson().fromJson(EntityUtils.toString(responseEntity), JsonElement.class); + JsonObject responseObject = responseAsJson.isJsonObject() ? responseAsJson.getAsJsonObject() : null; + if(responseObject != null && responseObject.get("access_token") != null){ + return responseObject.get("access_token").getAsString(); + } + } + return null; + }); + }catch(Exception exception){ + throw new Exception(exception.getMessage()); + } + return userAccessToken; } - private String fetchProjectScopedToken(String apiGatewayUrl, String projectId, String tokenId, - String audience, String privateKey, String passphrase, String keyId){ + + + /** + * @param apiGatewayUrl + * @param projectId + * @return String + * @throws Exception + */ + private String getProjectScopedToken() throws Exception{ - // TODO Implement fetchProjectScopedToken - return null; + String userAccessToken = getUserAccessToken(); + + final HttpPost httpPost = new HttpPost(apiGatewayUrl + AuthProviderConstants.projectScopeTokenApiPath); + + final List params = new ArrayList(); + params.add(new BasicNameValuePair("projectId", projectId)); + httpPost.setEntity(new UrlEncodedFormEntity(params)); + + httpPost.setHeader("Authorization", "Bearer "+userAccessToken); + httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded"); + + CloseableHttpClient client = HttpClients.createDefault(); + String projectScopeToken = null; + try{ + projectScopeToken = client.execute(httpPost, + response -> { + if(response.getCode() >=200 && response.getCode() < 300){ + HttpEntity responseEntity = response.getEntity(); + + + JsonElement responseAsJson = new Gson().fromJson(EntityUtils.toString(responseEntity), JsonElement.class); + JsonObject responseObject = responseAsJson.isJsonObject() ? responseAsJson.getAsJsonObject() : null; + if(responseObject != null && responseObject.get("accessToken") != null){ + return responseObject.get("accessToken").getAsString(); + } + + } + return null; + }); + }catch(Exception exception){ + throw new Exception(exception.getMessage()); + } + + return projectScopeToken; } + + /** + * @param iotaConfigId + * @param did + * @param iotaSessionId + * @return IotaJwtOutput + * @throws Exception + */ public IotaJwtOutput signIotaJwt(String iotaConfigId, String did, String iotaSessionId) throws Exception { String iotaTokenId = "token/"+tokenId; @@ -68,6 +192,14 @@ public IotaJwtOutput signIotaJwt(String iotaConfigId, String did, String iotaSes return new IotaJwtOutput(iotaSessionID, iotaJwt); } + + /** + * This class provides a way to pass configurations to the AuthProvider + * It also helps to build an instance of AuthProvider which uses + * these configurations + * + * + */ public static class Configurations{ private String projectId; private String tokenId; @@ -102,48 +234,91 @@ public AuthProvider build() throws Exception{ } return new AuthProvider(this); } + public AuthProvider buildWithEnv() throws Exception{ + + if(this.projectId != null || this.privateKey != null || this.tokenId != null || this.passphrase != null || this.privateKey != null){ + throw new Exception("Please do not pass configurations values while using buildWithEnv. "+ + " These values will picked from .env. Alternatively you may use build() in order to explicitly pass values"); + } + + EnvironmentUtil envUtil = new EnvironmentUtil(); + this.keyId = envUtil.getValueFromEnvConfig(AuthProviderConstants.keyIdPropertyNameinEnv); + this.projectId = envUtil.getValueFromEnvConfig(AuthProviderConstants.projectIdPropertyNameinEnv); + this.passphrase = envUtil.getValueFromEnvConfig(AuthProviderConstants.passphrasePropertyNameinEnv); + this.tokenId = envUtil.getValueFromEnvConfig(AuthProviderConstants.tokenIdPropertyNameinEnv); + this.privateKey = envUtil.getValueFromEnvConfig(AuthProviderConstants.privateKeyPropertyNameinEnv); + + if(this.projectId == null || this.privateKey == null || this.tokenId == null){ + throw new Exception("Cannot create Auth provider without projectId, privateKey and tokenId. Please ensure these values are configured in .env"); + } + return new AuthProvider(this); + } } + + /** + * @return String + */ public String getProjectId() { return projectId; } - + /** + * @return String + */ public String getTokenId() { return tokenId; } - + /** + * @return String + */ public String getPrivateKey() { return privateKey; } - + /** + * @return String + */ public String getKeyId() { return keyId; } - + /** + * @return String + */ public String getPassphrase() { return passphrase; } - + /** + * @return String + */ public String getApiGatewayUrl() { return apiGatewayUrl; } - + /** + * @return String + */ public String getTokenEndPoint() { return tokenEndPoint; } - + /** + * @return String + */ public String getPublicKey() { return publicKey; } - + /** + * @return String + */ public void setPublicKey(String publicKey) { this.publicKey = publicKey; } - + /** + * @return String + */ public String getProjectScopeToken() { return projectScopeToken; } - + /** + * @return String + */ public void setProjectScopeToken(String projectScopeToken) { this.projectScopeToken = projectScopeToken; } diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/InvalidPrivateKeyException.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/InvalidPrivateKeyException.java new file mode 100644 index 000000000..1e5625103 --- /dev/null +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/InvalidPrivateKeyException.java @@ -0,0 +1,9 @@ +package com.affinidi.tdk.authProvider.exception; + +public class InvalidPrivateKeyException extends Exception{ + + public InvalidPrivateKeyException(String errorMessage) { + super(errorMessage); + } +} + diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/InvalidPublicKeyException.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/InvalidPublicKeyException.java new file mode 100644 index 000000000..1324821a9 --- /dev/null +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/InvalidPublicKeyException.java @@ -0,0 +1,8 @@ +package com.affinidi.tdk.authProvider.exception; + +public class InvalidPublicKeyException extends Exception{ + public InvalidPublicKeyException(String errorMessage) { + super(errorMessage); + } + +} diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/JwtGenerationException.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/JwtGenerationException.java new file mode 100644 index 000000000..08b20c8c8 --- /dev/null +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/JwtGenerationException.java @@ -0,0 +1,9 @@ +package com.affinidi.tdk.authProvider.exception; + +public class JwtGenerationException extends Exception{ + + public JwtGenerationException(String errorMessage) { + super(errorMessage); + } +} + diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/AuthProviderConstants.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/AuthProviderConstants.java new file mode 100644 index 000000000..62c21fee7 --- /dev/null +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/AuthProviderConstants.java @@ -0,0 +1,15 @@ +package com.affinidi.tdk.authProvider.helper; + +public interface AuthProviderConstants { + public static final String publicKeyPath = "/iam/.well-known/jwks.json"; + public static final String projectScopeTokenApiPath = "/iam/v1/sts/create-project-scoped-token"; + public static final String contentTypeHeader = "Content-Type"; + public static final String applicationJsonContentType = "application/json"; + + public static final String keyIdPropertyNameinEnv = "KEY_ID"; + public static final String tokenIdPropertyNameinEnv = "TOKEN_ID"; + public static final String passphrasePropertyNameinEnv = "PASSPHRASE"; + public static final String privateKeyPropertyNameinEnv = "PRIVATE_KEY"; + public static final String projectIdPropertyNameinEnv = "PROJECT_ID"; + +} diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java index 1a7fc8319..c7ab92683 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java @@ -1,5 +1,6 @@ package com.affinidi.tdk.authProvider.helper; +import java.io.IOException; import java.security.AlgorithmParameters; import java.security.Key; import java.security.KeyFactory; @@ -10,23 +11,65 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; -import java.util.logging.Logger; + import javax.crypto.Cipher; import javax.crypto.EncryptedPrivateKeyInfo; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; + +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.ParseException; +import org.apache.hc.core5.http.io.entity.EntityUtils; + +import com.affinidi.tdk.authProvider.exception.InvalidPrivateKeyException; +import com.affinidi.tdk.authProvider.exception.InvalidPublicKeyException; +import com.affinidi.tdk.authProvider.exception.JwtGenerationException; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSyntaxException; + import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.MalformedJwtException; +import io.jsonwebtoken.security.Jwk; +import io.jsonwebtoken.security.Jwks; +/** +* This class provides utility functions required +* by {@link AuthProvider} related for +* processing(creating, signing, validation) of JWT +* +* @author Priyanka +* +*/ public class JwtUtil { - private static Logger logger = Logger.getLogger(JwtUtil.class.getName()); - public static String signPayload(String tokenId, String audience, String privateKeyString, String passphrase, String keyId) throws Exception{ + /** + * This method builds a JSON web token for the provided claims. + * It then signs the token with the provided privatekey using RS256. + * + * In case of an encrypted private key; this method expects to receive + * the passphrase which was used during generation of the key pair. + * + * @param tokenId + * @param audience + * @param privateKeyString + * @param passphrase + * @param keyId + * @return String + * @throws JwtGenerationException in case the private key could not be processed or jwt generation failed + */ + public static String signPayload(String tokenId, String audience, String privateKeyString, String passphrase, + String keyId) throws JwtGenerationException{ + String signedJwtForClaims = null; long issueTimeInSeconds = System.currentTimeMillis() / 1000; - Map claims = new HashMap<>(); claims.put("iss", tokenId); claims.put("sub", tokenId); @@ -35,13 +78,40 @@ public static String signPayload(String tokenId, String audience, String private claims.put("exp", issueTimeInSeconds + (5 * 60)); claims.put("iat", issueTimeInSeconds); - return generateJwt(derivePrivateKey(privateKeyString, passphrase), claims); + try { + signedJwtForClaims = generateJwt(derivePrivateKey(privateKeyString, passphrase), claims); + } catch (JwtGenerationException jwtGenerationException) { + throw jwtGenerationException; + } catch (InvalidPrivateKeyException iPrivateKeyExceptione) { + throw new JwtGenerationException(iPrivateKeyExceptione.getMessage()); + } + return signedJwtForClaims; } - - public static String signIotaPayload(String tokenId, String audience, String privateKeyString, String passphrase, - String keyId, String projectId, String iotaConfigId, String iotaSessionId) throws Exception{ + + /** + * This method builds a JSON web token for the provided claims specific + * to an iota request. + * It then signs the token with the provided privatekey using RS256. + * + * In case of an encrypted private key; this method expects to receive + * the passphrase which was used during generation of the key pair. + * + * @param tokenId + * @param audience + * @param privateKeyString + * @param passphrase + * @param keyId + * @param projectId + * @param iotaConfigId + * @param iotaSessionId + * @return String + * @throws JwtGenerationException in case the private key could not be processed or jwt generation failed + */ + public static String signIotaPayload(String tokenId, String audience, String privateKeyString, String passphrase, + String keyId, String projectId, String iotaConfigId, String iotaSessionId) throws JwtGenerationException { + + String signedJwtForClaims = null; long issueTimeInSeconds = System.currentTimeMillis() / 1000; - Map claims = new HashMap<>(); claims.put("iss", tokenId); claims.put("sub", tokenId); @@ -54,73 +124,174 @@ public static String signIotaPayload(String tokenId, String audience, String pri claims.put("iota_configuration_id", iotaConfigId); claims.put("iota_session_id", iotaSessionId); claims.put("scope", "iota_channel"); - - return generateJwt(derivePrivateKey(privateKeyString, passphrase), claims); - } - public static PublicKey fetchPublicKey(String apiGatewayUrl){ - // TODO Implement fetchPublicKey to call the apiGateway - // and extract public key from the received jwk + try { + signedJwtForClaims = generateJwt(derivePrivateKey(privateKeyString, passphrase), claims); + } catch (JwtGenerationException jwtGenerationException) { + throw jwtGenerationException; + } catch (InvalidPrivateKeyException iPrivateKeyExceptione) { + throw new JwtGenerationException(iPrivateKeyExceptione.getMessage()); + } - return null; + return signedJwtForClaims; } - public static boolean validProjectTokenPresent(String token, String apiGatewayUrl){ - PublicKey publicKey = fetchPublicKey(apiGatewayUrl); + + /** + * This method fetches the signature verification key, required to + * validate the jws for a projectScopeToken and converts it to + * {@link java.security.PublicKey} + * + * @param apiGatewayUrl + * @return PublicKey + * @throws InvalidPublicKeyException in case the public key could not be retrieved/extracted + */ + public static PublicKey fetchPublicKey(String apiGatewayUrl) throws InvalidPublicKeyException { + PublicKey publicKey = null; try{ + final HttpGet httpGet = new HttpGet(apiGatewayUrl + AuthProviderConstants.publicKeyPath); + httpGet.setHeader(AuthProviderConstants.contentTypeHeader, AuthProviderConstants.applicationJsonContentType); + + CloseableHttpClient client = HttpClients.createDefault(); + publicKey = client.execute(httpGet, + response -> { + if(response.getCode() >=200 && response.getCode() < 300){ + String jwkFromResponse = decodeRespose(response.getEntity()); + Jwk jwk = Jwks.parser().build() + .parse(jwkFromResponse); + if(jwk != null){ + return (PublicKey)jwk.toKey(); + } + } + return null; + }); + }catch(Exception exception){ + throw new InvalidPublicKeyException("Could not retreive/ extract the public "+ + "key required to validate projectScopeToken "+exception.getMessage()); + } + return publicKey; + } + + /** + * This method validates the projectScopeToken. + * Along with validating if the token is malformed or expired; + * it also verifies the token signature using the + * signature verification public key. + * + * If the public key could not be retrieved for any reason; + * the method would consider the token as invalid. + * + * @param token + * @param apiGatewayUrl + * @return boolean + */ + public static boolean validProjectTokenPresent(String token, String apiGatewayUrl) { + try { + PublicKey publicKey = fetchPublicKey(apiGatewayUrl); + if(publicKey == null){ + throw new Exception("Could not retrieve public key for token validation"); + } Jwts.parser().verifyWith(publicKey).build().parse(token); - }catch(ExpiredJwtException ejException){ - return true; - }catch(MalformedJwtException mjException){ - return true; - }catch(Exception eException){ - return true; + } catch (InvalidPublicKeyException iPublicKeyException) { + return false; + }catch (ExpiredJwtException ejException) { + return false; + } catch (MalformedJwtException mjException) { + return false; + } catch (Exception eException) { + return false; } - return false; + return true; } - private static PrivateKey derivePrivateKey(String privateKeyString, String passphrase) throws Exception{ + + /** + * This method converts the private key string passed to a + * {@link java.security.PrivateKey}. In case a passphrase is passed, + * the private key is treated as encrypted and processed accordingly. + * This passphrase should be same as the one used to create the + * public-private key pair + * + * @param privateKeyString + * @param passphrase + * @return PrivateKey + * @throws InvalidPrivateKeyException + */ + private static PrivateKey derivePrivateKey(String privateKeyString, String passphrase) throws InvalidPrivateKeyException { PrivateKey privateKey = null; - try{ - if (!passphrase.isEmpty()) { + try { + if (passphrase.isEmpty()) { privateKey = getPrivateKeyFromString(privateKeyString); } else { privateKey = getEncryptedPrivateKeyFromString(privateKeyString, passphrase); } - }catch(Exception exception){ - logger.severe("extractPrivateKey : Issue in retrieving private key from the string passed"); - throw new Exception("Could not derive private key out of the configurations. Exception : "+exception.toString()); + } catch (Exception exception) { + throw new InvalidPrivateKeyException( + "Could not derive private key out of the configurations. Exception : " + exception.toString()); } - return privateKey; } - private static String generateJwt(PrivateKey privateKey, Map claims){ - return Jwts.builder() + + /** + * This method generates a JSON payload representing all the + * claims passed. It then signs this payload with the + * {@link java.security.PrivateKey}, creating a json web + * signature (JWS) and then builds the JWT as a URL-safe + * string + * + * @param privateKey + * @param claims + * @return String + * @throws JwtGenerationException when the jwt generation fails. For instance if the private key is insufficient + */ + private static String generateJwt(PrivateKey privateKey, Map claims) throws JwtGenerationException{ + String jwToken = null; + try{ + jwToken = Jwts.builder() .claims(claims) .signWith(privateKey, Jwts.SIG.RS256) .compact(); + + }catch(Exception exception){ + throw new JwtGenerationException(" Could not generate the JWT representing the claims. Exception"+ exception.getMessage()); + } + return jwToken; } + + /** + * This method converts a private key string to + * {@link java.security.PrivateKey} object + * + * @param privateKeyPEM + * @return PrivateKey + * @throws Exception + */ private static PrivateKey getPrivateKeyFromString(String privateKeyPEM) throws Exception { - String privateKeyContent = privateKeyPEM - .replace("-----BEGIN PRIVATE KEY-----", "") - .replace("-----END PRIVATE KEY-----", "") - .replaceAll("\\s", ""); - + String privateKeyContent = extractPrivateKeyContent(privateKeyPEM); byte[] encoded = Base64.getDecoder().decode(privateKeyContent); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded); return keyFactory.generatePrivate(keySpec); } + + /** + * This method converts an encrypted private key string to + * {@link java.security.PrivateKey} object + * The passphrase which was used to generate the public-private + * keypair should be passed to this method for processing. + * + * @param encryptedPrivateKeyPEM + * @param password + * @return PrivateKey + * @throws Exception + */ private static PrivateKey getEncryptedPrivateKeyFromString(String encryptedPrivateKeyPEM, String password) throws Exception { - String privateKeyContent = encryptedPrivateKeyPEM - .replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "") - .replace("-----END ENCRYPTED PRIVATE KEY-----", ""); - + String privateKeyContent = extractPrivateKeyContent(encryptedPrivateKeyPEM); byte[] encodedKey = Base64.getDecoder().decode(privateKeyContent); EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(encodedKey); Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName()); @@ -135,6 +306,48 @@ private static PrivateKey getEncryptedPrivateKeyFromString(String encryptedPriva return keyFactory.generatePrivate(pkcs8KeySpec); } + + /** + * This method cleans up the private key string which is required to + * decode the key material + * + * @param privateKey + * @return String + */ + private static String extractPrivateKeyContent(String privateKey) { + return privateKey + .replaceAll("-----BEGIN (ENCRYPTED )?PRIVATE KEY-----|-----END (ENCRYPTED )?PRIVATE KEY-----", "") + .replace("\\n", "").trim(); + } + + + /** + * This method processes the response from the public key API call and extracts + * the first key listed from the response + * + * + * @param responseEntity + * @return String + * @throws IOException + * @throws JsonSyntaxException + * @throws ParseException + */ + private static String decodeRespose(HttpEntity responseEntity) throws IOException, JsonSyntaxException, ParseException{ + String extractedJwkFromResponse = null; + if(responseEntity != null){ + JsonElement responseAsJson = new Gson().fromJson(EntityUtils.toString(responseEntity), JsonElement.class); + JsonObject responseObject = responseAsJson.isJsonObject() ? responseAsJson.getAsJsonObject() : null; + if(responseObject != null){ + JsonArray setOfkeys = responseObject.getAsJsonArray("keys") ; + if(setOfkeys != null){ + JsonObject firstKeysAsObject = (setOfkeys != null && setOfkeys.size() > 0) ? setOfkeys.get(0).getAsJsonObject() : null; + extractedJwkFromResponse = (firstKeysAsObject != null) ? firstKeysAsObject.toString() : null; + } + } + + } + return extractedJwkFromResponse; + } } diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/type/IotaJwtOutput.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/types/IotaJwtOutput.java similarity index 69% rename from packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/type/IotaJwtOutput.java rename to packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/types/IotaJwtOutput.java index da31b2092..a8fd64968 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/type/IotaJwtOutput.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/types/IotaJwtOutput.java @@ -1,5 +1,13 @@ -package com.affinidi.tdk.authProvider.type; +package com.affinidi.tdk.authProvider.types; +/** +* This class represents the output returned +* when {@link AuthProvider#signIotaJwt()} is invoked +* +* +* @author Priyanka +* +*/ public class IotaJwtOutput { private final String iotaSessionId; private final String iotaJwt; diff --git a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/JwtUtilTest.java b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/JwtUtilTest.java index 736c10d2d..4b287d5bb 100644 --- a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/JwtUtilTest.java +++ b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/JwtUtilTest.java @@ -33,7 +33,4 @@ void testValidProjectTokenPresent() { } - - - } diff --git a/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java b/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java index 444f72f64..2db81a369 100644 --- a/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java +++ b/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java @@ -93,6 +93,13 @@ public String getApiGatewayUrlForEnvironment() { return getEnvironmentDetail().apiGatewayUrl; } + /** + * @return String + */ + public String getValueFromEnvConfig(String propertyName) { + return properties.get(propertyName); + } + /** * Returns the APi gateway URL for the configured environment * From cd66c42b88998e606e7e5b1a9984d843879882d8 Mon Sep 17 00:00:00 2001 From: Priyanka Sharma Date: Wed, 15 Jan 2025 21:36:34 +0800 Subject: [PATCH 07/15] feat: added javadoc and exception handling --- .../tdk/authProvider/AuthProvider.java | 138 ++++++++++++------ .../AccessTokenGenerationException.java | 9 ++ .../exception/ConfigurationException.java | 8 + .../exception/PSTGenerationException.java | 8 + .../helper/AuthProviderConstants.java | 1 + 5 files changed, 116 insertions(+), 48 deletions(-) create mode 100644 packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/AccessTokenGenerationException.java create mode 100644 packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/ConfigurationException.java create mode 100644 packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/PSTGenerationException.java diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java index e3a91dee5..dc0fd3633 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java @@ -13,6 +13,10 @@ import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.BasicNameValuePair; +import com.affinidi.tdk.authProvider.exception.AccessTokenGenerationException; +import com.affinidi.tdk.authProvider.exception.ConfigurationException; +import com.affinidi.tdk.authProvider.exception.JwtGenerationException; +import com.affinidi.tdk.authProvider.exception.PSTGenerationException; import com.affinidi.tdk.authProvider.helper.AuthProviderConstants; import com.affinidi.tdk.authProvider.helper.JwtUtil; import com.affinidi.tdk.authProvider.types.IotaJwtOutput; @@ -58,7 +62,7 @@ public class AuthProvider { * generating a new. * * The validation involves verifying token's signature against the - * public (verification) key. + * public (verification) key; validating token's expiration or malformation. * * @return boolean */ @@ -71,10 +75,20 @@ public boolean shouldRefreshToken() { /** + * This method generates a projectScopeToken required to call + * Affinidi services. + * + * In case there is an existing projectScopeToken in the + * authProvider instance; it is first validated and a new one is + * generated only if needed. + * + * Refer {@link JwtUtil#validProjectTokenPresent(String, String)} + * for validation details + * * @return String - * @throws Exception + * @throws PSTGenerationException incase access_token generation has issues or projectScopeToken end point */ - public String fetchProjectScopedToken() throws Exception{ + public String fetchProjectScopedToken() throws PSTGenerationException{ boolean tokenFetchRequired = shouldRefreshToken(); if(tokenFetchRequired){ @@ -85,32 +99,35 @@ public String fetchProjectScopedToken() throws Exception{ /** + * This method generates a user-access-token which is required + * as an API authorization token. + * * @return String - * @throws Exception + * @throws AccessTokenGenerationException in case the acceess token could not be generated */ - public String getUserAccessToken() throws Exception{ + public String getUserAccessToken() throws AccessTokenGenerationException{ + String userAccessToken = null; - String signedToken = JwtUtil.signPayload(this.tokenId, this.tokenEndPoint, this.privateKey, this.passphrase, this.keyId); - if(signedToken == null){ - return null; - } - final HttpPost httpPost = new HttpPost(this.getTokenEndPoint()); - httpPost.setHeader(AuthProviderConstants.contentTypeHeader, "application/x-www-form-urlencoded"); + try{ + String signedToken = JwtUtil.signPayload(this.tokenId, this.tokenEndPoint, this.privateKey, this.passphrase, this.keyId); + + if(signedToken == null){ + throw new JwtGenerationException("Could not generate signed JWT from the configurations "); + } + final HttpPost httpPost = new HttpPost(this.getTokenEndPoint()); + httpPost.setHeader(AuthProviderConstants.contentTypeHeader, AuthProviderConstants.applicationUrlEncodedContentType); - final List params = new ArrayList(); - params.add(new BasicNameValuePair("grant_type", "client_credentials")); - params.add(new BasicNameValuePair("scope", "openid")); - params.add(new BasicNameValuePair("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); - params.add(new BasicNameValuePair("client_assertion", signedToken)); - params.add(new BasicNameValuePair("client_id", this.tokenId)); - httpPost.setEntity(new UrlEncodedFormEntity(params)); + final List params = new ArrayList(); + params.add(new BasicNameValuePair("grant_type", "client_credentials")); + params.add(new BasicNameValuePair("scope", "openid")); + params.add(new BasicNameValuePair("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); + params.add(new BasicNameValuePair("client_assertion", signedToken)); + params.add(new BasicNameValuePair("client_id", this.tokenId)); + httpPost.setEntity(new UrlEncodedFormEntity(params)); - CloseableHttpClient client = HttpClients.createDefault(); - String userAccessToken = null; - try{ + CloseableHttpClient client = HttpClients.createDefault(); userAccessToken = client.execute(httpPost, response -> { - System.out.println(response.getCode()); if(response.getCode() >=200 && response.getCode() < 300){ HttpEntity responseEntity = response.getEntity(); @@ -122,36 +139,39 @@ public String getUserAccessToken() throws Exception{ } return null; }); + if(userAccessToken == null){ + throw new AccessTokenGenerationException("getUserAccessToken : Could not retrieve access_token from the token end point"); + } + }catch(JwtGenerationException jwtGenerationException){ + throw new AccessTokenGenerationException(jwtGenerationException.getMessage()); }catch(Exception exception){ - throw new Exception(exception.getMessage()); + throw new AccessTokenGenerationException(exception.getMessage()); } return userAccessToken; } - - /** - * @param apiGatewayUrl - * @param projectId + * This method generates a projectScopeToken for the configuration + * values associated to the AuthProvider + * * @return String - * @throws Exception + * @throws PSTGenerationException */ - private String getProjectScopedToken() throws Exception{ - - String userAccessToken = getUserAccessToken(); - - final HttpPost httpPost = new HttpPost(apiGatewayUrl + AuthProviderConstants.projectScopeTokenApiPath); - - final List params = new ArrayList(); - params.add(new BasicNameValuePair("projectId", projectId)); - httpPost.setEntity(new UrlEncodedFormEntity(params)); + private String getProjectScopedToken() throws PSTGenerationException{ + String projectScopeToken = null; + try{ + String userAccessToken = getUserAccessToken(); + + final HttpPost httpPost = new HttpPost(apiGatewayUrl + AuthProviderConstants.projectScopeTokenApiPath); - httpPost.setHeader("Authorization", "Bearer "+userAccessToken); - httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded"); + final List params = new ArrayList(); + params.add(new BasicNameValuePair("projectId", projectId)); + httpPost.setEntity(new UrlEncodedFormEntity(params)); + + httpPost.setHeader("Authorization", "Bearer "+userAccessToken); + httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded"); - CloseableHttpClient client = HttpClients.createDefault(); - String projectScopeToken = null; - try{ + CloseableHttpClient client = HttpClients.createDefault(); projectScopeToken = client.execute(httpPost, response -> { if(response.getCode() >=200 && response.getCode() < 300){ @@ -167,8 +187,13 @@ private String getProjectScopedToken() throws Exception{ } return null; }); + if(projectScopeToken == null){ + throw new PSTGenerationException("getProjectScopedToken : Could not retrieve accessToken from "+(apiGatewayUrl + AuthProviderConstants.projectScopeTokenApiPath)); + } + }catch(AccessTokenGenerationException accessTokenGenerationException){ + throw new PSTGenerationException(accessTokenGenerationException.getMessage()); }catch(Exception exception){ - throw new Exception(exception.getMessage()); + throw new PSTGenerationException(exception.getMessage()); } return projectScopeToken; @@ -176,6 +201,8 @@ private String getProjectScopedToken() throws Exception{ /** + * This method generates a signed jwt for an iota session + * * @param iotaConfigId * @param did * @param iotaSessionId @@ -228,16 +255,31 @@ public Configurations passphrase(String passphrase){ return this; } - public AuthProvider build() throws Exception{ + /** + * This method builds an instance of AuthProvider with the + * values passed through {@link Configuration} + * + * @return + * @throws ConfigurationException + */ + public AuthProvider build() throws ConfigurationException{ if(this.projectId == null || this.privateKey == null || this.tokenId == null){ - throw new Exception("Cannot create Auth provider without projectId, privateKey and toeknId"); + throw new ConfigurationException("Cannot create Auth provider without projectId, privateKey and toeknId"); } return new AuthProvider(this); } - public AuthProvider buildWithEnv() throws Exception{ + + /** + * This method builds an instance of AuthProvider with the + * configuration values present in the .env file + * + * @return + * @throws ConfigurationException + */ + public AuthProvider buildWithEnv() throws ConfigurationException{ if(this.projectId != null || this.privateKey != null || this.tokenId != null || this.passphrase != null || this.privateKey != null){ - throw new Exception("Please do not pass configurations values while using buildWithEnv. "+ + throw new ConfigurationException("Please do not pass configurations values while using buildWithEnv. "+ " These values will picked from .env. Alternatively you may use build() in order to explicitly pass values"); } @@ -249,7 +291,7 @@ public AuthProvider buildWithEnv() throws Exception{ this.privateKey = envUtil.getValueFromEnvConfig(AuthProviderConstants.privateKeyPropertyNameinEnv); if(this.projectId == null || this.privateKey == null || this.tokenId == null){ - throw new Exception("Cannot create Auth provider without projectId, privateKey and tokenId. Please ensure these values are configured in .env"); + throw new ConfigurationException("Cannot create Auth provider without projectId, privateKey and tokenId. Please ensure these values are configured in .env"); } return new AuthProvider(this); } diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/AccessTokenGenerationException.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/AccessTokenGenerationException.java new file mode 100644 index 000000000..3f01cf765 --- /dev/null +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/AccessTokenGenerationException.java @@ -0,0 +1,9 @@ +package com.affinidi.tdk.authProvider.exception; + +public class AccessTokenGenerationException extends Exception{ + + public AccessTokenGenerationException(String errorMessage) { + super(errorMessage); + } +} + diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/ConfigurationException.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/ConfigurationException.java new file mode 100644 index 000000000..04d55a7e1 --- /dev/null +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/ConfigurationException.java @@ -0,0 +1,8 @@ +package com.affinidi.tdk.authProvider.exception; + +public class ConfigurationException extends Exception{ + public ConfigurationException(String errorMessage) { + super(errorMessage); + } + +} diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/PSTGenerationException.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/PSTGenerationException.java new file mode 100644 index 000000000..89d3a6f0d --- /dev/null +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/PSTGenerationException.java @@ -0,0 +1,8 @@ +package com.affinidi.tdk.authProvider.exception; + +public class PSTGenerationException extends Exception{ + public PSTGenerationException(String errorMessage) { + super(errorMessage); + } + +} diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/AuthProviderConstants.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/AuthProviderConstants.java index 62c21fee7..a9a00edf0 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/AuthProviderConstants.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/AuthProviderConstants.java @@ -5,6 +5,7 @@ public interface AuthProviderConstants { public static final String projectScopeTokenApiPath = "/iam/v1/sts/create-project-scoped-token"; public static final String contentTypeHeader = "Content-Type"; public static final String applicationJsonContentType = "application/json"; + public static final String applicationUrlEncodedContentType = "application/x-www-form-urlencoded"; public static final String keyIdPropertyNameinEnv = "KEY_ID"; public static final String tokenIdPropertyNameinEnv = "TOKEN_ID"; From 40d9f787f66e3851bfe7ec7769a85a1dd0954618 Mon Sep 17 00:00:00 2001 From: Francis Pineda Date: Mon, 20 Jan 2025 18:44:25 +0800 Subject: [PATCH 08/15] docs: update readme --- README.md | 28 ++++++++++++------------ packages/java/auth.provider/.env.example | 3 --- packages/java/auth.provider/README.md | 26 ++++------------------ 3 files changed, 18 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 61ca29466..a5ab03816 100644 --- a/README.md +++ b/README.md @@ -21,26 +21,26 @@ The Affinidi TDK offers the following modules and support to programming languag -| | TypeScript | Python | Dart | PHP | -|----------- |-----------------------|-----------------------|----------------------|---------------------| +| | TypeScript | Python | Dart | PHP | Java | +|----------- |-----------------------|-----------------------|----------------------|---------------------|---------------------| |**Packages** | | | | -|[auth-provider](packages/auth-provider/) | 🟢 | 🟢 | 🔴 | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/AuthProvider) | -|[common](packages/common/) | 🟢 | 🟢 | 🔴 | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Common) | -|**Packages** | | | | -|credential-issuance-client | [🟢 Link](clients/typescript/credential-issuance-client/) | [🟢 Link](clients/python/credential_issuance_client/) | [🟡 Link](clients/dart/credential_issuance_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/CredentialIssuanceClient) | -|credential-verification-client | [🟢 Link](clients/typescript/credential-verification-client/) | [🟢 Link](clients/python/credential_verification_client/) | [🟡 Link](clients/dart/credential_verification_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/CredentialVerificationClient) | -|iam-client | [🟢 Link](clients/typescript/iam-client/) | [🟢 Link](clients/python/iam_client/) | [🟡 Link](clients/dart/iam_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/IamClient) | -|iota-client | [🟢 Link](clients/typescript/iota-client/) | [🟢 Link](clients/python/iota_client/) | [🟡 Link](clients/dart/iota_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/IotaClient) | -|login-configuration-client | [🟢 Link](clients/typescript/login-configuration-client/) | [🟢 Link](clients/python/login_configuration_client/) | [🟡 Link](clients/dart/login_configuration_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/LoginConfigurationClient) | -|wallets-client | [🟢 Link](clients/typescript/wallets-client/) | [🟢 Link](clients/python/wallets_client/) | [🟡 Link](clients/dart/wallets_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/WalletsClient) | +|[auth-provider](packages/auth-provider/) | 🟢 | 🟢 | 🔴 | [🟢](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/AuthProvider) | 🟢 | +|[common](packages/common/) | 🟢 | 🟢 | 🔴 | [🟢](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Common) | 🟢 | +|**Clients** | | | | +|credential-issuance-client | [🟢 Link](clients/typescript/credential-issuance-client/) | [🟢 Link](clients/python/credential_issuance_client/) | [🟡 Link](clients/dart/credential_issuance_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/CredentialIssuanceClient) | [🟢 Link](clients/java/credential-issuance-client/) | +|credential-verification-client | [🟢 Link](clients/typescript/credential-verification-client/) | [🟢 Link](clients/python/credential_verification_client/) | [🟡 Link](clients/dart/credential_verification_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/CredentialVerificationClient) | [🟢 Link](clients/java/credential-verification-client/) | +|iam-client | [🟢 Link](clients/typescript/iam-client/) | [🟢 Link](clients/python/iam_client/) | [🟡 Link](clients/dart/iam_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/IamClient) | [🟢 Link](clients/java/iam-client/) | +|iota-client | [🟢 Link](clients/typescript/iota-client/) | [🟢 Link](clients/python/iota_client/) | [🟡 Link](clients/dart/iota_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/IotaClient) | [🟢 Link](clients/java/iota-client/) | +|login-configuration-client | [🟢 Link](clients/typescript/login-configuration-client/) | [🟢 Link](clients/python/login_configuration_client/) | [🟡 Link](clients/dart/login_configuration_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/LoginConfigurationClient) | [🟢 Link](clients/java/login-configuration-client/) | +|wallets-client | [🟢 Link](clients/typescript/wallets-client/) | [🟢 Link](clients/python/wallets_client/) | [🟡 Link](clients/dart/wallets_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/WalletsClient) | [🟢 Link](clients/java/wallets-client/) | |**Libraries** | | | | -|[iota-browser](libs/iota-browser/) | 🟢 | 🔴 | 🔴 | 🔴 | -|[iota-core](libs/iota-core/) | 🟢 | 🟢 | 🔴 | 🔴 | +|[iota-browser](libs/iota-browser/) | 🟢 | 🔴 | 🔴 | 🔴 | 🔴 | +|[iota-core](libs/iota-core/) | 🟢 | 🟢 | 🔴 | 🔴 | 🟢 |
🟢 Supported
🟡 Experimental
-🔴 Not supported +🔴 Not supported ## Documentation diff --git a/packages/java/auth.provider/.env.example b/packages/java/auth.provider/.env.example index 69103468e..fc80469d9 100644 --- a/packages/java/auth.provider/.env.example +++ b/packages/java/auth.provider/.env.example @@ -1,6 +1,3 @@ -# Mandatory value required to derive the environment -AFFINIDI_TDK_ENVIRONMENT= - # Optional values needed to generate auth token. These values can also be directly passsed to the AuthProvider build fuunction PROJECT_ID= KEY_ID= diff --git a/packages/java/auth.provider/README.md b/packages/java/auth.provider/README.md index 1b5e8cb33..afc8d2281 100644 --- a/packages/java/auth.provider/README.md +++ b/packages/java/auth.provider/README.md @@ -1,8 +1,6 @@ # auth.provider -AuthProvider package - -- API version: 1.0.0 +AuthProvider package enables you to generate the required access token like Project Scoped Token to authenticate and access Affinidi Trust Network services. For more information, please visit [https://github.com/affinidi/affinidi-tdk](https://github.com/affinidi/affinidi-tdk) @@ -47,17 +45,11 @@ At first generate the JAR by executing: mvn clean package ``` -## Getting Started - -Configure .env file at your project base with the following values (Refer .env.example) +## Usage -AFFINIDI_TDK_ENVIRONMENT= (local,dev or prod) +Sample usage to generate Project Scoped Token to call Affinidi TDK clients. -PROJECT_ID= -KEY_ID= -TOKEN_ID= -PASSPHRASE= -PRIVATE_KEY= +> You can store the required parameters like the Token details into an environment file. ```java @@ -96,13 +88,3 @@ public class AuthProviderConsumer { } ``` - -## Documentation for API Endpoints - - -## Recommendation - - -## Author - -info@affinidi.com From fe26cb6fdc55f26d022f863c18ea4cca27b0bb37 Mon Sep 17 00:00:00 2001 From: aeffinidi <86773100+aeffinidi@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:51:00 +0100 Subject: [PATCH 09/15] test: auth provider unit tests [FTL-18596] (#464) * test: add more junits dependencies * test: add 2 failing test scenarios * refactor: add nested property with name * test: add test with static method stub * test: add invalid private-key with apikey endpoint stub * refactor: move error message to constants file * test: add happy path * fix: check proper exception --- packages/java/auth.provider/pom.xml | 114 +++++--- .../tdk/authProvider/AuthProvider.java | 262 ++++++++++-------- .../helper/AuthProviderConstants.java | 5 +- .../tdk/authProvider/helper/JwtUtil.java | 172 ++++++------ .../tdk/authProvider/AuthProviderTest.java | 185 +++++++++++-- .../resources/api-key-response.json | 49 ++++ .../resources/test-private-key.txt | 1 + 7 files changed, 528 insertions(+), 260 deletions(-) create mode 100644 packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/resources/api-key-response.json create mode 100644 packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/resources/test-private-key.txt diff --git a/packages/java/auth.provider/pom.xml b/packages/java/auth.provider/pom.xml index 06375c2f2..f97602a6f 100644 --- a/packages/java/auth.provider/pom.xml +++ b/packages/java/auth.provider/pom.xml @@ -1,6 +1,7 @@ - 4.0.0 jar @@ -10,20 +11,20 @@ auth.provider https://github.com/affinidi/affinidi-tdk - - Apache-2.0 - https://github.com/affinidi/affinidi-tdk/blob/main/LICENSE - repo - + + Apache-2.0 + https://github.com/affinidi/affinidi-tdk/blob/main/LICENSE + repo + - - Affinidi - ... - Affinidi - https://affinidi.com - + + Affinidi + ... + Affinidi + https://affinidi.com + @@ -38,39 +39,58 @@ 1.0 0.12.6 3.12.4 + 3.10.0 2.10.1 2.0.16 - org.junit.jupiter - junit-jupiter-api - ${junit-version} - test + org.junit.jupiter + junit-jupiter-api + ${junit-version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit-version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit-version} + test - org.junit.jupiter - junit-jupiter-engine - ${junit-version} - test + org.junit.jupiter + junit-jupiter-engine + ${junit-version} + test - io.github.cdimascio - dotenv-java - ${cdimascio-version} - + io.github.cdimascio + dotenv-java + ${cdimascio-version} + - org.glassfish.jersey.core - jersey-common - ${jersey-version} + org.glassfish.jersey.core + jersey-common + ${jersey-version} org.mockito - mockito-core + mockito-inline ${mockito-version} test + + org.wiremock + wiremock + ${wiremock-version} + test + com.affinidi.tdk common @@ -92,27 +112,30 @@ ${jsonwebtoken-version} - org.apache.httpcomponents.client5 - httpclient5 - ${apache-http-version} + org.apache.httpcomponents.client5 + httpclient5 + ${apache-http-version} - com.google.code.gson - gson - ${gson-version} + com.google.code.gson + gson + ${gson-version} - + - + - + maven-clean-plugin 3.1.0 - + maven-resources-plugin 3.0.2 @@ -121,14 +144,14 @@ maven-compiler-plugin 3.8.0 - + - org.apache.maven.plugins - maven-surefire-plugin + org.apache.maven.plugins + maven-surefire-plugin - maven-failsafe-plugin - 3.5.0 + maven-failsafe-plugin + 3.5.0 maven-jar-plugin @@ -142,7 +165,8 @@ maven-deploy-plugin 2.8.2 - + maven-site-plugin 3.7.1 @@ -154,4 +178,4 @@ - +
\ No newline at end of file diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java index dc0fd3633..dd121ddb2 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java @@ -26,12 +26,12 @@ import com.google.gson.JsonObject; /** -* This class provides -* -* -* @author Priyanka -* -*/ + * This class provides + * + * + * @author Priyanka + * + */ public class AuthProvider { private final String projectId; @@ -39,12 +39,12 @@ public class AuthProvider { private final String privateKey; private final String keyId; private final String passphrase; - private final String apiGatewayUrl; - private final String tokenEndPoint; + private String tokenEndPoint; + private String apiGatewayUrl; private String publicKey; private String projectScopeToken; - AuthProvider(Configurations configurations){ + AuthProvider(Configurations configurations) { this.projectId = configurations.projectId; this.tokenId = configurations.tokenId; this.privateKey = configurations.privateKey; @@ -55,152 +55,158 @@ public class AuthProvider { this.tokenEndPoint = eUtil.getElementAuthTokenUrlForEnvironment(); } - - /** + /** * This method identifies if the current AuthProvider has a valid existing - * projectScopeToken or not. This helps to reuse the valid tokens without always + * projectScopeToken or not. This helps to reuse the valid tokens without always * generating a new. * - * The validation involves verifying token's signature against the + * The validation involves verifying token's signature against the * public (verification) key; validating token's expiration or malformation. * * @return boolean */ public boolean shouldRefreshToken() { - if(this.projectScopeToken == null){ + if (this.projectScopeToken == null) { return true; } return !(JwtUtil.validProjectTokenPresent(this.projectScopeToken, this.apiGatewayUrl)); } - - /** - * This method generates a projectScopeToken required to call + /** + * This method generates a projectScopeToken required to call * Affinidi services. * - * In case there is an existing projectScopeToken in the + * In case there is an existing projectScopeToken in the * authProvider instance; it is first validated and a new one is - * generated only if needed. + * generated only if needed. * * Refer {@link JwtUtil#validProjectTokenPresent(String, String)} * for validation details * * @return String - * @throws PSTGenerationException incase access_token generation has issues or projectScopeToken end point + * @throws PSTGenerationException incase access_token generation has issues or + * projectScopeToken end point */ - public String fetchProjectScopedToken() throws PSTGenerationException{ + public String fetchProjectScopedToken() throws PSTGenerationException { boolean tokenFetchRequired = shouldRefreshToken(); - if(tokenFetchRequired){ + if (tokenFetchRequired) { this.projectScopeToken = getProjectScopedToken(); } return this.projectScopeToken; } - - /** - * This method generates a user-access-token which is required - * as an API authorization token. + /** + * This method generates a user-access-token which is required + * as an API authorization token. * * @return String - * @throws AccessTokenGenerationException in case the acceess token could not be generated + * @throws AccessTokenGenerationException in case the acceess token could not be + * generated */ - public String getUserAccessToken() throws AccessTokenGenerationException{ + public String getUserAccessToken() throws AccessTokenGenerationException { String userAccessToken = null; - try{ - String signedToken = JwtUtil.signPayload(this.tokenId, this.tokenEndPoint, this.privateKey, this.passphrase, this.keyId); + try { + String signedToken = JwtUtil.signPayload(this.tokenId, this.tokenEndPoint, this.privateKey, this.passphrase, + this.keyId); - if(signedToken == null){ + if (signedToken == null) { throw new JwtGenerationException("Could not generate signed JWT from the configurations "); } final HttpPost httpPost = new HttpPost(this.getTokenEndPoint()); - httpPost.setHeader(AuthProviderConstants.contentTypeHeader, AuthProviderConstants.applicationUrlEncodedContentType); + httpPost.setHeader(AuthProviderConstants.contentTypeHeader, + AuthProviderConstants.applicationUrlEncodedContentType); final List params = new ArrayList(); params.add(new BasicNameValuePair("grant_type", "client_credentials")); params.add(new BasicNameValuePair("scope", "openid")); - params.add(new BasicNameValuePair("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); + params.add(new BasicNameValuePair("client_assertion_type", + "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); params.add(new BasicNameValuePair("client_assertion", signedToken)); params.add(new BasicNameValuePair("client_id", this.tokenId)); httpPost.setEntity(new UrlEncodedFormEntity(params)); - CloseableHttpClient client = HttpClients.createDefault(); - userAccessToken = client.execute(httpPost, - response -> { - if(response.getCode() >=200 && response.getCode() < 300){ - HttpEntity responseEntity = response.getEntity(); + CloseableHttpClient client = HttpClients.createSystem(); + userAccessToken = client.execute(httpPost, + response -> { + if (response.getCode() >= 200 && response.getCode() < 300) { + HttpEntity responseEntity = response.getEntity(); - JsonElement responseAsJson = new Gson().fromJson(EntityUtils.toString(responseEntity), JsonElement.class); - JsonObject responseObject = responseAsJson.isJsonObject() ? responseAsJson.getAsJsonObject() : null; - if(responseObject != null && responseObject.get("access_token") != null){ - return responseObject.get("access_token").getAsString(); + JsonElement responseAsJson = new Gson().fromJson(EntityUtils.toString(responseEntity), + JsonElement.class); + JsonObject responseObject = responseAsJson.isJsonObject() ? responseAsJson.getAsJsonObject() + : null; + if (responseObject != null && responseObject.get("access_token") != null) { + return responseObject.get("access_token").getAsString(); + } } - } - return null; - }); - if(userAccessToken == null){ - throw new AccessTokenGenerationException("getUserAccessToken : Could not retrieve access_token from the token end point"); - } - }catch(JwtGenerationException jwtGenerationException){ + return null; + }); + if (userAccessToken == null) { + throw new AccessTokenGenerationException( + "getUserAccessToken : Could not retrieve access_token from the token end point"); + } + } catch (JwtGenerationException jwtGenerationException) { throw new AccessTokenGenerationException(jwtGenerationException.getMessage()); - }catch(Exception exception){ + } catch (Exception exception) { throw new AccessTokenGenerationException(exception.getMessage()); } return userAccessToken; } - - /** + + /** * This method generates a projectScopeToken for the configuration * values associated to the AuthProvider * * @return String * @throws PSTGenerationException */ - private String getProjectScopedToken() throws PSTGenerationException{ + private String getProjectScopedToken() throws PSTGenerationException { String projectScopeToken = null; - try{ + try { String userAccessToken = getUserAccessToken(); - + final HttpPost httpPost = new HttpPost(apiGatewayUrl + AuthProviderConstants.projectScopeTokenApiPath); - + final List params = new ArrayList(); params.add(new BasicNameValuePair("projectId", projectId)); httpPost.setEntity(new UrlEncodedFormEntity(params)); - - httpPost.setHeader("Authorization", "Bearer "+userAccessToken); + + httpPost.setHeader("Authorization", "Bearer " + userAccessToken); httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded"); - CloseableHttpClient client = HttpClients.createDefault(); - projectScopeToken = client.execute(httpPost, - response -> { - if(response.getCode() >=200 && response.getCode() < 300){ - HttpEntity responseEntity = response.getEntity(); - - - JsonElement responseAsJson = new Gson().fromJson(EntityUtils.toString(responseEntity), JsonElement.class); - JsonObject responseObject = responseAsJson.isJsonObject() ? responseAsJson.getAsJsonObject() : null; - if(responseObject != null && responseObject.get("accessToken") != null){ - return responseObject.get("accessToken").getAsString(); + CloseableHttpClient client = HttpClients.createSystem(); + projectScopeToken = client.execute(httpPost, + response -> { + if (response.getCode() >= 200 && response.getCode() < 300) { + HttpEntity responseEntity = response.getEntity(); + + JsonElement responseAsJson = new Gson().fromJson(EntityUtils.toString(responseEntity), + JsonElement.class); + JsonObject responseObject = responseAsJson.isJsonObject() ? responseAsJson.getAsJsonObject() + : null; + if (responseObject != null && responseObject.get("accessToken") != null) { + return responseObject.get("accessToken").getAsString(); + } + } - - } - return null; - }); - if(projectScopeToken == null){ - throw new PSTGenerationException("getProjectScopedToken : Could not retrieve accessToken from "+(apiGatewayUrl + AuthProviderConstants.projectScopeTokenApiPath)); + return null; + }); + if (projectScopeToken == null) { + throw new PSTGenerationException("getProjectScopedToken : Could not retrieve accessToken from " + + (apiGatewayUrl + AuthProviderConstants.projectScopeTokenApiPath)); } - }catch(AccessTokenGenerationException accessTokenGenerationException){ + } catch (AccessTokenGenerationException accessTokenGenerationException) { throw new PSTGenerationException(accessTokenGenerationException.getMessage()); - }catch(Exception exception){ + } catch (Exception exception) { throw new PSTGenerationException(exception.getMessage()); } return projectScopeToken; } - - /** + /** * This method generates a signed jwt for an iota session * * @param iotaConfigId @@ -211,60 +217,65 @@ private String getProjectScopedToken() throws PSTGenerationException{ */ public IotaJwtOutput signIotaJwt(String iotaConfigId, String did, String iotaSessionId) throws Exception { - String iotaTokenId = "token/"+tokenId; + String iotaTokenId = "token/" + tokenId; String iotaSessionID = (iotaSessionId != null) ? iotaSessionId : UUID.randomUUID().toString(); - String iotaJwt = JwtUtil.signIotaPayload(iotaTokenId, did, privateKey, passphrase, keyId, projectId, iotaConfigId, iotaSessionID); + String iotaJwt = JwtUtil.signIotaPayload(iotaTokenId, did, privateKey, passphrase, keyId, projectId, + iotaConfigId, iotaSessionID); return new IotaJwtOutput(iotaSessionID, iotaJwt); } - /** - * This class provides a way to pass configurations to the AuthProvider - * It also helps to build an instance of AuthProvider which uses - * these configurations - * - * - */ - public static class Configurations{ + * This class provides a way to pass configurations to the AuthProvider + * It also helps to build an instance of AuthProvider which uses + * these configurations + * + * + */ + public static class Configurations { private String projectId; private String tokenId; private String privateKey; private String keyId; private String passphrase; - public Configurations projectId(String projectId){ + public Configurations projectId(String projectId) { this.projectId = projectId; return this; } - public Configurations tokenId(String tokenId){ + + public Configurations tokenId(String tokenId) { this.tokenId = tokenId; return this; } - public Configurations privateKey(String privateKey){ + + public Configurations privateKey(String privateKey) { this.privateKey = privateKey; return this; } - public Configurations keyId(String keyId){ + + public Configurations keyId(String keyId) { this.keyId = keyId; return this; } - public Configurations passphrase(String passphrase){ + + public Configurations passphrase(String passphrase) { this.passphrase = passphrase; return this; } /** - * This method builds an instance of AuthProvider with the + * This method builds an instance of AuthProvider with the * values passed through {@link Configuration} * * @return * @throws ConfigurationException */ - public AuthProvider build() throws ConfigurationException{ - if(this.projectId == null || this.privateKey == null || this.tokenId == null){ - throw new ConfigurationException("Cannot create Auth provider without projectId, privateKey and toeknId"); + public AuthProvider build() throws ConfigurationException { + if (this.projectId == null || this.privateKey == null || this.tokenId == null) { + throw new ConfigurationException( + "Cannot create Auth provider without projectId, privateKey and toeknId"); } return new AuthProvider(this); } @@ -276,11 +287,12 @@ public AuthProvider build() throws ConfigurationException{ * @return * @throws ConfigurationException */ - public AuthProvider buildWithEnv() throws ConfigurationException{ + public AuthProvider buildWithEnv() throws ConfigurationException { - if(this.projectId != null || this.privateKey != null || this.tokenId != null || this.passphrase != null || this.privateKey != null){ - throw new ConfigurationException("Please do not pass configurations values while using buildWithEnv. "+ - " These values will picked from .env. Alternatively you may use build() in order to explicitly pass values"); + if (this.projectId != null || this.privateKey != null || this.tokenId != null || this.passphrase != null + || this.privateKey != null) { + throw new ConfigurationException("Please do not pass configurations values while using buildWithEnv. " + + " These values will picked from .env. Alternatively you may use build() in order to explicitly pass values"); } EnvironmentUtil envUtil = new EnvironmentUtil(); @@ -290,75 +302,93 @@ public AuthProvider buildWithEnv() throws ConfigurationException{ this.tokenId = envUtil.getValueFromEnvConfig(AuthProviderConstants.tokenIdPropertyNameinEnv); this.privateKey = envUtil.getValueFromEnvConfig(AuthProviderConstants.privateKeyPropertyNameinEnv); - if(this.projectId == null || this.privateKey == null || this.tokenId == null){ - throw new ConfigurationException("Cannot create Auth provider without projectId, privateKey and tokenId. Please ensure these values are configured in .env"); + if (this.projectId == null || this.privateKey == null || this.tokenId == null) { + throw new ConfigurationException( + "Cannot create Auth provider without projectId, privateKey and tokenId. Please ensure these values are configured in .env"); } return new AuthProvider(this); } } - - /** + /** * @return String */ public String getProjectId() { return projectId; } - /** + + /** * @return String */ public String getTokenId() { return tokenId; } - /** + + /** * @return String */ public String getPrivateKey() { return privateKey; } - /** + + /** * @return String */ public String getKeyId() { return keyId; } - /** + + /** * @return String */ public String getPassphrase() { return passphrase; } - /** + + /** * @return String */ public String getApiGatewayUrl() { return apiGatewayUrl; } - /** + + public void setApiGatewayUrl(String apiGatewayUrl) { + this.apiGatewayUrl = apiGatewayUrl; + } + + /** * @return String */ public String getTokenEndPoint() { return tokenEndPoint; } - /** + + public void setTokenEndPoint(String tokenEndPoint) { + this.tokenEndPoint = tokenEndPoint; + } + + /** * @return String */ public String getPublicKey() { return publicKey; } - /** + + /** * @return String */ public void setPublicKey(String publicKey) { this.publicKey = publicKey; } - /** + + /** * @return String */ public String getProjectScopeToken() { return projectScopeToken; } - /** + + /** * @return String */ public void setProjectScopeToken(String projectScopeToken) { diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/AuthProviderConstants.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/AuthProviderConstants.java index a9a00edf0..a1bc98c34 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/AuthProviderConstants.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/AuthProviderConstants.java @@ -12,5 +12,8 @@ public interface AuthProviderConstants { public static final String passphrasePropertyNameinEnv = "PASSPHRASE"; public static final String privateKeyPropertyNameinEnv = "PRIVATE_KEY"; public static final String projectIdPropertyNameinEnv = "PROJECT_ID"; - + + // Error messages: + public static final String couldNotDerivePrivateKeyErrorMsg = "Could not derive private key out of the configurations."; + } diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java index c7ab92683..7a7bbb2a8 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java @@ -40,21 +40,21 @@ import io.jsonwebtoken.security.Jwks; /** -* This class provides utility functions required -* by {@link AuthProvider} related for -* processing(creating, signing, validation) of JWT -* -* @author Priyanka -* -*/ + * This class provides utility functions required + * by {@link AuthProvider} related for + * processing(creating, signing, validation) of JWT + * + * @author Priyanka + * + */ public class JwtUtil { - /** + /** * This method builds a JSON web token for the provided claims. - * It then signs the token with the provided privatekey using RS256. + * It then signs the token with the provided privatekey using RS256. * - * In case of an encrypted private key; this method expects to receive + * In case of an encrypted private key; this method expects to receive * the passphrase which was used during generation of the key pair. * * @param tokenId @@ -63,10 +63,11 @@ public class JwtUtil { * @param passphrase * @param keyId * @return String - * @throws JwtGenerationException in case the private key could not be processed or jwt generation failed + * @throws JwtGenerationException in case the private key could not be processed + * or jwt generation failed */ public static String signPayload(String tokenId, String audience, String privateKeyString, String passphrase, - String keyId) throws JwtGenerationException{ + String keyId) throws JwtGenerationException { String signedJwtForClaims = null; long issueTimeInSeconds = System.currentTimeMillis() / 1000; @@ -87,13 +88,13 @@ public static String signPayload(String tokenId, String audience, String private } return signedJwtForClaims; } - - /** + + /** * This method builds a JSON web token for the provided claims specific * to an iota request. - * It then signs the token with the provided privatekey using RS256. + * It then signs the token with the provided privatekey using RS256. * - * In case of an encrypted private key; this method expects to receive + * In case of an encrypted private key; this method expects to receive * the passphrase which was used during generation of the key pair. * * @param tokenId @@ -105,11 +106,12 @@ public static String signPayload(String tokenId, String audience, String private * @param iotaConfigId * @param iotaSessionId * @return String - * @throws JwtGenerationException in case the private key could not be processed or jwt generation failed + * @throws JwtGenerationException in case the private key could not be processed + * or jwt generation failed */ public static String signIotaPayload(String tokenId, String audience, String privateKeyString, String passphrase, String keyId, String projectId, String iotaConfigId, String iotaSessionId) throws JwtGenerationException { - + String signedJwtForClaims = null; long issueTimeInSeconds = System.currentTimeMillis() / 1000; Map claims = new HashMap<>(); @@ -136,47 +138,52 @@ public static String signIotaPayload(String tokenId, String audience, String pri return signedJwtForClaims; } - - /** + /** * This method fetches the signature verification key, required to - * validate the jws for a projectScopeToken and converts it to + * validate the jws for a projectScopeToken and converts it to * {@link java.security.PublicKey} * * @param apiGatewayUrl * @return PublicKey - * @throws InvalidPublicKeyException in case the public key could not be retrieved/extracted + * @throws InvalidPublicKeyException in case the public key could not be + * retrieved/extracted */ public static PublicKey fetchPublicKey(String apiGatewayUrl) throws InvalidPublicKeyException { PublicKey publicKey = null; - try{ + try { final HttpGet httpGet = new HttpGet(apiGatewayUrl + AuthProviderConstants.publicKeyPath); - httpGet.setHeader(AuthProviderConstants.contentTypeHeader, AuthProviderConstants.applicationJsonContentType); + httpGet.setHeader(AuthProviderConstants.contentTypeHeader, + AuthProviderConstants.applicationJsonContentType); - CloseableHttpClient client = HttpClients.createDefault(); - publicKey = client.execute(httpGet, - response -> { - if(response.getCode() >=200 && response.getCode() < 300){ - String jwkFromResponse = decodeRespose(response.getEntity()); - Jwk jwk = Jwks.parser().build() - .parse(jwkFromResponse); - if(jwk != null){ - return (PublicKey)jwk.toKey(); + CloseableHttpClient client = HttpClients.createSystem(); + publicKey = client.execute(httpGet, + response -> { + if (response.getCode() >= 200 && response.getCode() < 300) { + HttpEntity entity = response.getEntity(); + String jwkFromResponse = decodeRespose(entity); + Jwk jwk = Jwks.parser().build() + .parse(jwkFromResponse); + if (jwk != null) { + return (PublicKey) jwk.toKey(); + } + } else { + String resp = decodeRespose(response.getEntity()); + System.out.println(resp.toString()); } - } - return null; - }); - }catch(Exception exception){ - throw new InvalidPublicKeyException("Could not retreive/ extract the public "+ - "key required to validate projectScopeToken "+exception.getMessage()); + return null; + }); + } catch (Exception exception) { + throw new InvalidPublicKeyException("Could not retreive/ extract the public " + + "key required to validate projectScopeToken " + exception.getMessage()); } return publicKey; } - - /** + + /** * This method validates the projectScopeToken. * Along with validating if the token is malformed or expired; - * it also verifies the token signature using the - * signature verification public key. + * it also verifies the token signature using the + * signature verification public key. * * If the public key could not be retrieved for any reason; * the method would consider the token as invalid. @@ -188,13 +195,13 @@ public static PublicKey fetchPublicKey(String apiGatewayUrl) throws InvalidPubli public static boolean validProjectTokenPresent(String token, String apiGatewayUrl) { try { PublicKey publicKey = fetchPublicKey(apiGatewayUrl); - if(publicKey == null){ + if (publicKey == null) { throw new Exception("Could not retrieve public key for token validation"); } Jwts.parser().verifyWith(publicKey).build().parse(token); } catch (InvalidPublicKeyException iPublicKeyException) { return false; - }catch (ExpiredJwtException ejException) { + } catch (ExpiredJwtException ejException) { return false; } catch (MalformedJwtException mjException) { return false; @@ -204,12 +211,11 @@ public static boolean validProjectTokenPresent(String token, String apiGatewayUr return true; } - - /** - * This method converts the private key string passed to a - * {@link java.security.PrivateKey}. In case a passphrase is passed, + /** + * This method converts the private key string passed to a + * {@link java.security.PrivateKey}. In case a passphrase is passed, * the private key is treated as encrypted and processed accordingly. - * This passphrase should be same as the one used to create the + * This passphrase should be same as the one used to create the * public-private key pair * * @param privateKeyString @@ -217,7 +223,8 @@ public static boolean validProjectTokenPresent(String token, String apiGatewayUr * @return PrivateKey * @throws InvalidPrivateKeyException */ - private static PrivateKey derivePrivateKey(String privateKeyString, String passphrase) throws InvalidPrivateKeyException { + private static PrivateKey derivePrivateKey(String privateKeyString, String passphrase) + throws InvalidPrivateKeyException { PrivateKey privateKey = null; try { @@ -228,40 +235,41 @@ private static PrivateKey derivePrivateKey(String privateKeyString, String passp } } catch (Exception exception) { throw new InvalidPrivateKeyException( - "Could not derive private key out of the configurations. Exception : " + exception.toString()); + AuthProviderConstants.couldNotDerivePrivateKeyErrorMsg + + " Exception : " + exception.toString()); } return privateKey; } - - /** + /** * This method generates a JSON payload representing all the - * claims passed. It then signs this payload with the + * claims passed. It then signs this payload with the * {@link java.security.PrivateKey}, creating a json web * signature (JWS) and then builds the JWT as a URL-safe * string - * + * * @param privateKey * @param claims * @return String - * @throws JwtGenerationException when the jwt generation fails. For instance if the private key is insufficient + * @throws JwtGenerationException when the jwt generation fails. For instance if + * the private key is insufficient */ - private static String generateJwt(PrivateKey privateKey, Map claims) throws JwtGenerationException{ + private static String generateJwt(PrivateKey privateKey, Map claims) throws JwtGenerationException { String jwToken = null; - try{ + try { jwToken = Jwts.builder() - .claims(claims) - .signWith(privateKey, Jwts.SIG.RS256) - .compact(); + .claims(claims) + .signWith(privateKey, Jwts.SIG.RS256) + .compact(); - }catch(Exception exception){ - throw new JwtGenerationException(" Could not generate the JWT representing the claims. Exception"+ exception.getMessage()); + } catch (Exception exception) { + throw new JwtGenerationException( + " Could not generate the JWT representing the claims. Exception" + exception.getMessage()); } - return jwToken; + return jwToken; } - - /** + /** * This method converts a private key string to * {@link java.security.PrivateKey} object * @@ -277,8 +285,7 @@ private static PrivateKey getPrivateKeyFromString(String privateKeyPEM) throws E return keyFactory.generatePrivate(keySpec); } - - /** + /** * This method converts an encrypted private key string to * {@link java.security.PrivateKey} object * The passphrase which was used to generate the public-private @@ -306,10 +313,9 @@ private static PrivateKey getEncryptedPrivateKeyFromString(String encryptedPriva return keyFactory.generatePrivate(pkcs8KeySpec); } - - /** - * This method cleans up the private key string which is required to - * decode the key material + /** + * This method cleans up the private key string which is required to + * decode the key material * * @param privateKey * @return String @@ -320,8 +326,7 @@ private static String extractPrivateKeyContent(String privateKey) { .replace("\\n", "").trim(); } - - /** + /** * This method processes the response from the public key API call and extracts * the first key listed from the response * @@ -332,17 +337,20 @@ private static String extractPrivateKeyContent(String privateKey) { * @throws JsonSyntaxException * @throws ParseException */ - private static String decodeRespose(HttpEntity responseEntity) throws IOException, JsonSyntaxException, ParseException{ + private static String decodeRespose(HttpEntity responseEntity) + throws IOException, JsonSyntaxException, ParseException { String extractedJwkFromResponse = null; - if(responseEntity != null){ + if (responseEntity != null) { JsonElement responseAsJson = new Gson().fromJson(EntityUtils.toString(responseEntity), JsonElement.class); - JsonObject responseObject = responseAsJson.isJsonObject() ? responseAsJson.getAsJsonObject() : null; + JsonObject responseObject = responseAsJson.isJsonObject() ? responseAsJson.getAsJsonObject() : null; - if(responseObject != null){ - JsonArray setOfkeys = responseObject.getAsJsonArray("keys") ; - if(setOfkeys != null){ + if (responseObject != null) { + JsonArray setOfkeys = responseObject.getAsJsonArray("keys"); + if (setOfkeys != null) { - JsonObject firstKeysAsObject = (setOfkeys != null && setOfkeys.size() > 0) ? setOfkeys.get(0).getAsJsonObject() : null; + JsonObject firstKeysAsObject = (setOfkeys != null && setOfkeys.size() > 0) + ? setOfkeys.get(0).getAsJsonObject() + : null; extractedJwkFromResponse = (firstKeysAsObject != null) ? firstKeysAsObject.toString() : null; } } diff --git a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/AuthProviderTest.java b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/AuthProviderTest.java index b0d721ead..c1995ff17 100644 --- a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/AuthProviderTest.java +++ b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/AuthProviderTest.java @@ -1,27 +1,180 @@ package com.affinidi.tdk.authProvider; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Paths; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EmptySource; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.givenThat; +import static com.github.tomakehurst.wiremock.client.WireMock.okJson; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; + +import com.affinidi.tdk.authProvider.exception.ConfigurationException; +import com.affinidi.tdk.authProvider.exception.InvalidPublicKeyException; +import com.affinidi.tdk.authProvider.exception.JwtGenerationException; +import com.affinidi.tdk.authProvider.exception.PSTGenerationException; +import com.affinidi.tdk.authProvider.helper.AuthProviderConstants; +import com.affinidi.tdk.authProvider.helper.JwtUtil; public class AuthProviderTest { + @Nested + @DisplayName("setting values to the authProvider configurations") + class ConfigurationsTest { + @Test + void testAuthProviderConfiguration() throws Exception { + AuthProvider provider = new AuthProvider.Configurations() + .projectId("test-project") + .tokenId("test-token") + .privateKey("test-key") + .build(); - @Test - void testFetchProjectScopedToken() { - + assertNotNull(provider); + assertEquals("test-project", provider.getProjectId()); + assertEquals("test-token", provider.getTokenId()); + assertEquals("test-key", provider.getPrivateKey()); + } } - @Test - void testGetUserAccessToken() { - - } + @WireMockTest(proxyMode = true) + @Nested + @DisplayName("fetchProjectScopedToken method") + class FetchProjectScopedTokenTest { + @ParameterizedTest + @DisplayName("given an invalid private-key and a empty or non-empty passphrase, the it throws") + @EmptySource + @ValueSource(strings = { "complicated-word" }) + void givenInvalidPrivateKey_thenThrows(String phrase) { + Exception exception = assertThrows(PSTGenerationException.class, () -> { + AuthProvider provider = new AuthProvider.Configurations() + .projectId("test-project") + .tokenId("test-token") + .privateKey("invalid-key") + .passphrase(phrase) + .build(); + provider.fetchProjectScopedToken(); + }); - @Test - void testShouldRefreshToken() { - - } + assertTrue(exception.getMessage().startsWith("Could not derive private key out of the configurations.")); + } - @Test - void testSignIotaJwt() { - - } + @Test + @DisplayName("given the failing api-key endpoint call and failing to sign the payload, then it throws") + void givenInvalidApiGatewayUrl_thenThrows() { + // arrange + String mockErrorMessage = "mock-exception-message"; + MockedStatic utilsMock = Mockito.mockStatic(JwtUtil.class); + utilsMock.when(() -> JwtUtil.fetchPublicKey(any())).thenThrow(InvalidPublicKeyException.class); + utilsMock.when(() -> JwtUtil.signPayload(any(), any(), any(), any(), any())) + .thenThrow(new JwtGenerationException(mockErrorMessage)); + + // act + Exception exception = assertThrows(PSTGenerationException.class, () -> { + AuthProvider provider = new AuthProvider.Configurations() + .projectId("test-project") + .tokenId("test-token") + .privateKey("test-key") + .build(); + provider.fetchProjectScopedToken(); + }); + + // assert + assertEquals(mockErrorMessage, exception.getMessage()); + + // cleanup + utilsMock.close(); + } + + @Test + @DisplayName("given an invalid private-key, when the api-key endpoint returns a response and it fails to sign the payload, then it throws") + void givenInvalidPrivateKey_AndApiKeyResponse_thenThrows(WireMockRuntimeInfo wmRuntimeInfo) + throws IOException, URISyntaxException { + // arrange + String apiUrl = wmRuntimeInfo.getHttpBaseUrl(); + URI uri = new URI(apiUrl); + String host = uri.getHost(); + String apiKeyJson = new String( + Files.readAllBytes(Paths.get( + "src/test/java/com/affinidi/tdk/authProvider/resources/api-key-response.json"))); + givenThat(get(AuthProviderConstants.publicKeyPath) + .withHost(equalTo(host)) + .willReturn(okJson(apiKeyJson))); + + // act + Exception exception = assertThrows(PSTGenerationException.class, () -> { + AuthProvider provider = new AuthProvider.Configurations() + .projectId("test-project") + .tokenId("test-token") + .privateKey("test-key") + .passphrase("") + .build(); + provider.setApiGatewayUrl(apiUrl); + provider.setProjectScopeToken("test-project-scope-token"); + provider.fetchProjectScopedToken(); + }); -} + // assert + assertTrue(exception.getMessage() + .startsWith(AuthProviderConstants.couldNotDerivePrivateKeyErrorMsg + " Exception : ")); + } + + @Test + @DisplayName("happy path: given a validprivate-key, when all the endponts succesfully returns 200, then it returns a JWT") + void givenValidApiKeyResponse_AndSuccessfulApiCalls_thenReturnsAJWT(WireMockRuntimeInfo wmRuntimeInfo) + throws IOException, URISyntaxException, ConfigurationException { + // arrange + String apiUrl = wmRuntimeInfo.getHttpBaseUrl(); + URI uri = new URI(apiUrl); + String host = uri.getHost(); + String fakeTokenUrl = apiUrl + "/auth-token"; + String testPrivateKey = new String( + Files.readAllBytes(Paths.get( + "src/test/java/com/affinidi/tdk/authProvider/resources/test-private-key.txt"))); + AuthProvider provider = new AuthProvider.Configurations() + .projectId("test-project") + .tokenId("test-token") + .privateKey(testPrivateKey) + .passphrase("") + .build(); + provider.setApiGatewayUrl(apiUrl); + provider.setTokenEndPoint(fakeTokenUrl); + givenThat(post("/auth-token") + .withHost(equalTo(host)) + .willReturn(okJson("{access_token: \"some-access-token\"}"))); + givenThat(post(AuthProviderConstants.projectScopeTokenApiPath).withHost(equalTo(host)) + .willReturn(okJson("{accessToken: \"some-project-scope-token\"}"))); + + // act and assert + String token = assertDoesNotThrow(() -> provider.fetchProjectScopedToken()); + assertEquals("some-project-scope-token", token); + } + + @Test + void testGetUserAccessToken() { + + } + + @Test + void testShouldRefreshToken() { + + } + + @Test + void testSignIotaJwt() { + + } + } +} \ No newline at end of file diff --git a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/resources/api-key-response.json b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/resources/api-key-response.json new file mode 100644 index 000000000..8ccca4310 --- /dev/null +++ b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/resources/api-key-response.json @@ -0,0 +1,49 @@ +{ + "keys": [ + { + "kid": "a622a999-9846-48cf-a470-22759e1f435a", + "alg": "ES256", + "use": "sig", + "kty": "EC", + "crv": "P-256", + "x": "b3kdYEBrlWjQwY55F8MhXC97pwkjTpcQZZ09oDDBK4c", + "y": "wlopQwIPWuT55M3ZfCDZdoBs1nh2kwEvzPjnkakf96U" + }, + { + "use": "sig", + "alg": "ES256", + "kty": "EC", + "x": "-ayYp8f4Yx47G-Fet4Yuib8rdqZ_XGJZpT60GPocpWw", + "y": "TAkQCuTywyl7hwmNN_qHJWKqFE0MIPOf2JAl2o2gPOI", + "crv": "P-256", + "kid": "03f8de27-d281-47a0-bf8a-affdff3492d6" + }, + { + "kid": "9e79e4bc-d93b-48d3-934f-c878ee83d683", + "kty": "EC", + "x": "fEJH7KRt2tjtAF4pdJEJX0dKXhCNfKtwswfJN11uQZk", + "y": "dROiiihrMI-jeCTWbcG6IMJM2oGQuP4gjh0oiCi0KgA", + "crv": "P-256", + "use": "sig", + "alg": "ES256" + }, + { + "kty": "EC", + "x": "9fajsaREKgJxugEZkms44nR4mJSvVrdYdqFe_-_eGxI", + "y": "2ZIdxAoW5NeHUmqLhAwGpKuikm62dFLE1lIhrt5TdxQ", + "crv": "P-256", + "kid": "12f6aa3d-e692-4d20-a629-52bb14ced005", + "alg": "ES256", + "use": "sig" + }, + { + "use": "sig", + "alg": "ES256", + "kty": "EC", + "x": "tvpex4bxW4K35o_eEqKEf2-KjnzvVn2kFAp6j7kBxZ0", + "y": "tRKgJ3faS92y3dxK97COcaLvNiS1VHyonaeRyHj2XHA", + "crv": "P-256", + "kid": "576b97f6-1ae3-495f-8157-4877cd3036b0" + } + ] +} \ No newline at end of file diff --git a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/resources/test-private-key.txt b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/resources/test-private-key.txt new file mode 100644 index 000000000..4c6181bc4 --- /dev/null +++ b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/resources/test-private-key.txt @@ -0,0 +1 @@ +-----BEGIN PRIVATE KEY-----\nMIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCvQ5WHhgFV4Swp\nqVOEi/tNQtjcKqk44KIoVx+pTMR4DhwRUcHNhFER8HDIJCUT7/5RZAJdRWSnyWJR\n8D7xXJoaicji0iQaUTGppSh85MSW4GJ8aR7sZaRb8+iLq5DmfG4EGaAU3LmaXBpN\n1TBQ7YXGckJXTeJbTO2K7l9QXFTx9tICqMb/lsi1hxKQ9WmdzbuHrO9qotgfQVAA\nE7FI326UOg5zgrTIyGF6HzQZXJV99wtxZcWPCsSHcHR2lE+5IXqHldzmijnMX2Nr\nM/YFaNQwES6iKXff51MwtXvGdtHcwS3tSNmHswSEFqSYVbtsxBb3Xh73w5362w+9\nHhXRASSs0dQDDgt1l4LqdtEF/24nCItdQjc50TW49rFs9/h1yTmY+LY8NZLy0JzX\nUFJzxYzWXrft7NKjLcY723RX2XJ07rFdAbmfKG+TV0bmDR8Kq8m/isuaDv3h6UXi\nznrc9Rkzojro9dFzxWgWbGx4zfIEQJz68PrrtRfigdjVqlivvmQli71KZ2KfcdRf\n2a3cu38zRUVkR52yUw56q8q36FFPLTKD/0G/w7B0huU74qvBegBMkAJbwWUkNuER\nXFmXJDfVWyX05DITV/8w7+jnbxeiLlcsxI1elylbub/6UbGdo3f8S2107d0VueQv\n3q1ukZH4JbNFljYpgGOzorb+dQeZqQIDAQABAoICAAECq5IwSGywKNAwAjkW54LU\nBP+2nUAkh2V3zx5rlgxiecXzHzdp8QpgmlMvu35TGWJrEwUU+StrIqPPS0Wan5F8\nmYm5OsJjanJhxIsnydGxhyUpeVH/6gPVCFJZ0bLfz8XlR0IniEhmtIjIrF0jFBeH\nTBudvtWA6t5XWSRToUe91mBz4aXSlDimXTSJvatOZtY1kzHIW4+ekZwBK7n8U8e1\n/U8BghJQRBjvT3O/+rqoWcq0aABW8w1xgrJg+4QD4zrJ4eVQXFyccNcgNxBj636V\ncsaLj6iHow1Pq+AtNNuIH5V+ddvtlnPpKizDUxp27PJN0Bw55qduFjf5vELMC2Po\nkDhv/NWZNi4XQ4SXl5LwleYgkNQqy0bbN881OD37WZgcEqo9liwRaulnl2E0j+tG\nwnUU0hPwt+jR/y+5C/WuKb6afB5/OGDo72AfJzcQENm1yjMZBTx2i3e24miq3k9U\n15nqXMcYxbABC3+3iZvik/vCH/oqwxPCXjrAUHP6bDYCRbwUWh20E71rhMggu5Kh\n8p6pi3y84cVEOa5sBxZ8T2VhlbxNh0u7XiWFIiB+jg/wvu9+W133GCClOnyfXU34\nkzeGIWsDBhKrpKB3CI0nVcxmssC256c2P4K7Y8vqMBWrhAxLX44HHRjx3ojTjWo1\nu9idEyb9sEw8IM3kbWgJAoIBAQDf9dDN7FM0+TSWfIP+CuujFTYX7wwiFzM2ZSWX\n1gCQviTernv9KlTN/z3fNkfNjPpUpb6qxs5Obr70EC8q1MzBHOO/62MWA/ojLNjQ\nzxaaMWqNwS4bOIdZpa9ykV+LNud6Uzr0CqZLJU1bC9ZV82fuU3CXXQ6GoVYmilrd\nYIuyV7dDpDUyUiPX2I+6IUELUl5SdcKjv/8B9Gg1aFvJXLrCX1RX5D99SPAbjreA\n+ysaOVgomKBmg/Qp+ntnEupOai/9Zt9uRXxxRNgSfvuZ1eo32/5ibSMV4iayilXa\nfTIrfeYis/BoXF8hqE3Qlws2wuYL7/C1otWe05ErdXOJnk09AoIBAQDIVlj0nePo\nDUnYdu896mGR1lVgEFlNejpvGxgKWIZvibWY/0uC/34ay5EyPoaS2QKJ2y9ojvxr\nzwx+l7rC7nhT6Y0W2zu6pjsmlVOuXSHWtQ3APEwvKzkO02fbNR09aDQc4k+Dgkj7\ntKbri7UTnBR1m9xCiIyd+uGm3FhUOjq3VwHio73bm8tO7VxQMitRhXS5DGxXUJQ8\n/t6iAilGsrxZSfdRPNQ5k7hd3o3nSvtRgqN0Qe7gVoYgCPpyFMAiE6hiP8AxaxTa\nBeAAjsY7x/jbwCGDG8ZLnaDTOfJzdXvaco98JCJUmH6VNok0uMEF87TWTx9nkOlj\nV/MzXKF14VzdAoIBAQCreGhbEWKPf3G8+i6Q1cmd/kKBRpvyifLC5eRHgSjDqWFP\nQSZbKgruAUtc2BFXlmWfY5s846PXN0FqWe3TDESMYlMZgN5HX9onDlIeZT+35Lfh\nOpPTcY9nmsxemmEdlHbcGFddu72gcTntyH1dJ1Rei3H74dqaR3JPZcS2FBJBBJmi\n51l3Yp5gx0UbzBQh1/mxhsn0V66lz3vt0C3eWOoAob5Q4MefY7W6U6W50hQpKtiN\nlHXSp5rMfSP4Qeo1CWYezKGqqvbhPkV0u9Zk7Pw7smrs7wRa3+Ci9/lNpGdCF02Z\nKk2ZiYeonFSfrNWAIu7BGv8gAkEuLmvKa01/gz09AoIBAQCJiL4JMVwFMxo/QWd9\nWishs8No79BfIEp9fFxEvgGNET1ai+mLRVuNibNkiwhYSCM2AQPLBF7GpJ4vSnss\n2ApDkVMLHfcjbHuvQVTxn35bqHCISN0EhOfnBbuHAi/QuHkOW0+7OPZh1uPBUWg+\nzig9lYLhKl1fF51SsHLzYAZvV4wzy7XLzmkFBm8Zn1ed38ECSUUXrHoYZeDx2An+\nPzaPFfh0DQ6leIRZ7S/+WbuvecA9UAZufPgXhhJpv1UoD9bJnHqVcq4d/2qI93ug\nDukJ9B0NpUO9JboDSQTpvv4IOh3HXAc1jHtadNY9G5loPKcpeahVsWaIjRipai5u\nfwkBAoIBADk2XzcQc79OKlalRIaIa1OuuyP9FLx1M8rhNE5ez7w/yF9i8wvFaOb4\nbjDKUn9BRcyQB3n6hbx99o3wKawdS7ZfplI1U3KJ3yq0EGsF7c8OKNgnWp68QFp7\nLT3RAeZV9XTkEfQcwZOy4tWxlu2pgBZzc+ZKmuH8P9tP6fJf/7WdXcweRu/VeQHt\n9kNwX1n9W9Ga5mGP6fL2efnZgN6VwtSoTeH5rKsNGdC/D9N2baWU2BxoXkzL27aZ\nf22QXdUj5bVoL6uPjtNHLuBcwibRFiIkj09umKG89xrOrR/y85ONv87Vk/vKbs3g\n2SXF2Id7dMeTOTULKZnKC+AH1OGzwN0=\n-----END PRIVATE KEY-----\n \ No newline at end of file From 60ac22bbf917d3b30423c1c5c2fd3f7f37c26723 Mon Sep 17 00:00:00 2001 From: Francis Pineda Date: Mon, 20 Jan 2025 18:55:14 +0800 Subject: [PATCH 10/15] docs: added vault-data-manager-client to the list --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a5ab03816..cc4bcd2d9 100644 --- a/README.md +++ b/README.md @@ -27,12 +27,13 @@ The Affinidi TDK offers the following modules and support to programming languag |[auth-provider](packages/auth-provider/) | 🟢 | 🟢 | 🔴 | [🟢](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/AuthProvider) | 🟢 | |[common](packages/common/) | 🟢 | 🟢 | 🔴 | [🟢](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Common) | 🟢 | |**Clients** | | | | -|credential-issuance-client | [🟢 Link](clients/typescript/credential-issuance-client/) | [🟢 Link](clients/python/credential_issuance_client/) | [🟡 Link](clients/dart/credential_issuance_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/CredentialIssuanceClient) | [🟢 Link](clients/java/credential-issuance-client/) | -|credential-verification-client | [🟢 Link](clients/typescript/credential-verification-client/) | [🟢 Link](clients/python/credential_verification_client/) | [🟡 Link](clients/dart/credential_verification_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/CredentialVerificationClient) | [🟢 Link](clients/java/credential-verification-client/) | -|iam-client | [🟢 Link](clients/typescript/iam-client/) | [🟢 Link](clients/python/iam_client/) | [🟡 Link](clients/dart/iam_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/IamClient) | [🟢 Link](clients/java/iam-client/) | -|iota-client | [🟢 Link](clients/typescript/iota-client/) | [🟢 Link](clients/python/iota_client/) | [🟡 Link](clients/dart/iota_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/IotaClient) | [🟢 Link](clients/java/iota-client/) | -|login-configuration-client | [🟢 Link](clients/typescript/login-configuration-client/) | [🟢 Link](clients/python/login_configuration_client/) | [🟡 Link](clients/dart/login_configuration_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/LoginConfigurationClient) | [🟢 Link](clients/java/login-configuration-client/) | -|wallets-client | [🟢 Link](clients/typescript/wallets-client/) | [🟢 Link](clients/python/wallets_client/) | [🟡 Link](clients/dart/wallets_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/WalletsClient) | [🟢 Link](clients/java/wallets-client/) | +|credential-issuance-client | [🟢 Link](clients/typescript/credential-issuance-client/) | [🟢 Link](clients/python/credential_issuance_client/) | [🟡 Link](clients/dart/credential_issuance_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/CredentialIssuanceClient) | [🟢 Link](clients/java/credential.issuance.client/) | +|credential-verification-client | [🟢 Link](clients/typescript/credential-verification-client/) | [🟢 Link](clients/python/credential_verification_client/) | [🟡 Link](clients/dart/credential_verification_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/CredentialVerificationClient) | [🟢 Link](clients/java/credential.verification.client/) | +|iam-client | [🟢 Link](clients/typescript/iam-client/) | [🟢 Link](clients/python/iam_client/) | [🟡 Link](clients/dart/iam_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/IamClient) | [🟢 Link](clients/java/iam.client/) | +|iota-client | [🟢 Link](clients/typescript/iota-client/) | [🟢 Link](clients/python/iota_client/) | [🟡 Link](clients/dart/iota_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/IotaClient) | [🟢 Link](clients/java/iota.client/) | +|login-configuration-client | [🟢 Link](clients/typescript/login-configuration-client/) | [🟢 Link](clients/python/login_configuration_client/) | [🟡 Link](clients/dart/login_configuration_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/LoginConfigurationClient) | [🟢 Link](clients/java/login.configuration.client/) | +|vault-data-manager-client | [🟢 Link](clients/typescript/vault-data-manager-client/) | [🟢 Link](clients/python/vault_data_manager_client/) | [🟡 Link](clients/dart/vault_data_manager_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/VaultDataManagerClient) | [🟢 Link](clients/java/vault.data.manager.client/) | +|wallets-client | [🟢 Link](clients/typescript/wallets-client/) | [🟢 Link](clients/python/wallets_client/) | [🟡 Link](clients/dart/wallets_client/) | [🟢 Link](https://github.com/affinidi/affinidi-tdk-php/tree/main/src/Clients/WalletsClient) | [🟢 Link](clients/java/wallets.client/) | |**Libraries** | | | | |[iota-browser](libs/iota-browser/) | 🟢 | 🔴 | 🔴 | 🔴 | 🔴 | |[iota-core](libs/iota-core/) | 🟢 | 🟢 | 🔴 | 🔴 | 🟢 | @@ -40,7 +41,7 @@ The Affinidi TDK offers the following modules and support to programming languag
🟢 Supported
🟡 Experimental
-🔴 Not supported +🔴 Not supported ## Documentation From 87a1867bb1f4db08ccdca57cf400090dedb11a44 Mon Sep 17 00:00:00 2001 From: Priyanka Sharma Date: Tue, 21 Jan 2025 00:39:17 +0800 Subject: [PATCH 11/15] Manual Integration tests and fix for authprovider junits --- packages/java/auth.provider/pom.xml | 6 - .../tdk/authProvider/AuthProvider.java | 5 +- packages/java/common/pom.xml | 5 + .../affinidi/tdk/common/EnvironmentUtil.java | 30 +++-- tests/integration/java/reference/.env.example | 7 ++ tests/integration/java/reference/pom.xml | 114 +++++++++++++++++ .../com/affinidi/reference/TdkTestUtil.java | 116 ++++++++++++++++++ 7 files changed, 267 insertions(+), 16 deletions(-) create mode 100644 tests/integration/java/reference/.env.example create mode 100644 tests/integration/java/reference/pom.xml create mode 100644 tests/integration/java/reference/src/main/java/com/affinidi/reference/TdkTestUtil.java diff --git a/packages/java/auth.provider/pom.xml b/packages/java/auth.provider/pom.xml index f97602a6f..12c74acd1 100644 --- a/packages/java/auth.provider/pom.xml +++ b/packages/java/auth.provider/pom.xml @@ -63,12 +63,6 @@ ${junit-version} test - - org.junit.jupiter - junit-jupiter-engine - ${junit-version} - test - io.github.cdimascio dotenv-java diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java index dd121ddb2..871a803f6 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java @@ -26,7 +26,8 @@ import com.google.gson.JsonObject; /** - * This class provides + * This class provides utility functions in order to generate + * projectScopeToken required to call Affinidi Services. * * * @author Priyanka @@ -44,7 +45,7 @@ public class AuthProvider { private String publicKey; private String projectScopeToken; - AuthProvider(Configurations configurations) { + private AuthProvider(Configurations configurations) { this.projectId = configurations.projectId; this.tokenId = configurations.tokenId; this.privateKey = configurations.privateKey; diff --git a/packages/java/common/pom.xml b/packages/java/common/pom.xml index a93811cb9..581cfe69c 100644 --- a/packages/java/common/pom.xml +++ b/packages/java/common/pom.xml @@ -56,6 +56,11 @@ jersey-common 3.1.7 + + jakarta.ws.rs + jakarta.ws.rs-api + 3.1.0 + org.mockito mockito-core diff --git a/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java b/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java index 2db81a369..fd806069f 100644 --- a/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java +++ b/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java @@ -21,8 +21,11 @@ public class EnvironmentUtil { private static Dotenv properties; private static Logger logger = Logger.getLogger(EnvironmentUtil.class.getName()); - static { - properties = Dotenv.load(); + public static Dotenv getProperties(){ + if(properties == null){ + properties = Dotenv.load(); + } + return properties; } @@ -35,12 +38,17 @@ public class EnvironmentUtil { */ public String getConfiguredEnvironment() { String configuredEnvironment = null; - if (properties != null) { - configuredEnvironment = properties.get(Environment.AFFINIDI_TDK_PROPERTY_NAME); - if (configuredEnvironment == null) { - configuredEnvironment = properties.get(Environment.NEXT_AFFINIDI_TDK_PROPERTY_NAME); + try{ + if (getProperties() != null) { + configuredEnvironment = getProperties().get(Environment.AFFINIDI_TDK_PROPERTY_NAME); + if (configuredEnvironment == null) { + configuredEnvironment = getProperties().get(Environment.NEXT_AFFINIDI_TDK_PROPERTY_NAME); + } } + }catch(Exception exception){ + logger.severe("Could not read .env file for TDK environment configuration"); } + if (configuredEnvironment == null) { logger.severe("Could not find environment details for "+configuredEnvironment+". Defaulting to production"); configuredEnvironment = Environment.PRODUCTION.environmentName; @@ -97,7 +105,13 @@ public String getApiGatewayUrlForEnvironment() { * @return String */ public String getValueFromEnvConfig(String propertyName) { - return properties.get(propertyName); + String propertyValue = null; + try{ + propertyValue = getProperties().get(propertyName); + }catch(Exception exception){ + logger.severe("Could not read .env file for "+propertyName); + } + return propertyValue; } /** @@ -111,8 +125,8 @@ private Environment getEnvironmentDetail() { Environment envDetail = (envName != null) ? Environment.getEnvSpecificDetails(envName) : null; if (envDetail == null) { + logger.severe("Could not find environment details for the specified name " + envName+". Hence defaulting to production"); envDetail = Environment.getEnvSpecificDetails(Environment.PRODUCTION.environmentName); - logger.severe("Could not find environment details for the specified name " + envName); } return envDetail; } diff --git a/tests/integration/java/reference/.env.example b/tests/integration/java/reference/.env.example new file mode 100644 index 000000000..b87060e4d --- /dev/null +++ b/tests/integration/java/reference/.env.example @@ -0,0 +1,7 @@ +AFFINIDI_TDK_ENVIRONMENT=prod + +PROJECT_ID="" +KEY_ID= +TOKEN_ID="" +PASSPHRASE="" +PRIVATE_KEY="" \ No newline at end of file diff --git a/tests/integration/java/reference/pom.xml b/tests/integration/java/reference/pom.xml new file mode 100644 index 000000000..2e30b9a65 --- /dev/null +++ b/tests/integration/java/reference/pom.xml @@ -0,0 +1,114 @@ + + + + 4.0.0 + jar + com.affinidi.tdk + reference + 1.0 + reference + https://github.com/affinidi/affinidi-tdk + + + Apache-2.0 + https://github.com/affinidi/affinidi-tdk/blob/main/LICENSE + repo + + + + + + Affinidi + ... + Affinidi + https://affinidi.com + + + + + UTF-8 + 1.8 + ${java.version} + ${java.version} + 1.0 + 1.0 + + + + + + com.affinidi.tdk + common + ${affinidi-common-version} + + + com.affinidi.tdk + auth.provider + ${affinidi-auth-version} + + + com.affinidi.tdk + wallets.client + 1.0.0 + compile + + + com.affinidi.tdk + credential.issuance.client + 1.0.0 + compile + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + + org.apache.maven.plugins + maven-surefire-plugin + + + maven-failsafe-plugin + 3.5.0 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + diff --git a/tests/integration/java/reference/src/main/java/com/affinidi/reference/TdkTestUtil.java b/tests/integration/java/reference/src/main/java/com/affinidi/reference/TdkTestUtil.java new file mode 100644 index 000000000..631606699 --- /dev/null +++ b/tests/integration/java/reference/src/main/java/com/affinidi/reference/TdkTestUtil.java @@ -0,0 +1,116 @@ + +package com.affinidi.reference; + + + + +import com.affinidi.tdk.authProvider.AuthProvider; + +import com.affinidi.tdk.credential.issuance.client.apis.IssuanceApi; + +import com.affinidi.tdk.wallets.client.ApiClient; +import com.affinidi.tdk.wallets.client.Configuration; +import com.affinidi.tdk.wallets.client.apis.WalletApi; +import com.affinidi.tdk.wallets.client.auth.ApiKeyAuth; +import com.affinidi.tdk.wallets.client.models.WalletsListDto; + + +/** +* To execute this class, +1. Ensure that base package of the working directory contains a valid .env file with values for each property +as present in .env.example +2. Ensure that auth-provider and common package jar is present in either the central maven respository +3. Or you can go to the resective pakage and run mvn clean install : in order to deply these jars in local +maven respository +* +* @author Priyanka +* +*/ + +public class TdkTestUtil { + + public static void main(String arg[]){ + try{ + System.out.println("\n\n============================="); + System.out.println("01. Testing Wallet Client using TDK java packages"); + System.out.println("=============================\n\n"); + testWalletClient(); + System.out.println("\n\n============================="); + System.out.println("Wallet Client testing using TDK java packages completed"); + System.out.println("=============================\n\n"); + + + System.out.println("\n\n============================="); + System.out.println("02. Testing Issuance Client using TDK java packages"); + System.out.println("=============================\n\n"); + testIssuanceClient(); + System.out.println("\n\n============================="); + System.out.println("Issuance Client testing using TDK java packages completed"); + System.out.println("=============================\n\n"); + }catch(Exception exception){ + exception.printStackTrace(); + } + } + + public static void testWalletClient() throws Exception{ + + System.out.println("Getting projectScopenToken before calling wallet API"); + + AuthProvider authProvider = new AuthProvider.Configurations().buildWithEnv(); + String projectToken = authProvider.fetchProjectScopedToken(); + System.out.println("ProjectScopeToken > > > > > : "+projectToken); + + // Creating an API Client using the above token + ApiClient defaultClient = Configuration.getDefaultApiClient(); + ApiKeyAuth ProjectTokenAuth = (ApiKeyAuth) defaultClient.getAuthentication("ProjectTokenAuth"); + ProjectTokenAuth.setApiKey(projectToken); + + WalletApi apiInstance = new WalletApi(defaultClient); + String didType = null; + + System.out.println("Calling listWallets "); + WalletsListDto response = apiInstance.listWallets(didType); + System.out.println("Total wallets : "+response.getWallets().size()); + + + System.out.println("Should AuthProvider refresh the token before any further call ? "+authProvider.shouldRefreshToken()); + } + + public static void testIssuanceClient() throws Exception{ + + System.out.println("Getting projectScopenToken before calling issuance API"); + AuthProvider authProvider = new AuthProvider.Configurations().buildWithEnv(); + String projectToken = authProvider.fetchProjectScopedToken(); + + // Creating an API Client using the above token + com.affinidi.tdk.credential.issuance.client.ApiClient issuanceClient = com.affinidi.tdk.credential.issuance.client.Configuration.getDefaultApiClient(); + com.affinidi.tdk.credential.issuance.client.auth.ApiKeyAuth issueTokenAuth = (com.affinidi.tdk.credential.issuance.client.auth.ApiKeyAuth) issuanceClient.getAuthentication("ProjectTokenAuth"); + issueTokenAuth.setApiKey(projectToken); + + IssuanceApi issuanceApi = new IssuanceApi(issuanceClient); + String projectIdForTesting = "084036a6-a775-478a-9a97-a1323738897f"; + System.out.println(issuanceApi.listIssuance(projectIdForTesting)); + + /* Uncomment to test startIssuance. Provide the relevant projectId and crdential data + StartIssuanceInput startIssuanceInput = new StartIssuanceInput(); + startIssuanceInput.setHolderDid("did:key:zQ3shNb7dEAa7z4LY8eAbPafNM4iSxppwuvndoHkTUUp8Hbt6"); + startIssuanceInput.setClaimMode(ClaimModeEnum.NORMAL); + + StartIssuanceInputDataInner innerData = new StartIssuanceInputDataInner(); + innerData.setCredentialTypeId("AlumniIdentityCard"); + List list = new ArrayList(); + list.add(innerData); + + startIssuanceInput.setData(list); + + System.out.println(issuanceApi.startIssuance("", startIssuanceInput)); + */ + + + + + } + + + +} From 082ce50b6e90582c93093926527ae28a5ffa7e6f Mon Sep 17 00:00:00 2001 From: Robert Kwolek Date: Tue, 21 Jan 2025 12:01:37 +0100 Subject: [PATCH 12/15] fix: util => static --- packages/java/common/.env | 0 packages/java/common/pom.xml | 227 +++++++++--------- .../affinidi/tdk/common/EnvironmentUtil.java | 120 +++++---- .../com/affinidi/tdk/common/VaultUtil.java | 57 +++-- .../tdk/common/EnvironmentUtilTest.java | 148 ++++++------ .../affinidi/tdk/common/VaultUtilTest.java | 151 +++++------- 6 files changed, 331 insertions(+), 372 deletions(-) create mode 100644 packages/java/common/.env diff --git a/packages/java/common/.env b/packages/java/common/.env new file mode 100644 index 000000000..e69de29bb diff --git a/packages/java/common/pom.xml b/packages/java/common/pom.xml index 581cfe69c..d9ea60422 100644 --- a/packages/java/common/pom.xml +++ b/packages/java/common/pom.xml @@ -1,123 +1,124 @@ - 4.0.0 - jar - com.affinidi.tdk - common - 1.0 - common - https://github.com/affinidi/affinidi-tdk - - - Apache-2.0 - https://github.com/affinidi/affinidi-tdk/blob/main/LICENSE - repo - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + jar + com.affinidi.tdk + common + 1.0 + common + https://github.com/affinidi/affinidi-tdk + + + Apache-2.0 + https://github.com/affinidi/affinidi-tdk/blob/main/LICENSE + repo + + - - - Affinidi - ... - Affinidi - https://affinidi.com - - + + + Affinidi + ... + Affinidi + https://affinidi.com + + - - UTF-8 - 1.8 - ${java.version} - ${java.version} - + + UTF-8 + 1.8 + ${java.version} + ${java.version} + - - - org.junit.jupiter - junit-jupiter-api - 5.11.0 - test - - - org.junit.jupiter - junit-jupiter-engine - 5.11.0 - test - - - io.github.cdimascio - dotenv-java - 2.2.0 - - - org.glassfish.jersey.core - jersey-common - 3.1.7 - - - jakarta.ws.rs - jakarta.ws.rs-api - 3.1.0 - - - org.mockito - mockito-core - 3.12.4 - test - + + + org.junit.jupiter + junit-jupiter-api + 5.11.0 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.11.0 + test + + + io.github.cdimascio + dotenv-java + 2.2.0 + + + org.glassfish.jersey.core + jersey-common + 3.1.7 + + + jakarta.ws.rs + jakarta.ws.rs-api + 3.1.0 + + + org.mockito + + mockito-inline + 3.12.4 + test + - + - - - - - - maven-clean-plugin - 3.1.0 - - - - maven-resources-plugin - 3.0.2 - - - maven-compiler-plugin - 3.8.0 - + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + - - org.apache.maven.plugins - maven-surefire-plugin - - - maven-failsafe-plugin - 3.5.0 - - - maven-jar-plugin - 3.0.2 - - - maven-install-plugin - 2.5.2 - - - maven-deploy-plugin - 2.8.2 - - - - maven-site-plugin - 3.7.1 - - - maven-project-info-reports-plugin - 3.0.0 - - - - + + org.apache.maven.plugins + maven-surefire-plugin + + + maven-failsafe-plugin + 3.5.0 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + diff --git a/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java b/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java index fd806069f..ac4a422a1 100644 --- a/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java +++ b/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java @@ -1,134 +1,129 @@ - package com.affinidi.tdk.common; + import java.util.HashMap; import java.util.Map; +import java.util.logging.Level; import java.util.logging.Logger; + import io.github.cdimascio.dotenv.Dotenv; /** -* This class provides utility functions required to access environment specific -* configurations. The environment can be specified in the .env file at the project -* base using AFFINIDI_TDK_PROPERTY_NAME or NEXT_PUBLIC_AFFINIDI_TDK_ENVIRONMENT as -* dev, local or prod. -* -* -* @author Priyanka -* -*/ - + * This class provides utility functions required to access environment specific + * configurations. The environment can be specified in the .env file at the + * project base using AFFINIDI_TDK_PROPERTY_NAME or + * NEXT_PUBLIC_AFFINIDI_TDK_ENVIRONMENT as dev, local or prod. + * + * + * @author Priyanka + * + */ public class EnvironmentUtil { - private static Dotenv properties; - private static Logger logger = Logger.getLogger(EnvironmentUtil.class.getName()); + private static final Dotenv PROPERTIES = Dotenv.load(); + private static final Logger LOGGER = Logger.getLogger(EnvironmentUtil.class.getName()); - public static Dotenv getProperties(){ - if(properties == null){ - properties = Dotenv.load(); - } - return properties; + public static Dotenv getProperties() { + return PROPERTIES; } - - /** - * Returns the environment name as configured in the .env file - * In case no configuration is found, the default return is - * production environment - * + /** + * Returns the environment name as configured in the .env file In case no + * configuration is found, the default return is production environment + * * @return String */ - public String getConfiguredEnvironment() { + public static String getConfiguredEnvironment() { String configuredEnvironment = null; - try{ + try { if (getProperties() != null) { configuredEnvironment = getProperties().get(Environment.AFFINIDI_TDK_PROPERTY_NAME); if (configuredEnvironment == null) { configuredEnvironment = getProperties().get(Environment.NEXT_AFFINIDI_TDK_PROPERTY_NAME); } } - }catch(Exception exception){ - logger.severe("Could not read .env file for TDK environment configuration"); + } catch (Exception exception) { + LOGGER.severe("Could not read .env file for TDK environment configuration"); } - + if (configuredEnvironment == null) { - logger.severe("Could not find environment details for "+configuredEnvironment+". Defaulting to production"); + LOGGER.log(Level.SEVERE, "Could not find environment details for {0}. Defaulting to production", configuredEnvironment); configuredEnvironment = Environment.PRODUCTION.environmentName; } return configuredEnvironment; } - - /** + /** * Return the default region name - * + * * @return String */ - public String getDefaultRegion() { + public static String getDefaultRegion() { return Environment.DEFAULT_REGION; } - - /** + /** * Returns the IOT url string for the configured environment - * + * * @return String */ - public String getIotUrlForEnvironment() { + public static String getIotUrlForEnvironment() { return getEnvironmentDetail().iotUrl; } - /** + /** * Returns the elements auth token url string for the configured environment - * + * * @return String */ - public String getElementAuthTokenUrlForEnvironment() { + public static String getElementAuthTokenUrlForEnvironment() { return getEnvironmentDetail().elementsAuthTokenUrl; } - /** + /** * Returns the vault URL for the configured environment - * + * * @return String */ - public String getVaultUrlForEnvironment() { + public static String getVaultUrlForEnvironment() { return getEnvironmentDetail().vaultUrl; } - /** + /** * @return String - */ - public String getApiGatewayUrlForEnvironment() { + */ + public static String getApiGatewayUrlForEnvironment() { return getEnvironmentDetail().apiGatewayUrl; } - /** + /** * @return String - */ - public String getValueFromEnvConfig(String propertyName) { + */ + public static String getValueFromEnvConfig(String propertyName) { String propertyValue = null; - try{ + try { propertyValue = getProperties().get(propertyName); - }catch(Exception exception){ - logger.severe("Could not read .env file for "+propertyName); + } catch (Exception exception) { + LOGGER.log(Level.SEVERE, "Could not read .env file for {0}", propertyName); } return propertyValue; } - /** + /** * Returns the APi gateway URL for the configured environment - * + * * @return String */ - private Environment getEnvironmentDetail() { + static Environment getEnvironmentDetail() { + System.out.println("XXX"); - String envName = getConfiguredEnvironment(); - Environment envDetail = (envName != null) ? Environment.getEnvSpecificDetails(envName) : null; + final String envName = getConfiguredEnvironment(); + final Environment envDetail = (envName != null) ? Environment.getEnvSpecificDetails(envName) : null; - if (envDetail == null) { - logger.severe("Could not find environment details for the specified name " + envName+". Hence defaulting to production"); - envDetail = Environment.getEnvSpecificDetails(Environment.PRODUCTION.environmentName); + if (envDetail != null) { + return envDetail; } - return envDetail; + LOGGER.log(Level.SEVERE, "Could not find environment details for the specified name {0}. Hence defaulting to production", envName); + return Environment.getEnvSpecificDetails(Environment.PRODUCTION.environmentName); } } @@ -158,6 +153,7 @@ private Environment(String environmentName, String iotUrl, String apiGatewayUrl, } private static final Map EnvironmentMap = new HashMap<>(); + static { for (Environment env : values()) { EnvironmentMap.put(env.environmentName, env); diff --git a/packages/java/common/src/main/java/com/affinidi/tdk/common/VaultUtil.java b/packages/java/common/src/main/java/com/affinidi/tdk/common/VaultUtil.java index 8cb09d125..1c0be9902 100644 --- a/packages/java/common/src/main/java/com/affinidi/tdk/common/VaultUtil.java +++ b/packages/java/common/src/main/java/com/affinidi/tdk/common/VaultUtil.java @@ -1,17 +1,19 @@ package com.affinidi.tdk.common; import java.net.URI; +import java.util.logging.Level; import java.util.logging.Logger; + import jakarta.ws.rs.core.UriBuilder; /** -* This class provides utility functions required to create vault specific links. -* Refer {@EnvironmentUtil} for more information -* -* -* @author Priyanka -* -*/ + * This class provides utility functions required to create vault specific + * links. Refer {@EnvironmentUtil} for more information + * + * + * @author Priyanka + * + */ public class VaultUtil { static final String VAULT_SHARE_PATH = "/login"; @@ -20,50 +22,45 @@ public class VaultUtil { static final String SHARE_CLIENT_PARAM = "clientId"; static final String CLAIM_CREDENTIAL_URI_PARAM = "credential_offer_uri"; - private static Logger logger = Logger.getLogger(VaultUtil.class.getName()); - EnvironmentUtil environmentUtil = new EnvironmentUtil(); + private static final Logger LOGGER = Logger.getLogger(VaultUtil.class.getName()); - /** - * Returns the credential offer claim link specific to the configured environment - * + /** + * Returns the credential offer claim link specific to the configured + * environment + * * @param credentialOfferUri * @return String */ public String buildClaimLink(String credentialOfferUri) { - String webVaultUrl = environmentUtil.getVaultUrlForEnvironment(); + String webVaultUrl = EnvironmentUtil.getVaultUrlForEnvironment(); - if(credentialOfferUri == null || credentialOfferUri.equals("")){ - logger.severe("Invalid Credential Offer URI passed to utility "+credentialOfferUri+". Returning vault url"); + if (credentialOfferUri == null || credentialOfferUri.equals("")) { + LOGGER.log(Level.SEVERE, "Invalid Credential Offer URI passed to utility {0}. Returning vault url", credentialOfferUri); return webVaultUrl; } - + webVaultUrl = String.join("", webVaultUrl, VAULT_CLAIM_PATH); URI claimUri = UriBuilder.fromUri(webVaultUrl).queryParam(CLAIM_CREDENTIAL_URI_PARAM, credentialOfferUri).build(); return claimUri.toString(); } - - /** - * Returns the vault share link given request and a client id, - * specific to the configured environment - * + + /** + * Returns the vault share link given request and a client id, specific to + * the configured environment + * * @param request * @param clientId * @return String */ - public String buildShareLink(String request, String clientId){ + public String buildShareLink(String request, String clientId) { - String webVaultUrl = environmentUtil.getVaultUrlForEnvironment(); + String webVaultUrl = EnvironmentUtil.getVaultUrlForEnvironment(); webVaultUrl = String.join("", webVaultUrl, VAULT_SHARE_PATH); URI shareUri = UriBuilder.fromUri(webVaultUrl). - queryParam(SHARE_REQUEST_PARAM, request).queryParam(SHARE_CLIENT_PARAM, clientId).build(); + queryParam(SHARE_REQUEST_PARAM, request).queryParam(SHARE_CLIENT_PARAM, clientId).build(); return shareUri.toString(); - + } - - - } - - diff --git a/packages/java/common/src/test/java/com/affinidi/tdk/common/EnvironmentUtilTest.java b/packages/java/common/src/test/java/com/affinidi/tdk/common/EnvironmentUtilTest.java index 68ad08ff5..678a9a7ca 100644 --- a/packages/java/common/src/test/java/com/affinidi/tdk/common/EnvironmentUtilTest.java +++ b/packages/java/common/src/test/java/com/affinidi/tdk/common/EnvironmentUtilTest.java @@ -1,119 +1,107 @@ package com.affinidi.tdk.common; -import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.concurrent.Callable; -import org.junit.jupiter.api.BeforeEach; +import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; -import org.mockito.MockitoAnnotations; -import org.mockito.Spy; - -import static org.mockito.Mockito.doReturn; +import org.mockito.MockedStatic; +import static org.mockito.Mockito.mockStatic; +import org.opentest4j.TestAbortedException; public class EnvironmentUtilTest { - @Spy - EnvironmentUtil spyEnvironmentUtil; - - @BeforeEach - void setUp() { - MockitoAnnotations.openMocks(this); + private void test_getUrlForEnvironment(String configuredEnvironment, Callable getUrlForEnvironment, String expectedUrlForEnvironment) { + try (MockedStatic mocked = mockStatic(EnvironmentUtil.class)) { + // Test the function call for local environment without reading the configured property + mocked.when(EnvironmentUtil::getConfiguredEnvironment).thenReturn(configuredEnvironment); + mocked.when(EnvironmentUtil::getEnvironmentDetail).thenCallRealMethod(); + mocked.when(EnvironmentUtil::getVaultUrlForEnvironment).thenCallRealMethod(); + mocked.when(EnvironmentUtil::getIotUrlForEnvironment).thenCallRealMethod(); + mocked.when(EnvironmentUtil::getElementAuthTokenUrlForEnvironment).thenCallRealMethod(); + mocked.when(EnvironmentUtil::getApiGatewayUrlForEnvironment).thenCallRealMethod(); + try { + // assertEquals(expectedUrlForEnvironment, EnvironmentUtil.getVaultUrlForEnvironment()); + assertEquals(expectedUrlForEnvironment, getUrlForEnvironment.call()); + } catch (Exception ex) { + throw new TestAbortedException(ex.getMessage()); + } + + } } - @Test - void testGetVaultUrlForEnvironment_InLocal(){ - - //Test the function call for local environment without reading the configured property - doReturn("LOCAL").when(spyEnvironmentUtil).getConfiguredEnvironment(); - - assertEquals(Environment.LOCAL_VAULT_URL, spyEnvironmentUtil.getVaultUrlForEnvironment()); + private void test_getVaultUrlForEnvironment(String configuredEnvironment, String expectedVaultUrlForEnvironment) { + test_getUrlForEnvironment(configuredEnvironment, () -> EnvironmentUtil.getVaultUrlForEnvironment(), expectedVaultUrlForEnvironment); } - @Test - void testGetVaultUrlForEnvironment_InDev(){ - //Test the function call for local environment without reading the configured property - doReturn("DEV").when(spyEnvironmentUtil).getConfiguredEnvironment(); - - assertEquals(Environment.DEV_VAULT_URL, spyEnvironmentUtil.getVaultUrlForEnvironment()); + private void test_getIotUrlForEnvironment(String configuredEnvironment, String expectedIotUrlForEnvironment) { + test_getUrlForEnvironment(configuredEnvironment, () -> EnvironmentUtil.getIotUrlForEnvironment(), expectedIotUrlForEnvironment); } - @Test - void testGetVaultUrlForEnvironment_InProd(){ - //Test the function call for local environment without reading the configured property - doReturn("PROD").when(spyEnvironmentUtil).getConfiguredEnvironment(); + private void test_getElementAuthTokenUrlForEnvironment(String configuredEnvironment, String expectedElementAuthTokenUrlForEnvironment) { + test_getUrlForEnvironment(configuredEnvironment, () -> EnvironmentUtil.getElementAuthTokenUrlForEnvironment(), expectedElementAuthTokenUrlForEnvironment); + } - assertEquals(Environment.PROD_VAULT_URL, spyEnvironmentUtil.getVaultUrlForEnvironment()); + private void test_getApiGatewayUrlForEnvironment(String configuredEnvironment, String expectedApiGatewayUrlForEnvironment) { + test_getUrlForEnvironment(configuredEnvironment, () -> EnvironmentUtil.getApiGatewayUrlForEnvironment(), expectedApiGatewayUrlForEnvironment); } @Test - void testGetIotaUrlForEnvironment_InLocal(){ - - //Test the function call for local environment without reading the configured property - doReturn("LOCAL").when(spyEnvironmentUtil).getConfiguredEnvironment(); - - assertEquals(Environment.LOCAL_IOT_URL, spyEnvironmentUtil.getIotUrlForEnvironment()); + void testGetVaultUrlForEnvironment_InLocal() { + test_getVaultUrlForEnvironment("LOCAL", Environment.LOCAL_VAULT_URL); } - @Test - void testGetIotaUrlForEnvironment_InDev(){ - //Test the function call for local environment without reading the configured property - doReturn("DEV").when(spyEnvironmentUtil).getConfiguredEnvironment(); - assertEquals(Environment.DEV_IOT_URL, spyEnvironmentUtil.getIotUrlForEnvironment()); - } @Test - void testGetIotaUrlForEnvironment_InProd(){ - - //Test the function call for local environment without reading the configured property - doReturn("PROD").when(spyEnvironmentUtil).getConfiguredEnvironment(); - - assertEquals(Environment.PROD_IOT_URL, spyEnvironmentUtil.getIotUrlForEnvironment()); + void testGetVaultUrlForEnvironment_InDev() { + test_getVaultUrlForEnvironment("DEV", Environment.DEV_VAULT_URL); } @Test - void testGetElementAuthTokenUrlForEnvironment_InLocal(){ - - //Test the function call for local environment without reading the configured property - doReturn("LOCAL").when(spyEnvironmentUtil).getConfiguredEnvironment(); - - assertEquals(Environment.LOCAL_ELEMENTS_AUTH_TOKEN_URL, spyEnvironmentUtil.getElementAuthTokenUrlForEnvironment()); + void testGetVaultUrlForEnvironment_InProd() { + test_getVaultUrlForEnvironment("PROD", Environment.PROD_VAULT_URL); } - @Test - void testGetElementAuthTokenUrlForEnvironment_InDev(){ - - //Test the function call for local environment without reading the configured property - doReturn("DEV").when(spyEnvironmentUtil).getConfiguredEnvironment(); - assertEquals(Environment.DEV_ELEMENTS_AUTH_TOKEN_URL, spyEnvironmentUtil.getElementAuthTokenUrlForEnvironment()); - } @Test - void testGetElementAuthTokenUrlForEnvironment_InProd(){ - - //Test the function call for local environment without reading the configured property - doReturn("PROD").when(spyEnvironmentUtil).getConfiguredEnvironment(); - - assertEquals(Environment.PROD_ELEMENTS_AUTH_TOKEN_URL, spyEnvironmentUtil.getElementAuthTokenUrlForEnvironment()); + void testGetIotaUrlForEnvironment_InLocal() { + test_getIotUrlForEnvironment("LOCAL", Environment.LOCAL_IOT_URL); } - @Test - void testGetApiGatewayUrlForEnvironment_InLocal(){ - //Test the function call for local environment without reading the configured property - doReturn("LOCAL").when(spyEnvironmentUtil).getConfiguredEnvironment(); + @Test + void testGetIotaUrlForEnvironment_InDev() { + test_getIotUrlForEnvironment("DEV", Environment.DEV_IOT_URL); + } - assertEquals(Environment.LOCAL_APIGATEWAY_URL, spyEnvironmentUtil.getApiGatewayUrlForEnvironment()); + @Test + void testGetIotaUrlForEnvironment_InProd() { + test_getIotUrlForEnvironment("PROD", Environment.PROD_IOT_URL); } + @Test - void testGetApiGatewayUrlForEnvironment_InDev(){ + void testGetElementAuthTokenUrlForEnvironment_InLocal() { + test_getElementAuthTokenUrlForEnvironment("LOCAL", Environment.LOCAL_ELEMENTS_AUTH_TOKEN_URL); + } - //Test the function call for local environment without reading the configured property - doReturn("DEV").when(spyEnvironmentUtil).getConfiguredEnvironment(); + @Test + void testGetElementAuthTokenUrlForEnvironment_InDev() { + test_getElementAuthTokenUrlForEnvironment("DEV", Environment.DEV_ELEMENTS_AUTH_TOKEN_URL); + } - assertEquals(Environment.DEV_APIGATEWAY_URL, spyEnvironmentUtil.getApiGatewayUrlForEnvironment()); + @Test + void testGetElementAuthTokenUrlForEnvironment_InProd() { + test_getElementAuthTokenUrlForEnvironment("PROD", Environment.PROD_ELEMENTS_AUTH_TOKEN_URL); } + @Test - void testGetApiGatewayUrlForEnvironment_InProd(){ + void testGetApiGatewayUrlForEnvironment_InLocal() { + test_getApiGatewayUrlForEnvironment("LOCAL", Environment.LOCAL_APIGATEWAY_URL); + } - //Test the function call for local environment without reading the configured property - doReturn("PROD").when(spyEnvironmentUtil).getConfiguredEnvironment(); + @Test + void testGetApiGatewayUrlForEnvironment_InDev() { + test_getApiGatewayUrlForEnvironment("DEV", Environment.DEV_APIGATEWAY_URL); + } - assertEquals(Environment.PROD_APIGATEWAY_URL, spyEnvironmentUtil.getApiGatewayUrlForEnvironment()); + @Test + void testGetApiGatewayUrlForEnvironment_InProd() { + test_getApiGatewayUrlForEnvironment("PROD", Environment.PROD_APIGATEWAY_URL); } } diff --git a/packages/java/common/src/test/java/com/affinidi/tdk/common/VaultUtilTest.java b/packages/java/common/src/test/java/com/affinidi/tdk/common/VaultUtilTest.java index 2f0885a1a..16aa5812d 100644 --- a/packages/java/common/src/test/java/com/affinidi/tdk/common/VaultUtilTest.java +++ b/packages/java/common/src/test/java/com/affinidi/tdk/common/VaultUtilTest.java @@ -1,124 +1,101 @@ package com.affinidi.tdk.common; -import static org.junit.jupiter.api.Assertions.assertEquals; - import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Mockito; +import org.mockito.MockedStatic; +import static org.mockito.Mockito.mockStatic; import org.mockito.MockitoAnnotations; public class VaultUtilTest { - @Mock - EnvironmentUtil environmentUtil; - @InjectMocks VaultUtil vaultUtil = new VaultUtil(); - @BeforeEach + @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); } @Test - void testBuildShareLink_InLocal(){ - - Mockito.when(environmentUtil.getVaultUrlForEnvironment()).thenReturn(Environment.LOCAL_VAULT_URL); - - String testRequestId = "request1234"; - String testClientId = "clientabcde"; - String shareLink = vaultUtil.buildShareLink(testRequestId, testClientId); - - String expectedValue = Environment.LOCAL_VAULT_URL + VaultUtil.VAULT_SHARE_PATH - + "?"+VaultUtil.SHARE_REQUEST_PARAM+"="+testRequestId - + "&"+VaultUtil.SHARE_CLIENT_PARAM+"="+testClientId; - - assertEquals(expectedValue, shareLink); + void testBuildShareLink_InLocal() { + try (MockedStatic mocked = mockStatic(EnvironmentUtil.class)) { + mocked.when(EnvironmentUtil::getVaultUrlForEnvironment).thenReturn(Environment.LOCAL_VAULT_URL); + String testRequestId = "request1234"; + String testClientId = "clientabcde"; + String shareLink = vaultUtil.buildShareLink(testRequestId, testClientId); + String expectedValue = Environment.LOCAL_VAULT_URL + VaultUtil.VAULT_SHARE_PATH + + "?" + VaultUtil.SHARE_REQUEST_PARAM + "=" + testRequestId + + "&" + VaultUtil.SHARE_CLIENT_PARAM + "=" + testClientId; + assertEquals(expectedValue, shareLink); + } } @Test - void testBuildShareLink_InDev(){ - - Mockito.when(environmentUtil.getVaultUrlForEnvironment()).thenReturn(Environment.DEV_VAULT_URL); - - String testRequestId = "request1234"; - String testClientId = "clientabcde"; - String shareLink = vaultUtil.buildShareLink(testRequestId, testClientId); - - String expectedValue = Environment.DEV_VAULT_URL + VaultUtil.VAULT_SHARE_PATH - + "?"+VaultUtil.SHARE_REQUEST_PARAM+"="+testRequestId - + "&"+VaultUtil.SHARE_CLIENT_PARAM+"="+testClientId; - - assertEquals(expectedValue, shareLink); + void testBuildShareLink_InDev() { + try (MockedStatic mocked = mockStatic(EnvironmentUtil.class)) { + mocked.when(EnvironmentUtil::getVaultUrlForEnvironment).thenReturn(Environment.DEV_VAULT_URL); + String testRequestId = "request1234"; + String testClientId = "clientabcde"; + String shareLink = vaultUtil.buildShareLink(testRequestId, testClientId); + String expectedValue = Environment.DEV_VAULT_URL + VaultUtil.VAULT_SHARE_PATH + + "?" + VaultUtil.SHARE_REQUEST_PARAM + "=" + testRequestId + + "&" + VaultUtil.SHARE_CLIENT_PARAM + "=" + testClientId; + assertEquals(expectedValue, shareLink); + } } @Test - void testBuildShareLink_InProd(){ - - Mockito.when(environmentUtil.getVaultUrlForEnvironment()).thenReturn(Environment.PROD_VAULT_URL); - - String testRequestId = "request1234"; - String testClientId = "clientabcde"; - String shareLink = vaultUtil.buildShareLink(testRequestId, testClientId); - - String expectedValue = Environment.PROD_VAULT_URL + VaultUtil.VAULT_SHARE_PATH - + "?"+VaultUtil.SHARE_REQUEST_PARAM+"="+testRequestId - + "&"+VaultUtil.SHARE_CLIENT_PARAM+"="+testClientId; - - assertEquals(expectedValue, shareLink); + void testBuildShareLink_InProd() { + try (MockedStatic mocked = mockStatic(EnvironmentUtil.class)) { + mocked.when(EnvironmentUtil::getVaultUrlForEnvironment).thenReturn(Environment.PROD_VAULT_URL); + String testRequestId = "request1234"; + String testClientId = "clientabcde"; + String shareLink = vaultUtil.buildShareLink(testRequestId, testClientId); + String expectedValue = Environment.PROD_VAULT_URL + VaultUtil.VAULT_SHARE_PATH + + "?" + VaultUtil.SHARE_REQUEST_PARAM + "=" + testRequestId + + "&" + VaultUtil.SHARE_CLIENT_PARAM + "=" + testClientId; + assertEquals(expectedValue, shareLink); + } } @Test - void testBuildClaimLink_InLocal(){ - - Mockito.when(environmentUtil.getVaultUrlForEnvironment()).thenReturn(Environment.LOCAL_VAULT_URL); - - String testCredentialOfferUri = "https://00112233-xxx-yyyy-aaaaa-abcd2345678.apse1.issuance.affinidi.io/offers/cb3817e6-4b16-4f19-a658-8eb096541bcc"; - - String claimLink = vaultUtil.buildClaimLink(testCredentialOfferUri); - - String expectedValue = Environment.LOCAL_VAULT_URL + VaultUtil.VAULT_CLAIM_PATH - + "?"+VaultUtil.CLAIM_CREDENTIAL_URI_PARAM+"="+URLEncoder.encode(testCredentialOfferUri, StandardCharsets.UTF_8); - - assertEquals(expectedValue, claimLink); + void testBuildClaimLink_InLocal() { + try (MockedStatic mocked = mockStatic(EnvironmentUtil.class)) { + mocked.when(EnvironmentUtil::getVaultUrlForEnvironment).thenReturn(Environment.LOCAL_VAULT_URL); + String testCredentialOfferUri = "https://00112233-xxx-yyyy-aaaaa-abcd2345678.apse1.issuance.affinidi.io/offers/cb3817e6-4b16-4f19-a658-8eb096541bcc"; + String claimLink = vaultUtil.buildClaimLink(testCredentialOfferUri); + String expectedValue = Environment.LOCAL_VAULT_URL + VaultUtil.VAULT_CLAIM_PATH + + "?" + VaultUtil.CLAIM_CREDENTIAL_URI_PARAM + "=" + URLEncoder.encode(testCredentialOfferUri, StandardCharsets.UTF_8); + assertEquals(expectedValue, claimLink); + } } - @Test - void testBuildClaimLink_InDev(){ - - Mockito.when(environmentUtil.getVaultUrlForEnvironment()).thenReturn(Environment.DEV_VAULT_URL); - - String testCredentialOfferUri = "https://00112233-xxx-yyyy-aaaaa-abcd2345678.apse1.issuance.affinidi.io/offers/cb3817e6-4b16-4f19-a658-8eb096541bcc"; - - String claimLink = vaultUtil.buildClaimLink(testCredentialOfferUri); - - String expectedValue = Environment.DEV_VAULT_URL + VaultUtil.VAULT_CLAIM_PATH - + "?"+VaultUtil.CLAIM_CREDENTIAL_URI_PARAM+"="+URLEncoder.encode(testCredentialOfferUri, StandardCharsets.UTF_8); - - assertEquals(expectedValue, claimLink); + void testBuildClaimLink_InDev() { + try (MockedStatic mocked = mockStatic(EnvironmentUtil.class)) { + mocked.when(EnvironmentUtil::getVaultUrlForEnvironment).thenReturn(Environment.DEV_VAULT_URL); + String testCredentialOfferUri = "https://00112233-xxx-yyyy-aaaaa-abcd2345678.apse1.issuance.affinidi.io/offers/cb3817e6-4b16-4f19-a658-8eb096541bcc"; + String claimLink = vaultUtil.buildClaimLink(testCredentialOfferUri); + String expectedValue = Environment.DEV_VAULT_URL + VaultUtil.VAULT_CLAIM_PATH + + "?" + VaultUtil.CLAIM_CREDENTIAL_URI_PARAM + "=" + URLEncoder.encode(testCredentialOfferUri, StandardCharsets.UTF_8); + assertEquals(expectedValue, claimLink); + } } - @Test - void testBuildClaimLink_InProd(){ - - Mockito.when(environmentUtil.getVaultUrlForEnvironment()).thenReturn(Environment.PROD_VAULT_URL); - - String testCredentialOfferUri = "https://00112233-xxx-yyyy-aaaaa-abcd2345678.apse1.issuance.affinidi.io/offers/cb3817e6-4b16-4f19-a658-8eb096541bcc"; - - String claimLink = vaultUtil.buildClaimLink(testCredentialOfferUri); - - String expectedValue = Environment.PROD_VAULT_URL + VaultUtil.VAULT_CLAIM_PATH - + "?"+VaultUtil.CLAIM_CREDENTIAL_URI_PARAM+"="+URLEncoder.encode(testCredentialOfferUri, StandardCharsets.UTF_8); - - assertEquals(expectedValue, claimLink); + void testBuildClaimLink_InProd() { + try (MockedStatic mocked = mockStatic(EnvironmentUtil.class)) { + mocked.when(EnvironmentUtil::getVaultUrlForEnvironment).thenReturn(Environment.PROD_VAULT_URL); + String testCredentialOfferUri = "https://00112233-xxx-yyyy-aaaaa-abcd2345678.apse1.issuance.affinidi.io/offers/cb3817e6-4b16-4f19-a658-8eb096541bcc"; + String claimLink = vaultUtil.buildClaimLink(testCredentialOfferUri); + String expectedValue = Environment.PROD_VAULT_URL + VaultUtil.VAULT_CLAIM_PATH + + "?" + VaultUtil.CLAIM_CREDENTIAL_URI_PARAM + "=" + URLEncoder.encode(testCredentialOfferUri, StandardCharsets.UTF_8); + assertEquals(expectedValue, claimLink); + } } - - } From 108e493daeb09de6d09bc64b1037c4430c47ba71 Mon Sep 17 00:00:00 2001 From: Robert Kwolek Date: Tue, 21 Jan 2025 12:10:33 +0100 Subject: [PATCH 13/15] fix: dotenv fixed --- packages/java/common/.env | 0 .../com/affinidi/tdk/common/EnvironmentUtil.java | 3 ++- .../java/com/affinidi/tdk/common/VaultUtil.java | 6 +++--- .../com/affinidi/tdk/common/VaultUtilTest.java | 16 ++++++---------- 4 files changed, 11 insertions(+), 14 deletions(-) delete mode 100644 packages/java/common/.env diff --git a/packages/java/common/.env b/packages/java/common/.env deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java b/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java index ac4a422a1..d87c158f4 100644 --- a/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java +++ b/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java @@ -6,6 +6,7 @@ import java.util.logging.Logger; import io.github.cdimascio.dotenv.Dotenv; +import io.github.cdimascio.dotenv.DotenvBuilder; /** * This class provides utility functions required to access environment specific @@ -19,7 +20,7 @@ */ public class EnvironmentUtil { - private static final Dotenv PROPERTIES = Dotenv.load(); + private static final Dotenv PROPERTIES = new DotenvBuilder().ignoreIfMissing().load(); private static final Logger LOGGER = Logger.getLogger(EnvironmentUtil.class.getName()); public static Dotenv getProperties() { diff --git a/packages/java/common/src/main/java/com/affinidi/tdk/common/VaultUtil.java b/packages/java/common/src/main/java/com/affinidi/tdk/common/VaultUtil.java index 1c0be9902..6041bc1d4 100644 --- a/packages/java/common/src/main/java/com/affinidi/tdk/common/VaultUtil.java +++ b/packages/java/common/src/main/java/com/affinidi/tdk/common/VaultUtil.java @@ -14,7 +14,7 @@ * @author Priyanka * */ -public class VaultUtil { +public final class VaultUtil { static final String VAULT_SHARE_PATH = "/login"; static final String VAULT_CLAIM_PATH = "/claim"; @@ -31,7 +31,7 @@ public class VaultUtil { * @param credentialOfferUri * @return String */ - public String buildClaimLink(String credentialOfferUri) { + public static String buildClaimLink(String credentialOfferUri) { String webVaultUrl = EnvironmentUtil.getVaultUrlForEnvironment(); if (credentialOfferUri == null || credentialOfferUri.equals("")) { @@ -52,7 +52,7 @@ public String buildClaimLink(String credentialOfferUri) { * @param clientId * @return String */ - public String buildShareLink(String request, String clientId) { + public static String buildShareLink(String request, String clientId) { String webVaultUrl = EnvironmentUtil.getVaultUrlForEnvironment(); webVaultUrl = String.join("", webVaultUrl, VAULT_SHARE_PATH); diff --git a/packages/java/common/src/test/java/com/affinidi/tdk/common/VaultUtilTest.java b/packages/java/common/src/test/java/com/affinidi/tdk/common/VaultUtilTest.java index 16aa5812d..17642b336 100644 --- a/packages/java/common/src/test/java/com/affinidi/tdk/common/VaultUtilTest.java +++ b/packages/java/common/src/test/java/com/affinidi/tdk/common/VaultUtilTest.java @@ -6,16 +6,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; import org.mockito.MockedStatic; import static org.mockito.Mockito.mockStatic; import org.mockito.MockitoAnnotations; public class VaultUtilTest { - @InjectMocks - VaultUtil vaultUtil = new VaultUtil(); - @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); @@ -27,7 +23,7 @@ void testBuildShareLink_InLocal() { mocked.when(EnvironmentUtil::getVaultUrlForEnvironment).thenReturn(Environment.LOCAL_VAULT_URL); String testRequestId = "request1234"; String testClientId = "clientabcde"; - String shareLink = vaultUtil.buildShareLink(testRequestId, testClientId); + String shareLink = VaultUtil.buildShareLink(testRequestId, testClientId); String expectedValue = Environment.LOCAL_VAULT_URL + VaultUtil.VAULT_SHARE_PATH + "?" + VaultUtil.SHARE_REQUEST_PARAM + "=" + testRequestId + "&" + VaultUtil.SHARE_CLIENT_PARAM + "=" + testClientId; @@ -41,7 +37,7 @@ void testBuildShareLink_InDev() { mocked.when(EnvironmentUtil::getVaultUrlForEnvironment).thenReturn(Environment.DEV_VAULT_URL); String testRequestId = "request1234"; String testClientId = "clientabcde"; - String shareLink = vaultUtil.buildShareLink(testRequestId, testClientId); + String shareLink = VaultUtil.buildShareLink(testRequestId, testClientId); String expectedValue = Environment.DEV_VAULT_URL + VaultUtil.VAULT_SHARE_PATH + "?" + VaultUtil.SHARE_REQUEST_PARAM + "=" + testRequestId + "&" + VaultUtil.SHARE_CLIENT_PARAM + "=" + testClientId; @@ -55,7 +51,7 @@ void testBuildShareLink_InProd() { mocked.when(EnvironmentUtil::getVaultUrlForEnvironment).thenReturn(Environment.PROD_VAULT_URL); String testRequestId = "request1234"; String testClientId = "clientabcde"; - String shareLink = vaultUtil.buildShareLink(testRequestId, testClientId); + String shareLink = VaultUtil.buildShareLink(testRequestId, testClientId); String expectedValue = Environment.PROD_VAULT_URL + VaultUtil.VAULT_SHARE_PATH + "?" + VaultUtil.SHARE_REQUEST_PARAM + "=" + testRequestId + "&" + VaultUtil.SHARE_CLIENT_PARAM + "=" + testClientId; @@ -68,7 +64,7 @@ void testBuildClaimLink_InLocal() { try (MockedStatic mocked = mockStatic(EnvironmentUtil.class)) { mocked.when(EnvironmentUtil::getVaultUrlForEnvironment).thenReturn(Environment.LOCAL_VAULT_URL); String testCredentialOfferUri = "https://00112233-xxx-yyyy-aaaaa-abcd2345678.apse1.issuance.affinidi.io/offers/cb3817e6-4b16-4f19-a658-8eb096541bcc"; - String claimLink = vaultUtil.buildClaimLink(testCredentialOfferUri); + String claimLink = VaultUtil.buildClaimLink(testCredentialOfferUri); String expectedValue = Environment.LOCAL_VAULT_URL + VaultUtil.VAULT_CLAIM_PATH + "?" + VaultUtil.CLAIM_CREDENTIAL_URI_PARAM + "=" + URLEncoder.encode(testCredentialOfferUri, StandardCharsets.UTF_8); assertEquals(expectedValue, claimLink); @@ -80,7 +76,7 @@ void testBuildClaimLink_InDev() { try (MockedStatic mocked = mockStatic(EnvironmentUtil.class)) { mocked.when(EnvironmentUtil::getVaultUrlForEnvironment).thenReturn(Environment.DEV_VAULT_URL); String testCredentialOfferUri = "https://00112233-xxx-yyyy-aaaaa-abcd2345678.apse1.issuance.affinidi.io/offers/cb3817e6-4b16-4f19-a658-8eb096541bcc"; - String claimLink = vaultUtil.buildClaimLink(testCredentialOfferUri); + String claimLink = VaultUtil.buildClaimLink(testCredentialOfferUri); String expectedValue = Environment.DEV_VAULT_URL + VaultUtil.VAULT_CLAIM_PATH + "?" + VaultUtil.CLAIM_CREDENTIAL_URI_PARAM + "=" + URLEncoder.encode(testCredentialOfferUri, StandardCharsets.UTF_8); assertEquals(expectedValue, claimLink); @@ -92,7 +88,7 @@ void testBuildClaimLink_InProd() { try (MockedStatic mocked = mockStatic(EnvironmentUtil.class)) { mocked.when(EnvironmentUtil::getVaultUrlForEnvironment).thenReturn(Environment.PROD_VAULT_URL); String testCredentialOfferUri = "https://00112233-xxx-yyyy-aaaaa-abcd2345678.apse1.issuance.affinidi.io/offers/cb3817e6-4b16-4f19-a658-8eb096541bcc"; - String claimLink = vaultUtil.buildClaimLink(testCredentialOfferUri); + String claimLink = VaultUtil.buildClaimLink(testCredentialOfferUri); String expectedValue = Environment.PROD_VAULT_URL + VaultUtil.VAULT_CLAIM_PATH + "?" + VaultUtil.CLAIM_CREDENTIAL_URI_PARAM + "=" + URLEncoder.encode(testCredentialOfferUri, StandardCharsets.UTF_8); assertEquals(expectedValue, claimLink); From 33284451a493a2466906027c43ca634498d31604 Mon Sep 17 00:00:00 2001 From: Robert Kwolek Date: Tue, 21 Jan 2025 12:47:30 +0100 Subject: [PATCH 14/15] feat: docs, linting, alignments --- .gitignore | 5 +- .../tdk/authProvider/AuthProvider.java | 227 ++++++------- .../AccessTokenGenerationException.java | 3 +- .../exception/ConfigurationException.java | 4 +- .../GenericAuthProviderException.java | 8 + .../exception/InvalidPrivateKeyException.java | 3 +- .../exception/InvalidPublicKeyException.java | 4 +- .../exception/JwtGenerationException.java | 3 +- .../exception/PSTGenerationException.java | 4 +- .../helper/AuthProviderConstants.java | 24 +- .../tdk/authProvider/helper/JwtUtil.java | 304 ++++++++---------- .../tdk/authProvider/types/IotaJwtOutput.java | 19 +- .../tdk/authProvider/AuthProviderTest.java | 78 +++-- .../tdk/authProvider/helper/JwtUtilTest.java | 15 +- .../affinidi/tdk/common/EnvironmentUtil.java | 20 +- .../com/affinidi/tdk/common/VaultUtil.java | 9 +- 16 files changed, 346 insertions(+), 384 deletions(-) create mode 100644 packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/GenericAuthProviderException.java diff --git a/.gitignore b/.gitignore index 6c0a6fb75..6761bfed0 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,7 @@ testem.log Thumbs.db .nx -.vscode \ No newline at end of file +.vscode + +# Java +**/target \ No newline at end of file diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java index 871a803f6..ce5dc0c75 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/AuthProvider.java @@ -1,6 +1,7 @@ package com.affinidi.tdk.authProvider; -import java.util.ArrayList; +import java.io.IOException; +import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -26,15 +27,20 @@ import com.google.gson.JsonObject; /** - * This class provides utility functions in order to generate - * projectScopeToken required to call Affinidi Services. - * + * This class provides utility functions in order to generate projectScopeToken + * required to call Affinidi Services. + * * * @author Priyanka - * + * */ public class AuthProvider { + /** + * Gson is thread safe. + */ + private static final Gson GSON = new Gson(); + private final String projectId; private final String tokenId; private final String privateKey; @@ -51,165 +57,119 @@ private AuthProvider(Configurations configurations) { this.privateKey = configurations.privateKey; this.keyId = configurations.keyId; this.passphrase = configurations.passphrase; - EnvironmentUtil eUtil = new EnvironmentUtil(); - this.apiGatewayUrl = eUtil.getApiGatewayUrlForEnvironment(); - this.tokenEndPoint = eUtil.getElementAuthTokenUrlForEnvironment(); + this.apiGatewayUrl = EnvironmentUtil.getApiGatewayUrlForEnvironment(); + this.tokenEndPoint = EnvironmentUtil.getElementAuthTokenUrlForEnvironment(); } /** * This method identifies if the current AuthProvider has a valid existing - * projectScopeToken or not. This helps to reuse the valid tokens without always - * generating a new. - * - * The validation involves verifying token's signature against the - * public (verification) key; validating token's expiration or malformation. - * + * projectScopeToken or not. This helps to reuse the valid tokens without + * always generating a new. + * + * The validation involves verifying token's signature against the public + * (verification) key; validating token's expiration or malformation. + * * @return boolean */ public boolean shouldRefreshToken() { - if (this.projectScopeToken == null) { - return true; - } - return !(JwtUtil.validProjectTokenPresent(this.projectScopeToken, this.apiGatewayUrl)); + return (this.projectScopeToken == null) || !(JwtUtil.validProjectTokenPresent(this.projectScopeToken, this.apiGatewayUrl)); } /** - * This method generates a projectScopeToken required to call - * Affinidi services. - * - * In case there is an existing projectScopeToken in the - * authProvider instance; it is first validated and a new one is - * generated only if needed. - * - * Refer {@link JwtUtil#validProjectTokenPresent(String, String)} - * for validation details - * + * This method generates a projectScopeToken required to call Affinidi + * services. + * + * In case there is an existing projectScopeToken in the authProvider + * instance; it is first validated and a new one is generated only if + * needed. + * + * Refer {@link JwtUtil#validProjectTokenPresent(String, String)} for + * validation details + * * @return String - * @throws PSTGenerationException incase access_token generation has issues or - * projectScopeToken end point + * @throws PSTGenerationException incase access_token generation has issues + * or projectScopeToken end point */ public String fetchProjectScopedToken() throws PSTGenerationException { - boolean tokenFetchRequired = shouldRefreshToken(); - - if (tokenFetchRequired) { + if (shouldRefreshToken()) { this.projectScopeToken = getProjectScopedToken(); } return this.projectScopeToken; } /** - * This method generates a user-access-token which is required - * as an API authorization token. - * + * This method generates a user-access-token which is required as an API + * authorization token. + * * @return String - * @throws AccessTokenGenerationException in case the acceess token could not be - * generated + * @throws AccessTokenGenerationException in case the access token could not + * be generated */ public String getUserAccessToken() throws AccessTokenGenerationException { - String userAccessToken = null; - try { - String signedToken = JwtUtil.signPayload(this.tokenId, this.tokenEndPoint, this.privateKey, this.passphrase, + final String signedToken = JwtUtil.signPayload(this.tokenId, this.tokenEndPoint, this.privateKey, this.passphrase, this.keyId); - if (signedToken == null) { throw new JwtGenerationException("Could not generate signed JWT from the configurations "); } final HttpPost httpPost = new HttpPost(this.getTokenEndPoint()); - httpPost.setHeader(AuthProviderConstants.contentTypeHeader, - AuthProviderConstants.applicationUrlEncodedContentType); - - final List params = new ArrayList(); - params.add(new BasicNameValuePair("grant_type", "client_credentials")); - params.add(new BasicNameValuePair("scope", "openid")); - params.add(new BasicNameValuePair("client_assertion_type", - "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); - params.add(new BasicNameValuePair("client_assertion", signedToken)); - params.add(new BasicNameValuePair("client_id", this.tokenId)); + httpPost.setHeader(AuthProviderConstants.CONTENT_TYPE_HEADER, + AuthProviderConstants.APPLICATION_URL_ENCODED_CONTENT_TYPE); + + final List params = Arrays.asList( + new BasicNameValuePair("grant_type", "client_credentials"), + new BasicNameValuePair("scope", "openid"), + new BasicNameValuePair("client_assertion_type", + "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"), + new BasicNameValuePair("client_assertion", signedToken), + new BasicNameValuePair("client_id", this.tokenId)); httpPost.setEntity(new UrlEncodedFormEntity(params)); - CloseableHttpClient client = HttpClients.createSystem(); - userAccessToken = client.execute(httpPost, - response -> { - if (response.getCode() >= 200 && response.getCode() < 300) { - HttpEntity responseEntity = response.getEntity(); - - JsonElement responseAsJson = new Gson().fromJson(EntityUtils.toString(responseEntity), - JsonElement.class); - JsonObject responseObject = responseAsJson.isJsonObject() ? responseAsJson.getAsJsonObject() - : null; - if (responseObject != null && responseObject.get("access_token") != null) { - return responseObject.get("access_token").getAsString(); - } - } - return null; - }); + String userAccessToken = executeHttp(httpPost, "access_token"); if (userAccessToken == null) { throw new AccessTokenGenerationException( "getUserAccessToken : Could not retrieve access_token from the token end point"); } - } catch (JwtGenerationException jwtGenerationException) { + return userAccessToken; + } catch (JwtGenerationException | AccessTokenGenerationException | IOException jwtGenerationException) { throw new AccessTokenGenerationException(jwtGenerationException.getMessage()); - } catch (Exception exception) { - throw new AccessTokenGenerationException(exception.getMessage()); } - return userAccessToken; } /** - * This method generates a projectScopeToken for the configuration - * values associated to the AuthProvider - * + * This method generates a projectScopeToken for the configuration values + * associated to the AuthProvider. + * * @return String * @throws PSTGenerationException */ private String getProjectScopedToken() throws PSTGenerationException { - String projectScopeToken = null; try { String userAccessToken = getUserAccessToken(); - final HttpPost httpPost = new HttpPost(apiGatewayUrl + AuthProviderConstants.projectScopeTokenApiPath); + final HttpPost httpPost = new HttpPost(apiGatewayUrl + AuthProviderConstants.PROJECT_SCOPE_TOKEN_API_PATH); - final List params = new ArrayList(); - params.add(new BasicNameValuePair("projectId", projectId)); + final List params = Arrays.asList( + new BasicNameValuePair("projectId", projectId)); httpPost.setEntity(new UrlEncodedFormEntity(params)); httpPost.setHeader("Authorization", "Bearer " + userAccessToken); httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded"); - CloseableHttpClient client = HttpClients.createSystem(); - projectScopeToken = client.execute(httpPost, - response -> { - if (response.getCode() >= 200 && response.getCode() < 300) { - HttpEntity responseEntity = response.getEntity(); - - JsonElement responseAsJson = new Gson().fromJson(EntityUtils.toString(responseEntity), - JsonElement.class); - JsonObject responseObject = responseAsJson.isJsonObject() ? responseAsJson.getAsJsonObject() - : null; - if (responseObject != null && responseObject.get("accessToken") != null) { - return responseObject.get("accessToken").getAsString(); - } - - } - return null; - }); - if (projectScopeToken == null) { + String token = executeHttp(httpPost, "accessToken"); + if (token == null) { throw new PSTGenerationException("getProjectScopedToken : Could not retrieve accessToken from " - + (apiGatewayUrl + AuthProviderConstants.projectScopeTokenApiPath)); + + (apiGatewayUrl + AuthProviderConstants.PROJECT_SCOPE_TOKEN_API_PATH)); } - } catch (AccessTokenGenerationException accessTokenGenerationException) { - throw new PSTGenerationException(accessTokenGenerationException.getMessage()); - } catch (Exception exception) { - throw new PSTGenerationException(exception.getMessage()); + return token; + } catch (AccessTokenGenerationException | PSTGenerationException | IOException ex) { + throw new PSTGenerationException(ex.getMessage()); } - - return projectScopeToken; } /** - * This method generates a signed jwt for an iota session - * + * This method generates a signed jwt for an iota session. + * * @param iotaConfigId * @param did * @param iotaSessionId @@ -227,14 +187,32 @@ public IotaJwtOutput signIotaJwt(String iotaConfigId, String did, String iotaSes return new IotaJwtOutput(iotaSessionID, iotaJwt); } + private String executeHttp(final HttpPost httpPost, String memberName) throws IOException { + try (CloseableHttpClient client = HttpClients.createSystem()) { + return client.execute(httpPost, + response -> { + if (response.getCode() >= 200 && response.getCode() < 300) { + final HttpEntity responseEntity = response.getEntity(); + final JsonElement responseAsJson = GSON.fromJson(EntityUtils.toString(responseEntity), + JsonElement.class); + final JsonObject responseObject = responseAsJson.isJsonObject() ? responseAsJson.getAsJsonObject() + : null; + if (responseObject != null && responseObject.get(memberName) != null) { + return responseObject.get(memberName).getAsString(); + } + } + return null; + }); + } + } + /** - * This class provides a way to pass configurations to the AuthProvider - * It also helps to build an instance of AuthProvider which uses - * these configurations - * - * + * This class provides a way to pass configurations to the AuthProvider It + * also helps to build an instance of AuthProvider which uses these + * configurations. */ public static class Configurations { + private String projectId; private String tokenId; private String privateKey; @@ -267,9 +245,9 @@ public Configurations passphrase(String passphrase) { } /** - * This method builds an instance of AuthProvider with the - * values passed through {@link Configuration} - * + * This method builds an instance of AuthProvider with the values passed + * through {@link Configuration}. + * * @return * @throws ConfigurationException */ @@ -282,9 +260,9 @@ public AuthProvider build() throws ConfigurationException { } /** - * This method builds an instance of AuthProvider with the - * configuration values present in the .env file - * + * This method builds an instance of AuthProvider with the configuration + * values present in the .env file. + * * @return * @throws ConfigurationException */ @@ -292,16 +270,15 @@ public AuthProvider buildWithEnv() throws ConfigurationException { if (this.projectId != null || this.privateKey != null || this.tokenId != null || this.passphrase != null || this.privateKey != null) { - throw new ConfigurationException("Please do not pass configurations values while using buildWithEnv. " + - " These values will picked from .env. Alternatively you may use build() in order to explicitly pass values"); + throw new ConfigurationException("Please do not pass configurations values while using buildWithEnv. " + + " These values will picked from .env. Alternatively you may use build() in order to explicitly pass values"); } - EnvironmentUtil envUtil = new EnvironmentUtil(); - this.keyId = envUtil.getValueFromEnvConfig(AuthProviderConstants.keyIdPropertyNameinEnv); - this.projectId = envUtil.getValueFromEnvConfig(AuthProviderConstants.projectIdPropertyNameinEnv); - this.passphrase = envUtil.getValueFromEnvConfig(AuthProviderConstants.passphrasePropertyNameinEnv); - this.tokenId = envUtil.getValueFromEnvConfig(AuthProviderConstants.tokenIdPropertyNameinEnv); - this.privateKey = envUtil.getValueFromEnvConfig(AuthProviderConstants.privateKeyPropertyNameinEnv); + this.keyId = EnvironmentUtil.getValueFromEnvConfig(AuthProviderConstants.KEY_ID_PROPERTY_NAME_IN_ENV); + this.projectId = EnvironmentUtil.getValueFromEnvConfig(AuthProviderConstants.PROJECT_ID_PROPERTY_NAME_IN_ENV); + this.passphrase = EnvironmentUtil.getValueFromEnvConfig(AuthProviderConstants.PASSPHRASE_PROPERTY_NAME_IN_ENV); + this.tokenId = EnvironmentUtil.getValueFromEnvConfig(AuthProviderConstants.TOKEN_ID_PROPERTY_NAME_IN_ENV); + this.privateKey = EnvironmentUtil.getValueFromEnvConfig(AuthProviderConstants.PRIVATE_KEY_PROPERTY_NAME_IN_ENV); if (this.projectId == null || this.privateKey == null || this.tokenId == null) { throw new ConfigurationException( diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/AccessTokenGenerationException.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/AccessTokenGenerationException.java index 3f01cf765..c05dfe247 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/AccessTokenGenerationException.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/AccessTokenGenerationException.java @@ -1,9 +1,8 @@ package com.affinidi.tdk.authProvider.exception; -public class AccessTokenGenerationException extends Exception{ +public class AccessTokenGenerationException extends GenericAuthProviderException { public AccessTokenGenerationException(String errorMessage) { super(errorMessage); } } - diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/ConfigurationException.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/ConfigurationException.java index 04d55a7e1..e1f0f7d00 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/ConfigurationException.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/ConfigurationException.java @@ -1,8 +1,8 @@ package com.affinidi.tdk.authProvider.exception; -public class ConfigurationException extends Exception{ +public class ConfigurationException extends GenericAuthProviderException { + public ConfigurationException(String errorMessage) { super(errorMessage); } - } diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/GenericAuthProviderException.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/GenericAuthProviderException.java new file mode 100644 index 000000000..d10400ab6 --- /dev/null +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/GenericAuthProviderException.java @@ -0,0 +1,8 @@ +package com.affinidi.tdk.authProvider.exception; + +public class GenericAuthProviderException extends Exception { + + public GenericAuthProviderException(String errorMessage) { + super(errorMessage); + } +} diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/InvalidPrivateKeyException.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/InvalidPrivateKeyException.java index 1e5625103..5664fbde2 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/InvalidPrivateKeyException.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/InvalidPrivateKeyException.java @@ -1,9 +1,8 @@ package com.affinidi.tdk.authProvider.exception; -public class InvalidPrivateKeyException extends Exception{ +public class InvalidPrivateKeyException extends GenericAuthProviderException { public InvalidPrivateKeyException(String errorMessage) { super(errorMessage); } } - diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/InvalidPublicKeyException.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/InvalidPublicKeyException.java index 1324821a9..a0a0120ae 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/InvalidPublicKeyException.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/InvalidPublicKeyException.java @@ -1,8 +1,8 @@ package com.affinidi.tdk.authProvider.exception; -public class InvalidPublicKeyException extends Exception{ +public class InvalidPublicKeyException extends GenericAuthProviderException { + public InvalidPublicKeyException(String errorMessage) { super(errorMessage); } - } diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/JwtGenerationException.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/JwtGenerationException.java index 08b20c8c8..8ee1a57e8 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/JwtGenerationException.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/JwtGenerationException.java @@ -1,9 +1,8 @@ package com.affinidi.tdk.authProvider.exception; -public class JwtGenerationException extends Exception{ +public class JwtGenerationException extends GenericAuthProviderException { public JwtGenerationException(String errorMessage) { super(errorMessage); } } - diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/PSTGenerationException.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/PSTGenerationException.java index 89d3a6f0d..5958dde90 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/PSTGenerationException.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/exception/PSTGenerationException.java @@ -1,8 +1,8 @@ package com.affinidi.tdk.authProvider.exception; -public class PSTGenerationException extends Exception{ +public class PSTGenerationException extends GenericAuthProviderException { + public PSTGenerationException(String errorMessage) { super(errorMessage); } - } diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/AuthProviderConstants.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/AuthProviderConstants.java index a1bc98c34..6bdcf4fa1 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/AuthProviderConstants.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/AuthProviderConstants.java @@ -1,19 +1,19 @@ package com.affinidi.tdk.authProvider.helper; public interface AuthProviderConstants { - public static final String publicKeyPath = "/iam/.well-known/jwks.json"; - public static final String projectScopeTokenApiPath = "/iam/v1/sts/create-project-scoped-token"; - public static final String contentTypeHeader = "Content-Type"; - public static final String applicationJsonContentType = "application/json"; - public static final String applicationUrlEncodedContentType = "application/x-www-form-urlencoded"; - public static final String keyIdPropertyNameinEnv = "KEY_ID"; - public static final String tokenIdPropertyNameinEnv = "TOKEN_ID"; - public static final String passphrasePropertyNameinEnv = "PASSPHRASE"; - public static final String privateKeyPropertyNameinEnv = "PRIVATE_KEY"; - public static final String projectIdPropertyNameinEnv = "PROJECT_ID"; + public static final String PUBLIC_KEY_PATH = "/iam/.well-known/jwks.json"; + public static final String PROJECT_SCOPE_TOKEN_API_PATH = "/iam/v1/sts/create-project-scoped-token"; + public static final String CONTENT_TYPE_HEADER = "Content-Type"; + public static final String APPLICATION_JSON_CONTENT_TYPE = "application/json"; + public static final String APPLICATION_URL_ENCODED_CONTENT_TYPE = "application/x-www-form-urlencoded"; - // Error messages: - public static final String couldNotDerivePrivateKeyErrorMsg = "Could not derive private key out of the configurations."; + public static final String KEY_ID_PROPERTY_NAME_IN_ENV = "KEY_ID"; + public static final String TOKEN_ID_PROPERTY_NAME_IN_ENV = "TOKEN_ID"; + public static final String PASSPHRASE_PROPERTY_NAME_IN_ENV = "PASSPHRASE"; + public static final String PRIVATE_KEY_PROPERTY_NAME_IN_ENV = "PRIVATE_KEY"; + public static final String PROJECT_ID_PROPERTY_NAME_IN_ENV = "PROJECT_ID"; + // Error messages: + public static final String COULD_NOT_DERIVE_PRIVATE_KEY_ERROR_MSG = "Could not derive private key out of the configurations."; } diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java index 7a7bbb2a8..6d85f689d 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/helper/JwtUtil.java @@ -11,6 +11,8 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.crypto.Cipher; import javax.crypto.EncryptedPrivateKeyInfo; @@ -33,45 +35,46 @@ import com.google.gson.JsonObject; import com.google.gson.JsonSyntaxException; -import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.MalformedJwtException; +import io.jsonwebtoken.security.InvalidKeyException; import io.jsonwebtoken.security.Jwk; import io.jsonwebtoken.security.Jwks; /** - * This class provides utility functions required - * by {@link AuthProvider} related for - * processing(creating, signing, validation) of JWT + * This class provides utility functions required by {@link AuthProvider} + * related for processing(creating, signing, validation) of JWT. * * @author Priyanka - * */ - public class JwtUtil { + private static final Logger LOGGER = Logger.getLogger(JwtUtil.class.getName()); + /** + * Gson is thread safe. + */ + private static final Gson GSON = new Gson(); + /** - * This method builds a JSON web token for the provided claims. - * It then signs the token with the provided privatekey using RS256. - * - * In case of an encrypted private key; this method expects to receive - * the passphrase which was used during generation of the key pair. - * + * This method builds a JSON web token for the provided claims. It then + * signs the token with the provided privatekey using RS256. + * + * In case of an encrypted private key; this method expects to receive the + * passphrase which was used during generation of the key pair. + * * @param tokenId * @param audience * @param privateKeyString * @param passphrase * @param keyId * @return String - * @throws JwtGenerationException in case the private key could not be processed - * or jwt generation failed + * @throws JwtGenerationException in case the private key could not be + * processed or jwt generation failed */ public static String signPayload(String tokenId, String audience, String privateKeyString, String passphrase, String keyId) throws JwtGenerationException { - String signedJwtForClaims = null; - long issueTimeInSeconds = System.currentTimeMillis() / 1000; - Map claims = new HashMap<>(); + final long issueTimeInSeconds = System.currentTimeMillis() / 1000; + final Map claims = new HashMap<>(); claims.put("iss", tokenId); claims.put("sub", tokenId); claims.put("aud", audience); @@ -80,23 +83,20 @@ public static String signPayload(String tokenId, String audience, String private claims.put("iat", issueTimeInSeconds); try { - signedJwtForClaims = generateJwt(derivePrivateKey(privateKeyString, passphrase), claims); - } catch (JwtGenerationException jwtGenerationException) { - throw jwtGenerationException; - } catch (InvalidPrivateKeyException iPrivateKeyExceptione) { - throw new JwtGenerationException(iPrivateKeyExceptione.getMessage()); + return generateJwt(derivePrivateKey(privateKeyString, passphrase), claims); + } catch (InvalidPrivateKeyException ipke) { + throw new JwtGenerationException(ipke.getMessage()); } - return signedJwtForClaims; } /** - * This method builds a JSON web token for the provided claims specific - * to an iota request. - * It then signs the token with the provided privatekey using RS256. - * - * In case of an encrypted private key; this method expects to receive - * the passphrase which was used during generation of the key pair. - * + * This method builds a JSON web token for the provided claims specific to + * an iota request. It then signs the token with the provided privatekey + * using RS256. + * + * In case of an encrypted private key; this method expects to receive the + * passphrase which was used during generation of the key pair. + * * @param tokenId * @param audience * @param privateKeyString @@ -106,15 +106,14 @@ public static String signPayload(String tokenId, String audience, String private * @param iotaConfigId * @param iotaSessionId * @return String - * @throws JwtGenerationException in case the private key could not be processed - * or jwt generation failed + * @throws JwtGenerationException in case the private key could not be + * processed or jwt generation failed */ public static String signIotaPayload(String tokenId, String audience, String privateKeyString, String passphrase, String keyId, String projectId, String iotaConfigId, String iotaSessionId) throws JwtGenerationException { - String signedJwtForClaims = null; - long issueTimeInSeconds = System.currentTimeMillis() / 1000; - Map claims = new HashMap<>(); + final long issueTimeInSeconds = System.currentTimeMillis() / 1000; + final Map claims = new HashMap<>(); claims.put("iss", tokenId); claims.put("sub", tokenId); claims.put("kid", tokenId); @@ -128,96 +127,87 @@ public static String signIotaPayload(String tokenId, String audience, String pri claims.put("scope", "iota_channel"); try { - signedJwtForClaims = generateJwt(derivePrivateKey(privateKeyString, passphrase), claims); - } catch (JwtGenerationException jwtGenerationException) { - throw jwtGenerationException; - } catch (InvalidPrivateKeyException iPrivateKeyExceptione) { - throw new JwtGenerationException(iPrivateKeyExceptione.getMessage()); + return generateJwt(derivePrivateKey(privateKeyString, passphrase), claims); + } catch (JwtGenerationException jge) { + throw jge; + } catch (InvalidPrivateKeyException ipke) { + throw new JwtGenerationException(ipke.getMessage()); } - - return signedJwtForClaims; } /** - * This method fetches the signature verification key, required to - * validate the jws for a projectScopeToken and converts it to - * {@link java.security.PublicKey} - * + * This method fetches the signature verification key, required to validate + * the jws for a projectScopeToken and converts it to + * {@link java.security.PublicKey}. + * * @param apiGatewayUrl * @return PublicKey * @throws InvalidPublicKeyException in case the public key could not be - * retrieved/extracted + * retrieved/extracted */ public static PublicKey fetchPublicKey(String apiGatewayUrl) throws InvalidPublicKeyException { - PublicKey publicKey = null; try { - final HttpGet httpGet = new HttpGet(apiGatewayUrl + AuthProviderConstants.publicKeyPath); - httpGet.setHeader(AuthProviderConstants.contentTypeHeader, - AuthProviderConstants.applicationJsonContentType); + final HttpGet httpGet = new HttpGet(apiGatewayUrl + AuthProviderConstants.PUBLIC_KEY_PATH); + httpGet.setHeader(AuthProviderConstants.CONTENT_TYPE_HEADER, + AuthProviderConstants.APPLICATION_JSON_CONTENT_TYPE); - CloseableHttpClient client = HttpClients.createSystem(); - publicKey = client.execute(httpGet, - response -> { - if (response.getCode() >= 200 && response.getCode() < 300) { - HttpEntity entity = response.getEntity(); - String jwkFromResponse = decodeRespose(entity); - Jwk jwk = Jwks.parser().build() - .parse(jwkFromResponse); - if (jwk != null) { - return (PublicKey) jwk.toKey(); + try (final CloseableHttpClient client = HttpClients.createSystem()) { + return client.execute(httpGet, + response -> { + if (response.getCode() >= 200 && response.getCode() < 300) { + final HttpEntity entity = response.getEntity(); + final String jwkFromResponse = decodeResponse(entity); + final Jwk jwk = Jwks.parser().build() + .parse(jwkFromResponse); + if (jwk != null) { + return (PublicKey) jwk.toKey(); + } + } else { + String resp = decodeResponse(response.getEntity()); + LOGGER.log(Level.INFO, "fetchPublicKey entity: {0}", resp); } - } else { - String resp = decodeRespose(response.getEntity()); - System.out.println(resp.toString()); - } - return null; - }); - } catch (Exception exception) { - throw new InvalidPublicKeyException("Could not retreive/ extract the public " + - "key required to validate projectScopeToken " + exception.getMessage()); + return null; + }); + } + } catch (IOException exception) { + throw new InvalidPublicKeyException("Could not retrieve/ extract the public " + + "key required to validate projectScopeToken " + exception.getMessage()); } - return publicKey; } /** - * This method validates the projectScopeToken. - * Along with validating if the token is malformed or expired; - * it also verifies the token signature using the - * signature verification public key. - * - * If the public key could not be retrieved for any reason; - * the method would consider the token as invalid. - * + * This method validates the projectScopeToken. Along with validating if the + * token is malformed or expired; it also verifies the token signature using + * the signature verification public key. + * + * If the public key could not be retrieved for any reason; the method would + * consider the token as invalid. + * * @param token * @param apiGatewayUrl * @return boolean */ public static boolean validProjectTokenPresent(String token, String apiGatewayUrl) { try { - PublicKey publicKey = fetchPublicKey(apiGatewayUrl); + final PublicKey publicKey = fetchPublicKey(apiGatewayUrl); if (publicKey == null) { throw new Exception("Could not retrieve public key for token validation"); } Jwts.parser().verifyWith(publicKey).build().parse(token); - } catch (InvalidPublicKeyException iPublicKeyException) { - return false; - } catch (ExpiredJwtException ejException) { - return false; - } catch (MalformedJwtException mjException) { - return false; - } catch (Exception eException) { - return false; + return true; + } catch (Exception e) { + // ignore } - return true; + return false; } /** * This method converts the private key string passed to a - * {@link java.security.PrivateKey}. In case a passphrase is passed, - * the private key is treated as encrypted and processed accordingly. - * This passphrase should be same as the one used to create the - * public-private key pair - * + * {@link java.security.PrivateKey}. In case a passphrase is passed, the + * private key is treated as encrypted and processed accordingly. This + * passphrase should be same as the one used to create the public-private + * key pair. + * * @param privateKeyString * @param passphrase * @return PrivateKey @@ -225,98 +215,91 @@ public static boolean validProjectTokenPresent(String token, String apiGatewayUr */ private static PrivateKey derivePrivateKey(String privateKeyString, String passphrase) throws InvalidPrivateKeyException { - - PrivateKey privateKey = null; try { if (passphrase.isEmpty()) { - privateKey = getPrivateKeyFromString(privateKeyString); - } else { - privateKey = getEncryptedPrivateKeyFromString(privateKeyString, passphrase); + return getPrivateKeyFromString(privateKeyString); } + return getEncryptedPrivateKeyFromString(privateKeyString, passphrase); } catch (Exception exception) { throw new InvalidPrivateKeyException( - AuthProviderConstants.couldNotDerivePrivateKeyErrorMsg + - " Exception : " + exception.toString()); + AuthProviderConstants.COULD_NOT_DERIVE_PRIVATE_KEY_ERROR_MSG + + " Exception : " + exception.toString()); } - return privateKey; } /** - * This method generates a JSON payload representing all the - * claims passed. It then signs this payload with the - * {@link java.security.PrivateKey}, creating a json web - * signature (JWS) and then builds the JWT as a URL-safe - * string - * + * This method generates a JSON payload representing all the claims passed. + * It then signs this payload with the {@link java.security.PrivateKey}, + * creating a json web signature (JWS) and then builds the JWT as a URL-safe + * string. + * * @param privateKey * @param claims * @return String - * @throws JwtGenerationException when the jwt generation fails. For instance if - * the private key is insufficient + * @throws JwtGenerationException when the jwt generation fails. For + * instance if the private key is insufficient */ private static String generateJwt(PrivateKey privateKey, Map claims) throws JwtGenerationException { - String jwToken = null; try { - jwToken = Jwts.builder() + return Jwts.builder() .claims(claims) .signWith(privateKey, Jwts.SIG.RS256) .compact(); - } catch (Exception exception) { + } catch (InvalidKeyException ike) { throw new JwtGenerationException( - " Could not generate the JWT representing the claims. Exception" + exception.getMessage()); + " Could not generate the JWT representing the claims. Exception" + ike.getMessage()); } - return jwToken; } /** * This method converts a private key string to - * {@link java.security.PrivateKey} object - * + * {@link java.security.PrivateKey} object. + * * @param privateKeyPEM * @return PrivateKey * @throws Exception */ private static PrivateKey getPrivateKeyFromString(String privateKeyPEM) throws Exception { - String privateKeyContent = extractPrivateKeyContent(privateKeyPEM); - byte[] encoded = Base64.getDecoder().decode(privateKeyContent); - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded); + final String privateKeyContent = extractPrivateKeyContent(privateKeyPEM); + final byte[] encoded = Base64.getDecoder().decode(privateKeyContent); + final KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + final PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded); return keyFactory.generatePrivate(keySpec); } /** * This method converts an encrypted private key string to - * {@link java.security.PrivateKey} object - * The passphrase which was used to generate the public-private - * keypair should be passed to this method for processing. - * + * {@link java.security.PrivateKey} object The passphrase which was used to + * generate the public-private keypair should be passed to this method for + * processing. + * * @param encryptedPrivateKeyPEM * @param password * @return PrivateKey - * @throws Exception + * @throws InvalidKeySpecException */ private static PrivateKey getEncryptedPrivateKeyFromString(String encryptedPrivateKeyPEM, String password) throws Exception { - String privateKeyContent = extractPrivateKeyContent(encryptedPrivateKeyPEM); - byte[] encodedKey = Base64.getDecoder().decode(privateKeyContent); - EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(encodedKey); - Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName()); - PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray()); - SecretKeyFactory secFac = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName()); - Key pbeKey = secFac.generateSecret(pbeKeySpec); - AlgorithmParameters algParams = encryptedPrivateKeyInfo.getAlgParameters(); + final String privateKeyContent = extractPrivateKeyContent(encryptedPrivateKeyPEM); + final byte[] encodedKey = Base64.getDecoder().decode(privateKeyContent); + final EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(encodedKey); + final Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName()); + final PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray()); + final SecretKeyFactory secFac = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName()); + final Key pbeKey = secFac.generateSecret(pbeKeySpec); + final AlgorithmParameters algParams = encryptedPrivateKeyInfo.getAlgParameters(); cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams); - PKCS8EncodedKeySpec pkcs8KeySpec = encryptedPrivateKeyInfo.getKeySpec(cipher); - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + final PKCS8EncodedKeySpec pkcs8KeySpec = encryptedPrivateKeyInfo.getKeySpec(cipher); + final KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePrivate(pkcs8KeySpec); } /** - * This method cleans up the private key string which is required to - * decode the key material - * + * This method cleans up the private key string which is required to decode + * the key material. + * * @param privateKey * @return String */ @@ -327,35 +310,32 @@ private static String extractPrivateKeyContent(String privateKey) { } /** - * This method processes the response from the public key API call and extracts - * the first key listed from the response - * - * + * This method processes the response from the public key API call and + * extracts the first key listed from the response. + * * @param responseEntity * @return String * @throws IOException * @throws JsonSyntaxException * @throws ParseException */ - private static String decodeRespose(HttpEntity responseEntity) + private static String decodeResponse(HttpEntity responseEntity) throws IOException, JsonSyntaxException, ParseException { - String extractedJwkFromResponse = null; - if (responseEntity != null) { - JsonElement responseAsJson = new Gson().fromJson(EntityUtils.toString(responseEntity), JsonElement.class); - JsonObject responseObject = responseAsJson.isJsonObject() ? responseAsJson.getAsJsonObject() : null; - - if (responseObject != null) { - JsonArray setOfkeys = responseObject.getAsJsonArray("keys"); - if (setOfkeys != null) { - - JsonObject firstKeysAsObject = (setOfkeys != null && setOfkeys.size() > 0) - ? setOfkeys.get(0).getAsJsonObject() - : null; - extractedJwkFromResponse = (firstKeysAsObject != null) ? firstKeysAsObject.toString() : null; - } - } - + if (responseEntity == null) { + return null; + } + final JsonElement responseAsJson = GSON.fromJson(EntityUtils.toString(responseEntity), JsonElement.class); + final JsonObject responseObject = responseAsJson.isJsonObject() ? responseAsJson.getAsJsonObject() : null; + if (responseObject == null) { + return null; + } + final JsonArray setOfKeys = responseObject.getAsJsonArray("keys"); + if (setOfKeys == null) { + return null; } - return extractedJwkFromResponse; + final JsonObject firstKeysAsObject = setOfKeys.size() > 0 + ? setOfKeys.get(0).getAsJsonObject() + : null; + return (firstKeysAsObject != null) ? firstKeysAsObject.toString() : null; } } diff --git a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/types/IotaJwtOutput.java b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/types/IotaJwtOutput.java index a8fd64968..2f0da14b8 100644 --- a/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/types/IotaJwtOutput.java +++ b/packages/java/auth.provider/src/main/java/com/affinidi/tdk/authProvider/types/IotaJwtOutput.java @@ -1,18 +1,18 @@ package com.affinidi.tdk.authProvider.types; /** -* This class represents the output returned -* when {@link AuthProvider#signIotaJwt()} is invoked -* -* -* @author Priyanka -* -*/ + * This class represents the output returned when + * {@link AuthProvider#signIotaJwt()} is invoked + * + * @author Priyanka + * + */ public class IotaJwtOutput { + private final String iotaSessionId; private final String iotaJwt; - public IotaJwtOutput(String iotaSessionId, String iotaJwt){ + public IotaJwtOutput(String iotaSessionId, String iotaJwt) { this.iotaJwt = iotaJwt; this.iotaSessionId = iotaSessionId; } @@ -20,9 +20,8 @@ public IotaJwtOutput(String iotaSessionId, String iotaJwt){ public String getIotaSessionId() { return iotaSessionId; } + public String getIotaJwt() { return iotaJwt; } - - } diff --git a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/AuthProviderTest.java b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/AuthProviderTest.java index c1995ff17..3e53ca3c8 100644 --- a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/AuthProviderTest.java +++ b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/AuthProviderTest.java @@ -1,27 +1,25 @@ package com.affinidi.tdk.authProvider; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Paths; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EmptySource; import org.junit.jupiter.params.provider.ValueSource; +import static org.mockito.ArgumentMatchers.any; import org.mockito.MockedStatic; import org.mockito.Mockito; -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.givenThat; -import static com.github.tomakehurst.wiremock.client.WireMock.okJson; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; import com.affinidi.tdk.authProvider.exception.ConfigurationException; import com.affinidi.tdk.authProvider.exception.InvalidPublicKeyException; @@ -29,11 +27,21 @@ import com.affinidi.tdk.authProvider.exception.PSTGenerationException; import com.affinidi.tdk.authProvider.helper.AuthProviderConstants; import com.affinidi.tdk.authProvider.helper.JwtUtil; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.givenThat; +import static com.github.tomakehurst.wiremock.client.WireMock.okJson; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import com.github.tomakehurst.wiremock.junit5.WireMockTest; public class AuthProviderTest { + @Nested @DisplayName("setting values to the authProvider configurations") + @SuppressWarnings("unused") class ConfigurationsTest { + @Test void testAuthProviderConfiguration() throws Exception { AuthProvider provider = new AuthProvider.Configurations() @@ -52,11 +60,13 @@ void testAuthProviderConfiguration() throws Exception { @WireMockTest(proxyMode = true) @Nested @DisplayName("fetchProjectScopedToken method") + @SuppressWarnings("unused") class FetchProjectScopedTokenTest { + @ParameterizedTest @DisplayName("given an invalid private-key and a empty or non-empty passphrase, the it throws") @EmptySource - @ValueSource(strings = { "complicated-word" }) + @ValueSource(strings = {"complicated-word"}) void givenInvalidPrivateKey_thenThrows(String phrase) { Exception exception = assertThrows(PSTGenerationException.class, () -> { AuthProvider provider = new AuthProvider.Configurations() @@ -76,26 +86,24 @@ void givenInvalidPrivateKey_thenThrows(String phrase) { void givenInvalidApiGatewayUrl_thenThrows() { // arrange String mockErrorMessage = "mock-exception-message"; - MockedStatic utilsMock = Mockito.mockStatic(JwtUtil.class); - utilsMock.when(() -> JwtUtil.fetchPublicKey(any())).thenThrow(InvalidPublicKeyException.class); - utilsMock.when(() -> JwtUtil.signPayload(any(), any(), any(), any(), any())) - .thenThrow(new JwtGenerationException(mockErrorMessage)); - - // act - Exception exception = assertThrows(PSTGenerationException.class, () -> { - AuthProvider provider = new AuthProvider.Configurations() - .projectId("test-project") - .tokenId("test-token") - .privateKey("test-key") - .build(); - provider.fetchProjectScopedToken(); - }); - - // assert - assertEquals(mockErrorMessage, exception.getMessage()); - - // cleanup - utilsMock.close(); + try (MockedStatic utilsMock = Mockito.mockStatic(JwtUtil.class)) { + utilsMock.when(() -> JwtUtil.fetchPublicKey(any())).thenThrow(InvalidPublicKeyException.class); + utilsMock.when(() -> JwtUtil.signPayload(any(), any(), any(), any(), any())) + .thenThrow(new JwtGenerationException(mockErrorMessage)); + + // act + Exception exception = assertThrows(PSTGenerationException.class, () -> { + AuthProvider provider = new AuthProvider.Configurations() + .projectId("test-project") + .tokenId("test-token") + .privateKey("test-key") + .build(); + provider.fetchProjectScopedToken(); + }); + + // assert + assertEquals(mockErrorMessage, exception.getMessage()); + } } @Test @@ -109,7 +117,7 @@ void givenInvalidPrivateKey_AndApiKeyResponse_thenThrows(WireMockRuntimeInfo wmR String apiKeyJson = new String( Files.readAllBytes(Paths.get( "src/test/java/com/affinidi/tdk/authProvider/resources/api-key-response.json"))); - givenThat(get(AuthProviderConstants.publicKeyPath) + givenThat(get(AuthProviderConstants.PUBLIC_KEY_PATH) .withHost(equalTo(host)) .willReturn(okJson(apiKeyJson))); @@ -128,11 +136,11 @@ void givenInvalidPrivateKey_AndApiKeyResponse_thenThrows(WireMockRuntimeInfo wmR // assert assertTrue(exception.getMessage() - .startsWith(AuthProviderConstants.couldNotDerivePrivateKeyErrorMsg + " Exception : ")); + .startsWith(AuthProviderConstants.COULD_NOT_DERIVE_PRIVATE_KEY_ERROR_MSG + " Exception : ")); } @Test - @DisplayName("happy path: given a validprivate-key, when all the endponts succesfully returns 200, then it returns a JWT") + @DisplayName("happy path: given a valid private-key, when all the endponts succesfully returns 200, then it returns a JWT") void givenValidApiKeyResponse_AndSuccessfulApiCalls_thenReturnsAJWT(WireMockRuntimeInfo wmRuntimeInfo) throws IOException, URISyntaxException, ConfigurationException { // arrange @@ -154,7 +162,7 @@ void givenValidApiKeyResponse_AndSuccessfulApiCalls_thenReturnsAJWT(WireMockRunt givenThat(post("/auth-token") .withHost(equalTo(host)) .willReturn(okJson("{access_token: \"some-access-token\"}"))); - givenThat(post(AuthProviderConstants.projectScopeTokenApiPath).withHost(equalTo(host)) + givenThat(post(AuthProviderConstants.PROJECT_SCOPE_TOKEN_API_PATH).withHost(equalTo(host)) .willReturn(okJson("{accessToken: \"some-project-scope-token\"}"))); // act and assert @@ -177,4 +185,4 @@ void testSignIotaJwt() { } } -} \ No newline at end of file +} diff --git a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/JwtUtilTest.java b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/JwtUtilTest.java index 4b287d5bb..41d8fa850 100644 --- a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/JwtUtilTest.java +++ b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/helper/JwtUtilTest.java @@ -1,36 +1,33 @@ package com.affinidi.tdk.authProvider.helper; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class JwtUtilTest { - - - @BeforeEach + @BeforeEach void setUp() { - + } @Test void testFetchPublicKey() { - + } @Test void testSignIotaPayload() { - + } @Test void testSignPayload() { - + } @Test void testValidProjectTokenPresent() { - + } } diff --git a/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java b/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java index d87c158f4..744975fe8 100644 --- a/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java +++ b/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java @@ -14,9 +14,7 @@ * project base using AFFINIDI_TDK_PROPERTY_NAME or * NEXT_PUBLIC_AFFINIDI_TDK_ENVIRONMENT as dev, local or prod. * - * * @author Priyanka - * */ public class EnvironmentUtil { @@ -29,7 +27,7 @@ public static Dotenv getProperties() { /** * Returns the environment name as configured in the .env file In case no - * configuration is found, the default return is production environment + * configuration is found, the default return is production environment. * * @return String */ @@ -54,7 +52,7 @@ public static String getConfiguredEnvironment() { } /** - * Return the default region name + * Return the default region name. * * @return String */ @@ -63,7 +61,7 @@ public static String getDefaultRegion() { } /** - * Returns the IOT url string for the configured environment + * Returns the IOT url string for the configured environment. * * @return String */ @@ -72,7 +70,8 @@ public static String getIotUrlForEnvironment() { } /** - * Returns the elements auth token url string for the configured environment + * Returns the elements auth token url string for the configured + * environment. * * @return String */ @@ -81,7 +80,7 @@ public static String getElementAuthTokenUrlForEnvironment() { } /** - * Returns the vault URL for the configured environment + * Returns the vault URL for the configured environment. * * @return String */ @@ -100,13 +99,12 @@ public static String getApiGatewayUrlForEnvironment() { * @return String */ public static String getValueFromEnvConfig(String propertyName) { - String propertyValue = null; try { - propertyValue = getProperties().get(propertyName); + return getProperties().get(propertyName); } catch (Exception exception) { LOGGER.log(Level.SEVERE, "Could not read .env file for {0}", propertyName); } - return propertyValue; + return null; } /** @@ -115,8 +113,6 @@ public static String getValueFromEnvConfig(String propertyName) { * @return String */ static Environment getEnvironmentDetail() { - System.out.println("XXX"); - final String envName = getConfiguredEnvironment(); final Environment envDetail = (envName != null) ? Environment.getEnvSpecificDetails(envName) : null; diff --git a/packages/java/common/src/main/java/com/affinidi/tdk/common/VaultUtil.java b/packages/java/common/src/main/java/com/affinidi/tdk/common/VaultUtil.java index 6041bc1d4..72d872f15 100644 --- a/packages/java/common/src/main/java/com/affinidi/tdk/common/VaultUtil.java +++ b/packages/java/common/src/main/java/com/affinidi/tdk/common/VaultUtil.java @@ -8,11 +8,10 @@ /** * This class provides utility functions required to create vault specific - * links. Refer {@EnvironmentUtil} for more information + * links. Refer {@EnvironmentUtil} for more information. * * * @author Priyanka - * */ public final class VaultUtil { @@ -26,7 +25,7 @@ public final class VaultUtil { /** * Returns the credential offer claim link specific to the configured - * environment + * environment. * * @param credentialOfferUri * @return String @@ -46,7 +45,7 @@ public static String buildClaimLink(String credentialOfferUri) { /** * Returns the vault share link given request and a client id, specific to - * the configured environment + * the configured environment. * * @param request * @param clientId @@ -60,7 +59,5 @@ public static String buildShareLink(String request, String clientId) { URI shareUri = UriBuilder.fromUri(webVaultUrl). queryParam(SHARE_REQUEST_PARAM, request).queryParam(SHARE_CLIENT_PARAM, clientId).build(); return shareUri.toString(); - } - } From 656ed03771e522b9dceffbce5bb29365be7a98e3 Mon Sep 17 00:00:00 2001 From: aeffinidi <86773100+aeffinidi@users.noreply.github.com> Date: Wed, 22 Jan 2025 12:18:01 +0100 Subject: [PATCH 15/15] test: auth provider shouldRefreshToken unit tests (#493) * test: add first shouldRefreshToken function test * test: when api-key endpoint call fails * test: when api-key endpint succeeds but the project-score-token is invalid * fix: remove line breaks --- .../tdk/authProvider/AuthProviderTest.java | 97 ++++++++++++++++--- .../affinidi/tdk/common/EnvironmentUtil.java | 16 ++- 2 files changed, 96 insertions(+), 17 deletions(-) diff --git a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/AuthProviderTest.java b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/AuthProviderTest.java index 3e53ca3c8..ff0eb4dea 100644 --- a/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/AuthProviderTest.java +++ b/packages/java/auth.provider/src/test/java/com/affinidi/tdk/authProvider/AuthProviderTest.java @@ -27,21 +27,20 @@ import com.affinidi.tdk.authProvider.exception.PSTGenerationException; import com.affinidi.tdk.authProvider.helper.AuthProviderConstants; import com.affinidi.tdk.authProvider.helper.JwtUtil; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.givenThat; import static com.github.tomakehurst.wiremock.client.WireMock.okJson; import static com.github.tomakehurst.wiremock.client.WireMock.post; +import com.github.tomakehurst.wiremock.http.Fault; import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; import com.github.tomakehurst.wiremock.junit5.WireMockTest; public class AuthProviderTest { - @Nested @DisplayName("setting values to the authProvider configurations") - @SuppressWarnings("unused") class ConfigurationsTest { - @Test void testAuthProviderConfiguration() throws Exception { AuthProvider provider = new AuthProvider.Configurations() @@ -60,13 +59,11 @@ void testAuthProviderConfiguration() throws Exception { @WireMockTest(proxyMode = true) @Nested @DisplayName("fetchProjectScopedToken method") - @SuppressWarnings("unused") class FetchProjectScopedTokenTest { - @ParameterizedTest @DisplayName("given an invalid private-key and a empty or non-empty passphrase, the it throws") @EmptySource - @ValueSource(strings = {"complicated-word"}) + @ValueSource(strings = { "complicated-word" }) void givenInvalidPrivateKey_thenThrows(String phrase) { Exception exception = assertThrows(PSTGenerationException.class, () -> { AuthProvider provider = new AuthProvider.Configurations() @@ -78,7 +75,8 @@ void givenInvalidPrivateKey_thenThrows(String phrase) { provider.fetchProjectScopedToken(); }); - assertTrue(exception.getMessage().startsWith("Could not derive private key out of the configurations.")); + assertTrue(exception.getMessage() + .startsWith("Could not derive private key out of the configurations.")); } @Test @@ -87,7 +85,8 @@ void givenInvalidApiGatewayUrl_thenThrows() { // arrange String mockErrorMessage = "mock-exception-message"; try (MockedStatic utilsMock = Mockito.mockStatic(JwtUtil.class)) { - utilsMock.when(() -> JwtUtil.fetchPublicKey(any())).thenThrow(InvalidPublicKeyException.class); + utilsMock.when(() -> JwtUtil.fetchPublicKey(any())) + .thenThrow(InvalidPublicKeyException.class); utilsMock.when(() -> JwtUtil.signPayload(any(), any(), any(), any(), any())) .thenThrow(new JwtGenerationException(mockErrorMessage)); @@ -136,7 +135,8 @@ void givenInvalidPrivateKey_AndApiKeyResponse_thenThrows(WireMockRuntimeInfo wmR // assert assertTrue(exception.getMessage() - .startsWith(AuthProviderConstants.COULD_NOT_DERIVE_PRIVATE_KEY_ERROR_MSG + " Exception : ")); + .startsWith(AuthProviderConstants.COULD_NOT_DERIVE_PRIVATE_KEY_ERROR_MSG + + " Exception : ")); } @Test @@ -148,6 +148,9 @@ void givenValidApiKeyResponse_AndSuccessfulApiCalls_thenReturnsAJWT(WireMockRunt URI uri = new URI(apiUrl); String host = uri.getHost(); String fakeTokenUrl = apiUrl + "/auth-token"; + String apiKeyJson = new String( + Files.readAllBytes(Paths.get( + "src/test/java/com/affinidi/tdk/authProvider/resources/api-key-response.json"))); String testPrivateKey = new String( Files.readAllBytes(Paths.get( "src/test/java/com/affinidi/tdk/authProvider/resources/test-private-key.txt"))); @@ -159,6 +162,9 @@ void givenValidApiKeyResponse_AndSuccessfulApiCalls_thenReturnsAJWT(WireMockRunt .build(); provider.setApiGatewayUrl(apiUrl); provider.setTokenEndPoint(fakeTokenUrl); + givenThat(get(AuthProviderConstants.PUBLIC_KEY_PATH) + .withHost(equalTo(host)) + .willReturn(okJson(apiKeyJson))); givenThat(post("/auth-token") .withHost(equalTo(host)) .willReturn(okJson("{access_token: \"some-access-token\"}"))); @@ -169,20 +175,87 @@ void givenValidApiKeyResponse_AndSuccessfulApiCalls_thenReturnsAJWT(WireMockRunt String token = assertDoesNotThrow(() -> provider.fetchProjectScopedToken()); assertEquals("some-project-scope-token", token); } + } + @WireMockTest(proxyMode = true) + @Nested + @DisplayName("shouldRefreshToken method") + class ShouldRefreshTokenTest { @Test - void testGetUserAccessToken() { + @DisplayName("given no project-token, then it returns true") + void givenNoProjectToken_thenReturnsTrue() throws ConfigurationException { + // arrange + AuthProvider provider = new AuthProvider.Configurations() + .projectId("test-project") + .tokenId("test-token") + .privateKey("test-key") + .build(); + // act and assert + assertTrue(provider.shouldRefreshToken()); } @Test - void testShouldRefreshToken() { + @DisplayName("given a project-token, when the api-key endpoint call fails, then it returns true") + void givenProjectToken_whenTheApiKeyEndpointCallFails_thenReturnsTrue(WireMockRuntimeInfo wmRuntimeInfo) + throws ConfigurationException, URISyntaxException { + // arrange + String apiUrl = wmRuntimeInfo.getHttpBaseUrl(); + URI uri = new URI(apiUrl); + String host = uri.getHost(); + givenThat(get(AuthProviderConstants.PUBLIC_KEY_PATH) + .withHost(equalTo(host)) + .willReturn(aResponse().withFault(Fault.MALFORMED_RESPONSE_CHUNK))); + + // act + AuthProvider provider = new AuthProvider.Configurations() + .projectId("test-project") + .tokenId("test-token") + .privateKey("test-key") + .passphrase("") + .build(); + provider.setApiGatewayUrl(apiUrl); + provider.setProjectScopeToken("test-project-scope-token"); + // assert + assertTrue(provider.shouldRefreshToken()); } @Test - void testSignIotaJwt() { + @DisplayName("given an invalid project-token, when the api-key endpoint call succeeds, then it returns true") + void givenInvalidPrivateKey_WhenTheApiKeyEndpointCallSucceeds_thenReturnsTrue( + WireMockRuntimeInfo wmRuntimeInfo) throws URISyntaxException, IOException, ConfigurationException { + // arrange + String apiUrl = wmRuntimeInfo.getHttpBaseUrl(); + URI uri = new URI(apiUrl); + String host = uri.getHost(); + String apiKeyJson = new String( + Files.readAllBytes(Paths.get( + "src/test/java/com/affinidi/tdk/authProvider/resources/api-key-response.json"))); + givenThat(get(AuthProviderConstants.PUBLIC_KEY_PATH) + .withHost(equalTo(host)) + .willReturn(okJson(apiKeyJson))); + AuthProvider provider = new AuthProvider.Configurations() + .projectId("test-project") + .tokenId("test-token") + .privateKey("test-key") + .passphrase("") + .build(); + provider.setApiGatewayUrl(apiUrl); + provider.setProjectScopeToken("test-project-scope-token"); + // act and assert + assertTrue(provider.shouldRefreshToken()); } } + + @Test + void testGetUserAccessToken() { + + } + + @Test + void testSignIotaJwt() { + + } } diff --git a/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java b/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java index 744975fe8..57604afd2 100644 --- a/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java +++ b/packages/java/common/src/main/java/com/affinidi/tdk/common/EnvironmentUtil.java @@ -45,7 +45,8 @@ public static String getConfiguredEnvironment() { } if (configuredEnvironment == null) { - LOGGER.log(Level.SEVERE, "Could not find environment details for {0}. Defaulting to production", configuredEnvironment); + LOGGER.log(Level.SEVERE, "Could not find environment details for {0}. Defaulting to production", + configuredEnvironment); configuredEnvironment = Environment.PRODUCTION.environmentName; } return configuredEnvironment; @@ -119,16 +120,21 @@ static Environment getEnvironmentDetail() { if (envDetail != null) { return envDetail; } - LOGGER.log(Level.SEVERE, "Could not find environment details for the specified name {0}. Hence defaulting to production", envName); + LOGGER.log(Level.SEVERE, + "Could not find environment details for the specified name {0}. Hence defaulting to production", + envName); return Environment.getEnvSpecificDetails(Environment.PRODUCTION.environmentName); } } enum Environment { - LOCAL("LOCAL", Environment.LOCAL_IOT_URL, Environment.LOCAL_APIGATEWAY_URL, Environment.LOCAL_ELEMENTS_AUTH_TOKEN_URL, Environment.LOCAL_VAULT_URL), - DEVELOPMENT("DEV", Environment.DEV_IOT_URL, Environment.DEV_APIGATEWAY_URL, Environment.DEV_ELEMENTS_AUTH_TOKEN_URL, Environment.DEV_VAULT_URL), - PRODUCTION("PROD", Environment.PROD_IOT_URL, Environment.PROD_APIGATEWAY_URL, Environment.PROD_ELEMENTS_AUTH_TOKEN_URL, Environment.PROD_VAULT_URL),; + LOCAL("LOCAL", Environment.LOCAL_IOT_URL, Environment.LOCAL_APIGATEWAY_URL, + Environment.LOCAL_ELEMENTS_AUTH_TOKEN_URL, Environment.LOCAL_VAULT_URL), + DEVELOPMENT("DEV", Environment.DEV_IOT_URL, Environment.DEV_APIGATEWAY_URL, Environment.DEV_ELEMENTS_AUTH_TOKEN_URL, + Environment.DEV_VAULT_URL), + PRODUCTION("PROD", Environment.PROD_IOT_URL, Environment.PROD_APIGATEWAY_URL, + Environment.PROD_ELEMENTS_AUTH_TOKEN_URL, Environment.PROD_VAULT_URL),; static Environment getEnvSpecificDetails(String environmentName) { return EnvironmentMap.get(environmentName.toUpperCase());