diff --git a/hub/src/main/java/cloud/katta/crypto/uvf/UvfJWKCallback.java b/hub/src/main/java/cloud/katta/crypto/uvf/UvfJWKCallback.java new file mode 100644 index 00000000..5c22c570 --- /dev/null +++ b/hub/src/main/java/cloud/katta/crypto/uvf/UvfJWKCallback.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025 shift7 GmbH. All rights reserved. + */ + +package cloud.katta.crypto.uvf; + +import ch.cyberduck.core.Host; +import ch.cyberduck.core.LoginOptions; +import ch.cyberduck.core.exception.LoginCanceledException; +import ch.cyberduck.core.vault.JWKCallback; +import ch.cyberduck.core.vault.JWKCredentials; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.nimbusds.jose.jwk.JWK; + +public class UvfJWKCallback implements JWKCallback { + + private final JWK key; + + public UvfJWKCallback(final JWK key) throws JsonProcessingException { + this.key = key; + } + + @Override + public void close(final String input) { + // + } + + @Override + public JWKCredentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) throws LoginCanceledException { + return new JWKCredentials(key); + } +} diff --git a/hub/src/main/java/cloud/katta/crypto/uvf/UvfMetadataPayloadPasswordCallback.java b/hub/src/main/java/cloud/katta/crypto/uvf/UvfMetadataPayloadPasswordCallback.java deleted file mode 100644 index 8d364e71..00000000 --- a/hub/src/main/java/cloud/katta/crypto/uvf/UvfMetadataPayloadPasswordCallback.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2025 shift7 GmbH. All rights reserved. - */ - -package cloud.katta.crypto.uvf; - -import ch.cyberduck.core.Credentials; -import ch.cyberduck.core.DisabledPasswordCallback; -import ch.cyberduck.core.Host; -import ch.cyberduck.core.LoginOptions; -import ch.cyberduck.core.exception.LoginCanceledException; -import ch.cyberduck.core.vault.VaultCredentials; - -import com.fasterxml.jackson.core.JsonProcessingException; - -public class UvfMetadataPayloadPasswordCallback extends DisabledPasswordCallback { - - private final String payloadJson; - - public UvfMetadataPayloadPasswordCallback(final UvfMetadataPayload payload) throws JsonProcessingException { - this(payload.toJSON()); - } - - public UvfMetadataPayloadPasswordCallback(final String payloadJson) { - this.payloadJson = payloadJson; - } - - @Override - public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) throws LoginCanceledException { - return new VaultCredentials(payloadJson); - } -} diff --git a/hub/src/main/java/cloud/katta/crypto/uvf/VaultIdMetadataUVFProvider.java b/hub/src/main/java/cloud/katta/crypto/uvf/VaultIdMetadataUVFProvider.java new file mode 100644 index 00000000..a097099e --- /dev/null +++ b/hub/src/main/java/cloud/katta/crypto/uvf/VaultIdMetadataUVFProvider.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2025 shift7 GmbH. All rights reserved. + */ + +package cloud.katta.crypto.uvf; + +import ch.cyberduck.core.Host; +import ch.cyberduck.core.HostUrlProvider; +import ch.cyberduck.core.cryptomator.impl.uvf.VaultMetadataUVFProvider; +import ch.cyberduck.core.vault.VaultMetadataProvider; + +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.nimbusds.jose.JOSEException; + +public class VaultIdMetadataUVFProvider implements VaultMetadataUVFProvider { + + private final UUID vaultId; + private final UvfMetadataPayload.UniversalVaultFormatJWKS jwks; + private final byte[] vaultMetadata; + private final byte[] rootDirectoryMetadata; + private final String hashedRootDirId; + + public VaultIdMetadataUVFProvider(final Host storage, final UUID vaultId, + final UvfMetadataPayload.UniversalVaultFormatJWKS jwks, final UvfMetadataPayload vaultMetadata) throws JOSEException, JsonProcessingException { + this(vaultId, jwks, vaultMetadata.encrypt( + String.format("%s/api", new HostUrlProvider(false, true).get(storage)), + vaultId, + jwks.toJWKSet() + ).getBytes(StandardCharsets.US_ASCII), + vaultMetadata.computeRootDirUvf(), + vaultMetadata.computeRootDirIdHash() + ); + } + + public VaultIdMetadataUVFProvider(final UUID vaultId, final UvfMetadataPayload.UniversalVaultFormatJWKS jwks, final byte[] vaultMetadata, final byte[] rootDirectoryMetadata, final String hashedRootDirId) { + this.vaultId = vaultId; + this.jwks = jwks; + this.vaultMetadata = vaultMetadata; + this.rootDirectoryMetadata = rootDirectoryMetadata; + this.hashedRootDirId = hashedRootDirId; + } + + public static VaultIdMetadataUVFProvider cast(VaultMetadataProvider provider) { + if(provider instanceof VaultIdMetadataUVFProvider) { + return (VaultIdMetadataUVFProvider) provider; + } + else { + throw new IllegalArgumentException("Unsupported metadata type " + provider.getClass()); + } + } + + public UUID getVaultId() { + return vaultId; + } + + public UvfMetadataPayload.UniversalVaultFormatJWKS getJwks() { + return jwks; + } + + @Override + public byte[] getMetadata() { + return vaultMetadata; + } + + @Override + public byte[] getRootDirectoryMetadata() { + return rootDirectoryMetadata; + } + + @Override + public String getDirPath() { + return hashedRootDirId; + } +} diff --git a/hub/src/main/java/cloud/katta/protocols/hub/HubStorageLocationService.java b/hub/src/main/java/cloud/katta/protocols/hub/HubStorageLocationService.java index f91caa17..a166bdd8 100644 --- a/hub/src/main/java/cloud/katta/protocols/hub/HubStorageLocationService.java +++ b/hub/src/main/java/cloud/katta/protocols/hub/HubStorageLocationService.java @@ -121,7 +121,7 @@ public UvfMetadataPayload toUvfMetadataPayload(final Path bucket) { .provider(this.getProfile()) .defaultPath(bucket.getAbsolute()) .region(this.getRegion()) - .nickname(null != bucket.attributes().getDisplayname() ? bucket.attributes().getDisplayname() : "Vault")) + .nickname(bucket.attributes().getDisplayname())) .withAutomaticAccessGrant(new VaultMetadataJWEAutomaticAccessGrantDto() .enabled(true) .maxWotDepth(null)); diff --git a/hub/src/main/java/cloud/katta/protocols/hub/HubUVFVault.java b/hub/src/main/java/cloud/katta/protocols/hub/HubUVFVault.java index c011f42d..728232d7 100644 --- a/hub/src/main/java/cloud/katta/protocols/hub/HubUVFVault.java +++ b/hub/src/main/java/cloud/katta/protocols/hub/HubUVFVault.java @@ -7,26 +7,19 @@ import ch.cyberduck.core.DisabledCancelCallback; import ch.cyberduck.core.DisabledHostKeyCallback; import ch.cyberduck.core.Host; -import ch.cyberduck.core.HostUrlProvider; import ch.cyberduck.core.LoginCallback; import ch.cyberduck.core.PasswordCallback; import ch.cyberduck.core.Path; -import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.Session; -import ch.cyberduck.core.cryptomator.ContentWriter; -import ch.cyberduck.core.cryptomator.UVFVault; +import ch.cyberduck.core.cryptomator.AbstractVault; +import ch.cyberduck.core.cryptomator.impl.uvf.CryptoVault; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.InteroperabilityException; import ch.cyberduck.core.exception.UnsupportedException; -import ch.cyberduck.core.features.AttributesFinder; -import ch.cyberduck.core.features.Directory; -import ch.cyberduck.core.features.Write; import ch.cyberduck.core.preferences.HostPreferencesFactory; -import ch.cyberduck.core.preferences.PreferencesFactory; import ch.cyberduck.core.proxy.ProxyFactory; import ch.cyberduck.core.s3.S3Session; -import ch.cyberduck.core.transfer.TransferStatus; -import ch.cyberduck.core.vault.VaultCredentials; +import ch.cyberduck.core.vault.VaultMetadataProvider; import ch.cyberduck.core.vault.VaultUnlockCancelException; import org.apache.logging.log4j.LogManager; @@ -35,7 +28,6 @@ import java.nio.charset.StandardCharsets; import java.util.Collections; -import java.util.EnumSet; import java.util.UUID; import cloud.katta.client.ApiException; @@ -44,8 +36,7 @@ import cloud.katta.client.model.VaultDto; import cloud.katta.core.DeviceSetupCallback; import cloud.katta.crypto.UserKeys; -import cloud.katta.crypto.uvf.UvfMetadataPayload; -import cloud.katta.crypto.uvf.UvfMetadataPayloadPasswordCallback; +import cloud.katta.crypto.uvf.VaultIdMetadataUVFProvider; import cloud.katta.protocols.hub.exceptions.HubExceptionMappingService; import cloud.katta.protocols.s3.S3AssumeRoleProtocol; import com.fasterxml.jackson.core.JsonProcessingException; @@ -54,12 +45,9 @@ /** * Unified vault format (UVF) implementation for Katta */ -public class HubUVFVault extends UVFVault { +public class HubUVFVault extends CryptoVault { private static final Logger log = LogManager.getLogger(HubUVFVault.class); - private final UUID vaultId; - private final UvfMetadataPayload vaultMetadata; - /** * Storage connection only available after loading vault */ @@ -68,17 +56,13 @@ public class HubUVFVault extends UVFVault { /** * - * @param storage Storage connection - * @param vaultId Vault Id - * @param vaultMetadata Vault UVF metadata - * @param prompt Login prompt to access storage + * @param storage Storage connection + * @param bucket Vault UVF metadata + * @param prompt Login prompt to access storage */ - public HubUVFVault(final Session storage, final UUID vaultId, final UvfMetadataPayload vaultMetadata, final LoginCallback prompt) { - super(new Path(vaultMetadata.storage().getDefaultPath(), EnumSet.of(Path.Type.directory, Path.Type.volume), - new PathAttributes().setDisplayname(vaultMetadata.storage().getNickname()))); + public HubUVFVault(final Session storage, final Path bucket, final LoginCallback prompt) { + super(bucket); this.storage = storage; - this.vaultId = vaultId; - this.vaultMetadata = vaultMetadata; this.login = prompt; } @@ -115,27 +99,23 @@ public synchronized void close() { } @Override - public Path create(final Session session, final String region, final VaultCredentials noop) throws BackgroundException { + public AbstractVault create(final Session session, final String region, final VaultMetadataProvider metadata) throws BackgroundException { try { final HubSession hub = HubSession.coerce(session); - log.debug("Created metadata JWE {}", vaultMetadata); - final UvfMetadataPayload.UniversalVaultFormatJWKS jwks = UvfMetadataPayload.createKeys(); + final Path home = this.getHome(); + final UUID vaultId = VaultIdMetadataUVFProvider.cast(metadata).getVaultId(); final VaultDto vaultDto = new VaultDto() .id(vaultId) - .name(vaultMetadata.storage().getNickname()) + .name(home.attributes().getDisplayname()) .description(null) .archived(false) .creationTime(DateTime.now()) - .uvfMetadataFile(vaultMetadata.encrypt( - String.format("%s/api", new HostUrlProvider(false, true).get(session.getHost())), - vaultId, - jwks.toJWKSet() - )) - .uvfKeySet(jwks.serializePublicRecoverykey()); + .uvfMetadataFile(new String(VaultIdMetadataUVFProvider.cast(metadata).getMetadata(), StandardCharsets.US_ASCII)) + .uvfKeySet(VaultIdMetadataUVFProvider.cast(metadata).getJwks().serializePublicRecoverykey()); // Create vault in Hub final VaultResourceApi vaultResourceApi = new VaultResourceApi(hub.getClient()); log.debug("Create vault {}", vaultDto); - vaultResourceApi.apiVaultsVaultIdPut(vaultDto.getId(), vaultDto, + vaultResourceApi.apiVaultsVaultIdPut(vaultId, vaultDto, storage.getHost().getProtocol().isRoleConfigurable() && !S3Session.isAwsHostname(storage.getHost().getHostname()), storage.getHost().getProtocol().isRoleConfigurable() && S3Session.isAwsHostname(storage.getHost().getHostname())); // Upload JWE @@ -143,8 +123,8 @@ public Path create(final Session session, final String region, final VaultCre final UserDto userDto = hub.getMe(); final DeviceSetupCallback setup = login.getFeature(DeviceSetupCallback.class); final UserKeys userKeys = hub.getUserKeys(setup); - vaultResourceApi.apiVaultsVaultIdAccessTokensPost(vaultDto.getId(), - Collections.singletonMap(userDto.getId(), jwks.toOwnerAccessToken().encryptForUser(userKeys.ecdhKeyPair().getPublic()))); + vaultResourceApi.apiVaultsVaultIdAccessTokensPost(vaultId, + Collections.singletonMap(userDto.getId(), VaultIdMetadataUVFProvider.cast(metadata).getJwks().toOwnerAccessToken().encryptForUser(userKeys.ecdhKeyPair().getPublic()))); // Upload vault template to storage log.debug("Connect to {}", storage); final Host configuration = storage.getHost(); @@ -156,36 +136,8 @@ public Path create(final Session session, final String region, final VaultCre // No role chaining when creating vault configuration.setProperty(S3AssumeRoleProtocol.S3_ASSUMEROLE_ROLEARN_TAG, null); storage.open(ProxyFactory.get(), new DisabledHostKeyCallback(), login, new DisabledCancelCallback()); - final Path vault; - if(false) { - log.debug("Upload vault template to {}", storage); - return super.create(storage, - HubStorageLocationService.StorageLocation.fromIdentifier(region).getRegion(), noop); - } - else { // Obsolete when implemented in super - final Directory directory = (Directory) storage._getFeature(Directory.class); - final Path home = this.getHome(); - log.debug("Create vault root directory at {}", home); - final TransferStatus status = (new TransferStatus()).setRegion(HubStorageLocationService.StorageLocation.fromIdentifier(region).getRegion()); - vault = directory.mkdir(storage._getFeature(Write.class), home, status); - - final String hashedRootDirId = vaultMetadata.computeRootDirIdHash(); - final Path dataDir = new Path(vault, "d", EnumSet.of(Path.Type.directory)); - final Path firstLevel = new Path(dataDir, hashedRootDirId.substring(0, 2), EnumSet.of(Path.Type.directory)); - final Path secondLevel = new Path(firstLevel, hashedRootDirId.substring(2), EnumSet.of(Path.Type.directory)); - - directory.mkdir(storage._getFeature(Write.class), dataDir, status); - directory.mkdir(storage._getFeature(Write.class), firstLevel, status); - directory.mkdir(storage._getFeature(Write.class), secondLevel, status); - - // vault.uvf - new ContentWriter(storage).write(new Path(home, PreferencesFactory.get().getProperty("cryptomator.vault.config.filename"), - EnumSet.of(Path.Type.file, Path.Type.vault)), vaultDto.getUvfMetadataFile().getBytes(StandardCharsets.US_ASCII)); - // dir.uvf - new ContentWriter(storage).write(new Path(secondLevel, "dir.uvf", EnumSet.of(Path.Type.file)), - vaultMetadata.computeRootDirUvf()); - } - return vault; + log.debug("Upload vault template to {}", storage); + return super.create(storage, HubStorageLocationService.StorageLocation.fromIdentifier(region).getRegion(), metadata); } catch(JOSEException | JsonProcessingException e) { throw new InteroperabilityException(e.getMessage(), e); @@ -199,38 +151,29 @@ public Path create(final Session session, final String region, final VaultCre * * @param session Hub Connection * @param prompt Return user keys + * @param metadata metadata * @return Vault configuration with storage connection */ @Override - public HubUVFVault load(final Session session, final PasswordCallback prompt) throws BackgroundException { + public HubUVFVault load(final Session session, final PasswordCallback prompt, final VaultMetadataProvider metadata) throws BackgroundException { + log.debug("Connect to {}", storage); try { - log.debug("Connect to {}", storage); - try { - storage.open(ProxyFactory.get(), new DisabledHostKeyCallback(), login, new DisabledCancelCallback()); - } - catch(BackgroundException e) { - log.warn("Skip loading vault with failure {} connecting to storage", e.toString()); - throw new VaultUnlockCancelException(this, e); - } - final Path home = this.getHome(); - home.setAttributes(storage.getFeature(AttributesFinder.class).find(home) - .setDisplayname(vaultMetadata.storage().getNickname())); - log.debug("Initialize vault {} with metadata {}", this, vaultMetadata); - // Initialize cryptors - super.load(storage, new UvfMetadataPayloadPasswordCallback(vaultMetadata.toJSON())); - return this; + storage.open(ProxyFactory.get(), new DisabledHostKeyCallback(), login, new DisabledCancelCallback()); } - catch(JsonProcessingException e) { - throw new InteroperabilityException(e.getMessage(), e); + catch(BackgroundException e) { + log.warn("Skip loading vault with failure {} connecting to storage", e.toString()); + throw new VaultUnlockCancelException(this, e); } + log.debug("Initialize vault {}", this); + // Initialize cryptors + super.load(storage, prompt, metadata); + return this; } @Override public String toString() { final StringBuilder sb = new StringBuilder("HubUVFVault{"); - sb.append("vaultId=").append(vaultId); - sb.append(", vaultMetadata=").append(vaultMetadata); - sb.append(", storage=").append(storage); + sb.append("storage=").append(storage); sb.append('}'); return sb.toString(); } diff --git a/hub/src/main/java/cloud/katta/protocols/hub/HubVaultListService.java b/hub/src/main/java/cloud/katta/protocols/hub/HubVaultListService.java index c3963ade..a4178a1d 100644 --- a/hub/src/main/java/cloud/katta/protocols/hub/HubVaultListService.java +++ b/hub/src/main/java/cloud/katta/protocols/hub/HubVaultListService.java @@ -10,6 +10,7 @@ import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.LoginCallback; import ch.cyberduck.core.Path; +import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.Session; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; @@ -22,16 +23,24 @@ import org.apache.logging.log4j.Logger; import java.text.MessageFormat; +import java.util.Base64; +import java.util.EnumSet; import cloud.katta.client.ApiException; import cloud.katta.client.api.VaultResourceApi; import cloud.katta.client.model.VaultDto; import cloud.katta.core.DeviceSetupCallback; +import cloud.katta.crypto.uvf.UvfAccessTokenPayload; +import cloud.katta.crypto.uvf.UvfJWKCallback; import cloud.katta.crypto.uvf.UvfMetadataPayload; +import cloud.katta.crypto.uvf.VaultIdMetadataUVFProvider; import cloud.katta.protocols.hub.exceptions.HubExceptionMappingService; import cloud.katta.workflows.VaultServiceImpl; import cloud.katta.workflows.exceptions.AccessException; import cloud.katta.workflows.exceptions.SecurityFailure; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.jwk.OctetSequenceKey; public class HubVaultListService implements ListService { private static final Logger log = LogManager.getLogger(HubVaultListService.class); @@ -62,15 +71,24 @@ public AttributedList list(final Path directory, final ListProgressListene final DeviceSetupCallback setup = prompt.getFeature(DeviceSetupCallback.class); final UvfMetadataPayload vaultMetadata = vaultService.getVaultMetadataJWE(vaultDto.getId(), session.getUserKeys(setup)); final Session storage = vaultService.getVaultStorageSession(session, vaultDto.getId(), vaultMetadata); - final HubUVFVault vault = new HubUVFVault(storage, vaultDto.getId(), vaultMetadata, prompt); + final Path bucket = new Path(vaultMetadata.storage().getDefaultPath(), EnumSet.of(Path.Type.directory, Path.Type.volume), + new PathAttributes().setDisplayname(vaultMetadata.storage().getNickname())); try { - registry.add(vault.load(session, prompt)); + final UvfAccessTokenPayload accessToken = vaultService.getVaultAccessTokenJWE(vaultDto.getId(), session.getUserKeys(setup)); + final OctetSequenceKey rawMemberKey = UvfMetadataPayload.UniversalVaultFormatJWKS.memberKeyFromRawKey(Base64.getDecoder().decode(accessToken.key())); + final HubUVFVault vault = new HubUVFVault(storage, bucket, prompt).load(session, new UvfJWKCallback(rawMemberKey), new VaultIdMetadataUVFProvider( + session.getHost(), vaultDto.getId(), null, vaultMetadata)); + log.info("Loaded vault {}", vault); + registry.add(vault); vaults.add(vault.getHome()); listener.chunk(directory, vaults); } catch(VaultUnlockCancelException e) { log.warn("Skip vault {} with failure {} loading", vaultDto, e); } + catch(JsonProcessingException | JOSEException e) { + throw new SecurityFailure(e); + } } catch(ApiException e) { if(HttpStatus.SC_FORBIDDEN == e.getCode()) { diff --git a/hub/src/test/java/cloud/katta/crypto/uvf/UvfMetadataPayloadTest.java b/hub/src/test/java/cloud/katta/crypto/uvf/UvfMetadataPayloadTest.java index 5f05a158..f72d3846 100644 --- a/hub/src/test/java/cloud/katta/crypto/uvf/UvfMetadataPayloadTest.java +++ b/hub/src/test/java/cloud/katta/crypto/uvf/UvfMetadataPayloadTest.java @@ -8,7 +8,8 @@ import ch.cyberduck.core.AlphanumericRandomStringService; import ch.cyberduck.core.Host; import ch.cyberduck.core.Path; -import ch.cyberduck.core.cryptomator.UVFVault; +import ch.cyberduck.core.TestProtocol; +import ch.cyberduck.core.cryptomator.impl.uvf.CryptoVault; import ch.cyberduck.core.cryptomator.random.FastSecureRandomProvider; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.ssl.DefaultX509KeyManager; @@ -169,10 +170,14 @@ void testUVFMasterkeyFromUvfMetadataPayload() throws JsonProcessingException { } @Test - void testUvfVaultLoadFromMetadataPayload() throws JsonProcessingException, BackgroundException { + void testUvfVaultLoadFromMetadataPayload() throws JsonProcessingException, BackgroundException, JOSEException { final UvfMetadataPayload uvfMetadataPayload = UvfMetadataPayload.create(); - final UVFVault uvfVault = new UVFVault(new Path("/", EnumSet.of(AbstractPath.Type.directory))); - uvfVault.load(new HubSession(new Host(new HubProtocol()), new DisabledX509TrustManager(), new DefaultX509KeyManager()), - new UvfMetadataPayloadPasswordCallback(uvfMetadataPayload)); + final UvfMetadataPayload.UniversalVaultFormatJWKS keys = UvfMetadataPayload.createKeys(); + final UUID vaultId = UUID.randomUUID(); + final VaultIdMetadataUVFProvider provider = new VaultIdMetadataUVFProvider(new Host(new TestProtocol()), vaultId, keys, uvfMetadataPayload); + final CryptoVault uvfVault = new CryptoVault(new Path("/", EnumSet.of(AbstractPath.Type.directory))); + final Host host = new Host(new HubProtocol()); + uvfVault.load(new HubSession(host, new DisabledX509TrustManager(), new DefaultX509KeyManager()), + new UvfJWKCallback(keys.memberKey()), provider); } } diff --git a/hub/src/test/java/cloud/katta/testsetup/AbstractHubTest.java b/hub/src/test/java/cloud/katta/testsetup/AbstractHubTest.java index 1c622fdb..35ba0533 100644 --- a/hub/src/test/java/cloud/katta/testsetup/AbstractHubTest.java +++ b/hub/src/test/java/cloud/katta/testsetup/AbstractHubTest.java @@ -5,6 +5,7 @@ package cloud.katta.testsetup; import ch.cyberduck.core.*; +import ch.cyberduck.core.cryptomator.CryptoVaultProvider; import ch.cyberduck.core.preferences.MemoryPreferences; import ch.cyberduck.core.preferences.Preferences; import ch.cyberduck.core.preferences.PreferencesFactory; @@ -141,6 +142,7 @@ protected void configureLogging(final String level) { preferences.setProperty("factory.supportdirectoryfinder.class", ch.cyberduck.core.preferences.TemporarySupportDirectoryFinder.class.getName()); preferences.setProperty("factory.passwordstore.class", UnsecureHostPasswordStore.class.getName()); preferences.setProperty("factory.vaultregistry.class", HubVaultRegistry.class.getName()); + preferences.setProperty("factory.vaultprovider.class", CryptoVaultProvider.class.getName()); preferences.setProperty("oauth.handler.scheme", "katta"); preferences.setProperty("hub.protocol.scheduler.period", 30); diff --git a/hub/src/test/java/cloud/katta/workflows/AbstractHubSynchronizeTest.java b/hub/src/test/java/cloud/katta/workflows/AbstractHubSynchronizeTest.java index b3792b07..42f3c325 100644 --- a/hub/src/test/java/cloud/katta/workflows/AbstractHubSynchronizeTest.java +++ b/hub/src/test/java/cloud/katta/workflows/AbstractHubSynchronizeTest.java @@ -13,6 +13,8 @@ import ch.cyberduck.core.ListService; import ch.cyberduck.core.OAuthTokens; import ch.cyberduck.core.Path; +import ch.cyberduck.core.PathAttributes; +import ch.cyberduck.core.Session; import ch.cyberduck.core.SimplePathPredicate; import ch.cyberduck.core.UUIDRandomStringService; import ch.cyberduck.core.exception.AccessDeniedException; @@ -26,7 +28,6 @@ import ch.cyberduck.core.features.Vault; import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; -import ch.cyberduck.core.vault.VaultCredentials; import ch.cyberduck.core.vault.VaultRegistry; import org.apache.commons.lang3.RandomUtils; @@ -56,6 +57,7 @@ import cloud.katta.client.model.StorageProfileS3Dto; import cloud.katta.client.model.StorageProfileS3STSDto; import cloud.katta.crypto.uvf.UvfMetadataPayload; +import cloud.katta.crypto.uvf.VaultIdMetadataUVFProvider; import cloud.katta.crypto.uvf.VaultMetadataJWEAutomaticAccessGrantDto; import cloud.katta.crypto.uvf.VaultMetadataJWEBackendDto; import cloud.katta.model.StorageProfileDtoWrapper; @@ -255,8 +257,8 @@ void test03AddVault(final HubTestConfig config) throws Exception { log.info("Creating vault in {}", hubSession); final UUID vaultId = UUID.fromString(new UUIDRandomStringService().random()); - final Path bucket = new Path(null == storageProfileWrapper.getBucketPrefix() ? "katta-test-" + vaultId : storageProfileWrapper.getBucketPrefix() + vaultId, - EnumSet.of(Path.Type.volume, Path.Type.directory)); + final String name = null == storageProfileWrapper.getBucketPrefix() ? "katta-test-" + vaultId : storageProfileWrapper.getBucketPrefix() + vaultId; + final Path bucket = new Path(name, EnumSet.of(Path.Type.volume, Path.Type.directory), new PathAttributes().setDisplayname(String.format("Vault %s", name))); final HubStorageLocationService.StorageLocation location = new HubStorageLocationService.StorageLocation(storageProfileWrapper.getId().toString(), storageProfileWrapper.getRegion(), storageProfileWrapper.getName()); final UvfMetadataPayload vaultMetadata = UvfMetadataPayload.create() @@ -266,13 +268,14 @@ void test03AddVault(final HubTestConfig config) throws Exception { .provider(location.getProfile()) .defaultPath(bucket.getAbsolute()) .region(location.getRegion()) - .nickname(null != bucket.attributes().getDisplayname() ? bucket.attributes().getDisplayname() : "Vault")) + .nickname(bucket.attributes().getDisplayname())) .withAutomaticAccessGrant(new VaultMetadataJWEAutomaticAccessGrantDto() .enabled(true) .maxWotDepth(null)); - final HubUVFVault cryptomator = new HubUVFVault(new VaultServiceImpl(hubSession).getVaultStorageSession(hubSession, vaultId, vaultMetadata), - vaultId, vaultMetadata, new DisabledLoginCallback()); - cryptomator.create(hubSession, location.getIdentifier(), new VaultCredentials(StringUtils.EMPTY)); + final Session storage = new VaultServiceImpl(hubSession).getVaultStorageSession(hubSession, vaultId, vaultMetadata); + final HubUVFVault cryptomator = new HubUVFVault(storage, bucket, new DisabledLoginCallback()); + cryptomator.create(hubSession, location.getIdentifier(), new VaultIdMetadataUVFProvider(storage.getHost(), vaultId, + UvfMetadataPayload.createKeys(), vaultMetadata)); final AttributedList vaults = hubSession.getFeature(ListService.class).list(Home.root(), new DisabledListProgressListener()); assertFalse(vaults.isEmpty()); diff --git a/hub/src/test/java/cloud/katta/workflows/AbstractHubWorkflowTest.java b/hub/src/test/java/cloud/katta/workflows/AbstractHubWorkflowTest.java index d2b42727..634cb5a2 100644 --- a/hub/src/test/java/cloud/katta/workflows/AbstractHubWorkflowTest.java +++ b/hub/src/test/java/cloud/katta/workflows/AbstractHubWorkflowTest.java @@ -6,10 +6,10 @@ import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.Path; +import ch.cyberduck.core.PathAttributes; +import ch.cyberduck.core.Session; import ch.cyberduck.core.UUIDRandomStringService; -import ch.cyberduck.core.vault.VaultCredentials; -import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.jupiter.params.ParameterizedTest; @@ -37,6 +37,7 @@ import cloud.katta.client.model.VaultDto; import cloud.katta.crypto.UserKeys; import cloud.katta.crypto.uvf.UvfMetadataPayload; +import cloud.katta.crypto.uvf.VaultIdMetadataUVFProvider; import cloud.katta.crypto.uvf.VaultMetadataJWEAutomaticAccessGrantDto; import cloud.katta.crypto.uvf.VaultMetadataJWEBackendDto; import cloud.katta.model.SetupCodeJWE; @@ -111,8 +112,8 @@ void testHubWorkflow(final HubTestConfig config) throws Exception { .filter(p -> p.getId().toString().equals(config.vault.storageProfileId.toLowerCase())).findFirst().get(); final UUID vaultId = UUID.fromString(new UUIDRandomStringService().random()); - final Path bucket = new Path(null == storageProfileWrapper.getBucketPrefix() ? "katta-test-" + vaultId : storageProfileWrapper.getBucketPrefix() + vaultId, - EnumSet.of(Path.Type.volume, Path.Type.directory)); + final String name = null == storageProfileWrapper.getBucketPrefix() ? "katta-test-" + vaultId : storageProfileWrapper.getBucketPrefix() + vaultId; + final Path bucket = new Path(name, EnumSet.of(Path.Type.volume, Path.Type.directory), new PathAttributes().setDisplayname(String.format("Vault %s", name))); final HubStorageLocationService.StorageLocation location = new HubStorageLocationService.StorageLocation(storageProfileWrapper.getId().toString(), storageProfileWrapper.getRegion(), storageProfileWrapper.getName()); final UvfMetadataPayload vaultMetadata = UvfMetadataPayload.create() @@ -122,13 +123,14 @@ void testHubWorkflow(final HubTestConfig config) throws Exception { .provider(location.getProfile()) .defaultPath(bucket.getAbsolute()) .region(location.getRegion()) - .nickname(null != bucket.attributes().getDisplayname() ? bucket.attributes().getDisplayname() : "Vault")) + .nickname(bucket.attributes().getDisplayname())) .withAutomaticAccessGrant(new VaultMetadataJWEAutomaticAccessGrantDto() .enabled(true) .maxWotDepth(3)); - final HubUVFVault cryptomator = new HubUVFVault(new VaultServiceImpl(hubSession).getVaultStorageSession(hubSession, vaultId, vaultMetadata), - vaultId, vaultMetadata, new DisabledLoginCallback()); - cryptomator.create(hubSession, location.getIdentifier(), new VaultCredentials(StringUtils.EMPTY)); + final Session storage = new VaultServiceImpl(hubSession).getVaultStorageSession(hubSession, vaultId, vaultMetadata); + final HubUVFVault cryptomator = new HubUVFVault(storage, bucket, new DisabledLoginCallback()); + cryptomator.create(hubSession, location.getIdentifier(), new VaultIdMetadataUVFProvider(storage.getHost(), vaultId, + UvfMetadataPayload.createKeys(), vaultMetadata)); checkNumberOfVaults(hubSession, config, vaultId, 0, 0, 1, 0, 0); diff --git a/pom.xml b/pom.xml index 0eb59da5..4633b9ce 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ 5.13.4 1.13.4 - 9.3.0.uvfdraft-SNAPSHOT + 9.3.0.uvfdraft.cryptolib-SNAPSHOT 1.21.3