From 0f8e3387192c23942695f9c67d9e92771d153349 Mon Sep 17 00:00:00 2001 From: Greg Schueler Date: Fri, 4 Apr 2014 11:41:09 -0700 Subject: [PATCH] Support API token endpoint (v11) --- .../java/org/rundeck/api/RundeckClient.java | 64 ++++++++++++++ .../org/rundeck/api/domain/RundeckToken.java | 36 ++++++++ .../api/parser/RundeckTokenParser.java | 33 ++++++++ .../org/rundeck/api/RundeckClientTest.java | 84 +++++++++++++++++++ .../betamax/tapes/api_token_delete.yaml | 36 ++++++++ .../betamax/tapes/api_token_generate.yaml | 25 ++++++ .../betamax/tapes/api_token_get.yaml | 24 ++++++ .../betamax/tapes/api_tokens_list_all.yaml | 24 ++++++ .../betamax/tapes/api_tokens_list_user.yaml | 24 ++++++ 9 files changed, 350 insertions(+) create mode 100644 src/main/java/org/rundeck/api/domain/RundeckToken.java create mode 100644 src/main/java/org/rundeck/api/parser/RundeckTokenParser.java create mode 100644 src/test/resources/betamax/tapes/api_token_delete.yaml create mode 100644 src/test/resources/betamax/tapes/api_token_generate.yaml create mode 100644 src/test/resources/betamax/tapes/api_token_get.yaml create mode 100644 src/test/resources/betamax/tapes/api_tokens_list_all.yaml create mode 100644 src/test/resources/betamax/tapes/api_tokens_list_user.yaml diff --git a/src/main/java/org/rundeck/api/RundeckClient.java b/src/main/java/org/rundeck/api/RundeckClient.java index 666af9b..e56bda1 100644 --- a/src/main/java/org/rundeck/api/RundeckClient.java +++ b/src/main/java/org/rundeck/api/RundeckClient.java @@ -2310,6 +2310,70 @@ public class RundeckClient implements Serializable { return new ApiCall(this).get(new ApiPathBuilder("/system/info"), new SystemInfoParser(rootXpath()+"/system")); } + + /* + * API token + */ + + /** + * List API tokens for a user. + * @param user username + * @return list of tokens + * @throws RundeckApiException + */ + public List listApiTokens(final String user) throws RundeckApiException { + AssertUtil.notNull(user, "user is mandatory to list API tokens for a user."); + return new ApiCall(this). + get(new ApiPathBuilder("/tokens/", user), + new ListParser(new RundeckTokenParser(), "/tokens/token")); + } + + /** + * List all API tokens + * @return list of tokens + * @throws RundeckApiException + */ + public List listApiTokens() throws RundeckApiException { + return new ApiCall(this). + get(new ApiPathBuilder("/tokens"), + new ListParser(new RundeckTokenParser(), "/tokens/token")); + } + + /** + * Generate an API token for a user. + * @param user + * @return + * @throws RundeckApiException + */ + public String generateApiToken(final String user) throws RundeckApiException{ + AssertUtil.notNull(user, "user is mandatory to generate an API token for a user."); + RundeckToken result = new ApiCall(this). + post(new ApiPathBuilder("/tokens/", user).emptyContent(), + new RundeckTokenParser("/token")); + return result.getToken(); + } + /** + * Delete an existing token + * @param token + * @return + * @throws RundeckApiException + */ + public boolean deleteApiToken(final String token) throws RundeckApiException{ + AssertUtil.notNull(token, "token is mandatory to delete an API token."); + new ApiCall(this).delete(new ApiPathBuilder("/token/", token)); + return true; + } + /** + * Return user info for an existing token + * @param token + * @return token info + * @throws RundeckApiException + */ + public RundeckToken getApiToken(final String token) throws RundeckApiException{ + AssertUtil.notNull(token, "token is mandatory to get an API token."); + return new ApiCall(this).get(new ApiPathBuilder("/token/", token), new RundeckTokenParser("/token")); + } + /** * @return the URL of the RunDeck instance ("http://localhost:4440", "http://rundeck.your-compagny.com/", etc) */ diff --git a/src/main/java/org/rundeck/api/domain/RundeckToken.java b/src/main/java/org/rundeck/api/domain/RundeckToken.java new file mode 100644 index 0000000..613fc82 --- /dev/null +++ b/src/main/java/org/rundeck/api/domain/RundeckToken.java @@ -0,0 +1,36 @@ +package org.rundeck.api.domain; + +/** + * RundeckToken is ... + * + * @author Greg Schueler + * @since 2014-04-04 + */ +public class RundeckToken { + private String user; + private String token; + + public RundeckToken() { + } + + public RundeckToken(String user, String token) { + this.setUser(user); + this.setToken(token); + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } +} diff --git a/src/main/java/org/rundeck/api/parser/RundeckTokenParser.java b/src/main/java/org/rundeck/api/parser/RundeckTokenParser.java new file mode 100644 index 0000000..b48543d --- /dev/null +++ b/src/main/java/org/rundeck/api/parser/RundeckTokenParser.java @@ -0,0 +1,33 @@ +package org.rundeck.api.parser; + +import org.dom4j.Node; +import org.rundeck.api.domain.RundeckToken; + +/** + * RundeckTokenParser is ... + * + * @author Greg Schueler + * @since 2014-04-04 + */ +public class RundeckTokenParser implements XmlNodeParser { + String xpath; + + public RundeckTokenParser() { + } + + public RundeckTokenParser(String xpath) { + this.xpath = xpath; + } + + @Override + public RundeckToken parseXmlNode(Node node) { + Node targetNode = xpath != null ? node.selectSingleNode(xpath) : node; + RundeckToken rundeckToken = new RundeckToken(); + String token = targetNode.valueOf("@id"); + String user = targetNode.valueOf("@user"); + rundeckToken.setToken(token); + rundeckToken.setUser(user); + + return rundeckToken; + } +} diff --git a/src/test/java/org/rundeck/api/RundeckClientTest.java b/src/test/java/org/rundeck/api/RundeckClientTest.java index f0aa4b6..053ff51 100644 --- a/src/test/java/org/rundeck/api/RundeckClientTest.java +++ b/src/test/java/org/rundeck/api/RundeckClientTest.java @@ -55,6 +55,7 @@ public class RundeckClientTest { public static final String TEST_TOKEN_4 = "sN5RRSNvu15DnV6EcNDdc2CkdPcv3s32"; public static final String TEST_TOKEN_5 = "C3O6d5O98Kr6Dpv71sdE4ERdCuU12P6d"; public static final String TEST_TOKEN_6 = "Do4d3NUD5DKk21DR4sNK755RcPk618vn"; + public static final String TEST_TOKEN_7 = "8Dp9op111ER6opsDRkddvE86K9sE499s"; @Rule public Recorder recorder = new Recorder(); @@ -1197,6 +1198,89 @@ public class RundeckClientTest { Assert.assertEquals(RundeckWFExecState.SUCCEEDED,output.getExecutionState()); } + /** + * generate api token + */ + @Test + @Betamax(tape = "api_token_generate", mode = TapeMode.READ_ONLY) + public void generateApiToken() throws Exception { + final RundeckClient client = createClient(TEST_TOKEN_7, 11); + String token = client.generateApiToken("bob"); + + Assert.assertNotNull(token); + Assert.assertEquals("MiquQjELTrEaugpmdgAKs1W3a7xonAwU", token); + } + /** + * get api token + */ + @Test + @Betamax(tape = "api_token_get", mode = TapeMode.READ_ONLY) + public void getApiToken() throws Exception { + final RundeckClient client = createClient(TEST_TOKEN_7, 11); + RundeckToken token = client.getApiToken("MiquQjELTrEaugpmdgAKs1W3a7xonAwU"); + + Assert.assertNotNull(token); + Assert.assertEquals("MiquQjELTrEaugpmdgAKs1W3a7xonAwU", token.getToken()); + Assert.assertEquals("bob", token.getUser()); + } + /** + * list api tokens for user + */ + @Test + @Betamax(tape = "api_tokens_list_user", mode = TapeMode.READ_ONLY) + public void listApiTokens_user() throws Exception { + final RundeckClient client = createClient(TEST_TOKEN_7, 11); + List tokens = client.listApiTokens("bob"); + + Assert.assertNotNull(tokens); + Assert.assertEquals(3, tokens.size()); + Assert.assertEquals("hINp5eGzvYA9UePbUChaKHd5NiRkwWbx", tokens.get(0).getToken()); + Assert.assertEquals("bob", tokens.get(0).getUser()); + Assert.assertEquals("NaNnwVzAHAG83qOS7Wtwh6mjcXViyWUV", tokens.get(1).getToken()); + Assert.assertEquals("bob", tokens.get(1).getUser()); + Assert.assertEquals("MiquQjELTrEaugpmdgAKs1W3a7xonAwU", tokens.get(2).getToken()); + Assert.assertEquals("bob", tokens.get(2).getUser()); + } + /** + * list api tokens all + */ + @Test + @Betamax(tape = "api_tokens_list_all"/*, mode = TapeMode.READ_ONLY*/) + public void listApiTokens() throws Exception { + final RundeckClient client = createClient(TEST_TOKEN_7, 11); + List tokens = client.listApiTokens(); + + Assert.assertNotNull(tokens); + Assert.assertEquals(4, tokens.size()); + Assert.assertEquals("8Dp9op111ER6opsDRkddvE86K9sE499s", tokens.get(0).getToken()); + Assert.assertEquals("admin", tokens.get(0).getUser()); + Assert.assertEquals("hINp5eGzvYA9UePbUChaKHd5NiRkwWbx", tokens.get(1).getToken()); + Assert.assertEquals("bob", tokens.get(1).getUser()); + Assert.assertEquals("NaNnwVzAHAG83qOS7Wtwh6mjcXViyWUV", tokens.get(2).getToken()); + Assert.assertEquals("bob", tokens.get(2).getUser()); + Assert.assertEquals("MiquQjELTrEaugpmdgAKs1W3a7xonAwU", tokens.get(3).getToken()); + Assert.assertEquals("bob", tokens.get(3).getUser()); + } + + /** + * get api token + */ + @Test + @Betamax(tape = "api_token_delete"/*, mode = TapeMode.READ_ONLY*/) + public void deleteApiToken() throws Exception { + final RundeckClient client = createClient(TEST_TOKEN_7, 11); + + client.deleteApiToken("MiquQjELTrEaugpmdgAKs1W3a7xonAwU"); + + //get should now return 404 + try { + client.getApiToken("MiquQjELTrEaugpmdgAKs1W3a7xonAwU"); + Assert.fail("expected failure"); + } catch (RundeckApiException.RundeckApiHttpStatusException e) { + Assert.assertEquals(404, e.getStatusCode()); + } + } + @Before public void setUp() throws Exception { // not that you can put whatever here, because we don't actually connect to the RunDeck instance diff --git a/src/test/resources/betamax/tapes/api_token_delete.yaml b/src/test/resources/betamax/tapes/api_token_delete.yaml new file mode 100644 index 0000000..92fc6c0 --- /dev/null +++ b/src/test/resources/betamax/tapes/api_token_delete.yaml @@ -0,0 +1,36 @@ +!tape +name: api_token_delete +interactions: +- recorded: 2014-04-04T18:38:18.432Z + request: + method: DELETE + uri: http://rundeck.local:4440/api/11/token/MiquQjELTrEaugpmdgAKs1W3a7xonAwU + headers: + Host: rundeck.local:4440 + Proxy-Connection: Keep-Alive + User-Agent: RunDeck API Java Client 11 + X-RunDeck-Auth-Token: 8Dp9op111ER6opsDRkddvE86K9sE499s + response: + status: 204 + headers: + Content-Type: text/html;charset=UTF-8 + Expires: Thu, 01 Jan 1970 00:00:00 GMT + Server: Jetty(7.6.0.v20120127) + Set-Cookie: JSESSIONID=j0fidhqqsmlt1qmvaawr52a42;Path=/ +- recorded: 2014-04-04T18:38:18.523Z + request: + method: GET + uri: http://rundeck.local:4440/api/11/token/MiquQjELTrEaugpmdgAKs1W3a7xonAwU + headers: + Accept: text/xml + Host: rundeck.local:4440 + Proxy-Connection: Keep-Alive + User-Agent: RunDeck API Java Client 11 + X-RunDeck-Auth-Token: 8Dp9op111ER6opsDRkddvE86K9sE499s + response: + status: 404 + headers: + Content-Type: text/xml;charset=UTF-8 + Server: Jetty(7.6.0.v20120127) + X-Rundeck-API-Version: '11' + body: "\n \n Token does not exist: MiquQjELTrEaugpmdgAKs1W3a7xonAwU\n \n" diff --git a/src/test/resources/betamax/tapes/api_token_generate.yaml b/src/test/resources/betamax/tapes/api_token_generate.yaml new file mode 100644 index 0000000..1a5f72d --- /dev/null +++ b/src/test/resources/betamax/tapes/api_token_generate.yaml @@ -0,0 +1,25 @@ +!tape +name: api_token_generate +interactions: +- recorded: 2014-04-04T18:21:07.759Z + request: + method: POST + uri: http://rundeck.local:4440/api/11/tokens/bob + headers: + Accept: text/xml + Content-Length: '0' + Host: rundeck.local:4440 + Proxy-Connection: Keep-Alive + User-Agent: RunDeck API Java Client 11 + X-RunDeck-Auth-Token: 8Dp9op111ER6opsDRkddvE86K9sE499s + response: + status: 201 + headers: + Content-Type: application/xml;charset=UTF-8 + Expires: Thu, 01 Jan 1970 00:00:00 GMT + Server: Jetty(7.6.0.v20120127) + Set-Cookie: JSESSIONID=1gt9t2gch2zff1a0werz1us5wk;Path=/ + X-Rundeck-API-Version: '11' + X-Rundeck-API-XML-Response-Wrapper: 'false' + body: !!binary |- + PHRva2VuIGlkPSdNaXF1UWpFTFRyRWF1Z3BtZGdBS3MxVzNhN3hvbkF3VScgdXNlcj0nYm9iJyAvPg== diff --git a/src/test/resources/betamax/tapes/api_token_get.yaml b/src/test/resources/betamax/tapes/api_token_get.yaml new file mode 100644 index 0000000..8377d6f --- /dev/null +++ b/src/test/resources/betamax/tapes/api_token_get.yaml @@ -0,0 +1,24 @@ +!tape +name: api_token_get +interactions: +- recorded: 2014-04-04T18:23:05.986Z + request: + method: GET + uri: http://rundeck.local:4440/api/11/token/MiquQjELTrEaugpmdgAKs1W3a7xonAwU + headers: + Accept: text/xml + Host: rundeck.local:4440 + Proxy-Connection: Keep-Alive + User-Agent: RunDeck API Java Client 11 + X-RunDeck-Auth-Token: 8Dp9op111ER6opsDRkddvE86K9sE499s + response: + status: 200 + headers: + Content-Type: application/xml;charset=UTF-8 + Expires: Thu, 01 Jan 1970 00:00:00 GMT + Server: Jetty(7.6.0.v20120127) + Set-Cookie: JSESSIONID=1tdpszk6b3v191p0ng2u94rohw;Path=/ + X-Rundeck-API-Version: '11' + X-Rundeck-API-XML-Response-Wrapper: 'false' + body: !!binary |- + PHRva2VuIGlkPSdNaXF1UWpFTFRyRWF1Z3BtZGdBS3MxVzNhN3hvbkF3VScgdXNlcj0nYm9iJyAvPg== diff --git a/src/test/resources/betamax/tapes/api_tokens_list_all.yaml b/src/test/resources/betamax/tapes/api_tokens_list_all.yaml new file mode 100644 index 0000000..f1318ad --- /dev/null +++ b/src/test/resources/betamax/tapes/api_tokens_list_all.yaml @@ -0,0 +1,24 @@ +!tape +name: api_tokens_list_all +interactions: +- recorded: 2014-04-04T18:32:37.397Z + request: + method: GET + uri: http://rundeck.local:4440/api/11/tokens + headers: + Accept: text/xml + Host: rundeck.local:4440 + Proxy-Connection: Keep-Alive + User-Agent: RunDeck API Java Client 11 + X-RunDeck-Auth-Token: 8Dp9op111ER6opsDRkddvE86K9sE499s + response: + status: 200 + headers: + Content-Type: application/xml;charset=UTF-8 + Expires: Thu, 01 Jan 1970 00:00:00 GMT + Server: Jetty(7.6.0.v20120127) + Set-Cookie: JSESSIONID=ixag173yjktz1c5o9yrbe5z5a;Path=/ + X-Rundeck-API-Version: '11' + X-Rundeck-API-XML-Response-Wrapper: 'false' + body: !!binary |- + PHRva2VucyBjb3VudD0nNCcgYWxsdXNlcnM9J3RydWUnPgogIDx0b2tlbiBpZD0nOERwOW9wMTExRVI2b3BzRFJrZGR2RTg2SzlzRTQ5OXMnIHVzZXI9J2FkbWluJyAvPgogIDx0b2tlbiBpZD0naElOcDVlR3p2WUE5VWVQYlVDaGFLSGQ1TmlSa3dXYngnIHVzZXI9J2JvYicgLz4KICA8dG9rZW4gaWQ9J05hTm53VnpBSEFHODNxT1M3V3R3aDZtamNYVml5V1VWJyB1c2VyPSdib2InIC8+CiAgPHRva2VuIGlkPSdNaXF1UWpFTFRyRWF1Z3BtZGdBS3MxVzNhN3hvbkF3VScgdXNlcj0nYm9iJyAvPgo8L3Rva2Vucz4= diff --git a/src/test/resources/betamax/tapes/api_tokens_list_user.yaml b/src/test/resources/betamax/tapes/api_tokens_list_user.yaml new file mode 100644 index 0000000..15ec26d --- /dev/null +++ b/src/test/resources/betamax/tapes/api_tokens_list_user.yaml @@ -0,0 +1,24 @@ +!tape +name: api_tokens_list_user +interactions: +- recorded: 2014-04-04T18:26:33.394Z + request: + method: GET + uri: http://rundeck.local:4440/api/11/tokens/bob + headers: + Accept: text/xml + Host: rundeck.local:4440 + Proxy-Connection: Keep-Alive + User-Agent: RunDeck API Java Client 11 + X-RunDeck-Auth-Token: 8Dp9op111ER6opsDRkddvE86K9sE499s + response: + status: 200 + headers: + Content-Type: application/xml;charset=UTF-8 + Expires: Thu, 01 Jan 1970 00:00:00 GMT + Server: Jetty(7.6.0.v20120127) + Set-Cookie: JSESSIONID=114794elwavo26cx4ugkv7pe7;Path=/ + X-Rundeck-API-Version: '11' + X-Rundeck-API-XML-Response-Wrapper: 'false' + body: !!binary |- + PHRva2VucyBjb3VudD0nMycgdXNlcj0nYm9iJz4KICA8dG9rZW4gaWQ9J2hJTnA1ZUd6dllBOVVlUGJVQ2hhS0hkNU5pUmt3V2J4JyB1c2VyPSdib2InIC8+CiAgPHRva2VuIGlkPSdOYU5ud1Z6QUhBRzgzcU9TN1d0d2g2bWpjWFZpeVdVVicgdXNlcj0nYm9iJyAvPgogIDx0b2tlbiBpZD0nTWlxdVFqRUxUckVhdWdwbWRnQUtzMVczYTd4b25Bd1UnIHVzZXI9J2JvYicgLz4KPC90b2tlbnM+