From eea5ebe81b48be6acec4a40de1d11cc0c376f119 Mon Sep 17 00:00:00 2001 From: Selvasundaram Date: Mon, 18 Jul 2011 22:11:44 +0530 Subject: Import SSH keys feature --- .../.classpath | 1 + .../META-INF/MANIFEST.MF | 1 + .../build.properties | 3 +- .../lib/jersey-1.5/jersey-multipart-1.5.jar | Bin 0 -> 49330 bytes .../storage/management/client/AbstractClient.java | 51 +++++++-------- .../storage/management/client/KeysClient.java | 20 +++--- .../gui/actions/AbstractActionDelegate.java | 20 ++---- .../gui/actions/ImportSshKeysAction.java | 35 +++++++++-- .../WEB-INF/lib/jersey-multipart-1.5.jar | Bin 0 -> 49330 bytes .../WebContent/WEB-INF/lib/mimepull-1.3.jar | Bin 0 -> 38683 bytes .../server/resources/v1_0/KeysResource.java | 69 +++++++++++++++++++-- .../storage/management/server/utils/SshUtil.java | 2 +- 12 files changed, 145 insertions(+), 57 deletions(-) create mode 100644 src/com.gluster.storage.management.client/lib/jersey-1.5/jersey-multipart-1.5.jar create mode 100644 src/com.gluster.storage.management.server/WebContent/WEB-INF/lib/jersey-multipart-1.5.jar create mode 100644 src/com.gluster.storage.management.server/WebContent/WEB-INF/lib/mimepull-1.3.jar (limited to 'src') diff --git a/src/com.gluster.storage.management.client/.classpath b/src/com.gluster.storage.management.client/.classpath index 218503d5..b90a101e 100644 --- a/src/com.gluster.storage.management.client/.classpath +++ b/src/com.gluster.storage.management.client/.classpath @@ -1,5 +1,6 @@ + diff --git a/src/com.gluster.storage.management.client/META-INF/MANIFEST.MF b/src/com.gluster.storage.management.client/META-INF/MANIFEST.MF index 56cfe32e..dcd16136 100644 --- a/src/com.gluster.storage.management.client/META-INF/MANIFEST.MF +++ b/src/com.gluster.storage.management.client/META-INF/MANIFEST.MF @@ -12,4 +12,5 @@ Export-Package: com.gluster.storage.management.client, Bundle-ClassPath: ., lib/jersey-1.5/jersey-client-1.5.jar, lib/jersey-1.5/jersey-core-1.5.jar, + lib/jersey-1.5/jersey-multipart-1.5.jar, keystore/ diff --git a/src/com.gluster.storage.management.client/build.properties b/src/com.gluster.storage.management.client/build.properties index 4dded7a7..bc0c59d7 100644 --- a/src/com.gluster.storage.management.client/build.properties +++ b/src/com.gluster.storage.management.client/build.properties @@ -3,7 +3,8 @@ output.. = bin/ bin.includes = .,\ META-INF/,\ lib/,\ - keystore/ + keystore/,\ + lib/jersey-1.5/jersey-multipart-1.5.jar src.includes = src/,\ lib/,\ keystore/,\ diff --git a/src/com.gluster.storage.management.client/lib/jersey-1.5/jersey-multipart-1.5.jar b/src/com.gluster.storage.management.client/lib/jersey-1.5/jersey-multipart-1.5.jar new file mode 100644 index 00000000..1c134f05 Binary files /dev/null and b/src/com.gluster.storage.management.client/lib/jersey-1.5/jersey-multipart-1.5.jar differ diff --git a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/AbstractClient.java b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/AbstractClient.java index 92037741..64a9a653 100644 --- a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/AbstractClient.java +++ b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/AbstractClient.java @@ -35,7 +35,7 @@ import com.sun.jersey.api.client.config.DefaultClientConfig; import com.sun.jersey.api.representation.Form; import com.sun.jersey.client.urlconnection.HTTPSProperties; import com.sun.jersey.core.util.MultivaluedMapImpl; - +import com.sun.jersey.multipart.FormDataMultiPart; public abstract class AbstractClient { private static final String HTTP_HEADER_AUTH = "Authorization"; @@ -66,7 +66,7 @@ public abstract class AbstractClient { this.clusterName = clusterName; setSecurityToken(securityToken); - createClient(); + createClient(); // this must be after setting clusterName as sub-classes may refer to cluster name in the getResourcePath method resource = client.resource(ClientUtil.getServerBaseURI()).path(getResourcePath()); @@ -143,10 +143,10 @@ public abstract class AbstractClient { } private GlusterRuntimeException createGlusterException(Exception e) { - if(e instanceof GlusterRuntimeException) { - return (GlusterRuntimeException)e; + if (e instanceof GlusterRuntimeException) { + return (GlusterRuntimeException) e; } - + if (e instanceof UniformInterfaceException) { UniformInterfaceException uie = (UniformInterfaceException) e; if ((uie.getResponse().getStatus() == Response.Status.UNAUTHORIZED.getStatusCode())) { @@ -177,9 +177,9 @@ public abstract class AbstractClient { InputStream inputStream = response.getEntityInputStream(); FileOutputStream outputStream = new FileOutputStream(filePath); - + int c; - while((c = inputStream.read()) != -1) { + while ((c = inputStream.read()) != -1) { outputStream.write(c); } inputStream.close(); @@ -188,13 +188,14 @@ public abstract class AbstractClient { throw new GlusterRuntimeException("Error while downloading resource [" + res.getURI().getPath() + "]", e); } } - - -/* public void uploadResource(WebResource res, FormDataMultiPart form) { - ClientResponse response = res.header(HTTP_HEADER_AUTH, authHeader).type(MediaType.MULTIPART_FORM_DATA) - .accept(MediaType.TEXT_PLAIN).header(name, value)post(form); + + public void uploadResource(WebResource res, FormDataMultiPart form) { + try { + res.header(HTTP_HEADER_AUTH, authHeader).type(MediaType.MULTIPART_FORM_DATA_TYPE).post(String.class, form); + } catch (Exception e) { + throw new GlusterRuntimeException("Error while importing resource [" + e.getMessage() + "]", e); + } } -*/ /** * Fetches the default resource (the one returned by {@link AbstractClient#getResourcePath()}) by dispatching a GET @@ -306,7 +307,7 @@ public abstract class AbstractClient { protected void postRequest(String subResourceName, Form form) { postRequest(resource.path(subResourceName), form); } - + private ClientResponse putRequest(WebResource resource, Form form) { try { ClientResponse response = prepareFormRequestBuilder(resource).put(ClientResponse.class, form); @@ -315,7 +316,7 @@ public abstract class AbstractClient { setSecurityToken(null); throw new GlusterRuntimeException("Invalid credentials!"); } - if(response.getStatus() >= 300) { + if (response.getStatus() >= 300) { throw new GlusterRuntimeException(response.getEntity(String.class)); } return response; @@ -325,10 +326,10 @@ public abstract class AbstractClient { } public Builder prepareFormRequestBuilder(WebResource resource) { - return resource.type(MediaType.APPLICATION_FORM_URLENCODED_TYPE) - .header(HTTP_HEADER_AUTH, authHeader).accept(MediaType.APPLICATION_XML); + return resource.type(MediaType.APPLICATION_FORM_URLENCODED_TYPE).header(HTTP_HEADER_AUTH, authHeader) + .accept(MediaType.APPLICATION_XML); } - + /** * Submits given Form using PUT method to the given sub-resource and returns the object received as response * @@ -340,8 +341,7 @@ public abstract class AbstractClient { protected void putRequest(String subResourceName, Form form) { putRequest(resource.path(subResourceName), form); } - - + protected URI putRequestURI(String subResourceName, Form form) { ClientResponse response = putRequest(resource.path(subResourceName), form); return response.getLocation(); @@ -373,13 +373,12 @@ public abstract class AbstractClient { private void deleteResource(WebResource resource, MultivaluedMap queryParams) { try { - resource.queryParams(queryParams).header(HTTP_HEADER_AUTH, authHeader) - .delete(); + resource.queryParams(queryParams).header(HTTP_HEADER_AUTH, authHeader).delete(); } catch (UniformInterfaceException e) { throw new GlusterRuntimeException(e.getResponse().getEntity(String.class)); } } - + protected void deleteResource(MultivaluedMap queryParams) { deleteResource(resource, queryParams); } @@ -415,8 +414,10 @@ public abstract class AbstractClient { } /** - * @param uri The URI to be fetched using GET API - * @param responseClass Expected type of response object + * @param uri + * The URI to be fetched using GET API + * @param responseClass + * Expected type of response object * @return Object of the given class */ protected T fetchResource(URI uri, Class responseClass) { diff --git a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/KeysClient.java b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/KeysClient.java index dfadd67c..51fda466 100644 --- a/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/KeysClient.java +++ b/src/com.gluster.storage.management.client/src/com/gluster/storage/management/client/KeysClient.java @@ -20,9 +20,13 @@ */ package com.gluster.storage.management.client; -import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; + +import javax.ws.rs.core.MediaType; import com.gluster.storage.management.core.constants.RESTConstants; +import com.sun.jersey.multipart.FormDataMultiPart; public class KeysClient extends AbstractClient { @@ -30,10 +34,6 @@ public class KeysClient extends AbstractClient { super(); } - public KeysClient(String clusterName) { - super(clusterName); - } - @Override public String getResourcePath() { return RESTConstants.RESOURCE_KEYS; @@ -43,7 +43,13 @@ public class KeysClient extends AbstractClient { downloadResource(resource, filePath); } - public void importSshKeys(File keysFile) { - + public void importSshKeys(String keysFile) { + FormDataMultiPart form = new FormDataMultiPart(); + try { + form.field("file", new FileInputStream(keysFile), MediaType.TEXT_PLAIN_TYPE); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + uploadResource(resource, form); } } diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/AbstractActionDelegate.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/AbstractActionDelegate.java index 0f8121e6..ef7d0979 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/AbstractActionDelegate.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/AbstractActionDelegate.java @@ -52,17 +52,10 @@ public abstract class AbstractActionDelegate implements IWorkbenchWindowActionDe // Real action code must be executed using Display#asyncExec. // Otherwise the system can hang when opening new dialog boxes on linux platform try { - PlatformUI.getWorkbench().getProgressService().busyCursorWhile(new IRunnableWithProgress() { - public void run(final IProgressMonitor monitor) { - Display.getDefault().asyncExec(new Runnable() { - @Override - public void run() { - monitor.beginTask(action.getDescription(), 1); - performAction(action); - monitor.worked(1); - monitor.done(); - } - }); + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + performAction(action); } }); } catch (final Exception e) { @@ -102,10 +95,7 @@ public abstract class AbstractActionDelegate implements IWorkbenchWindowActionDe } protected Shell getShell() { - if(window == null) { - return Display.getDefault().getActiveShell(); - } - return window.getShell(); + return getWindow().getShell(); } protected IWorkbenchWindow getWindow() { diff --git a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/ImportSshKeysAction.java b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/ImportSshKeysAction.java index 8cedc920..a166f509 100644 --- a/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/ImportSshKeysAction.java +++ b/src/com.gluster.storage.management.gui/src/com/gluster/storage/management/gui/actions/ImportSshKeysAction.java @@ -1,17 +1,44 @@ package com.gluster.storage.management.gui.actions; import org.eclipse.jface.action.IAction; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.FileDialog; + +import com.gluster.storage.management.client.KeysClient; public class ImportSshKeysAction extends AbstractActionDelegate { - @Override protected void performAction(IAction action) { - - } + final KeysClient client = new KeysClient(); + + Display.getDefault().asyncExec(new Runnable() { + + @Override + public void run() { + FileDialog dialog = new FileDialog(getShell(), SWT.OPEN); + dialog.setText("Open"); + dialog.setFilterNames(new String[] { "ssh-keys (*.tar)" }); + dialog.setFilterExtensions(new String[] { "*.tar" }); + + String selectedFile = dialog.open(); + if (selectedFile == null) { + return; + } + + String title = "Import SSH Keys"; + try { + client.importSshKeys(selectedFile); + showInfoDialog(title, "SSH keys imported successfully!"); + } catch (Exception e) { + showErrorDialog(title, e.getMessage()); + } + } + }); + } @Override public void dispose() { } - } diff --git a/src/com.gluster.storage.management.server/WebContent/WEB-INF/lib/jersey-multipart-1.5.jar b/src/com.gluster.storage.management.server/WebContent/WEB-INF/lib/jersey-multipart-1.5.jar new file mode 100644 index 00000000..1c134f05 Binary files /dev/null and b/src/com.gluster.storage.management.server/WebContent/WEB-INF/lib/jersey-multipart-1.5.jar differ diff --git a/src/com.gluster.storage.management.server/WebContent/WEB-INF/lib/mimepull-1.3.jar b/src/com.gluster.storage.management.server/WebContent/WEB-INF/lib/mimepull-1.3.jar new file mode 100644 index 00000000..48cc9295 Binary files /dev/null and b/src/com.gluster.storage.management.server/WebContent/WEB-INF/lib/mimepull-1.3.jar differ diff --git a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/v1_0/KeysResource.java b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/v1_0/KeysResource.java index a589c2a2..5ac37bd1 100644 --- a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/v1_0/KeysResource.java +++ b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/resources/v1_0/KeysResource.java @@ -23,10 +23,16 @@ package com.gluster.storage.management.server.resources.v1_0; import static com.gluster.storage.management.core.constants.RESTConstants.RESOURCE_PATH_KEYS; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import javax.ws.rs.Consumes; import javax.ws.rs.GET; +import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; @@ -34,14 +40,17 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; +import com.gluster.storage.management.core.exceptions.GlusterRuntimeException; import com.gluster.storage.management.core.utils.FileUtil; +import com.gluster.storage.management.core.utils.ProcessResult; import com.gluster.storage.management.core.utils.ProcessUtil; import com.gluster.storage.management.server.utils.SshUtil; +import com.sun.jersey.multipart.FormDataParam; @Path(RESOURCE_PATH_KEYS) public class KeysResource extends AbstractResource { + ProcessUtil processUtil = new ProcessUtil(); - @GET @Produces(MediaType.APPLICATION_OCTET_STREAM) public Response exportSshkeys() { @@ -65,25 +74,77 @@ public class KeysResource extends AbstractResource { } } - public String createSskKeyZipFile() { + private String createSskKeyZipFile() { String targetDir = System.getProperty("java.io.tmpdir"); String zipFile = targetDir + "ssh-keys.tar"; String sourcePemFile = SshUtil.PEM_FILE.getAbsolutePath(); String sourcePubKeyFile = SshUtil.PUBLIC_KEY_FILE.getAbsolutePath(); String targetPemFile = targetDir + File.separator + SshUtil.PEM_FILE.getName(); String targetPubKeyFile = targetDir + File.separator + SshUtil.PUBLIC_KEY_FILE.getName(); - ProcessUtil processUtil = new ProcessUtil(); // Copy keys to temp folder processUtil.executeCommand("cp", sourcePemFile, targetPemFile); processUtil.executeCommand("cp", sourcePubKeyFile, targetPubKeyFile); // To zip the key files - processUtil.executeCommand("tar", "cvf", zipFile, "-C", "/tmp", SshUtil.PEM_FILE.getName(), SshUtil.PUBLIC_KEY_FILE.getName()); + processUtil.executeCommand("tar", "cvf", zipFile, "-C", "/tmp", SshUtil.PEM_FILE.getName(), + SshUtil.PUBLIC_KEY_FILE.getName()); // To remove the copied key files processUtil.executeCommand("rm", "-f", targetPubKeyFile, targetPubKeyFile); return zipFile; } + + @POST + @Consumes(MediaType.MULTIPART_FORM_DATA) + public Response importSshKeys(@FormDataParam("file") InputStream uploadedInputStream) { + File uploadedFile = new File(System.getProperty("java.io.tmpdir") + File.separator + "keys.tar"); + String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); + try { + writeToFile(uploadedInputStream, uploadedFile.getAbsolutePath()); + + // To backup existing SSH pem and public keys + if (SshUtil.PEM_FILE.isFile()) { + if (!SshUtil.PEM_FILE.renameTo(new File(SshUtil.PEM_FILE.getAbsolutePath() + "-" + timestamp))) { + throw new GlusterRuntimeException("Unable to backup pem key!"); + } + } + + if (SshUtil.PUBLIC_KEY_FILE.isFile()) { + if (!SshUtil.PUBLIC_KEY_FILE.renameTo(new File(SshUtil.PUBLIC_KEY_FILE.getAbsolutePath() + "-" + + timestamp))) { + throw new GlusterRuntimeException("Unable to backup public key!"); + } + } + // Extract SSH pem and public key files. + ProcessResult output = processUtil.executeCommand("tar", "xvf", uploadedFile.getName(), "-C", + SshUtil.SSH_AUTHORIZED_KEYS_DIR); + uploadedFile.delete(); + if (output.isSuccess()) { + return createdResponse("SSH Key imported successfully"); + } else { + return errorResponse(output.getOutput()); + } + } catch (Exception e) { + return errorResponse(e.getMessage()); + } + } + + // save uploaded file to the file (with path) + private void writeToFile(InputStream inputStream, String toFile) { + try { + int read = 0; + byte[] bytes = new byte[1024]; + + OutputStream out = new FileOutputStream(new File(toFile)); + while ((read = inputStream.read(bytes)) != -1) { + out.write(bytes, 0, read); + } + out.flush(); + out.close(); + } catch (IOException e) { + throw new GlusterRuntimeException(e.getMessage()); + } + } } diff --git a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/SshUtil.java b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/SshUtil.java index 4771a230..746b8832 100644 --- a/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/SshUtil.java +++ b/src/com.gluster.storage.management.server/src/com/gluster/storage/management/server/utils/SshUtil.java @@ -49,7 +49,7 @@ import com.gluster.storage.management.core.utils.ProcessResult; @Component public class SshUtil { private static final String TEMP_DIR = "/tmp/"; - private static final String SSH_AUTHORIZED_KEYS_DIR = "/root/.ssh/"; + public static final String SSH_AUTHORIZED_KEYS_DIR = "/root/.ssh/"; private static final String SSH_AUTHORIZED_KEYS_FILE = "authorized_keys"; private static final String SSH_AUTHORIZED_KEYS_PATH = SSH_AUTHORIZED_KEYS_DIR + SSH_AUTHORIZED_KEYS_FILE; private LRUCache sshConnCache = new LRUCache(10); -- cgit