mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-04-19 17:16:53 -04:00
Merge branch 'develop' into release/1.6.0
This commit is contained in:
@@ -109,7 +109,7 @@ public class Vault {
|
||||
} else if(vaultSettings.maxCleartextFilenameLength().get() == -1) {
|
||||
LOG.debug("Determining cleartext filename length limitations...");
|
||||
var checker = new FileSystemCapabilityChecker();
|
||||
int shorteningThreshold = getUnverifiedVaultConfig().orElseThrow().allegedShorteningThreshold();
|
||||
int shorteningThreshold = getUnverifiedVaultConfig().allegedShorteningThreshold();
|
||||
int ciphertextLimit = checker.determineSupportedCiphertextFileNameLength(getPath());
|
||||
if (ciphertextLimit < shorteningThreshold) {
|
||||
int cleartextLimit = checker.determineSupportedCleartextFileNameLength(getPath());
|
||||
@@ -327,14 +327,10 @@ public class Vault {
|
||||
return stats;
|
||||
}
|
||||
|
||||
public Optional<UnverifiedVaultConfig> getUnverifiedVaultConfig() {
|
||||
public UnverifiedVaultConfig getUnverifiedVaultConfig() throws IOException {
|
||||
Path configPath = getPath().resolve(org.cryptomator.common.Constants.VAULTCONFIG_FILENAME);
|
||||
try {
|
||||
String token = Files.readString(configPath, StandardCharsets.US_ASCII);
|
||||
return Optional.of(VaultConfig.decode(token));
|
||||
} catch (IOException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
String token = Files.readString(configPath, StandardCharsets.US_ASCII);
|
||||
return VaultConfig.decode(token);
|
||||
}
|
||||
|
||||
public Observable[] observables() {
|
||||
|
||||
@@ -98,7 +98,6 @@ public class VaultModule {
|
||||
flags.append(" -oatomic_o_trunc");
|
||||
flags.append(" -oauto_xattr");
|
||||
flags.append(" -oauto_cache");
|
||||
flags.append(" -omodules=iconv,from_code=UTF-8,to_code=UTF-8-MAC"); // show files names in Unicode NFD encoding
|
||||
flags.append(" -onoappledouble"); // vastly impacts performance for some reason...
|
||||
flags.append(" -odefault_permissions"); // let the kernel assume permissions based on file attributes etc
|
||||
|
||||
|
||||
10
main/pom.xml
10
main/pom.xml
@@ -25,14 +25,14 @@
|
||||
<project.jdk.version>16</project.jdk.version>
|
||||
|
||||
<!-- cryptomator dependencies -->
|
||||
<cryptomator.cryptofs.version>2.0.0-rc2</cryptomator.cryptofs.version>
|
||||
<cryptomator.cryptofs.version>2.1.0-beta5</cryptomator.cryptofs.version>
|
||||
<cryptomator.integrations.version>1.0.0-beta2</cryptomator.integrations.version>
|
||||
<cryptomator.integrations.win.version>1.0.0-beta2</cryptomator.integrations.win.version>
|
||||
<cryptomator.integrations.mac.version>1.0.0-beta2</cryptomator.integrations.mac.version>
|
||||
<cryptomator.integrations.linux.version>1.0.0-beta1</cryptomator.integrations.linux.version>
|
||||
<cryptomator.fuse.version>1.3.1</cryptomator.fuse.version>
|
||||
<cryptomator.dokany.version>1.3.1</cryptomator.dokany.version>
|
||||
<cryptomator.webdav.version>1.2.0</cryptomator.webdav.version>
|
||||
<cryptomator.webdav.version>1.2.2</cryptomator.webdav.version>
|
||||
|
||||
<!-- 3rd party dependencies -->
|
||||
<javafx.version>16</javafx.version>
|
||||
@@ -76,12 +76,6 @@
|
||||
<artifactId>cryptofs</artifactId>
|
||||
<version>${cryptomator.cryptofs.version}</version>
|
||||
</dependency>
|
||||
<!--TODO: only temporary workaround until 1.6.0-beta -->
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>cryptolib</artifactId>
|
||||
<version>2.0.0-rc1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>fuse-nio-adapter</artifactId>
|
||||
|
||||
@@ -6,10 +6,10 @@ import dagger.Provides;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.DefaultSceneFactory;
|
||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxControllerKey;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.NewPasswordController;
|
||||
import org.cryptomator.ui.common.PasswordStrengthUtil;
|
||||
@@ -33,13 +33,6 @@ import java.util.ResourceBundle;
|
||||
@Module
|
||||
public abstract class AddVaultModule {
|
||||
|
||||
@Provides
|
||||
@AddVaultWizardScoped
|
||||
@Named("newPassword")
|
||||
static ObjectProperty<CharSequence> provideNewPasswordProperty() {
|
||||
return new SimpleObjectProperty<>("");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@AddVaultWizardWindow
|
||||
@AddVaultWizardScoped
|
||||
@@ -167,8 +160,8 @@ public abstract class AddVaultModule {
|
||||
@Provides
|
||||
@IntoMap
|
||||
@FxControllerKey(NewPasswordController.class)
|
||||
static FxController provideNewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater, @Named("newPassword") ObjectProperty<CharSequence> password) {
|
||||
return new NewPasswordController(resourceBundle, strengthRater, password);
|
||||
static FxController provideNewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater) {
|
||||
return new NewPasswordController(resourceBundle, strengthRater);
|
||||
}
|
||||
|
||||
@Binds
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.NewPasswordController;
|
||||
import org.cryptomator.ui.common.Tasks;
|
||||
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
|
||||
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
|
||||
@@ -23,7 +24,6 @@ import org.slf4j.LoggerFactory;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.binding.ObjectBinding;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
@@ -41,7 +41,6 @@ import java.net.URI;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.security.SecureRandom;
|
||||
@@ -70,7 +69,6 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
private final StringProperty recoveryKeyProperty;
|
||||
private final VaultListManager vaultListManager;
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final ObjectProperty<CharSequence> password;
|
||||
private final ReadmeGenerator readmeGenerator;
|
||||
private final SecureRandom csprng;
|
||||
private final MasterkeyFileAccess masterkeyFileAccess;
|
||||
@@ -81,9 +79,10 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
public ToggleGroup recoveryKeyChoice;
|
||||
public Toggle showRecoveryKey;
|
||||
public Toggle skipRecoveryKey;
|
||||
public NewPasswordController newPasswordSceneController;
|
||||
|
||||
@Inject
|
||||
CreateNewVaultPasswordController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_RECOVERYKEY) Lazy<Scene> recoveryKeyScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, ErrorComponent.Builder errorComponent, ExecutorService executor, RecoveryKeyFactory recoveryKeyFactory, @Named("vaultName") StringProperty vaultName, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, @Named("recoveryKey") StringProperty recoveryKey, VaultListManager vaultListManager, ResourceBundle resourceBundle, @Named("newPassword") ObjectProperty<CharSequence> password, ReadmeGenerator readmeGenerator, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) {
|
||||
CreateNewVaultPasswordController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_RECOVERYKEY) Lazy<Scene> recoveryKeyScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, ErrorComponent.Builder errorComponent, ExecutorService executor, RecoveryKeyFactory recoveryKeyFactory, @Named("vaultName") StringProperty vaultName, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, @Named("recoveryKey") StringProperty recoveryKey, VaultListManager vaultListManager, ResourceBundle resourceBundle, ReadmeGenerator readmeGenerator, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) {
|
||||
this.window = window;
|
||||
this.chooseLocationScene = chooseLocationScene;
|
||||
this.recoveryKeyScene = recoveryKeyScene;
|
||||
@@ -97,7 +96,6 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
this.recoveryKeyProperty = recoveryKey;
|
||||
this.vaultListManager = vaultListManager;
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.password = password;
|
||||
this.readmeGenerator = readmeGenerator;
|
||||
this.csprng = csprng;
|
||||
this.masterkeyFileAccess = masterkeyFileAccess;
|
||||
@@ -108,8 +106,11 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
BooleanBinding isValidNewPassword = Bindings.createBooleanBinding(() -> password.get() != null && password.get().length() > 0, password);
|
||||
readyToCreateVault.bind(isValidNewPassword.and(recoveryKeyChoice.selectedToggleProperty().isNotNull()).and(processing.not()));
|
||||
readyToCreateVault.bind(newPasswordSceneController.passwordsMatchAndSufficientProperty().and(recoveryKeyChoice.selectedToggleProperty().isNotNull()).and(processing.not()));
|
||||
window.setOnHiding(event -> {
|
||||
newPasswordSceneController.passwordField.wipe();
|
||||
newPasswordSceneController.reenterField.wipe();
|
||||
});
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -142,8 +143,8 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
Path pathToVault = vaultPathProperty.get();
|
||||
processing.set(true);
|
||||
Tasks.create(() -> {
|
||||
initializeVault(pathToVault, password.get());
|
||||
return recoveryKeyFactory.createRecoveryKey(pathToVault, password.get());
|
||||
initializeVault(pathToVault);
|
||||
return recoveryKeyFactory.createRecoveryKey(pathToVault, newPasswordSceneController.passwordField.getCharacters());
|
||||
}).onSuccess(recoveryKey -> {
|
||||
initializationSucceeded(pathToVault);
|
||||
recoveryKeyProperty.set(recoveryKey);
|
||||
@@ -160,7 +161,7 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
Path pathToVault = vaultPathProperty.get();
|
||||
processing.set(true);
|
||||
Tasks.create(() -> {
|
||||
initializeVault(pathToVault, password.get());
|
||||
initializeVault(pathToVault);
|
||||
}).onSuccess(() -> {
|
||||
initializationSucceeded(pathToVault);
|
||||
window.setScene(successScene.get());
|
||||
@@ -172,11 +173,11 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
}).runOnce(executor);
|
||||
}
|
||||
|
||||
private void initializeVault(Path path, CharSequence passphrase) throws IOException {
|
||||
private void initializeVault(Path path) throws IOException {
|
||||
// 1. write masterkey:
|
||||
Path masterkeyFilePath = path.resolve(MASTERKEY_FILENAME);
|
||||
try (Masterkey masterkey = Masterkey.generate(csprng)) {
|
||||
masterkeyFileAccess.persist(masterkey, masterkeyFilePath, passphrase);
|
||||
masterkeyFileAccess.persist(masterkey, masterkeyFilePath, newPasswordSceneController.passwordField.getCharacters());
|
||||
|
||||
// 2. initialize vault:
|
||||
try {
|
||||
|
||||
@@ -10,21 +10,18 @@ import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||
import org.cryptomator.ui.common.Animations;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.NewPasswordController;
|
||||
import org.cryptomator.ui.controls.NiceSecurePasswordField;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.stage.Stage;
|
||||
import java.io.IOException;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
@@ -41,7 +38,6 @@ public class ChangePasswordController implements FxController {
|
||||
|
||||
private final Stage window;
|
||||
private final Vault vault;
|
||||
private final ObjectProperty<CharSequence> newPassword;
|
||||
private final ErrorComponent.Builder errorComponent;
|
||||
private final KeychainManager keychain;
|
||||
private final SecureRandom csprng;
|
||||
@@ -50,12 +46,12 @@ public class ChangePasswordController implements FxController {
|
||||
public NiceSecurePasswordField oldPasswordField;
|
||||
public CheckBox finalConfirmationCheckbox;
|
||||
public Button finishButton;
|
||||
public NewPasswordController newPasswordController;
|
||||
|
||||
@Inject
|
||||
public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, @Named("newPassword") ObjectProperty<CharSequence> newPassword, ErrorComponent.Builder errorComponent, KeychainManager keychain, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) {
|
||||
public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, ErrorComponent.Builder errorComponent, KeychainManager keychain, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) {
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
this.newPassword = newPassword;
|
||||
this.errorComponent = errorComponent;
|
||||
this.keychain = keychain;
|
||||
this.csprng = csprng;
|
||||
@@ -66,8 +62,12 @@ public class ChangePasswordController implements FxController {
|
||||
public void initialize() {
|
||||
BooleanBinding checkboxNotConfirmed = finalConfirmationCheckbox.selectedProperty().not();
|
||||
BooleanBinding oldPasswordFieldEmpty = oldPasswordField.textProperty().isEmpty();
|
||||
BooleanBinding newPasswordInvalid = Bindings.createBooleanBinding(() -> newPassword.get() == null || newPassword.get().length() == 0, newPassword);
|
||||
finishButton.disableProperty().bind(checkboxNotConfirmed.or(oldPasswordFieldEmpty).or(newPasswordInvalid));
|
||||
finishButton.disableProperty().bind(checkboxNotConfirmed.or(oldPasswordFieldEmpty).or(newPasswordController.passwordsMatchAndSufficientProperty().not()));
|
||||
window.setOnHiding(event -> {
|
||||
oldPasswordField.wipe();
|
||||
newPasswordController.passwordField.wipe();
|
||||
newPasswordController.reenterField.wipe();
|
||||
});
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -78,10 +78,8 @@ public class ChangePasswordController implements FxController {
|
||||
@FXML
|
||||
public void finish() {
|
||||
try {
|
||||
//String normalizedOldPassphrase = Normalizer.normalize(oldPasswordField.getCharacters(), Normalizer.Form.NFC);
|
||||
//String normalizedNewPassphrase = Normalizer.normalize(newPassword.get(), Normalizer.Form.NFC);
|
||||
CharSequence oldPassphrase = oldPasswordField.getCharacters(); // TODO verify: is this already NFC-normalized?
|
||||
CharSequence newPassphrase = newPassword.get(); // TODO verify: is this already NFC-normalized?
|
||||
CharSequence oldPassphrase = oldPasswordField.getCharacters();
|
||||
CharSequence newPassphrase = newPasswordController.passwordField.getCharacters();
|
||||
Path masterkeyPath = vault.getPath().resolve(MASTERKEY_FILENAME);
|
||||
byte[] oldMasterkeyBytes = Files.readAllBytes(masterkeyPath);
|
||||
byte[] newMasterkeyBytes = masterkeyFileAccess.changePassphrase(oldMasterkeyBytes, oldPassphrase, newPassphrase);
|
||||
@@ -89,8 +87,8 @@ public class ChangePasswordController implements FxController {
|
||||
Files.move(masterkeyPath, backupKeyPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||
Files.write(masterkeyPath, newMasterkeyBytes, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE);
|
||||
LOG.info("Successfully changed password for {}", vault.getDisplayName());
|
||||
window.close();
|
||||
updatePasswordInSystemkeychain();
|
||||
window.close();
|
||||
} catch (InvalidPassphraseException e) {
|
||||
Animations.createShakeWindowAnimation(window).play();
|
||||
oldPasswordField.selectAll();
|
||||
@@ -104,7 +102,7 @@ public class ChangePasswordController implements FxController {
|
||||
private void updatePasswordInSystemkeychain() {
|
||||
if (keychain.isSupported()) {
|
||||
try {
|
||||
keychain.changePassphrase(vault.getId(), CharBuffer.wrap(newPassword.get()));
|
||||
keychain.changePassphrase(vault.getId(), newPasswordController.passwordField.getCharacters());
|
||||
LOG.info("Successfully updated password in system keychain for {}", vault.getDisplayName());
|
||||
} catch (KeychainAccessException e) {
|
||||
LOG.error("Failed to update password in system keychain.", e);
|
||||
|
||||
@@ -5,10 +5,10 @@ import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import org.cryptomator.ui.common.DefaultSceneFactory;
|
||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxControllerKey;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.NewPasswordController;
|
||||
import org.cryptomator.ui.common.PasswordStrengthUtil;
|
||||
@@ -16,8 +16,6 @@ import org.cryptomator.ui.common.StageFactory;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
@@ -27,13 +25,6 @@ import java.util.ResourceBundle;
|
||||
@Module
|
||||
abstract class ChangePasswordModule {
|
||||
|
||||
@Provides
|
||||
@ChangePasswordScoped
|
||||
@Named("newPassword")
|
||||
static ObjectProperty<CharSequence> provideNewPasswordProperty() {
|
||||
return new SimpleObjectProperty<>("");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ChangePasswordWindow
|
||||
@ChangePasswordScoped
|
||||
@@ -71,8 +62,8 @@ abstract class ChangePasswordModule {
|
||||
@Provides
|
||||
@IntoMap
|
||||
@FxControllerKey(NewPasswordController.class)
|
||||
static FxController provideNewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater, @Named("newPassword") ObjectProperty<CharSequence> password) {
|
||||
return new NewPasswordController(resourceBundle, strengthRater, password);
|
||||
static FxController provideNewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater) {
|
||||
return new NewPasswordController(resourceBundle, strengthRater);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ public enum FxmlFile {
|
||||
CHANGEPASSWORD("/fxml/changepassword.fxml"), //
|
||||
ERROR("/fxml/error.fxml"), //
|
||||
FORGET_PASSWORD("/fxml/forget_password.fxml"), //
|
||||
HEALTH_START("/fxml/health_start.fxml"), //
|
||||
HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), //
|
||||
LOCK_FORCED("/fxml/lock_forced.fxml"), //
|
||||
LOCK_FAILED("/fxml/lock_failed.fxml"), //
|
||||
MAIN_WINDOW("/fxml/main_window.fxml"), //
|
||||
|
||||
@@ -8,7 +8,8 @@ import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
@@ -18,8 +19,8 @@ public class NewPasswordController implements FxController {
|
||||
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final PasswordStrengthUtil strengthRater;
|
||||
private final ObjectProperty<CharSequence> password;
|
||||
private final IntegerProperty passwordStrength = new SimpleIntegerProperty(-1);
|
||||
private final ReadOnlyBooleanWrapper passwordsMatchAndSufficient = new ReadOnlyBooleanWrapper();
|
||||
|
||||
public NiceSecurePasswordField passwordField;
|
||||
public NiceSecurePasswordField reenterField;
|
||||
@@ -31,10 +32,9 @@ public class NewPasswordController implements FxController {
|
||||
public FontAwesome5IconView passwordMatchCheckmark;
|
||||
public FontAwesome5IconView passwordMatchCross;
|
||||
|
||||
public NewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater, ObjectProperty<CharSequence> password) {
|
||||
public NewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater) {
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.strengthRater = strengthRater;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -44,7 +44,7 @@ public class NewPasswordController implements FxController {
|
||||
passwordStrengthLabel.graphicProperty().bind(Bindings.createObjectBinding(this::getIconViewForPasswordStrengthLabel, passwordField.textProperty(), passwordStrength));
|
||||
passwordStrengthLabel.textProperty().bind(EasyBind.map(passwordStrength, strengthRater::getStrengthDescription));
|
||||
|
||||
BooleanBinding passwordsMatch = Bindings.createBooleanBinding(this::hasSamePasswordInBothFields, passwordField.textProperty(), reenterField.textProperty());
|
||||
BooleanBinding passwordsMatch = Bindings.createBooleanBinding(this::passwordFieldsMatch, passwordField.textProperty(), reenterField.textProperty());
|
||||
BooleanBinding reenterFieldNotEmpty = reenterField.textProperty().isNotEmpty();
|
||||
passwordMatchLabel.visibleProperty().bind(reenterFieldNotEmpty);
|
||||
passwordMatchLabel.graphicProperty().bind(Bindings.when(passwordsMatch.and(reenterFieldNotEmpty)).then(passwordMatchCheckmark).otherwise(passwordMatchCross));
|
||||
@@ -54,6 +54,7 @@ public class NewPasswordController implements FxController {
|
||||
reenterField.textProperty().addListener(this::passwordsDidChange);
|
||||
}
|
||||
|
||||
|
||||
private FontAwesome5IconView getIconViewForPasswordStrengthLabel() {
|
||||
if (passwordField.getCharacters().length() == 0) {
|
||||
return null;
|
||||
@@ -67,17 +68,19 @@ public class NewPasswordController implements FxController {
|
||||
}
|
||||
|
||||
private void passwordsDidChange(@SuppressWarnings("unused") Observable observable) {
|
||||
if (hasSamePasswordInBothFields() && strengthRater.fulfillsMinimumRequirements(passwordField.getCharacters())) {
|
||||
password.set(passwordField.getCharacters());
|
||||
} else {
|
||||
password.set("");
|
||||
if (passwordFieldsMatch() && strengthRater.fulfillsMinimumRequirements(passwordField.getCharacters())) {
|
||||
passwordsMatchAndSufficient.setValue(true);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasSamePasswordInBothFields() {
|
||||
private boolean passwordFieldsMatch() {
|
||||
return CharSequence.compare(passwordField.getCharacters(), reenterField.getCharacters()) == 0;
|
||||
}
|
||||
|
||||
public ReadOnlyBooleanProperty passwordsMatchAndSufficientProperty() {
|
||||
return passwordsMatchAndSufficient.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
/* Getter/Setter */
|
||||
|
||||
public IntegerProperty passwordStrengthProperty() {
|
||||
|
||||
@@ -6,8 +6,10 @@ package org.cryptomator.ui.controls;
|
||||
public enum FontAwesome5Icon {
|
||||
ANCHOR("\uF13D"), //
|
||||
ARROW_UP("\uF062"), //
|
||||
BAN("\uF05E"), //
|
||||
BUG("\uF188"), //
|
||||
CHECK("\uF00C"), //
|
||||
CLOCK("\uF017"), //
|
||||
COG("\uF013"), //
|
||||
COGS("\uF085"), //
|
||||
COPY("\uF0C5"), //
|
||||
|
||||
@@ -12,6 +12,7 @@ public class FormattedLabel extends Label {
|
||||
|
||||
private final StringProperty format = new SimpleStringProperty("");
|
||||
private final ObjectProperty<Object> arg1 = new SimpleObjectProperty<>();
|
||||
private final ObjectProperty<Object> arg2 = new SimpleObjectProperty<>();
|
||||
// add arg2, arg3, ... on demand
|
||||
|
||||
public FormattedLabel() {
|
||||
@@ -19,11 +20,11 @@ public class FormattedLabel extends Label {
|
||||
}
|
||||
|
||||
protected StringBinding createStringBinding() {
|
||||
return Bindings.createStringBinding(this::updateText, format, arg1);
|
||||
return Bindings.createStringBinding(this::updateText, format, arg1, arg2);
|
||||
}
|
||||
|
||||
private String updateText() {
|
||||
return String.format(format.get(), arg1.get());
|
||||
return String.format(format.get(), arg1.get(), arg2.get());
|
||||
}
|
||||
|
||||
/* Observables */
|
||||
@@ -51,4 +52,16 @@ public class FormattedLabel extends Label {
|
||||
public void setArg1(Object arg1) {
|
||||
this.arg1.set(arg1);
|
||||
}
|
||||
|
||||
public ObjectProperty<Object> arg2Property() {
|
||||
return arg2;
|
||||
}
|
||||
|
||||
public Object getArg2() {
|
||||
return arg2.get();
|
||||
}
|
||||
|
||||
public void setArg2(Object arg2) {
|
||||
this.arg2.set(arg2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ public class SecurePasswordField extends TextField {
|
||||
}
|
||||
|
||||
private void updateCapsLocked() {
|
||||
// AWT code needed until https://bugs.openjdk.java.net/browse/JDK-8090882 is closed:
|
||||
//TODO: fixed in JavaFX 17. AWT code needed until update (see https://bugs.openjdk.java.net/browse/JDK-8259680)
|
||||
capsLocked.set(isFocused() && Toolkit.getDefaultToolkit().getLockingKeyState(java.awt.event.KeyEvent.VK_CAPS_LOCK));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.cryptomator.ui.health;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Suppliers;
|
||||
import dagger.Lazy;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.concurrent.Service;
|
||||
import javafx.concurrent.Task;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class BatchService extends Service<Void> {
|
||||
|
||||
private final Iterator<HealthCheckTask> remainingTasks;
|
||||
|
||||
@Inject
|
||||
public BatchService(Iterable<HealthCheckTask> tasks) {
|
||||
this.remainingTasks = tasks.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Task<Void> createTask() {
|
||||
Preconditions.checkState(remainingTasks.hasNext(), "No remaining tasks");
|
||||
return remainingTasks.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void succeeded() {
|
||||
if (remainingTasks.hasNext()) {
|
||||
this.restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
package org.cryptomator.ui.health;
|
||||
|
||||
import com.tobiasdiez.easybind.EasyBind;
|
||||
import com.tobiasdiez.easybind.EasyObservableList;
|
||||
import com.tobiasdiez.easybind.Subscription;
|
||||
import com.tobiasdiez.easybind.optional.OptionalBinding;
|
||||
import org.cryptomator.cryptofs.health.api.DiagnosticResult;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.concurrent.Worker;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.ListView;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@HealthCheckScoped
|
||||
public class CheckDetailController implements FxController {
|
||||
|
||||
private final EasyObservableList<DiagnosticResult> results;
|
||||
private final OptionalBinding<Worker.State> taskState;
|
||||
private final Binding<String> taskName;
|
||||
private final Binding<Number> taskDuration;
|
||||
private final ResultListCellFactory resultListCellFactory;
|
||||
private final Binding<Boolean> taskRunning;
|
||||
private final Binding<Boolean> taskScheduled;
|
||||
private final Binding<Boolean> taskFinished;
|
||||
private final Binding<Boolean> taskNotStarted;
|
||||
private final Binding<Boolean> taskSucceeded;
|
||||
private final Binding<Boolean> taskFailed;
|
||||
private final Binding<Boolean> taskCancelled;
|
||||
private final Binding<Number> countOfWarnSeverity;
|
||||
private final Binding<Number> countOfCritSeverity;
|
||||
|
||||
public ListView<DiagnosticResult> resultsListView;
|
||||
private Subscription resultSubscription;
|
||||
|
||||
@Inject
|
||||
public CheckDetailController(ObjectProperty<HealthCheckTask> selectedTask, ResultListCellFactory resultListCellFactory) {
|
||||
this.results = EasyBind.wrapList(FXCollections.observableArrayList());
|
||||
this.taskState = EasyBind.wrapNullable(selectedTask).mapObservable(HealthCheckTask::stateProperty);
|
||||
this.taskName = EasyBind.wrapNullable(selectedTask).map(HealthCheckTask::getTitle).orElse("");
|
||||
this.taskDuration = EasyBind.wrapNullable(selectedTask).mapObservable(HealthCheckTask::durationInMillisProperty).orElse(-1L);
|
||||
this.resultListCellFactory = resultListCellFactory;
|
||||
this.taskRunning = EasyBind.wrapNullable(selectedTask).mapObservable(HealthCheckTask::runningProperty).orElse(false); //TODO: DOES NOT WORK
|
||||
this.taskScheduled = taskState.map(Worker.State.SCHEDULED::equals).orElse(false);
|
||||
this.taskNotStarted = taskState.map(Worker.State.READY::equals).orElse(false);
|
||||
this.taskSucceeded = taskState.map(Worker.State.SUCCEEDED::equals).orElse(false);
|
||||
this.taskFailed = taskState.map(Worker.State.FAILED::equals).orElse(false);
|
||||
this.taskCancelled = taskState.map(Worker.State.CANCELLED::equals).orElse(false);
|
||||
this.taskFinished = EasyBind.combine(taskSucceeded, taskFailed, taskCancelled, (a, b, c) -> a || b || c);
|
||||
this.countOfWarnSeverity = results.reduce(countSeverity(DiagnosticResult.Severity.WARN));
|
||||
this.countOfCritSeverity = results.reduce(countSeverity(DiagnosticResult.Severity.CRITICAL));
|
||||
selectedTask.addListener(this::selectedTaskChanged);
|
||||
}
|
||||
|
||||
private void selectedTaskChanged(ObservableValue<? extends HealthCheckTask> observable, HealthCheckTask oldValue, HealthCheckTask newValue) {
|
||||
if (resultSubscription != null) {
|
||||
resultSubscription.unsubscribe();
|
||||
}
|
||||
if (newValue != null) {
|
||||
resultSubscription = EasyBind.bindContent(results, newValue.results());
|
||||
}
|
||||
}
|
||||
|
||||
private Function<Stream<? extends DiagnosticResult>, Long> countSeverity(DiagnosticResult.Severity severity) {
|
||||
return stream -> stream.filter(item -> severity.equals(item.getServerity())).count();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
resultsListView.setItems(results);
|
||||
resultsListView.setCellFactory(resultListCellFactory);
|
||||
}
|
||||
|
||||
/* Getter/Setter */
|
||||
|
||||
public String getTaskName() {
|
||||
return taskName.getValue();
|
||||
}
|
||||
|
||||
public Binding<String> taskNameProperty() {
|
||||
return taskName;
|
||||
}
|
||||
|
||||
public Number getTaskDuration() {
|
||||
return taskDuration.getValue();
|
||||
}
|
||||
|
||||
public Binding<Number> taskDurationProperty() {
|
||||
return taskDuration;
|
||||
}
|
||||
|
||||
public long getCountOfWarnSeverity() {
|
||||
return countOfWarnSeverity.getValue().longValue();
|
||||
}
|
||||
|
||||
public Binding<Number> countOfWarnSeverityProperty() {
|
||||
return countOfWarnSeverity;
|
||||
}
|
||||
|
||||
public long getCountOfCritSeverity() {
|
||||
return countOfCritSeverity.getValue().longValue();
|
||||
}
|
||||
|
||||
public Binding<Number> countOfCritSeverityProperty() {
|
||||
return countOfCritSeverity;
|
||||
}
|
||||
|
||||
public boolean isTaskRunning() {
|
||||
return taskRunning.getValue();
|
||||
}
|
||||
|
||||
public Binding<Boolean> taskRunningProperty() {
|
||||
return taskRunning;
|
||||
}
|
||||
|
||||
public boolean isTaskFinished() {
|
||||
return taskFinished.getValue();
|
||||
}
|
||||
|
||||
public Binding<Boolean> taskFinishedProperty() {
|
||||
return taskFinished;
|
||||
}
|
||||
|
||||
public boolean isTaskScheduled() {
|
||||
return taskScheduled.getValue();
|
||||
}
|
||||
|
||||
public Binding<Boolean> taskScheduledProperty() {
|
||||
return taskScheduled;
|
||||
}
|
||||
|
||||
public boolean isTaskNotStarted() {
|
||||
return taskNotStarted.getValue();
|
||||
}
|
||||
|
||||
public Binding<Boolean> taskNotStartedProperty() {
|
||||
return taskNotStarted;
|
||||
}
|
||||
|
||||
public boolean isTaskSucceeded() {
|
||||
return taskSucceeded.getValue();
|
||||
}
|
||||
|
||||
public Binding<Boolean> taskSucceededProperty() {
|
||||
return taskSucceeded;
|
||||
}
|
||||
|
||||
public boolean isTaskFailed() {
|
||||
return taskFailed.getValue();
|
||||
}
|
||||
|
||||
public Binding<Boolean> taskFailedProperty() {
|
||||
return taskFailed;
|
||||
}
|
||||
|
||||
public boolean isTaskCancelled() {
|
||||
return taskCancelled.getValue();
|
||||
}
|
||||
|
||||
public Binding<Boolean> taskCancelledProperty() {
|
||||
return taskCancelled;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.cryptomator.ui.health;
|
||||
|
||||
import org.cryptomator.ui.controls.FontAwesome5Icon;
|
||||
import org.cryptomator.ui.controls.FontAwesome5IconView;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.concurrent.Worker;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.ContentDisplay;
|
||||
import javafx.scene.control.ListCell;
|
||||
|
||||
class CheckListCell extends ListCell<HealthCheckTask> {
|
||||
|
||||
private final FontAwesome5IconView stateIcon = new FontAwesome5IconView();
|
||||
|
||||
CheckListCell() {
|
||||
setPadding(new Insets(6));
|
||||
setAlignment(Pos.CENTER_LEFT);
|
||||
setContentDisplay(ContentDisplay.LEFT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateItem(HealthCheckTask item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (item != null) {
|
||||
textProperty().bind(item.titleProperty());
|
||||
item.stateProperty().addListener(this::stateChanged);
|
||||
graphicProperty().bind(Bindings.createObjectBinding(() -> graphicForState(item.getState()),item.stateProperty()));
|
||||
stateIcon.setGlyph(glyphForState(item.getState()));
|
||||
} else {
|
||||
textProperty().unbind();
|
||||
graphicProperty().unbind();
|
||||
setGraphic(null);
|
||||
setText(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void stateChanged(ObservableValue<? extends Worker.State> observable, Worker.State oldState, Worker.State newState) {
|
||||
stateIcon.setGlyph(glyphForState(newState));
|
||||
stateIcon.setVisible(true);
|
||||
}
|
||||
|
||||
private Node graphicForState(Worker.State state) {
|
||||
return switch (state) {
|
||||
case READY -> null;
|
||||
case SCHEDULED, RUNNING, FAILED, CANCELLED, SUCCEEDED -> stateIcon;
|
||||
};
|
||||
}
|
||||
|
||||
private FontAwesome5Icon glyphForState(Worker.State state) {
|
||||
return switch (state) {
|
||||
case READY -> FontAwesome5Icon.COG; //just a placeholder
|
||||
case SCHEDULED -> FontAwesome5Icon.CLOCK;
|
||||
case RUNNING -> FontAwesome5Icon.SPINNER;
|
||||
case FAILED -> FontAwesome5Icon.EXCLAMATION_TRIANGLE;
|
||||
case CANCELLED -> FontAwesome5Icon.BAN;
|
||||
case SUCCEEDED -> FontAwesome5Icon.CHECK;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package org.cryptomator.ui.health;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.tobiasdiez.easybind.EasyBind;
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.concurrent.Worker;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.control.cell.CheckBoxListCell;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.util.StringConverter;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
@HealthCheckScoped
|
||||
public class CheckListController implements FxController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CheckListController.class);
|
||||
private static final Set<Worker.State> END_STATES = Set.of(Worker.State.FAILED, Worker.State.CANCELLED, Worker.State.SUCCEEDED);
|
||||
|
||||
private final Stage window;
|
||||
private final ObservableList<HealthCheckTask> tasks;
|
||||
private final ReportWriter reportWriter;
|
||||
private final ExecutorService executorService;
|
||||
private final ObjectProperty<HealthCheckTask> selectedTask;
|
||||
private final Lazy<ErrorComponent.Builder> errorComponenBuilder;
|
||||
private final SimpleObjectProperty<Worker<?>> runningTask;
|
||||
private final Binding<Boolean> running;
|
||||
private final Binding<Boolean> finished;
|
||||
private final Map<HealthCheckTask, BooleanProperty> listPickIndicators;
|
||||
private final IntegerProperty numberOfPickedChecks;
|
||||
private final BooleanBinding anyCheckSelected;
|
||||
private final BooleanProperty showResultScreen;
|
||||
|
||||
/* FXML */
|
||||
public ListView<HealthCheckTask> checksListView;
|
||||
|
||||
|
||||
@Inject
|
||||
public CheckListController(@HealthCheckWindow Stage window, Lazy<Collection<HealthCheckTask>> tasks, ReportWriter reportWriteTask, ObjectProperty<HealthCheckTask> selectedTask, ExecutorService executorService, Lazy<ErrorComponent.Builder> errorComponenBuilder) {
|
||||
this.window = window;
|
||||
this.tasks = FXCollections.observableArrayList(tasks.get());
|
||||
this.reportWriter = reportWriteTask;
|
||||
this.executorService = executorService;
|
||||
this.selectedTask = selectedTask;
|
||||
this.errorComponenBuilder = errorComponenBuilder;
|
||||
this.runningTask = new SimpleObjectProperty<>();
|
||||
this.running = EasyBind.wrapNullable(runningTask).mapObservable(Worker::runningProperty).orElse(false);
|
||||
this.finished = EasyBind.wrapNullable(runningTask).mapObservable(Worker::stateProperty).map(END_STATES::contains).orElse(false);
|
||||
this.listPickIndicators = new HashMap<>();
|
||||
this.numberOfPickedChecks = new SimpleIntegerProperty(0);
|
||||
this.tasks.forEach(task -> {
|
||||
var entrySelectedProp = new SimpleBooleanProperty(false);
|
||||
entrySelectedProp.addListener((observable, oldValue, newValue) -> numberOfPickedChecks.set(numberOfPickedChecks.get() + (newValue ? 1 : -1)));
|
||||
listPickIndicators.put(task, entrySelectedProp);
|
||||
});
|
||||
this.anyCheckSelected = selectedTask.isNotNull();
|
||||
this.showResultScreen = new SimpleBooleanProperty(false);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
checksListView.setItems(tasks);
|
||||
checksListView.setCellFactory(CheckBoxListCell.forListView(listPickIndicators::get, new StringConverter<HealthCheckTask>() {
|
||||
@Override
|
||||
public String toString(HealthCheckTask object) {
|
||||
return object.getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HealthCheckTask fromString(String string) {
|
||||
return null;
|
||||
}
|
||||
}));
|
||||
selectedTask.bind(checksListView.getSelectionModel().selectedItemProperty());
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void toggleSelectAll(ActionEvent event) {
|
||||
if (event.getSource() instanceof CheckBox c) {
|
||||
listPickIndicators.forEach( (task, pickProperty) -> pickProperty.set(c.isSelected()));
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void runSelectedChecks() {
|
||||
Preconditions.checkState(runningTask.get() == null);
|
||||
var batch = checksListView.getItems().filtered(item -> listPickIndicators.get(item).get());
|
||||
var batchService = new BatchService(batch);
|
||||
batchService.setExecutor(executorService);
|
||||
batchService.start();
|
||||
runningTask.set(batchService);
|
||||
showResultScreen.set(true);
|
||||
checksListView.getSelectionModel().select(batch.get(0));
|
||||
checksListView.setCellFactory(view -> new CheckListCell());
|
||||
window.sizeToScene();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public synchronized void cancelCheck() {
|
||||
Preconditions.checkState(runningTask.get() != null);
|
||||
runningTask.get().cancel();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void exportResults() {
|
||||
try {
|
||||
reportWriter.writeReport(tasks);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to write health check report.", e);
|
||||
errorComponenBuilder.get().cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
|
||||
}
|
||||
}
|
||||
|
||||
/* Getter/Setter */
|
||||
public boolean isRunning() {
|
||||
return running.getValue();
|
||||
}
|
||||
|
||||
public Binding<Boolean> runningProperty() {
|
||||
return running;
|
||||
}
|
||||
|
||||
public boolean isFinished() {
|
||||
return finished.getValue();
|
||||
}
|
||||
|
||||
public Binding<Boolean> finishedProperty() {
|
||||
return finished;
|
||||
}
|
||||
|
||||
public boolean isAnyCheckSelected() {
|
||||
return anyCheckSelected.get();
|
||||
}
|
||||
|
||||
public BooleanBinding anyCheckSelectedProperty() {
|
||||
return anyCheckSelected;
|
||||
}
|
||||
|
||||
public boolean getShowResultScreen() {
|
||||
return showResultScreen.get();
|
||||
}
|
||||
|
||||
public BooleanProperty showResultScreenProperty() {
|
||||
return showResultScreen;
|
||||
}
|
||||
|
||||
public int getNumberOfPickedChecks() {
|
||||
return numberOfPickedChecks.get();
|
||||
}
|
||||
|
||||
public IntegerProperty numberOfPickedChecksProperty() {
|
||||
return numberOfPickedChecks;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.cryptomator.ui.health;
|
||||
|
||||
import dagger.BindsInstance;
|
||||
import dagger.Lazy;
|
||||
import dagger.Subcomponent;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
@HealthCheckScoped
|
||||
@Subcomponent(modules = {HealthCheckModule.class})
|
||||
public interface HealthCheckComponent {
|
||||
|
||||
@HealthCheckWindow
|
||||
Stage window();
|
||||
|
||||
@FxmlScene(FxmlFile.HEALTH_START)
|
||||
Lazy<Scene> scene();
|
||||
|
||||
default Stage showHealthCheckWindow() {
|
||||
Stage stage = window();
|
||||
stage.setScene(scene().get());
|
||||
stage.show();
|
||||
return stage;
|
||||
}
|
||||
|
||||
@Subcomponent.Builder
|
||||
interface Builder {
|
||||
|
||||
@BindsInstance
|
||||
Builder vault(@HealthCheckWindow Vault vault);
|
||||
|
||||
HealthCheckComponent build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package org.cryptomator.ui.health;
|
||||
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.cryptofs.VaultConfig;
|
||||
import org.cryptomator.cryptofs.health.api.HealthCheck;
|
||||
import org.cryptomator.cryptolib.api.Masterkey;
|
||||
import org.cryptomator.ui.common.DefaultSceneFactory;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxControllerKey;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.StageFactory;
|
||||
import org.cryptomator.ui.keyloading.KeyLoadingComponent;
|
||||
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
|
||||
import org.cryptomator.ui.mainwindow.MainWindow;
|
||||
|
||||
import javax.inject.Provider;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@Module(subcomponents = {KeyLoadingComponent.class})
|
||||
abstract class HealthCheckModule {
|
||||
|
||||
@Provides
|
||||
@HealthCheckScoped
|
||||
static AtomicReference<Masterkey> provideMasterkeyRef() {
|
||||
return new AtomicReference<>();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@HealthCheckScoped
|
||||
static AtomicReference<VaultConfig> provideVaultConfigRef() {
|
||||
return new AtomicReference<>();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@HealthCheckScoped
|
||||
static Collection<HealthCheck> provideAvailableHealthChecks() {
|
||||
return HealthCheck.allChecks();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@HealthCheckScoped
|
||||
static ObjectProperty<HealthCheckTask> provideSelectedHealthCheckTask() {
|
||||
return new SimpleObjectProperty<>();
|
||||
}
|
||||
|
||||
/* Only inject with Lazy-Wrapper!*/
|
||||
@Provides
|
||||
@HealthCheckScoped
|
||||
static Collection<HealthCheckTask> provideAvailableHealthCheckTasks(Collection<HealthCheck> availableHealthChecks, @HealthCheckWindow Vault vault, AtomicReference<Masterkey> masterkeyRef, AtomicReference<VaultConfig> vaultConfigRef, SecureRandom csprng, ResourceBundle resourceBundle) {
|
||||
return availableHealthChecks.stream().map(check -> new HealthCheckTask(vault.getPath(), vaultConfigRef.get(), masterkeyRef.get(), csprng, check, resourceBundle)).toList();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@HealthCheckWindow
|
||||
@HealthCheckScoped
|
||||
static KeyLoadingStrategy provideKeyLoadingStrategy(KeyLoadingComponent.Builder compBuilder, @HealthCheckWindow Vault vault, @HealthCheckWindow Stage window) {
|
||||
return compBuilder.vault(vault).window(window).build().keyloadingStrategy();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@HealthCheckWindow
|
||||
@HealthCheckScoped
|
||||
static FxmlLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
|
||||
return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@HealthCheckWindow
|
||||
@HealthCheckScoped
|
||||
static Stage provideStage(StageFactory factory, @MainWindow Stage owner, ResourceBundle resourceBundle, ChangeListener<Boolean> showingListener) {
|
||||
Stage stage = factory.create();
|
||||
stage.setTitle(resourceBundle.getString("health.title"));
|
||||
stage.setResizable(true);
|
||||
stage.initModality(Modality.WINDOW_MODAL);
|
||||
stage.initOwner(owner);
|
||||
stage.showingProperty().addListener(showingListener); // bind masterkey lifecycle to window
|
||||
return stage;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@HealthCheckScoped
|
||||
static ChangeListener<Boolean> provideWindowShowingChangeListener(AtomicReference<Masterkey> masterkey) {
|
||||
return (observable, wasShowing, isShowing) -> {
|
||||
if (!isShowing) {
|
||||
Optional.ofNullable(masterkey.getAndSet(null)).ifPresent(Masterkey::destroy);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.HEALTH_START)
|
||||
@HealthCheckScoped
|
||||
static Scene provideHealthStartScene(@HealthCheckWindow FxmlLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene(FxmlFile.HEALTH_START);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxmlScene(FxmlFile.HEALTH_CHECK_LIST)
|
||||
@HealthCheckScoped
|
||||
static Scene provideHealthCheckListScene(@HealthCheckWindow FxmlLoaderFactory fxmlLoaders) {
|
||||
return fxmlLoaders.createScene(FxmlFile.HEALTH_CHECK_LIST);
|
||||
}
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(StartController.class)
|
||||
abstract FxController bindStartController(StartController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(CheckListController.class)
|
||||
abstract FxController bindCheckController(CheckListController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(CheckDetailController.class)
|
||||
abstract FxController bindCheckDetailController(CheckDetailController controller);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FxControllerKey(ResultListCellController.class)
|
||||
abstract FxController bindResultListCellController(ResultListCellController controller);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.cryptomator.ui.health;
|
||||
|
||||
import javax.inject.Scope;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Scope
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface HealthCheckScoped {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package org.cryptomator.ui.health;
|
||||
|
||||
import org.cryptomator.cryptofs.VaultConfig;
|
||||
import org.cryptomator.cryptofs.health.api.DiagnosticResult;
|
||||
import org.cryptomator.cryptofs.health.api.HealthCheck;
|
||||
import org.cryptomator.cryptolib.api.Masterkey;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.LongProperty;
|
||||
import javafx.beans.property.SimpleLongProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.concurrent.Task;
|
||||
import java.nio.file.Path;
|
||||
import java.security.SecureRandom;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Objects;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.CancellationException;
|
||||
|
||||
class HealthCheckTask extends Task<Void> {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HealthCheckTask.class);
|
||||
|
||||
private final Path vaultPath;
|
||||
private final VaultConfig vaultConfig;
|
||||
private final Masterkey masterkey;
|
||||
private final SecureRandom csprng;
|
||||
private final HealthCheck check;
|
||||
private final ObservableList<DiagnosticResult> results;
|
||||
private final LongProperty durationInMillis;
|
||||
|
||||
public HealthCheckTask(Path vaultPath, VaultConfig vaultConfig, Masterkey masterkey, SecureRandom csprng, HealthCheck check, ResourceBundle resourceBundle) {
|
||||
this.vaultPath = Objects.requireNonNull(vaultPath);
|
||||
this.vaultConfig = Objects.requireNonNull(vaultConfig);
|
||||
this.masterkey = Objects.requireNonNull(masterkey);
|
||||
this.csprng = Objects.requireNonNull(csprng);
|
||||
this.check = Objects.requireNonNull(check);
|
||||
this.results = FXCollections.observableArrayList();
|
||||
try {
|
||||
updateTitle(resourceBundle.getString("health." + check.identifier()));
|
||||
} catch (MissingResourceException e) {
|
||||
LOG.warn("Missing proper name for health check {}, falling back to default.", check.identifier());
|
||||
updateTitle(check.identifier());
|
||||
}
|
||||
this.durationInMillis = new SimpleLongProperty(-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void call() {
|
||||
Instant start = Instant.now();
|
||||
try (var masterkeyClone = masterkey.clone(); //
|
||||
var cryptor = vaultConfig.getCipherCombo().getCryptorProvider(csprng).withKey(masterkeyClone)) {
|
||||
check.check(vaultPath, vaultConfig, masterkeyClone, cryptor, result -> {
|
||||
if (isCancelled()) {
|
||||
throw new CancellationException();
|
||||
}
|
||||
// FIXME: slowdown for demonstration purposes only:
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
if (isCancelled()) {
|
||||
return;
|
||||
} else {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
Platform.runLater(() -> results.add(result));
|
||||
});
|
||||
}
|
||||
Platform.runLater(() ->durationInMillis.set(Duration.between(start, Instant.now()).toMillis()));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void scheduled() {
|
||||
LOG.info("starting {}", check.identifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
LOG.info("finished {}", check.identifier());
|
||||
}
|
||||
|
||||
/* Getter */
|
||||
|
||||
public ObservableList<DiagnosticResult> results() {
|
||||
return results;
|
||||
}
|
||||
|
||||
public HealthCheck getCheck() {
|
||||
return check;
|
||||
}
|
||||
|
||||
public LongProperty durationInMillisProperty() {
|
||||
return durationInMillis;
|
||||
}
|
||||
|
||||
public long getDurationInMillis() {
|
||||
return durationInMillis.get();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.cryptomator.ui.health;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
@Qualifier
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@interface HealthCheckWindow {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package org.cryptomator.ui.health;
|
||||
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.cryptomator.common.Environment;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.cryptofs.VaultConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Application;
|
||||
import javafx.concurrent.Worker;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@HealthCheckScoped
|
||||
public class ReportWriter {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ReportWriter.class);
|
||||
private static final String REPORT_HEADER = """
|
||||
**************************************
|
||||
* Cryptomator Vault Health Report *
|
||||
**************************************
|
||||
Analyzed vault: %s (Current name "%s")
|
||||
Vault storage path: %s
|
||||
""";
|
||||
private static final String REPORT_CHECK_HEADER = """
|
||||
|
||||
|
||||
Check %s
|
||||
------------------------------
|
||||
""";
|
||||
private static final String REPORT_CHECK_RESULT = "%8s - %s\n";
|
||||
private static final DateTimeFormatter TIME_STAMP = DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss").withZone(ZoneId.systemDefault());
|
||||
|
||||
private final Vault vault;
|
||||
private final VaultConfig vaultConfig;
|
||||
private final Application application;
|
||||
private final Path exportDestination;
|
||||
|
||||
@Inject
|
||||
public ReportWriter(@HealthCheckWindow Vault vault, AtomicReference<VaultConfig> vaultConfigRef, Application application, Environment env) {
|
||||
this.vault = vault;
|
||||
this.vaultConfig = Objects.requireNonNull(vaultConfigRef.get());
|
||||
this.application = application;
|
||||
this.exportDestination = env.getLogDir().orElse(Path.of(System.getProperty("user.home"))).resolve("healthReport_" + vault.getDisplayName() + "_" + TIME_STAMP.format(Instant.now()) + ".log");
|
||||
}
|
||||
|
||||
protected void writeReport(Collection<HealthCheckTask> tasks) throws IOException {
|
||||
try (var out = Files.newOutputStream(exportDestination, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING); //
|
||||
var writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8))) {
|
||||
writer.write(REPORT_HEADER.formatted(vaultConfig.getId(), vault.getDisplayName(), vault.getPath()));
|
||||
for (var task : tasks) {
|
||||
if (task.getState() == Worker.State.READY) {
|
||||
LOG.debug("Skipping not performed check {}.", task.getCheck().identifier());
|
||||
continue;
|
||||
}
|
||||
writer.write(REPORT_CHECK_HEADER.formatted(task.getCheck().identifier()));
|
||||
switch (task.getState()) {
|
||||
case SUCCEEDED -> {
|
||||
writer.write("STATUS: SUCCESS\nRESULTS:\n");
|
||||
for (var result : task.results()) {
|
||||
writer.write(REPORT_CHECK_RESULT.formatted(result.getServerity(), result.toString()));
|
||||
}
|
||||
}
|
||||
case CANCELLED -> writer.write("STATUS: CANCELED\n");
|
||||
case FAILED -> {
|
||||
writer.write("STATUS: FAILED\nREASON:\n" + task.getCheck().identifier());
|
||||
writer.write(prepareFailureMsg(task));
|
||||
}
|
||||
case RUNNING, SCHEDULED -> throw new IllegalStateException("Checks are still running.");
|
||||
}
|
||||
}
|
||||
}
|
||||
reveal();
|
||||
}
|
||||
|
||||
private String prepareFailureMsg(HealthCheckTask task) {
|
||||
if (task.getException() != null) {
|
||||
return ExceptionUtils.getStackTrace(task.getException()) //
|
||||
.lines() //
|
||||
.map(line -> "\t\t" + line + "\n") //
|
||||
.collect(Collectors.joining());
|
||||
} else {
|
||||
return "Unknown reason of failure.";
|
||||
}
|
||||
}
|
||||
|
||||
private void reveal() {
|
||||
application.getHostServices().showDocument(exportDestination.getParent().toUri().toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.cryptomator.ui.health;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.cryptofs.VaultConfig;
|
||||
import org.cryptomator.cryptofs.health.api.DiagnosticResult;
|
||||
import org.cryptomator.cryptolib.api.Masterkey;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.scene.control.Alert;
|
||||
import java.nio.file.Path;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@HealthCheckScoped
|
||||
class ResultFixApplier {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ResultFixApplier.class);
|
||||
|
||||
private final Path vaultPath;
|
||||
private final SecureRandom csprng;
|
||||
private final Masterkey masterkey;
|
||||
private final VaultConfig vaultConfig;
|
||||
|
||||
@Inject
|
||||
public ResultFixApplier(@HealthCheckWindow Vault vault, AtomicReference<Masterkey> masterkeyRef, AtomicReference<VaultConfig> vaultConfigRef, SecureRandom csprng) {
|
||||
this.vaultPath = vault.getPath();
|
||||
this.masterkey = masterkeyRef.get();
|
||||
this.vaultConfig = vaultConfigRef.get();
|
||||
this.csprng = csprng;
|
||||
}
|
||||
|
||||
public void fix(DiagnosticResult result) {
|
||||
Preconditions.checkArgument(result.getServerity() == DiagnosticResult.Severity.WARN, "Unfixable result");
|
||||
try (var masterkeyClone = masterkey.clone(); //
|
||||
var cryptor = vaultConfig.getCipherCombo().getCryptorProvider(csprng).withKey(masterkeyClone)) {
|
||||
result.fix(vaultPath, vaultConfig, masterkeyClone, cryptor);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to apply fix", e);
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR, e.getMessage());
|
||||
alert.showAndWait();
|
||||
//TODO: real error/not supported handling
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package org.cryptomator.ui.health;
|
||||
|
||||
import com.tobiasdiez.easybind.EasyBind;
|
||||
import org.cryptomator.cryptofs.health.api.DiagnosticResult;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.controls.FontAwesome5Icon;
|
||||
import org.cryptomator.ui.controls.FontAwesome5IconView;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
|
||||
// unscoped because each cell needs its own controller
|
||||
public class ResultListCellController implements FxController {
|
||||
|
||||
private final ResultFixApplier fixApplier;
|
||||
private final ObjectProperty<DiagnosticResult> result;
|
||||
private final Binding<String> description;
|
||||
|
||||
public FontAwesome5IconView iconView;
|
||||
public Button actionButton;
|
||||
|
||||
@Inject
|
||||
public ResultListCellController(ResultFixApplier fixApplier) {
|
||||
this.result = new SimpleObjectProperty<>(null);
|
||||
this.description = EasyBind.wrapNullable(result).map(DiagnosticResult::toString).orElse("");
|
||||
this.fixApplier = fixApplier;
|
||||
result.addListener(this::updateCellContent);
|
||||
}
|
||||
|
||||
private void updateCellContent(ObservableValue<? extends DiagnosticResult> observable, DiagnosticResult oldVal, DiagnosticResult newVal) {
|
||||
iconView.getStyleClass().clear();
|
||||
actionButton.setVisible(false);
|
||||
//TODO: see comment in case WARN
|
||||
actionButton.setManaged(false);
|
||||
switch (newVal.getServerity()) {
|
||||
case INFO -> {
|
||||
iconView.setGlyph(FontAwesome5Icon.INFO_CIRCLE);
|
||||
iconView.getStyleClass().add("glyph-icon-muted");
|
||||
}
|
||||
case GOOD -> {
|
||||
iconView.setGlyph(FontAwesome5Icon.CHECK);
|
||||
iconView.getStyleClass().add("glyph-icon-primary");
|
||||
}
|
||||
case WARN -> {
|
||||
iconView.setGlyph(FontAwesome5Icon.EXCLAMATION_TRIANGLE);
|
||||
iconView.getStyleClass().add("glyph-icon-orange");
|
||||
//TODO: Neither is any fix implemented, nor it is ensured, that only fix is executed at a time with good ui indication
|
||||
// before both are not fix, do not show the button
|
||||
//actionButton.setVisible(true);
|
||||
}
|
||||
case CRITICAL -> {
|
||||
iconView.setGlyph(FontAwesome5Icon.TIMES);
|
||||
iconView.getStyleClass().add("glyph-icon-red");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void runResultAction() {
|
||||
final var realResult = result.get();
|
||||
if (realResult != null) {
|
||||
fixApplier.fix(realResult);
|
||||
}
|
||||
}
|
||||
/* Getter & Setter */
|
||||
|
||||
|
||||
public DiagnosticResult getResult() {
|
||||
return result.get();
|
||||
}
|
||||
|
||||
public void setResult(DiagnosticResult result) {
|
||||
this.result.set(result);
|
||||
}
|
||||
|
||||
public ObjectProperty<DiagnosticResult> resultProperty() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description.getValue();
|
||||
}
|
||||
|
||||
public Binding<String> descriptionProperty() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package org.cryptomator.ui.health;
|
||||
|
||||
|
||||
import org.cryptomator.cryptofs.health.api.DiagnosticResult;
|
||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.control.ContentDisplay;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.util.Callback;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
|
||||
@HealthCheckScoped
|
||||
public class ResultListCellFactory implements Callback<ListView<DiagnosticResult>, ListCell<DiagnosticResult>> {
|
||||
|
||||
private final FxmlLoaderFactory fxmlLoaders;
|
||||
|
||||
@Inject
|
||||
ResultListCellFactory(@HealthCheckWindow FxmlLoaderFactory fxmlLoaders) {
|
||||
this.fxmlLoaders = fxmlLoaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListCell<DiagnosticResult> call(ListView<DiagnosticResult> param) {
|
||||
try {
|
||||
FXMLLoader fxmlLoader = fxmlLoaders.load("/fxml/health_result_listcell.fxml");
|
||||
return new ResultListCellFactory.Cell(fxmlLoader.getRoot(), fxmlLoader.getController());
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to load /fxml/health_result_listcell.fxml.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Cell extends ListCell<DiagnosticResult> {
|
||||
|
||||
private final Parent node;
|
||||
private final ResultListCellController controller;
|
||||
|
||||
public Cell(Parent node, ResultListCellController controller) {
|
||||
this.node = node;
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateItem(DiagnosticResult item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (item == null || empty) {
|
||||
setText(null);
|
||||
setGraphic(null);
|
||||
} else {
|
||||
controller.setResult(item);
|
||||
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
|
||||
setGraphic(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package org.cryptomator.ui.health;
|
||||
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.cryptofs.VaultConfig;
|
||||
import org.cryptomator.cryptofs.VaultConfigLoadException;
|
||||
import org.cryptomator.cryptofs.VaultKeyInvalidException;
|
||||
import org.cryptomator.cryptolib.api.Masterkey;
|
||||
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
|
||||
import org.cryptomator.ui.unlock.UnlockCancelledException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@HealthCheckScoped
|
||||
public class StartController implements FxController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(StartController.class);
|
||||
|
||||
private final Stage window;
|
||||
private final Optional<VaultConfig.UnverifiedVaultConfig> unverifiedVaultConfig;
|
||||
private final KeyLoadingStrategy keyLoadingStrategy;
|
||||
private final ExecutorService executor;
|
||||
private final AtomicReference<Masterkey> masterkeyRef;
|
||||
private final AtomicReference<VaultConfig> vaultConfigRef;
|
||||
private final Lazy<Scene> checkScene;
|
||||
private final Lazy<ErrorComponent.Builder> errorComponent;
|
||||
|
||||
/* FXML */
|
||||
|
||||
@Inject
|
||||
public StartController(@HealthCheckWindow Vault vault, @HealthCheckWindow Stage window, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference<Masterkey> masterkeyRef, AtomicReference<VaultConfig> vaultConfigRef, @FxmlScene(FxmlFile.HEALTH_CHECK_LIST) Lazy<Scene> checkScene, Lazy<ErrorComponent.Builder> errorComponent) {
|
||||
this.window = window;
|
||||
this.keyLoadingStrategy = keyLoadingStrategy;
|
||||
this.executor = executor;
|
||||
this.masterkeyRef = masterkeyRef;
|
||||
this.vaultConfigRef = vaultConfigRef;
|
||||
this.checkScene = checkScene;
|
||||
this.errorComponent = errorComponent;
|
||||
|
||||
//TODO: this is ugly
|
||||
//idea: delay the loading of the vault config and show a spinner (something like "check/load config") and react to the result of the loading
|
||||
//or: load vault config in a previous step to see if it is loadable.
|
||||
VaultConfig.UnverifiedVaultConfig tmp;
|
||||
try {
|
||||
tmp = vault.getUnverifiedVaultConfig();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
tmp = null;
|
||||
}
|
||||
this.unverifiedVaultConfig = Optional.ofNullable(tmp);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void close() {
|
||||
LOG.trace("StartController.close()");
|
||||
window.close();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void next() {
|
||||
LOG.trace("StartController.next()");
|
||||
executor.submit(this::loadKey);
|
||||
}
|
||||
|
||||
private void loadKey() {
|
||||
assert !Platform.isFxApplicationThread();
|
||||
assert unverifiedVaultConfig.isPresent();
|
||||
try (var masterkey = keyLoadingStrategy.loadKey(unverifiedVaultConfig.orElseThrow().getKeyId())) {
|
||||
var unverifiedCfg = unverifiedVaultConfig.get();
|
||||
var verifiedCfg = unverifiedCfg.verify(masterkey.getEncoded(), unverifiedCfg.allegedVaultVersion());
|
||||
vaultConfigRef.set(verifiedCfg);
|
||||
var old = masterkeyRef.getAndSet(masterkey.clone());
|
||||
if (old != null) {
|
||||
old.destroy();
|
||||
}
|
||||
Platform.runLater(this::loadedKey);
|
||||
} catch (MasterkeyLoadingFailedException e) {
|
||||
if (keyLoadingStrategy.recoverFromException(e)) {
|
||||
// retry
|
||||
loadKey();
|
||||
} else {
|
||||
Platform.runLater(() -> loadingKeyFailed(e));
|
||||
}
|
||||
} catch (VaultKeyInvalidException e) {
|
||||
Platform.runLater(() -> loadingKeyFailed(e));
|
||||
} catch (VaultConfigLoadException e) {
|
||||
Platform.runLater(() -> loadingKeyFailed(e));
|
||||
}
|
||||
}
|
||||
|
||||
private void loadedKey() {
|
||||
LOG.debug("Loaded valid key");
|
||||
window.setScene(checkScene.get());
|
||||
}
|
||||
|
||||
private void loadingKeyFailed(Exception e) {
|
||||
if (e instanceof UnlockCancelledException) {
|
||||
// ok
|
||||
} else if (e instanceof VaultKeyInvalidException) {
|
||||
LOG.error("Invalid key"); //TODO: specific error screen
|
||||
errorComponent.get().window(window).cause(e).build().showErrorScene();
|
||||
} else {
|
||||
LOG.error("Failed to load key.", e);
|
||||
errorComponent.get().window(window).cause(e).build().showErrorScene();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isInvalidConfig() {
|
||||
return unverifiedVaultConfig.isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingModule;
|
||||
|
||||
import javax.inject.Provider;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -28,20 +29,13 @@ abstract class KeyLoadingModule {
|
||||
@Provides
|
||||
@KeyLoading
|
||||
@KeyLoadingScoped
|
||||
static Optional<URI> provideKeyId(@KeyLoading Vault vault) {
|
||||
return vault.getUnverifiedVaultConfig().map(UnverifiedVaultConfig::getKeyId);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@KeyLoading
|
||||
@KeyLoadingScoped
|
||||
static KeyLoadingStrategy provideKeyLoaderProvider(@KeyLoading Optional<URI> keyId, Map<String, Provider<KeyLoadingStrategy>> strategies) {
|
||||
if (keyId.isEmpty()) {
|
||||
return KeyLoadingStrategy.failed(new IllegalArgumentException("No key id provided"));
|
||||
} else {
|
||||
String scheme = keyId.get().getScheme();
|
||||
static KeyLoadingStrategy provideKeyLoaderProvider(@KeyLoading Vault vault, Map<String, Provider<KeyLoadingStrategy>> strategies) {
|
||||
try {
|
||||
String scheme = vault.getUnverifiedVaultConfig().getKeyId().getScheme();
|
||||
var fallback = KeyLoadingStrategy.failed(new IllegalArgumentException("Unsupported key id " + scheme));
|
||||
return strategies.getOrDefault(scheme, () -> fallback).get();
|
||||
} catch (IOException e) {
|
||||
return KeyLoadingStrategy.failed(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
|
||||
}
|
||||
|
||||
private Path getAlternateMasterkeyFilePath() throws UnlockCancelledException, InterruptedException {
|
||||
if (filePath == null) {
|
||||
if (filePath.get() == null) {
|
||||
return switch (askUserForMasterkeyFilePath()) {
|
||||
case MASTERKEYFILE_PROVIDED -> filePath.get();
|
||||
case CANCELED -> throw new UnlockCancelledException("Choosing masterkey file cancelled.");
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.cryptomator.ui.common.FxControllerKey;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.StageFactory;
|
||||
import org.cryptomator.ui.health.HealthCheckComponent;
|
||||
import org.cryptomator.ui.migration.MigrationComponent;
|
||||
import org.cryptomator.ui.removevault.RemoveVaultComponent;
|
||||
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
|
||||
@@ -27,7 +28,7 @@ import javafx.stage.StageStyle;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
@Module(subcomponents = {AddVaultWizardComponent.class, MigrationComponent.class, RemoveVaultComponent.class, VaultOptionsComponent.class, VaultStatisticsComponent.class, WrongFileAlertComponent.class})
|
||||
@Module(subcomponents = {AddVaultWizardComponent.class, HealthCheckComponent.class, MigrationComponent.class, RemoveVaultComponent.class, VaultOptionsComponent.class, VaultStatisticsComponent.class, WrongFileAlertComponent.class})
|
||||
abstract class MainWindowModule {
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.cryptomator.common.keychain.KeychainManager;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplication;
|
||||
import org.cryptomator.ui.health.HealthCheckComponent;
|
||||
import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab;
|
||||
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
|
||||
|
||||
@@ -13,6 +14,7 @@ import javafx.beans.binding.BooleanExpression;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.Optional;
|
||||
@@ -28,7 +30,7 @@ public class VaultDetailLockedController implements FxController {
|
||||
private final BooleanExpression passwordSaved;
|
||||
|
||||
@Inject
|
||||
VaultDetailLockedController(ObjectProperty<Vault> vault, FxApplication application, VaultOptionsComponent.Builder vaultOptionsWindow, KeychainManager keychain, @MainWindow Stage mainWindow) {
|
||||
VaultDetailLockedController(ObjectProperty<Vault> vault, FxApplication application, VaultOptionsComponent.Builder vaultOptionsWindow, KeychainManager keychain, @MainWindow Stage mainWindow) {
|
||||
this.vault = vault;
|
||||
this.application = application;
|
||||
this.vaultOptionsWindow = vaultOptionsWindow;
|
||||
|
||||
@@ -6,10 +6,10 @@ import dagger.Provides;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.DefaultSceneFactory;
|
||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxControllerKey;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.NewPasswordController;
|
||||
import org.cryptomator.ui.common.PasswordStrengthUtil;
|
||||
@@ -17,8 +17,6 @@ import org.cryptomator.ui.common.StageFactory;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.scene.Scene;
|
||||
@@ -56,14 +54,6 @@ abstract class RecoveryKeyModule {
|
||||
return new SimpleStringProperty();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@RecoveryKeyScoped
|
||||
@Named("newPassword")
|
||||
static ObjectProperty<CharSequence> provideNewPasswordProperty() {
|
||||
return new SimpleObjectProperty<>("");
|
||||
}
|
||||
|
||||
|
||||
// ------------------
|
||||
|
||||
@Provides
|
||||
@@ -126,8 +116,8 @@ abstract class RecoveryKeyModule {
|
||||
@Provides
|
||||
@IntoMap
|
||||
@FxControllerKey(NewPasswordController.class)
|
||||
static FxController provideNewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater, @Named("newPassword") ObjectProperty<CharSequence> password) {
|
||||
return new NewPasswordController(resourceBundle, strengthRater, password);
|
||||
static FxController provideNewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater) {
|
||||
return new NewPasswordController(resourceBundle, strengthRater);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,14 +6,12 @@ import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.NewPasswordController;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.fxml.FXML;
|
||||
@@ -32,21 +30,19 @@ public class RecoveryKeyResetPasswordController implements FxController {
|
||||
private final RecoveryKeyFactory recoveryKeyFactory;
|
||||
private final ExecutorService executor;
|
||||
private final StringProperty recoveryKey;
|
||||
private final ObjectProperty<CharSequence> newPassword;
|
||||
private final Lazy<Scene> recoverScene;
|
||||
private final BooleanBinding invalidNewPassword;
|
||||
private final ErrorComponent.Builder errorComponent;
|
||||
|
||||
public NewPasswordController newPasswordController;
|
||||
|
||||
@Inject
|
||||
public RecoveryKeyResetPasswordController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, RecoveryKeyFactory recoveryKeyFactory, ExecutorService executor, @RecoveryKeyWindow StringProperty recoveryKey, @Named("newPassword") ObjectProperty<CharSequence> newPassword, @FxmlScene(FxmlFile.RECOVERYKEY_RECOVER) Lazy<Scene> recoverScene, ErrorComponent.Builder errorComponent) {
|
||||
public RecoveryKeyResetPasswordController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, RecoveryKeyFactory recoveryKeyFactory, ExecutorService executor, @RecoveryKeyWindow StringProperty recoveryKey, @FxmlScene(FxmlFile.RECOVERYKEY_RECOVER) Lazy<Scene> recoverScene, ErrorComponent.Builder errorComponent) {
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
this.recoveryKeyFactory = recoveryKeyFactory;
|
||||
this.executor = executor;
|
||||
this.recoveryKey = recoveryKey;
|
||||
this.newPassword = newPassword;
|
||||
this.recoverScene = recoverScene;
|
||||
this.invalidNewPassword = Bindings.createBooleanBinding(this::isInvalidNewPassword, newPassword);
|
||||
this.errorComponent = errorComponent;
|
||||
}
|
||||
|
||||
@@ -81,7 +77,7 @@ public class RecoveryKeyResetPasswordController implements FxController {
|
||||
|
||||
@Override
|
||||
protected Void call() throws IOException, IllegalArgumentException {
|
||||
recoveryKeyFactory.resetPasswordWithRecoveryKey(vault.getPath(), recoveryKey.get(), newPassword.get());
|
||||
recoveryKeyFactory.resetPasswordWithRecoveryKey(vault.getPath(), recoveryKey.get(), newPasswordController.passwordField.getCharacters());
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -89,11 +85,12 @@ public class RecoveryKeyResetPasswordController implements FxController {
|
||||
|
||||
/* Getter/Setter */
|
||||
|
||||
public BooleanBinding invalidNewPasswordProperty() {
|
||||
return invalidNewPassword;
|
||||
public ReadOnlyBooleanProperty validPasswordProperty() {
|
||||
return newPasswordController.passwordsMatchAndSufficientProperty();
|
||||
}
|
||||
|
||||
public boolean isInvalidNewPassword() {
|
||||
return newPassword.get() == null || newPassword.get().length() == 0;
|
||||
public boolean isValidPassword() {
|
||||
return newPasswordController.passwordsMatchAndSufficientProperty().get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.cryptomator.ui.vaultoptions;
|
||||
import org.cryptomator.common.settings.WhenUnlocked;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.health.HealthCheckComponent;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.beans.Observable;
|
||||
@@ -22,6 +23,7 @@ public class GeneralVaultOptionsController implements FxController {
|
||||
|
||||
private final Stage window;
|
||||
private final Vault vault;
|
||||
private final HealthCheckComponent.Builder healthCheckWindow;
|
||||
private final ResourceBundle resourceBundle;
|
||||
|
||||
public TextField vaultName;
|
||||
@@ -29,9 +31,10 @@ public class GeneralVaultOptionsController implements FxController {
|
||||
public ChoiceBox<WhenUnlocked> actionAfterUnlockChoiceBox;
|
||||
|
||||
@Inject
|
||||
GeneralVaultOptionsController(@VaultOptionsWindow Stage window, @VaultOptionsWindow Vault vault, ResourceBundle resourceBundle) {
|
||||
GeneralVaultOptionsController(@VaultOptionsWindow Stage window, @VaultOptionsWindow Vault vault, HealthCheckComponent.Builder healthCheckWindow, ResourceBundle resourceBundle) {
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
this.healthCheckWindow = healthCheckWindow;
|
||||
this.resourceBundle = resourceBundle;
|
||||
}
|
||||
|
||||
@@ -61,6 +64,12 @@ public class GeneralVaultOptionsController implements FxController {
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void showHealthCheck() {
|
||||
healthCheckWindow.vault(vault).build().showHealthCheckWindow();
|
||||
}
|
||||
|
||||
|
||||
private static class WhenUnlockedConverter extends StringConverter<WhenUnlocked> {
|
||||
|
||||
private final ResourceBundle resourceBundle;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<Insets topRightBottomLeft="24"/>
|
||||
</padding>
|
||||
<children>
|
||||
<fx:include source="/fxml/new_password.fxml"/>
|
||||
<fx:include fx:id="newPasswordScene" source="/fxml/new_password.fxml"/>
|
||||
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
<Region prefHeight="12" VBox.vgrow="NEVER"/>
|
||||
|
||||
<fx:include source="/fxml/new_password.fxml"/>
|
||||
<fx:include fx:id="newPassword" source="/fxml/new_password.fxml"/>
|
||||
|
||||
<CheckBox fx:id="finalConfirmationCheckbox" text="%changepassword.finalConfirmation" wrapText="true"/>
|
||||
|
||||
|
||||
24
main/ui/src/main/resources/fxml/health_check_details.fxml
Normal file
24
main/ui/src/main/resources/fxml/health_check_details.fxml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import org.cryptomator.ui.controls.FormattedLabel?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ListView?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.health.CheckDetailController"
|
||||
prefWidth="500"
|
||||
spacing="6">
|
||||
<FormattedLabel fx:id="checkTitle" styleClass="label-large" format="%health.check.detail.header" arg1="${controller.taskName}"/>
|
||||
|
||||
<Label text="%health.check.detail.taskNotStarted" visible="${controller.taskNotStarted}" managed="${controller.taskNotStarted}"/>
|
||||
<Label text="%health.check.detail.taskRunning" visible="${controller.taskRunning}" managed="${controller.taskRunning}"/>
|
||||
<Label text="%health.check.detail.taskScheduled" visible="${controller.taskScheduled}" managed="${controller.taskScheduled}"/>
|
||||
<Label text="%health.check.detail.taskCancelled" visible="${controller.taskCancelled}" managed="${controller.taskCancelled}"/>
|
||||
<Label text="%health.check.detail.taskFailed" visible="${controller.taskFailed}" managed="${controller.taskFailed}"/>
|
||||
<FormattedLabel styleClass="label" format="%health.check.detail.taskSucceeded" arg1="${controller.taskDuration}" visible="${controller.taskSucceeded}" managed="${controller.taskSucceeded}"/>
|
||||
|
||||
<FormattedLabel styleClass="label" format="%health.check.detail.problemCount" arg1="${controller.countOfWarnSeverity}" arg2="${controller.countOfCritSeverity}" visible="${!controller.taskNotStarted}"
|
||||
managed="${!controller.taskNotStarted}" />
|
||||
<ListView fx:id="resultsListView" VBox.vgrow="ALWAYS"/>
|
||||
</VBox>
|
||||
46
main/ui/src/main/resources/fxml/health_check_list.fxml
Normal file
46
main/ui/src/main/resources/fxml/health_check_list.fxml
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ButtonBar?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ListView?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import java.lang.Integer?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.control.CheckBox?>
|
||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.health.CheckListController"
|
||||
minHeight="145"
|
||||
spacing="12">
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<fx:define>
|
||||
<Integer fx:id="ZERO" fx:value="0"/>
|
||||
</fx:define>
|
||||
<children>
|
||||
<HBox spacing="12" VBox.vgrow="ALWAYS">
|
||||
<VBox minWidth="80" maxWidth="200" spacing="6" HBox.hgrow="ALWAYS" >
|
||||
<Label fx:id="listHeading" text="%health.checkList.header"/>
|
||||
<CheckBox onAction="#toggleSelectAll" text="%health.checkList.selectAllBox" visible="${!controller.showResultScreen}" managed="${!controller.showResultScreen}" />
|
||||
<ListView fx:id="checksListView" VBox.vgrow="ALWAYS"/>
|
||||
</VBox>
|
||||
<StackPane visible="${controller.showResultScreen}" managed="${controller.showResultScreen}" HBox.hgrow="ALWAYS" >
|
||||
<VBox minWidth="300" alignment="CENTER" visible="${!controller.anyCheckSelected}" managed="${!controller.anyCheckSelected}" >
|
||||
<Label text="%health.check.detail.noSelectedCheck" wrapText="true" alignment="CENTER" />
|
||||
</VBox>
|
||||
<fx:include source="/fxml/health_check_details.fxml" visible="${controller.anyCheckSelected}" managed="${controller.anyCheckSelected}" />
|
||||
</StackPane>
|
||||
</HBox>
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+CX">
|
||||
<buttons>
|
||||
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" onAction="#cancelCheck" disable="${!controller.running}" visible="${controller.showResultScreen}" managed="${controller.showResultScreen}" />
|
||||
<Button text="%health.check.exportBtn" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" disable="${!controller.finished}" visible="${controller.showResultScreen}" managed="${controller.showResultScreen}" onAction="#exportResults"/>
|
||||
<Button text="%health.check.runBatchBtn" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#runSelectedChecks" disable="${controller.numberOfPickedChecks == ZERO}" visible="${!controller.showResultScreen}" managed="${!controller.showResultScreen}"/>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</children>
|
||||
</VBox>
|
||||
29
main/ui/src/main/resources/fxml/health_result_listcell.fxml
Normal file
29
main/ui/src/main/resources/fxml/health_result_listcell.fxml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
<HBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.health.ResultListCellController"
|
||||
prefHeight="25"
|
||||
prefWidth="200"
|
||||
spacing="6"
|
||||
alignment="CENTER_LEFT">
|
||||
<!-- Remark: Check the containing list view for a fixed cell size before editing height properties -->
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="6"/>
|
||||
</padding>
|
||||
<children>
|
||||
<FontAwesome5IconView fx:id="iconView" HBox.hgrow="NEVER" glyphSize="16"/>
|
||||
<Label text="${controller.description}"/>
|
||||
<Region HBox.hgrow="ALWAYS"/>
|
||||
<!-- TODO: setting the minWidth of the button is just a workaround.
|
||||
What we actually want to do is to prevent shrinking the button more than the text
|
||||
-> own subclass of HBox is needed -->
|
||||
<Button fx:id="actionButton" text="%health.check.fixBtn" onAction="#runResultAction" alignment="CENTER" visible="false" minWidth="-Infinity"/>
|
||||
</children>
|
||||
</HBox>
|
||||
40
main/ui/src/main/resources/fxml/health_start.fxml
Normal file
40
main/ui/src/main/resources/fxml/health_start.fxml
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ButtonBar?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.health.StartController"
|
||||
minWidth="400"
|
||||
maxWidth="400"
|
||||
minHeight="145"
|
||||
spacing="12">
|
||||
<padding>
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<children>
|
||||
<Label text="%health.start.introduction" wrapText="true"/>
|
||||
|
||||
<!-- TODO: combine the two below labels to one and bind the properties accordingly or, preferably think about a new flow -->
|
||||
<Label text="%health.start.configInvalid" visible="${controller.invalidConfig}" managed="${controller.invalidConfig}" wrapText="true" contentDisplay="LEFT">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="TIMES" styleClass="glyph-icon-red" />
|
||||
</graphic>
|
||||
</Label>
|
||||
<Label text="%health.start.configValid" visible="${!controller.invalidConfig}" managed="${!controller.invalidConfig}" wrapText="true" contentDisplay="LEFT">
|
||||
<graphic>
|
||||
<FontAwesome5IconView glyph="CHECK" styleClass="glyph-icon-primary" />
|
||||
</graphic>
|
||||
</Label>
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+CX">
|
||||
<buttons>
|
||||
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
|
||||
<Button text="%generic.button.next" ButtonBar.buttonData="NEXT_FORWARD" disable="${controller.invalidConfig}" defaultButton="true" onAction="#next"/>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</children>
|
||||
</VBox>
|
||||
@@ -17,7 +17,7 @@
|
||||
<Insets topRightBottomLeft="12"/>
|
||||
</padding>
|
||||
<children>
|
||||
<fx:include source="/fxml/new_password.fxml"/>
|
||||
<fx:include fx:id="newPassword" source="/fxml/new_password.fxml"/>
|
||||
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="B+I">
|
||||
<buttons>
|
||||
<Button text="%generic.button.back" ButtonBar.buttonData="BACK_PREVIOUS" cancelButton="true" onAction="#back"/>
|
||||
<Button text="%generic.button.done" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#done" disable="${controller.invalidNewPassword}"/>
|
||||
<Button text="%generic.button.done" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#done" disable="${!controller.validPassword}"/>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</VBox>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
||||
xmlns="http://javafx.com/javafx"
|
||||
fx:controller="org.cryptomator.ui.vaultoptions.GeneralVaultOptionsController"
|
||||
@@ -26,5 +27,6 @@
|
||||
<Label text="%vaultOptions.general.actionAfterUnlock"/>
|
||||
<ChoiceBox fx:id="actionAfterUnlockChoiceBox"/>
|
||||
</HBox>
|
||||
<Button text="%vaultOptions.general.healthBtn" onAction="#showHealthCheck"/>
|
||||
</children>
|
||||
</VBox>
|
||||
|
||||
@@ -146,6 +146,29 @@ migration.impossible.heading=Unable to migrate vault
|
||||
migration.impossible.reason=The vault cannot be automatically migrated because its storage location or access point is not compatible.
|
||||
migration.impossible.moreInfo=The vault can still be opened with an older version. For instructions on how to manually migrate a vault, visit
|
||||
|
||||
# Health Check
|
||||
health.title=Vault Health Check
|
||||
health.start.introduction=The Vault Health Check is a collection of checks to detect and possilby fix problems in the internal structure of your vault. Please note, that not all problems are fixable. You need the vault password to perform the checks.
|
||||
health.start.configValid=Reading and parsing vault configuration file was successful. Proceed to select checks.
|
||||
health.start.configInvalid=Error while reading and parsing the vault configuration file.
|
||||
health.checkList.header=Available Health Checks
|
||||
health.checkList.selectAllBox=Select All
|
||||
health.check.runBatchBtn=Run selected Checks
|
||||
## Detail view
|
||||
health.check.detail.noSelectedCheck=For results select a finished health check in the left list.
|
||||
health.check.detail.header=Results of %s
|
||||
health.check.detail.taskNotStarted=The check was not selected to run.
|
||||
health.check.detail.taskScheduled=The check is scheduled.
|
||||
health.check.detail.taskRunning=The check is currently running…
|
||||
health.check.detail.taskSucceeded=The check finished successfully after %d milliseconds.
|
||||
health.check.detail.taskFailed=The check exited due to an error.
|
||||
health.check.detail.taskCancelled=The check was cancelled.
|
||||
health.check.detail.problemCount=Found %d problems and %d unfixable errors.
|
||||
health.check.exportBtn=Export Report
|
||||
health.check.fixBtn=Fix
|
||||
## Checks
|
||||
health.org.cryptomator.cryptofs.health.dirid.DirIdCheck=Directory Check
|
||||
|
||||
# Preferences
|
||||
preferences.title=Preferences
|
||||
## General
|
||||
@@ -287,6 +310,7 @@ vaultOptions.general.actionAfterUnlock=After successful unlock
|
||||
vaultOptions.general.actionAfterUnlock.ignore=Do nothing
|
||||
vaultOptions.general.actionAfterUnlock.reveal=Reveal Drive
|
||||
vaultOptions.general.actionAfterUnlock.ask=Ask
|
||||
vaultOptions.general.healthBtn=Start Health Check
|
||||
## Mount
|
||||
vaultOptions.mount=Mounting
|
||||
vaultOptions.mount.readonly=Read-Only
|
||||
|
||||
@@ -19,10 +19,10 @@ Cryptomator uses 45 third-party dependencies under the following licenses:
|
||||
- jnr-ffi (com.github.jnr:jnr-ffi:2.1.12 - http://github.com/jnr/jnr-ffi)
|
||||
- FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.2 - http://findbugs.sourceforge.net/)
|
||||
- Gson (com.google.code.gson:gson:2.8.6 - https://github.com/google/gson/gson)
|
||||
- Dagger (com.google.dagger:dagger:2.32 - https://github.com/google/dagger)
|
||||
- error-prone annotations (com.google.errorprone:error_prone_annotations:2.3.4 - http://nexus.sonatype.org/oss-repository-hosting.html/error_prone_parent/error_prone_annotations)
|
||||
- Dagger (com.google.dagger:dagger:2.35.1 - https://github.com/google/dagger)
|
||||
- error-prone annotations (com.google.errorprone:error_prone_annotations:2.5.1 - http://nexus.sonatype.org/oss-repository-hosting.html/error_prone_parent/error_prone_annotations)
|
||||
- Guava InternalFutureFailureAccess and InternalFutures (com.google.guava:failureaccess:1.0.1 - https://github.com/google/guava/failureaccess)
|
||||
- Guava: Google Core Libraries for Java (com.google.guava:guava:30.1-jre - https://github.com/google/guava/guava)
|
||||
- Guava: Google Core Libraries for Java (com.google.guava:guava:30.1.1-jre - https://github.com/google/guava/guava)
|
||||
- Guava ListenableFuture only (com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava - https://github.com/google/guava/listenablefuture)
|
||||
- J2ObjC Annotations (com.google.j2objc:j2objc-annotations:1.3 - https://github.com/google/j2objc/)
|
||||
- Apache Commons CLI (commons-cli:commons-cli:1.4 - http://commons.apache.org/proper/commons-cli/)
|
||||
@@ -32,35 +32,37 @@ Cryptomator uses 45 third-party dependencies under the following licenses:
|
||||
- Apache Commons Lang (org.apache.commons:commons-lang3:3.11 - https://commons.apache.org/proper/commons-lang/)
|
||||
- Apache HttpCore (org.apache.httpcomponents:httpcore:4.4.14 - http://hc.apache.org/httpcomponents-core-ga)
|
||||
- Jackrabbit WebDAV Library (org.apache.jackrabbit:jackrabbit-webdav:2.21.5 - http://jackrabbit.apache.org/jackrabbit-webdav/)
|
||||
- Jetty :: Http Utility (org.eclipse.jetty:jetty-http:10.0.1 - https://eclipse.org/jetty/jetty-http)
|
||||
- Jetty :: IO Utility (org.eclipse.jetty:jetty-io:10.0.1 - https://eclipse.org/jetty/jetty-io)
|
||||
- Jetty :: Security (org.eclipse.jetty:jetty-security:10.0.1 - https://eclipse.org/jetty/jetty-security)
|
||||
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:10.0.1 - https://eclipse.org/jetty/jetty-server)
|
||||
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:10.0.1 - https://eclipse.org/jetty/jetty-servlet)
|
||||
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:10.0.1 - https://eclipse.org/jetty/jetty-util)
|
||||
- Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:10.0.1 - https://eclipse.org/jetty/jetty-webapp)
|
||||
- Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:10.0.1 - https://eclipse.org/jetty/jetty-xml)
|
||||
- Jetty :: Http Utility (org.eclipse.jetty:jetty-http:10.0.2 - https://eclipse.org/jetty/jetty-http)
|
||||
- Jetty :: IO Utility (org.eclipse.jetty:jetty-io:10.0.2 - https://eclipse.org/jetty/jetty-io)
|
||||
- Jetty :: Security (org.eclipse.jetty:jetty-security:10.0.2 - https://eclipse.org/jetty/jetty-security)
|
||||
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:10.0.2 - https://eclipse.org/jetty/jetty-server)
|
||||
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:10.0.2 - https://eclipse.org/jetty/jetty-servlet)
|
||||
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:10.0.2 - https://eclipse.org/jetty/jetty-util)
|
||||
- Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:10.0.2 - https://eclipse.org/jetty/jetty-webapp)
|
||||
- Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:10.0.2 - https://eclipse.org/jetty/jetty-xml)
|
||||
- Jetty :: Servlet API and Schemas for JPMS and OSGi (org.eclipse.jetty.toolchain:jetty-servlet-api:4.0.6 - https://eclipse.org/jetty/jetty-servlet-api)
|
||||
BSD:
|
||||
- asm (org.ow2.asm:asm:7.1 - http://asm.ow2.org/)
|
||||
- asm-analysis (org.ow2.asm:asm-analysis:7.1 - http://asm.ow2.org/)
|
||||
- asm-commons (org.ow2.asm:asm-commons:7.1 - http://asm.ow2.org/)
|
||||
- asm-tree (org.ow2.asm:asm-tree:7.1 - http://asm.ow2.org/)
|
||||
- asm-util (org.ow2.asm:asm-util:7.1 - http://asm.ow2.org/)
|
||||
Eclipse Public License - Version 1.0:
|
||||
- Jetty :: Servlet API and Schemas for JPMS and OSGi (org.eclipse.jetty.toolchain:jetty-servlet-api:4.0.6 - https://eclipse.org/jetty/jetty-servlet-api)
|
||||
Eclipse Public License - Version 2.0:
|
||||
- Jetty :: Http Utility (org.eclipse.jetty:jetty-http:10.0.1 - https://eclipse.org/jetty/jetty-http)
|
||||
- Jetty :: IO Utility (org.eclipse.jetty:jetty-io:10.0.1 - https://eclipse.org/jetty/jetty-io)
|
||||
- Jetty :: Security (org.eclipse.jetty:jetty-security:10.0.1 - https://eclipse.org/jetty/jetty-security)
|
||||
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:10.0.1 - https://eclipse.org/jetty/jetty-server)
|
||||
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:10.0.1 - https://eclipse.org/jetty/jetty-servlet)
|
||||
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:10.0.1 - https://eclipse.org/jetty/jetty-util)
|
||||
- Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:10.0.1 - https://eclipse.org/jetty/jetty-webapp)
|
||||
- Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:10.0.1 - https://eclipse.org/jetty/jetty-xml)
|
||||
- Jetty :: Http Utility (org.eclipse.jetty:jetty-http:10.0.2 - https://eclipse.org/jetty/jetty-http)
|
||||
- Jetty :: IO Utility (org.eclipse.jetty:jetty-io:10.0.2 - https://eclipse.org/jetty/jetty-io)
|
||||
- Jetty :: Security (org.eclipse.jetty:jetty-security:10.0.2 - https://eclipse.org/jetty/jetty-security)
|
||||
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:10.0.2 - https://eclipse.org/jetty/jetty-server)
|
||||
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:10.0.2 - https://eclipse.org/jetty/jetty-servlet)
|
||||
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:10.0.2 - https://eclipse.org/jetty/jetty-util)
|
||||
- Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:10.0.2 - https://eclipse.org/jetty/jetty-webapp)
|
||||
- Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:10.0.2 - https://eclipse.org/jetty/jetty-xml)
|
||||
Eclipse Public License - v 2.0:
|
||||
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix)
|
||||
GPLv2:
|
||||
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix)
|
||||
GPLv2+CE:
|
||||
- Java Servlet API (javax.servlet:javax.servlet-api:4.0.1 - https://javaee.github.io/servlet-spec/)
|
||||
- javafx-base (org.openjfx:javafx-base:16 - https://openjdk.java.net/projects/openjfx/javafx-base/)
|
||||
- javafx-controls (org.openjfx:javafx-controls:16 - https://openjdk.java.net/projects/openjfx/javafx-controls/)
|
||||
- javafx-fxml (org.openjfx:javafx-fxml:16 - https://openjdk.java.net/projects/openjfx/javafx-fxml/)
|
||||
@@ -70,11 +72,11 @@ Cryptomator uses 45 third-party dependencies under the following licenses:
|
||||
- Java Native Access (net.java.dev.jna:jna:5.7.0 - https://github.com/java-native-access/jna)
|
||||
- Java Native Access Platform (net.java.dev.jna:jna-platform:5.7.0 - https://github.com/java-native-access/jna)
|
||||
MIT License:
|
||||
- java jwt (com.auth0:java-jwt:3.13.0 - https://github.com/auth0/java-jwt)
|
||||
- java jwt (com.auth0:java-jwt:3.15.0 - https://github.com/auth0/java-jwt)
|
||||
- jnr-x86asm (com.github.jnr:jnr-x86asm:1.0.2 - http://github.com/jnr/jnr-x86asm)
|
||||
- jnr-fuse (com.github.serceman:jnr-fuse:0.5.5 - https://github.com/SerCeMan/jnr-fuse)
|
||||
- zxcvbn4j (com.nulab-inc:zxcvbn:1.3.0 - https://github.com/nulab/zxcvbn4j)
|
||||
- Checker Qual (org.checkerframework:checker-qual:3.5.0 - https://checkerframework.org)
|
||||
- Checker Qual (org.checkerframework:checker-qual:3.8.0 - https://checkerframework.org)
|
||||
- SLF4J API Module (org.slf4j:slf4j-api:1.7.30 - http://www.slf4j.org)
|
||||
The BSD 2-Clause License:
|
||||
- EasyBind (com.tobiasdiez:easybind:2.1.0 - https://github.com/tobiasdiez/EasyBind)
|
||||
|
||||
Reference in New Issue
Block a user