diff --git a/.github/workflows/av-whitelist.yml b/.github/workflows/av-whitelist.yml index ca74a3281..16dd61bcf 100644 --- a/.github/workflows/av-whitelist.yml +++ b/.github/workflows/av-whitelist.yml @@ -7,6 +7,16 @@ on: description: "Url to the file to upload" required: true type: string + avast: + description: "Upload to Avast" + required: false + type: boolean + default: true + kaspersky: + description: "Upload to Kaspersky" + required: false + type: boolean + default: true workflow_dispatch: inputs: url: @@ -39,7 +49,7 @@ jobs: url="${INPUT_URL}" echo "fileName=${url##*/}" >> $GITHUB_OUTPUT - name: Download file - run: curl --remote-name ${INPUT_URL} -L -o ${{steps.extractName.outputs.fileName}} + run: curl "${INPUT_URL}" -L -o "${{steps.extractName.outputs.fileName}}" --fail-with-body - name: Upload artifact uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: @@ -50,7 +60,7 @@ jobs: name: Anti Virus Allowlisting Kaspersky runs-on: ubuntu-latest needs: download-file - if: github.event_name == 'workflow_call' || inputs.kaspersky + if: inputs.kaspersky steps: - name: Download artifact uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 @@ -70,7 +80,7 @@ jobs: name: Anti Virus Allowlisting Avast runs-on: ubuntu-latest needs: download-file - if: github.event_name == 'workflow_call' || inputs.avast + if: inputs.avast steps: - name: Download artifact uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 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 5012e5c78..b5158411b 100644 --- a/src/main/java/org/cryptomator/common/vaults/Vault.java +++ b/src/main/java/org/cryptomator/common/vaults/Vault.java @@ -10,6 +10,7 @@ package org.cryptomator.common.vaults; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.Constants; +import org.cryptomator.common.FilsystemOwnerSupplier; import org.cryptomator.common.mount.Mounter; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.VaultSettings; @@ -149,14 +150,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) // - .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/event/FileSystemEventAggregator.java b/src/main/java/org/cryptomator/event/FileSystemEventAggregator.java index c871436fd..b27238cee 100644 --- a/src/main/java/org/cryptomator/event/FileSystemEventAggregator.java +++ b/src/main/java/org/cryptomator/event/FileSystemEventAggregator.java @@ -6,6 +6,7 @@ import org.cryptomator.cryptofs.event.BrokenFileNodeEvent; import org.cryptomator.cryptofs.event.ConflictResolutionFailedEvent; import org.cryptomator.cryptofs.event.ConflictResolvedEvent; import org.cryptomator.cryptofs.event.DecryptionFailedEvent; +import org.cryptomator.cryptofs.event.FileIsInUseEvent; import org.cryptomator.cryptofs.event.FilesystemEvent; import javax.inject.Inject; @@ -101,6 +102,7 @@ public class FileSystemEventAggregator { case ConflictResolutionFailedEvent(_, _, Path conflictingCiphertext, _) -> conflictingCiphertext; case BrokenDirFileEvent(_, Path ciphertext) -> ciphertext; case BrokenFileNodeEvent(_, _, Path ciphertext) -> ciphertext; + case FileIsInUseEvent(_, _, Path ciphertext, _, _, _) -> ciphertext; }; return new FSEventBucket(v, p, event.getClass()); } diff --git a/src/main/java/org/cryptomator/ui/eventview/EventListCellController.java b/src/main/java/org/cryptomator/ui/eventview/EventListCellController.java index 3157f9f40..4c195db10 100644 --- a/src/main/java/org/cryptomator/ui/eventview/EventListCellController.java +++ b/src/main/java/org/cryptomator/ui/eventview/EventListCellController.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.eventview; +import org.cryptomator.cryptofs.event.FileIsInUseEvent; import org.cryptomator.event.FSEventBucket; import org.cryptomator.event.FSEventBucketContent; import org.cryptomator.event.FileSystemEventAggregator; @@ -115,7 +116,7 @@ public class EventListCellController implements FxController { eventActionsMenu.hide(); eventActionsMenu.getItems().clear(); eventTooltip.setText(item.getKey().vault().getDisplayName()); - addAction("generic.action.dismiss", () -> { + addLocalizedAction("generic.action.dismiss", () -> { fileSystemEventAggregator.remove(item.getKey()); }); switch (item.getValue().mostRecentEvent()) { @@ -124,20 +125,36 @@ public class EventListCellController implements FxController { case DecryptionFailedEvent fse -> this.adjustToDecryptionFailedEvent(fse); case BrokenDirFileEvent fse -> this.adjustToBrokenDirFileEvent(fse); case BrokenFileNodeEvent fse -> this.adjustToBrokenFileNodeEvent(fse); + case FileIsInUseEvent fse -> this.adjustToFileInUseEvent(fse); } } + private void adjustToFileInUseEvent(FileIsInUseEvent fse) { + eventIcon.setValue(FontAwesome5Icon.TIMES); + eventMessage.setValue("File is in use by " + fse.owner()); + var indexFileName = fse.cleartextPath().lastIndexOf("/"); + eventDescription.setValue(fse.cleartextPath().substring(indexFileName + 1)); + if (revealService != null) { + addAction("Show decrypted file", () -> reveal(revealService, convertVaultPathToSystemPath(fse.cleartextPath()))); + addAction("Show encrypted Path", () -> reveal(revealService, fse.ciphertextPath())); + } else { + addAction("Copy decrypted Path", () -> copyToClipboard(convertVaultPathToSystemPath(fse.cleartextPath()).toString())); + addAction("Copy encrypted Path", () -> copyToClipboard(fse.ciphertextPath().toString())); + } + addAction("Ignore in use status ", fse.ignoreMethod()); + } + private void adjustToBrokenFileNodeEvent(BrokenFileNodeEvent bfe) { eventIcon.setValue(FontAwesome5Icon.TIMES); eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenFileNode.message")); eventDescription.setValue(bfe.ciphertextPath().getFileName().toString()); if (revealService != null) { - addAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath())); + addLocalizedAction("eventView.entry.brokenFileNode.showEncrypted", () -> reveal(revealService, bfe.ciphertextPath())); } else { - addAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(bfe.ciphertextPath().toString())); + addLocalizedAction("eventView.entry.brokenFileNode.copyEncrypted", () -> copyToClipboard(bfe.ciphertextPath().toString())); } - addAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString())); + addLocalizedAction("eventView.entry.brokenFileNode.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(bfe.cleartextPath()).toString())); } private void adjustToConflictResolvedEvent(ConflictResolvedEvent cre) { @@ -145,9 +162,9 @@ public class EventListCellController implements FxController { eventMessage.setValue(resourceBundle.getString("eventView.entry.conflictResolved.message")); eventDescription.setValue(cre.resolvedCiphertextPath().getFileName().toString()); if (revealService != null) { - addAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath()))); + addLocalizedAction("eventView.entry.conflictResolved.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cre.resolvedCleartextPath()))); } else { - addAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString())); + addLocalizedAction("eventView.entry.conflictResolved.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cre.resolvedCleartextPath()).toString())); } } @@ -156,11 +173,11 @@ public class EventListCellController implements FxController { eventMessage.setValue(resourceBundle.getString("eventView.entry.conflict.message")); eventDescription.setValue(cfe.conflictingCiphertextPath().getFileName().toString()); if (revealService != null) { - addAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath()))); - addAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath())); + addLocalizedAction("eventView.entry.conflict.showDecrypted", () -> reveal(revealService, convertVaultPathToSystemPath(cfe.canonicalCleartextPath()))); + addLocalizedAction("eventView.entry.conflict.showEncrypted", () -> reveal(revealService, cfe.conflictingCiphertextPath())); } else { - addAction("eventView.entry.conflict.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cfe.canonicalCleartextPath()).toString())); - addAction("eventView.entry.conflict.copyEncrypted", () -> copyToClipboard(cfe.conflictingCiphertextPath().toString())); + addLocalizedAction("eventView.entry.conflict.copyDecrypted", () -> copyToClipboard(convertVaultPathToSystemPath(cfe.canonicalCleartextPath()).toString())); + addLocalizedAction("eventView.entry.conflict.copyEncrypted", () -> copyToClipboard(cfe.conflictingCiphertextPath().toString())); } } @@ -169,9 +186,9 @@ public class EventListCellController implements FxController { eventMessage.setValue(resourceBundle.getString("eventView.entry.decryptionFailed.message")); eventDescription.setValue(dfe.ciphertextPath().getFileName().toString()); if (revealService != null) { - addAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath())); + addLocalizedAction("eventView.entry.decryptionFailed.showEncrypted", () -> reveal(revealService, dfe.ciphertextPath())); } else { - addAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString())); + addLocalizedAction("eventView.entry.decryptionFailed.copyEncrypted", () -> copyToClipboard(dfe.ciphertextPath().toString())); } } @@ -180,14 +197,19 @@ public class EventListCellController implements FxController { eventMessage.setValue(resourceBundle.getString("eventView.entry.brokenDirFile.message")); eventDescription.setValue(bde.ciphertextPath().getParent().getFileName().toString()); if (revealService != null) { - addAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath())); + addLocalizedAction("eventView.entry.brokenDirFile.showEncrypted", () -> reveal(revealService, bde.ciphertextPath())); } else { - addAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString())); + addLocalizedAction("eventView.entry.brokenDirFile.copyEncrypted", () -> copyToClipboard(bde.ciphertextPath().toString())); } } - private void addAction(String localizationKey, Runnable action) { - var entry = new MenuItem(resourceBundle.getString(localizationKey)); + private void addLocalizedAction(String localizationKey, Runnable action) { + var entryText = resourceBundle.getString(localizationKey); + addAction(entryText, action); + } + + private void addAction(String entryText, Runnable action) { + var entry = new MenuItem(entryText); entry.getStyleClass().addLast("dropdown-button-context-menu-item"); entry.setOnAction(_ -> action.run()); eventActionsMenu.getItems().addLast(entry); @@ -234,18 +256,14 @@ public class EventListCellController implements FxController { } } - private Path convertVaultPathToSystemPath(Path p) { - if (!(p instanceof CryptoPath)) { - throw new IllegalArgumentException("Path " + p + " is not a vault path"); - } + private Path convertVaultPathToSystemPath(String vaultInternalPath) { var v = eventEntry.getValue().getKey().vault(); if (!v.isUnlocked()) { return Path.of(System.getProperty("user.home")); } var mountUri = v.getMountPoint().uri(); - var internalPath = p.toString().substring(1); - return Path.of(mountUri.getPath().concat(internalPath).substring(1)); + return Path.of(mountUri.getPath().concat(vaultInternalPath.substring(1)).substring(1)); } private void reveal(RevealPathService s, Path p) { diff --git a/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java b/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java index c36f486e0..19595e0e9 100644 --- a/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java +++ b/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java @@ -63,8 +63,8 @@ abstract class HealthCheckModule { @Provides @HealthCheckWindow @HealthCheckScoped - static KeyLoadingStrategy provideKeyLoadingStrategy(KeyLoadingComponent.Builder compBuilder, @HealthCheckWindow Vault vault, @Named("unlockWindow") Stage window ) { - return compBuilder.vault(vault).window(window).build().keyloadingStrategy(); + static KeyLoadingStrategy provideKeyLoadingStrategy(KeyLoadingComponent.Factory compFactory, @HealthCheckWindow Vault vault, @Named("unlockWindow") Stage window ) { + return compFactory.create(vault, window).keyloadingStrategy(); } @Provides diff --git a/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingComponent.java b/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingComponent.java index 190bf0e1f..f035388f6 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingComponent.java +++ b/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingComponent.java @@ -3,11 +3,8 @@ package org.cryptomator.ui.keyloading; import dagger.BindsInstance; import dagger.Subcomponent; import org.cryptomator.common.vaults.Vault; -import org.cryptomator.cryptolib.api.MasterkeyLoader; import javafx.stage.Stage; -import java.util.Map; -import java.util.function.Supplier; @KeyLoadingScoped @Subcomponent(modules = {KeyLoadingModule.class}) @@ -16,16 +13,10 @@ public interface KeyLoadingComponent { @KeyLoading KeyLoadingStrategy keyloadingStrategy(); - @Subcomponent.Builder - interface Builder { + @Subcomponent.Factory + interface Factory { - @BindsInstance - Builder vault(@KeyLoading Vault vault); - - @BindsInstance - Builder window(@KeyLoading Stage window); - - KeyLoadingComponent build(); + KeyLoadingComponent create(@BindsInstance @KeyLoading Vault vault, @KeyLoading @BindsInstance Stage window); } } 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) {} diff --git a/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java b/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java index 1c8d758fc..50240c685 100644 --- a/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java +++ b/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java @@ -57,8 +57,8 @@ abstract class UnlockModule { @Provides @UnlockWindow @UnlockScoped - static KeyLoadingStrategy provideKeyLoadingStrategy(KeyLoadingComponent.Builder compBuilder, @UnlockWindow Vault vault, @UnlockWindow Stage window) { - return compBuilder.vault(vault).window(window).build().keyloadingStrategy(); + static KeyLoadingStrategy provideKeyLoadingStrategy(KeyLoadingComponent.Factory compFactory, @UnlockWindow Vault vault, @UnlockWindow Stage window) { + return compFactory.create(vault, window).keyloadingStrategy(); } @Provides diff --git a/src/main/resources/fxml/addvault_new_expert_settings.fxml b/src/main/resources/fxml/addvault_new_expert_settings.fxml index 202f7c4d7..220444f71 100644 --- a/src/main/resources/fxml/addvault_new_expert_settings.fxml +++ b/src/main/resources/fxml/addvault_new_expert_settings.fxml @@ -39,9 +39,9 @@ - -