diff --git a/build.gradle b/build.gradle index 10bedee..f3ce63b 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,7 @@ configurations{ } dependencies { - implementation 'org.rundeck:rundeck-core:4.13.0-20230515' + implementation 'org.rundeck:rundeck-core:4.14.0-rc1-20230606' pluginLibs group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.14' pluginLibs group: 'com.google.code.gson', name: 'gson', version:'2.10.1' diff --git a/src/main/java/edu/ohio/ais/rundeck/HttpBuilder.java b/src/main/java/edu/ohio/ais/rundeck/HttpBuilder.java index bb84437..99f3fce 100644 --- a/src/main/java/edu/ohio/ais/rundeck/HttpBuilder.java +++ b/src/main/java/edu/ohio/ais/rundeck/HttpBuilder.java @@ -11,6 +11,7 @@ import com.google.gson.GsonBuilder; import com.google.gson.JsonParser; import edu.ohio.ais.rundeck.util.OAuthClient; +import edu.ohio.ais.rundeck.util.SecretBundleUtil; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; @@ -331,25 +332,15 @@ String getAuthHeader(PluginStepContext pluginStepContext, Map o if(options.containsKey("password") ){ String passwordRaw = options.containsKey("password") ? options.get("password").toString() : null; - //to avid the test error add a try-catch //if it didn't find the key path, it will use the password directly - try { - ResourceMeta contents = pluginStepContext.getExecutionContext().getStorageTree().getResource(passwordRaw).getContents(); - - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - - contents.writeContent(byteArrayOutputStream); - - password = new String(byteArrayOutputStream.toByteArray()); - } catch (Exception e) { - password=null; + byte[] content = SecretBundleUtil.getStoragePassword(pluginStepContext.getExecutionContext(),passwordRaw ); + if(content!=null){ + password = new String(content); } - if(password==null){ password=passwordRaw; } - } if(authentication.equals(AUTH_BASIC)) { diff --git a/src/main/java/edu/ohio/ais/rundeck/HttpWorkflowNodeStepPlugin.java b/src/main/java/edu/ohio/ais/rundeck/HttpWorkflowNodeStepPlugin.java index 7172743..ff4cd30 100644 --- a/src/main/java/edu/ohio/ais/rundeck/HttpWorkflowNodeStepPlugin.java +++ b/src/main/java/edu/ohio/ais/rundeck/HttpWorkflowNodeStepPlugin.java @@ -2,6 +2,9 @@ import com.dtolabs.rundeck.core.common.INodeEntry; import com.dtolabs.rundeck.core.dispatcher.DataContextUtils; +import com.dtolabs.rundeck.core.execution.ExecutionContext; +import com.dtolabs.rundeck.core.execution.proxy.ProxySecretBundleCreator; +import com.dtolabs.rundeck.core.execution.proxy.SecretBundle; import com.dtolabs.rundeck.core.execution.workflow.steps.StepException; import com.dtolabs.rundeck.core.execution.workflow.steps.StepFailureReason; import com.dtolabs.rundeck.core.execution.workflow.steps.node.NodeStepException; @@ -13,18 +16,17 @@ import com.dtolabs.rundeck.plugins.step.NodeStepPlugin; import com.dtolabs.rundeck.plugins.step.PluginStepContext; import edu.ohio.ais.rundeck.util.OAuthClient; +import edu.ohio.ais.rundeck.util.SecretBundleUtil; import org.apache.http.HttpEntity; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.RequestBuilder; import org.apache.http.entity.ByteArrayEntity; import java.io.UnsupportedEncodingException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.*; @Plugin(name = HttpWorkflowNodeStepPlugin.SERVICE_PROVIDER_NAME, service = ServiceNameConstants.WorkflowNodeStep) -public class HttpWorkflowNodeStepPlugin implements NodeStepPlugin, Describable { +public class HttpWorkflowNodeStepPlugin implements NodeStepPlugin, Describable, ProxySecretBundleCreator { public static final String SERVICE_PROVIDER_NAME = "edu.ohio.ais.rundeck.HttpWorkflowNodeStepPlugin"; /** @@ -134,4 +136,14 @@ public void executeNodeStep(PluginStepContext context, Map confi } } + + @Override + public SecretBundle prepareSecretBundleWorkflowNodeStep(ExecutionContext context, INodeEntry node, Map configuration) { + return SecretBundleUtil.getSecrets(context, configuration); + } + + @Override + public List listSecretsPathWorkflowNodeStep(ExecutionContext context, INodeEntry node, Map configuration) { + return SecretBundleUtil.getListSecrets(configuration); + } } diff --git a/src/main/java/edu/ohio/ais/rundeck/HttpWorkflowStepPlugin.java b/src/main/java/edu/ohio/ais/rundeck/HttpWorkflowStepPlugin.java index e8355e7..fbe4dbb 100644 --- a/src/main/java/edu/ohio/ais/rundeck/HttpWorkflowStepPlugin.java +++ b/src/main/java/edu/ohio/ais/rundeck/HttpWorkflowStepPlugin.java @@ -1,6 +1,9 @@ package edu.ohio.ais.rundeck; import com.dtolabs.rundeck.core.dispatcher.DataContextUtils; +import com.dtolabs.rundeck.core.execution.ExecutionContext; +import com.dtolabs.rundeck.core.execution.proxy.ProxySecretBundleCreator; +import com.dtolabs.rundeck.core.execution.proxy.SecretBundle; import com.dtolabs.rundeck.core.execution.workflow.steps.StepException; import com.dtolabs.rundeck.core.execution.workflow.steps.StepFailureReason; import com.dtolabs.rundeck.core.plugins.Plugin; @@ -11,15 +14,14 @@ import com.dtolabs.rundeck.plugins.step.PluginStepContext; import com.dtolabs.rundeck.plugins.step.StepPlugin; import edu.ohio.ais.rundeck.util.OAuthClient; +import edu.ohio.ais.rundeck.util.SecretBundleUtil; import org.apache.http.HttpEntity; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.RequestBuilder; import org.apache.http.entity.ByteArrayEntity; import java.io.*; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.*; /** @@ -27,7 +29,7 @@ * tokens when they're expired and sending the appropriate request. */ @Plugin(name = HttpWorkflowStepPlugin.SERVICE_PROVIDER_NAME, service = ServiceNameConstants.WorkflowStep) -public class HttpWorkflowStepPlugin implements StepPlugin, Describable { +public class HttpWorkflowStepPlugin implements StepPlugin, Describable, ProxySecretBundleCreator { /** * Maximum number of attempts with which to try the request. @@ -63,7 +65,6 @@ public Description getDescription() { return new HttpDescription(SERVICE_PROVIDER_NAME, "HTTP Request Step", "Performs an HTTP request with or without authentication").getDescription(); } - @Override public void executeStep(PluginStepContext pluginStepContext, Map options) throws StepException { PluginLogger log = pluginStepContext.getLogger(); @@ -131,4 +132,14 @@ public void executeStep(PluginStepContext pluginStepContext, Map builder.doRequest(options, request.build(), 1); } + @Override + public SecretBundle prepareSecretBundleWorkflowStep(ExecutionContext context, Map configuration) { + return SecretBundleUtil.getSecrets(context, configuration); + } + + @Override + public List listSecretsPathWorkflowStep(ExecutionContext context, Map configuration) { + return SecretBundleUtil.getListSecrets(configuration); + } + } diff --git a/src/main/java/edu/ohio/ais/rundeck/util/SecretBundleUtil.java b/src/main/java/edu/ohio/ais/rundeck/util/SecretBundleUtil.java new file mode 100644 index 0000000..c6a836e --- /dev/null +++ b/src/main/java/edu/ohio/ais/rundeck/util/SecretBundleUtil.java @@ -0,0 +1,49 @@ +package edu.ohio.ais.rundeck.util; + +import com.dtolabs.rundeck.core.execution.ExecutionContext; +import com.dtolabs.rundeck.core.execution.proxy.DefaultSecretBundle; +import com.dtolabs.rundeck.core.execution.proxy.SecretBundle; +import com.dtolabs.rundeck.core.storage.ResourceMeta; + +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class SecretBundleUtil { + + public static List getListSecrets(Map configuration) { + List listSecretPath = new ArrayList<>(); + String passwordPath = (String)configuration.get("password"); + + if(passwordPath!=null && !passwordPath.isEmpty() ){ + listSecretPath.add(passwordPath); + } + + return listSecretPath; + } + + public static SecretBundle getSecrets(ExecutionContext context, Map configuration){ + DefaultSecretBundle secretBundle = new DefaultSecretBundle(); + String passwordPath = (String)configuration.get("password"); + + if(passwordPath!=null && !passwordPath.isEmpty() ){ + byte[] content = SecretBundleUtil.getStoragePassword(context,passwordPath); + if(content!=null){ + secretBundle.addSecret(passwordPath, content); + } + } + return secretBundle; + } + + public static byte[] getStoragePassword(ExecutionContext context, String path){ + try(ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { + ResourceMeta contents = context.getStorageTree().getResource(path).getContents(); + contents.writeContent(byteArrayOutputStream); + return byteArrayOutputStream.toByteArray(); + } catch (Exception e) { + context.getExecutionLogger().log(0, e.getMessage()); + return null; + } + } +} diff --git a/src/test/java/edu/ohio/ais/rundeck/HttpWorkflowNodeStepPluginTest.java b/src/test/java/edu/ohio/ais/rundeck/HttpWorkflowNodeStepPluginTest.java index 7987184..448ddbc 100644 --- a/src/test/java/edu/ohio/ais/rundeck/HttpWorkflowNodeStepPluginTest.java +++ b/src/test/java/edu/ohio/ais/rundeck/HttpWorkflowNodeStepPluginTest.java @@ -154,9 +154,12 @@ public void setUp() { .willReturn(WireMock.aResponse().withFixedDelay(SLOW_TIMEOUT).withStatus(200))); node = Mockito.mock(INodeEntry.class); - pluginContext = Mockito.mock(PluginStepContext.class); pluginLogger = Mockito.mock(PluginLogger.class); + pluginContext = Mockito.mock(PluginStepContext.class); + ExecutionContext executionContext = Mockito.mock(ExecutionContext.class); + when(executionContext.getExecutionLogger()).thenReturn(pluginLogger); when(pluginContext.getLogger()).thenReturn(pluginLogger); + when(pluginContext.getExecutionContext()).thenReturn(executionContext); dataContext =new HashMap<>(); when(pluginContext.getDataContext()).thenReturn(dataContext); diff --git a/src/test/java/edu/ohio/ais/rundeck/HttpWorkflowStepPluginTest.java b/src/test/java/edu/ohio/ais/rundeck/HttpWorkflowStepPluginTest.java index ce8a213..ba7ccf1 100644 --- a/src/test/java/edu/ohio/ais/rundeck/HttpWorkflowStepPluginTest.java +++ b/src/test/java/edu/ohio/ais/rundeck/HttpWorkflowStepPluginTest.java @@ -1,5 +1,6 @@ package edu.ohio.ais.rundeck; +import com.dtolabs.rundeck.core.execution.ExecutionContext; import com.dtolabs.rundeck.core.execution.workflow.steps.PluginStepContextImpl; import com.dtolabs.rundeck.core.execution.workflow.steps.StepException; import com.dtolabs.rundeck.core.execution.workflow.steps.StepFailureReason; @@ -19,6 +20,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; +import static org.mockito.Mockito.when; public class HttpWorkflowStepPluginTest { protected static final String REMOTE_URL = "/trigger"; @@ -153,7 +155,10 @@ public void setUp() { pluginContext = Mockito.mock(PluginStepContext.class); pluginLogger = Mockito.mock(PluginLogger.class); - Mockito.when(pluginContext.getLogger()).thenReturn(pluginLogger); + ExecutionContext executionContext = Mockito.mock(ExecutionContext.class); + when(executionContext.getExecutionLogger()).thenReturn(pluginLogger); + when(pluginContext.getLogger()).thenReturn(pluginLogger); + when(pluginContext.getExecutionContext()).thenReturn(executionContext); dataContext =new HashMap<>(); Mockito.when(pluginContext.getDataContext()).thenReturn(dataContext);