From 8d0a1e9a52f1c0304c69ea22e212001e176d8450 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Mon, 19 Sep 2022 13:04:17 -0700 Subject: [PATCH 01/24] secrets manager --- build.gradle | 6 +- .../io/orkes/conductor/client/ApiClient.java | 29 +++++++ .../conductor/client/SecretsManager.java | 8 ++ .../integration/aws/AwsSecretsManager.java | 77 +++++++++++++++++++ 4 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 src/main/java/io/orkes/conductor/client/SecretsManager.java create mode 100644 src/main/java/io/orkes/conductor/client/integration/aws/AwsSecretsManager.java diff --git a/build.gradle b/build.gradle index 5ca9c0d6..623109d6 100644 --- a/build.gradle +++ b/build.gradle @@ -34,7 +34,8 @@ ext { slf4j : '1.7.36', spectator : '0.122.0', spock : '2.1-groovy-2.5', - wiremock : '2.33.2' + wiremock : '2.33.2', + revAwsSdk : '1.12.153', ] } @@ -71,6 +72,9 @@ dependencies { implementation 'org.threeten:threetenbp:1.3.5' + //Integrations + implementation "com.amazonaws:aws-java-sdk-ssm:${versions.revAwsSdk}" + // test dependencies testImplementation "org.junit.jupiter:junit-jupiter-api:${versions.junit}" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${versions.junit}" diff --git a/src/main/java/io/orkes/conductor/client/ApiClient.java b/src/main/java/io/orkes/conductor/client/ApiClient.java index cb3f3905..55d423bd 100644 --- a/src/main/java/io/orkes/conductor/client/ApiClient.java +++ b/src/main/java/io/orkes/conductor/client/ApiClient.java @@ -81,6 +81,8 @@ public class ApiClient { private String token; private Object tokenMutex; + private SecretsManager secretsManager; + /* * Constructor for ApiClient */ @@ -89,6 +91,11 @@ public ApiClient() { this("http://localhost:8080/api"); } + public ApiClient(String basePath, SecretsManager secretsManager) { + this(basePath); + this.secretsManager = secretsManager; + } + public ApiClient(String basePath) { this.basePath = basePath; @@ -107,6 +114,11 @@ public ApiClient(String basePath) { this.tokenMutex = new Object(); } + public ApiClient(String basePath, SecretsManager secretsManager, String key, String secret) { + this(basePath, key, secret); + this.secretsManager = secretsManager; + } + public ApiClient(String basePath, String keyId, String keySecret) { this(basePath); this.keyId = keyId; @@ -467,6 +479,14 @@ public ApiClient setWriteTimeout(int writeTimeout) { return this; } + public SecretsManager getSecretsManager() { + return secretsManager; + } + + public void setSecretsManager(SecretsManager secretsManager) { + this.secretsManager = secretsManager; + } + /** * Format the given parameter object into string. * @@ -1217,6 +1237,15 @@ public void refreshToken() throws Exception { "KeyId and KeySecret must be set in order to get an authentication token"); } synchronized (this.tokenMutex) { + + String secretKey = this.keyId; + String secretValue = this.keySecret; + + if(secretsManager != null) { + secretKey = secretsManager.getSecret(this.keyId); + secretValue = secretsManager.getSecret(this.keySecret); + } + GenerateTokenRequest generateTokenRequest = new GenerateTokenRequest().keyId(this.keyId).keySecret(this.keySecret); Map response = diff --git a/src/main/java/io/orkes/conductor/client/SecretsManager.java b/src/main/java/io/orkes/conductor/client/SecretsManager.java new file mode 100644 index 00000000..31e88cc6 --- /dev/null +++ b/src/main/java/io/orkes/conductor/client/SecretsManager.java @@ -0,0 +1,8 @@ +package io.orkes.conductor.client; + +public interface SecretsManager { + + String getSecret(String key); + + void storeSecret(String key, String secret); +} diff --git a/src/main/java/io/orkes/conductor/client/integration/aws/AwsSecretsManager.java b/src/main/java/io/orkes/conductor/client/integration/aws/AwsSecretsManager.java new file mode 100644 index 00000000..8f5ad9ac --- /dev/null +++ b/src/main/java/io/orkes/conductor/client/integration/aws/AwsSecretsManager.java @@ -0,0 +1,77 @@ +package io.orkes.conductor.client.integration.aws; + +import com.amazonaws.ClientConfiguration; +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.regions.Regions; +import com.amazonaws.retry.PredefinedRetryPolicies; +import com.amazonaws.retry.RetryPolicy; +import com.amazonaws.services.simplesystemsmanagement.AWSSimpleSystemsManagement; +import com.amazonaws.services.simplesystemsmanagement.AWSSimpleSystemsManagementClientBuilder; +import com.amazonaws.services.simplesystemsmanagement.model.GetParameterRequest; +import com.amazonaws.services.simplesystemsmanagement.model.ParameterType; +import com.amazonaws.services.simplesystemsmanagement.model.PutParameterRequest; +import io.orkes.conductor.client.SecretsManager; + +public class AwsSecretsManager implements SecretsManager { + + private final AWSSimpleSystemsManagement client; + + public AwsSecretsManager(AWSCredentialsProvider credentialsProvider, String region) { + this.client = createClient(credentialsProvider, region); + } + + public AwsSecretsManager(AWSCredentialsProvider credentialsProvider) { + this.client = createClient(credentialsProvider, getRegion()); + } + + @Override + public String getSecret(String key) { + GetParameterRequest request = new GetParameterRequest() + .withName(key) + .withWithDecryption(true); + return client.getParameter(request).getParameter().getValue(); + } + + @Override + public void storeSecret(String key, String secret) { + PutParameterRequest request = new PutParameterRequest() + .withName(key) + .withType(ParameterType.SecureString) + .withValue(secret) + .withOverwrite(true); + client.putParameter(request); + } + + private AWSSimpleSystemsManagement createClient(AWSCredentialsProvider credentialsProvider, String region) { + RetryPolicy retryPolicy = new RetryPolicy( + PredefinedRetryPolicies.DEFAULT_RETRY_CONDITION, + PredefinedRetryPolicies.DEFAULT_BACKOFF_STRATEGY, + 3, + false); + + AWSSimpleSystemsManagementClientBuilder builder = AWSSimpleSystemsManagementClientBuilder.standard() + .withClientConfiguration(new ClientConfiguration().withRetryPolicy(retryPolicy)) + .withRegion(region) + .withCredentials(credentialsProvider); + + return builder.build(); + } + + private String getRegion() { + String region = System.getenv("aws.region"); + + if(region == null) { + region = System.getProperty("aws.region"); + } + + if(region == null) { + region = System.getenv("AWS_REGION"); + } + + if(region == null) { + region = System.getProperty("AWS_REGION"); + } + + return region; + } +} From 15b1a0aefcb0904ad80b0c2ebbe8f949be97d9e9 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Mon, 19 Sep 2022 13:07:03 -0700 Subject: [PATCH 02/24] new methods --- .../io/orkes/conductor/sdk/examples/Test.java | 46 +++++++++++++++++++ .../conductor/client/AuthorizationClient.java | 2 + .../client/http/OrkesAuthorizationClient.java | 7 +++ 3 files changed, 55 insertions(+) create mode 100644 example/java/io/orkes/conductor/sdk/examples/Test.java diff --git a/example/java/io/orkes/conductor/sdk/examples/Test.java b/example/java/io/orkes/conductor/sdk/examples/Test.java new file mode 100644 index 00000000..7be12b45 --- /dev/null +++ b/example/java/io/orkes/conductor/sdk/examples/Test.java @@ -0,0 +1,46 @@ +package io.orkes.conductor.sdk.examples; + +import com.netflix.conductor.common.metadata.tasks.Task; +import com.netflix.conductor.common.run.Workflow; +import io.orkes.conductor.client.ApiClient; +import io.orkes.conductor.client.OrkesClients; +import io.orkes.conductor.client.WorkflowClient; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class Test { + + public static void main(String[] args) { + + ApiClient apiCient = new ApiClient("http://localhost:8080/api"); + apiCient.setReadTimeout(600_000); + OrkesClients orkesClients = new OrkesClients(apiCient); + WorkflowClient workflowClient = orkesClients.getWorkflowClient(); + Workflow workflow = workflowClient.getWorkflow("f70f12a7-3851-11ed-a091-0242ac110002", true); + Map byStatus = workflow.getTasks().stream().collect(Collectors.groupingBy(Task::getStatus, Collectors.counting())); + System.out.println(byStatus); + System.out.println(workflow.getStatus() + "," + workflow.getReasonForIncompletion()); + Map counts = new HashMap<>(); + for (Task task : workflow.getTasks()) { + String key = task.getReferenceTaskName(); + int count = counts.getOrDefault(key, 0); + count++; + counts.put(key, count); + } + + Set tasks = workflow.getTasks().stream().filter(task -> task.getTaskId().equals("7e08586b-37d2-11ed-bf01-0242ac110002")).collect(Collectors.toSet()); + System.out.println("here: " + tasks); + for (Map.Entry e : counts.entrySet()) { + if(e.getValue() > 1) { + System.out.println(e.getKey() + ", got executed " + e.getValue() + " times."); + } + } + Set dups = workflow.getTasks().stream().filter(t -> t.getReferenceTaskName().equals("5a974bc5-9632-4bab-b8cc-ffcbb530d7b7")).collect(Collectors.toSet()); + for (Task dup : dups) { + System.out.println("dup " + dup.getTaskId() + "," + dup.getReferenceTaskName() + "," + dup.getStatus() + "," + dup.getRetryCount()); + } + } +} diff --git a/src/main/java/io/orkes/conductor/client/AuthorizationClient.java b/src/main/java/io/orkes/conductor/client/AuthorizationClient.java index f4fd640d..5531fe10 100644 --- a/src/main/java/io/orkes/conductor/client/AuthorizationClient.java +++ b/src/main/java/io/orkes/conductor/client/AuthorizationClient.java @@ -62,6 +62,8 @@ public interface AuthorizationClient { CreateAccessKeyResponse createAccessKey(String id); + void createAccessKey(String id, SecretsManager secretsManager, String secretPath); + ConductorApplication createApplication( CreateOrUpdateApplicationRequest createOrUpdateApplicationRequest); diff --git a/src/main/java/io/orkes/conductor/client/http/OrkesAuthorizationClient.java b/src/main/java/io/orkes/conductor/client/http/OrkesAuthorizationClient.java index fd9ffb4c..c32abd39 100644 --- a/src/main/java/io/orkes/conductor/client/http/OrkesAuthorizationClient.java +++ b/src/main/java/io/orkes/conductor/client/http/OrkesAuthorizationClient.java @@ -17,6 +17,7 @@ import io.orkes.conductor.client.ApiClient; import io.orkes.conductor.client.AuthorizationClient; +import io.orkes.conductor.client.SecretsManager; import io.orkes.conductor.client.http.api.ApplicationResourceApi; import io.orkes.conductor.client.http.api.AuthorizationResourceApi; import io.orkes.conductor.client.http.api.GroupResourceApi; @@ -134,6 +135,12 @@ public CreateAccessKeyResponse createAccessKey(String id) throws ApiException { return applicationResourceApi.createAccessKey(id); } + @Override + public void createAccessKey(String id, SecretsManager secretsManager, String secretPath) { + CreateAccessKeyResponse response = applicationResourceApi.createAccessKey(id); + secretsManager.storeSecret(secretPath, response.getSecret()); + } + @Override public ConductorApplication createApplication( CreateOrUpdateApplicationRequest createOrUpdateApplicationRequest) throws ApiException { From 8b220b7f6e104a213088b68870d49b00037ec988 Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Mon, 19 Sep 2022 13:07:29 -0700 Subject: [PATCH 03/24] Delete Test.java --- .../io/orkes/conductor/sdk/examples/Test.java | 46 ------------------- 1 file changed, 46 deletions(-) delete mode 100644 example/java/io/orkes/conductor/sdk/examples/Test.java diff --git a/example/java/io/orkes/conductor/sdk/examples/Test.java b/example/java/io/orkes/conductor/sdk/examples/Test.java deleted file mode 100644 index 7be12b45..00000000 --- a/example/java/io/orkes/conductor/sdk/examples/Test.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.orkes.conductor.sdk.examples; - -import com.netflix.conductor.common.metadata.tasks.Task; -import com.netflix.conductor.common.run.Workflow; -import io.orkes.conductor.client.ApiClient; -import io.orkes.conductor.client.OrkesClients; -import io.orkes.conductor.client.WorkflowClient; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -public class Test { - - public static void main(String[] args) { - - ApiClient apiCient = new ApiClient("http://localhost:8080/api"); - apiCient.setReadTimeout(600_000); - OrkesClients orkesClients = new OrkesClients(apiCient); - WorkflowClient workflowClient = orkesClients.getWorkflowClient(); - Workflow workflow = workflowClient.getWorkflow("f70f12a7-3851-11ed-a091-0242ac110002", true); - Map byStatus = workflow.getTasks().stream().collect(Collectors.groupingBy(Task::getStatus, Collectors.counting())); - System.out.println(byStatus); - System.out.println(workflow.getStatus() + "," + workflow.getReasonForIncompletion()); - Map counts = new HashMap<>(); - for (Task task : workflow.getTasks()) { - String key = task.getReferenceTaskName(); - int count = counts.getOrDefault(key, 0); - count++; - counts.put(key, count); - } - - Set tasks = workflow.getTasks().stream().filter(task -> task.getTaskId().equals("7e08586b-37d2-11ed-bf01-0242ac110002")).collect(Collectors.toSet()); - System.out.println("here: " + tasks); - for (Map.Entry e : counts.entrySet()) { - if(e.getValue() > 1) { - System.out.println(e.getKey() + ", got executed " + e.getValue() + " times."); - } - } - Set dups = workflow.getTasks().stream().filter(t -> t.getReferenceTaskName().equals("5a974bc5-9632-4bab-b8cc-ffcbb530d7b7")).collect(Collectors.toSet()); - for (Task dup : dups) { - System.out.println("dup " + dup.getTaskId() + "," + dup.getReferenceTaskName() + "," + dup.getStatus() + "," + dup.getRetryCount()); - } - } -} From 40f1015203b1fe458c9f8fff82070056b0b404ae Mon Sep 17 00:00:00 2001 From: Viren Baraiya Date: Mon, 19 Sep 2022 13:16:39 -0700 Subject: [PATCH 04/24] styles --- .../io/orkes/conductor/client/ApiClient.java | 3 +- .../conductor/client/SecretsManager.java | 12 ++++ .../integration/aws/AwsSecretsManager.java | 62 ++++++++++++------- 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/src/main/java/io/orkes/conductor/client/ApiClient.java b/src/main/java/io/orkes/conductor/client/ApiClient.java index 55d423bd..7e6cf476 100644 --- a/src/main/java/io/orkes/conductor/client/ApiClient.java +++ b/src/main/java/io/orkes/conductor/client/ApiClient.java @@ -1237,11 +1237,10 @@ public void refreshToken() throws Exception { "KeyId and KeySecret must be set in order to get an authentication token"); } synchronized (this.tokenMutex) { - String secretKey = this.keyId; String secretValue = this.keySecret; - if(secretsManager != null) { + if (secretsManager != null) { secretKey = secretsManager.getSecret(this.keyId); secretValue = secretsManager.getSecret(this.keySecret); } diff --git a/src/main/java/io/orkes/conductor/client/SecretsManager.java b/src/main/java/io/orkes/conductor/client/SecretsManager.java index 31e88cc6..fd7fc9da 100644 --- a/src/main/java/io/orkes/conductor/client/SecretsManager.java +++ b/src/main/java/io/orkes/conductor/client/SecretsManager.java @@ -1,3 +1,15 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed 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 CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ package io.orkes.conductor.client; public interface SecretsManager { diff --git a/src/main/java/io/orkes/conductor/client/integration/aws/AwsSecretsManager.java b/src/main/java/io/orkes/conductor/client/integration/aws/AwsSecretsManager.java index 8f5ad9ac..6c401ff2 100644 --- a/src/main/java/io/orkes/conductor/client/integration/aws/AwsSecretsManager.java +++ b/src/main/java/io/orkes/conductor/client/integration/aws/AwsSecretsManager.java @@ -1,8 +1,21 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed 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 CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ package io.orkes.conductor.client.integration.aws; +import io.orkes.conductor.client.SecretsManager; + import com.amazonaws.ClientConfiguration; import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.regions.Regions; import com.amazonaws.retry.PredefinedRetryPolicies; import com.amazonaws.retry.RetryPolicy; import com.amazonaws.services.simplesystemsmanagement.AWSSimpleSystemsManagement; @@ -10,7 +23,6 @@ import com.amazonaws.services.simplesystemsmanagement.model.GetParameterRequest; import com.amazonaws.services.simplesystemsmanagement.model.ParameterType; import com.amazonaws.services.simplesystemsmanagement.model.PutParameterRequest; -import io.orkes.conductor.client.SecretsManager; public class AwsSecretsManager implements SecretsManager { @@ -26,33 +38,37 @@ public AwsSecretsManager(AWSCredentialsProvider credentialsProvider) { @Override public String getSecret(String key) { - GetParameterRequest request = new GetParameterRequest() - .withName(key) - .withWithDecryption(true); + GetParameterRequest request = + new GetParameterRequest().withName(key).withWithDecryption(true); return client.getParameter(request).getParameter().getValue(); } @Override public void storeSecret(String key, String secret) { - PutParameterRequest request = new PutParameterRequest() - .withName(key) - .withType(ParameterType.SecureString) - .withValue(secret) - .withOverwrite(true); + PutParameterRequest request = + new PutParameterRequest() + .withName(key) + .withType(ParameterType.SecureString) + .withValue(secret) + .withOverwrite(true); client.putParameter(request); } - private AWSSimpleSystemsManagement createClient(AWSCredentialsProvider credentialsProvider, String region) { - RetryPolicy retryPolicy = new RetryPolicy( - PredefinedRetryPolicies.DEFAULT_RETRY_CONDITION, - PredefinedRetryPolicies.DEFAULT_BACKOFF_STRATEGY, - 3, - false); + private AWSSimpleSystemsManagement createClient( + AWSCredentialsProvider credentialsProvider, String region) { + RetryPolicy retryPolicy = + new RetryPolicy( + PredefinedRetryPolicies.DEFAULT_RETRY_CONDITION, + PredefinedRetryPolicies.DEFAULT_BACKOFF_STRATEGY, + 3, + false); - AWSSimpleSystemsManagementClientBuilder builder = AWSSimpleSystemsManagementClientBuilder.standard() - .withClientConfiguration(new ClientConfiguration().withRetryPolicy(retryPolicy)) - .withRegion(region) - .withCredentials(credentialsProvider); + AWSSimpleSystemsManagementClientBuilder builder = + AWSSimpleSystemsManagementClientBuilder.standard() + .withClientConfiguration( + new ClientConfiguration().withRetryPolicy(retryPolicy)) + .withRegion(region) + .withCredentials(credentialsProvider); return builder.build(); } @@ -60,15 +76,15 @@ private AWSSimpleSystemsManagement createClient(AWSCredentialsProvider credentia private String getRegion() { String region = System.getenv("aws.region"); - if(region == null) { + if (region == null) { region = System.getProperty("aws.region"); } - if(region == null) { + if (region == null) { region = System.getenv("AWS_REGION"); } - if(region == null) { + if (region == null) { region = System.getProperty("AWS_REGION"); } From 04ba9bc6412ccff1e28b9c4e33bb280fe2ad07de Mon Sep 17 00:00:00 2001 From: gardusig Date: Tue, 20 Sep 2022 16:26:52 -0300 Subject: [PATCH 05/24] Updated API client to receive secret manager and its respective paths --- .../io/orkes/conductor/client/ApiClient.java | 39 ++++++++----------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/src/main/java/io/orkes/conductor/client/ApiClient.java b/src/main/java/io/orkes/conductor/client/ApiClient.java index 7e6cf476..3f1b7b0c 100644 --- a/src/main/java/io/orkes/conductor/client/ApiClient.java +++ b/src/main/java/io/orkes/conductor/client/ApiClient.java @@ -62,7 +62,7 @@ public class ApiClient { private final String basePath; private final Map defaultHeaderMap = new HashMap(); - private String tempFolderPath = null; + private String tempFolderPath; private Map authentications; @@ -82,6 +82,8 @@ public class ApiClient { private Object tokenMutex; private SecretsManager secretsManager; + private String ssmKeyPath; + private String ssmSecretPath; /* * Constructor for ApiClient @@ -91,11 +93,6 @@ public ApiClient() { this("http://localhost:8080/api"); } - public ApiClient(String basePath, SecretsManager secretsManager) { - this(basePath); - this.secretsManager = secretsManager; - } - public ApiClient(String basePath) { this.basePath = basePath; @@ -105,7 +102,6 @@ public ApiClient(String basePath) { json = new JSON(); - // Setup authentications (key: authentication name, value: authentication). authentications = new HashMap(); this.keyId = null; @@ -114,9 +110,12 @@ public ApiClient(String basePath) { this.tokenMutex = new Object(); } - public ApiClient(String basePath, SecretsManager secretsManager, String key, String secret) { - this(basePath, key, secret); + public ApiClient( + String basePath, SecretsManager secretsManager, String keyPath, String secretPath) { + this(basePath); this.secretsManager = secretsManager; + this.ssmKeyPath = keyPath; + this.ssmSecretPath = secretPath; } public ApiClient(String basePath, String keyId, String keySecret) { @@ -394,7 +393,7 @@ public ApiClient addDefaultHeader(String key, String value) { /** * The path of temporary folder used to store downloaded files from endpoints with file - * response. The default value is null, i.e. using the system's default tempopary + * response. The default value is null, i.e. using the system's default temporary * folder. * * @see queryParams, List collectio /** * Set header parameters to the request builder, including default headers. * - * @param headerParams Header parameters in the ofrm of Map - * @param reqBuilder Reqeust.Builder + * @param headerParams Header parameters in the from of Map + * @param reqBuilder Request.Builder */ public void processHeaderParams(Map headerParams, Request.Builder reqBuilder) { for (Entry param : headerParams.entrySet()) { @@ -1232,19 +1231,15 @@ public void refreshToken() throws Exception { if (this.getToken() != null) { return; } - if (this.keyId == null || this.keySecret == null) { - throw new Exception( - "KeyId and KeySecret must be set in order to get an authentication token"); - } synchronized (this.tokenMutex) { - String secretKey = this.keyId; - String secretValue = this.keySecret; - if (secretsManager != null) { - secretKey = secretsManager.getSecret(this.keyId); - secretValue = secretsManager.getSecret(this.keySecret); + keyId = secretsManager.getSecret(this.ssmKeyPath); + keySecret = secretsManager.getSecret(this.ssmSecretPath); + } + if (this.keyId == null || this.keySecret == null) { + throw new Exception( + "KeyId and KeySecret must be set in order to get an authentication token"); } - GenerateTokenRequest generateTokenRequest = new GenerateTokenRequest().keyId(this.keyId).keySecret(this.keySecret); Map response = From 50e9855939812114159a257fefecc3d1646e2995 Mon Sep 17 00:00:00 2001 From: gardusig Date: Tue, 20 Sep 2022 17:42:46 -0300 Subject: [PATCH 06/24] Added AzureSecretsManager --- build.gradle | 8 ++- .../conductor/client/SecretsManager.java | 23 ++++++++ .../azure/AzureSecretsManager.java | 52 +++++++++++++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 src/main/java/io/orkes/conductor/client/integration/azure/AzureSecretsManager.java diff --git a/build.gradle b/build.gradle index 623109d6..e1267f91 100644 --- a/build.gradle +++ b/build.gradle @@ -35,7 +35,9 @@ ext { spectator : '0.122.0', spock : '2.1-groovy-2.5', wiremock : '2.33.2', - revAwsSdk : '1.12.153', + awsSsm : '1.12.153', + azureSsm : '4.2.3', + azureIdentity: '1.5.2', ] } @@ -73,7 +75,9 @@ dependencies { implementation 'org.threeten:threetenbp:1.3.5' //Integrations - implementation "com.amazonaws:aws-java-sdk-ssm:${versions.revAwsSdk}" + implementation "com.amazonaws:aws-java-sdk-ssm:${versions.awsSsm}" + implementation "com.azure:azure-security-keyvault-secrets:${versions.azureSsm}" + implementation "com.azure:azure-identity:${versions.azureIdentity}" // test dependencies testImplementation "org.junit.jupiter:junit-jupiter-api:${versions.junit}" diff --git a/src/main/java/io/orkes/conductor/client/SecretsManager.java b/src/main/java/io/orkes/conductor/client/SecretsManager.java index fd7fc9da..131347ba 100644 --- a/src/main/java/io/orkes/conductor/client/SecretsManager.java +++ b/src/main/java/io/orkes/conductor/client/SecretsManager.java @@ -12,9 +12,32 @@ */ package io.orkes.conductor.client; +import java.util.List; + public interface SecretsManager { String getSecret(String key); void storeSecret(String key, String secret); + + default String getProperty(String propertyName) { + String property = null; + List tentativeList = + List.of( + propertyName, + propertyName.toUpperCase(), + propertyName.replace('.', '_'), + propertyName.toUpperCase().replace('.', '_'), + propertyName.replace('_', '.'), + propertyName.toUpperCase().replace('_', '.')); + for (String name : tentativeList) { + if (property == null) { + property = System.getenv(name); + } + if (property == null) { + property = System.getProperty(name); + } + } + return property; + } } diff --git a/src/main/java/io/orkes/conductor/client/integration/azure/AzureSecretsManager.java b/src/main/java/io/orkes/conductor/client/integration/azure/AzureSecretsManager.java new file mode 100644 index 00000000..c93ca4ce --- /dev/null +++ b/src/main/java/io/orkes/conductor/client/integration/azure/AzureSecretsManager.java @@ -0,0 +1,52 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed 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 CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.integration.azure; + +import io.orkes.conductor.client.SecretsManager; + +import com.azure.identity.DefaultAzureCredentialBuilder; +import com.azure.security.keyvault.secrets.SecretClient; +import com.azure.security.keyvault.secrets.SecretClientBuilder; + +public class AzureSecretsManager implements SecretsManager { + private final SecretClient client; + + public AzureSecretsManager() { + this.client = createClient(); + } + + @Override + public String getSecret(String key) { + return getSecretFromKeyVault(key); + } + + @Override + public void storeSecret(String key, String secret) { + client.setSecret(key, secret); + } + + private String getSecretFromKeyVault(String key) { + return client.getSecret(key).getValue(); + } + + private SecretClient createClient() { + String keyVaultName = getProperty("azure.keyvault.name"); + if (keyVaultName == null) { + throw new RuntimeException("Key Vault name is not specified, cannot create client"); + } + return new SecretClientBuilder() + .vaultUrl(String.format("https://%s.vault.azure.net/", keyVaultName)) + .credential(new DefaultAzureCredentialBuilder().build()) + .buildClient(); + } +} From 8fe64ac330d910061d3194055d45b60afd20e320 Mon Sep 17 00:00:00 2001 From: gardusig Date: Tue, 20 Sep 2022 17:42:59 -0300 Subject: [PATCH 07/24] Minor improvements for aws secrets manager --- .../integration/aws/AwsSecretsManager.java | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/main/java/io/orkes/conductor/client/integration/aws/AwsSecretsManager.java b/src/main/java/io/orkes/conductor/client/integration/aws/AwsSecretsManager.java index 6c401ff2..b9766b33 100644 --- a/src/main/java/io/orkes/conductor/client/integration/aws/AwsSecretsManager.java +++ b/src/main/java/io/orkes/conductor/client/integration/aws/AwsSecretsManager.java @@ -62,32 +62,16 @@ private AWSSimpleSystemsManagement createClient( PredefinedRetryPolicies.DEFAULT_BACKOFF_STRATEGY, 3, false); - AWSSimpleSystemsManagementClientBuilder builder = AWSSimpleSystemsManagementClientBuilder.standard() .withClientConfiguration( new ClientConfiguration().withRetryPolicy(retryPolicy)) .withRegion(region) .withCredentials(credentialsProvider); - return builder.build(); } private String getRegion() { - String region = System.getenv("aws.region"); - - if (region == null) { - region = System.getProperty("aws.region"); - } - - if (region == null) { - region = System.getenv("AWS_REGION"); - } - - if (region == null) { - region = System.getProperty("AWS_REGION"); - } - - return region; + return getProperty("aws.region"); } } From ee3ec555ea6bce03076eef28a046823ea73911cd Mon Sep 17 00:00:00 2001 From: gardusig Date: Tue, 20 Sep 2022 22:12:31 -0300 Subject: [PATCH 08/24] Partially fixed build issues --- build.gradle | 2 +- .../client/integration/azure/AzureSecretsManager.java | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index e1267f91..d8a2ff28 100644 --- a/build.gradle +++ b/build.gradle @@ -77,7 +77,7 @@ dependencies { //Integrations implementation "com.amazonaws:aws-java-sdk-ssm:${versions.awsSsm}" implementation "com.azure:azure-security-keyvault-secrets:${versions.azureSsm}" - implementation "com.azure:azure-identity:${versions.azureIdentity}" + // implementation "com.azure:azure-identity:${versions.azureIdentity}" // test dependencies testImplementation "org.junit.jupiter:junit-jupiter-api:${versions.junit}" diff --git a/src/main/java/io/orkes/conductor/client/integration/azure/AzureSecretsManager.java b/src/main/java/io/orkes/conductor/client/integration/azure/AzureSecretsManager.java index c93ca4ce..73e0f92a 100644 --- a/src/main/java/io/orkes/conductor/client/integration/azure/AzureSecretsManager.java +++ b/src/main/java/io/orkes/conductor/client/integration/azure/AzureSecretsManager.java @@ -14,7 +14,6 @@ import io.orkes.conductor.client.SecretsManager; -import com.azure.identity.DefaultAzureCredentialBuilder; import com.azure.security.keyvault.secrets.SecretClient; import com.azure.security.keyvault.secrets.SecretClientBuilder; @@ -46,7 +45,7 @@ private SecretClient createClient() { } return new SecretClientBuilder() .vaultUrl(String.format("https://%s.vault.azure.net/", keyVaultName)) - .credential(new DefaultAzureCredentialBuilder().build()) + // .credential(new DefaultAzureCredentialBuilder().build()) .buildClient(); } } From fdff19511921da1eabba8af81f70610253f70cc9 Mon Sep 17 00:00:00 2001 From: gardusig Date: Wed, 21 Sep 2022 12:05:55 -0300 Subject: [PATCH 09/24] Increased wait time to wait for workflow execution tests --- .../orkes/conductor/client/worker/WorkflowExecutionTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/orkes/conductor/client/worker/WorkflowExecutionTests.java b/src/test/java/io/orkes/conductor/client/worker/WorkflowExecutionTests.java index a6d45cd7..75c0d75a 100644 --- a/src/test/java/io/orkes/conductor/client/worker/WorkflowExecutionTests.java +++ b/src/test/java/io/orkes/conductor/client/worker/WorkflowExecutionTests.java @@ -58,7 +58,7 @@ public void workflow() throws Exception { List workflowIds = startWorkflows(10, Commons.WORKFLOW_NAME); workflowIds.add(startWorkflow(Commons.WORKFLOW_NAME)); this.taskRunnerConfigurer.init(); - Thread.sleep(5 * 1000); + Thread.sleep(7 * 1000); workflowIds.forEach(workflowId -> validateCompletedWorkflow(workflowId)); this.taskRunnerConfigurer.shutdown(); } From 5db6e8380cfc8fb69c3de08bbeb8062a68142bbf Mon Sep 17 00:00:00 2001 From: gardusig Date: Wed, 21 Sep 2022 12:06:42 -0300 Subject: [PATCH 10/24] Added azure identity dependency for ssm with default credentials builder --- build.gradle | 4 ++-- .../client/integration/azure/AzureSecretsManager.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index d8a2ff28..d2ed3a42 100644 --- a/build.gradle +++ b/build.gradle @@ -37,7 +37,7 @@ ext { wiremock : '2.33.2', awsSsm : '1.12.153', azureSsm : '4.2.3', - azureIdentity: '1.5.2', + azureIdentity: '1.3.7', ] } @@ -77,7 +77,7 @@ dependencies { //Integrations implementation "com.amazonaws:aws-java-sdk-ssm:${versions.awsSsm}" implementation "com.azure:azure-security-keyvault-secrets:${versions.azureSsm}" - // implementation "com.azure:azure-identity:${versions.azureIdentity}" + implementation "com.azure:azure-identity:${versions.azureIdentity}" // test dependencies testImplementation "org.junit.jupiter:junit-jupiter-api:${versions.junit}" diff --git a/src/main/java/io/orkes/conductor/client/integration/azure/AzureSecretsManager.java b/src/main/java/io/orkes/conductor/client/integration/azure/AzureSecretsManager.java index 73e0f92a..c93ca4ce 100644 --- a/src/main/java/io/orkes/conductor/client/integration/azure/AzureSecretsManager.java +++ b/src/main/java/io/orkes/conductor/client/integration/azure/AzureSecretsManager.java @@ -14,6 +14,7 @@ import io.orkes.conductor.client.SecretsManager; +import com.azure.identity.DefaultAzureCredentialBuilder; import com.azure.security.keyvault.secrets.SecretClient; import com.azure.security.keyvault.secrets.SecretClientBuilder; @@ -45,7 +46,7 @@ private SecretClient createClient() { } return new SecretClientBuilder() .vaultUrl(String.format("https://%s.vault.azure.net/", keyVaultName)) - // .credential(new DefaultAzureCredentialBuilder().build()) + .credential(new DefaultAzureCredentialBuilder().build()) .buildClient(); } } From f906c96c7b087030da27d2628af11ba8b82f57d2 Mon Sep 17 00:00:00 2001 From: gardusig Date: Wed, 21 Sep 2022 12:54:40 -0300 Subject: [PATCH 11/24] Upgraded AWS ssm sdk version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d2ed3a42..ae879091 100644 --- a/build.gradle +++ b/build.gradle @@ -35,7 +35,7 @@ ext { spectator : '0.122.0', spock : '2.1-groovy-2.5', wiremock : '2.33.2', - awsSsm : '1.12.153', + awsSsm : '1.12.300', azureSsm : '4.2.3', azureIdentity: '1.3.7', ] From 7ce2c1c352b1315ce59f378232584e0093a5da1d Mon Sep 17 00:00:00 2001 From: gardusig Date: Wed, 21 Sep 2022 12:57:11 -0300 Subject: [PATCH 12/24] Upgraded groovy version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ae879091..aae15edc 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,7 @@ ext { commonsLang: '3.12.0', conductor : '3.8.1', eureka : '1.10.10', - groovy : '2.5.15', + groovy : '3.0.12', jackson : '2.11.4!!', jersey : '1.19.4', junit : '5.6.3', From 0ac13290adc66f74831e373e2af9d5c8a41202a3 Mon Sep 17 00:00:00 2001 From: gardusig Date: Wed, 21 Sep 2022 13:00:30 -0300 Subject: [PATCH 13/24] Upgraded gson version --- build.gradle | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index aae15edc..3793bb39 100644 --- a/build.gradle +++ b/build.gradle @@ -64,9 +64,8 @@ dependencies { implementation 'com.squareup.okhttp:okhttp:2.7.5' implementation 'com.squareup.okhttp:logging-interceptor:2.7.5' - implementation 'com.google.code.gson:gson:2.8.1' - - implementation 'io.gsonfire:gson-fire:1.8.3' + implementation 'com.google.code.gson:gson:2.9.0' + implementation 'io.gsonfire:gson-fire:1.8.5' implementation 'io.swagger.core.v3:swagger-annotations:2.0.0' From d1b25c792b09b0a2f830d9ebcfd209137bb737b2 Mon Sep 17 00:00:00 2001 From: gardusig Date: Wed, 21 Sep 2022 13:03:05 -0300 Subject: [PATCH 14/24] Upgraded junit version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 3793bb39..caaf2edf 100644 --- a/build.gradle +++ b/build.gradle @@ -30,7 +30,7 @@ ext { groovy : '3.0.12', jackson : '2.11.4!!', jersey : '1.19.4', - junit : '5.6.3', + junit : '5.9.0', slf4j : '1.7.36', spectator : '0.122.0', spock : '2.1-groovy-2.5', From aef1b9bec9f09cce5de54be086d07569fc994117 Mon Sep 17 00:00:00 2001 From: gardusig Date: Wed, 21 Sep 2022 13:08:11 -0300 Subject: [PATCH 15/24] Upgraded sl4j version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index caaf2edf..6a2198ea 100644 --- a/build.gradle +++ b/build.gradle @@ -31,7 +31,7 @@ ext { jackson : '2.11.4!!', jersey : '1.19.4', junit : '5.9.0', - slf4j : '1.7.36', + slf4j : '2.0.0', spectator : '0.122.0', spock : '2.1-groovy-2.5', wiremock : '2.33.2', From f247d05326f50dd7cc9d4046317f4fd115c8eb6d Mon Sep 17 00:00:00 2001 From: gardusig Date: Wed, 21 Sep 2022 13:18:55 -0300 Subject: [PATCH 16/24] Upgraded versions --- build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 6a2198ea..7cba734f 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ plugins { ext { versions = [ - awaitility : '3.1.6', + awaitility : '4.2.0', commonsLang: '3.12.0', conductor : '3.8.1', eureka : '1.10.10', @@ -32,7 +32,7 @@ ext { jersey : '1.19.4', junit : '5.9.0', slf4j : '2.0.0', - spectator : '0.122.0', + spectator : '1.3.7', spock : '2.1-groovy-2.5', wiremock : '2.33.2', awsSsm : '1.12.300', @@ -67,7 +67,7 @@ dependencies { implementation 'com.google.code.gson:gson:2.9.0' implementation 'io.gsonfire:gson-fire:1.8.5' - implementation 'io.swagger.core.v3:swagger-annotations:2.0.0' + implementation 'io.swagger.core.v3:swagger-annotations:2.2.2' implementation "org.apache.commons:commons-lang3:${versions.commonsLang}" From e09393df440f24ebc64fa7b4ffca28e2e332b411 Mon Sep 17 00:00:00 2001 From: gardusig Date: Wed, 21 Sep 2022 13:21:17 -0300 Subject: [PATCH 17/24] Upgraded eureka client --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 7cba734f..55cca5cc 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,7 @@ ext { awaitility : '4.2.0', commonsLang: '3.12.0', conductor : '3.8.1', - eureka : '1.10.10', + eureka : '1.10.17', groovy : '3.0.12', jackson : '2.11.4!!', jersey : '1.19.4', From bb2084fcb304d21dde8773ea79002bb2b5db144a Mon Sep 17 00:00:00 2001 From: gardusig Date: Wed, 21 Sep 2022 13:26:39 -0300 Subject: [PATCH 18/24] Upgraded spock dependency --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 55cca5cc..07d3b2b6 100644 --- a/build.gradle +++ b/build.gradle @@ -33,7 +33,7 @@ ext { junit : '5.9.0', slf4j : '2.0.0', spectator : '1.3.7', - spock : '2.1-groovy-2.5', + spock : '2.2-groovy-2.5', wiremock : '2.33.2', awsSsm : '1.12.300', azureSsm : '4.2.3', From 889b28ad1941504709e4c5160b33ef6f951206c5 Mon Sep 17 00:00:00 2001 From: gardusig Date: Wed, 21 Sep 2022 13:28:42 -0300 Subject: [PATCH 19/24] Upgraded threetenbp dependency --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 07d3b2b6..a3e68953 100644 --- a/build.gradle +++ b/build.gradle @@ -71,7 +71,7 @@ dependencies { implementation "org.apache.commons:commons-lang3:${versions.commonsLang}" - implementation 'org.threeten:threetenbp:1.3.5' + implementation 'org.threeten:threetenbp:1.6.1' //Integrations implementation "com.amazonaws:aws-java-sdk-ssm:${versions.awsSsm}" From 072dc13965d93b11b72112dd1b59e444ba9fd946 Mon Sep 17 00:00:00 2001 From: gardusig Date: Thu, 22 Sep 2022 00:33:04 -0300 Subject: [PATCH 20/24] Added Secrets manager and related tests --- build.gradle | 3 +- .../io/orkes/conductor/client/ApiClient.java | 70 ++++++++----------- .../manager}/aws/AwsSecretsManager.java | 2 +- .../manager}/azure/AzureSecretsManager.java | 14 ++-- .../secrets/manager/SecretsManagerTests.java | 65 +++++++++++++++++ .../orkes/conductor/client/util/ApiUtil.java | 20 ++++-- 6 files changed, 118 insertions(+), 56 deletions(-) rename src/main/java/io/orkes/conductor/client/{integration => secrets/manager}/aws/AwsSecretsManager.java (98%) rename src/main/java/io/orkes/conductor/client/{integration => secrets/manager}/azure/AzureSecretsManager.java (83%) create mode 100644 src/test/java/io/orkes/conductor/client/secrets/manager/SecretsManagerTests.java diff --git a/build.gradle b/build.gradle index a3e68953..40970d12 100644 --- a/build.gradle +++ b/build.gradle @@ -31,7 +31,7 @@ ext { jackson : '2.11.4!!', jersey : '1.19.4', junit : '5.9.0', - slf4j : '2.0.0', + slf4j : '1.7.36', spectator : '1.3.7', spock : '2.2-groovy-2.5', wiremock : '2.33.2', @@ -88,6 +88,7 @@ dependencies { testImplementation("com.github.tomakehurst:wiremock-jre8:${versions.wiremock}") { exclude group: 'com.fasterxml.jackson' } + testImplementation 'org.mockito:mockito-all:1.10.19' } repositories { diff --git a/src/main/java/io/orkes/conductor/client/ApiClient.java b/src/main/java/io/orkes/conductor/client/ApiClient.java index 3f1b7b0c..c68be7cf 100644 --- a/src/main/java/io/orkes/conductor/client/ApiClient.java +++ b/src/main/java/io/orkes/conductor/client/ApiClient.java @@ -79,7 +79,6 @@ public class ApiClient { private String keySecret; private String token; - private Object tokenMutex; private SecretsManager secretsManager; private String ssmKeyPath; @@ -95,19 +94,10 @@ public ApiClient() { public ApiClient(String basePath) { this.basePath = basePath; - httpClient = new OkHttpClient(); - verifyingSsl = true; - json = new JSON(); - authentications = new HashMap(); - - this.keyId = null; - this.keySecret = null; - this.token = null; - this.tokenMutex = new Object(); } public ApiClient( @@ -116,6 +106,11 @@ public ApiClient( this.secretsManager = secretsManager; this.ssmKeyPath = keyPath; this.ssmSecretPath = secretPath; + try { + this.refreshToken(); + } catch (Exception e) { + LOGGER.warn("Failed to set authentication token. Reason: " + e.getMessage()); + } } public ApiClient(String basePath, String keyId, String keySecret) { @@ -1227,47 +1222,38 @@ private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityExcepti } } - public void refreshToken() throws Exception { + public synchronized String getToken() { + return this.token; + } + + synchronized void refreshToken() throws Exception { if (this.getToken() != null) { return; } - synchronized (this.tokenMutex) { - if (secretsManager != null) { - keyId = secretsManager.getSecret(this.ssmKeyPath); - keySecret = secretsManager.getSecret(this.ssmSecretPath); - } - if (this.keyId == null || this.keySecret == null) { - throw new Exception( - "KeyId and KeySecret must be set in order to get an authentication token"); - } - GenerateTokenRequest generateTokenRequest = - new GenerateTokenRequest().keyId(this.keyId).keySecret(this.keySecret); - Map response = - TokenResourceApi.generateTokenWithHttpInfo(this, generateTokenRequest) - .getData(); - final String token = response.get("token"); - this.setToken(token); + if (secretsManager != null) { + keyId = secretsManager.getSecret(this.ssmKeyPath); + keySecret = secretsManager.getSecret(this.ssmSecretPath); } - } - - public String getToken() { - synchronized (this.tokenMutex) { - return this.token; + if (this.keyId == null || this.keySecret == null) { + throw new Exception( + "KeyId and KeySecret must be set in order to get an authentication token"); } + GenerateTokenRequest generateTokenRequest = + new GenerateTokenRequest().keyId(this.keyId).keySecret(this.keySecret); + Map response = + TokenResourceApi.generateTokenWithHttpInfo(this, generateTokenRequest).getData(); + final String token = response.get("token"); + this.setToken(token); } - void setToken(String token) { - synchronized (this.tokenMutex) { - this.token = token; - } + synchronized void setToken(String token) { + this.token = token; this.setApiKeyHeader(token); } - void setApiKeyHeader(String token) { - synchronized (this.tokenMutex) { - ApiKeyAuth apiKeyAuth = new ApiKeyAuth("header", "X-Authorization"); - apiKeyAuth.setApiKey(token); - authentications.put("api_key", apiKeyAuth); - } + synchronized void setApiKeyHeader(String token) { + ApiKeyAuth apiKeyAuth = new ApiKeyAuth("header", "X-Authorization"); + apiKeyAuth.setApiKey(token); + authentications.put("api_key", apiKeyAuth); } } diff --git a/src/main/java/io/orkes/conductor/client/integration/aws/AwsSecretsManager.java b/src/main/java/io/orkes/conductor/client/secrets/manager/aws/AwsSecretsManager.java similarity index 98% rename from src/main/java/io/orkes/conductor/client/integration/aws/AwsSecretsManager.java rename to src/main/java/io/orkes/conductor/client/secrets/manager/aws/AwsSecretsManager.java index b9766b33..ee513df8 100644 --- a/src/main/java/io/orkes/conductor/client/integration/aws/AwsSecretsManager.java +++ b/src/main/java/io/orkes/conductor/client/secrets/manager/aws/AwsSecretsManager.java @@ -10,7 +10,7 @@ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ -package io.orkes.conductor.client.integration.aws; +package io.orkes.conductor.client.secrets.manager.aws; import io.orkes.conductor.client.SecretsManager; diff --git a/src/main/java/io/orkes/conductor/client/integration/azure/AzureSecretsManager.java b/src/main/java/io/orkes/conductor/client/secrets/manager/azure/AzureSecretsManager.java similarity index 83% rename from src/main/java/io/orkes/conductor/client/integration/azure/AzureSecretsManager.java rename to src/main/java/io/orkes/conductor/client/secrets/manager/azure/AzureSecretsManager.java index c93ca4ce..83ac9b2e 100644 --- a/src/main/java/io/orkes/conductor/client/integration/azure/AzureSecretsManager.java +++ b/src/main/java/io/orkes/conductor/client/secrets/manager/azure/AzureSecretsManager.java @@ -10,7 +10,7 @@ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ -package io.orkes.conductor.client.integration.azure; +package io.orkes.conductor.client.secrets.manager.azure; import io.orkes.conductor.client.SecretsManager; @@ -19,6 +19,8 @@ import com.azure.security.keyvault.secrets.SecretClientBuilder; public class AzureSecretsManager implements SecretsManager { + private static final String PROPERTY_NAME = "azure.keyvault.name"; + private final SecretClient client; public AzureSecretsManager() { @@ -26,8 +28,8 @@ public AzureSecretsManager() { } @Override - public String getSecret(String key) { - return getSecretFromKeyVault(key); + public String getSecret(String keyPath) { + return client.getSecret(keyPath).getValue(); } @Override @@ -35,12 +37,8 @@ public void storeSecret(String key, String secret) { client.setSecret(key, secret); } - private String getSecretFromKeyVault(String key) { - return client.getSecret(key).getValue(); - } - private SecretClient createClient() { - String keyVaultName = getProperty("azure.keyvault.name"); + String keyVaultName = getProperty(PROPERTY_NAME); if (keyVaultName == null) { throw new RuntimeException("Key Vault name is not specified, cannot create client"); } diff --git a/src/test/java/io/orkes/conductor/client/secrets/manager/SecretsManagerTests.java b/src/test/java/io/orkes/conductor/client/secrets/manager/SecretsManagerTests.java new file mode 100644 index 00000000..0c875d23 --- /dev/null +++ b/src/test/java/io/orkes/conductor/client/secrets/manager/SecretsManagerTests.java @@ -0,0 +1,65 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed 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 CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.secrets.manager; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +import io.orkes.conductor.client.ApiClient; +import io.orkes.conductor.client.SecretsManager; +import io.orkes.conductor.client.secrets.manager.aws.AwsSecretsManager; +import io.orkes.conductor.client.secrets.manager.azure.AzureSecretsManager; +import io.orkes.conductor.client.util.ApiUtil; + +import static org.junit.jupiter.api.Assertions.assertIterableEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class SecretsManagerTests { + private static final String KEY_PATH = "ABCDJSFNFJQNW"; + private static final String SECRET_PATH = "NASDQJWDNQW"; + + private final ApiClient apiClient; + + public SecretsManagerTests() { + apiClient = ApiUtil.getApiClientWithCredentials(); + } + + @Test + void testAwsSecretsManager() { + AwsSecretsManager awsSecretsManager = mock(AwsSecretsManager.class); + testSecretManager(awsSecretsManager); + } + + @Test + void testAzureSecretsManager() { + AzureSecretsManager azureSecretsManager = mock(AzureSecretsManager.class); + testSecretManager(azureSecretsManager); + } + + void testSecretManager(SecretsManager secretsManager) { + when(secretsManager.getSecret(KEY_PATH)).thenReturn(ApiUtil.getKeyId()); + when(secretsManager.getSecret(SECRET_PATH)).thenReturn(ApiUtil.getKeySecret()); + ApiClient customApiClient = + new ApiClient(ApiUtil.getBasePath(), secretsManager, KEY_PATH, SECRET_PATH); + List expected = getCommonTokenPrefix(this.apiClient.getToken()); + List received = getCommonTokenPrefix(customApiClient.getToken()); + assertIterableEquals(expected, received); + } + + List getCommonTokenPrefix(String token) { + String[] splitted = token.split("[.]", 0); + return List.of(splitted[0]); + } +} diff --git a/src/test/java/io/orkes/conductor/client/util/ApiUtil.java b/src/test/java/io/orkes/conductor/client/util/ApiUtil.java index 71f65f74..6292d35d 100644 --- a/src/test/java/io/orkes/conductor/client/util/ApiUtil.java +++ b/src/test/java/io/orkes/conductor/client/util/ApiUtil.java @@ -28,15 +28,27 @@ public static OrkesClients getOrkesClient() { } public static ApiClient getApiClientWithCredentials() { - String basePath = getEnv(ENV_ROOT_URI); + String basePath = getBasePath(); assertNotNull(basePath, ENV_ROOT_URI + " env not set"); - String keyId = getEnv(ENV_KEY_ID); + String keyId = getKeyId(); assertNotNull(keyId, ENV_KEY_ID + " env not set"); - String keySecret = getEnv(ENV_SECRET); - assertNotNull(keyId, ENV_SECRET + " env not set"); + String keySecret = getKeySecret(); + assertNotNull(keySecret, ENV_SECRET + " env not set"); return new ApiClient(basePath, keyId, keySecret); } + public static String getBasePath() { + return getEnv(ENV_ROOT_URI); + } + + public static String getKeyId() { + return getEnv(ENV_KEY_ID); + } + + public static String getKeySecret() { + return getEnv(ENV_SECRET); + } + static String getEnv(String key) { return System.getenv(key); } From 0d4bd0a7d973e14dec14fa9a1f590895d726179d Mon Sep 17 00:00:00 2001 From: gardusig Date: Thu, 22 Sep 2022 22:27:13 -0300 Subject: [PATCH 21/24] Added aws ssm tests with localstack --- build.gradle | 3 + .../manager/aws/AwsSecretsManager.java | 4 ++ .../secrets/manager/SecretsManagerTests.java | 68 ++++++++++++++----- .../secrets/manager/util/AWSContainer.java | 59 ++++++++++++++++ 4 files changed, 116 insertions(+), 18 deletions(-) create mode 100644 src/test/java/io/orkes/conductor/client/secrets/manager/util/AWSContainer.java diff --git a/build.gradle b/build.gradle index 40970d12..ea126437 100644 --- a/build.gradle +++ b/build.gradle @@ -89,6 +89,9 @@ dependencies { exclude group: 'com.fasterxml.jackson' } testImplementation 'org.mockito:mockito-all:1.10.19' + testImplementation 'org.testcontainers:localstack:1.17.1' + testImplementation 'org.testcontainers:testcontainers:1.17.1' + testImplementation 'com.amazonaws:aws-java-sdk-core:1.12.138' } repositories { diff --git a/src/main/java/io/orkes/conductor/client/secrets/manager/aws/AwsSecretsManager.java b/src/main/java/io/orkes/conductor/client/secrets/manager/aws/AwsSecretsManager.java index ee513df8..32382d1f 100644 --- a/src/main/java/io/orkes/conductor/client/secrets/manager/aws/AwsSecretsManager.java +++ b/src/main/java/io/orkes/conductor/client/secrets/manager/aws/AwsSecretsManager.java @@ -28,6 +28,10 @@ public class AwsSecretsManager implements SecretsManager { private final AWSSimpleSystemsManagement client; + public AwsSecretsManager(AWSSimpleSystemsManagement awsSimpleSystemsManagement) { + this.client = awsSimpleSystemsManagement; + } + public AwsSecretsManager(AWSCredentialsProvider credentialsProvider, String region) { this.client = createClient(credentialsProvider, region); } diff --git a/src/test/java/io/orkes/conductor/client/secrets/manager/SecretsManagerTests.java b/src/test/java/io/orkes/conductor/client/secrets/manager/SecretsManagerTests.java index 0c875d23..ada0ad9e 100644 --- a/src/test/java/io/orkes/conductor/client/secrets/manager/SecretsManagerTests.java +++ b/src/test/java/io/orkes/conductor/client/secrets/manager/SecretsManagerTests.java @@ -12,54 +12,86 @@ */ package io.orkes.conductor.client.secrets.manager; -import java.util.List; - import org.junit.jupiter.api.Test; +import org.testcontainers.containers.localstack.LocalStackContainer; import io.orkes.conductor.client.ApiClient; import io.orkes.conductor.client.SecretsManager; import io.orkes.conductor.client.secrets.manager.aws.AwsSecretsManager; import io.orkes.conductor.client.secrets.manager.azure.AzureSecretsManager; +import io.orkes.conductor.client.secrets.manager.util.AWSContainer; import io.orkes.conductor.client.util.ApiUtil; -import static org.junit.jupiter.api.Assertions.assertIterableEquals; +import com.amazonaws.services.simplesystemsmanagement.AWSSimpleSystemsManagement; +import com.amazonaws.services.simplesystemsmanagement.AWSSimpleSystemsManagementClientBuilder; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class SecretsManagerTests { - private static final String KEY_PATH = "ABCDJSFNFJQNW"; - private static final String SECRET_PATH = "NASDQJWDNQW"; - - private final ApiClient apiClient; + private static final String KEY_PATH = "path/to/key"; + private static final String SECRET_PATH = "path/to/secret"; - public SecretsManagerTests() { - apiClient = ApiUtil.getApiClientWithCredentials(); + @Test + void testAwsSecretsManagerWithoutCredentials() throws Exception { + AWSContainer awsContainer = new AWSContainer(null, LocalStackContainer.Service.SSM); + awsContainer.start(); + AwsSecretsManager awsSecretsManager = getAwsSecretsManager(awsContainer); + ApiClient customApiClient = + new ApiClient(ApiUtil.getBasePath(), awsSecretsManager, KEY_PATH, SECRET_PATH); + assertNull(customApiClient.getToken()); + awsContainer.close(); } @Test - void testAwsSecretsManager() { - AwsSecretsManager awsSecretsManager = mock(AwsSecretsManager.class); + void testAwsSecretsManager() throws Exception { + AWSContainer awsContainer = new AWSContainer(null, LocalStackContainer.Service.SSM); + awsContainer.start(); + AwsSecretsManager awsSecretsManager = getAwsSecretsManager(awsContainer); + awsSecretsManager.storeSecret(KEY_PATH, ApiUtil.getKeyId()); + awsSecretsManager.storeSecret(SECRET_PATH, ApiUtil.getKeySecret()); testSecretManager(awsSecretsManager); + awsContainer.close(); } @Test void testAzureSecretsManager() { AzureSecretsManager azureSecretsManager = mock(AzureSecretsManager.class); + when(azureSecretsManager.getSecret(KEY_PATH)).thenReturn(ApiUtil.getKeyId()); + when(azureSecretsManager.getSecret(SECRET_PATH)).thenReturn(ApiUtil.getKeySecret()); testSecretManager(azureSecretsManager); } void testSecretManager(SecretsManager secretsManager) { - when(secretsManager.getSecret(KEY_PATH)).thenReturn(ApiUtil.getKeyId()); - when(secretsManager.getSecret(SECRET_PATH)).thenReturn(ApiUtil.getKeySecret()); + ApiClient apiClient = ApiUtil.getApiClientWithCredentials(); ApiClient customApiClient = new ApiClient(ApiUtil.getBasePath(), secretsManager, KEY_PATH, SECRET_PATH); - List expected = getCommonTokenPrefix(this.apiClient.getToken()); - List received = getCommonTokenPrefix(customApiClient.getToken()); - assertIterableEquals(expected, received); + assertEquals( + getCommonTokenPrefix(apiClient.getToken()), + getCommonTokenPrefix(customApiClient.getToken())); } - List getCommonTokenPrefix(String token) { + String getCommonTokenPrefix(String token) { + if (token == null) { + return null; + } String[] splitted = token.split("[.]", 0); - return List.of(splitted[0]); + return splitted[0]; + } + + AwsSecretsManager getAwsSecretsManager(AWSContainer awsContainer) { + AWSSimpleSystemsManagement awsSimpleSystemsManagement = + getAWSSimpleSystemsManagement(awsContainer); + return new AwsSecretsManager(awsSimpleSystemsManagement); + } + + AWSSimpleSystemsManagement getAWSSimpleSystemsManagement(AWSContainer awsContainer) { + return AWSSimpleSystemsManagementClientBuilder.standard() + .withEndpointConfiguration( + awsContainer.getEndpointConfiguration(LocalStackContainer.Service.SSM)) + .withCredentials(awsContainer.getDefaultCredentialsProvider()) + .build(); } } diff --git a/src/test/java/io/orkes/conductor/client/secrets/manager/util/AWSContainer.java b/src/test/java/io/orkes/conductor/client/secrets/manager/util/AWSContainer.java new file mode 100644 index 00000000..5b080301 --- /dev/null +++ b/src/test/java/io/orkes/conductor/client/secrets/manager/util/AWSContainer.java @@ -0,0 +1,59 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed 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 CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.secrets.manager.util; + +import java.time.Duration; +import java.util.Objects; + +import org.testcontainers.containers.Network; +import org.testcontainers.containers.localstack.LocalStackContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.utility.DockerImageName; + +public class AWSContainer extends LocalStackContainer { + private static final String NETWORK_ALIAS = "localstack"; + private static final String DOCKER_IMAGE_NAME = "localstack/localstack:0.14.2"; + + private final LocalStackContainer.Service[] services; + + public AWSContainer(Network network, LocalStackContainer.Service... services) { + super(DockerImageName.parse(DOCKER_IMAGE_NAME)); + this.services = services; + if (Objects.nonNull(network)) { + withNetwork(network); + withNetworkAliases(NETWORK_ALIAS); + } + withServices(services); + waitingFor(Wait.forLogMessage(".*Ready\\.\n", 1).withStartupTimeout(Duration.ofMinutes(3))); + } + + public void start() { + super.start(); + setProperties(services); + } + + void setProperties(LocalStackContainer.Service... services) { + System.setProperty("aws.region", getRegion()); + System.setProperty("aws.accessKeyId", getAccessKey()); + System.setProperty("aws.secretAccessKey", getSecretKey()); + for (LocalStackContainer.Service service : services) { + setServiceProperty(service); + } + } + + void setServiceProperty(LocalStackContainer.Service service) { + String propertyKey = String.format("aws.%s.endpoint", service.getName()); + String propertyValue = this.getEndpointOverride(service).toString(); + System.setProperty(propertyKey, propertyValue); + } +} From 7225d7e6235a24238d49804561717648a0c018dd Mon Sep 17 00:00:00 2001 From: gardusig Date: Fri, 23 Sep 2022 12:22:21 -0300 Subject: [PATCH 22/24] Improved secrets manager integration tests with review comments --- .../manager/AwsSecretsManagerTests.java | 54 +++++++++++ .../manager/AzureSecretsManagerTests.java | 40 ++++++++ .../secrets/manager/SecretsManagerTests.java | 97 ------------------- .../orkes/conductor/client/util/Commons.java | 2 + .../secrets/manager}/AWSContainer.java | 13 ++- .../secrets/manager/SecretsManagerUtil.java | 43 ++++++++ .../secrets/manager/TestWithAwsContainer.java | 25 +++++ 7 files changed, 176 insertions(+), 98 deletions(-) create mode 100644 src/test/java/io/orkes/conductor/client/secrets/manager/AwsSecretsManagerTests.java create mode 100644 src/test/java/io/orkes/conductor/client/secrets/manager/AzureSecretsManagerTests.java delete mode 100644 src/test/java/io/orkes/conductor/client/secrets/manager/SecretsManagerTests.java rename src/test/java/io/orkes/conductor/client/{secrets/manager/util => util/secrets/manager}/AWSContainer.java (79%) create mode 100644 src/test/java/io/orkes/conductor/client/util/secrets/manager/SecretsManagerUtil.java create mode 100644 src/test/java/io/orkes/conductor/client/util/secrets/manager/TestWithAwsContainer.java diff --git a/src/test/java/io/orkes/conductor/client/secrets/manager/AwsSecretsManagerTests.java b/src/test/java/io/orkes/conductor/client/secrets/manager/AwsSecretsManagerTests.java new file mode 100644 index 00000000..2268dcb0 --- /dev/null +++ b/src/test/java/io/orkes/conductor/client/secrets/manager/AwsSecretsManagerTests.java @@ -0,0 +1,54 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed 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 CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.secrets.manager; + +import org.junit.Test; + +import io.orkes.conductor.client.ApiClient; +import io.orkes.conductor.client.secrets.manager.aws.AwsSecretsManager; +import io.orkes.conductor.client.util.ApiUtil; +import io.orkes.conductor.client.util.Commons; +import io.orkes.conductor.client.util.secrets.manager.SecretsManagerUtil; +import io.orkes.conductor.client.util.secrets.manager.TestWithAwsContainer; + +import static org.junit.jupiter.api.Assertions.assertNull; + +public class AwsSecretsManagerTests extends TestWithAwsContainer { + @Test + void testAwsSecretsManagerWithoutCredentials() throws Exception { + awsContainer.start(); + AwsSecretsManager awsSecretsManager = getAwsSecretsManager(); + ApiClient customApiClient = + new ApiClient( + ApiUtil.getBasePath(), + awsSecretsManager, + "random_key_path", + "random_secret_path"); + assertNull(customApiClient.getToken()); + awsContainer.close(); + } + + @Test + void testAwsSecretsManager() throws Exception { + awsContainer.start(); + AwsSecretsManager awsSecretsManager = getAwsSecretsManager(); + awsSecretsManager.storeSecret(Commons.SECRET_MANAGER_KEY_PATH, ApiUtil.getKeyId()); + awsSecretsManager.storeSecret(Commons.SECRET_MANAGER_SECRET_PATH, ApiUtil.getKeySecret()); + SecretsManagerUtil.validateApiClientWithSecretsManager(awsSecretsManager); + awsContainer.close(); + } + + AwsSecretsManager getAwsSecretsManager() { + return new AwsSecretsManager(awsContainer.getAWSSimpleSystemsManagement()); + } +} diff --git a/src/test/java/io/orkes/conductor/client/secrets/manager/AzureSecretsManagerTests.java b/src/test/java/io/orkes/conductor/client/secrets/manager/AzureSecretsManagerTests.java new file mode 100644 index 00000000..9ed0d195 --- /dev/null +++ b/src/test/java/io/orkes/conductor/client/secrets/manager/AzureSecretsManagerTests.java @@ -0,0 +1,40 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed 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 CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.secrets.manager; + +import org.junit.jupiter.api.Test; + +import io.orkes.conductor.client.secrets.manager.azure.AzureSecretsManager; +import io.orkes.conductor.client.util.ApiUtil; +import io.orkes.conductor.client.util.Commons; +import io.orkes.conductor.client.util.secrets.manager.SecretsManagerUtil; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class AzureSecretsManagerTests { + @Test + void testAzureSecretsManager() { + AzureSecretsManager azureSecretsManager = getAzureSecretsManager(); + SecretsManagerUtil.validateApiClientWithSecretsManager(azureSecretsManager); + } + + AzureSecretsManager getAzureSecretsManager() { + AzureSecretsManager azureSecretsManager = mock(AzureSecretsManager.class); + when(azureSecretsManager.getSecret(Commons.SECRET_MANAGER_KEY_PATH)) + .thenReturn(ApiUtil.getKeyId()); + when(azureSecretsManager.getSecret(Commons.SECRET_MANAGER_SECRET_PATH)) + .thenReturn(ApiUtil.getKeySecret()); + return azureSecretsManager; + } +} diff --git a/src/test/java/io/orkes/conductor/client/secrets/manager/SecretsManagerTests.java b/src/test/java/io/orkes/conductor/client/secrets/manager/SecretsManagerTests.java deleted file mode 100644 index ada0ad9e..00000000 --- a/src/test/java/io/orkes/conductor/client/secrets/manager/SecretsManagerTests.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2022 Orkes, Inc. - *

- * Licensed 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 CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ -package io.orkes.conductor.client.secrets.manager; - -import org.junit.jupiter.api.Test; -import org.testcontainers.containers.localstack.LocalStackContainer; - -import io.orkes.conductor.client.ApiClient; -import io.orkes.conductor.client.SecretsManager; -import io.orkes.conductor.client.secrets.manager.aws.AwsSecretsManager; -import io.orkes.conductor.client.secrets.manager.azure.AzureSecretsManager; -import io.orkes.conductor.client.secrets.manager.util.AWSContainer; -import io.orkes.conductor.client.util.ApiUtil; - -import com.amazonaws.services.simplesystemsmanagement.AWSSimpleSystemsManagement; -import com.amazonaws.services.simplesystemsmanagement.AWSSimpleSystemsManagementClientBuilder; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class SecretsManagerTests { - private static final String KEY_PATH = "path/to/key"; - private static final String SECRET_PATH = "path/to/secret"; - - @Test - void testAwsSecretsManagerWithoutCredentials() throws Exception { - AWSContainer awsContainer = new AWSContainer(null, LocalStackContainer.Service.SSM); - awsContainer.start(); - AwsSecretsManager awsSecretsManager = getAwsSecretsManager(awsContainer); - ApiClient customApiClient = - new ApiClient(ApiUtil.getBasePath(), awsSecretsManager, KEY_PATH, SECRET_PATH); - assertNull(customApiClient.getToken()); - awsContainer.close(); - } - - @Test - void testAwsSecretsManager() throws Exception { - AWSContainer awsContainer = new AWSContainer(null, LocalStackContainer.Service.SSM); - awsContainer.start(); - AwsSecretsManager awsSecretsManager = getAwsSecretsManager(awsContainer); - awsSecretsManager.storeSecret(KEY_PATH, ApiUtil.getKeyId()); - awsSecretsManager.storeSecret(SECRET_PATH, ApiUtil.getKeySecret()); - testSecretManager(awsSecretsManager); - awsContainer.close(); - } - - @Test - void testAzureSecretsManager() { - AzureSecretsManager azureSecretsManager = mock(AzureSecretsManager.class); - when(azureSecretsManager.getSecret(KEY_PATH)).thenReturn(ApiUtil.getKeyId()); - when(azureSecretsManager.getSecret(SECRET_PATH)).thenReturn(ApiUtil.getKeySecret()); - testSecretManager(azureSecretsManager); - } - - void testSecretManager(SecretsManager secretsManager) { - ApiClient apiClient = ApiUtil.getApiClientWithCredentials(); - ApiClient customApiClient = - new ApiClient(ApiUtil.getBasePath(), secretsManager, KEY_PATH, SECRET_PATH); - assertEquals( - getCommonTokenPrefix(apiClient.getToken()), - getCommonTokenPrefix(customApiClient.getToken())); - } - - String getCommonTokenPrefix(String token) { - if (token == null) { - return null; - } - String[] splitted = token.split("[.]", 0); - return splitted[0]; - } - - AwsSecretsManager getAwsSecretsManager(AWSContainer awsContainer) { - AWSSimpleSystemsManagement awsSimpleSystemsManagement = - getAWSSimpleSystemsManagement(awsContainer); - return new AwsSecretsManager(awsSimpleSystemsManagement); - } - - AWSSimpleSystemsManagement getAWSSimpleSystemsManagement(AWSContainer awsContainer) { - return AWSSimpleSystemsManagementClientBuilder.standard() - .withEndpointConfiguration( - awsContainer.getEndpointConfiguration(LocalStackContainer.Service.SSM)) - .withCredentials(awsContainer.getDefaultCredentialsProvider()) - .build(); - } -} diff --git a/src/test/java/io/orkes/conductor/client/util/Commons.java b/src/test/java/io/orkes/conductor/client/util/Commons.java index a9883a15..91edcb14 100644 --- a/src/test/java/io/orkes/conductor/client/util/Commons.java +++ b/src/test/java/io/orkes/conductor/client/util/Commons.java @@ -27,6 +27,8 @@ public class Commons { public static String USER_NAME = "Orkes User"; public static String USER_EMAIL = "user@orkes.io"; public static String APPLICATION_ID = "46f0bf10-b59d-4fbd-a053-935307c8cb86"; + public static final String SECRET_MANAGER_KEY_PATH = "path/to/key"; + public static final String SECRET_MANAGER_SECRET_PATH = "path/to/secret"; public static TagObject getTagObject() { TagObject tagObject = new TagObject(); diff --git a/src/test/java/io/orkes/conductor/client/secrets/manager/util/AWSContainer.java b/src/test/java/io/orkes/conductor/client/util/secrets/manager/AWSContainer.java similarity index 79% rename from src/test/java/io/orkes/conductor/client/secrets/manager/util/AWSContainer.java rename to src/test/java/io/orkes/conductor/client/util/secrets/manager/AWSContainer.java index 5b080301..3c02248f 100644 --- a/src/test/java/io/orkes/conductor/client/secrets/manager/util/AWSContainer.java +++ b/src/test/java/io/orkes/conductor/client/util/secrets/manager/AWSContainer.java @@ -10,7 +10,7 @@ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ -package io.orkes.conductor.client.secrets.manager.util; +package io.orkes.conductor.client.util.secrets.manager; import java.time.Duration; import java.util.Objects; @@ -20,6 +20,9 @@ import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.utility.DockerImageName; +import com.amazonaws.services.simplesystemsmanagement.AWSSimpleSystemsManagement; +import com.amazonaws.services.simplesystemsmanagement.AWSSimpleSystemsManagementClientBuilder; + public class AWSContainer extends LocalStackContainer { private static final String NETWORK_ALIAS = "localstack"; private static final String DOCKER_IMAGE_NAME = "localstack/localstack:0.14.2"; @@ -42,6 +45,14 @@ public void start() { setProperties(services); } + public AWSSimpleSystemsManagement getAWSSimpleSystemsManagement() { + return AWSSimpleSystemsManagementClientBuilder.standard() + .withEndpointConfiguration( + getEndpointConfiguration(LocalStackContainer.Service.SSM)) + .withCredentials(getDefaultCredentialsProvider()) + .build(); + } + void setProperties(LocalStackContainer.Service... services) { System.setProperty("aws.region", getRegion()); System.setProperty("aws.accessKeyId", getAccessKey()); diff --git a/src/test/java/io/orkes/conductor/client/util/secrets/manager/SecretsManagerUtil.java b/src/test/java/io/orkes/conductor/client/util/secrets/manager/SecretsManagerUtil.java new file mode 100644 index 00000000..42678b65 --- /dev/null +++ b/src/test/java/io/orkes/conductor/client/util/secrets/manager/SecretsManagerUtil.java @@ -0,0 +1,43 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed 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 CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.util.secrets.manager; + +import io.orkes.conductor.client.ApiClient; +import io.orkes.conductor.client.SecretsManager; +import io.orkes.conductor.client.util.ApiUtil; +import io.orkes.conductor.client.util.Commons; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public final class SecretsManagerUtil { + public static void validateApiClientWithSecretsManager(SecretsManager secretsManager) { + ApiClient apiClient = ApiUtil.getApiClientWithCredentials(); + ApiClient customApiClient = + new ApiClient( + ApiUtil.getBasePath(), + secretsManager, + Commons.SECRET_MANAGER_KEY_PATH, + Commons.SECRET_MANAGER_SECRET_PATH); + assertEquals( + getCommonTokenPrefix(apiClient.getToken()), + getCommonTokenPrefix(customApiClient.getToken())); + } + + private static String getCommonTokenPrefix(String token) { + if (token == null) { + return null; + } + String[] splitted = token.split("[.]", 0); + return splitted[0]; + } +} diff --git a/src/test/java/io/orkes/conductor/client/util/secrets/manager/TestWithAwsContainer.java b/src/test/java/io/orkes/conductor/client/util/secrets/manager/TestWithAwsContainer.java new file mode 100644 index 00000000..53cff2bc --- /dev/null +++ b/src/test/java/io/orkes/conductor/client/util/secrets/manager/TestWithAwsContainer.java @@ -0,0 +1,25 @@ +/* + * Copyright 2022 Orkes, Inc. + *

+ * Licensed 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 CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.orkes.conductor.client.util.secrets.manager; + +import org.junit.jupiter.api.BeforeAll; +import org.testcontainers.containers.localstack.LocalStackContainer; + +public abstract class TestWithAwsContainer { + protected static AWSContainer awsContainer; + + @BeforeAll + static void setupAwsContainer() { + awsContainer = new AWSContainer(null, LocalStackContainer.Service.SSM); + } +} From 184f54b1fc764fbc07d9b5acc83927a93ae38413 Mon Sep 17 00:00:00 2001 From: gardusig Date: Fri, 23 Sep 2022 16:20:06 -0300 Subject: [PATCH 23/24] Addressed review comments for integration tests --- .../secrets/manager/AwsSecretsManagerTests.java | 8 ++------ .../util/secrets/manager/TestWithAwsContainer.java | 12 ++++++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/test/java/io/orkes/conductor/client/secrets/manager/AwsSecretsManagerTests.java b/src/test/java/io/orkes/conductor/client/secrets/manager/AwsSecretsManagerTests.java index 2268dcb0..3bc2b950 100644 --- a/src/test/java/io/orkes/conductor/client/secrets/manager/AwsSecretsManagerTests.java +++ b/src/test/java/io/orkes/conductor/client/secrets/manager/AwsSecretsManagerTests.java @@ -26,26 +26,22 @@ public class AwsSecretsManagerTests extends TestWithAwsContainer { @Test void testAwsSecretsManagerWithoutCredentials() throws Exception { - awsContainer.start(); AwsSecretsManager awsSecretsManager = getAwsSecretsManager(); ApiClient customApiClient = new ApiClient( ApiUtil.getBasePath(), awsSecretsManager, - "random_key_path", - "random_secret_path"); + Commons.SECRET_MANAGER_KEY_PATH, + Commons.SECRET_MANAGER_SECRET_PATH); assertNull(customApiClient.getToken()); - awsContainer.close(); } @Test void testAwsSecretsManager() throws Exception { - awsContainer.start(); AwsSecretsManager awsSecretsManager = getAwsSecretsManager(); awsSecretsManager.storeSecret(Commons.SECRET_MANAGER_KEY_PATH, ApiUtil.getKeyId()); awsSecretsManager.storeSecret(Commons.SECRET_MANAGER_SECRET_PATH, ApiUtil.getKeySecret()); SecretsManagerUtil.validateApiClientWithSecretsManager(awsSecretsManager); - awsContainer.close(); } AwsSecretsManager getAwsSecretsManager() { diff --git a/src/test/java/io/orkes/conductor/client/util/secrets/manager/TestWithAwsContainer.java b/src/test/java/io/orkes/conductor/client/util/secrets/manager/TestWithAwsContainer.java index 53cff2bc..cf1ef3b2 100644 --- a/src/test/java/io/orkes/conductor/client/util/secrets/manager/TestWithAwsContainer.java +++ b/src/test/java/io/orkes/conductor/client/util/secrets/manager/TestWithAwsContainer.java @@ -12,7 +12,9 @@ */ package io.orkes.conductor.client.util.secrets.manager; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.testcontainers.containers.localstack.LocalStackContainer; public abstract class TestWithAwsContainer { @@ -22,4 +24,14 @@ public abstract class TestWithAwsContainer { static void setupAwsContainer() { awsContainer = new AWSContainer(null, LocalStackContainer.Service.SSM); } + + @BeforeEach + void init() { + awsContainer.start(); + } + + @AfterEach + void shutdown() { + awsContainer.close(); + } } From 7b29f7133ae1b362c784770adfdcdf04f2c5e7e4 Mon Sep 17 00:00:00 2001 From: gardusig Date: Fri, 23 Sep 2022 19:33:12 -0300 Subject: [PATCH 24/24] Updated aws ssm integration tests to share container instance between all tests --- .../secrets/manager/TestWithAwsContainer.java | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/test/java/io/orkes/conductor/client/util/secrets/manager/TestWithAwsContainer.java b/src/test/java/io/orkes/conductor/client/util/secrets/manager/TestWithAwsContainer.java index cf1ef3b2..2260d5fa 100644 --- a/src/test/java/io/orkes/conductor/client/util/secrets/manager/TestWithAwsContainer.java +++ b/src/test/java/io/orkes/conductor/client/util/secrets/manager/TestWithAwsContainer.java @@ -12,26 +12,13 @@ */ package io.orkes.conductor.client.util.secrets.manager; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.testcontainers.containers.localstack.LocalStackContainer; public abstract class TestWithAwsContainer { protected static AWSContainer awsContainer; - @BeforeAll - static void setupAwsContainer() { + static { awsContainer = new AWSContainer(null, LocalStackContainer.Service.SSM); - } - - @BeforeEach - void init() { awsContainer.start(); } - - @AfterEach - void shutdown() { - awsContainer.close(); - } }