From be781415080e0353be03131505abb3b4e64c8306 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Thu, 3 Aug 2023 10:47:57 -0500 Subject: [PATCH 01/35] Update Copyright year. --- copyright_header.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copyright_header.txt b/copyright_header.txt index 9206653f..322a50f3 100644 --- a/copyright_header.txt +++ b/copyright_header.txt @@ -1,4 +1,4 @@ -Copyright 2017 Adobe. All rights reserved. +Copyright 2023 Adobe. All rights reserved. This file is licensed to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 From cc1b8a05dcf03a97b02c2ee7c2b8a80c61e2b615 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Thu, 3 Aug 2023 10:50:43 -0500 Subject: [PATCH 02/35] Use JUnit Jupiter --- pom.xml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 337fddf9..06796650 100644 --- a/pom.xml +++ b/pom.xml @@ -98,7 +98,7 @@ 1.6 3.6 - 4.11 + 5.9.2 2.32.0 4.9.1 4.4.0 @@ -213,8 +213,8 @@ - junit - junit + org.junit.jupiter + junit-jupiter ${junit.version} test @@ -279,10 +279,15 @@ org.apache.maven.plugins maven-jar-plugin + + + + + + - com.mycila - license-maven-plugin - + org.jacoco + jacoco-maven-plugin org.sonatype.plugins @@ -388,8 +393,8 @@ maven-compiler-plugin 3.10.1 - 1.8 - 1.8 + 11 + 11 From 3317b6ab2b98962d9e162f5fb1d3aa2f8b4eeb96 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Thu, 3 Aug 2023 14:34:54 -0500 Subject: [PATCH 03/35] Add Mockito and some tests. --- core/pom.xml | 12 +++++-- .../aio/feign/AIOHeaderInterceptorTest.java | 35 +++++++++++++++++++ pom.xml | 6 ++++ 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 core/src/test/java/com/adobe/aio/feign/AIOHeaderInterceptorTest.java diff --git a/core/pom.xml b/core/pom.xml index c768436d..17f42166 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -64,8 +64,16 @@ - junit - junit + org.junit.jupiter + junit-jupiter + + + org.mockito + mockito-core + + + org.mockito + mockito-junit-jupiter org.slf4j diff --git a/core/src/test/java/com/adobe/aio/feign/AIOHeaderInterceptorTest.java b/core/src/test/java/com/adobe/aio/feign/AIOHeaderInterceptorTest.java new file mode 100644 index 00000000..9e1e4c57 --- /dev/null +++ b/core/src/test/java/com/adobe/aio/feign/AIOHeaderInterceptorTest.java @@ -0,0 +1,35 @@ +package com.adobe.aio.feign; + +import com.adobe.aio.workspace.Workspace; +import feign.RequestTemplate; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static com.adobe.aio.util.Constants.*; +import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.*; + +@ExtendWith(MockitoExtension.class) +public class AIOHeaderInterceptorTest { + + @Mock + private Workspace workspace; + + private static final String imsOrgId = "IMS ORG Id"; + private static final String apiKey = "API KEY"; + + @Test + void apply() { + RequestTemplate requestTemplate = new RequestTemplate(); + when(workspace.getApiKey()).thenReturn(apiKey); + when(workspace.getImsOrgId()).thenReturn(imsOrgId); + + final AIOHeaderInterceptor interceptor = AIOHeaderInterceptor.builder().workspace(workspace).build(); + interceptor.apply(requestTemplate); + + assertEquals(apiKey, requestTemplate.headers().get(API_KEY_HEADER).stream().findFirst().get()); + assertEquals(imsOrgId, requestTemplate.headers().get(IMS_ORG_HEADER).stream().findFirst().get()); + } +} diff --git a/pom.xml b/pom.xml index 06796650..89302c9d 100644 --- a/pom.xml +++ b/pom.xml @@ -246,6 +246,12 @@ ${mockito.version} test + + org.mockito + mockito-junit-jupiter + ${mockito.version} + test + From 31306600da4402c8bc25861268b08c8af1167261 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Thu, 3 Aug 2023 14:36:12 -0500 Subject: [PATCH 04/35] Upgrade to JUnit Jupiter. --- .../test/java/com/adobe/aio/util/FileUtilTest.java | 14 ++++++-------- .../java/com/adobe/aio/util/JacksonUtilTest.java | 14 ++++++-------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/core/src/test/java/com/adobe/aio/util/FileUtilTest.java b/core/src/test/java/com/adobe/aio/util/FileUtilTest.java index 7d91cc96..f4bd618d 100644 --- a/core/src/test/java/com/adobe/aio/util/FileUtilTest.java +++ b/core/src/test/java/com/adobe/aio/util/FileUtilTest.java @@ -11,15 +11,13 @@ */ package com.adobe.aio.util; -import static org.junit.Assert.assertEquals; - -import com.adobe.aio.util.FileUtil; - import java.util.HashMap; import java.util.Map; import java.util.Properties; -import org.junit.Assert; -import org.junit.Test; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; public class FileUtilTest { @@ -42,8 +40,8 @@ public void testGetMapFromProperties() { @Test public void testReadPropertiesFromFile() { - Assert.assertFalse(FileUtil.readPropertiesFromFile("").isPresent()); - Assert.assertFalse(FileUtil.readPropertiesFromFile(null).isPresent()); + assertFalse(FileUtil.readPropertiesFromFile("").isPresent()); + assertFalse(FileUtil.readPropertiesFromFile(null).isPresent()); } @Test diff --git a/core/src/test/java/com/adobe/aio/util/JacksonUtilTest.java b/core/src/test/java/com/adobe/aio/util/JacksonUtilTest.java index 9f1d1782..3e206d1e 100644 --- a/core/src/test/java/com/adobe/aio/util/JacksonUtilTest.java +++ b/core/src/test/java/com/adobe/aio/util/JacksonUtilTest.java @@ -11,13 +11,11 @@ */ package com.adobe.aio.util; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import com.adobe.aio.util.JacksonUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.node.TextNode; -import org.junit.Test; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; public class JacksonUtilTest { @@ -35,12 +33,12 @@ public void testGetJsonNode() throws Exception { assertEquals(new TextNode(aWeirdString), JacksonUtil.getJsonNode(aWeirdString)); String aJsonTree = " { \"" + KEY + "\" : \"" + VALUE + "\" } "; assertTrue(JacksonUtil.getJsonNode(aJsonTree).has(KEY)); - assertTrue(JacksonUtil.getJsonNode(aJsonTree).size() == 1); + assertEquals(1, JacksonUtil.getJsonNode(aJsonTree).size()); assertEquals(VALUE, JacksonUtil.getJsonNode(aJsonTree).get(KEY).textValue()); } - @Test(expected = JsonProcessingException.class) + @Test public void testGetJsonNode_ThrowsJsonProcessingException() throws Exception { - JacksonUtil.getJsonNode(" { an invalid Json Payload "); + assertThrows(JsonProcessingException.class, () -> JacksonUtil.getJsonNode(" { an invalid Json Payload ")); } } From 236d5dc1653ad9bb324a67123d9ab3f6e18b89bc Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Thu, 3 Aug 2023 14:36:51 -0500 Subject: [PATCH 05/35] Extract JWT context/credentials --- .../main/java/com/adobe/aio/auth/Context.java | 34 +++ .../java/com/adobe/aio/auth/JwtContext.java | 203 +++++++++++++++ .../com/adobe/aio/workspace/Workspace.java | 241 ++++++++---------- .../com/adobe/aio/auth/JwtContextTest.java | 82 ++++++ .../adobe/aio/workspace/WorkspaceTest.java | 141 +++++++--- 5 files changed, 533 insertions(+), 168 deletions(-) create mode 100644 core/src/main/java/com/adobe/aio/auth/Context.java create mode 100644 core/src/main/java/com/adobe/aio/auth/JwtContext.java create mode 100644 core/src/test/java/com/adobe/aio/auth/JwtContextTest.java diff --git a/core/src/main/java/com/adobe/aio/auth/Context.java b/core/src/main/java/com/adobe/aio/auth/Context.java new file mode 100644 index 00000000..84d19415 --- /dev/null +++ b/core/src/main/java/com/adobe/aio/auth/Context.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +package com.adobe.aio.auth; + +/** + * Represents an Authentication context to Adobe IMS. + * + * @since 1.1 + */ +public interface Context { + + /** + * Property name used in maps and config files for setting the AIO IMS URL. + */ + public static final String IMS_URL = "aio_ims_url"; + /** + * Property name used in maps and config files for setting the AIO IMS Org Id. + */ + public static final String IMS_ORG_ID = "aio_ims_org_id"; + + /** + * Validates this context is minimally populated and able to function. + */ + void validate(); +} diff --git a/core/src/main/java/com/adobe/aio/auth/JwtContext.java b/core/src/main/java/com/adobe/aio/auth/JwtContext.java new file mode 100644 index 00000000..4f4ca233 --- /dev/null +++ b/core/src/main/java/com/adobe/aio/auth/JwtContext.java @@ -0,0 +1,203 @@ +/* + * Copyright 2023 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +package com.adobe.aio.auth; + +import java.security.PrivateKey; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import static com.adobe.aio.util.FileUtil.*; + +/** + * JWT Authentication context. + */ +public class JwtContext implements Context { + public static final String CREDENTIAL_ID = "aio_credential_id"; + public static final String CLIENT_SECRET = "aio_client_secret"; + public static final String TECHNICAL_ACCOUNT_ID = "aio_technical_account_id"; + public static final String META_SCOPES = "aio_meta_scopes"; + + private final String credentialId; + private final String technicalAccountId; + private final Set metascopes; + private final String clientSecret; + private final PrivateKey privateKey; + + public JwtContext(final String credentialId, final String clientSecret, final String technicalAccountId, + final Set metascopes, final PrivateKey privateKey) { + this.credentialId = credentialId; + this.clientSecret = clientSecret; + this.technicalAccountId = technicalAccountId; + this.metascopes = metascopes; + this.privateKey = privateKey; + } + + public static Builder builder() { + return new Builder(); + } + + public void validate() { + if (StringUtils.isEmpty(clientSecret)) { + throw new IllegalStateException("Your `JwtContext` is missing a clientSecret"); + } + if (StringUtils.isEmpty(technicalAccountId)) { + throw new IllegalStateException("Your `JwtContext` is missing a technicalAccountId"); + } + if (metascopes.isEmpty()) { + throw new IllegalStateException("Your `JwtContext` is missing a metascope"); + } + if (privateKey == null) { + throw new IllegalStateException("Your `JwtContext` is missing a privateKey"); + } + } + + public String getCredentialId() { + return credentialId; + } + + public String getTechnicalAccountId() { + return technicalAccountId; + } + + public Set getMetascopes() { + return metascopes; + } + + // we want to avoid serializing this secret + @JsonIgnore + public String getClientSecret() { + return clientSecret; + } + + public boolean isClientSecretDefined() { + return !StringUtils.isEmpty(this.clientSecret); + } + + // we want to avoid serializing this secret + @JsonIgnore + public PrivateKey getPrivateKey() { + return privateKey; + } + + public boolean isPrivateKeyDefined() { + return (this.privateKey != null); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + JwtContext that = (JwtContext) o; + + if (!Objects.equals(credentialId, that.credentialId)) return false; + if (!Objects.equals(technicalAccountId, that.technicalAccountId)) + return false; + if (!Objects.equals(metascopes, that.metascopes)) return false; + if (!Objects.equals(clientSecret, that.clientSecret)) return false; + return Objects.equals(privateKey, that.privateKey); + } + + @Override + public int hashCode() { + int result = credentialId != null ? credentialId.hashCode() : 0; + result = 31 * result + (technicalAccountId != null ? technicalAccountId.hashCode() : 0); + result = 31 * result + (metascopes != null ? metascopes.hashCode() : 0); + result = 31 * result + (clientSecret != null ? clientSecret.hashCode() : 0); + result = 31 * result + (privateKey != null ? privateKey.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "JwtContext{" + + "credentialId='" + credentialId + '\'' + + ", technicalAccountId='" + technicalAccountId + '\'' + + ", metascopes=" + metascopes + + '}'; + } + + public static class Builder { + + private String credentialId; + private String clientSecret; + private String technicalAccountId; + private PrivateKey privateKey; + private final Set metascopes = new HashSet<>(); + + public Builder credentialId(final String credentialId) { + this.credentialId = credentialId; + return this; + } + + public Builder clientSecret(final String clientSecret) { + this.clientSecret = clientSecret; + return this; + } + + public Builder technicalAccountId(final String technicalAccountId) { + this.technicalAccountId = technicalAccountId; + return this; + } + + public Builder addMetascope(final String metascope) { + this.metascopes.add(metascope); + return this; + } + + public Builder privateKey(final PrivateKey privateKey) { + this.privateKey = privateKey; + return this; + } + + public Builder configMap(final Map configMap) { + this + .credentialId(configMap.get(CREDENTIAL_ID)) + .clientSecret(configMap.get(CLIENT_SECRET)) + .technicalAccountId(configMap.get(TECHNICAL_ACCOUNT_ID)); + if (!StringUtils.isEmpty(configMap.get(META_SCOPES))) { + String[] metascopeArray = configMap.get(META_SCOPES).split(","); + for (String metascope : metascopeArray) { + this.addMetascope(metascope); + } + } + return this; + } + + public Builder systemEnv() { + return configMap(System.getenv()); + } + + public Builder propertiesPath(final String propertiesPath) { + return properties( + readPropertiesFromFile(propertiesPath) + .orElse(readPropertiesFromClassPath(propertiesPath))); + } + + public Builder properties(final Properties properties) { + return configMap(getMapFromProperties(properties)); + } + + public JwtContext build() { + return new JwtContext(credentialId, clientSecret, technicalAccountId, metascopes, privateKey); + } + } +} diff --git a/core/src/main/java/com/adobe/aio/workspace/Workspace.java b/core/src/main/java/com/adobe/aio/workspace/Workspace.java index a37fc69d..10363bdd 100644 --- a/core/src/main/java/com/adobe/aio/workspace/Workspace.java +++ b/core/src/main/java/com/adobe/aio/workspace/Workspace.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Adobe. All rights reserved. + * Copyright 2023 Adobe. All rights reserved. * This file is licensed to you under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. You may obtain a copy * of the License at http://www.apache.org/licenses/LICENSE-2.0 @@ -11,21 +11,20 @@ */ package com.adobe.aio.workspace; -import static com.adobe.aio.util.FileUtil.getMapFromProperties; -import static com.adobe.aio.util.FileUtil.readPropertiesFromClassPath; -import static com.adobe.aio.util.FileUtil.readPropertiesFromFile; - -import com.adobe.aio.util.Constants; -import com.fasterxml.jackson.annotation.JsonIgnore; -import java.io.IOException; import java.security.PrivateKey; -import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Properties; -import java.util.Set; + import org.apache.commons.lang3.StringUtils; +import com.adobe.aio.auth.Context; +import com.adobe.aio.auth.JwtContext; +import com.adobe.aio.auth.OAuthContext; +import com.adobe.aio.util.Constants; + +import static com.adobe.aio.util.FileUtil.*; + public class Workspace { public static final String IMS_URL = "aio_ims_url"; @@ -33,44 +32,50 @@ public class Workspace { public static final String CONSUMER_ORG_ID = "aio_consumer_org_id"; public static final String PROJECT_ID = "aio_project_id"; public static final String WORKSPACE_ID = "aio_workspace_id"; + public static final String API_KEY = "aio_api_key"; + /** + * @deprecated This will be removed in v2.0 of the library. + */ + @Deprecated(since = "1.1", forRemoval = true) public static final String CREDENTIAL_ID = "aio_credential_id"; + /** + * @deprecated This will be removed in v2.0 of the library. + */ + @Deprecated(since = "1.1", forRemoval = true) public static final String CLIENT_SECRET = "aio_client_secret"; + /** + * @deprecated This will be removed in v2.0 of the library. + */ + @Deprecated(since = "1.1", forRemoval = true) public static final String TECHNICAL_ACCOUNT_ID = "aio_technical_account_id"; + /** + * @deprecated This will be removed in v2.0 of the library. + */ + @Deprecated(since = "1.1", forRemoval = true) public static final String META_SCOPES = "aio_meta_scopes"; - // Auth related : + // workspace context related: private final String imsUrl; private final String imsOrgId; private final String apiKey; - private final String credentialId; - private final String technicalAccountId; - private final Set metascopes; - private final String clientSecret; - private final PrivateKey privateKey; - - // workspace context related: private final String consumerOrgId; private final String projectId; private final String workspaceId; + private final Context authContext; - private Workspace(final String imsUrl, final String imsOrgId, final String consumerOrgId, - final String projectId, final String workspaceId, - final String apiKey, final String credentialId, final String clientSecret, - final String technicalAccountId, - final Set metascopes, final PrivateKey privateKey) { + private Workspace(final String imsUrl, final String imsOrgId, final String apiKey, + final String consumerOrgId, final String projectId, final String workspaceId, + Context authContext) { this.imsUrl = StringUtils.isEmpty(imsUrl) ? Constants.IMS_URL : imsUrl; this.imsOrgId = imsOrgId; - this.apiKey = apiKey; - this.credentialId = credentialId; - this.clientSecret = clientSecret; - this.technicalAccountId = technicalAccountId; - this.metascopes = metascopes; - this.privateKey = privateKey; + this.apiKey = apiKey; this.consumerOrgId = consumerOrgId; this.projectId = projectId; this.workspaceId = workspaceId; + + this.authContext = authContext; } public static Builder builder() { @@ -78,43 +83,30 @@ public static Builder builder() { } public void validateAll() { - validateJwtCredentialConfig(); + authContext.validate(); validateWorkspaceContext(); } - public void validateJwtCredentialConfig() { - if (StringUtils.isEmpty(apiKey)) { - throw new IllegalArgumentException("Your `Worskpace` is missing an apiKey"); - } - if (StringUtils.isEmpty(clientSecret)) { - throw new IllegalArgumentException("Your `Worskpace` is missing a clientSecret"); - } + /** + * Validates that this workspace context is populated. + * + * @throws IllegalStateException if any properties are not specified. + */ + public void validateWorkspaceContext() throws IllegalStateException { if (StringUtils.isEmpty(imsOrgId)) { - throw new IllegalArgumentException("Your `Worskpace` is missing an imsOrgId"); - } - if (StringUtils.isEmpty(technicalAccountId)) { - throw new IllegalArgumentException("Your `Worskpace` is missing a technicalAccountId"); - } - if (metascopes.isEmpty()) { - throw new IllegalArgumentException("Your `Worskpace` is missing a metascope"); + throw new IllegalStateException("Your `Workspace` is missing an imsOrgId"); } - if (privateKey == null) { - throw new IllegalArgumentException("Your `Worskpace` is missing a privateKey"); - } - } - - public void validateWorkspaceContext() { if (StringUtils.isEmpty(this.getConsumerOrgId())) { - throw new IllegalArgumentException("Your `Worskpace` is missing a consumerOrgId"); + throw new IllegalStateException("Your `Workspace` is missing a consumerOrgId"); + } + if (StringUtils.isEmpty(apiKey)) { + throw new IllegalStateException("Your `Workspace` is missing an apiKey"); } if (StringUtils.isEmpty(this.getProjectId())) { - throw new IllegalArgumentException("Your `Worskpace` is missing a projectId"); + throw new IllegalStateException("Your `Workspace` is missing a projectId"); } if (StringUtils.isEmpty(this.getWorkspaceId())) { - throw new IllegalArgumentException("Your `Worskpace` is missing a workspaceId"); - } - if (StringUtils.isEmpty(this.getApiKey())) { - throw new IllegalArgumentException("Your `Worskpace` is missing an apiKey"); + throw new IllegalStateException("Your `Workspace` is missing a workspaceId"); } } @@ -136,6 +128,10 @@ public String getImsOrgId() { return imsOrgId; } + public String getApiKey() { + return apiKey; + } + public String getConsumerOrgId() { return consumerOrgId; } @@ -148,42 +144,6 @@ public String getWorkspaceId() { return workspaceId; } - public String getApiKey() { - return apiKey; - } - - public String getCredentialId() { - return credentialId; - } - - public String getTechnicalAccountId() { - return technicalAccountId; - } - - public Set getMetascopes() { - return metascopes; - } - - // we want to avoid serializing this secret - @JsonIgnore - public String getClientSecret() { - return clientSecret; - } - - public boolean isClientSecretDefined() { - return !StringUtils.isEmpty(this.clientSecret); - } - - // we want to avoid serializing this secret - @JsonIgnore - public PrivateKey getPrivateKey() { - return privateKey; - } - - public boolean isPrivateKeyDefined() { - return (this.privateKey != null); - } - @Override public boolean equals(Object o) { if (this == o) { @@ -193,24 +153,18 @@ public boolean equals(Object o) { return false; } Workspace workspace = (Workspace) o; - return Objects.equals(imsUrl, workspace.imsUrl) && - Objects.equals(imsOrgId, workspace.imsOrgId) && - Objects.equals(consumerOrgId, workspace.consumerOrgId) && - Objects.equals(projectId, workspace.projectId) && - Objects.equals(workspaceId, workspace.workspaceId) && - Objects.equals(apiKey, workspace.apiKey) && - Objects.equals(credentialId, workspace.credentialId) && - Objects.equals(clientSecret, workspace.clientSecret) && - Objects.equals(technicalAccountId, workspace.technicalAccountId) && - Objects.equals(metascopes, workspace.metascopes) && - Objects.equals(privateKey, workspace.privateKey); + return + Objects.equals(imsUrl, workspace.imsUrl) && + Objects.equals(imsOrgId, workspace.imsOrgId) && + Objects.equals(consumerOrgId, workspace.consumerOrgId) && + Objects.equals(projectId, workspace.projectId) && + Objects.equals(workspaceId, workspace.workspaceId); } @Override public int hashCode() { return Objects - .hash(imsUrl, imsOrgId, consumerOrgId, projectId, workspaceId, apiKey, credentialId, - clientSecret, technicalAccountId, metascopes, privateKey); + .hash(imsUrl, imsOrgId, consumerOrgId, projectId, workspaceId); } @Override @@ -219,32 +173,26 @@ public String toString() { "imsUrl='" + imsUrl + '\'' + ", imsOrgId='" + imsOrgId + '\'' + ", consumerOrgId='" + consumerOrgId + '\'' + + ", apiKey='" + apiKey + '\'' + ", projectId='" + projectId + '\'' + ", workspaceId='" + workspaceId + '\'' + - ", apiKey='" + apiKey + '\'' + - ", credentialId='" + credentialId + '\'' + - ", clientSecret='" + clientSecret + '\'' + - ", technicalAccountId='" + technicalAccountId + '\'' + - ", metascopes=" + metascopes + '}'; } public static class Builder { - private final Set metascopes = new HashSet<>(); private String imsUrl; private String imsOrgId; + private String apiKey; private String consumerOrgId; private String projectId; private String workspaceId; - private String apiKey; - private String credentialId; - private String clientSecret; - private String technicalAccountId; - private PrivateKey privateKey; private Map workspaceProperties; + private JwtContext.Builder jwtbuilder; + private Context authContext; + private Builder() { } @@ -258,6 +206,11 @@ public Builder imsOrgId(final String imsOrgId) { return this; } + public Builder apiKey(final String apiKey) { + this.apiKey = apiKey; + return this; + } + public Builder consumerOrgId(final String consumerOrgId) { this.consumerOrgId = consumerOrgId; return this; @@ -273,33 +226,49 @@ public Builder workspaceId(final String workspaceId) { return this; } - public Builder apiKey(final String apiKey) { - this.apiKey = apiKey; + public Builder authContext(final Context authContext) { + this.authContext = authContext; return this; } + public Builder credentialId(final String credentialId) { - this.credentialId = credentialId; + if (jwtbuilder == null) { + jwtbuilder = JwtContext.builder(); + } + jwtbuilder.credentialId(credentialId); return this; } public Builder clientSecret(final String clientSecret) { - this.clientSecret = clientSecret; + if (jwtbuilder == null) { + jwtbuilder = JwtContext.builder(); + } + jwtbuilder.clientSecret(clientSecret); return this; } public Builder technicalAccountId(final String technicalAccountId) { - this.technicalAccountId = technicalAccountId; + if (jwtbuilder == null) { + jwtbuilder = JwtContext.builder(); + } + jwtbuilder.technicalAccountId(technicalAccountId); return this; } public Builder addMetascope(final String metascope) { - this.metascopes.add(metascope); + if (jwtbuilder == null) { + jwtbuilder = JwtContext.builder(); + } + jwtbuilder.addMetascope(metascope); return this; } public Builder privateKey(final PrivateKey privateKey) { - this.privateKey = privateKey; + if (jwtbuilder == null) { + jwtbuilder = JwtContext.builder(); + } + jwtbuilder.privateKey(privateKey); return this; } @@ -307,19 +276,10 @@ public Builder configMap(final Map configMap) { this .imsUrl(configMap.get(IMS_URL)) .imsOrgId(configMap.get(IMS_ORG_ID)) + .apiKey(configMap.get(API_KEY)) .consumerOrgId(configMap.get(CONSUMER_ORG_ID)) .projectId(configMap.get(PROJECT_ID)) - .workspaceId(configMap.get(WORKSPACE_ID)) - .apiKey(configMap.get(API_KEY)) - .credentialId(configMap.get(CREDENTIAL_ID)) - .clientSecret(configMap.get(CLIENT_SECRET)) - .technicalAccountId(configMap.get(TECHNICAL_ACCOUNT_ID)); - if (!StringUtils.isEmpty(configMap.get(META_SCOPES))) { - String[] metascopeArray = configMap.get(META_SCOPES).split(","); - for (String metascope : metascopeArray) { - this.addMetascope(metascope); - } - } + .workspaceId(configMap.get(WORKSPACE_ID)); return this; } @@ -333,14 +293,19 @@ public Builder propertiesPath(final String propertiesPath) { .orElse(readPropertiesFromClassPath(propertiesPath))); } - public Builder properties(final Properties properties) { + public Builder properties(final Properties properties) { return configMap(getMapFromProperties(properties)); } public Workspace build() { - return new Workspace(imsUrl, imsOrgId, consumerOrgId, projectId, workspaceId, - apiKey, credentialId, clientSecret, technicalAccountId, - metascopes, privateKey); + + if (authContext != null) { + return new Workspace(imsUrl, imsOrgId, apiKey, consumerOrgId, projectId, workspaceId, authContext); + } + if (jwtbuilder == null) { + jwtbuilder = JwtContext.builder(); + } + return new Workspace(imsUrl, imsOrgId, apiKey, consumerOrgId, projectId, workspaceId, jwtbuilder.build()); } } } diff --git a/core/src/test/java/com/adobe/aio/auth/JwtContextTest.java b/core/src/test/java/com/adobe/aio/auth/JwtContextTest.java new file mode 100644 index 00000000..60259f93 --- /dev/null +++ b/core/src/test/java/com/adobe/aio/auth/JwtContextTest.java @@ -0,0 +1,82 @@ +package com.adobe.aio.auth; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; + +import static com.adobe.aio.auth.JwtContext.*; +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class JwtContextTest { + private static final String TEST_PROPERTIES = "workspace.properties"; + private static final String TEST_VALUE = "_changeMe"; + + private static JwtContext expected; + + private static PrivateKey privateKey; + + @BeforeAll + public static void beforeClass() throws Exception { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + kpg.initialize(2048); + KeyPair kp = kpg.generateKeyPair(); + privateKey = kp.getPrivate(); + expected = JwtContext.builder().propertiesPath(TEST_PROPERTIES).privateKey(privateKey).build(); + } + + @Test + void properties() { + JwtContext actual = JwtContext.builder() + .credentialId(CREDENTIAL_ID + TEST_VALUE) + .technicalAccountId(TECHNICAL_ACCOUNT_ID + TEST_VALUE) + .addMetascope(META_SCOPES + TEST_VALUE) + .clientSecret(CLIENT_SECRET + TEST_VALUE) + .privateKey(privateKey) + .build(); + + assertEquals(actual, expected); + assertEquals(actual.hashCode(), expected.hashCode()); + assertEquals(actual.toString(), expected.toString()); + actual.validate(); + } + + @Test + void missingClientSecret() { + JwtContext actual = JwtContext.builder().build(); + Exception ex = assertThrows(IllegalStateException.class, actual::validate); + assertEquals("Your `JwtContext` is missing a clientSecret", ex.getMessage()); + } + + @Test + void missingTechnicalAccountId() { + JwtContext actual = JwtContext.builder() + .clientSecret(CLIENT_SECRET + TEST_VALUE) + .build(); + Exception ex = assertThrows(IllegalStateException.class, actual::validate); + assertEquals("Your `JwtContext` is missing a technicalAccountId", ex.getMessage()); + } + + @Test + void missingMetascopes() { + JwtContext actual = JwtContext.builder() + .clientSecret(CLIENT_SECRET + TEST_VALUE) + .technicalAccountId(TECHNICAL_ACCOUNT_ID + TEST_VALUE) + .build(); + Exception ex = assertThrows(IllegalStateException.class, actual::validate); + assertEquals("Your `JwtContext` is missing a metascope", ex.getMessage()); + } + + @Test + void missingPrivateKey() { + JwtContext actual = JwtContext.builder() + .clientSecret(CLIENT_SECRET + TEST_VALUE) + .technicalAccountId(TECHNICAL_ACCOUNT_ID + TEST_VALUE) + .addMetascope(META_SCOPES + TEST_VALUE) + .build(); + Exception ex = assertThrows(IllegalStateException.class, actual::validate); + assertEquals("Your `JwtContext` is missing a privateKey", ex.getMessage()); + } +} diff --git a/core/src/test/java/com/adobe/aio/workspace/WorkspaceTest.java b/core/src/test/java/com/adobe/aio/workspace/WorkspaceTest.java index f2189cc8..16692c4d 100644 --- a/core/src/test/java/com/adobe/aio/workspace/WorkspaceTest.java +++ b/core/src/test/java/com/adobe/aio/workspace/WorkspaceTest.java @@ -11,56 +11,137 @@ */ package com.adobe.aio.workspace; -import com.adobe.aio.workspace.Workspace; -import com.adobe.aio.util.Constants; import java.io.IOException; -import org.junit.Assert; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; -public class WorkspaceTest { +import com.adobe.aio.auth.Context; +import com.adobe.aio.auth.JwtContext; +import com.adobe.aio.util.Constants; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; - @Rule - public ExpectedException expectedEx = ExpectedException.none(); +import static org.junit.jupiter.api.Assertions.*; +public class WorkspaceTest { + + private static Workspace expected; private static final String TEST_PROPERTIES = "workspace.properties"; private static final String TEST_VALUE = "_changeMe"; + private static PrivateKey privateKey; - private static Workspace getTestWorkspaceFromProperties() { - return Workspace.builder() - .propertiesPath(TEST_PROPERTIES) - .build(); + @BeforeAll + public static void beforeClass() throws Exception { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + kpg.initialize(2048); + KeyPair kp = kpg.generateKeyPair(); + privateKey = kp.getPrivate(); + expected = Workspace.builder().propertiesPath(TEST_PROPERTIES).privateKey(privateKey).build(); } @Test - public void testProperties() throws IOException { - Workspace fromProperties = getTestWorkspaceFromProperties(); - Workspace expected = Workspace.builder() + public void properties() throws IOException { + + class MockContext implements Context { + @Override + public void validate() { + + } + } + + Workspace actual = Workspace.builder() .imsUrl(Constants.IMS_URL) + .imsOrgId(Workspace.IMS_ORG_ID + TEST_VALUE) .apiKey(Workspace.API_KEY + TEST_VALUE) - .clientSecret(Workspace.CLIENT_SECRET + TEST_VALUE) - .apiKey(Workspace.API_KEY + TEST_VALUE) - .credentialId(Workspace.CREDENTIAL_ID + TEST_VALUE) .consumerOrgId(Workspace.CONSUMER_ORG_ID + TEST_VALUE) .projectId(Workspace.PROJECT_ID + TEST_VALUE) .workspaceId(Workspace.WORKSPACE_ID + TEST_VALUE) + .authContext(new MockContext()) + .build(); + assertEquals(actual, expected); + assertEquals(actual.hashCode(), expected.hashCode()); + assertEquals(actual.toString(), expected.toString()); + actual.validateAll(); + } + + @Test + public void missingImsOrg() { + Workspace actual = Workspace.builder().build(); + Exception ex = assertThrows(IllegalStateException.class, actual::validateWorkspaceContext); + assertEquals("Your `Workspace` is missing an imsOrgId", ex.getMessage()); + } + + @Test + public void missingConsumerOrgId() { + Workspace actual = Workspace.builder() + .imsOrgId(Workspace.IMS_ORG_ID + TEST_VALUE) + .build(); + Exception ex = assertThrows(IllegalStateException.class, actual::validateWorkspaceContext); + assertEquals("Your `Workspace` is missing a consumerOrgId", ex.getMessage()); + } + + @Test + public void missingApiKey() { + Workspace actual = Workspace.builder() + .imsOrgId(Workspace.IMS_ORG_ID + TEST_VALUE) + .consumerOrgId(Workspace.CONSUMER_ORG_ID + TEST_VALUE) + .build(); + Exception ex = assertThrows(IllegalStateException.class, actual::validateWorkspaceContext); + assertEquals("Your `Workspace` is missing an apiKey", ex.getMessage()); + } + + @Test + public void missingProjectId() { + Workspace actual = Workspace.builder() .imsOrgId(Workspace.IMS_ORG_ID + TEST_VALUE) - .technicalAccountId(Workspace.TECHNICAL_ACCOUNT_ID + TEST_VALUE) - .addMetascope(Workspace.META_SCOPES + TEST_VALUE) + .consumerOrgId(Workspace.CONSUMER_ORG_ID + TEST_VALUE) + .apiKey(Workspace.API_KEY + TEST_VALUE) .build(); - Assert.assertEquals(expected, fromProperties); - Assert.assertEquals(expected.hashCode(), fromProperties.hashCode()); - Assert.assertEquals(expected.toString(), fromProperties.toString()); - fromProperties.validateWorkspaceContext(); + Exception ex = assertThrows(IllegalStateException.class, actual::validateWorkspaceContext); + assertEquals("Your `Workspace` is missing a projectId", ex.getMessage()); } @Test - public void testValidateJwtCredentialConfig() throws IOException { - Workspace workspace = getTestWorkspaceFromProperties(); - expectedEx.expect(IllegalArgumentException.class); - expectedEx.expectMessage("Your `Worskpace` is missing a privateKey"); - workspace.validateJwtCredentialConfig(); + public void missingWorkspaceId() { + Workspace actual = Workspace.builder() + .imsOrgId(Workspace.IMS_ORG_ID + TEST_VALUE) + .consumerOrgId(Workspace.CONSUMER_ORG_ID + TEST_VALUE) + .apiKey(Workspace.API_KEY + TEST_VALUE) + .projectId(Workspace.PROJECT_ID + TEST_VALUE) + .build(); + Exception ex = assertThrows(IllegalStateException.class, actual::validateWorkspaceContext); + assertEquals("Your `Workspace` is missing a workspaceId", ex.getMessage()); + } + + @Test + public void projectUrl() { + Workspace actual = Workspace.builder() + .consumerOrgId(Workspace.CONSUMER_ORG_ID + TEST_VALUE) + .projectId(Workspace.PROJECT_ID + TEST_VALUE) + .build(); + + assertEquals("https://developer.adobe.com/console/projects/aio_consumer_org_id_changeMe/aio_project_id_changeMe/overview", actual.getProjectUrl()); } + @Test + public void jwtBackwardsCompat() throws Exception { + Workspace actual = Workspace.builder() + .imsUrl(Constants.IMS_URL) + .imsOrgId(Workspace.IMS_ORG_ID + TEST_VALUE) + .apiKey(Workspace.API_KEY + TEST_VALUE) + .consumerOrgId(Workspace.CONSUMER_ORG_ID + TEST_VALUE) + .projectId(Workspace.PROJECT_ID + TEST_VALUE) + .workspaceId(Workspace.WORKSPACE_ID + TEST_VALUE) + .clientSecret(JwtContext.CLIENT_SECRET + TEST_VALUE) + .credentialId(JwtContext.CREDENTIAL_ID + TEST_VALUE) + .technicalAccountId(JwtContext.TECHNICAL_ACCOUNT_ID + TEST_VALUE) + .privateKey(privateKey) + .addMetascope(JwtContext.META_SCOPES + TEST_VALUE) + .build(); + assertEquals(actual, expected); + assertEquals(actual.hashCode(), expected.hashCode()); + assertEquals(actual.toString(), expected.toString()); + actual.validateAll(); + } } From 56974cb392d5ddbcacee9f2f6409273c32f26ad5 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Thu, 3 Aug 2023 15:58:29 -0500 Subject: [PATCH 06/35] Add OAuthContext support. --- .../java/com/adobe/aio/auth/OAuthContext.java | 139 ++++++++++++++++++ .../com/adobe/aio/workspace/Workspace.java | 1 - .../com/adobe/aio/auth/OAuthContextTest.java | 56 +++++++ core/src/test/resources/workspace.properties | 6 +- 4 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 core/src/main/java/com/adobe/aio/auth/OAuthContext.java create mode 100644 core/src/test/java/com/adobe/aio/auth/OAuthContextTest.java diff --git a/core/src/main/java/com/adobe/aio/auth/OAuthContext.java b/core/src/main/java/com/adobe/aio/auth/OAuthContext.java new file mode 100644 index 00000000..f361caa6 --- /dev/null +++ b/core/src/main/java/com/adobe/aio/auth/OAuthContext.java @@ -0,0 +1,139 @@ +/* + * Copyright 2023 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +package com.adobe.aio.auth; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import static com.adobe.aio.util.FileUtil.*; + +/** + * Represents an OAuth Client Credentials authentication context. + * Reference: OAuth Client Credentials + */ +public class OAuthContext implements Context { + /** + * Property name for looking up Authentication Client Secret in various contexts. + * Reference: AIO Developer Documentation + */ + public static final String CLIENT_SECRET = "aio_client_secret"; + /** + * Property name for looking up Authentication Scopes in various contexts. + * Reference: AIO Developer Documentation + */ + public static final String SCOPES = "aio_oauth_scopes"; + + private final String clientSecret; + private final Set scopes; + + public OAuthContext(final String clientSecret, final Set scopes) { + this.clientSecret = clientSecret; + this.scopes = scopes; + + } + + public static Builder builder() { + return new Builder(); + } + + @Override + public void validate() { + if (StringUtils.isBlank(clientSecret)) { + throw new IllegalStateException("Your `OAuthContext` is missing a clientSecret."); + } + if (scopes.isEmpty()) { + throw new IllegalStateException("Your `OAuthContext` is missing a scope."); + } + } + + @JsonIgnore + public String getClientSecret() { + return clientSecret; + } + + public Set getScopes() { + return scopes; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + OAuthContext that = (OAuthContext) o; + + if (!Objects.equals(clientSecret, that.clientSecret)) return false; + return Objects.equals(scopes, that.scopes); + } + + @Override + public int hashCode() { + int result = clientSecret != null ? clientSecret.hashCode() : 0; + result = 31 * result + (scopes != null ? scopes.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "OAuthContext{" + "scopes=" + scopes + '}'; + } + + public static class Builder { + private String clientSecret; + private final Set scopes = new HashSet<>(); + + public Builder clientSecret(final String clientSecret) { + this.clientSecret = clientSecret; + return this; + } + + public Builder addScope(final String scope) { + if (StringUtils.isBlank(scope)) { + return this; + } + this.scopes.add(scope.trim()); + return this; + } + + public Builder configMap(final Map configMap) { + this.clientSecret(configMap.get(CLIENT_SECRET)); + if (!StringUtils.isEmpty(configMap.get(SCOPES))) { + Arrays.stream(configMap.get(SCOPES).split(",")).forEach(this::addScope); + } + return this; + } + + public Builder systemEvn() { + return configMap(System.getenv()); + } + + public Builder propertiesPath(final String propertiesPath) { + return properties(readPropertiesFromFile(propertiesPath).orElse(readPropertiesFromClassPath(propertiesPath))); + } + + public Builder properties(final Properties properties) { + return configMap(getMapFromProperties(properties)); + } + + public OAuthContext build() { + return new OAuthContext(clientSecret, scopes); + } + } +} diff --git a/core/src/main/java/com/adobe/aio/workspace/Workspace.java b/core/src/main/java/com/adobe/aio/workspace/Workspace.java index 10363bdd..edb25a53 100644 --- a/core/src/main/java/com/adobe/aio/workspace/Workspace.java +++ b/core/src/main/java/com/adobe/aio/workspace/Workspace.java @@ -20,7 +20,6 @@ import com.adobe.aio.auth.Context; import com.adobe.aio.auth.JwtContext; -import com.adobe.aio.auth.OAuthContext; import com.adobe.aio.util.Constants; import static com.adobe.aio.util.FileUtil.*; diff --git a/core/src/test/java/com/adobe/aio/auth/OAuthContextTest.java b/core/src/test/java/com/adobe/aio/auth/OAuthContextTest.java new file mode 100644 index 00000000..778a3762 --- /dev/null +++ b/core/src/test/java/com/adobe/aio/auth/OAuthContextTest.java @@ -0,0 +1,56 @@ +package com.adobe.aio.auth; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; +import static com.adobe.aio.auth.OAuthContext.*; + +public class OAuthContextTest { + + private static final String TEST_PROPERTIES = "workspace.properties"; + private static final String TEST_VALUE = "_changeMe"; + + private static OAuthContext expected; + + @BeforeAll + public static void beforeClass() throws Exception { + expected = OAuthContext.builder().propertiesPath(TEST_PROPERTIES).build(); + } + + @Test + void properties() { + OAuthContext actual = OAuthContext.builder() + .clientSecret(CLIENT_SECRET + TEST_VALUE) + .addScope(SCOPES + TEST_VALUE) + .build(); + assertEquals(actual, expected); + assertEquals(actual.hashCode(), expected.hashCode()); + assertEquals(actual.toString(), expected.toString()); + actual.validate(); + } + + @Test + void missingClientSecret() { + OAuthContext actual = OAuthContext.builder().build(); + Exception ex = assertThrows(IllegalStateException.class, actual::validate); + assertEquals("Your `OAuthContext` is missing a clientSecret.", ex.getMessage()); + } + + @Test + void missingScope() { + OAuthContext actual = OAuthContext.builder().clientSecret(CLIENT_SECRET + TEST_VALUE).build(); + Exception ex = assertThrows(IllegalStateException.class, actual::validate); + assertEquals("Your `OAuthContext` is missing a scope.", ex.getMessage()); + + // Blank scope + actual = OAuthContext.builder() + .clientSecret(CLIENT_SECRET + TEST_VALUE) + .addScope(" ") + .build(); + ex = assertThrows(IllegalStateException.class, actual::validate); + assertEquals("Your `OAuthContext` is missing a scope.", ex.getMessage()); + + } + +} diff --git a/core/src/test/resources/workspace.properties b/core/src/test/resources/workspace.properties index 2aae79da..bd16d06d 100644 --- a/core/src/test/resources/workspace.properties +++ b/core/src/test/resources/workspace.properties @@ -20,7 +20,7 @@ aio_ims_org_id=aio_ims_org_id_changeMe aio_workspace_id=aio_workspace_id_changeMe # aio_credential_id = your Adobe Developer Console jwt credential id (project.workspace.details.credentials[i].id) aio_credential_id=aio_credential_id_changeMe -# aio_client_secret = your Adobe Developer Console jwt credential client secret (project.workspace.details.credentials[i].jwt.client_secret) +# aio_client_secret = your Adobe Developer Console jwt or OAuth credential client secret (project.workspace.details.credentials[i].jwt.client_secret) aio_client_secret=aio_client_secret_changeMe # aio_api_key = your Adobe Developer Console jwt credential API Key (or Client ID) (project.workspace.details.credentials[i].jwt.client_id aio_api_key=aio_api_key_changeMe @@ -30,4 +30,8 @@ aio_meta_scopes=aio_meta_scopes_changeMe # aio_technical_account_id = your Adobe Developer Console jwt credential technical account id (project.workspace.details.credentials[i].jwt.technical_account_id) aio_technical_account_id=aio_technical_account_id_changeMe +# aio_oauth_scopes : comma separated list of scopes associated with your API, see your Adobe Developer Console OAuth credential scopes +# sample aio_meta_scopes: openid, AdobeID, read_organizations +aio_oauth_scopes=aio_oauth_scopes_changeMe + From ce631a17d16ad0d39e7a9f590e2458024b729388 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Fri, 4 Aug 2023 16:11:27 -0500 Subject: [PATCH 07/35] Move test utilty to test folder. --- .../java/com/adobe/aio/ims}/util/WorkspaceUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename ims/src/{main/java/com/adobe/aio => test/java/com/adobe/aio/ims}/util/WorkspaceUtil.java (97%) diff --git a/ims/src/main/java/com/adobe/aio/util/WorkspaceUtil.java b/ims/src/test/java/com/adobe/aio/ims/util/WorkspaceUtil.java similarity index 97% rename from ims/src/main/java/com/adobe/aio/util/WorkspaceUtil.java rename to ims/src/test/java/com/adobe/aio/ims/util/WorkspaceUtil.java index 95ce7276..f21502aa 100644 --- a/ims/src/main/java/com/adobe/aio/util/WorkspaceUtil.java +++ b/ims/src/test/java/com/adobe/aio/ims/util/WorkspaceUtil.java @@ -9,9 +9,9 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ -package com.adobe.aio.util; +package com.adobe.aio.ims.util; -import com.adobe.aio.ims.util.PrivateKeyBuilder; +import com.adobe.aio.util.FileUtil; import com.adobe.aio.workspace.Workspace; import java.security.PrivateKey; import java.util.Properties; From fedb0e0f15c8126676759a63b8d9cfd35050ad8c Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Fri, 4 Aug 2023 16:12:16 -0500 Subject: [PATCH 08/35] Use mockserver for validating API calls. --- ims/pom.xml | 17 ++++++++++++++--- pom.xml | 19 +++++++++---------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/ims/pom.xml b/ims/pom.xml index fec04d86..7be64f8e 100644 --- a/ims/pom.xml +++ b/ims/pom.xml @@ -57,10 +57,21 @@ slf4j-simple - junit - junit + org.junit.jupiter + junit-jupiter + + + org.mockito + mockito-core + + + org.mockito + mockito-junit-jupiter + + + org.mock-server + mockserver-junit-jupiter-no-dependencies - diff --git a/pom.xml b/pom.xml index 89302c9d..0318787f 100644 --- a/pom.xml +++ b/pom.xml @@ -100,12 +100,11 @@ 3.6 5.9.2 2.32.0 - 4.9.1 - 4.4.0 + 5.4.0 1.7.21 [1.7.21,1.7.25] 2.12.3 - 0.11.2 + 0.11.5 1.2.0 @@ -233,19 +232,13 @@ test - - com.squareup.okhttp3 - mockwebserver - ${okhttp3.mockwebserver.version} - test - - org.mockito mockito-core ${mockito.version} test + org.mockito mockito-junit-jupiter @@ -253,6 +246,12 @@ test + + org.mock-server + mockserver-junit-jupiter-no-dependencies + 5.15.0 + test + From 70c94b7f86718bffe53a071348f9d9af094520d3 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Fri, 4 Aug 2023 16:14:34 -0500 Subject: [PATCH 09/35] Add test cases and modify under-the-hood for JWT deprecation. --- .../java/com/adobe/aio/ims/ImsService.java | 37 +++- .../com/adobe/aio/ims/JwtTokenBuilder.java | 27 ++- .../adobe/aio/ims/feign/FeignImsService.java | 23 ++- .../adobe/aio/ims/JwtTokenBuilderTest.java | 65 +++++++ .../feign/FeignImsServiceIntegrationTest.java | 33 ++-- .../aio/ims/feign/FeignImsServiceTest.java | 168 ++++++++++++++++++ .../ims/feign/FeignImsServiceTestDrive.java | 2 +- ims/src/test/resources/mockserver.properties | 1 + 8 files changed, 319 insertions(+), 37 deletions(-) create mode 100644 ims/src/test/java/com/adobe/aio/ims/JwtTokenBuilderTest.java create mode 100644 ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceTest.java create mode 100644 ims/src/test/resources/mockserver.properties diff --git a/ims/src/main/java/com/adobe/aio/ims/ImsService.java b/ims/src/main/java/com/adobe/aio/ims/ImsService.java index a9db5e0f..dd915da2 100644 --- a/ims/src/main/java/com/adobe/aio/ims/ImsService.java +++ b/ims/src/main/java/com/adobe/aio/ims/ImsService.java @@ -11,34 +11,61 @@ */ package com.adobe.aio.ims; - import com.adobe.aio.ims.feign.FeignImsService; import com.adobe.aio.workspace.Workspace; import com.adobe.aio.ims.model.AccessToken; public interface ImsService { + /** + * Returns an {@link AccessToken} that can be used for other AIO API Calls. + * + * @deprecated this will be removed in v2.0 + * @return AccessToken a valid API authentication token + */ + @Deprecated(since = "1.1", forRemoval = true) AccessToken getJwtExchangeAccessToken(); + /** + * Checks that the access token is still valid. + * + * @deprecated this will be removed in v2.0 + * @param accessToken the token to check + * @return true if the provided access token is still valid, false otherwise + */ + @Deprecated(since = "1.1", forRemoval = true) + boolean validateAccessToken(String accessToken); + static Builder builder() { return new Builder(); } - Boolean validateAccessToken(String accessToken); - class Builder { private Workspace workspace; - public Builder() { + private Builder() { } + /** + * Add the workspace context to this builder. + * + * @param workspace the workspace context + * @return this builder + */ public Builder workspace(Workspace workspace) { this.workspace = workspace; return this; } - public ImsService build() { + /** + * Builds an IMS Service instance. + * + * @return a configured IMS Service + * @throws IllegalStateException if the Workspace authentication context is not valid. + */ + public ImsService build() throws IllegalStateException { + this.workspace.getAuthContext().validate(); return new FeignImsService(this.workspace); } } diff --git a/ims/src/main/java/com/adobe/aio/ims/JwtTokenBuilder.java b/ims/src/main/java/com/adobe/aio/ims/JwtTokenBuilder.java index c502710b..b4eabe1c 100644 --- a/ims/src/main/java/com/adobe/aio/ims/JwtTokenBuilder.java +++ b/ims/src/main/java/com/adobe/aio/ims/JwtTokenBuilder.java @@ -11,7 +11,10 @@ */ package com.adobe.aio.ims; +import com.adobe.aio.auth.Context; +import com.adobe.aio.auth.JwtContext; import com.adobe.aio.workspace.Workspace; +import io.jsonwebtoken.Jwt; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.security.PrivateKey; @@ -32,7 +35,10 @@ * * It will also help you getting this signed with a `RSASSA-PKCS1-V1_5` Digital Signatures with `SHA-2` and a `RS256` The JWT algorithm/`alg` header value. * For this, it leverages a third-party open source library : [jjwt](https://github.com/jwtk/jjwt) + * + * @deprecated See Developer Console documentation */ +@Deprecated public class JwtTokenBuilder { private final Map claims; @@ -47,18 +53,23 @@ public class JwtTokenBuilder { private static final String AUD_SUFFIX = "/c/"; public JwtTokenBuilder(final Workspace workspace) { - workspace.validateJwtCredentialConfig(); - this.claims = getClaims(workspace); - this.privateKey = workspace.getPrivateKey(); + if (!(workspace.getAuthContext() instanceof JwtContext)) { + throw new IllegalStateException("AuthContext in workspace not of type `JwtContext`."); + } + + JwtContext context = (JwtContext) workspace.getAuthContext(); + context.validate(); + this.claims = getClaims(workspace, context); + this.privateKey = context.getPrivateKey(); } - private static Map getClaims(final Workspace workspace) { + private static Map getClaims(final Workspace workspace, JwtContext context) { Map claims = new HashMap(); claims.put(ISS, workspace.getImsOrgId()); - claims.put(SUB, workspace.getTechnicalAccountId()); + claims.put(SUB, context.getTechnicalAccountId()); claims.put(AUD, workspace.getImsUrl() + AUD_SUFFIX + workspace.getApiKey()); - for (String metascope : workspace.getMetascopes()) { + for (String metascope : context.getMetascopes()) { claims.put(workspace.getImsUrl() + metascope, true); } @@ -69,9 +80,7 @@ private static Map getClaims(final Workspace workspace) { } public String build() { - String jwt = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.RS256, privateKey) - .compact(); - return jwt; + return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.RS256, privateKey).compact(); } } diff --git a/ims/src/main/java/com/adobe/aio/ims/feign/FeignImsService.java b/ims/src/main/java/com/adobe/aio/ims/feign/FeignImsService.java index f5c0ec97..01d8b079 100644 --- a/ims/src/main/java/com/adobe/aio/ims/feign/FeignImsService.java +++ b/ims/src/main/java/com/adobe/aio/ims/feign/FeignImsService.java @@ -11,6 +11,7 @@ */ package com.adobe.aio.ims.feign; +import com.adobe.aio.auth.JwtContext; import com.adobe.aio.ims.ImsService; import com.adobe.aio.ims.JwtTokenBuilder; import com.adobe.aio.workspace.Workspace; @@ -26,19 +27,29 @@ public class FeignImsService implements ImsService { public FeignImsService(final Workspace workspace) { this.workspace = workspace; - this.imsApi = FeignUtil.getBuilderWithFormEncoder() - .target(ImsApi.class, workspace.getImsUrl()); + this.imsApi = FeignUtil.getBuilderWithFormEncoder().target(ImsApi.class, workspace.getImsUrl()); } @Override public AccessToken getJwtExchangeAccessToken() { - workspace.validateJwtCredentialConfig(); - return imsApi.getAccessToken(workspace.getApiKey(), - workspace.getClientSecret(), new JwtTokenBuilder(workspace).build()); + if (!(workspace.getAuthContext() instanceof JwtContext)) { + throw new IllegalStateException("AuthContext in workspace not of type `JwtContext`."); + } + + JwtContext context = (JwtContext) workspace.getAuthContext(); + context.validate(); + + JwtTokenBuilder builder = new JwtTokenBuilder(workspace); + String token = builder.build(); + return imsApi.getAccessToken(workspace.getApiKey(), context.getClientSecret(), token); } @Override - public Boolean validateAccessToken(String accessToken) { + public boolean validateAccessToken(String accessToken) { + if (!(workspace.getAuthContext() instanceof JwtContext)) { + throw new IllegalStateException("AuthContext in workspace not of type `JwtContext`."); + } + return imsApi.validateToken(ACCESS_TOKEN, workspace.getApiKey(), accessToken).getValid(); } diff --git a/ims/src/test/java/com/adobe/aio/ims/JwtTokenBuilderTest.java b/ims/src/test/java/com/adobe/aio/ims/JwtTokenBuilderTest.java new file mode 100644 index 00000000..c750704d --- /dev/null +++ b/ims/src/test/java/com/adobe/aio/ims/JwtTokenBuilderTest.java @@ -0,0 +1,65 @@ +package com.adobe.aio.ims; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; + +import com.adobe.aio.auth.Context; +import com.adobe.aio.auth.JwtContext; +import com.adobe.aio.workspace.Workspace; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Header; +import io.jsonwebtoken.Jwt; +import io.jsonwebtoken.Jwts; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class JwtTokenBuilderTest { + + @Test + void wrongContext() { + class MockContext implements Context {} + Workspace workspace = Workspace.builder().authContext(new MockContext()).build(); + Exception ex = assertThrows(IllegalStateException.class, () -> new JwtTokenBuilder(workspace)); + assertEquals("AuthContext in workspace not of type `JwtContext`.", ex.getMessage()); + } + + @Test + void invalidJwtContext() { + Workspace workspace = Workspace.builder().authContext(JwtContext.builder().build()).build(); + assertThrows(IllegalStateException.class, () -> new JwtTokenBuilder(workspace)); + } + + @Test + void build() throws Exception{ + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + kpg.initialize(2048); + KeyPair kp = kpg.generateKeyPair(); + PrivateKey privateKey = kp.getPrivate(); + PublicKey publicKey = kp.getPublic(); + + Workspace.Builder builder = Workspace.builder(); + Workspace workspace = builder.propertiesPath("workspace.properties").privateKey(privateKey).build(); + String actual = new JwtTokenBuilder(workspace).build(); + + Jwt jwt = Jwts.parserBuilder().setSigningKey(publicKey).build().parseClaimsJws(actual); + + JwtContext jwtContext = (JwtContext) workspace.getAuthContext(); + Claims claims = jwt.getBody(); + assertEquals(workspace.getImsOrgId(), claims.getIssuer(), "Issuer specified."); + assertEquals(jwtContext.getTechnicalAccountId(), claims.getSubject(), "Subject specified."); + + String audience = workspace.getImsUrl() + "/c/" + workspace.getApiKey(); + assertEquals(audience, claims.getAudience(), "Subject specified."); + + // Scopes + for (String scope : jwtContext.getMetascopes()) { + assertTrue((Boolean) claims.get(workspace.getImsUrl() + scope)); + } + + assertNotNull(claims.getIssuedAt(), "Issued At Time specified."); + assertNotNull(claims.getExpiration(), "Expires Time specified."); + } +} diff --git a/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceIntegrationTest.java b/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceIntegrationTest.java index 2a61a494..49888322 100644 --- a/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceIntegrationTest.java +++ b/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceIntegrationTest.java @@ -11,17 +11,17 @@ */ package com.adobe.aio.ims.feign; -import com.adobe.aio.exception.feign.IOUpstreamError; import com.adobe.aio.ims.ImsService; import com.adobe.aio.ims.model.AccessToken; -import com.adobe.aio.util.WorkspaceUtil; +import com.adobe.aio.ims.util.WorkspaceUtil; import com.adobe.aio.workspace.Workspace; import feign.FeignException; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.junit.jupiter.api.Assertions.*; + public class FeignImsServiceIntegrationTest { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @@ -32,38 +32,39 @@ public void getAndValidateJwtExchangeAccessToken() { ImsService imsService = ImsService.builder().workspace(workspace).build(); AccessToken accessToken = imsService.getJwtExchangeAccessToken(); logger.info("JWT Exchange token flow complete"); - Assert.assertNotNull(accessToken); - Assert.assertNotNull(accessToken.getAccessToken()); - Assert.assertTrue(accessToken.getExpiresIn()>0); - Assert.assertTrue(imsService.validateAccessToken(accessToken.getAccessToken())); + assertNotNull(accessToken); + assertNotNull(accessToken.getAccessToken()); + assertTrue(accessToken.getExpiresIn()>0); + assertTrue(imsService.validateAccessToken(accessToken.getAccessToken())); logger.info("JWT Exchange access token validated"); } - @Test(expected = FeignException.BadRequest.class) + @Test public void getAndValidateJwtExchangeAccessTokenWithBadApiKey() { Workspace workspace = WorkspaceUtil.getSystemWorkspaceBuilder().apiKey("bad_api_key").build(); ImsService imsService = ImsService.builder().workspace(workspace).build(); - imsService.getJwtExchangeAccessToken(); + assertThrows(FeignException.BadRequest.class, imsService::getJwtExchangeAccessToken); } - @Test(expected = FeignException.BadRequest.class) + @Test public void getAndValidateJwtExchangeAccessTokenWithBadSecret() { Workspace workspace = WorkspaceUtil.getSystemWorkspaceBuilder().clientSecret("bad_secret").build(); ImsService imsService = ImsService.builder().workspace(workspace).build(); - imsService.getJwtExchangeAccessToken(); + assertThrows(FeignException.BadRequest.class, imsService::getJwtExchangeAccessToken); } - @Test(expected = FeignException.BadRequest.class) + @Test public void getAndValidateJwtExchangeAccessTokenWithBadTechAccount() { Workspace workspace = WorkspaceUtil.getSystemWorkspaceBuilder().technicalAccountId("bad_tech_account_id@techacct.adobe.com").build(); ImsService imsService = ImsService.builder().workspace(workspace).build(); - imsService.getJwtExchangeAccessToken(); + assertThrows(FeignException.BadRequest.class, imsService::getJwtExchangeAccessToken); } - @Test(expected = IllegalArgumentException.class) + @Test public void getAndValidateJwtExchangeAccessTokenWithMissingPrivateKey() { Workspace workspace = WorkspaceUtil.getSystemWorkspaceBuilder().privateKey(null).build(); ImsService imsService = ImsService.builder().workspace(workspace).build(); - imsService.getJwtExchangeAccessToken(); + assertThrows(IllegalStateException.class, imsService::getJwtExchangeAccessToken); + } } diff --git a/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceTest.java b/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceTest.java new file mode 100644 index 00000000..4c957491 --- /dev/null +++ b/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceTest.java @@ -0,0 +1,168 @@ +package com.adobe.aio.ims.feign; + +import com.adobe.aio.auth.Context; +import com.adobe.aio.auth.JwtContext; +import com.adobe.aio.exception.feign.IOUpstreamError; +import com.adobe.aio.ims.ImsService; +import com.adobe.aio.ims.JwtTokenBuilder; +import com.adobe.aio.ims.model.AccessToken; +import com.adobe.aio.util.Constants; +import com.adobe.aio.workspace.Workspace; +import feign.FeignException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedConstruction; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockserver.client.MockServerClient; +import org.mockserver.junit.jupiter.MockServerExtension; +import org.mockserver.model.HttpStatusCode; +import org.mockserver.model.MediaType; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; +import static org.mockserver.model.HttpRequest.*; +import static org.mockserver.model.HttpResponse.*; + +@ExtendWith({ MockitoExtension.class, MockServerExtension.class }) +public class FeignImsServiceTest { + + protected MockServerClient client; + + @Mock(strictness = Mock.Strictness.LENIENT) + private Workspace workspace; + + @BeforeEach + void before() { + when(workspace.getImsUrl()).thenReturn(Constants.IMS_URL); + } + + @Test + void getOAuthAccessToken() { + fail("Not implemented."); + } + + @Test + void getJwtInvalidAuthContext() { + when(workspace.getAuthContext()).thenReturn(mock(Context.class)); + ImsService service = new FeignImsService(workspace); + Exception ex = assertThrows(IllegalStateException.class, service::getJwtExchangeAccessToken); + assertEquals("AuthContext in workspace not of type `JwtContext`.", ex.getMessage()); + } + + @Test + void getJwtInvalidJwtAuthContext() { + Context context = JwtContext.builder().build(); + when(workspace.getAuthContext()).thenReturn(context); + ImsService service = new FeignImsService(workspace); + assertThrows(IllegalStateException.class, service::getJwtExchangeAccessToken); + } + + @Test + void getJwtExchangeAccessTokenError(MockServerClient client) { + final String imsUrl = "http://localhost:" + client.getPort(); + final String apiKey = "API_KEY"; + final String clientSecret = "CLIENT_SECRET"; + final String jwtToken = "JWT_TOKEN_400"; + JwtContext context = mock(JwtContext.class); + when(workspace.getImsUrl()).thenReturn(imsUrl); + when(workspace.getAuthContext()).thenReturn(context); + when(workspace.getApiKey()).thenReturn(apiKey); + when(context.getClientSecret()).thenReturn(clientSecret); + + client.when( + request() + .withMethod("POST") + .withPath("/ims/exchange/jwt") + .withHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") + .withBody("client_id=API_KEY&client_secret=CLIENT_SECRET&jwt_token=JWT_TOKEN_400") + ).respond( + response().withStatusCode(HttpStatusCode.BAD_REQUEST_400.code()) + ); + + try (MockedConstruction ignored = mockConstruction(JwtTokenBuilder.class, + (mock, mockContext) -> { + // Have to tell the mocks to return the Desired JWT Token value. + when(mock.build()).thenReturn(jwtToken); + } + )) { + ImsService service = new FeignImsService(workspace); + assertThrows(FeignException.class, service::getJwtExchangeAccessToken); + } + verify(context).validate(); + } + + @Test + void getJwtExchangeAccessTokenSuccess(MockServerClient client) { + final String imsUrl = "http://localhost:" + client.getPort(); + final String apiKey = "API_KEY"; + final String clientSecret = "CLIENT_SECRET"; + final String jwtToken = "JWT_TOKEN_200"; + JwtContext context = mock(JwtContext.class); + when(workspace.getImsUrl()).thenReturn(imsUrl); + when(workspace.getAuthContext()).thenReturn(context); + when(workspace.getApiKey()).thenReturn(apiKey); + when(context.getClientSecret()).thenReturn(clientSecret); + + client.when( + request() + .withMethod("POST") + .withPath("/ims/exchange/jwt") + .withHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") + .withBody("client_id=API_KEY&client_secret=CLIENT_SECRET&jwt_token=JWT_TOKEN_200") + ).respond( + response() + .withStatusCode(HttpStatusCode.OK_200.code()) + .withHeader("Content-Type", MediaType.APPLICATION_JSON.toString()) + .withBody("{ \"access_token\": \"ACCESS_TOKEN\", \"token_type\": \"bearer\", \"expires_in\": \"1000\" }") + ); + + try (MockedConstruction ignored = mockConstruction(JwtTokenBuilder.class, + (mock, mockContext) -> { + // Have to tell the mocks to return the Desired JWT Token value. + when(mock.build()).thenReturn(jwtToken); + } + )) { + ImsService service = new FeignImsService(workspace); + AccessToken token = service.getJwtExchangeAccessToken(); + assertNotNull(token); + assertEquals("ACCESS_TOKEN", token.getAccessToken()); + } + verify(context).validate(); + } + + + + @Test + void validateInvalidJwtAuthContext() { + when(workspace.getAuthContext()).thenReturn(mock(Context.class)); + ImsService service = new FeignImsService(workspace); + assertThrows(IllegalStateException.class, service::getJwtExchangeAccessToken); + } + + @Test + void validateAccessToken(MockServerClient client) { + final String imsUrl = "http://localhost:" + client.getPort(); + final String apiKey = "API_KEY"; + final String accessToken = "ACCESS_TOKEN"; + client.when( + request() + .withMethod("POST") + .withPath("/ims/validate_token/v1") + .withHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") + .withBody("type=access_token&client_id=API_KEY&token=ACCESS_TOKEN") + ).respond( + response() + .withStatusCode(HttpStatusCode.OK_200.code()) + .withHeader("Content-Type", MediaType.APPLICATION_JSON.toString()) + .withBody("{ \"valid\": \"true\" }") + ); + JwtContext context = mock(JwtContext.class); + when(workspace.getAuthContext()).thenReturn(context); + when(workspace.getImsUrl()).thenReturn(imsUrl); + when(workspace.getApiKey()).thenReturn(apiKey); + ImsService service = new FeignImsService(workspace); + assertTrue(service.validateAccessToken(accessToken)); + } +} diff --git a/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceTestDrive.java b/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceTestDrive.java index 4dcead17..d8599346 100644 --- a/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceTestDrive.java +++ b/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceTestDrive.java @@ -12,7 +12,7 @@ package com.adobe.aio.ims.feign; import com.adobe.aio.ims.ImsService; -import com.adobe.aio.util.WorkspaceUtil; +import com.adobe.aio.ims.util.WorkspaceUtil; import com.adobe.aio.workspace.Workspace; import com.adobe.aio.ims.model.AccessToken; import com.adobe.aio.ims.util.PrivateKeyBuilder; diff --git a/ims/src/test/resources/mockserver.properties b/ims/src/test/resources/mockserver.properties new file mode 100644 index 00000000..201596c6 --- /dev/null +++ b/ims/src/test/resources/mockserver.properties @@ -0,0 +1 @@ +mockserver.logLevel=WARN From 2f34fb53c4d9818cb392d7d879518ac3e9d85d92 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Mon, 7 Aug 2023 08:43:54 -0500 Subject: [PATCH 10/35] Add OAuth API. --- .../java/com/adobe/aio/ims/ImsService.java | 2 + .../java/com/adobe/aio/ims/api/ImsApi.java | 12 +++- .../adobe/aio/ims/feign/FeignImsService.java | 20 ++++++- .../aio/ims/feign/FeignImsServiceTest.java | 59 +++++++++++++++++-- 4 files changed, 84 insertions(+), 9 deletions(-) diff --git a/ims/src/main/java/com/adobe/aio/ims/ImsService.java b/ims/src/main/java/com/adobe/aio/ims/ImsService.java index dd915da2..a31b6aa7 100644 --- a/ims/src/main/java/com/adobe/aio/ims/ImsService.java +++ b/ims/src/main/java/com/adobe/aio/ims/ImsService.java @@ -36,6 +36,8 @@ public interface ImsService { @Deprecated(since = "1.1", forRemoval = true) boolean validateAccessToken(String accessToken); + AccessToken getOAuthAccessToken(); + static Builder builder() { return new Builder(); } diff --git a/ims/src/main/java/com/adobe/aio/ims/api/ImsApi.java b/ims/src/main/java/com/adobe/aio/ims/api/ImsApi.java index 8c2436f1..cdb7e183 100644 --- a/ims/src/main/java/com/adobe/aio/ims/api/ImsApi.java +++ b/ims/src/main/java/com/adobe/aio/ims/api/ImsApi.java @@ -13,6 +13,7 @@ import com.adobe.aio.ims.model.AccessToken; import com.adobe.aio.ims.model.TokenValidation; +import feign.Body; import feign.Headers; import feign.Param; import feign.RequestLine; @@ -21,17 +22,24 @@ public interface ImsApi { @RequestLine("POST /ims/exchange/jwt") @Headers("Content-Type: application/x-www-form-urlencoded") - AccessToken getAccessToken( + AccessToken getJwtAccessToken( @Param("client_id") String clientId, @Param("client_secret") String clientSecret, @Param("jwt_token") String jwtToken); @RequestLine("POST /ims/validate_token/v1") @Headers("Content-Type: application/x-www-form-urlencoded") - TokenValidation validateToken( + TokenValidation validateJwtToken( @Param("type") String type, @Param("client_id") String clientId, @Param("token") String accessToken); + @RequestLine("POST /ims/token/v3?client_id={client_id}") + @Headers("Content-Type: application/x-www-form-urlencoded") + @Body("client_secret={client_secret}&grant_type=client_credentials&scopes={scopes}") + AccessToken getOAuthAccessToken( + @Param("client_id") String clientId, + @Param("client_secret") String clientSecret, + @Param("scopes") String scopes); } diff --git a/ims/src/main/java/com/adobe/aio/ims/feign/FeignImsService.java b/ims/src/main/java/com/adobe/aio/ims/feign/FeignImsService.java index 01d8b079..c293194a 100644 --- a/ims/src/main/java/com/adobe/aio/ims/feign/FeignImsService.java +++ b/ims/src/main/java/com/adobe/aio/ims/feign/FeignImsService.java @@ -11,7 +11,12 @@ */ package com.adobe.aio.ims.feign; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; + import com.adobe.aio.auth.JwtContext; +import com.adobe.aio.auth.OAuthContext; import com.adobe.aio.ims.ImsService; import com.adobe.aio.ims.JwtTokenBuilder; import com.adobe.aio.workspace.Workspace; @@ -41,7 +46,7 @@ public AccessToken getJwtExchangeAccessToken() { JwtTokenBuilder builder = new JwtTokenBuilder(workspace); String token = builder.build(); - return imsApi.getAccessToken(workspace.getApiKey(), context.getClientSecret(), token); + return imsApi.getJwtAccessToken(workspace.getApiKey(), context.getClientSecret(), token); } @Override @@ -50,7 +55,18 @@ public boolean validateAccessToken(String accessToken) { throw new IllegalStateException("AuthContext in workspace not of type `JwtContext`."); } - return imsApi.validateToken(ACCESS_TOKEN, workspace.getApiKey(), accessToken).getValid(); + return imsApi.validateJwtToken(ACCESS_TOKEN, workspace.getApiKey(), accessToken).getValid(); } + @Override + public AccessToken getOAuthAccessToken() { + if (!(workspace.getAuthContext() instanceof OAuthContext)) { + throw new IllegalStateException("AuthContext in workspace not of type `OAuthContext`."); + } + OAuthContext context = (OAuthContext) workspace.getAuthContext(); + String scopes = context.getScopes().stream().filter(StringUtils::isNotBlank).map(String::trim).collect(Collectors.joining(",")); + + return imsApi.getOAuthAccessToken(workspace.getApiKey(), context.getClientSecret(), scopes); + } } + diff --git a/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceTest.java b/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceTest.java index 4c957491..e106b522 100644 --- a/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceTest.java +++ b/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceTest.java @@ -1,8 +1,11 @@ package com.adobe.aio.ims.feign; +import java.util.HashSet; +import java.util.Set; + import com.adobe.aio.auth.Context; import com.adobe.aio.auth.JwtContext; -import com.adobe.aio.exception.feign.IOUpstreamError; +import com.adobe.aio.auth.OAuthContext; import com.adobe.aio.ims.ImsService; import com.adobe.aio.ims.JwtTokenBuilder; import com.adobe.aio.ims.model.AccessToken; @@ -24,12 +27,12 @@ import static org.mockito.Mockito.*; import static org.mockserver.model.HttpRequest.*; import static org.mockserver.model.HttpResponse.*; +import static org.mockserver.model.Parameter.*; +import static org.mockserver.model.ParameterBody.*; @ExtendWith({ MockitoExtension.class, MockServerExtension.class }) public class FeignImsServiceTest { - protected MockServerClient client; - @Mock(strictness = Mock.Strictness.LENIENT) private Workspace workspace; @@ -39,8 +42,54 @@ void before() { } @Test - void getOAuthAccessToken() { - fail("Not implemented."); + void getOauthInvalidAuthContext() { + when(workspace.getAuthContext()).thenReturn(mock(Context.class)); + ImsService service = new FeignImsService(workspace); + Exception ex = assertThrows(IllegalStateException.class, service::getOAuthAccessToken); + assertEquals("AuthContext in workspace not of type `OAuthContext`.", ex.getMessage()); + } + + @Test + void getOAuthAccessToken(MockServerClient client) { + final String imsUrl = "http://localhost:" + client.getPort(); + final String apiKey = "API_KEY"; + final String clientSecret = "CLIENT_SECRET"; + OAuthContext context = mock(OAuthContext.class); + when(workspace.getImsUrl()).thenReturn(imsUrl); + when(workspace.getAuthContext()).thenReturn(context); + when(workspace.getApiKey()).thenReturn(apiKey); + when(context.getClientSecret()).thenReturn(clientSecret); + + Set scopes = new HashSet<>(); + scopes.add(" SCOPE1 "); + scopes.add(" "); + scopes.add("SCOPE2"); + when(context.getScopes()).thenReturn(scopes); + + client.when( + request() + .withMethod("POST") + .withPath("/ims/token/v3") + .withQueryStringParameter("client_id", apiKey) + .withHeader("Content-Type", "application/x-www-form-urlencoded") + .withBody( + params( + param("client_secret", clientSecret), + param("grant_type", "client_credentials"), + param("scopes", "SCOPE2,SCOPE1") + ) + ) + ).respond( + response() + .withStatusCode(HttpStatusCode.OK_200.code()) + .withHeader("Content-Type", MediaType.APPLICATION_JSON.toString()) + .withBody("{ \"access_token\": \"ACCESS_TOKEN\", \"token_type\": \"bearer\", \"expires_in\": \"1000\" }") + ); + ImsService service = new FeignImsService(workspace); + AccessToken token = service.getOAuthAccessToken(); + assertNotNull(token); + assertEquals("ACCESS_TOKEN", token.getAccessToken()); + } @Test From af95cf50cf6b3def2393553b6f1b734ecfb8342c Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Mon, 7 Aug 2023 17:08:05 -0500 Subject: [PATCH 11/35] Some clean up. --- .../java/com/adobe/aio/ims/feign/JWTAuthInterceptor.java | 3 +-- ims/src/main/java/com/adobe/aio/ims/util/KeyStoreUtil.java | 2 +- .../main/java/com/adobe/aio/ims/util/PrivateKeyBuilder.java | 6 ++---- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/ims/src/main/java/com/adobe/aio/ims/feign/JWTAuthInterceptor.java b/ims/src/main/java/com/adobe/aio/ims/feign/JWTAuthInterceptor.java index 7bdb70eb..3b2c0d91 100644 --- a/ims/src/main/java/com/adobe/aio/ims/feign/JWTAuthInterceptor.java +++ b/ims/src/main/java/com/adobe/aio/ims/feign/JWTAuthInterceptor.java @@ -12,11 +12,10 @@ package com.adobe.aio.ims.feign; import com.adobe.aio.ims.ImsService; -import com.adobe.aio.workspace.Workspace; import com.adobe.aio.ims.model.AccessToken; +import com.adobe.aio.workspace.Workspace; import feign.RequestInterceptor; import feign.RequestTemplate; -import org.apache.commons.lang3.StringUtils; import static com.adobe.aio.util.Constants.*; diff --git a/ims/src/main/java/com/adobe/aio/ims/util/KeyStoreUtil.java b/ims/src/main/java/com/adobe/aio/ims/util/KeyStoreUtil.java index 85e6a346..201b68ee 100644 --- a/ims/src/main/java/com/adobe/aio/ims/util/KeyStoreUtil.java +++ b/ims/src/main/java/com/adobe/aio/ims/util/KeyStoreUtil.java @@ -42,7 +42,7 @@ public static PrivateKey getPrivateKeyFromPkcs12File(String filePath, String ali String password) throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException { KeyStore keystore = KeyStore.getInstance(PKCS_12); - keystore.load(new FileInputStream(new File(filePath)), password.toCharArray()); + keystore.load(new FileInputStream(filePath), password.toCharArray()); return (PrivateKey) keystore.getKey(alias, password.toCharArray()); } diff --git a/ims/src/main/java/com/adobe/aio/ims/util/PrivateKeyBuilder.java b/ims/src/main/java/com/adobe/aio/ims/util/PrivateKeyBuilder.java index bdaa0eb5..ce28b32b 100644 --- a/ims/src/main/java/com/adobe/aio/ims/util/PrivateKeyBuilder.java +++ b/ims/src/main/java/com/adobe/aio/ims/util/PrivateKeyBuilder.java @@ -57,8 +57,8 @@ public PrivateKeyBuilder properties(Properties properties) { return this; } - public PrivateKeyBuilder encodedPkcs8Key(String encodePkcs8Key) { - this.encodedPkcs8Key = encodePkcs8Key; + public PrivateKeyBuilder encodedPkcs8Key(String encodedPkcs8Key) { + this.encodedPkcs8Key = encodedPkcs8Key; return this; } @@ -98,6 +98,4 @@ private static PrivateKey getPrivateKey(final Map imsConfig) { + "" + e.getMessage(), e); } } - - } From 80385c54b5029d06873f7c4284fda33c283dab3c Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Mon, 7 Aug 2023 17:09:07 -0500 Subject: [PATCH 12/35] Add test cases for JWT Interceptor. --- .../main/java/com/adobe/aio/auth/Context.java | 4 +- .../com/adobe/aio/workspace/Workspace.java | 9 +- .../aio/ims/feign/JwtAuthInterceptorTest.java | 159 ++++++++++++++++++ 3 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 ims/src/test/java/com/adobe/aio/ims/feign/JwtAuthInterceptorTest.java diff --git a/core/src/main/java/com/adobe/aio/auth/Context.java b/core/src/main/java/com/adobe/aio/auth/Context.java index 84d19415..f0bd8025 100644 --- a/core/src/main/java/com/adobe/aio/auth/Context.java +++ b/core/src/main/java/com/adobe/aio/auth/Context.java @@ -29,6 +29,8 @@ public interface Context { /** * Validates this context is minimally populated and able to function. + * + * @throws IllegalStateException if this context is not valid for use for generating access tokens */ - void validate(); + default void validate() throws IllegalStateException {} } diff --git a/core/src/main/java/com/adobe/aio/workspace/Workspace.java b/core/src/main/java/com/adobe/aio/workspace/Workspace.java index edb25a53..61889c1c 100644 --- a/core/src/main/java/com/adobe/aio/workspace/Workspace.java +++ b/core/src/main/java/com/adobe/aio/workspace/Workspace.java @@ -143,6 +143,10 @@ public String getWorkspaceId() { return workspaceId; } + public Context getAuthContext() { + return authContext; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -230,7 +234,6 @@ public Builder authContext(final Context authContext) { return this; } - public Builder credentialId(final String credentialId) { if (jwtbuilder == null) { jwtbuilder = JwtContext.builder(); @@ -279,6 +282,10 @@ public Builder configMap(final Map configMap) { .consumerOrgId(configMap.get(CONSUMER_ORG_ID)) .projectId(configMap.get(PROJECT_ID)) .workspaceId(configMap.get(WORKSPACE_ID)); + + // For backwards compatibility - should this be kept? + jwtbuilder = JwtContext.builder(); + jwtbuilder.configMap(configMap); return this; } diff --git a/ims/src/test/java/com/adobe/aio/ims/feign/JwtAuthInterceptorTest.java b/ims/src/test/java/com/adobe/aio/ims/feign/JwtAuthInterceptorTest.java new file mode 100644 index 00000000..39a226db --- /dev/null +++ b/ims/src/test/java/com/adobe/aio/ims/feign/JwtAuthInterceptorTest.java @@ -0,0 +1,159 @@ +package com.adobe.aio.ims.feign; + +import java.lang.reflect.Field; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import com.adobe.aio.ims.ImsService; +import com.adobe.aio.ims.model.AccessToken; +import feign.RequestTemplate; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedConstruction; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.verification.VerificationMode; + +import static com.adobe.aio.util.Constants.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +public class JwtAuthInterceptorTest { + + private static final String ACCESS_TOKEN = "ACCESS_TOKEN"; + + @Mock + private ImsService imsService; + + @Mock + private RequestTemplate template; + + @Test + void isUpExpirationNull() throws Exception { + AccessToken token = new AccessToken(ACCESS_TOKEN, new Date().getTime()); + + when(imsService.getJwtExchangeAccessToken()).thenReturn(token); + when(imsService.validateAccessToken(ACCESS_TOKEN)).thenReturn(true); + + try (MockedConstruction ignored = mockConstruction(ImsService.Builder.class, + (mock, mockContext) -> { + when(mock.workspace(null)).thenReturn(mock); + when(mock.build()).thenReturn(imsService); + } + )) { + JWTAuthInterceptor interceptor = JWTAuthInterceptor.builder().workspace(null).build(); + assertTrue(interceptor.isUp()); + } + } + + @Test + void isUpTokenExpired() throws Exception { + AccessToken token = new AccessToken(ACCESS_TOKEN, '1'); + + when(imsService.getJwtExchangeAccessToken()).thenReturn(token); + when(imsService.validateAccessToken(ACCESS_TOKEN)).thenReturn(true); + + try (MockedConstruction ignored = mockConstruction(ImsService.Builder.class, + (mock, mockContext) -> { + when(mock.workspace(null)).thenReturn(mock); + when(mock.build()).thenReturn(imsService); + } + )) { + JWTAuthInterceptor interceptor = JWTAuthInterceptor.builder().workspace(null).build(); + Field expiresField = interceptor.getClass().getDeclaredField("expirationTimeMillis"); + expiresField.setAccessible(true); + expiresField.set(interceptor, 1L); + assertTrue(interceptor.isUp()); + } + } + + + @Test + void isUpTokenValid() throws Exception { + Calendar expires = Calendar.getInstance(); + expires.add(Calendar.HOUR, 1); + AccessToken token = new AccessToken(ACCESS_TOKEN, expires.getTimeInMillis()); + + when(imsService.validateAccessToken(ACCESS_TOKEN)).thenReturn(true); + + try (MockedConstruction ignored = mockConstruction(ImsService.Builder.class, + (mock, mockContext) -> { + when(mock.workspace(null)).thenReturn(mock); + when(mock.build()).thenReturn(imsService); + } + )) { + JWTAuthInterceptor interceptor = JWTAuthInterceptor.builder().workspace(null).build(); + Field expiresField = interceptor.getClass().getDeclaredField("expirationTimeMillis"); + expiresField.setAccessible(true); + expiresField.set(interceptor, expires.getTimeInMillis()); + + Field tokenField = interceptor.getClass().getDeclaredField("accessToken"); + tokenField.setAccessible(true); + tokenField.set(interceptor, token); + + assertTrue(interceptor.isUp()); + } + } + + @Test + void applyAlreadySet() throws Exception { + Calendar expires = Calendar.getInstance(); + expires.add(Calendar.HOUR, 1); + AccessToken token = new AccessToken(ACCESS_TOKEN, expires.getTimeInMillis()); + + Map> headers = new HashMap<>(); + headers.put(AUTHORIZATION_HEADER, Collections.EMPTY_LIST); + + when(template.headers()).thenReturn(headers); + + try (MockedConstruction ignored = mockConstruction(ImsService.Builder.class, + (mock, mockContext) -> { + when(mock.workspace(null)).thenReturn(mock); + when(mock.build()).thenReturn(imsService); + } + )) { + JWTAuthInterceptor interceptor = JWTAuthInterceptor.builder().workspace(null).build(); + Field expiresField = interceptor.getClass().getDeclaredField("expirationTimeMillis"); + expiresField.setAccessible(true); + expiresField.set(interceptor, expires.getTimeInMillis()); + + Field tokenField = interceptor.getClass().getDeclaredField("accessToken"); + tokenField.setAccessible(true); + tokenField.set(interceptor, token); + interceptor.apply(template); + } + } + + @Test + void apply() throws Exception { + Calendar expires = Calendar.getInstance(); + expires.add(Calendar.HOUR, 1); + AccessToken token = new AccessToken(ACCESS_TOKEN, expires.getTimeInMillis()); + + when(template.headers()).thenReturn(Collections.emptyMap()); + + try (MockedConstruction ignored = mockConstruction(ImsService.Builder.class, + (mock, mockContext) -> { + when(mock.workspace(null)).thenReturn(mock); + when(mock.build()).thenReturn(imsService); + } + )) { + JWTAuthInterceptor interceptor = JWTAuthInterceptor.builder().workspace(null).build(); + Field expiresField = interceptor.getClass().getDeclaredField("expirationTimeMillis"); + expiresField.setAccessible(true); + expiresField.set(interceptor, expires.getTimeInMillis()); + + Field tokenField = interceptor.getClass().getDeclaredField("accessToken"); + tokenField.setAccessible(true); + tokenField.set(interceptor, token); + interceptor.apply(template); + + verify(template).header(AUTHORIZATION_HEADER, BEARER_PREFIX + ACCESS_TOKEN); + } + } +} From bf71392217c96434d864f8869356c816c92c6a52 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Tue, 15 Aug 2023 10:48:35 -0500 Subject: [PATCH 13/35] Update to JUnit Jupiter. --- events_mgmt/pom.xml | 11 +++- .../feign/FeignProviderServiceTestDrive.java | 2 +- .../FeignRegistrationServiceTestDrive.java | 2 +- .../model/EventMetadataModelTest.java | 15 ++--- .../model/EventsOfInterestInputModelTest.java | 21 +++---- .../model/ProviderInputModelTest.java | 16 +++--- .../model/RegistrationCreateModelTest.java | 57 ++++++++++--------- .../model/RegistrationUpdateModelTest.java | 54 ++++++++++-------- 8 files changed, 99 insertions(+), 79 deletions(-) diff --git a/events_mgmt/pom.xml b/events_mgmt/pom.xml index e7b2bcf8..40489f0e 100644 --- a/events_mgmt/pom.xml +++ b/events_mgmt/pom.xml @@ -36,8 +36,8 @@ - junit - junit + org.junit.jupiter + junit-jupiter org.slf4j @@ -53,6 +53,13 @@ jackson-dataformat-hal + + com.adobe.aio + aio-lib-java-ims + ${project.version} + tests + test + diff --git a/events_mgmt/src/test/java/com/adobe/aio/event/management/feign/FeignProviderServiceTestDrive.java b/events_mgmt/src/test/java/com/adobe/aio/event/management/feign/FeignProviderServiceTestDrive.java index cbcbdd44..9f7fec14 100644 --- a/events_mgmt/src/test/java/com/adobe/aio/event/management/feign/FeignProviderServiceTestDrive.java +++ b/events_mgmt/src/test/java/com/adobe/aio/event/management/feign/FeignProviderServiceTestDrive.java @@ -15,7 +15,7 @@ import com.adobe.aio.event.management.model.EventMetadata; import com.adobe.aio.event.management.model.Provider; import com.adobe.aio.event.management.model.ProviderInputModel; -import com.adobe.aio.util.WorkspaceUtil; +import com.adobe.aio.ims.util.WorkspaceUtil; import com.adobe.aio.workspace.Workspace; import java.util.List; import java.util.Optional; diff --git a/events_mgmt/src/test/java/com/adobe/aio/event/management/feign/FeignRegistrationServiceTestDrive.java b/events_mgmt/src/test/java/com/adobe/aio/event/management/feign/FeignRegistrationServiceTestDrive.java index d214b959..dd651252 100644 --- a/events_mgmt/src/test/java/com/adobe/aio/event/management/feign/FeignRegistrationServiceTestDrive.java +++ b/events_mgmt/src/test/java/com/adobe/aio/event/management/feign/FeignRegistrationServiceTestDrive.java @@ -13,7 +13,7 @@ import com.adobe.aio.event.management.RegistrationService; import com.adobe.aio.event.management.model.RegistrationCreateModel; -import com.adobe.aio.util.WorkspaceUtil; +import com.adobe.aio.ims.util.WorkspaceUtil; import com.adobe.aio.workspace.Workspace; import com.adobe.aio.event.management.model.EventsOfInterestInputModel; import com.adobe.aio.event.management.model.Registration; diff --git a/events_mgmt/src/test/java/com/adobe/aio/event/management/model/EventMetadataModelTest.java b/events_mgmt/src/test/java/com/adobe/aio/event/management/model/EventMetadataModelTest.java index 0fc51c6a..d9148643 100644 --- a/events_mgmt/src/test/java/com/adobe/aio/event/management/model/EventMetadataModelTest.java +++ b/events_mgmt/src/test/java/com/adobe/aio/event/management/model/EventMetadataModelTest.java @@ -11,14 +11,15 @@ */ package com.adobe.aio.event.management.model; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; public class EventMetadataModelTest { - @Test(expected = IllegalArgumentException.class) + @Test public void invalid() { - EventMetadata.builder().build(); + assertThrows(IllegalArgumentException.class, () -> EventMetadata.builder().build()); } @Test @@ -28,8 +29,8 @@ public void valid() { EventMetadata eventMetadata = EventMetadata.builder() .eventCode(eventCode) .description(eventDescription).build(); - Assert.assertEquals(eventCode, eventMetadata.getEventCode()); - Assert.assertEquals(eventCode, eventMetadata.getLabel()); - Assert.assertEquals(eventDescription, eventMetadata.getDescription()); + assertEquals(eventCode, eventMetadata.getEventCode()); + assertEquals(eventCode, eventMetadata.getLabel()); + assertEquals(eventDescription, eventMetadata.getDescription()); } } diff --git a/events_mgmt/src/test/java/com/adobe/aio/event/management/model/EventsOfInterestInputModelTest.java b/events_mgmt/src/test/java/com/adobe/aio/event/management/model/EventsOfInterestInputModelTest.java index c96b09f2..c516a45a 100644 --- a/events_mgmt/src/test/java/com/adobe/aio/event/management/model/EventsOfInterestInputModelTest.java +++ b/events_mgmt/src/test/java/com/adobe/aio/event/management/model/EventsOfInterestInputModelTest.java @@ -11,24 +11,25 @@ */ package com.adobe.aio.event.management.model; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; public class EventsOfInterestInputModelTest { - @Test(expected = IllegalArgumentException.class) + @Test public void invalidEmpty() { - EventsOfInterestInputModel.builder().build(); + assertThrows(IllegalArgumentException.class, () -> EventsOfInterestInputModel.builder().build()); } - @Test(expected = IllegalArgumentException.class) + @Test public void invalidMissingProviderId() { - EventsOfInterestInputModel.builder().eventCode("some.event.code").build(); + assertThrows(IllegalArgumentException.class, () -> EventsOfInterestInputModel.builder().eventCode("some.event.code").build()); } - @Test(expected = IllegalArgumentException.class) + @Test public void invalidMissingEventCode() { - EventsOfInterestInputModel.builder().providerId("someId").build(); + assertThrows(IllegalArgumentException.class, () -> EventsOfInterestInputModel.builder().providerId("someId").build()); } @Test @@ -38,8 +39,8 @@ public void valid() { EventsOfInterestInputModel eventsOfInterestInputModel = EventsOfInterestInputModel.builder() .eventCode(eventCode) .providerId(providerId).build(); - Assert.assertEquals(eventCode, eventsOfInterestInputModel.getEventCode()); - Assert.assertEquals(providerId, eventsOfInterestInputModel.getProviderId()); + assertEquals(eventCode, eventsOfInterestInputModel.getEventCode()); + assertEquals(providerId, eventsOfInterestInputModel.getProviderId()); } } diff --git a/events_mgmt/src/test/java/com/adobe/aio/event/management/model/ProviderInputModelTest.java b/events_mgmt/src/test/java/com/adobe/aio/event/management/model/ProviderInputModelTest.java index 584224c7..35db2b96 100644 --- a/events_mgmt/src/test/java/com/adobe/aio/event/management/model/ProviderInputModelTest.java +++ b/events_mgmt/src/test/java/com/adobe/aio/event/management/model/ProviderInputModelTest.java @@ -11,16 +11,16 @@ */ package com.adobe.aio.event.management.model; -import static com.adobe.aio.util.Constants.CUSTOM_EVENTS_PROVIDER_METADATA_ID; +import org.junit.jupiter.api.Test; -import org.junit.Assert; -import org.junit.Test; +import static com.adobe.aio.util.Constants.CUSTOM_EVENTS_PROVIDER_METADATA_ID; +import static org.junit.jupiter.api.Assertions.*; public class ProviderInputModelTest { - @Test(expected = IllegalArgumentException.class) + @Test public void invalid() { - ProviderInputModel.builder().build(); + assertThrows(IllegalArgumentException.class, () -> ProviderInputModel.builder().build()); } @Test @@ -32,9 +32,9 @@ public void valid() { .description(description) .docsUrl("https://github.com/adobe/aio-lib-java") .build(); - Assert.assertEquals(description, providerInputModel.getDescription()); - Assert.assertEquals(label, providerInputModel.getLabel()); - Assert.assertEquals(CUSTOM_EVENTS_PROVIDER_METADATA_ID, + assertEquals(description, providerInputModel.getDescription()); + assertEquals(label, providerInputModel.getLabel()); + assertEquals(CUSTOM_EVENTS_PROVIDER_METADATA_ID, providerInputModel.getProviderMetadataId()); } } diff --git a/events_mgmt/src/test/java/com/adobe/aio/event/management/model/RegistrationCreateModelTest.java b/events_mgmt/src/test/java/com/adobe/aio/event/management/model/RegistrationCreateModelTest.java index eba5ca03..a1b9d604 100644 --- a/events_mgmt/src/test/java/com/adobe/aio/event/management/model/RegistrationCreateModelTest.java +++ b/events_mgmt/src/test/java/com/adobe/aio/event/management/model/RegistrationCreateModelTest.java @@ -11,52 +11,57 @@ */ package com.adobe.aio.event.management.model; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; public class RegistrationCreateModelTest { - @Test(expected = IllegalArgumentException.class) + @Test public void invalidEmpty() { - RegistrationCreateModel.builder().build(); + assertThrows(IllegalArgumentException.class, () -> RegistrationCreateModel.builder().build()); } - @Test(expected = IllegalArgumentException.class) + @Test public void invalidMissingClientId() { - RegistrationCreateModel registrationInputModel = RegistrationCreateModel.builder() + assertThrows(IllegalArgumentException.class, () -> RegistrationCreateModel.builder() .name("some name") .description("some description") - .build(); + .build() + ); } - @Test(expected = IllegalArgumentException.class) + @Test public void invalidMissingName() { - RegistrationCreateModel.builder() + assertThrows(IllegalArgumentException.class, () -> RegistrationCreateModel.builder() .description("some description") - .clientId("some client Id").build(); + .clientId("some client Id").build() + ); } - @Test(expected = IllegalArgumentException.class) + @Test public void missingDeliveryType() { String name = "com.adobe.aio.event.management.test.registration"; String description = name + " description"; String clientId = "some_clientId"; - RegistrationCreateModel registrationInputModel = RegistrationCreateModel.builder() + assertThrows(IllegalArgumentException.class, () -> RegistrationCreateModel.builder() .name(name) .description(description) - .clientId(clientId).build(); + .clientId(clientId).build() + ); } - @Test(expected = IllegalArgumentException.class) + @Test public void invalidWebhookUrlMissing() { String name = "com.adobe.aio.event.management.test.registration"; String description = name + " description"; String clientId = "some_clientId"; - RegistrationCreateModel registrationInputModel = RegistrationCreateModel.builder() - .name(name) - .description(description) - .clientId(clientId) - .deliveryType("webhook").build(); + assertThrows(IllegalArgumentException.class, () -> RegistrationCreateModel.builder() + .name(name) + .description(description) + .clientId(clientId) + .deliveryType("webhook").build() + ); } @Test @@ -65,12 +70,12 @@ public void valid() { String description = name + " description"; String clientId = "some_clientId"; RegistrationCreateModel registrationInputModel = RegistrationCreateModel.builder() - .name(name) - .description(description) - .clientId(clientId) - .deliveryType("journal").build(); - Assert.assertEquals(clientId, registrationInputModel.getClientId()); - Assert.assertEquals(name, registrationInputModel.getName()); - Assert.assertEquals(description, registrationInputModel.getDescription()); + .name(name) + .description(description) + .clientId(clientId) + .deliveryType("journal").build(); + assertEquals(clientId, registrationInputModel.getClientId()); + assertEquals(name, registrationInputModel.getName()); + assertEquals(description, registrationInputModel.getDescription()); } } diff --git a/events_mgmt/src/test/java/com/adobe/aio/event/management/model/RegistrationUpdateModelTest.java b/events_mgmt/src/test/java/com/adobe/aio/event/management/model/RegistrationUpdateModelTest.java index 06691040..d23d3be5 100644 --- a/events_mgmt/src/test/java/com/adobe/aio/event/management/model/RegistrationUpdateModelTest.java +++ b/events_mgmt/src/test/java/com/adobe/aio/event/management/model/RegistrationUpdateModelTest.java @@ -11,8 +11,9 @@ */ package com.adobe.aio.event.management.model; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; public class RegistrationUpdateModelTest { @@ -20,42 +21,47 @@ public class RegistrationUpdateModelTest { public static final String DESCRIPTION = NAME + " description"; public static final String DELIVERY_TYPE = "journal"; - @Test(expected = IllegalArgumentException.class) + @Test public void invalidEmpty() { - RegistrationCreateModel.builder().build(); + assertThrows(IllegalArgumentException.class, () -> RegistrationCreateModel.builder().build()); } - - @Test(expected = IllegalArgumentException.class) + @Test public void invalidMissingName() { - RegistrationUpdateModel.builder() - .description(DESCRIPTION) - .build(); + assertThrows(IllegalArgumentException.class, () -> + RegistrationUpdateModel.builder() + .description(DESCRIPTION) + .build() + ); } - @Test(expected = IllegalArgumentException.class) + @Test public void missingDeliveryType() { - RegistrationUpdateModel.builder() - .name(NAME) - .description(DESCRIPTION) - .build(); + assertThrows(IllegalArgumentException.class, () -> + RegistrationUpdateModel.builder() + .name(NAME) + .description(DESCRIPTION) + .build() + ); } - @Test(expected = IllegalArgumentException.class) + @Test public void invalidWebhookUrlMissing() { - RegistrationUpdateModel.builder() - .name(NAME) - .description(DESCRIPTION) - .deliveryType("webhook").build(); + assertThrows(IllegalArgumentException.class, () -> + RegistrationUpdateModel.builder() + .name(NAME) + .description(DESCRIPTION) + .deliveryType("webhook").build() + ); } @Test public void valid() { RegistrationUpdateModel registrationInputModel = RegistrationUpdateModel.builder() - .name(NAME) - .description(DESCRIPTION) - .deliveryType(DELIVERY_TYPE).build(); - Assert.assertEquals(NAME, registrationInputModel.getName()); - Assert.assertEquals(DESCRIPTION, registrationInputModel.getDescription()); + .name(NAME) + .description(DESCRIPTION) + .deliveryType(DELIVERY_TYPE).build(); + assertEquals(NAME, registrationInputModel.getName()); + assertEquals(DESCRIPTION, registrationInputModel.getDescription()); } } From b9a2de24d10b92ce4c9168f9fadd7c09f84c877d Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Tue, 15 Aug 2023 10:49:09 -0500 Subject: [PATCH 14/35] Update to JUnit Jupiter. --- events_ingress/pom.xml | 11 +++++++++-- .../publish/feign/FeignPublishServiceTestDrive.java | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/events_ingress/pom.xml b/events_ingress/pom.xml index 77b41691..6e9c7d55 100644 --- a/events_ingress/pom.xml +++ b/events_ingress/pom.xml @@ -36,8 +36,8 @@ - junit - junit + org.junit.jupiter + junit-jupiter org.slf4j @@ -49,6 +49,13 @@ ${project.version} + + com.adobe.aio + aio-lib-java-ims + ${project.version} + tests + test + diff --git a/events_ingress/src/test/java/com/adobe/aio/event/publish/feign/FeignPublishServiceTestDrive.java b/events_ingress/src/test/java/com/adobe/aio/event/publish/feign/FeignPublishServiceTestDrive.java index 4cccce47..90dba8bb 100644 --- a/events_ingress/src/test/java/com/adobe/aio/event/publish/feign/FeignPublishServiceTestDrive.java +++ b/events_ingress/src/test/java/com/adobe/aio/event/publish/feign/FeignPublishServiceTestDrive.java @@ -14,7 +14,7 @@ import com.adobe.aio.event.publish.PublishService; import com.adobe.aio.event.publish.model.CloudEvent; import com.adobe.aio.util.JacksonUtil; -import com.adobe.aio.util.WorkspaceUtil; +import com.adobe.aio.ims.util.WorkspaceUtil; import com.adobe.aio.workspace.Workspace; import org.slf4j.Logger; import org.slf4j.LoggerFactory; From 3c234f8c1a3e0bec82846e6942f8edc30d0d2fbd Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Tue, 15 Aug 2023 10:49:45 -0500 Subject: [PATCH 15/35] Update to JUnit Jupiter & move away from mocking server calls --- events_journal/pom.xml | 28 ++- .../journal/feign/JournalLinkDecoder.java | 42 +++-- .../feign/FeignJournalServiceTestDrive.java | 2 +- .../journal/feign/JournalLinkDecoderTest.java | 170 ++++++++++-------- 4 files changed, 136 insertions(+), 106 deletions(-) diff --git a/events_journal/pom.xml b/events_journal/pom.xml index 1196309d..e5f4dac4 100644 --- a/events_journal/pom.xml +++ b/events_journal/pom.xml @@ -48,13 +48,8 @@ test - junit - junit - - - com.squareup.okhttp3 - mockwebserver - test + org.junit.jupiter + junit-jupiter org.slf4j @@ -66,6 +61,25 @@ ${project.version} + + com.adobe.aio + aio-lib-java-ims + ${project.version} + tests + test + + + org.mockito + mockito-core + + + org.mockito + mockito-junit-jupiter + + + org.mock-server + mockserver-junit-jupiter-no-dependencies + diff --git a/events_journal/src/main/java/com/adobe/aio/event/journal/feign/JournalLinkDecoder.java b/events_journal/src/main/java/com/adobe/aio/event/journal/feign/JournalLinkDecoder.java index ba7fd652..25111629 100644 --- a/events_journal/src/main/java/com/adobe/aio/event/journal/feign/JournalLinkDecoder.java +++ b/events_journal/src/main/java/com/adobe/aio/event/journal/feign/JournalLinkDecoder.java @@ -53,39 +53,38 @@ public JournalLinkDecoder(Decoder delegate) { public Object decode(Response response, Type type) throws IOException { if (!rawClass(type).equals(JournalEntry.class)) { - throw new DecodeException(response.status(), - String.format("%s is not a type supported by this decoder.", type), response.request()); - } else if (response.status() >= 200 && response.status() < 300) { - JournalEntry entry = new JournalEntry(); - if (response.status() == 204 && response.headers() != null && response.headers() - .containsKey(RETRY_AFTER_HEADER)) { + throw new DecodeException(response.status(), String.format("%s is not a type supported by this decoder.", type), response.request()); + } + + if (response.status() < 200 || response.status() >= 300) { + logger.info("Not decoding Journal header link values when the response status is `{}`", response.status()); + return Util.emptyValueOf(type); + } + + JournalEntry entry = (JournalEntry) this.delegate.decode(response, type); + // Decode could return null. + if (entry == null) entry = new JournalEntry(); + + if (response.headers() != null) { + if (response.status() == 204 && response.headers().containsKey(RETRY_AFTER_HEADER)) { entry.setRetryAfterInSeconds(response.headers().get(RETRY_AFTER_HEADER).iterator().next()); } - if (response.status() != 204) { - entry = (JournalEntry) this.delegate.decode(response, type); - } - if (entry != null && response.headers() != null && response.headers() - .containsKey(LINK_HEADER)) { + if (response.headers().containsKey(LINK_HEADER)) { Map links = new HashMap<>(); - URL requestUrl = new URL(response.request().url()); Collection linkValues = response.headers().get(LINK_HEADER); for (String linkValue : linkValues) { Pattern pattern = Pattern.compile(LINK_PATH_NAME_REGEXP); Matcher matcher = pattern.matcher(linkValue); - if (!matcher.find()) { - logger.error("unexpected Journal header link values format."); - } else { + if (matcher.find()) { links.put(matcher.group(2), getRootUrl(response.request()) + matcher.group(1)); + } else { + logger.error("unexpected Journal header link values format."); } } entry.setLinks(links); } - return entry; - } else { - logger.info("Not decoding Journal header link values when the response status is `{}`", - response.status()); - return Util.emptyValueOf(type); } + return entry; } private String getRootUrl(Request request) throws MalformedURLException { @@ -94,8 +93,7 @@ private String getRootUrl(Request request) throws MalformedURLException { ("http".equals(requestUrl.getProtocol()) && requestUrl.getPort() == 80)) { return requestUrl.getProtocol() + "://" + requestUrl.getHost(); } else { - return requestUrl.getProtocol() + "://" + requestUrl.getHost() + ":" - + requestUrl.getPort(); + return requestUrl.getProtocol() + "://" + requestUrl.getHost() + ":" + requestUrl.getPort(); } } } diff --git a/events_journal/src/test/java/com/adobe/aio/event/journal/feign/FeignJournalServiceTestDrive.java b/events_journal/src/test/java/com/adobe/aio/event/journal/feign/FeignJournalServiceTestDrive.java index 9e4f06df..4a6e7de1 100644 --- a/events_journal/src/test/java/com/adobe/aio/event/journal/feign/FeignJournalServiceTestDrive.java +++ b/events_journal/src/test/java/com/adobe/aio/event/journal/feign/FeignJournalServiceTestDrive.java @@ -13,7 +13,7 @@ import com.adobe.aio.event.journal.JournalService; import com.adobe.aio.event.journal.model.JournalEntry; -import com.adobe.aio.util.WorkspaceUtil; +import com.adobe.aio.ims.util.WorkspaceUtil; import com.adobe.aio.workspace.Workspace; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/events_journal/src/test/java/com/adobe/aio/event/journal/feign/JournalLinkDecoderTest.java b/events_journal/src/test/java/com/adobe/aio/event/journal/feign/JournalLinkDecoderTest.java index 649b61e8..609e53c7 100644 --- a/events_journal/src/test/java/com/adobe/aio/event/journal/feign/JournalLinkDecoderTest.java +++ b/events_journal/src/test/java/com/adobe/aio/event/journal/feign/JournalLinkDecoderTest.java @@ -11,99 +11,117 @@ */ package com.adobe.aio.event.journal.feign; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; -import com.adobe.aio.event.journal.api.JournalApi; import com.adobe.aio.event.journal.model.JournalEntry; -import com.adobe.aio.util.JacksonUtil; -import com.adobe.aio.util.feign.FeignUtil; -import feign.Feign; -import feign.jackson.JacksonDecoder; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import org.junit.Assert; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; +import com.fasterxml.jackson.databind.ObjectMapper; +import feign.Request; +import feign.Response; +import feign.codec.DecodeException; +import feign.codec.Decoder; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) public class JournalLinkDecoderTest { + @Mock + private Decoder mockDelegate; + + @Mock + private Request request; + + @Mock + private Response response; + private static final String TEST_IMS_ORG_ID = "testImsOrgId"; private static final int RETRY_AFTER_VALUE = 10; - @Rule - public ExpectedException expectedEx = ExpectedException.none(); @Test - public void get204() { - final MockWebServer server = new MockWebServer(); - String rootServerUrl = server.url("/").toString(); - server.enqueue(new MockResponse() - .addHeader(JournalLinkDecoder.RETRY_AFTER_HEADER, RETRY_AFTER_VALUE) - .addHeader(JournalLinkDecoder.LINK_HEADER, getNextLink()) - .addHeader(JournalLinkDecoder.LINK_HEADER, getCountLink()) - .setResponseCode(204)); - server.enqueue(new MockResponse().setBody("foo")); - - final JournalApi api = Feign.builder() - .decoder(new JournalLinkDecoder(new JacksonDecoder(JacksonUtil.DEFAULT_OBJECT_MAPPER))) - .target(JournalApi.class, rootServerUrl); - JournalEntry entry = api.get(TEST_IMS_ORG_ID); - assertTrue(entry.isEmpty()); - assertEquals("http://localhost:" + server.getPort() - + "/events-fast/organizations/organizations/junit/integrations/junit/junit?since=salmon:1.salmon:2", - entry.getNextLink()); - assertEquals("http://localhost:" + server.getPort() - + "/count/organizations/organizations/junit/integrations/junit/junit?since=salmon:1.salmon:2", - entry.getLinks().get("count")); - assertEquals(RETRY_AFTER_VALUE, entry.getRetryAfterInSeconds()); + public void unsupportedType() { + when(response.request()).thenReturn(request); + JournalLinkDecoder decoder = new JournalLinkDecoder(mockDelegate); + assertThrows(DecodeException.class, () -> decoder.decode(response, JournalLinkDecoder.class)); } @Test - public void get200() { - final MockWebServer server = new MockWebServer(); - String rootServerUrl = server.url("/").toString(); - String entryJsonPayload = "{\"events\":" - + " [ { \"position\": \"aposition\", \"event\": { \"key\": \"value\" }} ]," - + " \"_page\": { \"last\": \"aposition\", \"count\": 1 }}"; - server.enqueue(new MockResponse() - .addHeader(JournalLinkDecoder.LINK_HEADER, getNextLink()) - .addHeader(JournalLinkDecoder.LINK_HEADER, getCountLink()) - .setResponseCode(200) - .setBody(entryJsonPayload)); - - final JournalApi api = Feign.builder() - .decoder(new JournalLinkDecoder(new JacksonDecoder(JacksonUtil.DEFAULT_OBJECT_MAPPER))) - .target(JournalApi.class, rootServerUrl); - JournalEntry entry = api.get(TEST_IMS_ORG_ID); - assertTrue(!entry.isEmpty()); - assertEquals("http://localhost:" + server.getPort() - + "/events-fast/organizations/organizations/junit/integrations/junit/junit?since=salmon:1.salmon:2", - entry.getNextLink()); - assertEquals("http://localhost:" + server.getPort() - + "/count/organizations/organizations/junit/integrations/junit/junit?since=salmon:1.salmon:2", - entry.getLinks().get("count")); - assertEquals(1, entry.size()); - assertEquals("aposition", entry.getEvents().get(0).getPosition()); - assertEquals("{\"key\":\"value\"}", entry.getEvents().get(0).getEvent().toString()); - assertEquals("aposition", entry.getPage().getLast()); - assertEquals(1, entry.getPage().getCount()); + public void errorResponse() throws Exception { + when(response.status()).thenReturn(302); + JournalLinkDecoder decoder = new JournalLinkDecoder(mockDelegate); + assertNull(decoder.decode(response, JournalEntry.class)); } @Test - public void get404() { - final MockWebServer server = new MockWebServer(); - String rootServerUrl = server.url("/").toString(); - server.enqueue(new MockResponse() - .setBody("some, non json, page: that is !") - .setResponseCode(404)); - - final JournalApi api = FeignUtil.getBaseBuilder() - .decoder(new JournalLinkDecoder(new JacksonDecoder(JacksonUtil.DEFAULT_OBJECT_MAPPER))) - .target(JournalApi.class, rootServerUrl); - Assert.assertNull(api.get(TEST_IMS_ORG_ID)); + public void get204() throws Exception { + final String rootServerUrl = "http://localhost:1234"; + + Map> headers = new HashMap<>(); + headers.put(JournalLinkDecoder.RETRY_AFTER_HEADER, Collections.singleton("" + RETRY_AFTER_VALUE)); + List links = new ArrayList<>(); + links.add(getNextLink()); + links.add(getCountLink()); + headers.put(JournalLinkDecoder.LINK_HEADER, links); + + when(response.status()).thenReturn(204); + when(response.headers()).thenReturn(headers); + when(response.request()).thenReturn(request); + + when(request.url()).thenReturn(rootServerUrl); + + JournalLinkDecoder decoder = new JournalLinkDecoder(mockDelegate); + JournalEntry actual = (JournalEntry) decoder.decode(response, JournalEntry.class); + + assertTrue(actual.isEmpty()); + assertEquals("http://localhost:1234/events-fast/organizations/organizations/junit/integrations/junit/junit?since=salmon:1.salmon:2", actual.getNextLink()); + assertEquals("http://localhost:1234/count/organizations/organizations/junit/integrations/junit/junit?since=salmon:1.salmon:2", actual.getLinks().get("count")); + assertEquals(RETRY_AFTER_VALUE, actual.getRetryAfterInSeconds()); } + @Test + public void get200() throws Exception { + final String rootServerUrl = "http://localhost:1234"; + + String payload = "{\"events\": [ { \"position\": \"aposition\", \"event\": { \"key\": \"value\" }} ], \"_page\": { \"last\": \"aposition\", \"count\": 1 }}"; + JournalEntry entry = new ObjectMapper().readValue(payload, JournalEntry.class); + + Map> headers = new HashMap<>(); + headers.put(JournalLinkDecoder.RETRY_AFTER_HEADER, Collections.singleton("" + RETRY_AFTER_VALUE)); + List links = new ArrayList<>(); + links.add(getNextLink()); + links.add(getCountLink()); + headers.put(JournalLinkDecoder.LINK_HEADER, links); + + when(response.status()).thenReturn(200); + when(response.headers()).thenReturn(headers); + when(response.request()).thenReturn(request); + + when(request.url()).thenReturn(rootServerUrl); + + when(mockDelegate.decode(response, JournalEntry.class)).thenReturn(entry); + + JournalLinkDecoder decoder = new JournalLinkDecoder(mockDelegate); + JournalEntry actual = (JournalEntry) decoder.decode(response, JournalEntry.class); + + assertFalse(actual.isEmpty()); + assertEquals("http://localhost:1234/events-fast/organizations/organizations/junit/integrations/junit/junit?since=salmon:1.salmon:2", actual.getNextLink()); + assertEquals("http://localhost:1234/count/organizations/organizations/junit/integrations/junit/junit?since=salmon:1.salmon:2", actual.getLinks().get("count")); + assertEquals(1, actual.size()); + assertEquals("aposition", entry.getEvents().get(0).getPosition()); + assertEquals("{\"key\":\"value\"}", entry.getEvents().get(0).getEvent().toString()); + assertEquals("aposition", entry.getPage().getLast()); + assertEquals(1, entry.getPage().getCount()); + } private String getNextLink() { return "; rel=\"next\""; From 4bc67b2e41679f2d07ed6718e2f3e511a345bd69 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Tue, 15 Aug 2023 10:50:13 -0500 Subject: [PATCH 16/35] Build test jar for WorkflowUtil --- ims/pom.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ims/pom.xml b/ims/pom.xml index 7be64f8e..b4bb4473 100644 --- a/ims/pom.xml +++ b/ims/pom.xml @@ -74,4 +74,20 @@ + + + + org.apache.maven.plugins + maven-jar-plugin + + + test-jar + + test-jar + + + + + + From 5d1e40a0f9789c18263e1a29e367622632c1a0d2 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Wed, 16 Aug 2023 13:14:23 -0500 Subject: [PATCH 17/35] Remove network requirement for tests. --- events_webhook/pom.xml | 18 +- .../webhook/service/EventVerifierTest.java | 280 ++++++++++-------- 2 files changed, 172 insertions(+), 126 deletions(-) diff --git a/events_webhook/pom.xml b/events_webhook/pom.xml index fc96312d..53acc62f 100644 --- a/events_webhook/pom.xml +++ b/events_webhook/pom.xml @@ -34,17 +34,25 @@ - junit - junit + org.junit.jupiter + junit-jupiter org.slf4j slf4j-api - com.github.tomakehurst - wiremock-jre8 + org.mockito + mockito-core + + + org.mockito + mockito-junit-jupiter + + + org.mock-server + mockserver-junit-jupiter-no-dependencies - \ No newline at end of file + diff --git a/events_webhook/src/test/java/com/adobe/aio/event/webhook/service/EventVerifierTest.java b/events_webhook/src/test/java/com/adobe/aio/event/webhook/service/EventVerifierTest.java index 84685625..c407672d 100644 --- a/events_webhook/src/test/java/com/adobe/aio/event/webhook/service/EventVerifierTest.java +++ b/events_webhook/src/test/java/com/adobe/aio/event/webhook/service/EventVerifierTest.java @@ -12,29 +12,25 @@ package com.adobe.aio.event.webhook.service; -import static com.adobe.aio.event.webhook.service.EventVerifier.ADOBE_IOEVENTS_DIGI_SIGN_1; -import static com.adobe.aio.event.webhook.service.EventVerifier.ADOBE_IOEVENTS_DIGI_SIGN_2; -import static com.adobe.aio.event.webhook.service.EventVerifier.ADOBE_IOEVENTS_PUB_KEY_1_PATH; -import static com.adobe.aio.event.webhook.service.EventVerifier.ADOBE_IOEVENTS_PUB_KEY_2_PATH; -import static com.adobe.aio.event.webhook.service.EventVerifier.RECIPIENT_CLIENT_ID; -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.configureFor; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.notFound; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import com.github.tomakehurst.wiremock.WireMockServer; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import java.util.HashMap; import java.util.Map; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import com.adobe.aio.event.webhook.feign.FeignPubKeyService; +import com.adobe.aio.exception.AIOException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedConstruction; +import org.mockito.junit.jupiter.MockitoExtension; + +import static com.adobe.aio.event.webhook.service.EventVerifier.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) public class EventVerifierTest { private static final String API_KEY = "client_id1"; @@ -45,122 +41,170 @@ public class EventVerifierTest { private static final String PUB_KEY1_PATH = "/junit/pub-key-1.pem"; private static final String PUB_KEY2_PATH = "/junit/pub-key-2.pem"; private static final String ANOTHER_PUB_KEY_PATH = "/junit/another-pub-key.pem"; - private static final String NOT_FOUND_PUB_KEY_PATH = "/junit/not-found-pub-key.pem"; - - private EventVerifier underTest; + private static final String VALID_PAYLOAD = "{\"event_id\":\"eventId1\",\"event\":{\"hello\":\"world\"},\"" + RECIPIENT_CLIENT_ID + "\":\"" + API_KEY + "\"}"; - private WireMockServer wireMockServer; + private static final PublicKey PUBLIC_KEY1; + private static final PublicKey PUBLIC_KEY2; + private static final PublicKey ANOTHER_PUBLIC_KEY; - @Before - public void setup() { - - wireMockServer = new WireMockServer(wireMockConfig().dynamicPort()); - wireMockServer.start(); - int port = wireMockServer.port(); - setupEndpointStub(port); - underTest = new EventVerifier("http://localhost:" + port); - } - - @After - public void tearDown() { - wireMockServer.stop(); - } - - private void setupEndpointStub(int port) { - configureFor("localhost", port); - stubFor(get(urlEqualTo(PUB_KEY1_PATH)) - .willReturn(aResponse().withBody(getPubKey1()))); - stubFor(get(urlEqualTo(PUB_KEY2_PATH)) - .willReturn(aResponse().withBody(getPubKey2()))); - stubFor(get(urlEqualTo(ANOTHER_PUB_KEY_PATH)) - .willReturn(aResponse().withBody(getAnotherPubKey()))); - stubFor(get(urlEqualTo(NOT_FOUND_PUB_KEY_PATH)) - .willReturn(notFound())); + private static final Map VALID_HEADERS = new HashMap<>(); + static { + VALID_HEADERS.put(ADOBE_IOEVENTS_DIGI_SIGN_1, VALID_SIGNATURE_1); + VALID_HEADERS.put(ADOBE_IOEVENTS_DIGI_SIGN_2, VALID_SIGNATURE_2); + VALID_HEADERS.put(ADOBE_IOEVENTS_PUB_KEY_1_PATH, PUB_KEY1_PATH); + VALID_HEADERS.put(ADOBE_IOEVENTS_PUB_KEY_2_PATH, PUB_KEY2_PATH); + try { + PUBLIC_KEY1 = getPubKey1(); + PUBLIC_KEY2 = getPubKey2(); + ANOTHER_PUBLIC_KEY = getAnotherPubKey(); + } catch (Exception e) { + throw new RuntimeException(e); + } } @Test - public void testVerifyWithValidSignatures() { - assertTrue(underTest.verify(getValidPayload(), API_KEY, getValidHeaders())); + public void validSignatures() { + try (MockedConstruction ignored = mockConstruction(FeignPubKeyService.class, + (mock, mockContext) -> { + when(mock.getAioEventsPublicKey(PUB_KEY1_PATH)).thenReturn(PUBLIC_KEY1); + } + )) { + assertTrue(new EventVerifier().verify(VALID_PAYLOAD, API_KEY, VALID_HEADERS)); + } } @Test - public void testVerifyValidSignature1() { - Map headers = getValidHeaders(); + public void invalidValidSignature1() { + Map headers = new HashMap<>(VALID_HEADERS); headers.put(ADOBE_IOEVENTS_DIGI_SIGN_1, INVALID_SIGNATURE); - assertTrue(underTest.verify(getValidPayload(), API_KEY, headers)); + + try (MockedConstruction ignored = mockConstruction(FeignPubKeyService.class, + (mock, mockContext) -> { + when(mock.getAioEventsPublicKey(PUB_KEY1_PATH)).thenReturn(PUBLIC_KEY1); + when(mock.getAioEventsPublicKey(PUB_KEY2_PATH)).thenReturn(PUBLIC_KEY2); + } + )) { + assertTrue(new EventVerifier().verify(VALID_PAYLOAD, API_KEY, headers)); + } } @Test - public void testVerifyValidSignature2() { - Map headers = getValidHeaders(); + public void invalidSignature2() { + Map headers = new HashMap<>(VALID_HEADERS); headers.put(ADOBE_IOEVENTS_DIGI_SIGN_2, INVALID_SIGNATURE); - assertTrue(underTest.verify(getValidPayload(), API_KEY, headers)); + try (MockedConstruction ignored = mockConstruction(FeignPubKeyService.class, + (mock, mockContext) -> { + when(mock.getAioEventsPublicKey(PUB_KEY1_PATH)).thenReturn(PUBLIC_KEY1); + } + )) { + assertTrue(new EventVerifier().verify(VALID_PAYLOAD, API_KEY, headers)); + } } @Test - public void testVerifyInvalidSignature() { - Map headers = getValidHeaders(); + public void invalidSignatures() { + Map headers = new HashMap<>(VALID_HEADERS); + headers.put(ADOBE_IOEVENTS_DIGI_SIGN_1, INVALID_SIGNATURE); headers.put(ADOBE_IOEVENTS_DIGI_SIGN_2, INVALID_SIGNATURE); - assertFalse(underTest.verify(getValidPayload(), API_KEY, headers)); + try (MockedConstruction ignored = mockConstruction(FeignPubKeyService.class, + (mock, mockContext) -> { + when(mock.getAioEventsPublicKey(PUB_KEY1_PATH)).thenReturn(PUBLIC_KEY1); + when(mock.getAioEventsPublicKey(PUB_KEY2_PATH)).thenReturn(PUBLIC_KEY2); + } + )) { + assertFalse(new EventVerifier().verify(VALID_PAYLOAD, API_KEY, headers)); + } } @Test - public void testVerifyBadlyFormattedSignature() { - String testPayload = getValidPayload(); - Map headers = getValidHeaders(); - // not base64 encoded - headers.put(ADOBE_IOEVENTS_DIGI_SIGN_1, "some random String"); - headers.put(ADOBE_IOEVENTS_DIGI_SIGN_2, "some random String"); - assertFalse(underTest.verify(testPayload, API_KEY, headers)); - - // just base64 encoded - headers.put(ADOBE_IOEVENTS_DIGI_SIGN_1, - Base64.getEncoder().encodeToString("some random String".getBytes())); - headers.put(ADOBE_IOEVENTS_DIGI_SIGN_2, - Base64.getEncoder().encodeToString("some random String".getBytes())); - assertFalse(underTest.verify(testPayload, API_KEY, headers)); + public void badlyFormattedSignature() { + Map headers = new HashMap<>(VALID_HEADERS); + try (MockedConstruction ignored = mockConstruction(FeignPubKeyService.class, + (mock, mockContext) -> { + when(mock.getAioEventsPublicKey(PUB_KEY1_PATH)).thenReturn(PUBLIC_KEY1); + when(mock.getAioEventsPublicKey(PUB_KEY2_PATH)).thenReturn(PUBLIC_KEY2); + } + )) { + EventVerifier underTest = new EventVerifier(); + // not base64 encoded + headers.put(ADOBE_IOEVENTS_DIGI_SIGN_1, "some random String"); + headers.put(ADOBE_IOEVENTS_DIGI_SIGN_2, "some random String"); + assertFalse(underTest.verify(VALID_PAYLOAD, API_KEY, headers)); + + // just base64 encoded + headers.put(ADOBE_IOEVENTS_DIGI_SIGN_1, Base64.getEncoder().encodeToString("some random String".getBytes())); + headers.put(ADOBE_IOEVENTS_DIGI_SIGN_2, Base64.getEncoder().encodeToString("some random String".getBytes())); + assertFalse(underTest.verify(VALID_PAYLOAD, API_KEY, headers)); + } } @Test - public void testVerifyPublicKeyNotFound() { - Map headers = getValidHeaders(); - headers.put(ADOBE_IOEVENTS_PUB_KEY_1_PATH, NOT_FOUND_PUB_KEY_PATH); - headers.put(ADOBE_IOEVENTS_PUB_KEY_2_PATH, NOT_FOUND_PUB_KEY_PATH); - assertFalse(underTest.verify(getValidPayload(), API_KEY, headers)); + public void publicKeyNotFound() { + Map headers = new HashMap<>(VALID_HEADERS); + try (MockedConstruction ignored = mockConstruction(FeignPubKeyService.class, + (mock, mockContext) -> { + when(mock.getAioEventsPublicKey(PUB_KEY1_PATH)).thenThrow(new AIOException("Error")); + when(mock.getAioEventsPublicKey(PUB_KEY2_PATH)).thenThrow(new AIOException("Error")); + } + )) { + assertFalse(new EventVerifier().verify(VALID_PAYLOAD, API_KEY, headers)); + } } @Test - public void testVerifyOnePublicKeyNotFound() { - Map headers = getValidHeaders(); - headers.put(ADOBE_IOEVENTS_PUB_KEY_1_PATH, NOT_FOUND_PUB_KEY_PATH); - assertTrue(underTest.verify(getValidPayload(), API_KEY, headers)); + public void onePublicKeyNotFound() { + Map headers = new HashMap<>(VALID_HEADERS); + try (MockedConstruction ignored = mockConstruction(FeignPubKeyService.class, + (mock, mockContext) -> { + when(mock.getAioEventsPublicKey(PUB_KEY1_PATH)).thenThrow(new AIOException("Error")); + when(mock.getAioEventsPublicKey(PUB_KEY2_PATH)).thenReturn(PUBLIC_KEY2); + } + )) { + EventVerifier underTest = new EventVerifier(); + assertTrue(underTest.verify(VALID_PAYLOAD, API_KEY, headers)); + } } @Test - public void testVerifyPublicKeyMismatch() { - Map headers = getValidHeaders(); + public void publicKeyMismatch() { + Map headers = new HashMap<>(VALID_HEADERS); headers.put(ADOBE_IOEVENTS_PUB_KEY_1_PATH, ANOTHER_PUB_KEY_PATH); headers.put(ADOBE_IOEVENTS_PUB_KEY_2_PATH, ANOTHER_PUB_KEY_PATH); - assertFalse(underTest.verify(getValidPayload(), API_KEY, headers)); + try (MockedConstruction ignored = mockConstruction(FeignPubKeyService.class, + (mock, mockContext) -> { + when(mock.getAioEventsPublicKey(ANOTHER_PUB_KEY_PATH)).thenReturn(ANOTHER_PUBLIC_KEY); + when(mock.getAioEventsPublicKey(ANOTHER_PUB_KEY_PATH)).thenReturn(ANOTHER_PUBLIC_KEY); + } + )) { + assertFalse(new EventVerifier().verify(VALID_PAYLOAD, API_KEY, headers)); + } } @Test - public void testVerifyOnePublicKeyMismatch() { - Map headers = getValidHeaders(); + public void onePublicKeyMismatch() { + Map headers = new HashMap<>(VALID_HEADERS); headers.put(ADOBE_IOEVENTS_PUB_KEY_1_PATH, ANOTHER_PUB_KEY_PATH); - assertTrue(underTest.verify(getValidPayload(), API_KEY, headers)); + try (MockedConstruction ignored = mockConstruction(FeignPubKeyService.class, + (mock, mockContext) -> { + when(mock.getAioEventsPublicKey(ANOTHER_PUB_KEY_PATH)).thenReturn(ANOTHER_PUBLIC_KEY); + when(mock.getAioEventsPublicKey(PUB_KEY2_PATH)).thenReturn(PUBLIC_KEY2); + } + )) { + assertTrue(new EventVerifier().verify(VALID_PAYLOAD, API_KEY, headers)); + } } @Test - public void testVerifyWithApiKeyMismatch() { - assertFalse(underTest.verify(getValidPayload(), ANOTHER_API_KEY, getValidHeaders())); + public void withApiKeyMismatch() { + assertFalse(new EventVerifier().verify(VALID_PAYLOAD, ANOTHER_API_KEY, VALID_HEADERS)); } @Test - public void testVerifyInvalidEventPayloads() { - Map headers = getValidHeaders(); + public void invalidEventPayloads() { + Map headers = VALID_HEADERS; + EventVerifier underTest = new EventVerifier(); assertFalse(underTest.verify("aSimpleString", API_KEY, headers)); assertFalse(underTest.verify("{\"key\":\"value\"}", API_KEY, headers)); assertFalse(underTest.verify("{\"key\":\"value\"", API_KEY, headers)); @@ -172,62 +216,56 @@ public void testMissingOneHeader() { headers.put(ADOBE_IOEVENTS_DIGI_SIGN_1, VALID_SIGNATURE_1); headers.put(ADOBE_IOEVENTS_DIGI_SIGN_2, VALID_SIGNATURE_2); headers.put(ADOBE_IOEVENTS_PUB_KEY_1_PATH, PUB_KEY1_PATH); - assertFalse(underTest.verify(getValidPayload(), API_KEY, headers)); + assertFalse(new EventVerifier().verify(VALID_PAYLOAD, API_KEY, headers)); } @Test public void testEmptyHeaders() { - assertFalse(underTest.verify(getValidPayload(), API_KEY, new HashMap<>())); + assertFalse(new EventVerifier().verify(VALID_PAYLOAD, API_KEY, new HashMap<>())); } - // ============================ PRIVATE HELPER METHODS ================================ - private String getValidPayload() { - return "{\"event_id\":\"eventId1\",\"event\":{\"hello\":\"world\"},\"" - + RECIPIENT_CLIENT_ID + "\":\"" + API_KEY + "\"}"; + private static PublicKey stringToKey(String publicK) throws Exception { + byte[] publicBytes = Base64.getDecoder().decode(publicK); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return keyFactory.generatePublic(keySpec); } - private Map getValidHeaders() { - Map headers = new HashMap<>(); - headers.put(ADOBE_IOEVENTS_DIGI_SIGN_1, VALID_SIGNATURE_1); - headers.put(ADOBE_IOEVENTS_DIGI_SIGN_2, VALID_SIGNATURE_2); - headers.put(ADOBE_IOEVENTS_PUB_KEY_1_PATH, PUB_KEY1_PATH); - headers.put(ADOBE_IOEVENTS_PUB_KEY_2_PATH, PUB_KEY2_PATH); - return headers; - } + private static PublicKey getPubKey1() throws Exception { - private String getPubKey1() { - return "-----BEGIN PUBLIC KEY-----\n" - + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzxbiCd7hyiKbksssNEup\n" + String publicK = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzxbiCd7hyiKbksssNEup\n" + "SBhnNRHaPFHUVbi44k82GlFrLBF2MbviEWPxxJxRfqRkysHwMaE+3w74sR9oEunF\n" + "Uz3J2vGcXHT4UWfEuKxO/C8dSt7Hex5EoK2R4eld/P7j/p55jp8ODvTW/Yd9ej8q\n" + "Dk9dia8ZbkOOuVht2NJlZW4+4p8OCp4MLnSjprvPLAIHU5pD8sIcS+LFYYA3kAz2\n" + "pAo/La7W4PFd6f3fuOQrhlBKsL99W6ALyXUOsHHBk0YrcgoxVeDYWO0N3NZLYIZd\n" + "aWMxNONoH9kH2mhguidf8MCWwIuYyqO+J+IzsshXVWGyMyn3q7fVZCra9ISEZqWE\n" - + "iwIDAQAB\n" - + "-----END PUBLIC KEY-----"; + + "iwIDAQAB"; + + publicK = publicK.replaceAll(System.lineSeparator(), ""); + return stringToKey(publicK); } - private String getPubKey2() { - return "-----BEGIN PUBLIC KEY-----\n" - + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuyszY9i34MeIfmmaFSUz\n" + private static PublicKey getPubKey2() throws Exception { + String publicK = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuyszY9i34MeIfmmaFSUz\n" + "R0Y4ORhTkwiUafGbRntE0u0wTAKhe9Mewnmrxclh5OrX9jEjWY6lBkxxLYHAa+w2\n" + "B4jDExTwiz/o1GKHYf0CGlVw0JqGQVfLlvEGFg5lQsVfOBdSdnxXBSH0FOw7ZQUb\n" + "60MD7YKSbk40PRHKzHEcxlHLiHreoqPAIDn3JZ9A7b6QjKOB4LTR6jb3rUtfxnzl\n" + "jku8atEfdo341WcHSHW2hf/Gx2mazhGg1of6wZVforXo3R1HVqIVMlOk6GMcz4HH\n" + "iLOuEOURFucux3jm4gF2DF1B627vCqaGDoduvyIjitXQS6KqSx3dzB2dGOBDPpsr\n" - + "8wIDAQAB\n" - + "-----END PUBLIC KEY-----"; + + "8wIDAQAB"; + publicK = publicK.replaceAll(System.lineSeparator(), ""); + return stringToKey(publicK); } - private String getAnotherPubKey() { - return "-----BEGIN PUBLIC KEY-----\n" - + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQypvXnGrmnfoWdcdYg1\n" + private static PublicKey getAnotherPubKey() throws Exception { + String publicK = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQypvXnGrmnfoWdcdYg1\n" + "+LfHdIVpwU5ycTclnYyWp4zpogug+AfG40j4alKfuUPbuCNQh8DFSRGTgZdHY6lI\n" + "mzHcpbcJzkV1ZrW1foFEsNnulO9a7Vv8LNo4UAbZwrha5ozEbOUIttI+B6DKLLYa\n" + "4+BjHj0WtxHHuRPibf46qrliMd2St3gdp1yUjGO2qHOHlKHm15K9uwN5SYKANMK2\n" + "mZ5+3/uF4Ms21BqRSGCUEwNKpSxXT2bFNlUw0/DbM6gJuE1CJdk5z/sbLA0S3b1z\n" + "PR1LpgOeG84lFG7c0gcIaeZX+c3dLdmNBfkOQwacFP3m0urlJkSxI8MomaeEOS2y\n" - + "hQIDAQAB\n" - + "-----END PUBLIC KEY-----"; + + "hQIDAQAB"; + publicK = publicK.replaceAll(System.lineSeparator(), ""); + return stringToKey(publicK); } } From 284c3ae703a4ef234f1877aacce6565c0dc5cb79 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Wed, 16 Aug 2023 15:32:36 -0500 Subject: [PATCH 18/35] update to JUnit Jupiter, and use test library. --- events_test/pom.xml | 23 +++--- .../event/journal/JournalServiceTester.java | 2 +- .../management/ProviderServiceTester.java | 37 +++++----- .../management/RegistrationServiceTester.java | 37 +++++----- .../event/publish/PublishServiceTester.java | 19 ++--- .../JournalServiceIntegrationTest.java | 36 +++++----- .../ProviderServiceIntegrationTest.java | 70 ++++++++++--------- .../RegistrationServiceIntegrationTest.java | 45 ++++++------ .../PublishServiceIntegrationTest.java | 27 ++++--- 9 files changed, 151 insertions(+), 145 deletions(-) diff --git a/events_test/pom.xml b/events_test/pom.xml index 216a584b..2689adc8 100644 --- a/events_test/pom.xml +++ b/events_test/pom.xml @@ -12,8 +12,8 @@ governing permissions and limitations under the License. --> - - + com.adobe.aio aio-lib-java @@ -21,13 +21,10 @@ ../pom.xml 4.0.0 - aio-lib-java-events-test Adobe I/O - Events Test Utility Library Adobe I/O - Java SDK - Events Test Utility Library - - com.adobe.aio aio-lib-java-core @@ -53,16 +50,20 @@ aio-lib-java-events-journal ${project.version} - - junit - junit + org.slf4j + slf4j-simple + + + org.junit.jupiter + junit-jupiter compile - org.slf4j - slf4j-simple + com.adobe.aio + aio-lib-java-ims + ${project.version} + tests - diff --git a/events_test/src/main/java/com/adobe/aio/event/journal/JournalServiceTester.java b/events_test/src/main/java/com/adobe/aio/event/journal/JournalServiceTester.java index f6982491..2e3a0263 100644 --- a/events_test/src/main/java/com/adobe/aio/event/journal/JournalServiceTester.java +++ b/events_test/src/main/java/com/adobe/aio/event/journal/JournalServiceTester.java @@ -15,7 +15,7 @@ import com.adobe.aio.event.journal.model.Event; import com.adobe.aio.event.journal.model.JournalEntry; -import com.adobe.aio.util.WorkspaceUtil; +import com.adobe.aio.ims.util.WorkspaceUtil; import com.adobe.aio.workspace.Workspace; import java.util.HashSet; import java.util.Iterator; diff --git a/events_test/src/main/java/com/adobe/aio/event/management/ProviderServiceTester.java b/events_test/src/main/java/com/adobe/aio/event/management/ProviderServiceTester.java index 53205bd4..fcfdbdf9 100644 --- a/events_test/src/main/java/com/adobe/aio/event/management/ProviderServiceTester.java +++ b/events_test/src/main/java/com/adobe/aio/event/management/ProviderServiceTester.java @@ -15,7 +15,7 @@ import com.adobe.aio.event.management.model.Provider; import com.adobe.aio.event.management.model.ProviderInputModel; import com.adobe.aio.event.publish.model.CloudEvent; -import com.adobe.aio.util.WorkspaceUtil; +import com.adobe.aio.ims.util.WorkspaceUtil; import com.adobe.aio.workspace.Workspace; import java.util.Collections; @@ -23,10 +23,11 @@ import java.util.Set; import java.util.function.Supplier; import org.apache.commons.lang3.StringUtils; -import org.junit.Assert; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.junit.jupiter.api.Assertions.*; + public class ProviderServiceTester { public static final String DEFAULT_DESC_SUFFIX = " description"; @@ -79,31 +80,31 @@ public Provider createProvider(ProviderInputModel providerInputModel, private Provider assertProviderResponseAndCreateEventMetadata(ProviderInputModel providerInputModel, Set eventMetadataSet, Supplier> providerSupplier) { Optional provider = providerSupplier.get(); - Assert.assertTrue(provider.isPresent()); + assertTrue(provider.isPresent()); logger.info("Created AIO Events Provider: {}", provider); String providerId = provider.get().getId(); - Assert.assertTrue(StringUtils.isNotBlank(providerId)); - Assert.assertTrue(StringUtils.isNotBlank(provider.get().getInstanceId())); - Assert.assertNotNull(provider.get().getId()); - Assert.assertNotNull(provider.get().getInstanceId()); - Assert.assertEquals(providerInputModel.getLabel(), provider.get().getLabel()); - Assert.assertEquals(providerInputModel.getDescription(), provider.get().getDescription()); - Assert.assertEquals(providerInputModel.getDocsUrl(), provider.get().getDocsUrl()); - Assert.assertEquals(WorkspaceUtil.getSystemProperty(Workspace.IMS_ORG_ID), + assertTrue(StringUtils.isNotBlank(providerId)); + assertTrue(StringUtils.isNotBlank(provider.get().getInstanceId())); + assertNotNull(provider.get().getId()); + assertNotNull(provider.get().getInstanceId()); + assertEquals(providerInputModel.getLabel(), provider.get().getLabel()); + assertEquals(providerInputModel.getDescription(), provider.get().getDescription()); + assertEquals(providerInputModel.getDocsUrl(), provider.get().getDocsUrl()); + assertEquals(WorkspaceUtil.getSystemProperty(Workspace.IMS_ORG_ID), provider.get().getPublisher()); - Assert.assertEquals(CloudEvent.SOURCE_URN_PREFIX + provider.get().getId(), + assertEquals(CloudEvent.SOURCE_URN_PREFIX + provider.get().getId(), provider.get().getSource()); - Assert.assertEquals(providerInputModel.getProviderMetadataId(), + assertEquals(providerInputModel.getProviderMetadataId(), provider.get().getProviderMetadata()); for (EventMetadata eventMetadataInput : eventMetadataSet) { Optional eventMetadata = providerService.createEventMetadata(providerId, eventMetadataInput); - Assert.assertTrue(eventMetadata.isPresent()); - Assert.assertEquals(eventMetadataInput.getEventCode(), eventMetadata.get().getEventCode()); - Assert.assertEquals(eventMetadataInput.getDescription(), + assertTrue(eventMetadata.isPresent()); + assertEquals(eventMetadataInput.getEventCode(), eventMetadata.get().getEventCode()); + assertEquals(eventMetadataInput.getDescription(), eventMetadata.get().getDescription()); - Assert.assertEquals(eventMetadataInput.getLabel(), eventMetadata.get().getLabel()); + assertEquals(eventMetadataInput.getLabel(), eventMetadata.get().getLabel()); logger.info("Added EventMetadata `{}` to AIO Events Provider `{}`", eventMetadata, providerId); } @@ -114,7 +115,7 @@ public void deleteProvider(String providerId) { providerService.deleteProvider(providerId); logger.info("Deleted AIO Events Provider: {}", providerId); Optional deleted = providerService.findProviderById(providerId); - Assert.assertFalse(deleted.isPresent()); + assertFalse(deleted.isPresent()); logger.info("No more AIO Events Provider with id: {}", providerId); } diff --git a/events_test/src/main/java/com/adobe/aio/event/management/RegistrationServiceTester.java b/events_test/src/main/java/com/adobe/aio/event/management/RegistrationServiceTester.java index 9973f6a0..6ee0c33b 100644 --- a/events_test/src/main/java/com/adobe/aio/event/management/RegistrationServiceTester.java +++ b/events_test/src/main/java/com/adobe/aio/event/management/RegistrationServiceTester.java @@ -15,16 +15,17 @@ import com.adobe.aio.event.management.model.EventsOfInterestInputModel; import com.adobe.aio.event.management.model.Registration; import com.adobe.aio.event.management.model.RegistrationCreateModel; -import com.adobe.aio.util.WorkspaceUtil; +import com.adobe.aio.ims.util.WorkspaceUtil; import com.adobe.aio.workspace.Workspace; import java.net.MalformedURLException; import java.net.URL; import java.util.Optional; import java.util.Set; -import org.junit.Assert; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.junit.jupiter.api.Assertions.*; + public class RegistrationServiceTester { public static final String TEST_DESCRIPTION = "Test description"; @@ -65,46 +66,46 @@ public Registration createRegistration( RegistrationCreateModel registrationInputModel = registrationInputModelBuilder.clientId(this.workspace.getApiKey()).build(); Optional registration = registrationService.createRegistration(registrationInputModelBuilder); - Assert.assertTrue(registration.isPresent()); + assertTrue(registration.isPresent()); Registration registratinCreated = registration.get(); logger.info("Created AIO Event Registration: {}", registration.get()); String registrationId = registratinCreated.getRegistrationId(); - Assert.assertNotNull(registrationId); - Assert.assertEquals(registrationInputModel.getDescription(), registratinCreated.getDescription()); - Assert.assertEquals(registrationInputModel.getName(), registratinCreated.getName()); - Assert.assertEquals(registrationInputModel.getDeliveryType(), registratinCreated.getDeliveryType()); + assertNotNull(registrationId); + assertEquals(registrationInputModel.getDescription(), registratinCreated.getDescription()); + assertEquals(registrationInputModel.getName(), registratinCreated.getName()); + assertEquals(registrationInputModel.getDeliveryType(), registratinCreated.getDeliveryType()); Set eventsOfInterestSet = registration.get().getEventsOfInterests(); - Assert.assertEquals(registrationInputModel.getEventsOfInterests().size(),eventsOfInterestSet.size()); + assertEquals(registrationInputModel.getEventsOfInterests().size(),eventsOfInterestSet.size()); for(EventsOfInterestInputModel eventsOfInterestInput: registrationInputModel.getEventsOfInterests()){ - Assert.assertTrue(eventsOfInterestSet.stream() + assertTrue(eventsOfInterestSet.stream() .anyMatch(eventsOfInterest -> eventsOfInterest.getEventCode() .equals(eventsOfInterestInput.getEventCode()))); } - Assert.assertEquals("verified", registratinCreated.getWebhookStatus()); - Assert.assertEquals(true, registratinCreated.isEnabled()); - Assert.assertNull(registration.get().getWebhookUrl()); + assertEquals("verified", registratinCreated.getWebhookStatus()); + assertEquals(true, registratinCreated.isEnabled()); + assertNull(registration.get().getWebhookUrl()); assertUrl(registration.get().getJournalUrl().getHref()); assertUrl(registration.get().getTraceUrl().getHref()); - Assert.assertNotNull(registration.get().getCreatedDate()); - Assert.assertNotNull(registration.get().getUpdatedDate()); - Assert.assertEquals(registration.get().getUpdatedDate(), registration.get().getCreatedDate()); + assertNotNull(registration.get().getCreatedDate()); + assertNotNull(registration.get().getUpdatedDate()); + assertEquals(registration.get().getUpdatedDate(), registration.get().getCreatedDate()); return registration.get(); } public void deleteRegistration(String registrationId) { registrationService.delete(registrationId); - Assert.assertFalse(registrationService.findById(registrationId).isPresent()); + assertFalse(registrationService.findById(registrationId).isPresent()); logger.info("Deleted AIO Event Registration: {}", registrationId); } private static void assertUrl(String stringUrl) { try { - Assert.assertNotNull(stringUrl); + assertNotNull(stringUrl); URL url = new URL(stringUrl); } catch (MalformedURLException e) { - Assert.fail("invalid url due to " + e.getMessage()); + fail("invalid url due to " + e.getMessage()); } } diff --git a/events_test/src/main/java/com/adobe/aio/event/publish/PublishServiceTester.java b/events_test/src/main/java/com/adobe/aio/event/publish/PublishServiceTester.java index 706f4757..4009ec67 100644 --- a/events_test/src/main/java/com/adobe/aio/event/publish/PublishServiceTester.java +++ b/events_test/src/main/java/com/adobe/aio/event/publish/PublishServiceTester.java @@ -14,13 +14,14 @@ import static com.adobe.aio.event.publish.model.CloudEvent.SPEC_VERSION; import com.adobe.aio.event.publish.model.CloudEvent; -import com.adobe.aio.util.WorkspaceUtil; +import com.adobe.aio.ims.util.WorkspaceUtil; import com.fasterxml.jackson.core.JsonProcessingException; import java.util.UUID; -import org.junit.Assert; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.junit.jupiter.api.Assertions.*; + public class PublishServiceTester { protected final Logger logger = LoggerFactory.getLogger(this.getClass()); @@ -42,15 +43,15 @@ public String publishCloudEvent(String providerId, String eventCode) { CloudEvent cloudEvent = publishService.publishCloudEvent( providerId, eventCode, eventId, getEventDataNode(eventId)); logger.info("Published CloudEvent: {}", cloudEvent); - Assert.assertEquals(eventId, cloudEvent.getId()); - Assert.assertEquals(CloudEvent.SOURCE_URN_PREFIX + providerId, cloudEvent.getSource()); - Assert.assertEquals(eventCode, cloudEvent.getType()); - Assert.assertEquals(eventId, cloudEvent.getData().get(DATA_EVENT_ID_NODE).asText()); - Assert.assertEquals(SPEC_VERSION, cloudEvent.getSpecVersion()); - Assert.assertEquals("application/json", cloudEvent.getDataContentType()); + assertEquals(eventId, cloudEvent.getId()); + assertEquals(CloudEvent.SOURCE_URN_PREFIX + providerId, cloudEvent.getSource()); + assertEquals(eventCode, cloudEvent.getType()); + assertEquals(eventId, cloudEvent.getData().get(DATA_EVENT_ID_NODE).asText()); + assertEquals(SPEC_VERSION, cloudEvent.getSpecVersion()); + assertEquals("application/json", cloudEvent.getDataContentType()); return eventId; } catch (JsonProcessingException e) { - Assert.fail("publishService.publishCloudEvent failed with "+e.getMessage()); + fail("publishService.publishCloudEvent failed with "+e.getMessage()); return null; } } diff --git a/events_test/src/test/java/com/adobe/aio/event/journal/JournalServiceIntegrationTest.java b/events_test/src/test/java/com/adobe/aio/event/journal/JournalServiceIntegrationTest.java index 8d1c3864..864a7031 100644 --- a/events_test/src/test/java/com/adobe/aio/event/journal/JournalServiceIntegrationTest.java +++ b/events_test/src/test/java/com/adobe/aio/event/journal/JournalServiceIntegrationTest.java @@ -11,19 +11,19 @@ */ package com.adobe.aio.event.journal; -import static com.adobe.aio.event.management.ProviderServiceIntegrationTest.TEST_EVENT_CODE; -import static com.adobe.aio.event.management.ProviderServiceIntegrationTest.TEST_EVENT_PROVIDER_LABEL; -import static com.adobe.aio.event.management.RegistrationServiceIntegrationTest.TEST_REGISTRATION_NAME; +import org.apache.commons.lang3.StringUtils; import com.adobe.aio.event.management.ProviderServiceIntegrationTest; import com.adobe.aio.event.management.ProviderServiceTester; import com.adobe.aio.event.management.RegistrationServiceTester; import com.adobe.aio.event.management.model.Registration; import com.adobe.aio.event.publish.PublishServiceTester; -import com.adobe.aio.util.WorkspaceUtil; -import org.apache.commons.lang3.StringUtils; -import org.junit.Assert; -import org.junit.Test; +import com.adobe.aio.ims.util.WorkspaceUtil; +import org.junit.jupiter.api.Test; + +import static com.adobe.aio.event.management.ProviderServiceIntegrationTest.*; +import static com.adobe.aio.event.management.RegistrationServiceIntegrationTest.*; +import static org.junit.jupiter.api.Assertions.*; public class JournalServiceIntegrationTest extends JournalServiceTester { @@ -38,19 +38,23 @@ public JournalServiceIntegrationTest() { publishServiceTester = new PublishServiceTester(); } - @Test(expected = IllegalArgumentException.class) + @Test public void missingWorkspace() { - JournalService journalService = JournalService.builder() + assertThrows(IllegalArgumentException.class, () -> + JournalService.builder() .workspace(null) .url("https://adobe.com") - .build(); + .build() + ); } - @Test(expected = IllegalArgumentException.class) + @Test public void missingUrl() { - JournalService journalService = JournalService.builder() + assertThrows(IllegalArgumentException.class, () -> + JournalService.builder() .workspace(WorkspaceUtil.getSystemWorkspaceBuilder().build()) - .build(); + .build() + ); } @Test @@ -73,10 +77,8 @@ public void testJournalPolling() boolean wasRawEventPolled = pollJournalForEvent(registration.getJournalUrl().getHref(), rawEventId, isEventIdInTheCloudEventData); - Assert.assertTrue("The published CloudEvent was not retrieved in the Journal", - wasCloudEventPolled); - Assert.assertTrue("The published Raw Event was not retrieved in the Journal", - wasRawEventPolled); + assertTrue(wasCloudEventPolled, "The published CloudEvent was not retrieved in the Journal"); + assertTrue(wasRawEventPolled, "The published Raw Event was not retrieved in the Journal"); } finally { if (!StringUtils.isEmpty(registrationId)) { registrationServiceTester.deleteRegistration(registrationId); diff --git a/events_test/src/test/java/com/adobe/aio/event/management/ProviderServiceIntegrationTest.java b/events_test/src/test/java/com/adobe/aio/event/management/ProviderServiceIntegrationTest.java index 00ac1c94..50a43e51 100644 --- a/events_test/src/test/java/com/adobe/aio/event/management/ProviderServiceIntegrationTest.java +++ b/events_test/src/test/java/com/adobe/aio/event/management/ProviderServiceIntegrationTest.java @@ -11,16 +11,18 @@ */ package com.adobe.aio.event.management; -import static com.adobe.aio.event.management.model.ProviderInputModel.DELIVERY_FORMAT_ADOBE_IO; +import java.util.List; +import java.util.Optional; import com.adobe.aio.event.management.feign.ConflictException; import com.adobe.aio.event.management.model.EventMetadata; import com.adobe.aio.event.management.model.Provider; -import com.adobe.aio.util.WorkspaceUtil; -import java.util.List; -import java.util.Optional; -import org.junit.Assert; -import org.junit.Test; +import com.adobe.aio.ims.util.WorkspaceUtil; +import org.junit.jupiter.api.Test; + +import static com.adobe.aio.event.management.model.ProviderInputModel.*; + +import static org.junit.jupiter.api.Assertions.*; public class ProviderServiceIntegrationTest extends ProviderServiceTester { @@ -31,9 +33,9 @@ public ProviderServiceIntegrationTest() { super(); } - @Test(expected = IllegalArgumentException.class) + @Test public void missingWorkspace() { - ProviderService.builder().build(); + assertThrows(IllegalArgumentException.class, () -> ProviderService.builder().build()); } @Test @@ -43,20 +45,20 @@ public void getProvidersWithInvalidConsumerOrgId() { .consumerOrgId("invalid").build()) .url(WorkspaceUtil.getSystemProperty(WorkspaceUtil.API_URL)) .build(); - Assert.assertTrue(providerService.getProviders().isEmpty()); + assertTrue(providerService.getProviders().isEmpty()); } - @Test(expected = IllegalArgumentException.class) + @Test public void invalidFindByArg() { - providerService.findProviderBy("", ""); + assertThrows(IllegalArgumentException.class, () -> providerService.findProviderBy("", "")); } @Test public void getNotFound() { String idNotToBeFound = "this_id_should_not_exist"; - Assert.assertFalse(providerService.findProviderById(idNotToBeFound).isPresent()); - Assert.assertTrue(providerService.getEventMetadata(idNotToBeFound).isEmpty()); - Assert.assertFalse( + assertFalse(providerService.findProviderById(idNotToBeFound).isPresent()); + assertTrue(providerService.getEventMetadata(idNotToBeFound).isEmpty()); + assertFalse( providerService.findCustomEventsProviderByInstanceId(idNotToBeFound).isPresent()); } @@ -67,41 +69,41 @@ public void createGetUpdateDelete() { try { String instanceId = provider.getInstanceId(); - Assert.assertEquals(1, providerService.getEventMetadata(providerId).size()); + assertEquals(1, providerService.getEventMetadata(providerId).size()); logger.info("Fetched All EventMetadata `{}` of AIO Events Provider `{}`", providerId); String updatedEventMetadataDescription = "updated EventMetadata Description"; Optional eventMetadata = providerService.updateEventMetadata(providerId, getTestEventMetadataBuilder(TEST_EVENT_CODE).description(updatedEventMetadataDescription) .build()); - Assert.assertTrue(eventMetadata.isPresent()); + assertTrue(eventMetadata.isPresent()); logger.info("Updated EventMetadata `{}` of AIO Events Provider `{}`", eventMetadata, providerId); - Assert.assertEquals(updatedEventMetadataDescription, eventMetadata.get().getDescription()); + assertEquals(updatedEventMetadataDescription, eventMetadata.get().getDescription()); Optional eventMetadataFromGet = providerService.getEventMetadata(providerId, TEST_EVENT_CODE); - Assert.assertTrue(eventMetadataFromGet.isPresent()); - Assert.assertEquals(eventMetadata, eventMetadataFromGet); + assertTrue(eventMetadataFromGet.isPresent()); + assertEquals(eventMetadata, eventMetadataFromGet); logger.info("Fetched EventMetadata `{}` of AIO Events Provider `{}`", eventMetadataFromGet, providerId); Optional providerById = providerService.findProviderById(providerId); - Assert.assertTrue(providerById.isPresent()); + assertTrue(providerById.isPresent()); List eventMetadataList = providerService.getEventMetadata(providerId); - Assert.assertTrue(eventMetadataList.size() == 1); - Assert.assertEquals(eventMetadata.get(), eventMetadataList.get(0)); + assertTrue(eventMetadataList.size() == 1); + assertEquals(eventMetadata.get(), eventMetadataList.get(0)); logger.info("Found AIO Events Provider `{}` by Id", providerById); Optional providerByInstanceId = providerService.findCustomEventsProviderByInstanceId( instanceId); - Assert.assertTrue(providerByInstanceId.isPresent()); - Assert.assertEquals(providerId, providerByInstanceId.get().getId()); + assertTrue(providerByInstanceId.isPresent()); + assertEquals(providerId, providerByInstanceId.get().getId()); logger.info("Found AIO Events Provider `{}` by InstanceId", providerById); providerService.deleteEventMetadata(providerId, TEST_EVENT_CODE); - Assert.assertFalse(providerService.getEventMetadata(providerId, TEST_EVENT_CODE).isPresent()); - Assert.assertTrue(providerService.getEventMetadata(providerId).isEmpty()); + assertFalse(providerService.getEventMetadata(providerId, TEST_EVENT_CODE).isPresent()); + assertTrue(providerService.getEventMetadata(providerId).isEmpty()); logger.info("Deleted EventMetadata {} from AIO Events Provider `{}`", TEST_EVENT_CODE, providerById); @@ -109,7 +111,7 @@ public void createGetUpdateDelete() { providerService.createProvider( getTestProviderInputModelBuilder(TEST_EVENT_PROVIDER_LABEL).instanceId(instanceId) .build()); - Assert.fail("We should have had a ConflictException thrown"); + fail("We should have had a ConflictException thrown"); } catch (ConflictException ex) { logger.info("Cannot create an AIO Events provider with the same instanceId: {}", ex.getMessage()); @@ -122,23 +124,23 @@ public void createGetUpdateDelete() { .description(updatedProviderDescription) .eventDeliveryFormat(DELIVERY_FORMAT_ADOBE_IO) .build()); - Assert.assertTrue(updatedProvider.isPresent()); + assertTrue(updatedProvider.isPresent()); logger.info("Updated AIO Events Provider: {}", provider); - Assert.assertEquals(providerId, updatedProvider.get().getId()); - Assert.assertEquals(updatedProviderDescription, updatedProvider.get().getDescription()); - Assert.assertEquals(DELIVERY_FORMAT_ADOBE_IO, updatedProvider.get().getEventDeliveryFormat()); + assertEquals(providerId, updatedProvider.get().getId()); + assertEquals(updatedProviderDescription, updatedProvider.get().getDescription()); + assertEquals(DELIVERY_FORMAT_ADOBE_IO, updatedProvider.get().getEventDeliveryFormat()); providerService.createEventMetadata(providerId, getTestEventMetadataBuilder(TEST_EVENT_CODE).build()); - Assert.assertTrue(eventMetadata.isPresent()); + assertTrue(eventMetadata.isPresent()); logger.info("Added EventMetadata `{}` to AIO Events Provider `{}`", eventMetadata, providerId); providerService.deleteAllEventMetadata(providerId); - Assert.assertTrue(providerService.getEventMetadata(providerId).isEmpty()); + assertTrue(providerService.getEventMetadata(providerId).isEmpty()); logger.info("Deleted All EventMetadata from AIO Events Provider `{}`", providerId); } catch (Exception e) { logger.error(e.getMessage(), e); - Assert.fail(e.getMessage()); + fail(e.getMessage()); } finally { deleteProvider(providerId); } diff --git a/events_test/src/test/java/com/adobe/aio/event/management/RegistrationServiceIntegrationTest.java b/events_test/src/test/java/com/adobe/aio/event/management/RegistrationServiceIntegrationTest.java index 8d00b972..e40c4493 100644 --- a/events_test/src/test/java/com/adobe/aio/event/management/RegistrationServiceIntegrationTest.java +++ b/events_test/src/test/java/com/adobe/aio/event/management/RegistrationServiceIntegrationTest.java @@ -11,15 +11,16 @@ */ package com.adobe.aio.event.management; +import java.util.Optional; -import static com.adobe.aio.event.management.ProviderServiceIntegrationTest.TEST_EVENT_CODE; -import static com.adobe.aio.event.management.ProviderServiceIntegrationTest.TEST_EVENT_PROVIDER_LABEL; +import org.apache.commons.lang3.StringUtils; import com.adobe.aio.event.management.model.Registration; -import java.util.Optional; -import org.apache.commons.lang3.StringUtils; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; + +import static com.adobe.aio.event.management.ProviderServiceIntegrationTest.*; + +import static org.junit.jupiter.api.Assertions.*; public class RegistrationServiceIntegrationTest extends RegistrationServiceTester { @@ -32,15 +33,15 @@ public RegistrationServiceIntegrationTest() { providerServiceTester = new ProviderServiceTester(); } - @Test(expected = IllegalArgumentException.class) + @Test public void missingWorkspace() { - RegistrationService.builder().build(); + assertThrows(IllegalArgumentException.class, () -> RegistrationService.builder().build()); } @Test public void getNotFound() { String idNotToBeFound = "this_id_should_not_exist"; - Assert.assertFalse(registrationService.findById(idNotToBeFound).isPresent()); + assertFalse(registrationService.findById(idNotToBeFound).isPresent()); } @Test @@ -54,23 +55,23 @@ public void createGetDeleteJournalRegistration() { TEST_EVENT_CODE); registrationId = registration.getRegistrationId(); Optional found = registrationService.findById(registrationId); - Assert.assertTrue(found.isPresent()); + assertTrue(found.isPresent()); logger.info("Found AIO Event Registration: {}", found.get()); - Assert.assertEquals(registrationId, found.get().getRegistrationId()); - Assert.assertEquals(registration.getClientId(), found.get().getClientId()); - Assert.assertEquals(registration.getDescription(), found.get().getDescription()); - Assert.assertEquals(registration.getName(), found.get().getName()); - Assert.assertEquals(registration.getDeliveryType(), found.get().getDeliveryType()); - Assert.assertEquals(registration.getEventsOfInterests(), + assertEquals(registrationId, found.get().getRegistrationId()); + assertEquals(registration.getClientId(), found.get().getClientId()); + assertEquals(registration.getDescription(), found.get().getDescription()); + assertEquals(registration.getName(), found.get().getName()); + assertEquals(registration.getDeliveryType(), found.get().getDeliveryType()); + assertEquals(registration.getEventsOfInterests(), found.get().getEventsOfInterests()); - Assert.assertEquals(registration.getWebhookStatus(), found.get().getWebhookStatus()); - Assert.assertEquals(registration.isEnabled(), found.get().isEnabled()); - Assert.assertEquals(registration.getWebhookUrl(), found.get().getWebhookUrl()); - Assert.assertEquals(registration.getJournalUrl().getHref(), found.get().getJournalUrl().getHref()); - Assert.assertEquals(registration.getTraceUrl().getHref(), found.get().getTraceUrl().getHref()); + assertEquals(registration.getWebhookStatus(), found.get().getWebhookStatus()); + assertEquals(registration.isEnabled(), found.get().isEnabled()); + assertEquals(registration.getWebhookUrl(), found.get().getWebhookUrl()); + assertEquals(registration.getJournalUrl().getHref(), found.get().getJournalUrl().getHref()); + assertEquals(registration.getTraceUrl().getHref(), found.get().getTraceUrl().getHref()); } catch (Exception e) { logger.error(e.getMessage(), e); - Assert.fail(e.getMessage()); + fail(e.getMessage()); } finally { if (!StringUtils.isEmpty(registrationId)) { this.deleteRegistration(registrationId); diff --git a/events_test/src/test/java/com/adobe/aio/event/publish/PublishServiceIntegrationTest.java b/events_test/src/test/java/com/adobe/aio/event/publish/PublishServiceIntegrationTest.java index 32da919a..a999f8f6 100644 --- a/events_test/src/test/java/com/adobe/aio/event/publish/PublishServiceIntegrationTest.java +++ b/events_test/src/test/java/com/adobe/aio/event/publish/PublishServiceIntegrationTest.java @@ -11,16 +11,16 @@ */ package com.adobe.aio.event.publish; - -import static com.adobe.aio.event.management.ProviderServiceIntegrationTest.TEST_EVENT_CODE; -import static com.adobe.aio.event.management.ProviderServiceIntegrationTest.TEST_EVENT_PROVIDER_LABEL; -import static com.adobe.aio.event.management.RegistrationServiceIntegrationTest.TEST_REGISTRATION_NAME; +import org.apache.commons.lang3.StringUtils; import com.adobe.aio.event.management.ProviderServiceTester; import com.adobe.aio.event.management.RegistrationServiceTester; -import org.apache.commons.lang3.StringUtils; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; + +import static com.adobe.aio.event.management.ProviderServiceIntegrationTest.*; +import static com.adobe.aio.event.management.RegistrationServiceIntegrationTest.*; + +import static org.junit.jupiter.api.Assertions.*; public class PublishServiceIntegrationTest extends PublishServiceTester { @@ -33,11 +33,9 @@ public PublishServiceIntegrationTest() { registrationServiceTester = new RegistrationServiceTester(); } - @Test(expected = IllegalArgumentException.class) + @Test public void missingWorkspace() { - PublishService publishService = PublishService.builder() - .workspace(null) - .build(); + assertThrows(IllegalArgumentException.class, () -> PublishService.builder().workspace(null).build()); } @Test @@ -50,11 +48,11 @@ public void publishEventTest() { registrationId = registrationServiceTester.createJournalRegistration( TEST_REGISTRATION_NAME, providerId, TEST_EVENT_CODE).getRegistrationId(); - Assert.assertNotNull(publishCloudEvent(providerId, TEST_EVENT_CODE)); - Assert.assertNotNull(publishRawEvent(providerId, TEST_EVENT_CODE)); + assertNotNull(publishCloudEvent(providerId, TEST_EVENT_CODE)); + assertNotNull(publishRawEvent(providerId, TEST_EVENT_CODE)); } catch (Exception e) { logger.error(e.getMessage(), e); - Assert.fail(e.getMessage()); + fail(e.getMessage()); } finally { if (!StringUtils.isEmpty(registrationId)) { registrationServiceTester.deleteRegistration(registrationId); @@ -64,5 +62,4 @@ public void publishEventTest() { } } } - } From 2f374b81094c7ac7b89c9dfc1dd57d7b5dec6ca8 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Wed, 16 Aug 2023 15:32:53 -0500 Subject: [PATCH 19/35] Fix Pom order. --- events_journal/pom.xml | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/events_journal/pom.xml b/events_journal/pom.xml index e5f4dac4..c0f95065 100644 --- a/events_journal/pom.xml +++ b/events_journal/pom.xml @@ -33,6 +33,12 @@ aio-lib-java-core ${project.version} + + com.adobe.aio + aio-lib-java-ims + ${project.version} + + @@ -47,6 +53,13 @@ ${project.version} test + + com.adobe.aio + aio-lib-java-ims + ${project.version} + tests + test + org.junit.jupiter junit-jupiter @@ -55,19 +68,7 @@ org.slf4j slf4j-simple - - com.adobe.aio - aio-lib-java-ims - ${project.version} - - - com.adobe.aio - aio-lib-java-ims - ${project.version} - tests - test - org.mockito mockito-core From 310a982c1fac25ec511411f9c6876dd0e2b35e37 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Wed, 16 Aug 2023 15:36:18 -0500 Subject: [PATCH 20/35] Upgrade to JUnit Jupiter --- events_xdm/pom.xml | 24 ++++--------------- .../adobe/xdm/event/SerializationTest.java | 10 ++++---- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/events_xdm/pom.xml b/events_xdm/pom.xml index 59315be8..f7096c2c 100644 --- a/events_xdm/pom.xml +++ b/events_xdm/pom.xml @@ -34,33 +34,19 @@ - com.fasterxml.jackson.core - jackson-databind - test - - - junit - junit - test + org.junit.jupiter + junit-jupiter org.slf4j slf4j-simple - test - - - org.hamcrest - hamcrest-all - 1.3 - test + - com.jayway.jsonpath - json-path-assert - 0.9.1 + com.fasterxml.jackson.core + jackson-databind test - diff --git a/events_xdm/src/test/java/com/adobe/xdm/event/SerializationTest.java b/events_xdm/src/test/java/com/adobe/xdm/event/SerializationTest.java index 79a81938..f6896251 100644 --- a/events_xdm/src/test/java/com/adobe/xdm/event/SerializationTest.java +++ b/events_xdm/src/test/java/com/adobe/xdm/event/SerializationTest.java @@ -11,7 +11,6 @@ */ package com.adobe.xdm.event; -import static org.junit.Assert.assertTrue; import com.adobe.xdm.Xdm; import com.adobe.xdm.assets.Asset; @@ -28,11 +27,14 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.Hashtable; -import org.junit.Before; -import org.junit.Test; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.junit.jupiter.api.Assertions.*; + public class SerializationTest { Logger logger = LoggerFactory.getLogger(SerializationTest.class); @@ -40,7 +42,7 @@ public class SerializationTest { //the above is new SimpleDateFormat(XdmContext.DATE_FORMAT,Locale.US).format(new Date(0)); private ObjectMapper mapper; - @Before + @BeforeEach public void setUp() { mapper = new ObjectMapper(); } From 9508b84a10295ce88dc211af27b8d20da249ad1e Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Wed, 16 Aug 2023 15:46:19 -0500 Subject: [PATCH 21/35] Need Java8 support. Remove JUnit from modules without tests. --- aem/core_aem/pom.xml | 9 --------- aem/events_ingress_aem/pom.xml | 10 ---------- aem/events_mgmt_aem/pom.xml | 10 ---------- aem/events_osgi_mapping/pom.xml | 11 ----------- pom.xml | 4 ++-- 5 files changed, 2 insertions(+), 42 deletions(-) diff --git a/aem/core_aem/pom.xml b/aem/core_aem/pom.xml index 8b408904..643817bf 100644 --- a/aem/core_aem/pom.xml +++ b/aem/core_aem/pom.xml @@ -52,15 +52,6 @@ jackson-databind - - - junit - junit - - - org.slf4j - slf4j-simple - diff --git a/aem/events_ingress_aem/pom.xml b/aem/events_ingress_aem/pom.xml index 33e3113b..c77f51ab 100644 --- a/aem/events_ingress_aem/pom.xml +++ b/aem/events_ingress_aem/pom.xml @@ -51,16 +51,6 @@ provided ${project.version} - - - - junit - junit - - - slf4j-simple - org.slf4j - diff --git a/aem/events_mgmt_aem/pom.xml b/aem/events_mgmt_aem/pom.xml index 58c5730d..af53e322 100644 --- a/aem/events_mgmt_aem/pom.xml +++ b/aem/events_mgmt_aem/pom.xml @@ -39,16 +39,6 @@ provided ${project.version} - - - - junit - junit - - - slf4j-simple - org.slf4j - diff --git a/aem/events_osgi_mapping/pom.xml b/aem/events_osgi_mapping/pom.xml index ab33cce8..a7dff45c 100644 --- a/aem/events_osgi_mapping/pom.xml +++ b/aem/events_osgi_mapping/pom.xml @@ -58,17 +58,6 @@ provided ${project.version} - - - - - junit - junit - - - slf4j-simple - org.slf4j - diff --git a/pom.xml b/pom.xml index 0318787f..31d2e4bb 100644 --- a/pom.xml +++ b/pom.xml @@ -398,8 +398,8 @@ maven-compiler-plugin 3.10.1 - 11 - 11 + 1.8 + 1.8 From 81a0780678bde1f9224b1957bb8c58de8dfa1dc9 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Wed, 16 Aug 2023 16:56:52 -0500 Subject: [PATCH 22/35] Bring versions up-to-date --- core/pom.xml | 13 +++++++++ .../java/com/adobe/aio/util/JacksonUtil.java | 17 +++++------- pom.xml | 27 +++++++------------ 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 17f42166..40b559e5 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -38,6 +38,19 @@ jackson-databind + + com.fasterxml.jackson.datatype + jackson-datatype-jdk8 + ${jackson.version} + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson.version} + + + io.github.openfeign feign-core diff --git a/core/src/main/java/com/adobe/aio/util/JacksonUtil.java b/core/src/main/java/com/adobe/aio/util/JacksonUtil.java index ab44b7ef..7832efe0 100644 --- a/core/src/main/java/com/adobe/aio/util/JacksonUtil.java +++ b/core/src/main/java/com/adobe/aio/util/JacksonUtil.java @@ -20,8 +20,7 @@ import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.node.TextNode; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; -import io.cloudevents.json.ZonedDateTimeDeserializer; -import io.cloudevents.json.ZonedDateTimeSerializer; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import io.openapitools.jackson.dataformat.hal.JacksonHALModule; import java.time.ZonedDateTime; import org.apache.commons.lang3.StringUtils; @@ -33,14 +32,12 @@ private JacksonUtil() { public static final ObjectMapper DEFAULT_OBJECT_MAPPER = JsonMapper.builder() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .serializationInclusion(Include.NON_NULL) - .addModule(new SimpleModule() - .addSerializer(ZonedDateTime.class, new ZonedDateTimeSerializer()) - .addDeserializer(ZonedDateTime.class, new ZonedDateTimeDeserializer())) - .addModule(new JacksonHALModule()) - .addModule(new Jdk8Module()) - .build(); + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .serializationInclusion(Include.NON_NULL) + .addModule(new JacksonHALModule()) + .addModule(new Jdk8Module()) + .addModule(new JavaTimeModule()) + .build(); public static JsonNode getJsonNode(String jsonPayload) throws JsonProcessingException { if (StringUtils.isEmpty(jsonPayload)) { diff --git a/pom.xml b/pom.xml index 31d2e4bb..bc2d1548 100644 --- a/pom.xml +++ b/pom.xml @@ -96,21 +96,19 @@ 0.8.3 - 1.6 - 3.6 - 5.9.2 - 2.32.0 + 1.10.0 + 3.13.0 + 5.10.0 5.4.0 - 1.7.21 - [1.7.21,1.7.25] - 2.12.3 + 2.0.7 + 2.15.2 0.11.5 - 1.2.0 + 2.5.0 1.0.9 - 11.2 + 12.4 3.8.0 @@ -152,7 +150,7 @@ org.slf4j slf4j-api - ${slf4j-api.version} + ${slf4j.version} provided @@ -175,7 +173,7 @@ jakarta.xml.bind jakarta.xml.bind-api - 3.0.1 + 4.0.0 @@ -218,13 +216,6 @@ test - - com.github.tomakehurst - wiremock-jre8 - ${wiremock.version} - test - - org.slf4j slf4j-simple From af8d525fb1441ca19f4d0e56d783bb15303f2444 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Wed, 16 Aug 2023 17:02:18 -0500 Subject: [PATCH 23/35] Update all plugins. --- pom.xml | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index bc2d1548..995d82de 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ ${maven.multiModuleProjectDirectory}/copyright_header.txt - 0.8.3 + 0.8.10 1.10.0 3.13.0 @@ -303,7 +303,7 @@ com.mycila license-maven-plugin - 4.1 + 4.2
${license.header.path}
@@ -350,26 +350,26 @@ org.apache.maven.plugins maven-source-plugin - 3.2.1 + 3.3.0 true org.apache.maven.plugins maven-jar-plugin - 3.2.2 + 3.3.0 org.apache.maven.plugins maven-clean-plugin - 3.1.0 + 3.3.1 org.apache.maven.plugins maven-release-plugin - 3.0.0-M5 + 3.0.1 false release @@ -381,13 +381,13 @@ org.apache.maven.plugins maven-resources-plugin - 3.2.0 + 3.3.1 org.apache.maven.plugins maven-compiler-plugin - 3.10.1 + 3.11.0 1.8 1.8 @@ -397,13 +397,13 @@ org.apache.maven.plugins maven-install-plugin - 2.5.2 + 3.1.1 org.apache.maven.plugins maven-surefire-plugin - 2.22.2 + 3.1.2 false @@ -415,37 +415,37 @@ org.apache.maven.plugins maven-failsafe-plugin - 2.22.2 + 3.1.2 org.apache.maven.plugins maven-deploy-plugin - 2.8.2 + 3.1.1 org.apache.maven.plugins maven-enforcer-plugin - 3.0.0 + 3.3.0 org.apache.maven.plugins maven-dependency-plugin - 3.2.0 + 3.6.0 org.codehaus.mojo build-helper-maven-plugin - 3.3.0 + 3.4.0 org.apache.maven.plugins maven-javadoc-plugin - 3.3.2 + 3.5.0 1.8 true @@ -471,7 +471,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.0.1 + 3.1.0 @@ -498,7 +498,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.12 + 1.6.13 From c30f72a66fa9ca033cfee28be1f11e5cb58c1669 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Thu, 17 Aug 2023 10:42:25 -0500 Subject: [PATCH 24/35] Expect exception at correct spot. --- .../adobe/aio/ims/feign/FeignImsServiceIntegrationTest.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceIntegrationTest.java b/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceIntegrationTest.java index 49888322..4951749a 100644 --- a/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceIntegrationTest.java +++ b/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceIntegrationTest.java @@ -63,8 +63,6 @@ public void getAndValidateJwtExchangeAccessTokenWithBadTechAccount() { @Test public void getAndValidateJwtExchangeAccessTokenWithMissingPrivateKey() { Workspace workspace = WorkspaceUtil.getSystemWorkspaceBuilder().privateKey(null).build(); - ImsService imsService = ImsService.builder().workspace(workspace).build(); - assertThrows(IllegalStateException.class, imsService::getJwtExchangeAccessToken); - + assertThrows(IllegalStateException.class, () -> ImsService.builder().workspace(workspace).build()); } } From 8dc5a5e62b345e5694aa097d28642d53ed5d7efc Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Thu, 17 Aug 2023 11:21:23 -0500 Subject: [PATCH 25/35] Add tests for fetching public keys. --- .../webhook/feign/FeignPubKeyService.java | 14 +++-- .../webhook/feign/FeignPubKeyServiceTest.java | 57 +++++++++++++++++++ .../src/test/resources/mockserver.properties | 1 + 3 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 events_webhook/src/test/java/com/adobe/aio/event/webhook/feign/FeignPubKeyServiceTest.java create mode 100644 events_webhook/src/test/resources/mockserver.properties diff --git a/events_webhook/src/main/java/com/adobe/aio/event/webhook/feign/FeignPubKeyService.java b/events_webhook/src/main/java/com/adobe/aio/event/webhook/feign/FeignPubKeyService.java index 2fc1ca5a..2295740a 100644 --- a/events_webhook/src/main/java/com/adobe/aio/event/webhook/feign/FeignPubKeyService.java +++ b/events_webhook/src/main/java/com/adobe/aio/event/webhook/feign/FeignPubKeyService.java @@ -15,6 +15,7 @@ import com.adobe.aio.event.webhook.service.PubKeyService; import com.adobe.aio.exception.AIOException; import com.adobe.aio.util.feign.FeignUtil; + import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.PublicKey; @@ -34,14 +35,19 @@ public FeignPubKeyService(final String pubKeyCdnBaseUrl) { * it is recommended to cache this public key fetched from the adobe hosted CDN. * after downloading the public key set it in the cache with cache expiry of not more than 24h. * refer our public doc for the same - https://developer.adobe.com/events/docs/guides/#security-considerations - * @param pubKeyPath cdn path for fetching the public key + * + * @param pubKeyPath cdn path for fetching the public key * @return {@link PublicKey} */ @Override public PublicKey getAioEventsPublicKey(String pubKeyPath) { try { - String publicKeyPEM = publicKeyCdnApi.getPubKeyFromCDN(pubKeyPath) - .replace("-----BEGIN PUBLIC KEY-----", "") + String publicKeyPEM = publicKeyCdnApi.getPubKeyFromCDN(pubKeyPath); + if (publicKeyPEM == null) { + throw new IllegalStateException(String.format("No key found at specified path %s", pubKeyPath)); + } + + publicKeyPEM = publicKeyPEM.replace("-----BEGIN PUBLIC KEY-----", "") .replaceAll(System.lineSeparator(), "") .replace("-----END PUBLIC KEY-----", ""); byte[] keyBytes = Base64.getDecoder().decode(publicKeyPEM); @@ -50,7 +56,7 @@ public PublicKey getAioEventsPublicKey(String pubKeyPath) { return keyFactory.generatePublic(keySpec); } catch (GeneralSecurityException e) { throw new AIOException("Error fetching public key from CDN path " + pubKeyPath - +". Reason -> " + e.getMessage(), e); + + ". Reason -> " + e.getMessage(), e); } } diff --git a/events_webhook/src/test/java/com/adobe/aio/event/webhook/feign/FeignPubKeyServiceTest.java b/events_webhook/src/test/java/com/adobe/aio/event/webhook/feign/FeignPubKeyServiceTest.java new file mode 100644 index 00000000..8152da24 --- /dev/null +++ b/events_webhook/src/test/java/com/adobe/aio/event/webhook/feign/FeignPubKeyServiceTest.java @@ -0,0 +1,57 @@ +package com.adobe.aio.event.webhook.feign; + +import java.io.File; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PublicKey; + +import javax.crypto.KeyGenerator; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockserver.client.MockServerClient; +import org.mockserver.junit.jupiter.MockServerExtension; +import shaded_package.org.apache.commons.codec.binary.Base64; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockserver.model.HttpRequest.*; +import static org.mockserver.model.HttpResponse.*; + +@ExtendWith(MockServerExtension.class) +public class FeignPubKeyServiceTest { + + @Test + public void response200(MockServerClient client) throws Exception { + final KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + kpg.initialize(2048); + KeyPair kp = kpg.generateKeyPair(); + final PublicKey publicKey = kp.getPublic(); + final String expected = Base64.encodeBase64String(publicKey.getEncoded()); + + final String url = "http://localhost:" + client.getPort(); + final String keyPath = "/junit/pubkey.pem"; + + client + .when(request().withMethod("GET").withPath(keyPath)) + .respond(response().withStatusCode(200).withBody(expected)); + + PublicKey actual = new FeignPubKeyService(url).getAioEventsPublicKey(keyPath); + assertNotNull(actual); + assertEquals(expected, Base64.encodeBase64String(actual.getEncoded())); + } + + @Test + public void response404(MockServerClient client) { + final String url = "http://localhost:" + client.getPort(); + final FeignPubKeyService service = new FeignPubKeyService(url); + + final String keyPath = "/junit/pubkey-not-found.pem"; + + client + .when(request().withMethod("GET").withPath(keyPath)) + .respond(response().withStatusCode(404)); + + assertThrows(IllegalStateException.class, () -> service.getAioEventsPublicKey(keyPath)); + } +} diff --git a/events_webhook/src/test/resources/mockserver.properties b/events_webhook/src/test/resources/mockserver.properties new file mode 100644 index 00000000..201596c6 --- /dev/null +++ b/events_webhook/src/test/resources/mockserver.properties @@ -0,0 +1 @@ +mockserver.logLevel=WARN From 2efef376e50f2dfb969f1cc48974b2805411e3cb Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Thu, 17 Aug 2023 11:21:43 -0500 Subject: [PATCH 26/35] Remove Java11 feature. --- core/src/main/java/com/adobe/aio/workspace/Workspace.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/adobe/aio/workspace/Workspace.java b/core/src/main/java/com/adobe/aio/workspace/Workspace.java index 61889c1c..e00682db 100644 --- a/core/src/main/java/com/adobe/aio/workspace/Workspace.java +++ b/core/src/main/java/com/adobe/aio/workspace/Workspace.java @@ -36,22 +36,22 @@ public class Workspace { /** * @deprecated This will be removed in v2.0 of the library. */ - @Deprecated(since = "1.1", forRemoval = true) + @Deprecated public static final String CREDENTIAL_ID = "aio_credential_id"; /** * @deprecated This will be removed in v2.0 of the library. */ - @Deprecated(since = "1.1", forRemoval = true) + @Deprecated public static final String CLIENT_SECRET = "aio_client_secret"; /** * @deprecated This will be removed in v2.0 of the library. */ - @Deprecated(since = "1.1", forRemoval = true) + @Deprecated public static final String TECHNICAL_ACCOUNT_ID = "aio_technical_account_id"; /** * @deprecated This will be removed in v2.0 of the library. */ - @Deprecated(since = "1.1", forRemoval = true) + @Deprecated public static final String META_SCOPES = "aio_meta_scopes"; // workspace context related: From e0c0f9bf22c35ec4b815665535f6a20fd8991025 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Thu, 17 Aug 2023 11:21:59 -0500 Subject: [PATCH 27/35] not using JMockit --- .github/dependabot.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ded4a21b..16d34b66 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,8 +5,6 @@ updates: schedule: interval: "weekly" ignore: - - dependency-name: "jacoco-maven-plugin" - versions: ["0.8.x"] - dependency-name: "org.osgi.service.component.annotations" versions: ["1.x"] - dependency-name: "org.osgi.service.component" From 6f87de91434ec143fbb492f42d74ae19e8716030 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Thu, 17 Aug 2023 11:22:13 -0500 Subject: [PATCH 28/35] Remove unneded dependency --- events_journal/pom.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/events_journal/pom.xml b/events_journal/pom.xml index c0f95065..00d7628f 100644 --- a/events_journal/pom.xml +++ b/events_journal/pom.xml @@ -77,10 +77,6 @@ org.mockito mockito-junit-jupiter
- - org.mock-server - mockserver-junit-jupiter-no-dependencies - From bace0ff944984e4bc6226983503161551e241b78 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Thu, 17 Aug 2023 11:23:10 -0500 Subject: [PATCH 29/35] Bump version to next minor release, API changed. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 995d82de..93ede0b8 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ com.adobe.aio aio-lib-java - 1.0.19-SNAPSHOT + 1.1.0-SNAPSHOT pom From 7db2de134aa0eb062d1aa477c064a7f08a2fa385 Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Mon, 21 Aug 2023 10:10:03 -0500 Subject: [PATCH 30/35] Bump all module versions --- aem/aio_aem_events/pom.xml | 2 +- aem/core_aem/pom.xml | 2 +- aem/events_ingress_aem/pom.xml | 2 +- aem/events_mgmt_aem/pom.xml | 2 +- aem/events_osgi_mapping/pom.xml | 2 +- aem/lib_osgi/pom.xml | 2 +- aem/pom.xml | 2 +- core/pom.xml | 2 +- events_ingress/pom.xml | 2 +- events_journal/pom.xml | 2 +- events_mgmt/pom.xml | 2 +- events_test/pom.xml | 2 +- events_webhook/pom.xml | 2 +- events_xdm/pom.xml | 2 +- ims/pom.xml | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/aem/aio_aem_events/pom.xml b/aem/aio_aem_events/pom.xml index 65534326..ff6988c2 100644 --- a/aem/aio_aem_events/pom.xml +++ b/aem/aio_aem_events/pom.xml @@ -22,7 +22,7 @@ com.adobe.aio.aem aio-aem ../pom.xml - 1.0.19-SNAPSHOT + 1.1.0-SNAPSHOT aio-aem-events diff --git a/aem/core_aem/pom.xml b/aem/core_aem/pom.xml index 643817bf..ea115590 100644 --- a/aem/core_aem/pom.xml +++ b/aem/core_aem/pom.xml @@ -19,7 +19,7 @@ com.adobe.aio.aem aio-aem ../pom.xml - 1.0.19-SNAPSHOT + 1.1.0-SNAPSHOT aio-aem-core diff --git a/aem/events_ingress_aem/pom.xml b/aem/events_ingress_aem/pom.xml index c77f51ab..5ff7cf95 100644 --- a/aem/events_ingress_aem/pom.xml +++ b/aem/events_ingress_aem/pom.xml @@ -19,7 +19,7 @@ com.adobe.aio.aem aio-aem ../pom.xml - 1.0.19-SNAPSHOT + 1.1.0-SNAPSHOT aio-aem-events-publish diff --git a/aem/events_mgmt_aem/pom.xml b/aem/events_mgmt_aem/pom.xml index af53e322..4153b123 100644 --- a/aem/events_mgmt_aem/pom.xml +++ b/aem/events_mgmt_aem/pom.xml @@ -19,7 +19,7 @@ com.adobe.aio.aem aio-aem ../pom.xml - 1.0.19-SNAPSHOT + 1.1.0-SNAPSHOT aio-aem-events-mgmt diff --git a/aem/events_osgi_mapping/pom.xml b/aem/events_osgi_mapping/pom.xml index a7dff45c..a32d6cc4 100644 --- a/aem/events_osgi_mapping/pom.xml +++ b/aem/events_osgi_mapping/pom.xml @@ -19,7 +19,7 @@ com.adobe.aio.aem aio-aem ../pom.xml - 1.0.19-SNAPSHOT + 1.1.0-SNAPSHOT aio-aem-events-osgi-mapping diff --git a/aem/lib_osgi/pom.xml b/aem/lib_osgi/pom.xml index 9aca417d..de8d7446 100644 --- a/aem/lib_osgi/pom.xml +++ b/aem/lib_osgi/pom.xml @@ -17,7 +17,7 @@ com.adobe.aio.aem aio-aem ../pom.xml - 1.0.19-SNAPSHOT + 1.1.0-SNAPSHOT aio-lib-osgi diff --git a/aem/pom.xml b/aem/pom.xml index f7706467..5176c5f1 100644 --- a/aem/pom.xml +++ b/aem/pom.xml @@ -19,7 +19,7 @@ com.adobe.aio aio-lib-java ../pom.xml - 1.0.19-SNAPSHOT + 1.1.0-SNAPSHOT com.adobe.aio.aem diff --git a/core/pom.xml b/core/pom.xml index 40b559e5..728aa03a 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -17,7 +17,7 @@ com.adobe.aio aio-lib-java - 1.0.19-SNAPSHOT + 1.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/events_ingress/pom.xml b/events_ingress/pom.xml index 6e9c7d55..f88735bc 100644 --- a/events_ingress/pom.xml +++ b/events_ingress/pom.xml @@ -17,7 +17,7 @@ com.adobe.aio aio-lib-java - 1.0.19-SNAPSHOT + 1.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/events_journal/pom.xml b/events_journal/pom.xml index 00d7628f..cd323f14 100644 --- a/events_journal/pom.xml +++ b/events_journal/pom.xml @@ -17,7 +17,7 @@ com.adobe.aio aio-lib-java - 1.0.19-SNAPSHOT + 1.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/events_mgmt/pom.xml b/events_mgmt/pom.xml index 40489f0e..c6db951e 100644 --- a/events_mgmt/pom.xml +++ b/events_mgmt/pom.xml @@ -17,7 +17,7 @@ com.adobe.aio aio-lib-java - 1.0.19-SNAPSHOT + 1.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/events_test/pom.xml b/events_test/pom.xml index 2689adc8..255446f9 100644 --- a/events_test/pom.xml +++ b/events_test/pom.xml @@ -17,7 +17,7 @@ com.adobe.aio aio-lib-java - 1.0.19-SNAPSHOT + 1.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/events_webhook/pom.xml b/events_webhook/pom.xml index 53acc62f..f016ea90 100644 --- a/events_webhook/pom.xml +++ b/events_webhook/pom.xml @@ -16,7 +16,7 @@ com.adobe.aio aio-lib-java - 1.0.19-SNAPSHOT + 1.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/events_xdm/pom.xml b/events_xdm/pom.xml index f7096c2c..b2080df1 100644 --- a/events_xdm/pom.xml +++ b/events_xdm/pom.xml @@ -17,7 +17,7 @@ com.adobe.aio aio-lib-java - 1.0.19-SNAPSHOT + 1.1.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/ims/pom.xml b/ims/pom.xml index b4bb4473..3a785988 100644 --- a/ims/pom.xml +++ b/ims/pom.xml @@ -17,7 +17,7 @@ com.adobe.aio aio-lib-java - 1.0.19-SNAPSHOT + 1.1.0-SNAPSHOT ../pom.xml 4.0.0 From 9fa7ee6921b82901acdb4de779871edf536c756e Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Mon, 21 Aug 2023 10:37:10 -0500 Subject: [PATCH 31/35] Remove Java11 Deprecated API reference. --- ims/src/main/java/com/adobe/aio/ims/ImsService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ims/src/main/java/com/adobe/aio/ims/ImsService.java b/ims/src/main/java/com/adobe/aio/ims/ImsService.java index a31b6aa7..954a17dd 100644 --- a/ims/src/main/java/com/adobe/aio/ims/ImsService.java +++ b/ims/src/main/java/com/adobe/aio/ims/ImsService.java @@ -23,7 +23,7 @@ public interface ImsService { * @deprecated this will be removed in v2.0 * @return AccessToken a valid API authentication token */ - @Deprecated(since = "1.1", forRemoval = true) + @Deprecated() AccessToken getJwtExchangeAccessToken(); /** @@ -33,7 +33,7 @@ public interface ImsService { * @param accessToken the token to check * @return true if the provided access token is still valid, false otherwise */ - @Deprecated(since = "1.1", forRemoval = true) + @Deprecated() boolean validateAccessToken(String accessToken); AccessToken getOAuthAccessToken(); From bb5724010fcb0263e0f8e6e041f7a4481265a91b Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Mon, 21 Aug 2023 17:30:40 -0500 Subject: [PATCH 32/35] Abstract the type of token from interceptor consumers. --- .../publish/feign/FeignPublishService.java | 9 +- .../journal/feign/FeignJournalService.java | 4 +- .../feign/FeignProviderService.java | 26 ++-- .../feign/FeignRegistrationService.java | 17 +-- .../adobe/aio/ims/feign/AuthInterceptor.java | 88 ++++++++++++ .../aio/ims/feign/JWTAuthInterceptor.java | 72 ++-------- .../adobe/aio/ims/feign/OAuthInterceptor.java | 26 ++++ .../com/adobe/aio/ims/model/AccessToken.java | 2 +- .../aio/ims/feign/AuthInterceptorTest.java | 131 ++++++++++++++++++ .../aio/ims/feign/JwtAuthInterceptorTest.java | 128 +++-------------- .../ims/feign/OAuthAuthInterceptorTest.java | 45 ++++++ 11 files changed, 349 insertions(+), 199 deletions(-) create mode 100644 ims/src/main/java/com/adobe/aio/ims/feign/AuthInterceptor.java create mode 100644 ims/src/main/java/com/adobe/aio/ims/feign/OAuthInterceptor.java create mode 100644 ims/src/test/java/com/adobe/aio/ims/feign/AuthInterceptorTest.java create mode 100644 ims/src/test/java/com/adobe/aio/ims/feign/OAuthAuthInterceptorTest.java diff --git a/events_ingress/src/main/java/com/adobe/aio/event/publish/feign/FeignPublishService.java b/events_ingress/src/main/java/com/adobe/aio/event/publish/feign/FeignPublishService.java index ec65d8fe..1504ffe1 100644 --- a/events_ingress/src/main/java/com/adobe/aio/event/publish/feign/FeignPublishService.java +++ b/events_ingress/src/main/java/com/adobe/aio/event/publish/feign/FeignPublishService.java @@ -11,21 +11,22 @@ */ package com.adobe.aio.event.publish.feign; +import org.apache.commons.lang3.StringUtils; + import com.adobe.aio.event.publish.PublishService; import com.adobe.aio.event.publish.api.PublishApi; import com.adobe.aio.event.publish.model.CloudEvent; import com.adobe.aio.exception.AIOException; import com.adobe.aio.feign.AIOHeaderInterceptor; -import com.adobe.aio.ims.feign.JWTAuthInterceptor; -import com.adobe.aio.util.feign.FeignUtil; +import com.adobe.aio.ims.feign.AuthInterceptor; import com.adobe.aio.util.JacksonUtil; +import com.adobe.aio.util.feign.FeignUtil; import com.adobe.aio.workspace.Workspace; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.TextNode; import feign.RequestInterceptor; -import org.apache.commons.lang3.StringUtils; public class FeignPublishService implements PublishService { @@ -38,7 +39,7 @@ public FeignPublishService(final Workspace workspace, final String url) { "PublishService is missing a workspace context"); } workspace.validateWorkspaceContext(); - RequestInterceptor authInterceptor = JWTAuthInterceptor.builder().workspace(workspace).build(); + RequestInterceptor authInterceptor = AuthInterceptor.builder().workspace(workspace).build(); this.publishApi = FeignUtil.getDefaultBuilder() .requestInterceptor(authInterceptor) .requestInterceptor(AIOHeaderInterceptor.builder().workspace(workspace).build()) diff --git a/events_journal/src/main/java/com/adobe/aio/event/journal/feign/FeignJournalService.java b/events_journal/src/main/java/com/adobe/aio/event/journal/feign/FeignJournalService.java index 157a79e3..3de112e1 100644 --- a/events_journal/src/main/java/com/adobe/aio/event/journal/feign/FeignJournalService.java +++ b/events_journal/src/main/java/com/adobe/aio/event/journal/feign/FeignJournalService.java @@ -15,7 +15,7 @@ import com.adobe.aio.event.journal.api.JournalApi; import com.adobe.aio.event.journal.model.JournalEntry; import com.adobe.aio.feign.AIOHeaderInterceptor; -import com.adobe.aio.ims.feign.JWTAuthInterceptor; +import com.adobe.aio.ims.feign.AuthInterceptor; import com.adobe.aio.util.JacksonUtil; import com.adobe.aio.util.feign.FeignUtil; import com.adobe.aio.workspace.Workspace; @@ -46,7 +46,7 @@ public FeignJournalService(final Workspace workspace, final String journalUrl) { } workspace.validateWorkspaceContext(); this.imsOrgId = workspace.getImsOrgId(); - authInterceptor = JWTAuthInterceptor.builder().workspace(workspace).build(); + authInterceptor = AuthInterceptor.builder().workspace(workspace).build(); aioHeaderInterceptor = AIOHeaderInterceptor.builder().workspace(workspace).build(); this.journalApi = FeignUtil.getBaseBuilder() .decoder(new JournalLinkDecoder(new JacksonDecoder(JacksonUtil.DEFAULT_OBJECT_MAPPER))) diff --git a/events_mgmt/src/main/java/com/adobe/aio/event/management/feign/FeignProviderService.java b/events_mgmt/src/main/java/com/adobe/aio/event/management/feign/FeignProviderService.java index d26d90b8..90f7478c 100644 --- a/events_mgmt/src/main/java/com/adobe/aio/event/management/feign/FeignProviderService.java +++ b/events_mgmt/src/main/java/com/adobe/aio/event/management/feign/FeignProviderService.java @@ -11,32 +11,34 @@ */ package com.adobe.aio.event.management.feign; -import static com.adobe.aio.util.Constants.API_MANAGEMENT_URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; import com.adobe.aio.event.management.ProviderService; -import com.adobe.aio.event.management.api.SampleEventApi; -import com.adobe.aio.event.management.model.SampleEvent; -import com.adobe.aio.exception.AIOException; -import com.adobe.aio.feign.AIOHeaderInterceptor; -import com.adobe.aio.ims.feign.JWTAuthInterceptor; -import com.adobe.aio.workspace.Workspace; import com.adobe.aio.event.management.api.EventMetadataApi; import com.adobe.aio.event.management.api.ProviderApi; +import com.adobe.aio.event.management.api.SampleEventApi; import com.adobe.aio.event.management.model.EventMetadata; import com.adobe.aio.event.management.model.EventMetadataCollection; import com.adobe.aio.event.management.model.Provider; import com.adobe.aio.event.management.model.ProviderCollection; import com.adobe.aio.event.management.model.ProviderInputModel; +import com.adobe.aio.event.management.model.SampleEvent; +import com.adobe.aio.exception.AIOException; +import com.adobe.aio.feign.AIOHeaderInterceptor; +import com.adobe.aio.ims.feign.AuthInterceptor; import com.adobe.aio.util.Constants; import com.adobe.aio.util.feign.FeignUtil; +import com.adobe.aio.workspace.Workspace; import feign.RequestInterceptor; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static com.adobe.aio.util.Constants.*; + public class FeignProviderService implements ProviderService { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @@ -53,7 +55,7 @@ public FeignProviderService(final Workspace workspace, final String url) { throw new IllegalArgumentException("ProviderService is missing a workspace context"); } workspace.validateWorkspaceContext(); - RequestInterceptor authInterceptor = JWTAuthInterceptor.builder().workspace(workspace).build(); + RequestInterceptor authInterceptor = AuthInterceptor.builder().workspace(workspace).build(); RequestInterceptor aioHeaderInterceptor = AIOHeaderInterceptor.builder().workspace(workspace).build(); this.providerApi = FeignUtil.getDefaultBuilder() diff --git a/events_mgmt/src/main/java/com/adobe/aio/event/management/feign/FeignRegistrationService.java b/events_mgmt/src/main/java/com/adobe/aio/event/management/feign/FeignRegistrationService.java index 41bdda00..b0baa51d 100644 --- a/events_mgmt/src/main/java/com/adobe/aio/event/management/feign/FeignRegistrationService.java +++ b/events_mgmt/src/main/java/com/adobe/aio/event/management/feign/FeignRegistrationService.java @@ -11,7 +11,11 @@ */ package com.adobe.aio.event.management.feign; -import static com.adobe.aio.util.Constants.API_MANAGEMENT_URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; import com.adobe.aio.event.management.RegistrationService; import com.adobe.aio.event.management.api.RegistrationApi; @@ -21,15 +25,12 @@ import com.adobe.aio.event.management.model.RegistrationPaginatedModel; import com.adobe.aio.event.management.model.RegistrationUpdateModel; import com.adobe.aio.feign.AIOHeaderInterceptor; -import com.adobe.aio.ims.feign.JWTAuthInterceptor; +import com.adobe.aio.ims.feign.AuthInterceptor; import com.adobe.aio.util.feign.FeignUtil; import com.adobe.aio.workspace.Workspace; -import com.fasterxml.jackson.core.JsonProcessingException; import feign.RequestInterceptor; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import org.apache.commons.lang3.StringUtils; + +import static com.adobe.aio.util.Constants.*; public class FeignRegistrationService implements RegistrationService { @@ -43,7 +44,7 @@ public FeignRegistrationService(final Workspace workspace, throw new IllegalArgumentException("RegistrationService is missing a workspace context"); } workspace.validateWorkspaceContext(); - RequestInterceptor authInterceptor = JWTAuthInterceptor.builder().workspace(workspace).build(); + RequestInterceptor authInterceptor = AuthInterceptor.builder().workspace(workspace).build(); this.registrationApi = FeignUtil.getDefaultBuilder() .requestInterceptor(authInterceptor) .requestInterceptor(AIOHeaderInterceptor.builder().workspace(workspace).build()) diff --git a/ims/src/main/java/com/adobe/aio/ims/feign/AuthInterceptor.java b/ims/src/main/java/com/adobe/aio/ims/feign/AuthInterceptor.java new file mode 100644 index 00000000..62826e35 --- /dev/null +++ b/ims/src/main/java/com/adobe/aio/ims/feign/AuthInterceptor.java @@ -0,0 +1,88 @@ +package com.adobe.aio.ims.feign; + +import com.adobe.aio.auth.Context; +import com.adobe.aio.auth.JwtContext; +import com.adobe.aio.auth.OAuthContext; +import com.adobe.aio.ims.ImsService; +import com.adobe.aio.ims.model.AccessToken; +import com.adobe.aio.workspace.Workspace; +import feign.RequestInterceptor; +import feign.RequestTemplate; + +import static com.adobe.aio.util.Constants.*; + +public abstract class AuthInterceptor implements RequestInterceptor { + + private volatile Long expirationTimeMillis; + private volatile AccessToken accessToken; + private final ImsService imsService; + + protected AuthInterceptor(final ImsService imsService) { + this.imsService = imsService; + } + + @Override + public void apply(RequestTemplate requestTemplate) { + applyAuthorization(requestTemplate); + } + + ImsService getImsService() { + return this.imsService; + } + + abstract AccessToken fetchAccessToken(); + + synchronized String getAccessToken() { + if (expirationTimeMillis == null || System.currentTimeMillis() >= expirationTimeMillis) { + updateAccessToken(); + } + return this.accessToken.getAccessToken(); + } + + private void applyAuthorization(RequestTemplate requestTemplate) { + // If the request already have an authorization + if (requestTemplate.headers().containsKey(AUTHORIZATION_HEADER)) { + return; + } + // If first time or of expired, get the token + if (getAccessToken() != null) { + requestTemplate.header(AUTHORIZATION_HEADER, BEARER_PREFIX + getAccessToken()); + } + } + + private synchronized void updateAccessToken() { + this.accessToken = fetchAccessToken(); + this.expirationTimeMillis = System.currentTimeMillis() + accessToken.getExpiresIn(); + // throw RetryableException and implement Feign Retry ? + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private Context authContext; + private ImsService imsService; + + private Builder() { + } + + public Builder workspace(Workspace workspace) { + this.authContext = workspace.getAuthContext(); + this.imsService = ImsService.builder().workspace(workspace).build(); + return this; + } + + public AuthInterceptor build() { + if (authContext instanceof JwtContext) { + return new JWTAuthInterceptor(imsService); + } else if (authContext instanceof OAuthContext) { + return new OAuthInterceptor(imsService); + } + throw new IllegalStateException("Unable to find interceptor for AuthContext"); + } + } + + +} diff --git a/ims/src/main/java/com/adobe/aio/ims/feign/JWTAuthInterceptor.java b/ims/src/main/java/com/adobe/aio/ims/feign/JWTAuthInterceptor.java index 3b2c0d91..ceecf3df 100644 --- a/ims/src/main/java/com/adobe/aio/ims/feign/JWTAuthInterceptor.java +++ b/ims/src/main/java/com/adobe/aio/ims/feign/JWTAuthInterceptor.java @@ -13,75 +13,19 @@ import com.adobe.aio.ims.ImsService; import com.adobe.aio.ims.model.AccessToken; -import com.adobe.aio.workspace.Workspace; -import feign.RequestInterceptor; -import feign.RequestTemplate; -import static com.adobe.aio.util.Constants.*; +@Deprecated +public class JWTAuthInterceptor extends AuthInterceptor { -public class JWTAuthInterceptor implements RequestInterceptor { - - private volatile Long expirationTimeMillis; - private volatile AccessToken accessToken; - - private final ImsService imsService; - - private JWTAuthInterceptor(final ImsService imsService) { - this.imsService = imsService; - } - - @Override - public void apply(RequestTemplate requestTemplate) { - applyAuthorization(requestTemplate); + protected JWTAuthInterceptor(ImsService imsService) { + super(imsService); } - public boolean isUp(){ - return imsService.validateAccessToken(this.getAccessToken()); + public boolean isUp() { + return getImsService().validateAccessToken(this.getAccessToken()); } - private void applyAuthorization(RequestTemplate requestTemplate) { - // If the request already have an authorization - if (requestTemplate.headers().containsKey(AUTHORIZATION_HEADER)) { - return; - } - // If first time or of expired, get the token - if (getAccessToken() != null) { - requestTemplate.header(AUTHORIZATION_HEADER, BEARER_PREFIX + getAccessToken()); - } + AccessToken fetchAccessToken() { + return getImsService().getJwtExchangeAccessToken(); } - - private synchronized void updateAccessToken() { - this.accessToken = imsService.getJwtExchangeAccessToken(); - this.expirationTimeMillis = System.currentTimeMillis() + accessToken.getExpiresIn(); - // throw RetryableException and implement Feign Retry ? - } - - private synchronized String getAccessToken() { - if (expirationTimeMillis == null || System.currentTimeMillis() >= expirationTimeMillis) { - updateAccessToken(); - } - return this.accessToken.getAccessToken(); - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private ImsService imsService; - - private Builder() { - } - - public Builder workspace(Workspace workspace) { - this.imsService = ImsService.builder().workspace(workspace).build(); - return this; - } - - public JWTAuthInterceptor build() { - return new JWTAuthInterceptor(imsService); - } - } - } diff --git a/ims/src/main/java/com/adobe/aio/ims/feign/OAuthInterceptor.java b/ims/src/main/java/com/adobe/aio/ims/feign/OAuthInterceptor.java new file mode 100644 index 00000000..beade471 --- /dev/null +++ b/ims/src/main/java/com/adobe/aio/ims/feign/OAuthInterceptor.java @@ -0,0 +1,26 @@ +/* + * Copyright 2017 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +package com.adobe.aio.ims.feign; + +import com.adobe.aio.ims.ImsService; +import com.adobe.aio.ims.model.AccessToken; + +public class OAuthInterceptor extends AuthInterceptor { + protected OAuthInterceptor(ImsService imsService) { + super(imsService); + } + + @Override + AccessToken fetchAccessToken() { + return getImsService().getOAuthAccessToken(); + } +} diff --git a/ims/src/main/java/com/adobe/aio/ims/model/AccessToken.java b/ims/src/main/java/com/adobe/aio/ims/model/AccessToken.java index 345a6920..0d8fc582 100644 --- a/ims/src/main/java/com/adobe/aio/ims/model/AccessToken.java +++ b/ims/src/main/java/com/adobe/aio/ims/model/AccessToken.java @@ -53,7 +53,7 @@ public long getExpiresIn() { @Override public String toString() { return "AccessToken{" + - ", accessToken='**secret**'" + + "accessToken='**secret**'" + ", expiresIn=" + expiresIn + '}'; } diff --git a/ims/src/test/java/com/adobe/aio/ims/feign/AuthInterceptorTest.java b/ims/src/test/java/com/adobe/aio/ims/feign/AuthInterceptorTest.java new file mode 100644 index 00000000..f538ae8c --- /dev/null +++ b/ims/src/test/java/com/adobe/aio/ims/feign/AuthInterceptorTest.java @@ -0,0 +1,131 @@ +package com.adobe.aio.ims.feign; + +import java.lang.reflect.Field; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import com.adobe.aio.auth.Context; +import com.adobe.aio.ims.ImsService; +import com.adobe.aio.ims.model.AccessToken; +import com.adobe.aio.workspace.Workspace; +import feign.RequestTemplate; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedConstruction; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import static com.adobe.aio.util.Constants.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +public class AuthInterceptorTest { + + private static final String ACCESS_TOKEN = "ACCESS_TOKEN"; + + @Mock + private RequestTemplate template; + + @Mock + private Context authContext; + + @Mock + private Workspace workspace; + + @Mock + private ImsService imsService; + + @Test + void invalidContext() { + when(workspace.getAuthContext()).thenReturn(authContext); + + try (MockedConstruction ignored = mockConstruction(ImsService.Builder.class, + (mock, mockContext) -> { + when(mock.workspace(workspace)).thenReturn(mock); + when(mock.build()).thenReturn(imsService); + } + )) { + assertThrows(IllegalStateException.class, () -> AuthInterceptor.builder().workspace(workspace).build()); + } + } + + @Test + void applyAlreadySet() throws Exception { + Calendar expires = Calendar.getInstance(); + expires.add(Calendar.HOUR, 1); + AccessToken token = new AccessToken(ACCESS_TOKEN, 3600000); + + Map> headers = new HashMap<>(); + headers.put(AUTHORIZATION_HEADER, Collections.EMPTY_LIST); + + when(template.headers()).thenReturn(headers); + + AuthInterceptor interceptor = mock(AuthInterceptor.class, + withSettings().useConstructor(imsService).defaultAnswer(CALLS_REAL_METHODS)); + Field expiresField = AuthInterceptor.class.getDeclaredField("expirationTimeMillis"); + expiresField.setAccessible(true); + expiresField.set(interceptor, expires.getTimeInMillis()); + + Field tokenField = AuthInterceptor.class.getDeclaredField("accessToken"); + tokenField.setAccessible(true); + tokenField.set(interceptor, token); + interceptor.apply(template); + + } + + @Test + void apply() throws Exception { + Calendar expires = Calendar.getInstance(); + expires.add(Calendar.HOUR, 1); + AccessToken token = new AccessToken(ACCESS_TOKEN, 3600000); + + when(template.headers()).thenReturn(Collections.emptyMap()); + + AuthInterceptor interceptor = mock(AuthInterceptor.class, + withSettings().useConstructor(imsService).defaultAnswer(CALLS_REAL_METHODS)); + Field expiresField = AuthInterceptor.class.getDeclaredField("expirationTimeMillis"); + expiresField.setAccessible(true); + expiresField.set(interceptor, expires.getTimeInMillis()); + + Field tokenField = AuthInterceptor.class.getDeclaredField("accessToken"); + tokenField.setAccessible(true); + tokenField.set(interceptor, token); + interceptor.apply(template); + + verify(template).header(AUTHORIZATION_HEADER, BEARER_PREFIX + ACCESS_TOKEN); + } + + @Test + void getAccessTokenNotSet() { + Calendar expires = Calendar.getInstance(); + expires.add(Calendar.HOUR, 1); + AccessToken token = new AccessToken(ACCESS_TOKEN, 3600000); + + AuthInterceptor interceptor = mock(AuthInterceptor.class, + withSettings().useConstructor(imsService).defaultAnswer(CALLS_REAL_METHODS)); + doReturn(token).when(interceptor).fetchAccessToken(); + assertEquals(ACCESS_TOKEN, interceptor.getAccessToken()); + } + + @Test + void getAccessTokenExpired() throws Exception { + Calendar expires = Calendar.getInstance(); + expires.add(Calendar.HOUR, 1); + AccessToken token = new AccessToken(ACCESS_TOKEN, 3600000); + + AuthInterceptor interceptor = mock(AuthInterceptor.class, + withSettings().useConstructor(imsService).defaultAnswer(CALLS_REAL_METHODS)); + Field expiresField = AuthInterceptor.class.getDeclaredField("expirationTimeMillis"); + expiresField.setAccessible(true); + expiresField.set(interceptor, 1L); + + doReturn(token).when(interceptor).fetchAccessToken(); + assertEquals(ACCESS_TOKEN, interceptor.getAccessToken()); + } +} diff --git a/ims/src/test/java/com/adobe/aio/ims/feign/JwtAuthInterceptorTest.java b/ims/src/test/java/com/adobe/aio/ims/feign/JwtAuthInterceptorTest.java index 39a226db..b99577e2 100644 --- a/ims/src/test/java/com/adobe/aio/ims/feign/JwtAuthInterceptorTest.java +++ b/ims/src/test/java/com/adobe/aio/ims/feign/JwtAuthInterceptorTest.java @@ -8,15 +8,16 @@ import java.util.HashMap; import java.util.Map; +import com.adobe.aio.auth.JwtContext; import com.adobe.aio.ims.ImsService; import com.adobe.aio.ims.model.AccessToken; -import feign.RequestTemplate; +import com.adobe.aio.workspace.Workspace; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.MockedConstruction; import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.verification.VerificationMode; +import shaded_package.org.checkerframework.checker.units.qual.A; import static com.adobe.aio.util.Constants.*; import static org.junit.jupiter.api.Assertions.*; @@ -24,136 +25,47 @@ @ExtendWith(MockitoExtension.class) public class JwtAuthInterceptorTest { - private static final String ACCESS_TOKEN = "ACCESS_TOKEN"; - - @Mock - private ImsService imsService; @Mock - private RequestTemplate template; - - @Test - void isUpExpirationNull() throws Exception { - AccessToken token = new AccessToken(ACCESS_TOKEN, new Date().getTime()); - - when(imsService.getJwtExchangeAccessToken()).thenReturn(token); - when(imsService.validateAccessToken(ACCESS_TOKEN)).thenReturn(true); - - try (MockedConstruction ignored = mockConstruction(ImsService.Builder.class, - (mock, mockContext) -> { - when(mock.workspace(null)).thenReturn(mock); - when(mock.build()).thenReturn(imsService); - } - )) { - JWTAuthInterceptor interceptor = JWTAuthInterceptor.builder().workspace(null).build(); - assertTrue(interceptor.isUp()); - } - } - - @Test - void isUpTokenExpired() throws Exception { - AccessToken token = new AccessToken(ACCESS_TOKEN, '1'); - - when(imsService.getJwtExchangeAccessToken()).thenReturn(token); - when(imsService.validateAccessToken(ACCESS_TOKEN)).thenReturn(true); + private Workspace workspace; - try (MockedConstruction ignored = mockConstruction(ImsService.Builder.class, - (mock, mockContext) -> { - when(mock.workspace(null)).thenReturn(mock); - when(mock.build()).thenReturn(imsService); - } - )) { - JWTAuthInterceptor interceptor = JWTAuthInterceptor.builder().workspace(null).build(); - Field expiresField = interceptor.getClass().getDeclaredField("expirationTimeMillis"); - expiresField.setAccessible(true); - expiresField.set(interceptor, 1L); - assertTrue(interceptor.isUp()); - } - } + @Mock + private JwtContext authContext; + @Mock + private ImsService imsService; @Test - void isUpTokenValid() throws Exception { - Calendar expires = Calendar.getInstance(); - expires.add(Calendar.HOUR, 1); - AccessToken token = new AccessToken(ACCESS_TOKEN, expires.getTimeInMillis()); - + void isUp() { + when(workspace.getAuthContext()).thenReturn(authContext); when(imsService.validateAccessToken(ACCESS_TOKEN)).thenReturn(true); try (MockedConstruction ignored = mockConstruction(ImsService.Builder.class, (mock, mockContext) -> { - when(mock.workspace(null)).thenReturn(mock); - when(mock.build()).thenReturn(imsService); - } - )) { - JWTAuthInterceptor interceptor = JWTAuthInterceptor.builder().workspace(null).build(); - Field expiresField = interceptor.getClass().getDeclaredField("expirationTimeMillis"); - expiresField.setAccessible(true); - expiresField.set(interceptor, expires.getTimeInMillis()); - - Field tokenField = interceptor.getClass().getDeclaredField("accessToken"); - tokenField.setAccessible(true); - tokenField.set(interceptor, token); - - assertTrue(interceptor.isUp()); - } - } - - @Test - void applyAlreadySet() throws Exception { - Calendar expires = Calendar.getInstance(); - expires.add(Calendar.HOUR, 1); - AccessToken token = new AccessToken(ACCESS_TOKEN, expires.getTimeInMillis()); - - Map> headers = new HashMap<>(); - headers.put(AUTHORIZATION_HEADER, Collections.EMPTY_LIST); - - when(template.headers()).thenReturn(headers); - - try (MockedConstruction ignored = mockConstruction(ImsService.Builder.class, - (mock, mockContext) -> { - when(mock.workspace(null)).thenReturn(mock); + when(mock.workspace(workspace)).thenReturn(mock); when(mock.build()).thenReturn(imsService); } )) { - JWTAuthInterceptor interceptor = JWTAuthInterceptor.builder().workspace(null).build(); - Field expiresField = interceptor.getClass().getDeclaredField("expirationTimeMillis"); - expiresField.setAccessible(true); - expiresField.set(interceptor, expires.getTimeInMillis()); - - Field tokenField = interceptor.getClass().getDeclaredField("accessToken"); - tokenField.setAccessible(true); - tokenField.set(interceptor, token); - interceptor.apply(template); + JWTAuthInterceptor spy = spy((JWTAuthInterceptor) AuthInterceptor.builder().workspace(workspace).build()); + doReturn(ACCESS_TOKEN).when(spy).getAccessToken(); + assertTrue(spy.isUp()); } } @Test - void apply() throws Exception { - Calendar expires = Calendar.getInstance(); - expires.add(Calendar.HOUR, 1); - AccessToken token = new AccessToken(ACCESS_TOKEN, expires.getTimeInMillis()); - - when(template.headers()).thenReturn(Collections.emptyMap()); + void fetchAccessToken() { + when(workspace.getAuthContext()).thenReturn(authContext); + when(imsService.getJwtExchangeAccessToken()).thenReturn(new AccessToken(ACCESS_TOKEN, 0)); try (MockedConstruction ignored = mockConstruction(ImsService.Builder.class, (mock, mockContext) -> { - when(mock.workspace(null)).thenReturn(mock); + when(mock.workspace(workspace)).thenReturn(mock); when(mock.build()).thenReturn(imsService); } )) { - JWTAuthInterceptor interceptor = JWTAuthInterceptor.builder().workspace(null).build(); - Field expiresField = interceptor.getClass().getDeclaredField("expirationTimeMillis"); - expiresField.setAccessible(true); - expiresField.set(interceptor, expires.getTimeInMillis()); - - Field tokenField = interceptor.getClass().getDeclaredField("accessToken"); - tokenField.setAccessible(true); - tokenField.set(interceptor, token); - interceptor.apply(template); - - verify(template).header(AUTHORIZATION_HEADER, BEARER_PREFIX + ACCESS_TOKEN); + JWTAuthInterceptor interceptor = (JWTAuthInterceptor) AuthInterceptor.builder().workspace(workspace).build(); + assertNotNull(interceptor.fetchAccessToken()); } } } diff --git a/ims/src/test/java/com/adobe/aio/ims/feign/OAuthAuthInterceptorTest.java b/ims/src/test/java/com/adobe/aio/ims/feign/OAuthAuthInterceptorTest.java new file mode 100644 index 00000000..de93714d --- /dev/null +++ b/ims/src/test/java/com/adobe/aio/ims/feign/OAuthAuthInterceptorTest.java @@ -0,0 +1,45 @@ +package com.adobe.aio.ims.feign; + +import com.adobe.aio.auth.JwtContext; +import com.adobe.aio.auth.OAuthContext; +import com.adobe.aio.ims.ImsService; +import com.adobe.aio.ims.model.AccessToken; +import com.adobe.aio.workspace.Workspace; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedConstruction; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +public class OAuthAuthInterceptorTest { + private static final String ACCESS_TOKEN = "ACCESS_TOKEN"; + + @Mock + private Workspace workspace; + + @Mock + private OAuthContext authContext; + + @Mock + private ImsService imsService; + + @Test + void fetchAccessToken() { + when(workspace.getAuthContext()).thenReturn(authContext); + when(imsService.getOAuthAccessToken()).thenReturn(new AccessToken(ACCESS_TOKEN, 0)); + + try (MockedConstruction ignored = mockConstruction(ImsService.Builder.class, + (mock, mockContext) -> { + when(mock.workspace(workspace)).thenReturn(mock); + when(mock.build()).thenReturn(imsService); + } + )) { + OAuthInterceptor interceptor = (OAuthInterceptor) AuthInterceptor.builder().workspace(workspace).build(); + assertNotNull(interceptor.fetchAccessToken()); + } + } +} From 1b3ea47341ff21aa77ee1f14b9ccead1bf9d2dae Mon Sep 17 00:00:00 2001 From: Francois Date: Tue, 22 Aug 2023 17:27:23 +0200 Subject: [PATCH 33/35] Some proposal for better Backward compatibility (#167) * re-introducing `Workspace.getCredentialId()` * moving back `WorkspaceUtil` to `com.adobe.aio.util` --- .../main/java/com/adobe/aio/workspace/Workspace.java | 12 ++++++++++++ .../publish/feign/FeignPublishServiceTestDrive.java | 2 +- .../journal/feign/FeignJournalServiceTestDrive.java | 2 +- .../feign/FeignProviderServiceTestDrive.java | 2 +- .../feign/FeignRegistrationServiceTestDrive.java | 2 +- .../aio/event/journal/JournalServiceTester.java | 2 +- .../aio/event/management/ProviderServiceTester.java | 2 +- .../event/management/RegistrationServiceTester.java | 2 +- .../aio/event/publish/PublishServiceTester.java | 2 +- .../event/journal/JournalServiceIntegrationTest.java | 2 +- .../management/ProviderServiceIntegrationTest.java | 2 +- .../java/com/adobe/aio}/util/WorkspaceUtil.java | 7 +++++-- .../ims/feign/FeignImsServiceIntegrationTest.java | 2 +- .../aio/ims/feign/FeignImsServiceTestDrive.java | 2 +- 14 files changed, 29 insertions(+), 14 deletions(-) rename ims/src/{test/java/com/adobe/aio/ims => main/java/com/adobe/aio}/util/WorkspaceUtil.java (92%) diff --git a/core/src/main/java/com/adobe/aio/workspace/Workspace.java b/core/src/main/java/com/adobe/aio/workspace/Workspace.java index e00682db..01014f50 100644 --- a/core/src/main/java/com/adobe/aio/workspace/Workspace.java +++ b/core/src/main/java/com/adobe/aio/workspace/Workspace.java @@ -147,6 +147,18 @@ public Context getAuthContext() { return authContext; } + /** + * @deprecated This will be removed in v2.0 of the library. + */ + @Deprecated + public String getCredentialId() { + if (authContext instanceof JwtContext) { + return ((JwtContext) authContext).getCredentialId(); + } else { + return null; + } + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/events_ingress/src/test/java/com/adobe/aio/event/publish/feign/FeignPublishServiceTestDrive.java b/events_ingress/src/test/java/com/adobe/aio/event/publish/feign/FeignPublishServiceTestDrive.java index 90dba8bb..4cccce47 100644 --- a/events_ingress/src/test/java/com/adobe/aio/event/publish/feign/FeignPublishServiceTestDrive.java +++ b/events_ingress/src/test/java/com/adobe/aio/event/publish/feign/FeignPublishServiceTestDrive.java @@ -14,7 +14,7 @@ import com.adobe.aio.event.publish.PublishService; import com.adobe.aio.event.publish.model.CloudEvent; import com.adobe.aio.util.JacksonUtil; -import com.adobe.aio.ims.util.WorkspaceUtil; +import com.adobe.aio.util.WorkspaceUtil; import com.adobe.aio.workspace.Workspace; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/events_journal/src/test/java/com/adobe/aio/event/journal/feign/FeignJournalServiceTestDrive.java b/events_journal/src/test/java/com/adobe/aio/event/journal/feign/FeignJournalServiceTestDrive.java index 4a6e7de1..9e4f06df 100644 --- a/events_journal/src/test/java/com/adobe/aio/event/journal/feign/FeignJournalServiceTestDrive.java +++ b/events_journal/src/test/java/com/adobe/aio/event/journal/feign/FeignJournalServiceTestDrive.java @@ -13,7 +13,7 @@ import com.adobe.aio.event.journal.JournalService; import com.adobe.aio.event.journal.model.JournalEntry; -import com.adobe.aio.ims.util.WorkspaceUtil; +import com.adobe.aio.util.WorkspaceUtil; import com.adobe.aio.workspace.Workspace; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/events_mgmt/src/test/java/com/adobe/aio/event/management/feign/FeignProviderServiceTestDrive.java b/events_mgmt/src/test/java/com/adobe/aio/event/management/feign/FeignProviderServiceTestDrive.java index 9f7fec14..cbcbdd44 100644 --- a/events_mgmt/src/test/java/com/adobe/aio/event/management/feign/FeignProviderServiceTestDrive.java +++ b/events_mgmt/src/test/java/com/adobe/aio/event/management/feign/FeignProviderServiceTestDrive.java @@ -15,7 +15,7 @@ import com.adobe.aio.event.management.model.EventMetadata; import com.adobe.aio.event.management.model.Provider; import com.adobe.aio.event.management.model.ProviderInputModel; -import com.adobe.aio.ims.util.WorkspaceUtil; +import com.adobe.aio.util.WorkspaceUtil; import com.adobe.aio.workspace.Workspace; import java.util.List; import java.util.Optional; diff --git a/events_mgmt/src/test/java/com/adobe/aio/event/management/feign/FeignRegistrationServiceTestDrive.java b/events_mgmt/src/test/java/com/adobe/aio/event/management/feign/FeignRegistrationServiceTestDrive.java index dd651252..d214b959 100644 --- a/events_mgmt/src/test/java/com/adobe/aio/event/management/feign/FeignRegistrationServiceTestDrive.java +++ b/events_mgmt/src/test/java/com/adobe/aio/event/management/feign/FeignRegistrationServiceTestDrive.java @@ -13,7 +13,7 @@ import com.adobe.aio.event.management.RegistrationService; import com.adobe.aio.event.management.model.RegistrationCreateModel; -import com.adobe.aio.ims.util.WorkspaceUtil; +import com.adobe.aio.util.WorkspaceUtil; import com.adobe.aio.workspace.Workspace; import com.adobe.aio.event.management.model.EventsOfInterestInputModel; import com.adobe.aio.event.management.model.Registration; diff --git a/events_test/src/main/java/com/adobe/aio/event/journal/JournalServiceTester.java b/events_test/src/main/java/com/adobe/aio/event/journal/JournalServiceTester.java index 2e3a0263..f6982491 100644 --- a/events_test/src/main/java/com/adobe/aio/event/journal/JournalServiceTester.java +++ b/events_test/src/main/java/com/adobe/aio/event/journal/JournalServiceTester.java @@ -15,7 +15,7 @@ import com.adobe.aio.event.journal.model.Event; import com.adobe.aio.event.journal.model.JournalEntry; -import com.adobe.aio.ims.util.WorkspaceUtil; +import com.adobe.aio.util.WorkspaceUtil; import com.adobe.aio.workspace.Workspace; import java.util.HashSet; import java.util.Iterator; diff --git a/events_test/src/main/java/com/adobe/aio/event/management/ProviderServiceTester.java b/events_test/src/main/java/com/adobe/aio/event/management/ProviderServiceTester.java index fcfdbdf9..406dca06 100644 --- a/events_test/src/main/java/com/adobe/aio/event/management/ProviderServiceTester.java +++ b/events_test/src/main/java/com/adobe/aio/event/management/ProviderServiceTester.java @@ -15,7 +15,7 @@ import com.adobe.aio.event.management.model.Provider; import com.adobe.aio.event.management.model.ProviderInputModel; import com.adobe.aio.event.publish.model.CloudEvent; -import com.adobe.aio.ims.util.WorkspaceUtil; +import com.adobe.aio.util.WorkspaceUtil; import com.adobe.aio.workspace.Workspace; import java.util.Collections; diff --git a/events_test/src/main/java/com/adobe/aio/event/management/RegistrationServiceTester.java b/events_test/src/main/java/com/adobe/aio/event/management/RegistrationServiceTester.java index 6ee0c33b..2e651dc4 100644 --- a/events_test/src/main/java/com/adobe/aio/event/management/RegistrationServiceTester.java +++ b/events_test/src/main/java/com/adobe/aio/event/management/RegistrationServiceTester.java @@ -15,7 +15,7 @@ import com.adobe.aio.event.management.model.EventsOfInterestInputModel; import com.adobe.aio.event.management.model.Registration; import com.adobe.aio.event.management.model.RegistrationCreateModel; -import com.adobe.aio.ims.util.WorkspaceUtil; +import com.adobe.aio.util.WorkspaceUtil; import com.adobe.aio.workspace.Workspace; import java.net.MalformedURLException; import java.net.URL; diff --git a/events_test/src/main/java/com/adobe/aio/event/publish/PublishServiceTester.java b/events_test/src/main/java/com/adobe/aio/event/publish/PublishServiceTester.java index 4009ec67..5fd6a27b 100644 --- a/events_test/src/main/java/com/adobe/aio/event/publish/PublishServiceTester.java +++ b/events_test/src/main/java/com/adobe/aio/event/publish/PublishServiceTester.java @@ -14,7 +14,7 @@ import static com.adobe.aio.event.publish.model.CloudEvent.SPEC_VERSION; import com.adobe.aio.event.publish.model.CloudEvent; -import com.adobe.aio.ims.util.WorkspaceUtil; +import com.adobe.aio.util.WorkspaceUtil; import com.fasterxml.jackson.core.JsonProcessingException; import java.util.UUID; import org.slf4j.Logger; diff --git a/events_test/src/test/java/com/adobe/aio/event/journal/JournalServiceIntegrationTest.java b/events_test/src/test/java/com/adobe/aio/event/journal/JournalServiceIntegrationTest.java index 864a7031..9458bbdf 100644 --- a/events_test/src/test/java/com/adobe/aio/event/journal/JournalServiceIntegrationTest.java +++ b/events_test/src/test/java/com/adobe/aio/event/journal/JournalServiceIntegrationTest.java @@ -18,7 +18,7 @@ import com.adobe.aio.event.management.RegistrationServiceTester; import com.adobe.aio.event.management.model.Registration; import com.adobe.aio.event.publish.PublishServiceTester; -import com.adobe.aio.ims.util.WorkspaceUtil; +import com.adobe.aio.util.WorkspaceUtil; import org.junit.jupiter.api.Test; import static com.adobe.aio.event.management.ProviderServiceIntegrationTest.*; diff --git a/events_test/src/test/java/com/adobe/aio/event/management/ProviderServiceIntegrationTest.java b/events_test/src/test/java/com/adobe/aio/event/management/ProviderServiceIntegrationTest.java index 50a43e51..46b494bb 100644 --- a/events_test/src/test/java/com/adobe/aio/event/management/ProviderServiceIntegrationTest.java +++ b/events_test/src/test/java/com/adobe/aio/event/management/ProviderServiceIntegrationTest.java @@ -17,7 +17,7 @@ import com.adobe.aio.event.management.feign.ConflictException; import com.adobe.aio.event.management.model.EventMetadata; import com.adobe.aio.event.management.model.Provider; -import com.adobe.aio.ims.util.WorkspaceUtil; +import com.adobe.aio.util.WorkspaceUtil; import org.junit.jupiter.api.Test; import static com.adobe.aio.event.management.model.ProviderInputModel.*; diff --git a/ims/src/test/java/com/adobe/aio/ims/util/WorkspaceUtil.java b/ims/src/main/java/com/adobe/aio/util/WorkspaceUtil.java similarity index 92% rename from ims/src/test/java/com/adobe/aio/ims/util/WorkspaceUtil.java rename to ims/src/main/java/com/adobe/aio/util/WorkspaceUtil.java index f21502aa..d087798c 100644 --- a/ims/src/test/java/com/adobe/aio/ims/util/WorkspaceUtil.java +++ b/ims/src/main/java/com/adobe/aio/util/WorkspaceUtil.java @@ -9,9 +9,9 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ -package com.adobe.aio.ims.util; +package com.adobe.aio.util; -import com.adobe.aio.util.FileUtil; +import com.adobe.aio.ims.util.PrivateKeyBuilder; import com.adobe.aio.workspace.Workspace; import java.security.PrivateKey; import java.util.Properties; @@ -63,7 +63,10 @@ public static String getSystemProperty(String key) { public static String getSystemProperty(String key, String propertyClassPath) { String value = System.getProperty(key); if (StringUtils.isBlank(value)) { + logger.debug("loading property `{}` from classpath `{}`", key, propertyClassPath); value = FileUtil.readPropertiesFromClassPath(propertyClassPath).getProperty(key); + } else { + logger.debug("loading property `{}`from JVM System Properties", key); } return value; } diff --git a/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceIntegrationTest.java b/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceIntegrationTest.java index 4951749a..1c1b770b 100644 --- a/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceIntegrationTest.java +++ b/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceIntegrationTest.java @@ -13,7 +13,7 @@ import com.adobe.aio.ims.ImsService; import com.adobe.aio.ims.model.AccessToken; -import com.adobe.aio.ims.util.WorkspaceUtil; +import com.adobe.aio.util.WorkspaceUtil; import com.adobe.aio.workspace.Workspace; import feign.FeignException; import org.junit.jupiter.api.Test; diff --git a/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceTestDrive.java b/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceTestDrive.java index d8599346..4dcead17 100644 --- a/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceTestDrive.java +++ b/ims/src/test/java/com/adobe/aio/ims/feign/FeignImsServiceTestDrive.java @@ -12,7 +12,7 @@ package com.adobe.aio.ims.feign; import com.adobe.aio.ims.ImsService; -import com.adobe.aio.ims.util.WorkspaceUtil; +import com.adobe.aio.util.WorkspaceUtil; import com.adobe.aio.workspace.Workspace; import com.adobe.aio.ims.model.AccessToken; import com.adobe.aio.ims.util.PrivateKeyBuilder; From e1e6e363de8eb878b1e3b7428e0129d1a43193ee Mon Sep 17 00:00:00 2001 From: Bryan Stopp Date: Tue, 22 Aug 2023 10:45:03 -0500 Subject: [PATCH 34/35] Remove tests jar dep as not needed anymore. --- events_ingress/pom.xml | 8 -------- events_journal/pom.xml | 7 ------- events_mgmt/pom.xml | 8 -------- events_test/pom.xml | 6 ------ ims/pom.xml | 17 ----------------- 5 files changed, 46 deletions(-) diff --git a/events_ingress/pom.xml b/events_ingress/pom.xml index f88735bc..8415d044 100644 --- a/events_ingress/pom.xml +++ b/events_ingress/pom.xml @@ -48,14 +48,6 @@ aio-lib-java-ims ${project.version}
- - - com.adobe.aio - aio-lib-java-ims - ${project.version} - tests - test - diff --git a/events_journal/pom.xml b/events_journal/pom.xml index cd323f14..658b055e 100644 --- a/events_journal/pom.xml +++ b/events_journal/pom.xml @@ -53,13 +53,6 @@ ${project.version} test - - com.adobe.aio - aio-lib-java-ims - ${project.version} - tests - test - org.junit.jupiter junit-jupiter diff --git a/events_mgmt/pom.xml b/events_mgmt/pom.xml index c6db951e..da0c55b9 100644 --- a/events_mgmt/pom.xml +++ b/events_mgmt/pom.xml @@ -52,14 +52,6 @@ io.openapitools.jackson.dataformat jackson-dataformat-hal - - - com.adobe.aio - aio-lib-java-ims - ${project.version} - tests - test - diff --git a/events_test/pom.xml b/events_test/pom.xml index 255446f9..05b32399 100644 --- a/events_test/pom.xml +++ b/events_test/pom.xml @@ -59,11 +59,5 @@ junit-jupiter compile - - com.adobe.aio - aio-lib-java-ims - ${project.version} - tests - diff --git a/ims/pom.xml b/ims/pom.xml index 3a785988..c48aa940 100644 --- a/ims/pom.xml +++ b/ims/pom.xml @@ -73,21 +73,4 @@ mockserver-junit-jupiter-no-dependencies - - - - - org.apache.maven.plugins - maven-jar-plugin - - - test-jar - - test-jar - - - - - - From f98e8ed08ef68d1a5db1b74520964de1c68e445e Mon Sep 17 00:00:00 2001 From: Francois Date: Wed, 23 Aug 2023 16:50:01 +0200 Subject: [PATCH 35/35] Let's stick to the old CloudEvents v1 spec and package for now (#168) as we did not test the latest package and as this format is key to our partners integration --- .../java/com/adobe/aio/util/JacksonUtil.java | 20 +++++++++++-------- pom.xml | 4 +++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/com/adobe/aio/util/JacksonUtil.java b/core/src/main/java/com/adobe/aio/util/JacksonUtil.java index 7832efe0..9febae2b 100644 --- a/core/src/main/java/com/adobe/aio/util/JacksonUtil.java +++ b/core/src/main/java/com/adobe/aio/util/JacksonUtil.java @@ -20,7 +20,8 @@ import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.node.TextNode; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import io.cloudevents.json.ZonedDateTimeDeserializer; +import io.cloudevents.json.ZonedDateTimeSerializer; import io.openapitools.jackson.dataformat.hal.JacksonHALModule; import java.time.ZonedDateTime; import org.apache.commons.lang3.StringUtils; @@ -31,13 +32,16 @@ private JacksonUtil() { } public static final ObjectMapper DEFAULT_OBJECT_MAPPER = - JsonMapper.builder() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .serializationInclusion(Include.NON_NULL) - .addModule(new JacksonHALModule()) - .addModule(new Jdk8Module()) - .addModule(new JavaTimeModule()) - .build(); + JsonMapper.builder() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .serializationInclusion(Include.NON_NULL) + .addModule(new SimpleModule() + .addSerializer(ZonedDateTime.class, new ZonedDateTimeSerializer()) + .addDeserializer(ZonedDateTime.class, new ZonedDateTimeDeserializer())) + // let's stick to CloudEvents v1 ISO_OFFSET_DATE_TIME date format + .addModule(new JacksonHALModule()) + .addModule(new Jdk8Module()) + .build(); public static JsonNode getJsonNode(String jsonPayload) throws JsonProcessingException { if (StringUtils.isEmpty(jsonPayload)) { diff --git a/pom.xml b/pom.xml index 93ede0b8..bab420df 100644 --- a/pom.xml +++ b/pom.xml @@ -103,7 +103,9 @@ 2.0.7 2.15.2 0.11.5 - 2.5.0 + + 1.2.0 + 1.0.9