com.amazonaws.eclipse.ec2.keypairs.KeyPairManager.java Source code

Java tutorial

Introduction

Here is the source code for com.amazonaws.eclipse.ec2.keypairs.KeyPairManager.java

Source

/*
 * Copyright 2008-2012 Amazon Technologies, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *    http://aws.amazon.com/apache2.0
 *
 * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
 * OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and
 * limitations under the License.
 */

package com.amazonaws.eclipse.ec2.keypairs;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;

import org.apache.commons.io.FileUtils;
import org.eclipse.core.runtime.Status;
import org.eclipse.ui.statushandlers.StatusManager;

import com.amazonaws.AmazonClientException;
import com.amazonaws.eclipse.core.AwsToolkitCore;
import com.amazonaws.eclipse.core.regions.Region;
import com.amazonaws.eclipse.core.regions.ServiceAbbreviations;
import com.amazonaws.eclipse.ec2.Ec2Plugin;
import com.amazonaws.services.ec2.AmazonEC2;
import com.amazonaws.services.ec2.model.CreateKeyPairRequest;
import com.amazonaws.services.ec2.model.CreateKeyPairResult;
import com.amazonaws.services.ec2.model.KeyPair;

/**
 * Manages the EC2 key pairs that the plugin knows about, including creating new
 * key pairs, registering private key files with a named key, etc.
 */
public class KeyPairManager {

    /** The suffix for private key files */
    private static final String PRIVATE_KEY_SUFFIX = ".pem";

    /**
     * Returns the private key file associated with the named key, or null if no
     * key file can be found.
     *
     * @param accountId
     *            The account id that owns the key name
     * @param keyPairName
     *            The name of the key being looked up.
     * @return The file path to the associated private key file for the named
     *         key.
     */
    public String lookupKeyPairPrivateKeyFile(String accountId, String keyPairName) {
        try {
            Properties registeredKeyPairs = loadRegisteredKeyPairs(accountId);
            String privateKeyPath = registeredKeyPairs.getProperty(keyPairName);
            if (privateKeyPath != null)
                return privateKeyPath;

        } catch (IOException e) {
            Status status = new Status(Status.WARNING, Ec2Plugin.PLUGIN_ID,
                    "Unable to load registered key pairs file: " + e.getMessage(), e);
            StatusManager.getManager().handle(status, StatusManager.LOG);
        }

        return null;
    }

    /**
     * Returns true if and only if the specified key pair is valid, meaning it
     * has a valid registered private key file.
     *
     * @param accountId
     *            The account id that owns the key name
     * @param keyPairName
     *            The name of the key pair to check.
     * @return True if and only if the specified key pair is valid, meaning it
     *         has a valid registered private key file.
     */
    public boolean isKeyPairValid(String accountId, String keyPairName) {
        try {
            String keyFile = lookupKeyPairPrivateKeyFile(accountId, keyPairName);
            if (keyFile == null)
                return false;

            File f = new File(keyFile);
            return f.isFile();
        } catch (Exception e) {
            // If we catch an exception, we know this must not be valid
        }

        return false;
    }

    /**
     * Requests a new key pair from EC2 with the specified name, and saves the
     * private key portion in the specified directory.
     *
     * @param accountId
     *            The account id that owns the key name
     * @param keyPairName
     *            The name of the requested key pair.
     * @param keyPairDirectory
     *            The directory in which to save the private key file.
     * @param ec2RegionOverride
     *            The region where the EC2 key pair is created.
     * @throws IOException
     *             If any problems were encountered storing the private key to
     *             disk.
     * @throws AmazonClientException
     *             If any problems were encountered requesting a new key pair
     *             from EC2.
     */
    public void createNewKeyPair(String accountId, String keyPairName, String keyPairDirectory,
            Region ec2RegionOverride) throws IOException, AmazonClientException {
        File keyPairDirectoryFile = new File(keyPairDirectory);
        if (!keyPairDirectoryFile.exists()) {
            if (!keyPairDirectoryFile.mkdirs()) {
                throw new IOException("Unable to create directory: " + keyPairDirectory);
            }
        }

        /**
         * It's possible that customers could have two keys with the same name,
         * so silently rename to avoid such a conflict. This isn't the most
         * straightforward user interface, but probably better than enforced
         * directory segregation by account, or else disallowing identical names
         * across accounts.
         */
        File privateKeyFile = new File(keyPairDirectoryFile, keyPairName + PRIVATE_KEY_SUFFIX);
        int i = 1;
        while (privateKeyFile.exists()) {
            privateKeyFile = new File(keyPairDirectoryFile, keyPairName + "-" + i + PRIVATE_KEY_SUFFIX);
        }

        CreateKeyPairRequest request = new CreateKeyPairRequest();
        request.setKeyName(keyPairName);
        AmazonEC2 ec2 = null;
        if (ec2RegionOverride == null) {
            ec2 = Ec2Plugin.getDefault().getDefaultEC2Client();
        } else {
            ec2 = AwsToolkitCore.getClientFactory()
                    .getEC2ClientByEndpoint(ec2RegionOverride.getServiceEndpoint(ServiceAbbreviations.EC2));
        }
        CreateKeyPairResult response = ec2.createKeyPair(request);
        KeyPair keyPair = response.getKeyPair();

        String privateKey = keyPair.getKeyMaterial();

        FileWriter writer = new FileWriter(privateKeyFile);
        try {
            writer.write(privateKey);
        } finally {
            writer.close();
        }

        registerKeyPair(accountId, keyPairName, privateKeyFile.getAbsolutePath());

        /*
         * SSH requires our private key be locked down.
         */
        try {
            /*
             * TODO: We should model these platform differences better (and
             * support windows).
             */
            Runtime.getRuntime().exec("chmod 600 " + privateKeyFile.getAbsolutePath());
        } catch (IOException e) {
            Status status = new Status(Status.WARNING, Ec2Plugin.PLUGIN_ID,
                    "Unable to restrict permissions on private key file: " + e.getMessage(), e);
            StatusManager.getManager().handle(status, StatusManager.LOG);
        }

    }

