Add support for SSH key management (api v11)

This commit is contained in:
Greg Schueler 2014-04-04 13:47:01 -07:00
parent c7153a5613
commit 459b498d35
21 changed files with 1002 additions and 6 deletions

View file

@ -387,10 +387,14 @@ class ApiCall {
if (null != apiPath.getAccept()) { if (null != apiPath.getAccept()) {
request.setHeader("Accept", apiPath.getAccept()); request.setHeader("Accept", apiPath.getAccept());
} }
WriteOutHandler handler = new WriteOutHandler(outputStream); final WriteOutHandler writeOutHandler = new WriteOutHandler(outputStream);
int wrote = execute(request, handler); Handler<HttpResponse,Integer> handler = writeOutHandler;
if(handler.thrown!=null){ if(null!=apiPath.getRequiredContentType()){
throw handler.thrown; handler = new RequireContentTypeHandler<Integer>(apiPath.getRequiredContentType(), handler);
}
final int wrote = execute(request, handler);
if(writeOutHandler.thrown!=null){
throw writeOutHandler.thrown;
} }
return wrote; return wrote;
} }
@ -435,6 +439,51 @@ class ApiCall {
} }
} }
/**
* Handles writing response to an output stream
*/
private static class ChainHandler<T> implements Handler<HttpResponse,T> {
Handler<HttpResponse, T> chain;
private ChainHandler(Handler<HttpResponse,T> chain) {
this.chain=chain;
}
@Override
public T handle(final HttpResponse response) {
return chain.handle(response);
}
}
/**
* Handles writing response to an output stream
*/
private static class RequireContentTypeHandler<T> extends ChainHandler<T> {
String contentType;
private RequireContentTypeHandler(final String contentType, final Handler<HttpResponse, T> chain) {
super(chain);
this.contentType = contentType;
}
@Override
public T handle(final HttpResponse response) {
final Header firstHeader = response.getFirstHeader("Content-Type");
final String[] split = firstHeader.getValue().split(";");
boolean matched=false;
for (int i = 0; i < split.length; i++) {
String s = split[i];
if (this.contentType.equalsIgnoreCase(s.trim())) {
matched=true;
break;
}
}
if(!matched) {
throw new RundeckApiException.RundeckApiHttpContentTypeException(firstHeader.getValue(),
this.contentType);
}
return super.handle(response);
}
}
/** /**
* Handles writing response to an output stream * Handles writing response to an output stream
*/ */

View file

@ -50,6 +50,7 @@ class ApiPathBuilder {
private InputStream contentStream; private InputStream contentStream;
private File contentFile; private File contentFile;
private String contentType; private String contentType;
private String requiredContentType;
private boolean emptyContent = false; private boolean emptyContent = false;
/** Marker for using the right separator between parameters ("?" or "&") */ /** Marker for using the right separator between parameters ("?" or "&") */
@ -416,6 +417,16 @@ class ApiPathBuilder {
public boolean isEmptyContent() { public boolean isEmptyContent() {
return emptyContent; return emptyContent;
} }
public ApiPathBuilder requireContentType(String contentType) {
this.requiredContentType=contentType;
return this;
}
public String getRequiredContentType() {
return requiredContentType;
}
/** /**
* BuildsParameters can add URL or POST parameters to an {@link ApiPathBuilder} * BuildsParameters can add URL or POST parameters to an {@link ApiPathBuilder}
* *

View file

@ -105,4 +105,42 @@ public class RundeckApiException extends RuntimeException {
} }
} }
/**
* Error due to unexpected HTTP content-type
*/
public static class RundeckApiHttpContentTypeException extends RundeckApiAuthException {
private static final long serialVersionUID = 1L;
private String contentType;
private String requiredContentType;
public RundeckApiHttpContentTypeException(final String contentType,
final String requiredContentType) {
super("Unexpected content-type: '" + contentType + "', expected: '" + requiredContentType + "'");
this.contentType = contentType;
this.requiredContentType = requiredContentType;
}
public RundeckApiHttpContentTypeException(final String message, final String contentType,
final String requiredContentType) {
super(message);
this.contentType = contentType;
this.requiredContentType = requiredContentType;
}
public RundeckApiHttpContentTypeException(final String message, final Throwable cause, final String contentType,
final String requiredContentType) {
super(message, cause);
this.contentType = contentType;
this.requiredContentType = requiredContentType;
}
public String getContentType() {
return contentType;
}
public String getRequiredContentType() {
return requiredContentType;
}
}
} }

View file

