mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-22 04:31:27 +00:00
Merge branch 'develop' into feature/event-view
This commit is contained in:
@@ -58,6 +58,7 @@ public class VaultSettings {
|
||||
public final StringExpression mountName;
|
||||
public final StringProperty mountService;
|
||||
public final IntegerProperty port;
|
||||
public final StringProperty lastKnownKeyLoader;
|
||||
|
||||
VaultSettings(VaultSettingsJson json) {
|
||||
this.id = json.id;
|
||||
@@ -74,6 +75,7 @@ public class VaultSettings {
|
||||
this.mountPoint = new SimpleObjectProperty<>(this, "mountPoint", json.mountPoint == null ? null : Path.of(json.mountPoint));
|
||||
this.mountService = new SimpleStringProperty(this, "mountService", json.mountService);
|
||||
this.port = new SimpleIntegerProperty(this, "port", json.port);
|
||||
this.lastKnownKeyLoader = new SimpleStringProperty(this, "lastKnownKeyLoader", json.lastKnownKeyLoader);
|
||||
// mount name is no longer an explicit setting, see https://github.com/cryptomator/cryptomator/pull/1318
|
||||
this.mountName = StringExpression.stringExpression(Bindings.createStringBinding(() -> {
|
||||
final String name;
|
||||
@@ -99,7 +101,7 @@ public class VaultSettings {
|
||||
}
|
||||
|
||||
Observable[] observables() {
|
||||
return new Observable[]{actionAfterUnlock, autoLockIdleSeconds, autoLockWhenIdle, displayName, maxCleartextFilenameLength, mountFlags, mountPoint, path, revealAfterMount, unlockAfterStartup, usesReadOnlyMode, port, mountService};
|
||||
return new Observable[]{actionAfterUnlock, autoLockIdleSeconds, autoLockWhenIdle, displayName, maxCleartextFilenameLength, mountFlags, mountPoint, path, revealAfterMount, unlockAfterStartup, usesReadOnlyMode, port, mountService, lastKnownKeyLoader};
|
||||
}
|
||||
|
||||
public static VaultSettings withRandomId() {
|
||||
@@ -130,6 +132,7 @@ public class VaultSettings {
|
||||
json.mountPoint = mountPoint.map(Path::toString).getValue();
|
||||
json.mountService = mountService.get();
|
||||
json.port = port.get();
|
||||
json.lastKnownKeyLoader = lastKnownKeyLoader.get();
|
||||
return json;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,9 @@ class VaultSettingsJson {
|
||||
@JsonProperty("mountService")
|
||||
String mountService;
|
||||
|
||||
@JsonProperty("lastKnownKeyLoader")
|
||||
String lastKnownKeyLoader;
|
||||
|
||||
@JsonProperty("port")
|
||||
int port = VaultSettings.DEFAULT_PORT;
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
@@ -49,9 +50,9 @@ public class VaultListManager {
|
||||
@Inject
|
||||
public VaultListManager(ObservableList<Vault> vaultList, //
|
||||
AutoLocker autoLocker, //
|
||||
List<MountService> mountServices,
|
||||
VaultComponent.Factory vaultComponentFactory,
|
||||
ResourceBundle resourceBundle,
|
||||
List<MountService> mountServices, //
|
||||
VaultComponent.Factory vaultComponentFactory, //
|
||||
ResourceBundle resourceBundle, //
|
||||
Settings settings) {
|
||||
this.vaultList = vaultList;
|
||||
this.autoLocker = autoLocker;
|
||||
@@ -114,6 +115,10 @@ public class VaultListManager {
|
||||
private Vault create(VaultSettings vaultSettings) {
|
||||
var wrapper = new VaultConfigCache(vaultSettings);
|
||||
try {
|
||||
if (Objects.isNull(vaultSettings.lastKnownKeyLoader.get())) {
|
||||
var keyIdScheme = wrapper.get().getKeyId().getScheme();
|
||||
vaultSettings.lastKnownKeyLoader.set(keyIdScheme);
|
||||
}
|
||||
var vaultState = determineVaultState(vaultSettings.path.get());
|
||||
if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state
|
||||
wrapper.reloadConfig();
|
||||
|
||||
@@ -76,8 +76,10 @@ public class ReadmeGenerator {
|
||||
input.chars().forEachOrdered(c -> {
|
||||
if (c < 128) {
|
||||
sb.append((char) c);
|
||||
} else if (c <= 0xFF) {
|
||||
sb.append("\\'").append(String.format("%02X", c));
|
||||
} else if (c < 0xFFFF) {
|
||||
sb.append("\\u").append(c);
|
||||
sb.append("\\uc1\\u").append(c);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
|
||||
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
import org.slf4j.Logger;
|
||||
@@ -108,6 +109,7 @@ public class HubToPasswordConvertController implements FxController {
|
||||
.thenRunAsync(this::convertInternal, backgroundExecutorService) //
|
||||
.whenCompleteAsync((result, exception) -> {
|
||||
if (exception == null) {
|
||||
vault.getVaultSettings().lastKnownKeyLoader.set(MasterkeyFileLoadingStrategy.SCHEME);
|
||||
LOG.info("Conversion of vault {} succeeded.", vault.getPath());
|
||||
window.setScene(successScene.get());
|
||||
} else {
|
||||
|
||||
@@ -38,7 +38,7 @@ public class Dialogs {
|
||||
.setMessageKey("removeVault.message") //
|
||||
.setDescriptionKey("removeVault.description") //
|
||||
.setIcon(FontAwesome5Icon.QUESTION) //
|
||||
.setOkButtonKey("removeVault.confirmBtn") //
|
||||
.setOkButtonKey("generic.button.remove") //
|
||||
.setCancelButtonKey("generic.button.cancel") //
|
||||
.setOkAction(stage -> {
|
||||
LOG.debug("Removing vault {}.", vault.getDisplayName());
|
||||
@@ -54,7 +54,7 @@ public class Dialogs {
|
||||
.setMessageKey("removeCert.message") //
|
||||
.setDescriptionKey("removeCert.description") //
|
||||
.setIcon(FontAwesome5Icon.QUESTION) //
|
||||
.setOkButtonKey("removeCert.confirmBtn") //
|
||||
.setOkButtonKey("generic.button.remove") //
|
||||
.setCancelButtonKey("generic.button.cancel") //
|
||||
.setOkAction(stage -> {
|
||||
settings.licenseKey.set(null);
|
||||
|
||||
@@ -31,8 +31,9 @@ public class SimpleDialog {
|
||||
FxmlLoaderFactory loaderFactory = FxmlLoaderFactory.forController( //
|
||||
new SimpleDialogController(resolveText(builder.messageKey, null), //
|
||||
resolveText(builder.descriptionKey, null), //
|
||||
builder.icon, resolveText(builder.okButtonKey, null), //
|
||||
resolveText(builder.cancelButtonKey, null), //
|
||||
builder.icon, //
|
||||
resolveText(builder.okButtonKey, null), //
|
||||
builder.cancelButtonKey != null ? resolveText(builder.cancelButtonKey, null) : null, //
|
||||
() -> builder.okAction.accept(dialogStage), //
|
||||
() -> builder.cancelAction.accept(dialogStage)), //
|
||||
Scene::new, builder.resourceBundle);
|
||||
@@ -67,7 +68,6 @@ public class SimpleDialog {
|
||||
private String descriptionKey;
|
||||
private String okButtonKey;
|
||||
private String cancelButtonKey;
|
||||
|
||||
private FontAwesome5Icon icon;
|
||||
private Consumer<Stage> okAction = Stage::close;
|
||||
private Consumer<Stage> cancelAction = Stage::close;
|
||||
@@ -128,7 +128,6 @@ public class SimpleDialog {
|
||||
Objects.requireNonNull(messageKey, "SimpleDialog messageKey must be set.");
|
||||
Objects.requireNonNull(descriptionKey, "SimpleDialog descriptionKey must be set.");
|
||||
Objects.requireNonNull(okButtonKey, "SimpleDialog okButtonKey must be set.");
|
||||
Objects.requireNonNull(cancelButtonKey, "SimpleDialog cancelButtonKey must be set.");
|
||||
|
||||
try {
|
||||
return new SimpleDialog(this);
|
||||
|
||||
@@ -14,6 +14,7 @@ public class SimpleDialogController implements FxController {
|
||||
private final String cancelButtonText;
|
||||
private final Runnable okAction;
|
||||
private final Runnable cancelAction;
|
||||
private final boolean cancelButtonVisible;
|
||||
|
||||
public SimpleDialogController(String message, String description, FontAwesome5Icon icon, String okButtonText, String cancelButtonText, Runnable okAction, Runnable cancelAction) {
|
||||
this.message = message;
|
||||
@@ -23,6 +24,11 @@ public class SimpleDialogController implements FxController {
|
||||
this.cancelButtonText = cancelButtonText;
|
||||
this.okAction = okAction;
|
||||
this.cancelAction = cancelAction;
|
||||
this.cancelButtonVisible = cancelButtonText != null && !cancelButtonText.isEmpty();
|
||||
}
|
||||
|
||||
public boolean isCancelButtonVisible() {
|
||||
return cancelButtonVisible;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
|
||||
@@ -3,6 +3,8 @@ package org.cryptomator.ui.keyloading;
|
||||
import org.cryptomator.cryptolib.api.Masterkey;
|
||||
import org.cryptomator.cryptolib.api.MasterkeyLoader;
|
||||
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
|
||||
import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy;
|
||||
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -28,6 +30,33 @@ public interface KeyLoadingStrategy extends MasterkeyLoader {
|
||||
@Override
|
||||
Masterkey loadKey(URI keyId) throws MasterkeyLoadingFailedException;
|
||||
|
||||
/**
|
||||
* Determines whether the provided key loader scheme corresponds to a Hub Vault.
|
||||
* <p>
|
||||
* This method compares the {@code keyLoader} parameter with the known Hub Vault schemes
|
||||
* {@link HubKeyLoadingStrategy#SCHEME_HUB_HTTP} and {@link HubKeyLoadingStrategy#SCHEME_HUB_HTTPS}.
|
||||
*
|
||||
* @param keyLoader A string representing the key loader scheme to be checked.
|
||||
* @return {@code true} if the given key loader scheme represents a Hub Vault; {@code false} otherwise.
|
||||
*/
|
||||
static boolean isHubVault(String keyLoader) {
|
||||
return HubKeyLoadingStrategy.SCHEME_HUB_HTTP.equals(keyLoader) || HubKeyLoadingStrategy.SCHEME_HUB_HTTPS.equals(keyLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the provided key loader scheme corresponds to a Masterkey File Vault.
|
||||
* <p>
|
||||
* This method checks if the {@code keyLoader} parameter matches the known Masterkey File Vault scheme
|
||||
* {@link MasterkeyFileLoadingStrategy#SCHEME}.
|
||||
* </p>
|
||||
*
|
||||
* @param keyLoader A string representing the key loader scheme to be checked.
|
||||
* @return {@code true} if the given key loader scheme represents a Masterkey File Vault; {@code false} otherwise.
|
||||
*/
|
||||
static boolean isMasterkeyFileVault(String keyLoader) {
|
||||
return MasterkeyFileLoadingStrategy.SCHEME.equals(keyLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the loader to try and recover from an exception thrown during the last attempt.
|
||||
*
|
||||
|
||||
@@ -165,6 +165,7 @@ public class ReceiveKeyController implements FxController {
|
||||
var vaultKeyUri = hubConfig.URIs.API.resolve("vaults/" + vaultId + "/access-token");
|
||||
var request = HttpRequest.newBuilder(vaultKeyUri) //
|
||||
.header("Authorization", "Bearer " + bearerToken) //
|
||||
.header("Hub-Device-ID", deviceId) //
|
||||
.GET() //
|
||||
.timeout(REQ_TIMEOUT) //
|
||||
.build();
|
||||
|
||||
@@ -19,9 +19,11 @@ import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Rectangle2D;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.stage.Screen;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.WindowEvent;
|
||||
|
||||
@MainWindowScoped
|
||||
public class MainWindowController implements FxController {
|
||||
@@ -68,18 +70,15 @@ public class MainWindowController implements FxController {
|
||||
int y = settings.windowYPosition.get();
|
||||
int width = settings.windowWidth.get();
|
||||
int height = settings.windowHeight.get();
|
||||
if (windowPositionSaved(x, y, width, height) ) {
|
||||
if(isWithinDisplayBounds(x, y, width, height)) { //use stored window position
|
||||
window.setX(x);
|
||||
window.setY(y);
|
||||
window.setWidth(Math.clamp(width, window.getMinWidth(), window.getMaxWidth()));
|
||||
window.setHeight(Math.clamp(height, window.getMinHeight(), window.getMaxHeight()));
|
||||
} else if(isWithinDisplayBounds((int) window.getX(), (int) window.getY(), width, height)) { //just reset position of upper left corner, keep window size
|
||||
window.setWidth(Math.clamp(width, window.getMinWidth(), window.getMaxWidth()));
|
||||
window.setHeight(Math.clamp(height, window.getMinHeight(), window.getMaxHeight()));
|
||||
} //else reset window completely
|
||||
if (windowPositionSaved(x, y, width, height)) {
|
||||
window.setX(x);
|
||||
window.setY(y);
|
||||
window.setWidth(Math.clamp(width, window.getMinWidth(), window.getMaxWidth()));
|
||||
window.setHeight(Math.clamp(height, window.getMinHeight(), window.getMaxHeight()));
|
||||
}
|
||||
|
||||
window.setOnShowing(this::checkDisplayBounds);
|
||||
|
||||
settings.windowXPosition.bind(window.xProperty());
|
||||
settings.windowYPosition.bind(window.yProperty());
|
||||
settings.windowWidth.bind(window.widthProperty());
|
||||
@@ -90,6 +89,39 @@ public class MainWindowController implements FxController {
|
||||
return x != 0 || y != 0 || width != 0 || height != 0;
|
||||
}
|
||||
|
||||
private void checkDisplayBounds(WindowEvent windowEvent) {
|
||||
int x = settings.windowXPosition.get();
|
||||
int y = settings.windowYPosition.get();
|
||||
int width = settings.windowWidth.get();
|
||||
int height = settings.windowHeight.get();
|
||||
|
||||
// Minimizing a window in Windows and closing it could result in an out of bounds position at (x, y) = (-32000, -32000)
|
||||
// See https://devblogs.microsoft.com/oldnewthing/20041028-00/?p=37453
|
||||
// If the position is (-32000, -32000), restore to the last saved position
|
||||
if (window.getX() == -32000 && window.getY() == -32000) {
|
||||
window.setX(x);
|
||||
window.setY(y);
|
||||
window.setWidth(width);
|
||||
window.setHeight(height);
|
||||
}
|
||||
|
||||
Rectangle2D primaryScreenBounds = Screen.getPrimary().getBounds();
|
||||
if (!isWithinDisplayBounds(x, y, width, height)) { //use stored window position
|
||||
LOG.debug("Resetting window position due to insufficient screen overlap");
|
||||
var centeredX = (primaryScreenBounds.getWidth() - window.getMinWidth()) / 2;
|
||||
var centeredY = (primaryScreenBounds.getHeight() - window.getMinHeight()) / 2;
|
||||
//check if we can keep width and height
|
||||
if (isWithinDisplayBounds((int) centeredX, (int) centeredY, width, height)) {
|
||||
//if so, keep window size
|
||||
window.setWidth(Math.clamp(width, window.getMinWidth(), window.getMaxWidth()));
|
||||
window.setHeight(Math.clamp(height, window.getMinHeight(), window.getMaxHeight()));
|
||||
}
|
||||
//reset position of upper left corner
|
||||
window.setX(centeredX);
|
||||
window.setY(centeredY);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isWithinDisplayBounds(int x, int y, int width, int height) {
|
||||
// define a rect which is inset on all sides from the window's rect:
|
||||
final int shrinkedX = x + 20; // 20px left
|
||||
|
||||
@@ -3,7 +3,7 @@ package org.cryptomator.ui.sharevault;
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy;
|
||||
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Application;
|
||||
@@ -33,8 +33,7 @@ public class ShareVaultController implements FxController {
|
||||
this.window = window;
|
||||
this.application = application;
|
||||
this.vault = vault;
|
||||
var vaultScheme = vault.getVaultConfigCache().getUnchecked().getKeyId().getScheme();
|
||||
this.hubVault = (vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTP) || vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTPS));
|
||||
this.hubVault = KeyLoadingStrategy.isHubVault(vault.getVaultSettings().lastKnownKeyLoader.get());
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.cryptomator.ui.vaultoptions;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
|
||||
import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy;
|
||||
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
|
||||
import org.slf4j.Logger;
|
||||
@@ -42,11 +43,11 @@ public class VaultOptionsController implements FxController {
|
||||
window.setOnShowing(this::windowWillAppear);
|
||||
selectedTabProperty.addListener(observable -> this.selectChosenTab());
|
||||
tabPane.getSelectionModel().selectedItemProperty().addListener(observable -> this.selectedTabChanged());
|
||||
var vaultScheme = vault.getVaultConfigCache().getUnchecked().getKeyId().getScheme();
|
||||
if(!vaultScheme.equals(MasterkeyFileLoadingStrategy.SCHEME)){
|
||||
var vaultKeyLoader = vault.getVaultSettings().lastKnownKeyLoader.get();
|
||||
if(!KeyLoadingStrategy.isMasterkeyFileVault(vaultKeyLoader)){
|
||||
tabPane.getTabs().remove(keyTab);
|
||||
}
|
||||
if(!(vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTP) || vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTPS))){
|
||||
if(!KeyLoadingStrategy.isHubVault(vaultKeyLoader)){
|
||||
tabPane.getTabs().remove(hubTab);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user