/*
* CreateKeyDialog.java
*
* Copyright (C) 2009-12 by RStudio, Inc.
*
* Unless you have received this program directly from RStudio pursuant
* to the terms of a commercial license agreement with RStudio, then
* this program is licensed to you under the terms of version 3 of the
* GNU Affero General Public License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
*
*/
package org.rstudio.studio.client.common.vcs;
import org.rstudio.core.client.files.FileSystemItem;
import org.rstudio.core.client.widget.CaptionWithHelp;
import org.rstudio.core.client.widget.FocusHelper;
import org.rstudio.core.client.widget.MessageDialog;
import org.rstudio.core.client.widget.ModalDialog;
import org.rstudio.core.client.widget.Operation;
import org.rstudio.core.client.widget.OperationWithInput;
import org.rstudio.core.client.widget.ProgressIndicator;
import org.rstudio.core.client.widget.ProgressOperationWithInput;
import org.rstudio.core.client.widget.ShowContentDialog;
import org.rstudio.studio.client.RStudioGinjector;
import org.rstudio.studio.client.common.GlobalDisplay;
import org.rstudio.studio.client.common.crypto.RSAEncrypt;
import org.rstudio.studio.client.server.ServerError;
import org.rstudio.studio.client.server.ServerRequestCallback;
import com.google.gwt.core.client.GWT;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
public class CreateKeyDialog extends ModalDialog<CreateKeyOptions>
{
public CreateKeyDialog(String rsaSshKeyPath,
final VCSServerOperations server,
final OperationWithInput<String> onCompleted)
{
super("Create RSA Key", new ProgressOperationWithInput<CreateKeyOptions>() {
@Override
public void execute(final CreateKeyOptions input,
final ProgressIndicator indicator)
{
final ProgressOperationWithInput<CreateKeyOptions>
thisOperation = this;
indicator.onProgress("Creating RSA Key...");
RSAEncrypt.encrypt_ServerOnly(
server,
input.getPassphrase(),
new RSAEncrypt.ResponseCallback() {
@Override
public void onSuccess(final String encryptedData)
{
// substitute encrypted data
final CreateKeyOptions options = CreateKeyOptions.create(
input.getPath(),
input.getType(),
encryptedData,
input.getOverwrite());
// call server to create the key
server.createSshKey(
options,
new ServerRequestCallback<CreateKeyResult>() {
@Override
public void onResponseReceived(CreateKeyResult res)
{
if (res.getFailedKeyExists())
{
indicator.clearProgress();
confirmOverwriteKey(
input.getPath(),
new Operation()
{
@Override
public void execute()
{
// re-execute with overwrite == true
thisOperation.execute(
CreateKeyOptions.create(
options.getPath(),
options.getType(),
input.getPassphrase(),
true),
indicator);
}
});
}
else
{
// close the dialog
indicator.onCompleted();
// update the key path
if (res.getExitStatus() == 0)
onCompleted.execute(input.getPath());
else if (input.getOverwrite())
onCompleted.execute(null);
// show the output
new ShowContentDialog(
"Create RSA Key",
res.getOutput()).showModal();
}
}
@Override
public void onError(ServerError error)
{
indicator.onError(error.getUserMessage());
}
});
}
@Override
public void onFailure(ServerError error)
{
indicator.onError(error.getUserMessage());
}
}
);
}
});
rsaSshKeyPath_ = FileSystemItem.createDir(rsaSshKeyPath);
setOkButtonCaption("Create");
}
@Override
protected CreateKeyOptions collectInput()
{
if (!getPassphrase().equals(getConfirmPassphrase()))
return null;
else
return CreateKeyOptions.create(rsaSshKeyPath_.getPath(),
"rsa",
getPassphrase(),
false);
}
@Override
protected boolean validate(CreateKeyOptions input)
{
if (input != null)
{
return true;
}
else
{
GlobalDisplay display = RStudioGinjector.INSTANCE.getGlobalDisplay();
if (!getPassphrase().equals(getConfirmPassphrase()))
{
display.showErrorMessage(
"Non-Matching Passphrases",
"The passphrase and passphrase confirmation do not match.",
txtConfirmPassphrase_);
}
return false;
}
}
@Override
protected Widget createMainWidget()
{
Styles styles = RESOURCES.styles();
VerticalPanel panel = new VerticalPanel();
panel.addStyleName(styles.mainWidget());
VerticalPanel namePanel = new VerticalPanel();
namePanel.setWidth("100%");
// path
CaptionWithHelp pathCaption = new CaptionWithHelp(
"The RSA key will be created at:",
"SSH/RSA key management",
"rsa_key_help");
pathCaption.setIncludeVersionInfo(false);
pathCaption.setWidth("100%");
namePanel.add(pathCaption);
TextBox txtKeyPath = new TextBox();
txtKeyPath.addStyleName(styles.keyPathTextBox());
txtKeyPath.setReadOnly(true);
txtKeyPath.setText(rsaSshKeyPath_.getPath());
txtKeyPath.setWidth("100%");
namePanel.add(txtKeyPath);
panel.add(namePanel);
HorizontalPanel passphrasePanel = new HorizontalPanel();
passphrasePanel.addStyleName(styles.newSection());
VerticalPanel passphrasePanel1 = new VerticalPanel();
Label passphraseLabel1 = new Label("Passphrase (optional):");
passphraseLabel1.addStyleName(styles.entryLabel());
passphrasePanel1.add(passphraseLabel1);
txtPassphrase_ = new PasswordTextBox();
txtPassphrase_.addStyleName(styles.passphrase());
passphrasePanel1.add(txtPassphrase_);
passphrasePanel.add(passphrasePanel1);
VerticalPanel passphrasePanel2 = new VerticalPanel();
passphrasePanel2.addStyleName(styles.lastSection());
Label passphraseLabel2 = new Label("Confirm:");
passphraseLabel2.addStyleName(styles.entryLabel());
passphrasePanel2.add(passphraseLabel2);
txtConfirmPassphrase_ = new PasswordTextBox();
txtConfirmPassphrase_.addStyleName(styles.passphraseConfirm());
passphrasePanel2.add(txtConfirmPassphrase_);
passphrasePanel.add(passphrasePanel2);
panel.add(passphrasePanel);
return panel;
}
@Override
protected void onLoad()
{
super.onLoad();
FocusHelper.setFocusDeferred(txtPassphrase_);
}
private static void confirmOverwriteKey(String path, Operation onConfirmed)
{
RStudioGinjector.INSTANCE.getGlobalDisplay().showYesNoMessage(
MessageDialog.WARNING,
"Key Already Exists",
"An RSA key already exists at " + path + ". " +
"Do you want to overwrite the existing key?",
onConfirmed,
false);
}
private String getPassphrase()
{
return txtPassphrase_.getText().trim();
}
private String getConfirmPassphrase()
{
return txtConfirmPassphrase_.getText().trim();
}
static interface Styles extends CssResource
{
String entryLabel();
String keyPathTextBox();
String mainWidget();
String newSection();
String lastSection();
String passphrase();
String passphraseConfirm();
}
static interface Resources extends ClientBundle
{
@Source("CreateKeyDialog.css")
Styles styles();
}
static Resources RESOURCES = (Resources)GWT.create(Resources.class) ;
public static void ensureStylesInjected()
{
RESOURCES.styles().ensureInjected();
}
private TextBox txtPassphrase_;
private TextBox txtConfirmPassphrase_;
private final FileSystemItem rsaSshKeyPath_;
}