@ -19,8 +19,6 @@ import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.dom4j.Document; import org.dom4j.Document;
import org.dom4j.DocumentFactory;
import org.dom4j.Element;
import org.rundeck.api.RundeckApiException.RundeckApiLoginException; import org.rundeck.api.RundeckApiException.RundeckApiLoginException;
import org.rundeck.api.RundeckApiException.RundeckApiTokenException; import org.rundeck.api.RundeckApiException.RundeckApiTokenException;
import org.rundeck.api.domain.*; import org.rundeck.api.domain.*;
@ -83,6 +81,8 @@ public class RundeckClient implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public static final String JOBS_IMPORT = "/jobs/import"; public static final String JOBS_IMPORT = "/jobs/import";
public static final String STORAGE_ROOT_PATH = "/storage/";
public static final String SSH_KEY_PATH = "ssh-key/";
/** /**
* Supported version numbers * Supported version numbers
@ -2374,6 +2374,139 @@ public class RundeckClient implements Serializable {
return new ApiCall(this).get(new ApiPathBuilder("/token/", token), new RundeckTokenParser("/token")); return new ApiCall(this).get(new ApiPathBuilder("/token/", token), new RundeckTokenParser("/token"));
} }
/**
* Store an SSH key file
* @param path ssh key storage path, must start with "ssh-key/"
* @param keyfile key file
* @param privateKey true to store private key, false to store public key
* @return the SSH key resource
* @throws RundeckApiException
*/
public SSHKeyResource storeSshKey(final String path, final File keyfile, boolean privateKey) throws RundeckApiException{
AssertUtil.notNull(path, "path is mandatory to store an SSH key.");
AssertUtil.notNull(keyfile, "keyfile is mandatory to store an SSH key.");
if (!path.startsWith(SSH_KEY_PATH)) {
throw new IllegalArgumentException("SSH key storage path must start with: " + SSH_KEY_PATH);
}
return new ApiCall(this).post(
new ApiPathBuilder(STORAGE_ROOT_PATH, path).content(
privateKey ? "application/octet-stream" : "application/pgp-keys",
keyfile
),
new SSHKeyResourceParser("/resource")
);
}
/**
* Get metadata for an SSH key file
*
* @param path ssh key storage path, must start with "ssh-key/"
*
* @return the ssh key resource
*
* @throws RundeckApiException if there is an error, or if the path is a directory not a file
*/
public SSHKeyResource getSshKey(final String path) throws RundeckApiException {
AssertUtil.notNull(path, "path is mandatory to get an SSH key.");
if (!path.startsWith(SSH_KEY_PATH)) {
throw new IllegalArgumentException("SSH key storage path must start with: " + SSH_KEY_PATH);
}
SSHKeyResource storageResource = new ApiCall(this).get(
new ApiPathBuilder(STORAGE_ROOT_PATH, path),
new SSHKeyResourceParser("/resource")
);
if (storageResource.isDirectory()) {
throw new RundeckApiException("SSH Key Path is a directory: " + path);
}
return storageResource;
}
/**
* Get content for a public SSH key file
* @param path ssh key storage path, must start with "ssh-key/"
* @param out outputstream to write data to
*
* @return length of written data
* @throws RundeckApiException
*/
public int getPublicSshKeyContent(final String path, final OutputStream out) throws
RundeckApiException, IOException {
AssertUtil.notNull(path, "path is mandatory to get an SSH key.");
if (!path.startsWith(SSH_KEY_PATH)) {
throw new IllegalArgumentException("SSH key storage path must start with: " + SSH_KEY_PATH);
}
try {
return new ApiCall(this).get(
new ApiPathBuilder(STORAGE_ROOT_PATH, path)
.accept("application/pgp-keys")
.requireContentType("application/pgp-keys"),
out
);
} catch (RundeckApiException.RundeckApiHttpContentTypeException e) {
throw new RundeckApiException("Requested SSH Key path was not a Public key: " + path, e);
}
}
/**
* Get content for a public SSH key file
* @param path ssh key storage path, must start with "ssh-key/"
* @param out file to write data to
* @return length of written data
* @throws RundeckApiException
*/
public int getPublicSshKeyContent(final String path, final File out) throws
RundeckApiException, IOException {
final FileOutputStream fileOutputStream = new FileOutputStream(out);
try {
return getPublicSshKeyContent(path, fileOutputStream);
}finally {
fileOutputStream.close();
}
}
/**
* List contents of root SSH key directory
*
* @return list of SSH key resources
* @throws RundeckApiException
*/
public List<SSHKeyResource> listSshKeyDirectoryRoot() throws RundeckApiException {
return listSshKeyDirectory(SSH_KEY_PATH);
}
/**
* List contents of SSH key directory
*
* @param path ssh key storage path, must start with "ssh-key/"
*
* @throws RundeckApiException if there is an error, or if the path is a file not a directory
*/
public List<SSHKeyResource> listSshKeyDirectory(final String path) throws RundeckApiException {
AssertUtil.notNull(path, "path is mandatory to get an SSH key.");
if (!path.startsWith(SSH_KEY_PATH)) {
throw new IllegalArgumentException("SSH key storage path must start with: " + SSH_KEY_PATH);
}
SSHKeyResource storageResource = new ApiCall(this).get(
new ApiPathBuilder(STORAGE_ROOT_PATH, path),
new SSHKeyResourceParser("/resource")
);
if(!storageResource.isDirectory()) {
throw new RundeckApiException("SSH key path is not a directory path: " + path);
}
return storageResource.getDirectoryContents();
}
/**
* Delete an SSH key file
* @param path a path to a SSH key file, must start with "ssh-key/"
*/
public void deleteSshKey(final String path){
AssertUtil.notNull(path, "path is mandatory to delete an SSH key.");
if (!path.startsWith(SSH_KEY_PATH)) {
throw new IllegalArgumentException("SSH key storage path must start with: " + SSH_KEY_PATH);
}
new ApiCall(this).delete(new ApiPathBuilder(STORAGE_ROOT_PATH, path));
}
/** /**
* @return the URL of the RunDeck instance ("http://localhost:4440", "http://rundeck.your-compagny.com/", etc) * @return the URL of the RunDeck instance ("http://localhost:4440", "http://rundeck.your-compagny.com/", etc)
*/ */

