diff --git a/src/main/java/org/cryptomator/common/Constants.java b/src/main/java/org/cryptomator/common/Constants.java index 105fba3d6..b34c4ad3b 100644 --- a/src/main/java/org/cryptomator/common/Constants.java +++ b/src/main/java/org/cryptomator/common/Constants.java @@ -13,5 +13,6 @@ public interface Constants { String CRYPTOMATOR_FILENAME_GLOB = "*.cryptomator"; URI DEFAULT_KEY_ID = URI.create(MasterkeyFileLoadingStrategy.SCHEME + ":" + MASTERKEY_FILENAME); byte[] PEPPER = new byte[0]; + String HUB_USER_DEVICE_SEPARATOR = "&"; } diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingModule.java b/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingModule.java index 97ab2b394..10a032a35 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingModule.java +++ b/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingModule.java @@ -67,9 +67,9 @@ public abstract class HubKeyLoadingModule { } @Provides - @Named("userName") + @Named("filesystemOwnerId") @KeyLoadingScoped - static AtomicReference provideUserNameRef() { + static AtomicReference provideFilesystemOwnerIdRef() { return new AtomicReference<>(); } diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingStrategy.java b/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingStrategy.java index c54b4ce5e..4b48169c0 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingStrategy.java +++ b/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingStrategy.java @@ -35,17 +35,17 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy, FilesystemOwne private final Stage window; private final KeychainManager keychainManager; - private final AtomicReference userName; + private final AtomicReference fsOwnerId; private final Lazy authFlowScene; private final Lazy noKeychainScene; private final CompletableFuture result; private final DeviceKey deviceKey; @Inject - public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy authFlowScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy noKeychainScene, CompletableFuture result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle, @Named("userName") AtomicReference userName) { + public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy authFlowScene, @FxmlScene(FxmlFile.HUB_NO_KEYCHAIN) Lazy noKeychainScene, CompletableFuture result, DeviceKey deviceKey, KeychainManager keychainManager, @Named("windowTitle") String windowTitle, @Named("filesystemOwnerId") AtomicReference fsOwnerId) { this.window = window; this.keychainManager = keychainManager; - this.userName = userName; + this.fsOwnerId = fsOwnerId; window.setTitle(windowTitle); window.setOnCloseRequest(_ -> result.cancel(true)); this.authFlowScene = authFlowScene; @@ -96,7 +96,7 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy, FilesystemOwne @Override public String getOwner() { - var name = userName.get(); + var name = fsOwnerId.get(); if (name == null) { throw new IllegalStateException("Owner is not yet determined"); } diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java b/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java index dd6ae3b68..353ac14ec 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java +++ b/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Preconditions; import com.nimbusds.jose.JWEObject; import dagger.Lazy; +import org.cryptomator.common.Constants; import org.cryptomator.common.vaults.Vault; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxmlFile; @@ -49,7 +50,7 @@ public class ReceiveKeyController implements FxController { private final String vaultId; private final String deviceId; private final String bearerToken; - private final AtomicReference userName; + private final AtomicReference fsOwnerId; private final CompletableFuture result; private final Lazy registerDeviceScene; private final Lazy legacyRegisterDeviceScene; @@ -65,7 +66,7 @@ public class ReceiveKeyController implements FxController { HubConfig hubConfig, // @Named("deviceId") String deviceId, // @Named("bearerToken") AtomicReference tokenRef, // - @Named("userName") AtomicReference userName, // + @Named("filesystemOwnerId") AtomicReference fsOwnerId, // CompletableFuture result, // @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE) Lazy registerDeviceScene, // @FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE) Lazy legacyRegisterDeviceScene, // @@ -77,7 +78,7 @@ public class ReceiveKeyController implements FxController { this.vaultId = extractVaultId(vault.getVaultConfigCache().getUnchecked().getKeyId()); // TODO: access vault config's JTI directly (requires changes in cryptofs) this.deviceId = deviceId; this.bearerToken = Objects.requireNonNull(tokenRef.get()); - this.userName = userName; + this.fsOwnerId = fsOwnerId; this.result = result; this.registerDeviceScene = registerDeviceScene; this.legacyRegisterDeviceScene = legacyRegisterDeviceScene; @@ -114,7 +115,7 @@ public class ReceiveKeyController implements FxController { try { if (response.statusCode() == 200) { var user = JSON.reader().readValue(response.body(), UserDto.class); - userName.set(user.name); + fsOwnerId.set(user.name); requestApiConfig(); } else { throw new IllegalStateException("Unexpected response " + response.statusCode()); @@ -184,6 +185,7 @@ public class ReceiveKeyController implements FxController { switch (response.statusCode()) { case 200 -> { var device = JSON.reader().readValue(response.body(), DeviceDto.class); + fsOwnerId.accumulateAndGet(device.name, (s1, s2) -> s1 + Constants.HUB_USER_DEVICE_SEPARATOR + s2); requestVaultMasterkey(device.userPrivateKey); } case 404 -> Platform.runLater(this::needsDeviceRegistration); @@ -338,7 +340,7 @@ public class ReceiveKeyController implements FxController { private record UserDto(@JsonProperty(value = "name", required = true) String name) {} @JsonIgnoreProperties(ignoreUnknown = true) - private record DeviceDto(@JsonProperty(value = "userPrivateKey", required = true) String userPrivateKey) {} + private record DeviceDto(@JsonProperty(value = "name", required = true) String name, @JsonProperty(value = "userPrivateKey", required = true) String userPrivateKey) {} @JsonIgnoreProperties(ignoreUnknown = true) private record ConfigDto(@JsonProperty(value = "apiLevel") int apiLevel) {} diff --git a/src/main/java/org/cryptomator/ui/notification/NotificationController.java b/src/main/java/org/cryptomator/ui/notification/NotificationController.java index 705071ca1..ce54ab851 100644 --- a/src/main/java/org/cryptomator/ui/notification/NotificationController.java +++ b/src/main/java/org/cryptomator/ui/notification/NotificationController.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.notification; +import org.cryptomator.common.Constants; import org.cryptomator.cryptofs.event.FileIsInUseEvent; import org.cryptomator.event.VaultEvent; import org.cryptomator.ui.common.FxController; @@ -84,7 +85,10 @@ public class NotificationController implements FxController { switch (newEvent.actualEvent()) { case FileIsInUseEvent fiiue -> { message.set(resourceBundle.getString("notification.inUse.message")); - description.set(resourceBundle.getString("notification.inUse.description").formatted(fiiue.cleartextPath(), fiiue.owner())); + var userAndDevice = fiiue.owner().split(Constants.HUB_USER_DEVICE_SEPARATOR); + var user = userAndDevice[0]; + var device = userAndDevice.length == 1? userAndDevice[0]:userAndDevice[1]; + description.set(resourceBundle.getString("notification.inUse.description").formatted(fiiue.cleartextPath(), user, device)); actionText.set(resourceBundle.getString("notification.inUse.action")); } default -> { diff --git a/src/main/resources/i18n/strings.properties b/src/main/resources/i18n/strings.properties index 3270f4ab4..0d973f390 100644 --- a/src/main/resources/i18n/strings.properties +++ b/src/main/resources/i18n/strings.properties @@ -700,5 +700,5 @@ eventView.entry.brokenFileNode.copyDecrypted=Copy decrypted path # Notifications ## FileIsInUse Notification notification.inUse.message=File is locked -notification.inUse.description=File %s is opened by user %s. Ask the user to close the file. Otherwise, you can ignore the lock and open it anyway, but be aware of the data loss risk. +notification.inUse.description=File %s is opened by user %s (%s). Ask the user to close the file. Otherwise, you can ignore the lock and open it anyway, but be aware of the data loss risk. notification.inUse.action=Ignore Lock \ No newline at end of file