From 3068252021fc4ae00f2c8f73f2d0dea1acf7ca91 Mon Sep 17 00:00:00 2001 From: Roy Sindre Norangshol Date: Thu, 2 Mar 2023 12:54:46 +0100 Subject: [PATCH 1/2] Test for either using token or username/password After upgrading rundeck plugin, I belive the login (read: username for associated password) was removed from the plugin's configuration page. At least I'm unable to see it, and I belive for long time users of this plugin, where it maybe had username stored there once-in-its-lifetime but have migrated over to api token usage instead .. gets bitten by this test case. --- pom.xml | 5 +++ .../client/RundeckClientManagerTest.java | 39 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/test/java/org/jenkinsci/plugins/rundeck/client/RundeckClientManagerTest.java diff --git a/pom.xml b/pom.xml index c5b6a9bb..8ad65a7e 100644 --- a/pom.xml +++ b/pom.xml @@ -141,6 +141,11 @@ 1.16 test + + org.mockito + mockito-core + test + org.jenkins-ci.plugins subversion diff --git a/src/test/java/org/jenkinsci/plugins/rundeck/client/RundeckClientManagerTest.java b/src/test/java/org/jenkinsci/plugins/rundeck/client/RundeckClientManagerTest.java new file mode 100644 index 00000000..4f555002 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/rundeck/client/RundeckClientManagerTest.java @@ -0,0 +1,39 @@ +package org.jenkinsci.plugins.rundeck.client; + +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import hudson.util.Secret; +import org.jenkinsci.plugins.rundeck.RundeckInstance; +import org.junit.Test; +import org.rundeck.client.RundeckClient; +import org.rundeck.client.RundeckClient.Builder; +import org.rundeck.client.api.RundeckApi; + +public class RundeckClientManagerTest { + + @Test + public void usingTokenDoesNotTryToSetUsernameAndPasswordCredentialsMethodWhenEarlierLoginInformationIsFoundInConfiguration() { + + Builder clientBuilderSpy = spy(RundeckClient.builder()); + + RundeckInstance rundeckInstanceMock = mock(RundeckInstance.class); + when(rundeckInstanceMock.getUrl()).thenReturn("http://localhost:4044"); + + when(rundeckInstanceMock.getToken()).thenReturn(Secret.fromString("aToken")); + when(rundeckInstanceMock.getTokenPlainText()).thenReturn("aToken"); + when(rundeckInstanceMock.getLogin()).thenReturn("bad_data_from_plugin_data_somehow"); + when(rundeckInstanceMock.getPassword()).thenReturn(Secret.fromString("")); + when(rundeckInstanceMock.getPasswordPlainText()).thenReturn(""); + + RundeckClientManager rundeckClientManager = new RundeckClientManager(rundeckInstanceMock); + rundeckClientManager.setRundeckClientBuilder(clientBuilderSpy); + rundeckClientManager.buildClient(); + + verify(clientBuilderSpy, never()).passwordAuth(anyString(), anyString()); + + } +} From bd5694306caf2fbe73da0592082413f2e83a231e Mon Sep 17 00:00:00 2001 From: Roy Sindre Norangshol Date: Thu, 2 Mar 2023 13:14:35 +0100 Subject: [PATCH 2/2] Validate both username and password has value before trying to use passwordAuth, ensure both are present. An empty string leaves a valid Secret type, and login information seems to stay from earlier plugin versions and has not been removed by the plugin-upgrade-process. This makes RundeckClientManager work-around the removed login configuration which is no longer available in the plugin (as far as I can see). --- .../rundeck/client/RundeckClientManager.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/rundeck/client/RundeckClientManager.java b/src/main/java/org/jenkinsci/plugins/rundeck/client/RundeckClientManager.java index 6b4c2953..7381eac5 100644 --- a/src/main/java/org/jenkinsci/plugins/rundeck/client/RundeckClientManager.java +++ b/src/main/java/org/jenkinsci/plugins/rundeck/client/RundeckClientManager.java @@ -1,9 +1,11 @@ package org.jenkinsci.plugins.rundeck.client; +import com.google.common.annotations.VisibleForTesting; import hudson.AbortException; import okhttp3.ResponseBody; import org.jenkinsci.plugins.rundeck.RundeckInstance; import org.rundeck.client.RundeckClient; +import org.rundeck.client.RundeckClient.Builder; import org.rundeck.client.api.RundeckApi; import org.rundeck.client.api.model.*; import org.rundeck.client.api.model.scheduler.ScheduledJobItem; @@ -23,6 +25,8 @@ public class RundeckClientManager implements RundeckManager { private RundeckInstance rundeckInstance; private Client client; + private Builder clientBuilder; + public RundeckClientManager() { } @@ -31,6 +35,20 @@ public RundeckClientManager(RundeckInstance rundeckInstance) { buildClient(); } + + private Builder createRundeckClientBuilder() { + if (clientBuilder != null) { + return clientBuilder; + } else { + return RundeckClient.builder(); + } + } + + @VisibleForTesting + protected void setRundeckClientBuilder(Builder builder) { + clientBuilder = builder; + } + public RundeckInstance getRundeckInstance() { return rundeckInstance; } @@ -49,12 +67,13 @@ public void setClient(Client client) { public void buildClient(){ if(client == null){ - RundeckClient.Builder builder = RundeckClient.builder().baseUrl(rundeckInstance.getUrl()); + RundeckClient.Builder builder = createRundeckClientBuilder().baseUrl(rundeckInstance.getUrl()); if(rundeckInstance.getToken()!=null && !rundeckInstance.getToken().getPlainText().isEmpty()){ builder.tokenAuth(rundeckInstance.getToken().getPlainText()); } - if(rundeckInstance.getLogin() != null && rundeckInstance.getPassword()!=null){ + if (rundeckInstance.getLogin() != null && rundeckInstance.getPassword() != null + && !rundeckInstance.getPassword().getPlainText().isEmpty()) { builder.passwordAuth(rundeckInstance.getLogin(),rundeckInstance.getPassword().getPlainText() ); }