/*
* JetS3t : Java S3 Toolkit
* Project hosted at http://bitbucket.org/jmurty/jets3t/
*
* Copyright 2006-2010 James Murty, 2008 Zmanda 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://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License 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 org.jets3t.apps.cockpit.gui;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.net.URL;
import javax.swing.AbstractAction;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jets3t.gui.ErrorDialog;
import org.jets3t.gui.HyperlinkActivatedListener;
import org.jets3t.gui.ProgressDialog;
import org.jets3t.service.Constants;
import org.jets3t.service.Jets3tProperties;
import org.jets3t.service.S3ServiceException;
import org.jets3t.service.ServiceException;
import org.jets3t.service.StorageService;
import org.jets3t.service.acl.AccessControlList;
import org.jets3t.service.impl.rest.httpclient.RestS3Service;
import org.jets3t.service.model.StorageObject;
import org.jets3t.service.security.AWSCredentials;
import org.jets3t.service.security.AWSDevPayCredentials;
import org.jets3t.service.security.GSCredentials;
import org.jets3t.service.security.ProviderCredentials;
import org.jets3t.service.utils.ServiceUtils;
import contribs.com.centerkey.utils.BareBonesBrowserLaunch;
/**
* Dialog box for obtaining a user's service credentials, and performing other startup
* tasks such as loading properties files.
* <p>
*
* @author James Murty
* @author Nikolas Coukouma
*/
public class StartupDialog extends JDialog implements ActionListener, ChangeListener {
private static final long serialVersionUID = -2520889480615456474L;
private static final Log log = LogFactory.getLog(StartupDialog.class);
public static final String EMPTY_PASSWORD_SURROGATE = "NONE";
private Frame ownerFrame = null;
private HyperlinkActivatedListener hyperlinkListener = null;
private Jets3tProperties myProperties = null;
private ProviderCredentials credentials = null;
private JRadioButton targetS3 = null;
private JRadioButton targetGS = null;
private JButton okButton = null;
private JButton cancelButton = null;
private JButton storeCredentialsButton = null;
private JTabbedPane tabbedPane = null;
private LoginPassphrasePanel loginPassphrasePanel = null;
private LoginLocalFolderPanel loginLocalFolderPanel = null;
private LoginCredentialsPanel loginCredentialsPanel = null;
private final Insets insetsZero = new Insets(0, 0, 0, 0);
private final Insets insetsDefault = new Insets(3, 5, 3, 5);
private static final int LOGIN_MODE_PASSPHRASE = 0;
private static final int LOGIN_MODE_LOCAL_FOLDER = 1;
private static final int LOGIN_MODE_DIRECT = 2;
private int loginMode = LOGIN_MODE_PASSPHRASE;
/**
* Creates a modal dialog box with a title.
*
* @param owner
* the frame within which this dialog will be displayed and centred.
* @param hyperlinkListener
*/
public StartupDialog(Frame owner, Jets3tProperties properties, HyperlinkActivatedListener hyperlinkListener) {
super(owner, "Cockpit Login", true);
this.ownerFrame = owner;
this.hyperlinkListener = hyperlinkListener;
this.myProperties = properties;
this.initGui();
}
/**
* Initialises all GUI elements.
*/
private void initGui() {
this.setResizable(false);
this.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
cancelButton = new JButton("Don't log in");
cancelButton.setActionCommand("Cancel");
cancelButton.addActionListener(this);
storeCredentialsButton = new JButton("Store Credentials");
storeCredentialsButton.setActionCommand("StoreCredentials");
storeCredentialsButton.addActionListener(this);
okButton = new JButton("Log in");
okButton.setActionCommand("LogIn");
okButton.addActionListener(this);
// Set default ENTER and ESCAPE buttons.
this.getRootPane().setDefaultButton(okButton);
this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(KeyStroke.getKeyStroke("ESCAPE"), "ESCAPE");
this.getRootPane().getActionMap().put("ESCAPE", new AbstractAction() {
private static final long serialVersionUID = -1742280851624947873L;
public void actionPerformed(ActionEvent actionEvent) {
setVisible(false);
}
});
JPanel buttonsPanel = new JPanel(new GridBagLayout());
buttonsPanel.add(cancelButton, new GridBagConstraints(0, 0,
1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, insetsZero, 0, 0));
buttonsPanel.add(storeCredentialsButton, new GridBagConstraints(1, 0,
1, 1, 1, 0, GridBagConstraints.EAST, GridBagConstraints.NONE, insetsZero, 0, 0));
buttonsPanel.add(okButton, new GridBagConstraints(2, 0,
1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE, insetsZero, 0, 0));
loginPassphrasePanel = new LoginPassphrasePanel(hyperlinkListener);
loginLocalFolderPanel = new LoginLocalFolderPanel(ownerFrame, hyperlinkListener);
loginCredentialsPanel = new LoginCredentialsPanel(false, hyperlinkListener);
// Target storage service selection
targetS3 = new JRadioButton("Amazon S3");
targetS3.setSelected(true);
targetGS = new JRadioButton("Google Storage");
ButtonGroup targetButtonGroup = new ButtonGroup();
targetButtonGroup.add(targetS3);
targetButtonGroup.add(targetGS);
JPanel targetServicePanel = new JPanel(new GridBagLayout());
targetServicePanel.add(targetS3, new GridBagConstraints(0, 0,
1, 1, 1, 0, GridBagConstraints.EAST, GridBagConstraints.NONE, insetsZero, 0, 0));
targetServicePanel.add(targetGS, new GridBagConstraints(1, 0,
1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, insetsZero, 0, 0));
// Tabbed Pane.
tabbedPane = new JTabbedPane();
tabbedPane.addChangeListener(this);
tabbedPane.add(loginPassphrasePanel, "Online");
tabbedPane.add(loginLocalFolderPanel, "Local Folder");
tabbedPane.add(loginCredentialsPanel, "Direct Login");
int row = 0;
this.getContentPane().setLayout(new GridBagLayout());
this.getContentPane().add(targetServicePanel, new GridBagConstraints(0, row++,
2, 1, 1, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, insetsDefault, 0, 0));
this.getContentPane().add(tabbedPane, new GridBagConstraints(0, row++,
2, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH, insetsZero, 0, 0));
this.getContentPane().add(buttonsPanel, new GridBagConstraints(0, row++,
2, 1, 1, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, insetsDefault, 0, 0));
this.pack();
this.setSize(500, 430);
this.setLocationRelativeTo(this.getOwner());
}
public boolean isTargetS3() {
return targetS3.isSelected();
}
protected StorageService getStorageService()
throws S3ServiceException
{
if (targetS3.isSelected()) {
return new RestS3Service(credentials);
} else {
// Override endpoint property in JetS3t properties
Jets3tProperties gsProperties = Jets3tProperties.getInstance(
Constants.JETS3T_PROPERTIES_FILENAME);
gsProperties.setProperty(
"s3service.s3-endpoint", Constants.GS_DEFAULT_HOSTNAME);
return new RestS3Service(credentials, null, null, gsProperties);
}
}
/**
* Event handler for this dialog.
*/
public void actionPerformed(ActionEvent e) {
if (e.getSource().equals(okButton)) {
if (loginMode == LOGIN_MODE_PASSPHRASE) {
retrieveCredentialsFromStorageService(
loginPassphrasePanel.getPassphrase(), loginPassphrasePanel.getPassword());
} else if (loginMode == LOGIN_MODE_LOCAL_FOLDER) {
retrieveCredentialsFromDirectory(loginLocalFolderPanel.getHomeFolder(),
loginLocalFolderPanel.getCredentialsFile(), loginLocalFolderPanel.getPassword());
} else if (loginMode == LOGIN_MODE_DIRECT) {
String[] inputErrors = loginCredentialsPanel.checkForInputErrors();
if (inputErrors.length > 0) {
String errorMessages = "<html>Please correct the following errors:<ul>";
for (int i = 0; i < inputErrors.length; i++) {
errorMessages += "<li>" + inputErrors[i] + "</li>";
}
errorMessages += "</ul></html>";
ErrorDialog.showDialog(this, null, errorMessages, null);
} else {
if (loginCredentialsPanel.getUsingDevPay()) {
this.credentials = new AWSDevPayCredentials(
loginCredentialsPanel.getAccessKey(),
loginCredentialsPanel.getSecretKey(),
loginCredentialsPanel.getAWSUserToken(),
loginCredentialsPanel.getAWSProductToken(),
loginCredentialsPanel.getFriendlyName());
} else {
if (targetS3.isSelected()) {
this.credentials = new AWSCredentials(
loginCredentialsPanel.getAccessKey(),
loginCredentialsPanel.getSecretKey(),
loginCredentialsPanel.getFriendlyName());
} else {
this.credentials = new GSCredentials(
loginCredentialsPanel.getAccessKey(),
loginCredentialsPanel.getSecretKey(),
loginCredentialsPanel.getFriendlyName());
}
}
this.setVisible(false);
}
}
} else if (e.getSource().equals(storeCredentialsButton)) {
if (loginMode == LOGIN_MODE_PASSPHRASE) {
storeCredentialsInStorageService(
loginPassphrasePanel.getPassphrase(), loginPassphrasePanel.getPassword());
} else if (loginMode == LOGIN_MODE_LOCAL_FOLDER) {
storeCredentialsInDirectory(
loginLocalFolderPanel.getHomeFolder(), loginLocalFolderPanel.getPassword());
} else if (loginMode == LOGIN_MODE_DIRECT) {
throw new IllegalStateException("Cannot store credentials from Direct Login panel");
}
} else if (e.getSource().equals(cancelButton)) {
this.credentials = null;
this.setVisible(false);
}
}
public void stateChanged(ChangeEvent e) {
if (e.getSource().equals(tabbedPane)) {
loginMode = tabbedPane.getSelectedIndex();
changedLoginMode();
}
}
private void changedLoginMode() {
if (loginMode == LOGIN_MODE_PASSPHRASE) {
storeCredentialsButton.setEnabled(true);
} else if (loginMode == LOGIN_MODE_LOCAL_FOLDER) {
storeCredentialsButton.setEnabled(true);
} else if (loginMode == LOGIN_MODE_DIRECT) {
storeCredentialsButton.setEnabled(false);
} else {
throw new IllegalStateException("Invalid value for loginMode: " + loginMode);
}
}
private String generateBucketNameFromPassphrase(String passphrase) throws Exception {
return "jets3t-" + ServiceUtils.toHex(
ServiceUtils.computeMD5Hash(passphrase.getBytes(Constants.DEFAULT_ENCODING)));
}
private String generateObjectKeyFromPassphrase(String passphrase, String password) throws Exception {
String combinedString = passphrase + password;
return ServiceUtils.toHex(
ServiceUtils.computeMD5Hash(combinedString.getBytes(Constants.DEFAULT_ENCODING)))
+ "/jets3t.credentials";
}
private boolean validPassphraseInputs(String passphrase, String password) {
String invalidInputsMessage = "";
if (passphrase.length() < 6) {
invalidInputsMessage += "Passphrase must be at least 6 characters.";
}
if (password.length() < 6) {
invalidInputsMessage += (invalidInputsMessage.length() > 0
? " and password"
: "Password")
+ " must be at least 6 characters";
}
if (invalidInputsMessage.length() > 0) {
ErrorDialog.showDialog(this, hyperlinkListener, invalidInputsMessage, null);
return false;
} else {
return true;
}
}
private boolean validFolderInputs(boolean isStoreAction, File directory,
File credentialsFile, String password, boolean allowLegacyPassword)
{
if (password.length() < 6) {
if (allowLegacyPassword) {
// Legacy password allowed for login, an error will be displayed later if it's incorrect.
} else if (EMPTY_PASSWORD_SURROGATE.equals(password)) {
// Surrogate empty password was used, not an error.
} else {
ErrorDialog.showDialog(this, hyperlinkListener,
"Password must be at least 6 characters. " +
"If you do not wish to set a password, use the password " +
EMPTY_PASSWORD_SURROGATE + ".",
null);
return false;
}
}
if (!directory.exists() || !directory.canWrite()) {
String invalidInputsMessage = "Directory '" + directory.getAbsolutePath()
+ "' does not exist or cannot be written to.";
ErrorDialog.showDialog(this, hyperlinkListener, invalidInputsMessage, null);
return false;
}
if (credentialsFile == null && !isStoreAction) {
String invalidInputsMessage = "You must choose which stored login to use";
ErrorDialog.showDialog(this, hyperlinkListener, invalidInputsMessage, null);
return false;
}
return true;
}
private void retrieveCredentialsFromStorageService(String passphrase, final String password) {
if (!validPassphraseInputs(passphrase, password)) {
return;
}
final String[] bucketName = new String[1];
final String[] credentialObjectKey = new String[1];
try {
bucketName[0] = generateBucketNameFromPassphrase(passphrase);
credentialObjectKey[0] = generateObjectKeyFromPassphrase(passphrase, password);
} catch (Exception e) {
String message = "Unable to generate bucket name or object key";
log.error(message, e);
ErrorDialog.showDialog(this, hyperlinkListener, message, e);
return;
}
final ProgressDialog progressDialog = new ProgressDialog(
ownerFrame, "Retrieving credentials", null);
final StartupDialog myself = this;
(new Thread(new Runnable() {
public void run() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progressDialog.startDialog("Downloading your credentials", "", 0, 0, null, null);
}
});
StorageObject encryptedCredentialsObject = null;
try {
credentials = null;
StorageService service = getStorageService();
encryptedCredentialsObject = service.getObject(
bucketName[0], credentialObjectKey[0]);
} catch (ServiceException e) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progressDialog.stopDialog();
}
});
String errorMessage = "<html><center>Unable to find your credentials online"
+ "<br><br>Please check your passphrase and password</center></html>";
log.error(errorMessage, e);
ErrorDialog.showDialog(myself, hyperlinkListener, errorMessage, null);
return;
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progressDialog.updateDialog("Decrypting your credentials", null, 0);
}
});
try {
if (targetS3.isSelected()) {
myself.credentials = AWSCredentials.load(password,
new BufferedInputStream(encryptedCredentialsObject.getDataInputStream()));
} else {
myself.credentials = GSCredentials.load(password,
new BufferedInputStream(encryptedCredentialsObject.getDataInputStream()));
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progressDialog.stopDialog();
}
});
myself.setVisible(false);
} catch (ServiceException e) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progressDialog.stopDialog();
}
});
String errorMessage =
"<html><center>Unable to load your online credentials"
+ "<br><br>Please check your password</center></html>";
log.error(errorMessage, e);
ErrorDialog.showDialog(myself, hyperlinkListener, errorMessage, null);
}
}
})).start();
}
private void storeCredentialsInStorageService(String passphrase, String password) {
if (!validPassphraseInputs(passphrase, password)) {
return;
}
final ProviderCredentials credentials =
CredentialsDialog.showDialog(ownerFrame,
(loginMode == LOGIN_MODE_LOCAL_FOLDER),
this.isTargetS3(),
myProperties, hyperlinkListener);
if (credentials == null) {
return;
}
final String[] bucketName = new String[1];
final String[] credentialObjectKey = new String[1];
try {
bucketName[0] = generateBucketNameFromPassphrase(passphrase);
credentialObjectKey[0] = generateObjectKeyFromPassphrase(passphrase, password);
} catch (Exception e) {
String message = "Unable to generate bucket name or object key";
log.error(message, e);
ErrorDialog.showDialog(this, hyperlinkListener, message, e);
return;
}
final ByteArrayInputStream[] bais = new ByteArrayInputStream[1];
try {
// Convert credentials into a readable input stream.
String algorithm = myProperties.getStringProperty("crypto.algorithm", "PBEWithMD5AndDES");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
credentials.save(password, baos, algorithm);
bais[0] = new ByteArrayInputStream(baos.toByteArray());
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
String message = "Unable to encrypt your credentials";
log.error(message, e);
ErrorDialog.showDialog(this, hyperlinkListener, message, e);
return;
}
final ProgressDialog progressDialog = new ProgressDialog(
ownerFrame, "Storing credentials", null);
final StartupDialog myself = this;
(new Thread(new Runnable() {
public void run() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progressDialog.startDialog("Uploading your credentials", null, 0, 0, null, null);
}
});
try {
StorageObject encryptedCredentialsObject =
new StorageObject(credentialObjectKey[0]);
encryptedCredentialsObject.setDataInputStream(bais[0]);
encryptedCredentialsObject.setAcl(AccessControlList.REST_CANNED_PUBLIC_READ);
// Store credentials
StorageService service = getStorageService();
service.createBucket(bucketName[0]);
service.putObject(bucketName[0], encryptedCredentialsObject);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progressDialog.stopDialog();
}
});
JOptionPane.showMessageDialog(ownerFrame,
"Your credentials have been stored online"
+ "\n\nBucket name: " + bucketName[0]
+ "\nObject key: " + credentialObjectKey[0]);
} catch (ServiceException e) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progressDialog.stopDialog();
}
});
String message = "Unable to store your credentials online";
log.error(message, e);
ErrorDialog.showDialog(myself, hyperlinkListener, message, e);
}
}
})).start();
}
private void retrieveCredentialsFromDirectory(File directory, File credentialsFile, String password) {
if (!validFolderInputs(false, directory, credentialsFile, password, true)) {
return;
}
try {
this.credentials = ProviderCredentials.load(password, credentialsFile);
this.setVisible(false);
} catch (Exception e) {
String message = "<html><center>Unable to load your credentials from the file: "
+ credentialsFile + "<br><br>Please check your password</center></html>";
log.error(message, e);
ErrorDialog.showDialog(this, hyperlinkListener, message, null);
}
}
private void storeCredentialsInDirectory(File directory, String password) {
if (!validFolderInputs(true, directory, null, password, false)) {
return;
}
if (EMPTY_PASSWORD_SURROGATE.equals(password.trim())) {
password = "";
}
ProviderCredentials myCredentials =
CredentialsDialog.showDialog(ownerFrame, true, this.isTargetS3(),
myProperties, hyperlinkListener);
if (myCredentials == null) {
return;
}
if (myCredentials.getFriendlyName() == null || myCredentials.getFriendlyName().length() == 0) {
String message = "You must enter a nickname when storing your credentials";
log.error(message);
ErrorDialog.showDialog(this, hyperlinkListener, message, null);
return;
}
File credentialsFile = new File(directory, myCredentials.getFriendlyName() + ".enc");
try {
String algorithm = myProperties.getStringProperty("crypto.algorithm", "PBEWithMD5AndDES");
myCredentials.save(password, credentialsFile, algorithm);
loginLocalFolderPanel.clearPassword();
loginLocalFolderPanel.refreshStoredCredentialsTable();
JOptionPane.showMessageDialog(ownerFrame, "Your credentials have been stored in the file:\n" +
credentialsFile.getAbsolutePath());
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
String message = "Unable to encrypt your credentials to a folder";
log.error(message, e);
ErrorDialog.showDialog(this, hyperlinkListener, message, e);
}
}
public ProviderCredentials getProviderCredentials() {
return this.credentials;
}
/**
* Creates stand-alone dialog box for testing only.
*
* @param args
* @throws Exception
*/
public static void main(String args[]) throws Exception {
JFrame f = new JFrame();
HyperlinkActivatedListener listener = new HyperlinkActivatedListener() {
private static final long serialVersionUID = -225585129296632961L;
public void followHyperlink(URL url, String target) {
BareBonesBrowserLaunch.openURL(url.toString());
}
};
StartupDialog startupDialog = new StartupDialog(f,
Jets3tProperties.getInstance(Constants.JETS3T_PROPERTIES_FILENAME), listener);
startupDialog.setVisible(true);
ProviderCredentials credentials = startupDialog.getProviderCredentials();
startupDialog.dispose();
if (credentials != null) {
System.out.println("Credentials: " + credentials.getLogString());
} else {
System.out.println("Credentials: null");
}
f.dispose();
}
}