    /**
     * Requests a new key pair from EC2 with the specified name, and saves the
     * private key portion in the specified directory.
     *
     * @param accountId
     *            The account id that owns the key name
     * @param keyPairName
     *            The name of the requested key pair.
     * @param keyPairDirectory
     *            The directory in which to save the private key file.
     * @throws IOException
     *             If any problems were encountered storing the private key to
     *             disk.
     * @throws AmazonClientException
     *             If any problems were encountered requesting a new key pair
     *             from EC2.
     */
    public void createNewKeyPair(String accountId, String keyPairName, String keyPairDirectory)
            throws IOException, AmazonClientException {
        createNewKeyPair(accountId, keyPairName, keyPairDirectory, null);
    }

    /**
     * Returns the default directory where the plugin assumes private keys are
     * stored.
     *
     * @return The default directory where the plugin assumes private keys are
     *         stored.
     */
    public static File getDefaultPrivateKeyDirectory() {
        String userHomeDir = System.getProperty("user.home");
        if (userHomeDir == null || userHomeDir.length() == 0)
            return null;

        return new File(userHomeDir + File.separator + ".ec2");
    }

    /**
     * Registers an existing key pair and private key file with this key pair
     * manager. This method is only for *existing* key pairs. If you need a new
     * key pair created, you should be using createNewKeyPair.
     *
     * @param accountId
     *            The account id that owns the key name
     * @param keyName
     *            The name of the key being registered.
     * @param privateKeyFile
     *            The path to the private key file for the specified key pair.
     * @throws IOException
     *             If any problems are encountered adding the specified key pair
     *             to the mapping of registered key pairs.
     */
    public void registerKeyPair(String accountId, String keyName, String privateKeyFile) throws IOException {
        Properties registeredKeyPairs = loadRegisteredKeyPairs(accountId);
        registeredKeyPairs.put(keyName, privateKeyFile);
        storeRegisteredKeyPairs(accountId, registeredKeyPairs);
    }

    /**
     * Attempts to convert any legacy private key files by renaming them.
     */
    public static void convertLegacyPrivateKeyFiles() throws IOException {
        String accountId = AwsToolkitCore.getDefault().getCurrentAccountId();
        File pluginStateLocation = Ec2Plugin.getDefault().getStateLocation().toFile();
        File keyPairsFile = new File(pluginStateLocation, getKeyPropertiesFileName(accountId));

        if (!keyPairsFile.exists()) {
            File legacyKeyPairsFile = new File(pluginStateLocation, "registeredKeyPairs.properties");
            if (legacyKeyPairsFile.exists()) {
                FileUtils.copyFile(legacyKeyPairsFile, keyPairsFile);
            }
        }
    }

    /*
     * Private Interface
     */

    private Properties loadRegisteredKeyPairs(String accountId) throws IOException {
        /*
         * If the plugin isn't running (such as during tests), just return an
         * empty property list.
         */
        Ec2Plugin plugin = Ec2Plugin.getDefault();
        if (plugin == null)
            return new Properties();

        /*
         * TODO: we could optimize this and only load the registered key pairs
         * on startup and after changes.
         */
        File pluginStateLocation = plugin.getStateLocation().toFile();
        File registeredKeyPairsFile = new File(pluginStateLocation, getKeyPropertiesFileName(accountId));
        registeredKeyPairsFile.createNewFile();
        FileInputStream fileInputStream = new FileInputStream(registeredKeyPairsFile);

        try {
            Properties registeredKeyPairs = new Properties();
            registeredKeyPairs.load(fileInputStream);

            return registeredKeyPairs;
        } finally {
            fileInputStream.close();
        }
    }

    private void storeRegisteredKeyPairs(String accountId, Properties registeredKeyPairs) throws IOException {
        File pluginStateLocation = Ec2Plugin.getDefault().getStateLocation().toFile();
        File registeredKeyPairsFile = new File(pluginStateLocation, getKeyPropertiesFileName(accountId));
        registeredKeyPairsFile.createNewFile();
        FileOutputStream fileOutputStream = new FileOutputStream(registeredKeyPairsFile);
        try {
            registeredKeyPairs.store(fileOutputStream, null);
        } finally {
            fileOutputStream.close();
        }
    }

    private static String getKeyPropertiesFileName(String accountId) {
        return "registeredKeyPairs." + accountId + ".properties";
    }

}