diff --git a/build.gradle b/build.gradle index 5ca9c0d6..ea126437 100644 --- a/build.gradle +++ b/build.gradle @@ -23,18 +23,21 @@ plugins { ext { versions = [ - awaitility : '3.1.6', + awaitility : '4.2.0', commonsLang: '3.12.0', conductor : '3.8.1', - eureka : '1.10.10', - groovy : '2.5.15', + eureka : '1.10.17', + 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', - wiremock : '2.33.2' + spectator : '1.3.7', + spock : '2.2-groovy-2.5', + wiremock : '2.33.2', + awsSsm : '1.12.300', + azureSsm : '4.2.3', + azureIdentity: '1.3.7', ] } @@ -61,15 +64,19 @@ 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 'com.google.code.gson:gson:2.9.0' + implementation 'io.gsonfire:gson-fire:1.8.5' - implementation 'io.gsonfire:gson-fire:1.8.3' - - 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}" - implementation 'org.threeten:threetenbp:1.3.5' + implementation 'org.threeten:threetenbp:1.6.1' + + //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}" // test dependencies testImplementation "org.junit.jupiter:junit-jupiter-api:${versions.junit}" @@ -81,6 +88,10 @@ dependencies { testImplementation("com.github.tomakehurst:wiremock-jre8:${versions.wiremock}") { 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/ApiClient.java b/src/main/java/io/orkes/conductor/client/ApiClient.java index cb3f3905..c68be7cf 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; @@ -79,7 +79,10 @@ public class ApiClient { private String keySecret; private String token; - private Object tokenMutex; + + private SecretsManager secretsManager; + private String ssmKeyPath; + private String ssmSecretPath; /* * Constructor for ApiClient @@ -91,20 +94,23 @@ public ApiClient() { public ApiClient(String basePath) { this.basePath = basePath; - httpClient = new OkHttpClient(); - verifyingSsl = true; - json = new JSON(); - - // Setup authentications (key: authentication name, value: authentication). authentications = new HashMap(); + } - this.keyId = null; - this.keySecret = null; - this.token = null; - this.tokenMutex = new Object(); + public ApiClient( + String basePath, SecretsManager secretsManager, String keyPath, String secretPath) { + this(basePath); + 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) { @@ -382,7 +388,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()) { @@ -1208,43 +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; } + 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"); } - synchronized (this.tokenMutex) { - 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); - } - } - - public String getToken() { - synchronized (this.tokenMutex) { - return this.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/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/SecretsManager.java b/src/main/java/io/orkes/conductor/client/SecretsManager.java new file mode 100644 index 00000000..131347ba --- /dev/null +++ b/src/main/java/io/orkes/conductor/client/SecretsManager.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; + +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/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 { 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 new file mode 100644 index 00000000..32382d1f --- /dev/null +++ b/src/main/java/io/orkes/conductor/client/secrets/manager/aws/AwsSecretsManager.java @@ -0,0 +1,81 @@ +/* + * 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.aws; + +import io.orkes.conductor.client.SecretsManager; + +import com.amazonaws.ClientConfiguration; +import com.amazonaws.auth.AWSCredentialsProvider; +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; + +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); + } + + 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() { + return getProperty("aws.region"); + } +} diff --git a/src/main/java/io/orkes/conductor/client/secrets/manager/azure/AzureSecretsManager.java b/src/main/java/io/orkes/conductor/client/secrets/manager/azure/AzureSecretsManager.java new file mode 100644 index 00000000..83ac9b2e --- /dev/null +++ b/src/main/java/io/orkes/conductor/client/secrets/manager/azure/AzureSecretsManager.java @@ -0,0 +1,50 @@ +/* + * 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.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 static final String PROPERTY_NAME = "azure.keyvault.name"; + + private final SecretClient client; + + public AzureSecretsManager() { + this.client = createClient(); + } + + @Override + public String getSecret(String keyPath) { + return client.getSecret(keyPath).getValue(); + } + + @Override + public void storeSecret(String key, String secret) { + client.setSecret(key, secret); + } + + private SecretClient createClient() { + String keyVaultName = getProperty(PROPERTY_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(); + } +} 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..3bc2b950 --- /dev/null +++ b/src/test/java/io/orkes/conductor/client/secrets/manager/AwsSecretsManagerTests.java @@ -0,0 +1,50 @@ +/* + * 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 { + AwsSecretsManager awsSecretsManager = getAwsSecretsManager(); + ApiClient customApiClient = + new ApiClient( + ApiUtil.getBasePath(), + awsSecretsManager, + Commons.SECRET_MANAGER_KEY_PATH, + Commons.SECRET_MANAGER_SECRET_PATH); + assertNull(customApiClient.getToken()); + } + + @Test + void testAwsSecretsManager() throws Exception { + AwsSecretsManager awsSecretsManager = getAwsSecretsManager(); + awsSecretsManager.storeSecret(Commons.SECRET_MANAGER_KEY_PATH, ApiUtil.getKeyId()); + awsSecretsManager.storeSecret(Commons.SECRET_MANAGER_SECRET_PATH, ApiUtil.getKeySecret()); + SecretsManagerUtil.validateApiClientWithSecretsManager(awsSecretsManager); + } + + 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/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); } 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/util/secrets/manager/AWSContainer.java b/src/test/java/io/orkes/conductor/client/util/secrets/manager/AWSContainer.java new file mode 100644 index 00000000..3c02248f --- /dev/null +++ b/src/test/java/io/orkes/conductor/client/util/secrets/manager/AWSContainer.java @@ -0,0 +1,70 @@ +/* + * 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 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; + +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"; + + 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); + } + + 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()); + 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); + } +} 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..2260d5fa --- /dev/null +++ b/src/test/java/io/orkes/conductor/client/util/secrets/manager/TestWithAwsContainer.java @@ -0,0 +1,24 @@ +/* + * 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.testcontainers.containers.localstack.LocalStackContainer; + +public abstract class TestWithAwsContainer { + protected static AWSContainer awsContainer; + + static { + awsContainer = new AWSContainer(null, LocalStackContainer.Service.SSM); + awsContainer.start(); + } +} 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(); }