Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update renewed Jira refresh token back to the secrets store #5324

Merged
merged 19 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
aa7ca59
Introduced PluginConfigVariable interaface to provide ability for the…
san81 Jan 13, 2025
0c34119
renewed access token and refresh tokens are now updated back in the s…
san81 Jan 13, 2025
cc82281
better naming
san81 Jan 13, 2025
2f640df
Merge branch 'opensearch-project:main' into secrets-variable-interface
san81 Jan 13, 2025
64bf7bc
fixing the test cases based on the new PluginConfigVariable attribute…
san81 Jan 13, 2025
4df4fba
improving the coverage
san81 Jan 13, 2025
a384d85
Keeping the existing values in the secret. Just updating an existing key
san81 Jan 14, 2025
2f26378
Merge branch 'opensearch-project:main' into secrets-variable-interface
san81 Jan 15, 2025
ca1ec88
Allowing secrets manager update without a key and also some additiona…
san81 Jan 15, 2025
2f98ebc
isUpdatable boolean is introduced and its corresponding tests
san81 Jan 15, 2025
f8802e5
additional coverage
san81 Jan 16, 2025
eb2e431
implementing newly added method
san81 Jan 16, 2025
76a136a
switching PluginConfigVariable from refreshToken to accessToken
san81 Jan 16, 2025
188264e
Only the master node is responsible for Token refresh
san81 Jan 16, 2025
009eafe
Added addition parameter in the API to accept the secrets version to …
san81 Jan 16, 2025
78de3aa
better naming
san81 Jan 16, 2025
7b15c95
removing setting a versionId for idempotency
san81 Jan 16, 2025
1d14234
removed constructor argument to PluginConfigVariable
san81 Jan 16, 2025
8b946af
Merge branch 'opensearch-project:main' into secrets-variable-interface
san81 Jan 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
*/
package org.opensearch.dataprepper.model.plugin;

