- Fixed "return" key in unlock view

- Fixed password field focus
- Don't show unlock error messages from one vault, when switching to another vault
- Hide advanced mount options by default (preparation for things like #74)
This commit is contained in:
Sebastian Stenzel
2015-10-04 15:38:41 +02:00
parent c1f32105d8
commit 1bef4e786d
9 changed files with 138 additions and 32 deletions

View File

@@ -129,12 +129,12 @@
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger</artifactId>
<version>2.0</version>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger-compiler</artifactId>
<version>2.0</version>
<version>2.0.1</version>
<scope>provided</scope>
</dependency>

View File

@@ -42,6 +42,7 @@ import javafx.scene.control.Hyperlink;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.text.Text;
public class UnlockController extends AbstractFXMLViewController {
@@ -57,6 +58,9 @@ public class UnlockController extends AbstractFXMLViewController {
@FXML
private TextField mountName;
@FXML
private Button advancedOptionsButton;
@FXML
private Button unlockButton;
@@ -69,6 +73,9 @@ public class UnlockController extends AbstractFXMLViewController {
@FXML
private Hyperlink downloadsPageLink;
@FXML
private GridPane advancedOptions;
private final ExecutorService exec;
private final Application app;
@@ -93,6 +100,17 @@ public class UnlockController extends AbstractFXMLViewController {
passwordField.textProperty().addListener(this::passwordFieldsDidChange);
mountName.addEventFilter(KeyEvent.KEY_TYPED, this::filterAlphanumericKeyEvents);
mountName.textProperty().addListener(this::mountNameDidChange);
advancedOptions.managedProperty().bind(advancedOptions.visibleProperty());
}
private void resetView() {
unlockButton.setDisable(true);
advancedOptions.setVisible(false);
advancedOptionsButton.setText(resourceBundle.getString("unlock.button.advancedOptions.show"));
progressIndicator.setVisible(false);
passwordField.clear();
downloadsPageLink.setVisible(false);
messageText.setText(null);
}
// ****************************************
@@ -113,6 +131,20 @@ public class UnlockController extends AbstractFXMLViewController {
app.getHostServices().showDocument("https://cryptomator.org/downloads/");
}
// ****************************************
// Advanced options button
// ****************************************
@FXML
private void didClickAdvancedOptionsButton(ActionEvent event) {
advancedOptions.setVisible(!advancedOptions.isVisible());
if (advancedOptions.isVisible()) {
advancedOptionsButton.setText(resourceBundle.getString("unlock.button.advancedOptions.hide"));
} else {
advancedOptionsButton.setText(resourceBundle.getString("unlock.button.advancedOptions.show"));
}
}
// ****************************************
// Unlock button
// ****************************************
@@ -138,23 +170,14 @@ public class UnlockController extends AbstractFXMLViewController {
final Future<Boolean> futureMount = exec.submit(() -> (boolean) vault.mount());
FXThreads.runOnMainThreadWhenFinished(exec, futureMount, this::unlockAndMountFinished);
} catch (IOException ex) {
setControlsDisabled(false);
progressIndicator.setVisible(false);
messageText.setText(resourceBundle.getString("unlock.errorMessage.decryptionFailed"));
LOG.error("Decryption failed for technical reasons.", ex);
} catch (WrongPasswordException e) {
setControlsDisabled(false);
progressIndicator.setVisible(false);
messageText.setText(resourceBundle.getString("unlock.errorMessage.wrongPassword"));
Platform.runLater(passwordField::requestFocus);
} catch (UnsupportedKeyLengthException ex) {
setControlsDisabled(false);
progressIndicator.setVisible(false);
messageText.setText(resourceBundle.getString("unlock.errorMessage.unsupportedKeyLengthInstallJCE"));
LOG.warn("Unsupported Key-Length. Please install Oracle Java Cryptography Extension (JCE).", ex);
} catch (UnsupportedVaultException e) {
setControlsDisabled(false);
progressIndicator.setVisible(false);
downloadsPageLink.setVisible(true);
if (e.isVaultOlderThanSoftware()) {
messageText.setText(resourceBundle.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " ");
@@ -162,11 +185,12 @@ public class UnlockController extends AbstractFXMLViewController {
messageText.setText(resourceBundle.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault") + " ");
}
} catch (DestroyFailedException e) {
setControlsDisabled(false);
progressIndicator.setVisible(false);
LOG.error("Destruction of cryptor threw an exception.", e);
} finally {
setControlsDisabled(false);
progressIndicator.setVisible(false);
passwordField.swipe();
Platform.runLater(passwordField::requestFocus);
}
}
@@ -174,6 +198,7 @@ public class UnlockController extends AbstractFXMLViewController {
passwordField.setDisable(disable);
mountName.setDisable(disable);
unlockButton.setDisable(disable);
advancedOptionsButton.setDisable(disable);
}
private void unlockAndMountFinished(boolean mountSuccess) {
@@ -214,9 +239,9 @@ public class UnlockController extends AbstractFXMLViewController {
}
public void setVault(Vault vault) {
this.resetView();
this.vault = vault;
this.mountName.setText(vault.getMountName());
this.passwordField.clear();
}
public UnlockListener getListener() {

View File

@@ -16,18 +16,18 @@ import javafx.scene.control.PasswordField;
* Compromise in security. While the text can be swiped, any access to the {@link #getText()} method will create a copy of the String in the heap.
*/
public class SecPasswordField extends PasswordField {
private static final char SWIPE_CHAR = ' ';
/**
* {@link #getContent()} uses a StringBuilder, which in turn is backed by a char[].
* The delete operation of AbstractStringBuilder closes the gap, that forms by deleting chars, by moving up the following chars.
* <br/>
* Imagine the following example with <code>pass</code> being the password, <code>x</code> being the swipe char and <code>'</code> being the offset of the char array:
* <ol>
* <li>Append filling chars to the end of the password: <code>passxxxx'</code></li>
* <li>Delete first 4 chars. Internal implementation will then copy the following chars to the position, where the deletion occured: <code>xxxx'xxxx</code></li>
* <li>Delete first 4 chars again, as we appended 4 chars in step 1: <code>'xxxxxx</code></li>
* <li>Append filling chars to the end of the password: <code>passxxxx'</code></li>
* <li>Delete first 4 chars. Internal implementation will then copy the following chars to the position, where the deletion occured: <code>xxxx'xxxx</code></li>
* <li>Delete first 4 chars again, as we appended 4 chars in step 1: <code>'xxxxxx</code></li>
* </ol>
*/
public void swipe() {
@@ -37,8 +37,8 @@ public class SecPasswordField extends PasswordField {
this.getContent().insert(pwLength, new String(fillingChars), false);
this.getContent().delete(0, pwLength, true);
this.getContent().delete(0, pwLength, true);
// previous text has now been overwritten. still we need to update the text to trigger some property bindings:
this.setText("");
}
}

View File

@@ -225,6 +225,22 @@
-fx-font-size: 0.8em;
}
/****************************************************************************
* *
* Separator *
* *
****************************************************************************/
.separator .line {
-fx-border-style: solid;
-fx-border-width: 1px;
-fx-background-color: null;
}
.separator:horizontal .line {
-fx-border-color: COLOR_BORDER transparent transparent transparent;
}
/****************************************************************************
* *
* CheckBox *

View File

@@ -291,6 +291,22 @@
-fx-font-size: 0.8em;
}
/****************************************************************************
* *
* Separator *
* *
****************************************************************************/
.separator .line {
-fx-border-style: solid;
-fx-border-width: 1px;
-fx-background-color: null;
}
.separator:horizontal .line {
-fx-border-color: COLOR_BORDER transparent transparent transparent;
}
/*******************************************************************************
* *
* CheckBox *

View File

@@ -281,6 +281,22 @@
-fx-font-size: 0.9em;
}
/****************************************************************************
* *
* Separator *
* *
****************************************************************************/
.separator .line {
-fx-border-style: solid;
-fx-border-width: 1px;
-fx-background-color: null;
}
.separator:horizontal .line {
-fx-border-color: COLOR_BORDER transparent transparent transparent;
}
/*******************************************************************************
* *
* CheckBox *

View File

@@ -21,6 +21,8 @@
<?import javafx.scene.text.TextFlow?>
<?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.text.Text?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Separator?>
<GridPane vgap="12.0" hgap="12.0" prefWidth="400.0" xmlns:fx="http://javafx.com/fxml">
<padding>
@@ -36,24 +38,52 @@
<!-- Row 0 -->
<Label text="%unlock.label.password" GridPane.rowIndex="0" GridPane.columnIndex="0" />
<SecPasswordField fx:id="passwordField" GridPane.rowIndex="0" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS" maxWidth="Infinity" />
<!-- Row 1 -->
<Label text="%unlock.label.mountName" GridPane.rowIndex="1" GridPane.columnIndex="0" />
<TextField fx:id="mountName" GridPane.rowIndex="1" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS" maxWidth="Infinity" />
<HBox GridPane.rowIndex="2" GridPane.columnIndex="0" GridPane.columnSpan="2" spacing="12.0" alignment="CENTER_RIGHT">
<Button fx:id="advancedOptionsButton" text="%unlock.button.advancedOptions.show" prefWidth="150.0" onAction="#didClickAdvancedOptionsButton"/>
<Button fx:id="unlockButton" text="%unlock.button.unlock" defaultButton="true" prefWidth="150.0" onAction="#didClickUnlockButton" disable="true"/>
</HBox>
<!-- Row 2 -->
<Button fx:id="unlockButton" text="%unlock.button.unlock" defaultButton="true" GridPane.rowIndex="2" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="RIGHT" prefWidth="150.0" onAction="#didClickUnlockButton" disable="true"/>
<!-- Row 3-->
<ProgressIndicator progress="-1" fx:id="progressIndicator" GridPane.rowIndex="3" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="CENTER" visible="false"/>
<!-- Row 3 -->
<GridPane fx:id="advancedOptions" vgap="12.0" hgap="12.0" prefWidth="400.0" GridPane.rowIndex="3" GridPane.columnIndex="0" GridPane.columnSpan="2" visible="false">
<padding>
<Insets top="24.0" />
</padding>
<columnConstraints>
<ColumnConstraints percentWidth="38.2"/>
<ColumnConstraints percentWidth="61.8"/>
</columnConstraints>
<!-- Row 3.0 -->
<Separator GridPane.rowIndex="0" GridPane.columnIndex="0" GridPane.columnSpan="2" />
<HBox alignment="CENTER" prefWidth="400.0" GridPane.rowIndex="0" GridPane.columnIndex="0" GridPane.columnSpan="2">
<Label text="%unlock.label.advancedHeading" style="-fx-background-color: COLOR_BACKGROUND;">
<padding>
<Insets left="6.0" right="6.0"/>
</padding>
</Label>
</HBox>
<!-- Row 3.1 -->
<Label text="%unlock.label.mountName" GridPane.rowIndex="1" GridPane.columnIndex="0" />
<TextField fx:id="mountName" GridPane.rowIndex="1" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS" maxWidth="Infinity" />
</GridPane>
<!-- Row 4 -->
<TextFlow GridPane.rowIndex="4" GridPane.columnIndex="0" GridPane.columnSpan="2" >
<TextFlow GridPane.rowIndex="4" GridPane.columnIndex="0" GridPane.columnSpan="2">
<GridPane.margin>
<Insets top="24.0"/>
</GridPane.margin>
<children>
<Text fx:id="messageText" />
<Text fx:id="messageText" text="asd"/>
<Hyperlink fx:id="downloadsPageLink" text="%unlock.label.downloadsPageLink" visible="false" onAction="#didClickDownloadsLink" />
</children>
</TextFlow>
<!-- Row 5-->
<ProgressIndicator progress="-1" fx:id="progressIndicator" GridPane.rowIndex="5" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="CENTER" />
</children>
</GridPane>

View File

@@ -36,7 +36,7 @@
<!-- Row 1 -->
<Label fx:id="messageLabel" GridPane.rowIndex="1" GridPane.columnIndex="0" />
<Button text="%unlocked.button.lock" defaultButton="true" GridPane.rowIndex="1" GridPane.columnIndex="1" GridPane.halignment="RIGHT" prefWidth="150.0" onAction="#didClickCloseVault" focusTraversable="false"/>
<Button text="%unlocked.button.lock" GridPane.rowIndex="1" GridPane.columnIndex="1" GridPane.halignment="RIGHT" prefWidth="150.0" onAction="#didClickCloseVault" focusTraversable="false"/>
</children>
</GridPane>

View File

@@ -31,7 +31,10 @@ initialize.button.ok=Create vault
unlock.label.password=Password
unlock.label.mountName=Drive name
unlock.label.downloadsPageLink=All Cryptomator versions
unlock.label.advancedHeading=Advanced options
unlock.button.unlock=Unlock vault
unlock.button.advancedOptions.show=More options
unlock.button.advancedOptions.hide=Less options
unlock.errorMessage.wrongPassword=Wrong password.
unlock.errorMessage.decryptionFailed=Decryption failed.
unlock.errorMessage.unsupportedKeyLengthInstallJCE=Decryption failed. Please install Oracle JCE Unlimited Strength Policy.