From 98d2442deb2dd68e5997269c7552e015ef4f0715 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Thu, 11 Dec 2025 14:34:45 +0100 Subject: [PATCH] For hub vaults set filesystem owner --- pom.xml | 2 +- .../common/FilsystemOwnerSupplier.java | 16 ++++++ .../org/cryptomator/common/vaults/Vault.java | 15 +++--- .../keyloading/hub/HubKeyLoadingModule.java | 7 +++ .../keyloading/hub/HubKeyLoadingStrategy.java | 19 +++++-- .../keyloading/hub/ReceiveKeyController.java | 51 +++++++++++++++++-- 6 files changed, 96 insertions(+), 14 deletions(-) create mode 100644 src/main/java/org/cryptomator/common/FilsystemOwnerSupplier.java diff --git a/pom.xml b/pom.xml index 24f35de0a..75ecf99d9 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents - 2.9.0 + 2.10.0-beta2 1.8.0-beta1 1.5.1 1.5.0-beta1 diff --git a/src/main/java/org/cryptomator/common/FilsystemOwnerSupplier.java b/src/main/java/org/cryptomator/common/FilsystemOwnerSupplier.java new file mode 100644 index 000000000..addea85ba --- /dev/null +++ b/src/main/java/org/cryptomator/common/FilsystemOwnerSupplier.java @@ -0,0 +1,16 @@ +package org.cryptomator.common; + +/** + * Objects which has some kind of owner. + */ +@FunctionalInterface +public interface FilsystemOwnerSupplier { + + /** + * Get the object owner. + * + * @return the object owner + */ + String getOwner(); + +} diff --git a/src/main/java/org/cryptomator/common/vaults/Vault.java b/src/main/java/org/cryptomator/common/vaults/Vault.java index 75790507c..758ed99a3 100644 --- a/src/main/java/org/cryptomator/common/vaults/Vault.java +++ b/src/main/java/org/cryptomator/common/vaults/Vault.java @@ -10,7 +10,7 @@ package org.cryptomator.common.vaults; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.Constants; -import org.cryptomator.event.FileSystemEventAggregator; +import org.cryptomator.common.FilsystemOwnerSupplier; import org.cryptomator.common.mount.Mounter; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.VaultSettings; @@ -23,6 +23,7 @@ import org.cryptomator.cryptofs.event.FilesystemEvent; import org.cryptomator.cryptolib.api.CryptoException; import org.cryptomator.cryptolib.api.MasterkeyLoader; import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException; +import org.cryptomator.event.FileSystemEventAggregator; import org.cryptomator.integrations.mount.MountFailedException; import org.cryptomator.integrations.mount.Mountpoint; import org.cryptomator.integrations.mount.UnmountFailedException; @@ -145,15 +146,17 @@ public class Vault { LOG.warn("Limiting cleartext filename length on this device to {}.", vaultSettings.maxCleartextFilenameLength.get()); } - CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() // + var fsPropsBuilder = CryptoFileSystemProperties.cryptoFileSystemProperties() // .withKeyLoader(keyLoader) // .withFlags(flags) // .withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength.get()) // .withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) // - .withFilesystemEventConsumer(this::consumeVaultEvent) // - .withOwner(System.getProperty("user.name")) - .build(); - return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps); + .withFilesystemEventConsumer(this::consumeVaultEvent); + if (keyLoader instanceof FilsystemOwnerSupplier oo) { + fsPropsBuilder.withOwnerGetter(oo::getOwner); + } + + return CryptoFileSystemProvider.newFileSystem(getPath(), fsPropsBuilder.build()); } private void destroyCryptoFileSystem() { 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 f8710b8c0..97ab2b394 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingModule.java +++ b/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingModule.java @@ -66,6 +66,13 @@ public abstract class HubKeyLoadingModule { return new AtomicReference<>(); } + @Provides + @Named("userName") + @KeyLoadingScoped + static AtomicReference provideUserNameRef() { + return new AtomicReference<>(); + } + @Provides @KeyLoadingScoped static CompletableFuture provideResult() { 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 40f845a63..6bf4c0bff 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingStrategy.java +++ b/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingStrategy.java @@ -2,6 +2,7 @@ package org.cryptomator.ui.keyloading.hub; import com.google.common.base.Preconditions; import dagger.Lazy; +import org.cryptomator.common.FilsystemOwnerSupplier; import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.keychain.NoKeychainAccessProviderException; import org.cryptomator.common.settings.DeviceKey; @@ -23,25 +24,28 @@ import java.net.URI; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicReference; @KeyLoading -public class HubKeyLoadingStrategy implements KeyLoadingStrategy { +public class HubKeyLoadingStrategy implements KeyLoadingStrategy, FilsystemOwnerSupplier { - private static final String SCHEME_PREFIX = "hub+"; + public static final String SCHEME_PREFIX = "hub+"; public static final String SCHEME_HUB_HTTP = SCHEME_PREFIX + "http"; public static final String SCHEME_HUB_HTTPS = SCHEME_PREFIX + "https"; private final Stage window; private final KeychainManager keychainManager; + private final AtomicReference userName; 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) { + 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) { this.window = window; this.keychainManager = keychainManager; + this.userName = userName; window.setTitle(windowTitle); window.setOnCloseRequest(_ -> result.cancel(true)); this.authFlowScene = authFlowScene; @@ -90,4 +94,13 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy { }); } + @Override + public String getOwner() { + var name = userName.get(); + if (name == null) { + throw new IllegalStateException("Owner is not yet determined"); + } + return name; + } + } 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 f94d882fa..dd6ae3b68 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java +++ b/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java @@ -41,7 +41,6 @@ import java.util.concurrent.atomic.AtomicReference; public class ReceiveKeyController implements FxController { private static final Logger LOG = LoggerFactory.getLogger(ReceiveKeyController.class); - private static final String SCHEME_PREFIX = "hub+"; private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true); private static final Duration REQ_TIMEOUT = Duration.ofSeconds(10); @@ -50,6 +49,7 @@ public class ReceiveKeyController implements FxController { private final String vaultId; private final String deviceId; private final String bearerToken; + private final AtomicReference userName; private final CompletableFuture result; private final Lazy registerDeviceScene; private final Lazy legacyRegisterDeviceScene; @@ -59,12 +59,25 @@ public class ReceiveKeyController implements FxController { private final HttpClient httpClient; @Inject - public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, HubConfig hubConfig, @Named("deviceId") String deviceId, @Named("bearerToken") AtomicReference tokenRef, CompletableFuture result, @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE) Lazy registerDeviceScene, @FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE) Lazy legacyRegisterDeviceScene, @FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy unauthorizedScene, @FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT) Lazy accountInitializationScene, @FxmlScene(FxmlFile.HUB_INVALID_LICENSE) Lazy invalidLicenseScene) { + public ReceiveKeyController(@KeyLoading Vault vault, // + ExecutorService executor, // + @KeyLoading Stage window, // + HubConfig hubConfig, // + @Named("deviceId") String deviceId, // + @Named("bearerToken") AtomicReference tokenRef, // + @Named("userName") AtomicReference userName, // + CompletableFuture result, // + @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE) Lazy registerDeviceScene, // + @FxmlScene(FxmlFile.HUB_LEGACY_REGISTER_DEVICE) Lazy legacyRegisterDeviceScene, // + @FxmlScene(FxmlFile.HUB_UNAUTHORIZED_DEVICE) Lazy unauthorizedScene, // + @FxmlScene(FxmlFile.HUB_REQUIRE_ACCOUNT_INIT) Lazy accountInitializationScene, // + @FxmlScene(FxmlFile.HUB_INVALID_LICENSE) Lazy invalidLicenseScene) { this.window = window; this.hubConfig = hubConfig; 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.result = result; this.registerDeviceScene = registerDeviceScene; this.legacyRegisterDeviceScene = legacyRegisterDeviceScene; @@ -81,7 +94,34 @@ public class ReceiveKeyController implements FxController { } public void receiveKey() { - requestApiConfig(); + requestUserData(); + } + + private void requestUserData() { + var userUri = hubConfig.URIs.API.resolve("users/me?withDevices=false"); + var request = HttpRequest.newBuilder(userUri) // + .header("Authorization", "Bearer " + bearerToken) // + .GET() // + .timeout(REQ_TIMEOUT) // + .build(); + httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)) // + .thenAcceptAsync(this::receivedUserData) // + .exceptionally(this::retrievalFailed); + } + + private void receivedUserData(HttpResponse response) { + LOG.debug("GET {} -> Status Code {}", response.request().uri(), response.statusCode()); + try { + if (response.statusCode() == 200) { + var user = JSON.reader().readValue(response.body(), UserDto.class); + userName.set(user.name); + requestApiConfig(); + } else { + throw new IllegalStateException("Unexpected response " + response.statusCode()); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } } /** @@ -289,11 +329,14 @@ public class ReceiveKeyController implements FxController { } private static String extractVaultId(URI vaultKeyUri) { - assert vaultKeyUri.getScheme().startsWith(SCHEME_PREFIX); + assert vaultKeyUri.getScheme().startsWith(HubKeyLoadingStrategy.SCHEME_PREFIX); var path = vaultKeyUri.getPath(); return path.substring(path.lastIndexOf('/') + 1); } + @JsonIgnoreProperties(ignoreUnknown = true) + private record UserDto(@JsonProperty(value = "name", required = true) String name) {} + @JsonIgnoreProperties(ignoreUnknown = true) private record DeviceDto(@JsonProperty(value = "userPrivateKey", required = true) String userPrivateKey) {}