/**
* Exception thrown when a secret could not be updated.
*
* @since 2.11
*/
public class FailedToUpdateSecretException extends RuntimeException {
san81 marked this conversation as resolved.
Show resolved Hide resolved

public FailedToUpdateSecretException(final String message) {
super(message);
}

public FailedToUpdateSecretException(final String message, Throwable e) {
super(message, e);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,48 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
*/
package org.opensearch.dataprepper.model.plugin;

/**
* Interface for a Plugin configuration value translator.
* It translates a string expression that is describing a secret store Id and secret Key in to a secretValue
* extracted from corresponding secret store.
*
* @since 2.0
*/

public interface PluginConfigValueTranslator {
/**
* Translates a string expression that is describing a secret store Id and secret Key in to a secretValue
* extracted from corresponding secret store.
* Example expression: ${{aws_secrets:secretId:secretKey}}
*
* @param value the string value to translate
* @return the translated object
*/
Object translate(final String value);

/**
* Returns the prefix for this translator.
*
* @return the prefix for this translator
*/
String getPrefix();

/**
* Translates a string expression that is describing a secret store Id and secret Key in to an instance
* of PluginConfigVariable with secretValue extracted from corresponding secret store. Additionally,
* this PluginConfigVariable helps with updating the secret value in the secret store, if required.
* Example expression: ${{aws_secrets:secretId:secretKey}}
*
* @param value the string value to translate
* @return the translated object
*/
PluginConfigVariable translateToPluginConfigVariable(final String value);
san81 marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,29 @@
* Interface for a Extension Plugin configuration variable.
* It gives access to the details of a defined extension variable.
*
* @since 1.2
* @since 2.11
*/
public interface PluginConfigVariable {
san81 marked this conversation as resolved.
Show resolved Hide resolved

/**
* Returns the value of this variable.
san81 marked this conversation as resolved.
Show resolved Hide resolved
*
* @return the value of this variable
*/
Object getValue();

/**
* If this variable is updatable, this method helps to set a new value for this variable
san81 marked this conversation as resolved.
Show resolved Hide resolved
*
* @param updatedValue the new value to set
* @param secretVersionIdToSet new version id to set for the secret in AWS. This helps with idempotency
*/
void setValue(Object someValue);
void setValue(Object updatedValue, String secretVersionIdToSet);
san81 marked this conversation as resolved.
Show resolved Hide resolved

/**
* Returns if the variable is updatable.
*
* @return true if this variable is updatable, false otherwise
*/
boolean isUpdatable();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
*/
package org.opensearch.dataprepper.model.plugin;


import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.UUID;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;

public class FailedToUpdateSecretExceptionTest extends RuntimeException {
private String message;

@BeforeEach
void setUp() {
message = UUID.randomUUID().toString();
}

@Test
void testGetMessage_should_return_correct_message() {
FailedToUpdateSecretException failedToUpdateSecretException = new FailedToUpdateSecretException(message);
assertThat(failedToUpdateSecretException.getMessage(), equalTo(message));
}

@Test
void testGetMessage_should_return_correct_message_with_throwable() {
RuntimeException cause = new RuntimeException("testException");
FailedToUpdateSecretException failedToUpdateSecretException = new FailedToUpdateSecretException(message, cause);
assertThat(failedToUpdateSecretException.getMessage(), equalTo(message));
assertThat(failedToUpdateSecretException.getCause(), equalTo(cause));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.opensearch.dataprepper.model.plugin.PluginConfigValueTranslator;
import org.opensearch.dataprepper.model.plugin.PluginConfigVariable;

import java.io.IOException;
import java.math.BigDecimal;
Expand All @@ -30,6 +31,8 @@

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;

Expand All @@ -43,6 +46,32 @@ class VariableExpanderTest {

private VariableExpander objectUnderTest;

private static Stream<Arguments> getNonStringTypeArguments() {
return Stream.of(Arguments.of(Boolean.class, "true", true),
Arguments.of(Short.class, "2", (short) 2),
Arguments.of(Integer.class, "10", 10),
Arguments.of(Long.class, "200", 200L),
Arguments.of(Double.class, "1.23", 1.23d),
Arguments.of(Float.class, "2.15", 2.15f),
Arguments.of(BigDecimal.class, "2.15", BigDecimal.valueOf(2.15)),
Arguments.of(Map.class, "{}", Collections.emptyMap()));
}

private static Stream<Arguments> getStringTypeArguments() {
final String testRandomValue = "non-secret-prefix-" + RandomStringUtils.randomAlphabetic(5);
return Stream.of(Arguments.of(String.class, String.format("\"%s\"", testRandomValue),
testRandomValue),
Arguments.of(Duration.class, "\"PT15M\"", Duration.parse("PT15M")),
Arguments.of(Boolean.class, "\"true\"", true),
Arguments.of(Short.class, "\"2\"", (short) 2),
Arguments.of(Integer.class, "\"10\"", 10),
Arguments.of(Long.class, "\"200\"", 200L),
Arguments.of(Double.class, "\"1.23\"", 1.23d),
Arguments.of(Float.class, "\"2.15\"", 2.15f),
Arguments.of(BigDecimal.class, "\"2.15\"", BigDecimal.valueOf(2.15)),
Arguments.of(Character.class, "\"c\"", 'c'));
}

@BeforeEach
void setUp() {
objectUnderTest = new VariableExpander(OBJECT_MAPPER, Set.of(pluginConfigValueTranslator));
Expand Down Expand Up @@ -107,29 +136,53 @@ void testTranslateJsonParserWithStringValue_translate_success(
assertThat(actualResult, equalTo(expectedResult));
}

private static Stream<Arguments> getNonStringTypeArguments() {
return Stream.of(Arguments.of(Boolean.class, "true", true),
Arguments.of(Short.class, "2", (short) 2),
Arguments.of(Integer.class, "10", 10),
Arguments.of(Long.class, "200", 200L),
Arguments.of(Double.class, "1.23", 1.23d),
Arguments.of(Float.class, "2.15", 2.15f),
Arguments.of(BigDecimal.class, "2.15", BigDecimal.valueOf(2.15)),
Arguments.of(Map.class, "{}", Collections.emptyMap()));
@Test
void testTranslateJsonParserWithSPluginConfigVariableValue_translate_success() throws IOException {
final String testSecretKey = "testSecretKey";
final String testTranslatorKey = "test_prefix";
final String testSecretReference = String.format("${{%s:%s}}", testTranslatorKey, testSecretKey);
final JsonParser jsonParser = JSON_FACTORY.createParser(String.format("\"%s\"", testSecretReference));
jsonParser.nextToken();
PluginConfigVariable mockPluginConfigVariable = new PluginConfigVariable() {

String secretValue = "samplePluginConfigValue";

@Override
public Object getValue() {
return secretValue;
}

@Override
public void setValue(Object updatedValue, String secretIdToSet) {
this.secretValue = updatedValue.toString();
}

@Override
public boolean isUpdatable() {
return true;
}
};
when(pluginConfigValueTranslator.getPrefix()).thenReturn(testTranslatorKey);
when(pluginConfigValueTranslator.translateToPluginConfigVariable(eq(testSecretKey)))
.thenReturn(mockPluginConfigVariable);
objectUnderTest = new VariableExpander(OBJECT_MAPPER, Set.of(pluginConfigValueTranslator));
final Object actualResult = objectUnderTest.translate(jsonParser, PluginConfigVariable.class);
assertNotNull(actualResult);
assertThat(actualResult, equalTo(mockPluginConfigVariable));
}

private static Stream<Arguments> getStringTypeArguments() {
final String testRandomValue = "non-secret-prefix-" + RandomStringUtils.randomAlphabetic(5);
return Stream.of(Arguments.of(String.class, String.format("\"%s\"", testRandomValue),
testRandomValue),
Arguments.of(Duration.class, "\"PT15M\"", Duration.parse("PT15M")),
Arguments.of(Boolean.class, "\"true\"", true),
Arguments.of(Short.class, "\"2\"", (short) 2),
Arguments.of(Integer.class, "\"10\"", 10),
Arguments.of(Long.class, "\"200\"", 200L),
Arguments.of(Double.class, "\"1.23\"", 1.23d),
Arguments.of(Float.class, "\"2.15\"", 2.15f),
Arguments.of(BigDecimal.class, "\"2.15\"", BigDecimal.valueOf(2.15)),
Arguments.of(Character.class, "\"c\"", 'c'));
@Test
void testTranslateJsonParserWithSPluginConfigVariableValue_translate_failure() throws IOException {
final String testSecretKey = "testSecretKey";
final String testTranslatorKey = "test_prefix";
final String testSecretReference = String.format("${{%s:%s}}", testTranslatorKey, testSecretKey);
final JsonParser jsonParser = JSON_FACTORY.createParser(String.format("\"%s\"", testSecretReference));
jsonParser.nextToken();
when(pluginConfigValueTranslator.getPrefix()).thenReturn(testTranslatorKey);
when(pluginConfigValueTranslator.translateToPluginConfigVariable(eq(testSecretKey)))
.thenThrow(IllegalArgumentException.class);
objectUnderTest = new VariableExpander(OBJECT_MAPPER, Set.of(pluginConfigValueTranslator));
assertThrows(IllegalArgumentException.class,
() -> objectUnderTest.translate(jsonParser, PluginConfigVariable.class));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/
package org.opensearch.dataprepper.plugins.aws;

import org.opensearch.dataprepper.model.plugin.FailedToUpdateSecretException;
import org.opensearch.dataprepper.model.plugin.PluginConfigVariable;

/**
Expand All @@ -19,14 +20,17 @@ public class AwsPluginConfigVariable implements PluginConfigVariable {
private final SecretsSupplier secretsSupplier;
private final String secretId;
private final String secretKey;
private final boolean isUpdatable;
private Object secretValue;

public AwsPluginConfigVariable(final SecretsSupplier secretsSupplier,
final String secretId, final String secretKey, Object secretValue) {
final String secretId, final String secretKey, Object secretValue,
final boolean isUpdatable) {
this.secretsSupplier = secretsSupplier;
this.secretId = secretId;
this.secretKey = secretKey;
this.secretValue = secretValue;
this.isUpdatable = isUpdatable;
}

@Override
Expand All @@ -35,8 +39,17 @@ public Object getValue() {
}

@Override
public void setValue(Object newValue) {
this.secretsSupplier.updateValue(secretId, secretKey, newValue);
public void setValue(Object newValue, String secretVersionIdToSet) {
if (!isUpdatable()) {
throw new FailedToUpdateSecretException(
String.format("Trying to update a secrets that is not updatable. SecretId: %s SecretKey: %s", this.secretId, this.secretKey));
}
this.secretsSupplier.updateValue(secretId, secretKey, newValue, secretVersionIdToSet);
this.secretValue = newValue;
}

@Override
public boolean isUpdatable() {
return isUpdatable;
san81 marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,10 @@ public GetSecretValueRequest createGetSecretValueRequest() {
.build();
}

public PutSecretValueRequest putSecretValueRequest(String secretKeyValueMapAsString) {
public PutSecretValueRequest putSecretValueRequest(String secretKeyValueMapAsString, String secretVersionIdToSet) {
return PutSecretValueRequest.builder()
.secretId(awsSecretId)
.clientRequestToken(secretVersionIdToSet)
.secretString(secretKeyValueMapAsString)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@ public PluginConfigVariable translateToPluginConfigVariable(String value) {
final String secretKey = matcher.group(SECRET_KEY_GROUP);
final Object secretValue = secretKey != null ? secretsSupplier.retrieveValue(secretId, secretKey) :
secretsSupplier.retrieveValue(secretId);
return new AwsPluginConfigVariable(secretsSupplier, secretId, secretKey, secretValue);
return new AwsPluginConfigVariable(secretsSupplier, secretId, secretKey, secretValue, true);
}
}
Loading
Loading