diff --git a/README.md b/README.md index 0716fb0..529d201 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Minimalistic configuration would be something like gocd.slack { login = "someuser" password = "somepassword" + api-token = "a-valid-token-from-gocd-server" server-host = "http://localhost:8153/" api-server-host = "http://localhost:8153/" webhookUrl = "https://hooks.slack.com/services/...." @@ -39,6 +40,7 @@ gocd.slack { ``` - `login` - Login for a Go user who is authorized to access the REST API. - `password` - Password for the user specified above. You might want to create a less privileged user for this plugin. +- `api-token` - Valid GoCD access token. Available starting from v19.2.0 (https://api.gocd.org/current/#bearer-token-authentication). If both login/password and api-token are present, api-token takes precedence. - `server-host` - FQDN of the Go Server. All links on the slack channel will be relative to this host. - `api-server-host` - This is an optional attribute. Set this field to localhost so server will use this endpoint to get `PipelineHistory` and `PipelineInstance` - `webhookUrl` - Slack Webhook URL diff --git a/src/main/java/in/ashwanthkumar/gocd/slack/jsonapi/Server.java b/src/main/java/in/ashwanthkumar/gocd/slack/jsonapi/Server.java index 38d76b6..6bd30a6 100644 --- a/src/main/java/in/ashwanthkumar/gocd/slack/jsonapi/Server.java +++ b/src/main/java/in/ashwanthkumar/gocd/slack/jsonapi/Server.java @@ -48,8 +48,13 @@ JsonElement getUrl(URL url) HttpURLConnection request = httpConnectionUtil.getConnection(normalizedUrl); - // Add in our HTTP authorization credentials if we have them. - if (isNotEmpty(mRules.getGoLogin()) && isNotEmpty(mRules.getGoPassword())) { + // Add in our HTTP authorization credentials if we have them. Favor the API Token + // over username/password + if (isNotEmpty(mRules.getGoAPIToken())) { + String bearerToken = "Bearer " + + DatatypeConverter.printBase64Binary(mRules.getGoAPIToken().getBytes()); + request.setRequestProperty("Authorization", bearerToken); + } else if (isNotEmpty(mRules.getGoLogin()) && isNotEmpty(mRules.getGoPassword())) { String userpass = mRules.getGoLogin() + ":" + mRules.getGoPassword(); String basicAuth = "Basic " + DatatypeConverter.printBase64Binary(userpass.getBytes()); diff --git a/src/main/java/in/ashwanthkumar/gocd/slack/ruleset/Rules.java b/src/main/java/in/ashwanthkumar/gocd/slack/ruleset/Rules.java index 05a210c..afdd7e3 100644 --- a/src/main/java/in/ashwanthkumar/gocd/slack/ruleset/Rules.java +++ b/src/main/java/in/ashwanthkumar/gocd/slack/ruleset/Rules.java @@ -29,6 +29,7 @@ public class Rules { private String goAPIServerHost; private String goLogin; private String goPassword; + private String goAPIToken; private boolean displayConsoleLogLinks; private boolean displayMaterialChanges; private boolean processAllRules; @@ -133,6 +134,15 @@ public Rules setGoPassword(String goPassword) { return this; } + public String getGoAPIToken() { + return goAPIToken; + } + + public Rules setGoAPIToken(String goAPIToken) { + this.goAPIToken = goAPIToken; + return this; + } + public boolean getDisplayConsoleLogLinks() { return displayConsoleLogLinks; } @@ -234,6 +244,11 @@ public static Rules fromConfig(Config config) { password = config.getString("password"); } + String apiToken = null; + if (config.hasPath("api-token")) { + apiToken = config.getString("api-token"); + } + boolean displayConsoleLogLinks = true; if (config.hasPath("display-console-log-links")) { displayConsoleLogLinks = config.getBoolean("display-console-log-links"); @@ -286,6 +301,7 @@ public PipelineRule apply(Config input) { .setGoAPIServerHost(apiServerHost) .setGoLogin(login) .setGoPassword(password) + .setGoAPIToken(apiToken) .setDisplayConsoleLogLinks(displayConsoleLogLinks) .setDisplayMaterialChanges(displayMaterialChanges) .setProcessAllRules(processAllRules) diff --git a/src/test/java/in/ashwanthkumar/gocd/slack/jsonapi/ServerTest.java b/src/test/java/in/ashwanthkumar/gocd/slack/jsonapi/ServerTest.java index e728f36..68b4e88 100644 --- a/src/test/java/in/ashwanthkumar/gocd/slack/jsonapi/ServerTest.java +++ b/src/test/java/in/ashwanthkumar/gocd/slack/jsonapi/ServerTest.java @@ -68,7 +68,23 @@ public void testGetPipelineInstance() throws Exception { } @Test - public void shouldConnectWithCredentials() throws IOException { + public void shouldConnectWithAPIToken() throws IOException { + HttpConnectionUtil httpConnectionUtil = mockConnection(); + Rules rules = new Rules(); + Server server = new Server(rules, httpConnectionUtil); + rules.setGoAPIToken("a-valid-token-from-gocd-server"); + + HttpURLConnection conn = mock(HttpURLConnection.class); + when(httpConnectionUtil.getConnection(any(URL.class))).thenReturn(conn); + when(conn.getContent()).thenReturn(new Object()); + + server.getUrl(new URL("http://exmaple.org/")); + + verify(conn).setRequestProperty("Authorization", "Bearer YS12YWxpZC10b2tlbi1mcm9tLWdvY2Qtc2VydmVy"); + } + + @Test + public void shouldConnectWithUserPassCredentials() throws IOException { HttpConnectionUtil httpConnectionUtil = mockConnection(); Rules rules = new Rules(); Server server = new Server(rules, httpConnectionUtil); @@ -84,6 +100,23 @@ public void shouldConnectWithCredentials() throws IOException { verify(conn).setRequestProperty("Authorization", "Basic bG9naW46cGFzcw=="); } + @Test + public void shouldConnectWithAPITokenFavoringOverUserPassCredential() throws IOException { + HttpConnectionUtil httpConnectionUtil = mockConnection(); + Rules rules = new Rules(); + Server server = new Server(rules, httpConnectionUtil); + rules.setGoAPIToken("a-valid-token-from-gocd-server"); + rules.setGoLogin("login"); + rules.setGoPassword("pass"); + + HttpURLConnection conn = mock(HttpURLConnection.class); + when(httpConnectionUtil.getConnection(any(URL.class))).thenReturn(conn); + when(conn.getContent()).thenReturn(new Object()); + + server.getUrl(new URL("http://exmaple.org/")); + + verify(conn).setRequestProperty("Authorization", "Bearer YS12YWxpZC10b2tlbi1mcm9tLWdvY2Qtc2VydmVy"); + } @Test public void shouldNotConnectWithoutCredentials() throws IOException { HttpConnectionUtil httpConnectionUtil = mockConnection(); diff --git a/src/test/java/in/ashwanthkumar/gocd/slack/ruleset/RulesReaderTest.java b/src/test/java/in/ashwanthkumar/gocd/slack/ruleset/RulesReaderTest.java index ab779cf..db7de05 100644 --- a/src/test/java/in/ashwanthkumar/gocd/slack/ruleset/RulesReaderTest.java +++ b/src/test/java/in/ashwanthkumar/gocd/slack/ruleset/RulesReaderTest.java @@ -49,6 +49,7 @@ public void shouldReadMinimalConfig() { assertThat(rules.getGoLogin(), is("someuser")); assertThat(rules.getGoPassword(), is("somepassword")); + assertThat(rules.getGoAPIToken(), is("a-valid-token-from-gocd-server")); assertThat(rules.getGoServerHost(), is("http://localhost:8153/")); assertThat(rules.getWebHookUrl(), is("https://hooks.slack.com/services/")); diff --git a/src/test/java/in/ashwanthkumar/gocd/slack/ruleset/RulesTest.java b/src/test/java/in/ashwanthkumar/gocd/slack/ruleset/RulesTest.java index 6b94253..ae31347 100644 --- a/src/test/java/in/ashwanthkumar/gocd/slack/ruleset/RulesTest.java +++ b/src/test/java/in/ashwanthkumar/gocd/slack/ruleset/RulesTest.java @@ -123,6 +123,14 @@ public void shouldGetAPIServerHost() { assertThat(rules.getGoAPIServerHost(), is("http://localhost")); } + @Test + public void shouldGetAPIToken() { + Rules rules = new Rules(); + + rules.setGoAPIToken("a-valid-token-from-gocd-server"); + assertThat(rules.getGoAPIToken(), is("a-valid-token-from-gocd-server")); + } + private static PipelineRule pipelineRule(String pipeline, String stage, String channel, Set statuses) { PipelineRule pipelineRule = new PipelineRule(pipeline, stage); pipelineRule.setStatus(statuses); diff --git a/src/test/resources/configs/test-config-minimal.conf b/src/test/resources/configs/test-config-minimal.conf index 111b927..a91ad4b 100644 --- a/src/test/resources/configs/test-config-minimal.conf +++ b/src/test/resources/configs/test-config-minimal.conf @@ -1,6 +1,7 @@ gocd.slack { login = "someuser" password = "somepassword" + api-token = "a-valid-token-from-gocd-server" server-host = "http://localhost:8153/" webhookUrl = "https://hooks.slack.com/services/"