View file

@ -0,0 +1,61 @@
package org.rundeck.api.domain;
import org.rundeck.api.RundeckClient;
import org.rundeck.api.parser.StorageResourceParser;
import java.util.ArrayList;
import java.util.List;
/**
* BaseSSHKeyResource is ...
*
* @author Greg Schueler <greg@simplifyops.com>
* @since 2014-04-04
*/
public class BaseSSHKeyResource extends BaseStorageResource implements SSHKeyResource {
private boolean privateKey;
public BaseSSHKeyResource() {
}
public boolean isPrivateKey() {
return privateKey;
}
public void setPrivateKey(boolean privateKey) {
this.privateKey = privateKey;
}
ArrayList<SSHKeyResource> sshKeyResources = new ArrayList<SSHKeyResource>();
@Override
public void setDirectoryContents(List<? extends StorageResource> directoryContents) {
for (StorageResource directoryContent : directoryContents) {
sshKeyResources.add(from(directoryContent));
}
}
@Override
public List<SSHKeyResource> getDirectoryContents() {
return sshKeyResources;
}
public static BaseSSHKeyResource from(final StorageResource source) {
final BaseSSHKeyResource baseSshKeyResource = new BaseSSHKeyResource();
baseSshKeyResource.setDirectory(source.isDirectory());
baseSshKeyResource.setPath(source.getPath());
baseSshKeyResource.setName(source.getName());
baseSshKeyResource.setMetadata(source.getMetadata());
baseSshKeyResource.setUrl(source.getUrl());
if (!baseSshKeyResource.isDirectory()) {
baseSshKeyResource.setPrivateKey(
null != baseSshKeyResource.getMetadata() && "private".equals(baseSshKeyResource.getMetadata().get
("Rundeck-ssh-key-type"))
);
} else if (null != source.getDirectoryContents()) {
baseSshKeyResource.setDirectoryContents(source.getDirectoryContents());
}
return baseSshKeyResource;
}
}

View file

@ -0,0 +1,86 @@
package org.rundeck.api.domain;
import java.util.List;
import java.util.Map;
/**
* BaseStorageResource is ...
*
* @author Greg Schueler <greg@simplifyops.com>
* @since 2014-04-04
*/
public class BaseStorageResource implements StorageResource {
private String path;
private String url;
private String name;
private Map<String,String> metadata;
private boolean directory;
private List<? extends StorageResource> directoryContents;
public BaseStorageResource() {
}
@Override
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
@Override
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Map<String, String> getMetadata() {
return metadata;
}
public void setMetadata(Map<String, String> metadata) {
this.metadata = metadata;
}
@Override
public boolean isDirectory() {
return directory;
}
public void setDirectory(boolean directory) {
this.directory = directory;
}
@Override
public List<? extends StorageResource> getDirectoryContents() {
return directoryContents;
}
public void setDirectoryContents(List<? extends StorageResource> directoryContents) {
this.directoryContents = directoryContents;
}
@Override
public String toString() {
return "BaseStorageResource{" +
"path='" + path + '\'' +
", url='" + url + '\'' +
", name='" + name + '\'' +
", directory=" + directory +
'}';
}
}

View file

@ -0,0 +1,23 @@
package org.rundeck.api.domain;
import java.util.List;
/**
* SSHKeyResource represents a directory or an SSH key file
*
* @author Greg Schueler <greg@simplifyops.com>
* @since 2014-04-04
*/
public interface SSHKeyResource extends StorageResource {
/**
* Return true if this is a file and is a private SSH key file.
* @return
*/
public boolean isPrivateKey();
/**
* Return the list of SSH Key resources if this is a directory
* @return
*/
public List<SSHKeyResource> getDirectoryContents();
}

View file

@ -0,0 +1,54 @@
package org.rundeck.api.domain;
import java.util.List;
import java.util.Map;
/**
* StorageResource represents a directory or a file
*
* @author Greg Schueler <greg@simplifyops.com>
* @since 2014-04-04
*/
public interface StorageResource {
/**
* Return the storage path for this resource
*
* @return
*/
public String getPath();
/**
* Return the URL for this resource
*
* @return
*/
public String getUrl();
/**
* Return the file name if this is a file
*
* @return
*/
public String getName();
/**
* Return the metadata for this file if this is a file
*
* @return
*/
public Map<String, String> getMetadata();
/**
* Return true if this is a directory, false if this is a file
*
* @return
*/
public boolean isDirectory();
/**
* Return the list of directory contents if this is a directory
*
* @return
*/
public List<? extends StorageResource> getDirectoryContents();
}

View file

@ -0,0 +1,29 @@
package org.rundeck.api.parser;
import org.dom4j.Node;
/**
* BaseXpathParser is ...
*
* @author Greg Schueler <greg@simplifyops.com>
* @since 2014-04-04
*/
public abstract class BaseXpathParser<T> implements XmlNodeParser<T> {
private String xpath;
public BaseXpathParser() {
}
public BaseXpathParser(String xpath) {
this.xpath = xpath;
}
public abstract T parse(Node node);
@Override
public T parseXmlNode(Node node) {
Node selectedNode = xpath != null ? node.selectSingleNode(xpath) : node;
return parse(selectedNode);
}
}

View file

@ -0,0 +1,26 @@
package org.rundeck.api.parser;
import org.dom4j.Node;
import org.rundeck.api.RundeckClient;
import org.rundeck.api.domain.BaseSSHKeyResource;
import org.rundeck.api.domain.SSHKeyResource;
/**
* SSHKeyResourceParser is ...
*
* @author Greg Schueler <greg@simplifyops.com>
* @since 2014-04-04
*/
public class SSHKeyResourceParser extends BaseXpathParser<SSHKeyResource> implements XmlNodeParser<SSHKeyResource> {
public SSHKeyResourceParser() {
}
public SSHKeyResourceParser(String xpath) {
super(xpath);
}
@Override
public SSHKeyResource parse(Node node) {
return BaseSSHKeyResource.from(new StorageResourceParser().parse(node));
}
}

View file

@ -0,0 +1,61 @@
package org.rundeck.api.parser;
import org.dom4j.Element;
import org.dom4j.Node;
import org.rundeck.api.domain.BaseStorageResource;
import org.rundeck.api.domain.StorageResource;
import java.util.HashMap;
/**
* StorageResourceParser is ...
*
* @author Greg Schueler <greg@simplifyops.com>
* @since 2014-04-04
*/
public class StorageResourceParser extends BaseXpathParser<StorageResource> {
private BaseStorageResource holder;
public StorageResourceParser() {
}
public StorageResourceParser(BaseStorageResource holder){
this.holder=holder;
}
public StorageResourceParser(String xpath) {
super(xpath);
}
@Override
public StorageResource parse(Node node) {
String path = node.valueOf("@path");
String type = node.valueOf("@type");
String url = node.valueOf("@url");
BaseStorageResource storageResource = null == holder ? new BaseStorageResource() : holder;
storageResource.setDirectory("directory".equals(type));
storageResource.setPath(path);
storageResource.setUrl(url);
if (storageResource.isDirectory()) {
if (node.selectSingleNode("contents") != null) {
storageResource.setDirectoryContents(new ListParser<StorageResource>(new StorageResourceParser(),
"contents/resource").parseXmlNode(node));
}
} else {
String name = node.valueOf("@name");
storageResource.setName(name);
Node meta = node.selectSingleNode("resource-meta");
HashMap<String, String> metamap = new HashMap<String, String>();
if (null != meta) {
Element metaEl = (Element) meta;
for (Object o : metaEl.elements()) {
Element sub = (Element) o;
metamap.put(sub.getName(), sub.getText().trim());
}
}
storageResource.setMetadata(metamap);
}
return storageResource;
}
}

View file

@ -1280,6 +1280,215 @@ public class RundeckClientTest {
Assert.assertEquals(404, e.getStatusCode()); Assert.assertEquals(404, e.getStatusCode());
} }
} }
/**
* Store ssh key
*/
@Test
@Betamax(tape = "ssh_key_store_private", mode = TapeMode.READ_ONLY)
public void storeSshKey_private() throws Exception {
final RundeckClient client = createClient(TEST_TOKEN_7, 11);
File temp = File.createTempFile("test-key", ".tmp");
temp.deleteOnExit();
FileOutputStream out = new FileOutputStream(temp);
try{
out.write("test1".getBytes());
}finally {
out.close();
}
SSHKeyResource storageResource = client.storeSshKey("ssh-key/test/example/file1.pem", temp, true);
Assert.assertNotNull(storageResource);
Assert.assertFalse(storageResource.isDirectory());
Assert.assertTrue(storageResource.isPrivateKey());
Assert.assertEquals("file1.pem", storageResource.getName());
Assert.assertEquals("ssh-key/test/example/file1.pem", storageResource.getPath());
Assert.assertEquals("http://dignan.local:4440/api/11/storage/ssh-key/test/example/file1.pem",
storageResource.getUrl());
Assert.assertEquals(0, storageResource.getDirectoryContents().size());
Map<String, String> metadata = storageResource.getMetadata();
Assert.assertNotNull(metadata);
Assert.assertEquals("application/octet-stream", metadata.get("Rundeck-content-type"));
Assert.assertEquals("private", metadata.get("Rundeck-ssh-key-type"));
}
/**
* Store ssh key
*/
@Test
@Betamax(tape = "ssh_key_store_public", mode = TapeMode.READ_ONLY)
public void storeSshKey_public() throws Exception {
final RundeckClient client = createClient(TEST_TOKEN_7, 11);
File temp = File.createTempFile("test-key", ".tmp");
temp.deleteOnExit();
FileOutputStream out = new FileOutputStream(temp);
try{
out.write("test1".getBytes());
}finally {
out.close();
}
SSHKeyResource storageResource = client.storeSshKey("ssh-key/test/example/file2.pub", temp, false);
Assert.assertNotNull(storageResource);
Assert.assertFalse(storageResource.isDirectory());
Assert.assertFalse(storageResource.isPrivateKey());
Assert.assertEquals("file2.pub", storageResource.getName());
Assert.assertEquals("ssh-key/test/example/file2.pub", storageResource.getPath());
Assert.assertEquals("http://dignan.local:4440/api/11/storage/ssh-key/test/example/file2.pub",
storageResource.getUrl());
Assert.assertEquals(0, storageResource.getDirectoryContents().size());
Map<String, String> metadata = storageResource.getMetadata();
Assert.assertNotNull(metadata);
Assert.assertEquals("application/pgp-keys", metadata.get("Rundeck-content-type"));
Assert.assertEquals("public", metadata.get("Rundeck-ssh-key-type"));
}
/**
* get ssh key
*/
@Test
@Betamax(tape = "ssh_key_get_public", mode = TapeMode.READ_ONLY)
public void getSshKey_public() throws Exception {
final RundeckClient client = createClient(TEST_TOKEN_7, 11);
SSHKeyResource storageResource = client.getSshKey("ssh-key/test/example/file2.pub");
Assert.assertNotNull(storageResource);
Assert.assertFalse(storageResource.isDirectory());
Assert.assertFalse(storageResource.isPrivateKey());
Assert.assertEquals("file2.pub", storageResource.getName());
Assert.assertEquals("ssh-key/test/example/file2.pub", storageResource.getPath());
Assert.assertEquals("http://dignan.local:4440/api/11/storage/ssh-key/test/example/file2.pub",
storageResource.getUrl());
Assert.assertEquals(0, storageResource.getDirectoryContents().size());
Map<String, String> metadata = storageResource.getMetadata();
Assert.assertNotNull(metadata);
Assert.assertEquals("application/pgp-keys", metadata.get("Rundeck-content-type"));
Assert.assertEquals("public", metadata.get("Rundeck-ssh-key-type"));
}
/**
* get ssh key
*/
@Test
@Betamax(tape = "ssh_key_get_private", mode = TapeMode.READ_ONLY)
public void getSshKey_private() throws Exception {
final RundeckClient client = createClient(TEST_TOKEN_7, 11);
SSHKeyResource storageResource = client.getSshKey("ssh-key/test/example/file1.pem");
Assert.assertNotNull(storageResource);
Assert.assertFalse(storageResource.isDirectory());
Assert.assertTrue(storageResource.isPrivateKey());
Assert.assertEquals("file1.pem", storageResource.getName());
Assert.assertEquals("ssh-key/test/example/file1.pem", storageResource.getPath());
Assert.assertEquals("http://dignan.local:4440/api/11/storage/ssh-key/test/example/file1.pem",
storageResource.getUrl());
Assert.assertEquals(0, storageResource.getDirectoryContents().size());
Map<String, String> metadata = storageResource.getMetadata();
Assert.assertNotNull(metadata);
Assert.assertEquals("application/octet-stream", metadata.get("Rundeck-content-type"));
Assert.assertEquals("private", metadata.get("Rundeck-ssh-key-type"));
}
/**
* get ssh key data
*/
@Test
@Betamax(tape = "ssh_key_get_data_private", mode = TapeMode.READ_ONLY)
public void getSshKeyData_private() throws Exception {
final RundeckClient client = createClient(TEST_TOKEN_7, 11);
File temp = File.createTempFile("test-key", ".tmp");
temp.deleteOnExit();
try {
int data = client.getPublicSshKeyContent("ssh-key/test/example/file1.pem", temp);
Assert.fail("expected failure");
} catch (RundeckApiException e) {
Assert.assertEquals("Requested SSH Key path was not a Public key: ssh-key/test/example/file1.pem",
e.getMessage());
}
}
/**
* get ssh key data
*/
@Test
@Betamax(tape = "ssh_key_get_data_public", mode = TapeMode.READ_ONLY)
public void getSshKeyData_public() throws Exception {
final RundeckClient client = createClient(TEST_TOKEN_7, 11);
File temp = File.createTempFile("test-key", ".tmp");
temp.deleteOnExit();
int length = client.getPublicSshKeyContent("ssh-key/test/example/file2.pub", temp);
Assert.assertEquals(5, length);
}
/**
* list directory
*/
@Test
@Betamax(tape = "ssh_key_list_directory", mode = TapeMode.READ_ONLY)
public void listSshKeyDirectory() throws Exception {
final RundeckClient client = createClient(TEST_TOKEN_7, 11);
List<SSHKeyResource> list = client.listSshKeyDirectory("ssh-key/test/example");
Assert.assertEquals(2, list.size());
SSHKeyResource storageResource1 = list.get(0);
SSHKeyResource storageResource2 = list.get(1);
Assert.assertFalse(storageResource2.isDirectory());
Assert.assertTrue(storageResource2.isPrivateKey());
Assert.assertEquals("file1.pem", storageResource2.getName());
Assert.assertEquals("ssh-key/test/example/file1.pem", storageResource2.getPath());
Assert.assertEquals("http://dignan.local:4440/api/11/storage/ssh-key/test/example/file1.pem", storageResource2.getUrl());
Assert.assertNotNull(storageResource2.getMetadata());
Assert.assertEquals("application/octet-stream", storageResource2.getMetadata().get("Rundeck-content-type"));
Assert.assertEquals("private", storageResource2.getMetadata().get("Rundeck-ssh-key-type"));
Assert.assertFalse(storageResource1.isDirectory());
Assert.assertFalse(storageResource1.isPrivateKey());
Assert.assertEquals("file2.pub", storageResource1.getName());
Assert.assertEquals("ssh-key/test/example/file2.pub", storageResource1.getPath());
Assert.assertEquals("http://dignan.local:4440/api/11/storage/ssh-key/test/example/file2.pub",
storageResource1.getUrl());
Assert.assertNotNull(storageResource1.getMetadata());
Assert.assertEquals("application/pgp-keys", storageResource1.getMetadata().get("Rundeck-content-type"));
Assert.assertEquals("public", storageResource1.getMetadata().get("Rundeck-ssh-key-type"));
}
/**
* list root
*/
@Test
@Betamax(tape = "ssh_key_list_root", mode = TapeMode.READ_ONLY)
public void listSshKeyDirectoryRoot() throws Exception {
final RundeckClient client = createClient(TEST_TOKEN_7, 11);
List<SSHKeyResource> list = client.listSshKeyDirectoryRoot();
Assert.assertEquals(2, list.size());
SSHKeyResource storageResource0 = list.get(0);
SSHKeyResource storageResource1 = list.get(1);
Assert.assertFalse(storageResource0.isDirectory());
Assert.assertTrue(storageResource0.isPrivateKey());
Assert.assertEquals("test1.pem", storageResource0.getName());
Assert.assertEquals("ssh-key/test1.pem", storageResource0.getPath());
Assert.assertEquals("http://dignan.local:4440/api/11/storage/ssh-key/test1.pem", storageResource0.getUrl());
Assert.assertNotNull(storageResource0.getMetadata());
Assert.assertEquals("application/octet-stream", storageResource0.getMetadata().get("Rundeck-content-type"));
Assert.assertEquals("private", storageResource0.getMetadata().get("Rundeck-ssh-key-type"));
Assert.assertTrue(storageResource1.toString(), storageResource1.isDirectory());
Assert.assertEquals(null, storageResource1.getName());
Assert.assertEquals("ssh-key/test", storageResource1.getPath());
Assert.assertEquals("http://dignan.local:4440/api/11/storage/ssh-key/test",
storageResource1.getUrl());
Assert.assertNull(storageResource1.getMetadata());
}
/**
* delete ssh key
*/
@Test
@Betamax(tape = "ssh_key_delete", mode = TapeMode.READ_ONLY)
public void deleteSshKey() throws Exception {
final RundeckClient client = createClient(TEST_TOKEN_7, 11);
client.deleteSshKey("ssh-key/test/example/file2.pub");
try {
client.getSshKey("ssh-key/test/example/file2.pub");
Assert.fail("expected failure");
} catch (RundeckApiException.RundeckApiHttpStatusException e) {
Assert.assertEquals(404,e.getStatusCode());
}
}
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {

View file

@ -0,0 +1,36 @@
!tape
name: ssh_key_delete
interactions:
- recorded: 2014-04-04T23:16:02.256Z
request:
method: DELETE
uri: http://rundeck.local:4440/api/11/storage/ssh-key/test/example/file2.pub
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=3rsjs6vicpc619b1uy7oshp4y;Path=/
- recorded: 2014-04-04T23:16:02.372Z
request:
method: GET
uri: http://rundeck.local:4440/api/11/storage/ssh-key/test/example/file2.pub
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: application/xml;charset=utf-8
Server: Jetty(7.6.0.v20120127)
body: !!binary |-
PGVycm9yPnJlc291cmNlIG5vdCBmb3VuZDogL3NzaC1rZXkvdGVzdC9leGFtcGxlL2ZpbGUyLnB1YjwvZXJyb3I+

View file

@ -0,0 +1,22 @@
!tape
name: ssh_key_get_data_private
interactions:
- recorded: 2014-04-04T19:50:59.155Z
request:
method: GET
uri: http://rundeck.local:4440/api/11/storage/ssh-key/test/example/file1.pem
headers:
Accept: application/pgp-keys
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/json;charset=UTF-8
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Server: Jetty(7.6.0.v20120127)
Set-Cookie: JSESSIONID=1gzu37lkjr0fitxhf5fgkgsfu;Path=/
body: !!binary |-
eyJwYXRoIjoic3NoLWtleS90ZXN0L2V4YW1wbGUvZmlsZTEucGVtIiwidHlwZSI6ImZpbGUiLCJuYW1lIjoiZmlsZTEucGVtIiwidXJsIjoiaHR0cDovL2RpZ25hbi5sb2NhbDo0NDQwL2FwaS8xMS9zdG9yYWdlL3NzaC1rZXkvdGVzdC9leGFtcGxlL2ZpbGUxLnBlbSIsIm1ldGEiOnsiUnVuZGVjay1jb250ZW50LXR5cGUiOiJhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0iLCJSdW5kZWNrLWNvbnRlbnQtc2l6ZSI6IjUiLCJSdW5kZWNrLWNvbnRlbnQtbWFzayI6ImNvbnRlbnQiLCJSdW5kZWNrLXNzaC1rZXktdHlwZSI6InByaXZhdGUifX0=

View file

@ -0,0 +1,22 @@
!tape
name: ssh_key_get_data_public
interactions:
- recorded: 2014-04-04T20:20:44.331Z
request:
method: GET
uri: http://rundeck.local:4440/api/11/storage/ssh-key/test/example/file2.pub
headers:
Accept: application/pgp-keys
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/pgp-keys
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Server: Jetty(7.6.0.v20120127)
Set-Cookie: JSESSIONID=1mrub15qsorpf10cisx24h8h03;Path=/
body: !!binary |-
dGVzdDE=

View file

@ -0,0 +1,22 @@
!tape
name: ssh_key_get_private
interactions:
- recorded: 2014-04-04T19:47:29.880Z
request:
method: GET
uri: http://rundeck.local:4440/api/11/storage/ssh-key/test/example/file1.pem
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=nc5p0he3nw19e4gegidc4bs7;Path=/
body: !!binary |-
PHJlc291cmNlIHBhdGg9J3NzaC1rZXkvdGVzdC9leGFtcGxlL2ZpbGUxLnBlbScgdHlwZT0nZmlsZScgdXJsPSdodHRwOi8vZGlnbmFuLmxvY2FsOjQ0NDAvYXBpLzExL3N0b3JhZ2Uvc3NoLWtleS90ZXN0L2V4YW1wbGUvZmlsZTEucGVtJyBuYW1lPSdmaWxlMS5wZW0nPjxyZXNvdXJjZS1tZXRhPjxSdW5kZWNrLWNvbnRlbnQtdHlwZT5hcHBsaWNhdGlvbi9vY3RldC1zdHJlYW08L1J1bmRlY2stY29udGVudC10eXBlPjxSdW5kZWNrLWNvbnRlbnQtc2l6ZT41PC9SdW5kZWNrLWNvbnRlbnQtc2l6ZT48UnVuZGVjay1jb250ZW50LW1hc2s+Y29udGVudDwvUnVuZGVjay1jb250ZW50LW1hc2s+PFJ1bmRlY2stc3NoLWtleS10eXBlPnByaXZhdGU8L1J1bmRlY2stc3NoLWtleS10eXBlPjwvcmVzb3VyY2UtbWV0YT48L3Jlc291cmNlPg==

View file

@ -0,0 +1,22 @@
!tape
name: ssh_key_get_public
interactions:
- recorded: 2014-04-04T19:47:29.626Z
request:
method: GET
uri: http://rundeck.local:4440/api/11/storage/ssh-key/test/example/file2.pub
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=r6p6fl87ftrb1mkwwi0pcak5i;Path=/
body: !!binary |-
PHJlc291cmNlIHBhdGg9J3NzaC1rZXkvdGVzdC9leGFtcGxlL2ZpbGUyLnB1YicgdHlwZT0nZmlsZScgdXJsPSdodHRwOi8vZGlnbmFuLmxvY2FsOjQ0NDAvYXBpLzExL3N0b3JhZ2Uvc3NoLWtleS90ZXN0L2V4YW1wbGUvZmlsZTIucHViJyBuYW1lPSdmaWxlMi5wdWInPjxyZXNvdXJjZS1tZXRhPjxSdW5kZWNrLWNvbnRlbnQtdHlwZT5hcHBsaWNhdGlvbi9wZ3Ata2V5czwvUnVuZGVjay1jb250ZW50LXR5cGU+PFJ1bmRlY2stY29udGVudC1zaXplPjU8L1J1bmRlY2stY29udGVudC1zaXplPjxSdW5kZWNrLXNzaC1rZXktdHlwZT5wdWJsaWM8L1J1bmRlY2stc3NoLWtleS10eXBlPjwvcmVzb3VyY2UtbWV0YT48L3Jlc291cmNlPg==

View file

@ -0,0 +1,21 @@
!tape
name: ssh_key_list_directory
interactions:
- recorded: 2014-04-04T20:33:07.968Z
request:
method: GET
uri: http://rundeck.local:4440/api/11/storage/ssh-key/test/example
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: text/html;charset=UTF-8
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Server: Jetty(7.6.0.v20120127)
Set-Cookie: JSESSIONID=1stwtoatsosy91ca0gcrzde698;Path=/
body: <resource path='ssh-key/test/example' type='directory' url='http://dignan.local:4440/api/11/storage/ssh-key/test/example'><contents count='2'><resource path='ssh-key/test/example/file2.pub' type='file' url='http://dignan.local:4440/api/11/storage/ssh-key/test/example/file2.pub' name='file2.pub'><resource-meta><Rundeck-content-type>application/pgp-keys</Rundeck-content-type><Rundeck-content-size>5</Rundeck-content-size><Rundeck-ssh-key-type>public</Rundeck-ssh-key-type></resource-meta></resource><resource path='ssh-key/test/example/file1.pem' type='file' url='http://dignan.local:4440/api/11/storage/ssh-key/test/example/file1.pem' name='file1.pem'><resource-meta><Rundeck-content-type>application/octet-stream</Rundeck-content-type><Rundeck-content-size>5</Rundeck-content-size><Rundeck-content-mask>content</Rundeck-content-mask><Rundeck-ssh-key-type>private</Rundeck-ssh-key-type></resource-meta></resource></contents></resource>

View file

@ -0,0 +1,21 @@
!tape
name: ssh_key_list_root
interactions:
- recorded: 2014-04-04T20:41:16.501Z
request:
method: GET
uri: http://rundeck.local:4440/api/11/storage/ssh-key/
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: text/html;charset=UTF-8
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Server: Jetty(7.6.0.v20120127)
Set-Cookie: JSESSIONID=5sj36vytg4y1182mziim4868b;Path=/
body: <resource path='ssh-key' type='directory' url='http://dignan.local:4440/api/11/storage/ssh-key'><contents count='2'><resource path='ssh-key/test1.pem' type='file' url='http://dignan.local:4440/api/11/storage/ssh-key/test1.pem' name='test1.pem'><resource-meta><Rundeck-content-type>application/octet-stream</Rundeck-content-type><Rundeck-content-size>1679</Rundeck-content-size><Rundeck-content-mask>content</Rundeck-content-mask><Rundeck-ssh-key-type>private</Rundeck-ssh-key-type></resource-meta></resource><resource path='ssh-key/test' type='directory' url='http://dignan.local:4440/api/11/storage/ssh-key/test'></resource></contents></resource>

View file

@ -0,0 +1,25 @@
!tape
name: ssh_key_store_private
interactions:
- recorded: 2014-04-04T19:30:35.367Z
request:
method: POST
uri: http://rundeck.local:4440/api/11/storage/ssh-key/test/example/file1.pem
headers:
Accept: text/xml
Content-Length: '5'
Content-Type: application/octet-stream
Host: rundeck.local:4440
Proxy-Connection: Keep-Alive
User-Agent: RunDeck API Java Client 11
X-RunDeck-Auth-Token: 8Dp9op111ER6opsDRkddvE86K9sE499s
body: ''
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=ktmwc4h53xfud6v2ch67x5p9;Path=/
body: !!binary |-
PHJlc291cmNlIHBhdGg9J3NzaC1rZXkvdGVzdC9leGFtcGxlL2ZpbGUxLnBlbScgdHlwZT0nZmlsZScgdXJsPSdodHRwOi8vZGlnbmFuLmxvY2FsOjQ0NDAvYXBpLzExL3N0b3JhZ2Uvc3NoLWtleS90ZXN0L2V4YW1wbGUvZmlsZTEucGVtJyBuYW1lPSdmaWxlMS5wZW0nPjxyZXNvdXJjZS1tZXRhPjxSdW5kZWNrLWNvbnRlbnQtdHlwZT5hcHBsaWNhdGlvbi9vY3RldC1zdHJlYW08L1J1bmRlY2stY29udGVudC10eXBlPjxSdW5kZWNrLWNvbnRlbnQtc2l6ZT41PC9SdW5kZWNrLWNvbnRlbnQtc2l6ZT48UnVuZGVjay1jb250ZW50LW1hc2s+Y29udGVudDwvUnVuZGVjay1jb250ZW50LW1hc2s+PFJ1bmRlY2stc3NoLWtleS10eXBlPnByaXZhdGU8L1J1bmRlY2stc3NoLWtleS10eXBlPjwvcmVzb3VyY2UtbWV0YT48L3Jlc291cmNlPg==

View file

@ -0,0 +1,25 @@
!tape
name: ssh_key_store_public
interactions:
- recorded: 2014-04-04T19:34:02.683Z
request:
method: POST
uri: http://rundeck.local:4440/api/11/storage/ssh-key/test/example/file2.pub
headers:
Accept: text/xml
Content-Length: '5'
Content-Type: application/pgp-keys
Host: rundeck.local:4440
Proxy-Connection: Keep-Alive
User-Agent: RunDeck API Java Client 11
X-RunDeck-Auth-Token: 8Dp9op111ER6opsDRkddvE86K9sE499s
body: ''
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=2l3g8m0tvwef19jn2bu23bzk6;Path=/
body: !!binary |-
PHJlc291cmNlIHBhdGg9J3NzaC1rZXkvdGVzdC9leGFtcGxlL2ZpbGUyLnB1YicgdHlwZT0nZmlsZScgdXJsPSdodHRwOi8vZGlnbmFuLmxvY2FsOjQ0NDAvYXBpLzExL3N0b3JhZ2Uvc3NoLWtleS90ZXN0L2V4YW1wbGUvZmlsZTIucHViJyBuYW1lPSdmaWxlMi5wdWInPjxyZXNvdXJjZS1tZXRhPjxSdW5kZWNrLWNvbnRlbnQtdHlwZT5hcHBsaWNhdGlvbi9wZ3Ata2V5czwvUnVuZGVjay1jb250ZW50LXR5cGU+PFJ1bmRlY2stY29udGVudC1zaXplPjU8L1J1bmRlY2stY29udGVudC1zaXplPjxSdW5kZWNrLXNzaC1rZXktdHlwZT5wdWJsaWM8L1J1bmRlY2stc3NoLWtleS10eXBlPjwvcmVzb3VyY2UtbWV0YT48L3Jlc291cmNlPg==