From a91d711b66f58408d646d93e0a305c92929f1dcf Mon Sep 17 00:00:00 2001 From: chenkins Date: Tue, 12 Dec 2023 12:57:03 +0100 Subject: [PATCH] Refactoring (R3) storage profile service WiP (#4 #6). --- backend/config/application.json | 46 ++++ backend/config/application.properties | 82 +----- backend/config/blup.json | 25 ++ .../hub/api/cipherduck/BackendsConfig.java | 15 - .../hub/api/cipherduck/BackendsConfigDto.java | 14 - .../cipherduck/BackendsConfigResource.java | 49 ---- .../hub/api/cipherduck/CipherduckConfig.java | 5 + .../hub/api/cipherduck/StorageConfig.java | 48 ---- .../hub/api/cipherduck/StorageConfigDto.java | 31 --- .../hub/api/cipherduck/StorageProfileDto.java | 258 ++++++++++++++++++ .../cipherduck/StorageProfileResource.java | 59 ++++ .../hub/api/cipherduck/StorageResource.java | 13 +- .../hub/api/cipherduck/VaultJWEBackend.java | 72 ----- .../api/cipherduck/VaultJWEBackendDto.java | 57 ++-- .../hub/api/cipherduck/storage/S3Storage.java | 12 +- .../KeycloakGrantAccessToVault.java | 9 +- frontend/src/common/backend.ts | 10 +- frontend/src/components/CreateVault.vue | 6 +- 18 files changed, 437 insertions(+), 374 deletions(-) create mode 100644 backend/config/application.json create mode 100644 backend/config/blup.json delete mode 100644 backend/src/main/java/org/cryptomator/hub/api/cipherduck/BackendsConfig.java delete mode 100644 backend/src/main/java/org/cryptomator/hub/api/cipherduck/BackendsConfigDto.java delete mode 100644 backend/src/main/java/org/cryptomator/hub/api/cipherduck/BackendsConfigResource.java delete mode 100644 backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageConfig.java delete mode 100644 backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageConfigDto.java create mode 100644 backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageProfileDto.java create mode 100644 backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageProfileResource.java delete mode 100644 backend/src/main/java/org/cryptomator/hub/api/cipherduck/VaultJWEBackend.java diff --git a/backend/config/application.json b/backend/config/application.json new file mode 100644 index 000000000..5b4957532 --- /dev/null +++ b/backend/config/application.json @@ -0,0 +1,46 @@ + + +# +################ +# AWS STS +################ +# +# storage configuration: backends.backends[1].id=https: //sts.amazonaws.com +backends.backends[1].name=AWS S3 STS +# +# bucket creation: backends.backends[1].bucket-prefix=cipherduck +backends.backends[1].sts-role-arn-hub=arn: aws: iam:: 930717317329: role/cipherduck-createbucket +backends.backends[1].sts-role-arn-client=arn: aws: iam:: 930717317329: role/cipherduck-createbucket +backends.backends[1].region=eu-west-1 +backends.backends[1].regions=eu-west-1, eu-west-2, eu-west-3 +# +# bookmark: backends.backends[1].jwe.protocol=s3-sts +backends.backends[1].jwe.provider=s3-sts-https +backends.backends[1].jwe.sts-role-arn=arn: aws: iam:: 930717317329: role/cipherduck_chain_01 +backends.backends[1].jwe.sts-role-arn2=arn: aws: iam:: 930717317329: role/cipherduck_chain_02 +# +################## +# MinIO localhost permanent +################### +# +# storage configuration: backends.backends[2].id=http: //minio:9000_static +backends.backends[2].name=MinIO S3 static +backends.backends[2].s3-endpoint=http: //minio:9000 +# +# bookmark: backends.backends[2].jwe.protocol=s3 +backends.backends[2].jwe.provider=s3-http-path-style +backends.backends[2].jwe.hostname=minio +backends.backends[2].jwe.port=9000 +# +# +################ +# AWS static +################ +# +# storage configuration: backends.backends[3].id=AWS static +backends.backends[3].name=AWS S3 static +backends.backends[3].region=eu-central-1 +# +# bookmark +backends.backends[3].jwe.protocol=s3 +backends.backends[3].jwe.provider=s3-https \ No newline at end of file diff --git a/backend/config/application.properties b/backend/config/application.properties index eec14fe01..e4e716a6e 100644 --- a/backend/config/application.properties +++ b/backend/config/application.properties @@ -10,84 +10,4 @@ %cipherduck-testing.hub.keycloak.local-url=https://testing.hub.cryptomator.org/kc %cipherduck-testing.quarkus.oidc.auth-server-url=https://testing.hub.cryptomator.org/kc/realms/cipherduck %cipherduck-staging.hub.keycloak.realm=cipherduck -%cipherduck-testing.hub.keycloak.realm=cipherduck - -# -################## -# MinIO localhost STS -################### -# -# storage configuration: -backends.backends[0].id=http://minio:9000 -backends.backends[0].name=MinIO S3 STS -# -# bucket creation: -backends.backends[0].bucket-prefix=cipherduck -backends.backends[0].region=eu-central-1 -backends.backends[0].regions=eu-west-1,eu-west-2,eu-west-3,eu-north-1,eu-south-1,eu-south-2,eu-central-1,eu-central-2 -backends.backends[0].with-path-style-access-enabled=true -# (role for cryptomatorhub client -> bucket creation from hub frontend) -backends.backends[0].sts-role-arn-hub=arn:minio:iam:::role/HGKdlY4eFFsXVvJmwlMYMhmbnDE -# (role for cryptomator client -> bucket creation from Desktop client) -backends.backends[0].sts-role-arn-client=arn:minio:iam:::role/IqZpDC5ahW_DCAvZPZA4ACjEnDE -backends.backends[0].sts-endpoint=http://minio:9000 -# -# bookmark: -backends.backends[0].jwe.protocol=s3-sts -backends.backends[0].jwe.provider=s3-sts-http -backends.backends[0].jwe.hostname=minio -backends.backends[0].jwe.port=9000 -backends.backends[0].jwe.sts-endpoint=http://minio:9000 -# (role for cryptomatorvaults client -> after token-exchange) -backends.backends[0].jwe.sts-role-arn=arn:minio:iam:::role/Hdms6XDZ6oOpuWYI3gu4gmgHN94 -# -# -################ -# AWS STS -################ -# -# storage configuration: -backends.backends[1].id=https://sts.amazonaws.com -backends.backends[1].name=AWS S3 STS -# -# bucket creation: -backends.backends[1].bucket-prefix=cipherduck -backends.backends[1].sts-role-arn-hub=arn:aws:iam::930717317329:role/cipherduck-createbucket -backends.backends[1].sts-role-arn-client=arn:aws:iam::930717317329:role/cipherduck-createbucket -backends.backends[1].region=eu-west-1 -backends.backends[1].regions=eu-west-1,eu-west-2,eu-west-3 -# -# bookmark: -backends.backends[1].jwe.protocol=s3-sts -backends.backends[1].jwe.provider=s3-sts-https -backends.backends[1].jwe.sts-role-arn=arn:aws:iam::930717317329:role/cipherduck_chain_01 -backends.backends[1].jwe.sts-role-arn2=arn:aws:iam::930717317329:role/cipherduck_chain_02 -# -################## -# MinIO localhost permanent -################### -# -# storage configuration: -backends.backends[2].id=http://minio:9000_static -backends.backends[2].name=MinIO S3 static -backends.backends[2].s3-endpoint=http://minio:9000 -# -# bookmark: -backends.backends[2].jwe.protocol=s3 -backends.backends[2].jwe.provider=s3-http-path-style -backends.backends[2].jwe.hostname=minio -backends.backends[2].jwe.port=9000 -# -# -################ -# AWS static -################ -# -# storage configuration: -backends.backends[3].id=AWS static -backends.backends[3].name=AWS S3 static -backends.backends[3].region=eu-central-1 -# -# bookmark -backends.backends[3].jwe.protocol=s3 -backends.backends[3].jwe.provider=s3-https \ No newline at end of file +%cipherduck-testing.hub.keycloak.realm=cipherduck \ No newline at end of file diff --git a/backend/config/blup.json b/backend/config/blup.json new file mode 100644 index 000000000..2817cf6df --- /dev/null +++ b/backend/config/blup.json @@ -0,0 +1,25 @@ +{ + "id": "8fe86719022530493c3c1b852a2c0b6748781f69", + "name": "MinIO S3 STS", + "bucketPrefix": "cipherduck", + "region": "eu-central-1", + "regions": [ + "eu-west-1", + "eu-west-2", + "eu-west-3", + "eu-north-1", + "eu-south-1", + "eu-south-2", + "eu-central-1", + "eu-central-2" + ], + "withPathStyleAccessEnabled": "true", + "stsRoleArnHub": "arn:minio:iam:::role/HGKdlY4eFFsXVvJmwlMYMhmbnDE", + "stsRoleArnClient": "arn:minio:iam:::role/IqZpDC5ahW_DCAvZPZA4ACjEnDE", + "stsEndpoint": "http://minio:9000", + "protocol": "s3-sts", + "scheme": "http", + "hostname": "minio", + "port": "9000", + "stsRoleArn": "arn:minio:iam:::role/Hdms6XDZ6oOpuWYI3gu4gmgHN94" +} \ No newline at end of file diff --git a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/BackendsConfig.java b/backend/src/main/java/org/cryptomator/hub/api/cipherduck/BackendsConfig.java deleted file mode 100644 index f31aeeb23..000000000 --- a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/BackendsConfig.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.cryptomator.hub.api.cipherduck; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.quarkus.runtime.annotations.StaticInitSafe; -import io.smallrye.config.ConfigMapping; - -import java.util.List; - -@StaticInitSafe -@ConfigMapping(prefix = "backends") - -public interface BackendsConfig { - @JsonProperty("backends") - List backends(); -} diff --git a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/BackendsConfigDto.java b/backend/src/main/java/org/cryptomator/hub/api/cipherduck/BackendsConfigDto.java deleted file mode 100644 index 95c6bf7a6..000000000 --- a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/BackendsConfigDto.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.cryptomator.hub.api.cipherduck; - -import java.util.List; - -public record BackendsConfigDto( - String hubId, - - List backends - -) implements BackendsConfig { - -} - - diff --git a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/BackendsConfigResource.java b/backend/src/main/java/org/cryptomator/hub/api/cipherduck/BackendsConfigResource.java deleted file mode 100644 index 4484129a9..000000000 --- a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/BackendsConfigResource.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.cryptomator.hub.api.cipherduck; - -import jakarta.annotation.security.RolesAllowed; -import jakarta.inject.Inject; -import jakarta.transaction.Transactional; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.MediaType; -import org.cryptomator.hub.entities.Settings; -import org.eclipse.microprofile.openapi.annotations.Operation; -import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; - -import java.util.Optional; -import java.util.stream.Collectors; - -@Path("/backendsconfig") -public class BackendsConfigResource { - - - @Inject - BackendsConfig backendsConfig; - - @Inject - CipherduckConfig cipherduckConfig; - - - @GET - @Path("/") - @RolesAllowed("user") - @Produces(MediaType.APPLICATION_JSON) - @Transactional - @Operation(summary = "get configs for storage backends", description = "get list of configs for storage backends") - @APIResponse(responseCode = "200", description = "uploaded storage configuration") - public BackendsConfigDto getBackendsConfig() { - return new BackendsConfigDto(Settings.get().hubId, backendsConfig.backends().stream() - .map(b -> new StorageConfigDto(b, new VaultJWEBackendDto(b.jwe(), - // TODO review: should we make this more explicit - attribute isPermanent? - b.jwe().stsRoleArn().map(ignored -> cipherduckConfig.authEndpoint()), - b.jwe().stsRoleArn().map(ignored -> cipherduckConfig.tokenEndpoint()), - b.jwe().stsRoleArn().map(ignored -> cipherduckConfig.keycloakClientIdCryptomator()), - b.jwe().stsRoleArn().map(ignored -> cipherduckConfig.keycloakClientIdCryptomatorVaults()), - Settings.get().hubId - ))) - .collect(Collectors.toList())); - } - - -} diff --git a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/CipherduckConfig.java b/backend/src/main/java/org/cryptomator/hub/api/cipherduck/CipherduckConfig.java index 3f3be4287..907c557d1 100644 --- a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/CipherduckConfig.java +++ b/backend/src/main/java/org/cryptomator/hub/api/cipherduck/CipherduckConfig.java @@ -5,6 +5,9 @@ import jakarta.inject.Inject; import org.eclipse.microprofile.config.inject.ConfigProperty; +import java.util.ArrayList; +import java.util.List; + // TODO review: backport to ConfigResource.ConfigDto upstream? @ApplicationScoped public class CipherduckConfig { @@ -37,6 +40,8 @@ public class CipherduckConfig { @Inject OidcConfigurationMetadata oidcConfData; + public List inMemoryStorageConfigs = new ArrayList<>(); + String replacePrefix(String str, String prefix, String replacement) { int index = str.indexOf(prefix); if (index == 0) { diff --git a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageConfig.java b/backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageConfig.java deleted file mode 100644 index 0f9b4081e..000000000 --- a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageConfig.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.cryptomator.hub.api.cipherduck; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.List; -import java.util.Optional; - - -public interface StorageConfig { - @JsonProperty("id") - String id(); - - @JsonProperty("name") - String name(); - - @JsonProperty("bucketPrefix") - Optional bucketPrefix(); - - - @JsonProperty("stsRoleArnClient") - Optional stsRoleArnClient(); - - @JsonProperty("stsRoleArnHub") - Optional stsRoleArnHub(); - - @JsonProperty("stsEndpoint") - Optional stsEndpoint(); - - @JsonProperty("region") - Optional region(); - - @JsonProperty(value = "regions") - Optional> regions(); - - - @JsonProperty(value = "withPathStyleAccessEnabled", defaultValue = "false", required = false) - Optional withPathStyleAccessEnabled(); - - @JsonProperty("s3Endpoint") - Optional s3Endpoint(); - - @JsonProperty("jwe") - VaultJWEBackend jwe(); - - -} - - diff --git a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageConfigDto.java b/backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageConfigDto.java deleted file mode 100644 index 28f344991..000000000 --- a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageConfigDto.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.cryptomator.hub.api.cipherduck; - -import com.amazonaws.regions.Regions; - -import java.util.Arrays; -import java.util.List; -import java.util.Optional; - -public record StorageConfigDto( - String id, - String name, - Optional bucketPrefix, - Optional stsRoleArnClient, - Optional stsRoleArnHub, - Optional stsEndpoint, - Optional region, - Optional> regions, - VaultJWEBackend jwe, - Optional withPathStyleAccessEnabled, - Optional s3Endpoint - -) implements StorageConfig { - public StorageConfigDto(final StorageConfig s, final VaultJWEBackend jwe) { - // workaround for defaultValue in JSONPrroperty not working as expected - this(s.id(), s.name(), s.bucketPrefix(), s.stsRoleArnClient(), s.stsRoleArnHub(), s.stsEndpoint(), - s.region().isPresent() ? s.region() : Optional.of("us-east-1"), - s.regions().isPresent() ? s.regions() : Optional.of(Arrays.stream(Regions.values()).map(r -> r.getName()).toList()), - jwe, s.withPathStyleAccessEnabled(), s.s3Endpoint()); - } - -} diff --git a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageProfileDto.java b/backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageProfileDto.java new file mode 100644 index 000000000..5482f2063 --- /dev/null +++ b/backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageProfileDto.java @@ -0,0 +1,258 @@ +package org.cryptomator.hub.api.cipherduck; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +public class StorageProfileDto { + // TODO auto-generate in DB - + // clients will use this as vendor in profile and provider in vault bookmark + @JsonProperty(value = "id", required = true) + String id; + + @JsonProperty(value = "name", required = true) + String name; + + @JsonProperty("bucketPrefix") + String bucketPrefix; + + @JsonProperty("stsRoleArnClient") + String stsRoleArnClient; + + @JsonProperty("stsRoleArnHub") + String stsRoleArnHub; + + @JsonProperty("stsEndpoint") + String stsEndpoint; + + @JsonProperty("region") + String region; + + @JsonProperty(value = "regions") + List regions; + + + @JsonProperty(value = "withPathStyleAccessEnabled", defaultValue = "false") + Boolean withPathStyleAccessEnabled; + + @JsonProperty("scheme") + String scheme; + + @JsonProperty("hostname") + String hostname; + + @JsonProperty("port") + Integer port; + + @JsonProperty(value = "protocol", required = true) + String protocol; + + + + // (2) protocol withtings (go into bookmark's custom properties) -> 5 + @JsonProperty(value = "oauthClientId") + String oauthClientId; // injected from hub config + + @JsonProperty(value = "oauthTokenUrl") + String oauthTokenUrl; // injected from hub config + + @JsonProperty(value = "oauthAuthorizationUrl") + String oauthAuthorizationUrl; // injected from hub config + + + // (3) boookmark custom properties -> 5 + @JsonProperty(value = "stsRoleArn") + String stsRoleArn; // X + + @JsonProperty(value = "stsRoleArn2") + String stsRoleArn2; // X + + // TODO default? + @JsonProperty(value = "stsDurationSeconds") + Integer stsDurationSeconds; // X + + @JsonProperty(value = "oAuthTokenExchangeAudience") + String oAuthTokenExchangeAudience; // injected from hub config + + + public String id() { + return id; + } + + public StorageProfileDto withId(String id) { + this.id = id; + return this; + } + + public String name() { + return name; + } + + public StorageProfileDto withName(String name) { + this.name = name; + return this; + } + + public String bucketPrefix() { + return bucketPrefix; + } + + public StorageProfileDto withBucketPrefix(String bucketPrefix) { + this.bucketPrefix = bucketPrefix; + return this; + } + + public String stsRoleArnClient() { + return stsRoleArnClient; + } + + public StorageProfileDto withStsRoleArnClient(String stsRoleArnClient) { + this.stsRoleArnClient = stsRoleArnClient; + return this; + } + + public String stsRoleArnHub() { + return stsRoleArnHub; + } + + public StorageProfileDto withStsRoleArnHub(String stsRoleArnHub) { + this.stsRoleArnHub = stsRoleArnHub; + return this; + } + + public String stsEndpoint() { + return stsEndpoint; + } + + public StorageProfileDto withStsEndpoint(String stsEndpoint) { + this.stsEndpoint = stsEndpoint; + return this; + } + + public String region() { + return region; + } + + public StorageProfileDto withRegion(String region) { + this.region = region; + return this; + } + + public List regions() { + return regions; + } + + public StorageProfileDto withRegions(List regions) { + this.regions = regions; + return this; + } + + public Boolean withPathStyleAccessEnabled() { + return withPathStyleAccessEnabled; + } + + public StorageProfileDto withWithPathStyleAccessEnabled(Boolean withPathStyleAccessEnabled) { + this.withPathStyleAccessEnabled = withPathStyleAccessEnabled; + return this; + } + + public String scheme() { + return scheme; + } + + public StorageProfileDto setScheme(String scheme) { + this.scheme = scheme; + return this; + } + + public String hostname() { + return hostname; + } + + public StorageProfileDto setHostname(String hostname) { + this.hostname = hostname; + return this; + } + + public Integer port() { + return port; + } + + public StorageProfileDto setPort(Integer port) { + this.port = port; + return this; + } + + public String protocol() { + return protocol; + } + + public StorageProfileDto withProtocol(String protocol) { + this.protocol = protocol; + return this; + } + + + public String oauthClientId() { + return oauthClientId; + } + + public StorageProfileDto withOauthClientId(String oauthClientId) { + this.oauthClientId = oauthClientId; + return this; + } + + public String oauthTokenUrl() { + return oauthTokenUrl; + } + + public StorageProfileDto withOauthTokenUrl(String oauthTokenUrl) { + this.oauthTokenUrl = oauthTokenUrl; + return this; + } + + public String oauthAuthorizationUrl() { + return oauthAuthorizationUrl; + } + + public StorageProfileDto withOauthAuthorizationUrl(String oauthAuthorizationUrl) { + this.oauthAuthorizationUrl = oauthAuthorizationUrl; + return this; + } + + public String stsRoleArn() { + return stsRoleArn; + } + + public StorageProfileDto withStsRoleArn(String stsRoleArn) { + this.stsRoleArn = stsRoleArn; + return this; + } + + public String stsRoleArn2() { + return stsRoleArn2; + } + + public StorageProfileDto withStsRoleArn2(String stsRoleArn2) { + this.stsRoleArn2 = stsRoleArn2; + return this; + } + + public Integer stsDurationSeconds() { + return stsDurationSeconds; + } + + public StorageProfileDto withStsDurationSeconds(Integer stsDurationSeconds) { + this.stsDurationSeconds = stsDurationSeconds; + return this; + } + + public String oAuthTokenExchangeAudience() { + return oAuthTokenExchangeAudience; + } + + public StorageProfileDto withoAuthTokenExchangeAudience(String oAuthTokenExchangeAudience) { + this.oAuthTokenExchangeAudience = oAuthTokenExchangeAudience; + return this; + } +} diff --git a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageProfileResource.java b/backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageProfileResource.java new file mode 100644 index 000000000..de2f0687a --- /dev/null +++ b/backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageProfileResource.java @@ -0,0 +1,59 @@ +package org.cryptomator.hub.api.cipherduck; + +import jakarta.annotation.security.PermitAll; +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; + +import java.util.List; + +@Path("/storageprofile") +public class StorageProfileResource { + + + @Inject + CipherduckConfig cipherduckConfig; + + + @PUT + @Path("/") + @PermitAll + @Consumes(MediaType.APPLICATION_JSON) + public void uploadStorageProfile(StorageProfileDto c) { + cipherduckConfig.inMemoryStorageConfigs.add(c); + } + + + @GET + @Path("/") +// @RolesAllowed("user") + @PermitAll + @Produces(MediaType.APPLICATION_JSON) + @Transactional + @Operation(summary = "get configs for storage backends", description = "get list of configs for storage backends") + @APIResponse(responseCode = "200", description = "uploaded storage configuration") + public List getStorageProfiles() { + for (StorageProfileDto storageProfileDto : cipherduckConfig.inMemoryStorageConfigs) { + // inject OAuth endpoints if STS + if (storageProfileDto.stsRoleArn() != null) { + storageProfileDto + .withOauthAuthorizationUrl(cipherduckConfig.authEndpoint()) + .withOauthTokenUrl(cipherduckConfig.tokenEndpoint()) + .withOauthClientId(cipherduckConfig.keycloakClientIdCryptomator()) + .withoAuthTokenExchangeAudience(cipherduckConfig.keycloakClientIdCryptomatorVaults()) + ; + } + } + + return cipherduckConfig.inMemoryStorageConfigs; + } + + +} diff --git a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageResource.java b/backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageResource.java index a4cc15748..5ac233644 100644 --- a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageResource.java +++ b/backend/src/main/java/org/cryptomator/hub/api/cipherduck/StorageResource.java @@ -30,9 +30,6 @@ public class StorageResource { private static final Logger LOG = Logger.getLogger(StorageResource.class); - @Inject - BackendsConfig backendsConfig; - @Inject SyncerConfig syncerConfig; @@ -52,15 +49,15 @@ public class StorageResource { @Operation(summary = "creates bucket and policy", description = "creates an S3 bucket and uploads policy for it.") @APIResponse(responseCode = "200", description = "uploaded storage configuration") @APIResponse(responseCode = "400", description = "Could not create bucket") - public Response createBucket(@PathParam("vaultId") UUID vaultId, StorageDto dto) { + public Response createBucket(@PathParam("vaultId") UUID vaultId, StorageDto storage) { - final Map storageConfigs = backendsConfig.backends().stream().collect(Collectors.toMap(StorageConfig::id, Function.identity())); - final StorageConfig storageConfig = storageConfigs.get(dto.storageConfigId()); + final Map storageConfigs = cipherduckConfig.inMemoryStorageConfigs.stream().collect(Collectors.toMap(StorageProfileDto::id, Function.identity())); + final StorageProfileDto storageProfile = storageConfigs.get(storage.storageConfigId()); // N.B. if the bucket already exists, this will fail, so we do not prevent calling this method several times. - makeS3Bucket(storageConfig, dto); + makeS3Bucket(storageProfile, storage); - keycloakPrepareVault(syncerConfig, vaultId.toString(), storageConfig, jwt.getSubject(), cipherduckConfig.keycloakClientIdCryptomatorVaults()); + keycloakPrepareVault(syncerConfig, vaultId.toString(), storageProfile, jwt.getSubject(), cipherduckConfig.keycloakClientIdCryptomatorVaults()); return Response.created(URI.create(".")).build(); } diff --git a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/VaultJWEBackend.java b/backend/src/main/java/org/cryptomator/hub/api/cipherduck/VaultJWEBackend.java deleted file mode 100644 index a59b762cd..000000000 --- a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/VaultJWEBackend.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.cryptomator.hub.api.cipherduck; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.Optional; - -public interface VaultJWEBackend { - // (1) bookmark properties -> 7 - @JsonProperty("protocol") - Optional protocol(); - - @JsonProperty("provider") - Optional provider(); - - @JsonProperty("hostname") - Optional hostname(); - - @JsonProperty("port") - Optional port(); - - @JsonProperty("defaultPath") - Optional defaultPath(); - - @JsonProperty("nickname") - Optional nickname(); - - @JsonProperty("uuid") - Optional uuid(); - - // (2) protocol settings (go into bookmark's custom properties) -> 5 - @JsonProperty("oAuthAuthorizationUrl") - Optional oauthAuthorizationUrl(); - - @JsonProperty("oAuthTokenUrl") - Optional oauthTokenUrl(); - - @JsonProperty("oAuthClientId") - Optional oauthClientId(); - - @JsonProperty("stsEndpoint") - Optional stsEndpoint(); - - @JsonProperty("region") - Optional region(); - - // (3) boookmark custom properties -> 5 - @JsonProperty("stsRoleArn") - Optional stsRoleArn(); - - @JsonProperty("stsRoleArn2") - Optional stsRoleArn2(); - - @JsonProperty("stsDurationSeconds") - Optional stsDurationSeconds(); - - @JsonProperty("parentUUID") - Optional parentUUID(); - - @JsonProperty("oAuthTokenExchangeAudience") - Optional oAuthTokenExchangeAudience(); - - // (4) keychain credentials -> 2 - @JsonProperty("username") - Optional username(); - - @JsonProperty("password") - Optional password(); - - // (5) misc -> 1 - @JsonProperty("automaticAccessGrant") - Optional automaticAccessGrant(); -} diff --git a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/VaultJWEBackendDto.java b/backend/src/main/java/org/cryptomator/hub/api/cipherduck/VaultJWEBackendDto.java index fea3210c8..c2de925fe 100644 --- a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/VaultJWEBackendDto.java +++ b/backend/src/main/java/org/cryptomator/hub/api/cipherduck/VaultJWEBackendDto.java @@ -2,36 +2,19 @@ import java.util.Optional; + +// TODO we actually don't need it here - only for code generation in client public record VaultJWEBackendDto( // (1) bookmark properties -> 7 - Optional protocol, - Optional provider, - Optional hostname, - Optional port, + String provider, // references vendor in backend config Optional defaultPath, Optional nickname, Optional uuid, - - // (2) protocol settings (go into bookmark's custom properties) -> 5 - Optional oauthClientId, - Optional oauthTokenUrl, - Optional oauthAuthorizationUrl, - Optional stsEndpoint, + // if overridden in bookmark Optional region, - // (3) boookmark custom properties -> 5 - Optional stsRoleArn, - - Optional stsRoleArn2, - - Optional stsDurationSeconds, - - Optional parentUUID, - - Optional oAuthTokenExchangeAudience, - // (4) keychain credentials -> 2 Optional username, @@ -40,20 +23,20 @@ public record VaultJWEBackendDto( // (5) misc -> 1 Optional automaticAccessGrant -) implements VaultJWEBackend { - - - public VaultJWEBackendDto(VaultJWEBackend s, final Optional oAuthAuthorizationUrl, final Optional oAuthTokenUrl, final Optional oAuthClientId, final Optional oAuthTokenExchangeAudience, final String hubId) { - this( - s.protocol(), s.provider(), s.hostname(), s.port(), s.defaultPath(), s.nickname(), s.uuid(), oAuthClientId, oAuthTokenUrl, oAuthAuthorizationUrl, s.stsEndpoint(), s.region(), - s.stsRoleArn(), - s.stsRoleArn2(), - s.stsDurationSeconds(), - Optional.of(hubId), - oAuthTokenExchangeAudience, - s.username(), - s.password(), - s.automaticAccessGrant() - ); - } +) { + + +// public VaultJWEBackendDto(VaultJWEBackend s, final Optional oAuthAuthorizationUrl, final Optional oAuthTokenUrl, final Optional oAuthClientId, final Optional oAuthTokenExchangeAudience, final String hubId) { +// this( +// s.protocol(), s.provider(), s.defaultPath(), s.nickname(), s.uuid(), s.hostname(), s.port(), oAuthClientId, oAuthTokenUrl, oAuthAuthorizationUrl, s.stsEndpoint(), s.region(), +// s.stsRoleArn(), +// s.stsRoleArn2(), +// s.stsDurationSeconds(), +// Optional.of(hubId), +// oAuthTokenExchangeAudience, +// s.username(), +// s.password(), +// s.automaticAccessGrant() +// ); +// } } diff --git a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/storage/S3Storage.java b/backend/src/main/java/org/cryptomator/hub/api/cipherduck/storage/S3Storage.java index aa44f9bb9..9c3119304 100644 --- a/backend/src/main/java/org/cryptomator/hub/api/cipherduck/storage/S3Storage.java +++ b/backend/src/main/java/org/cryptomator/hub/api/cipherduck/storage/S3Storage.java @@ -10,7 +10,7 @@ import jakarta.ws.rs.ClientErrorException; import jakarta.ws.rs.core.Response; import org.apache.commons.io.IOUtils; -import org.cryptomator.hub.api.cipherduck.StorageConfig; +import org.cryptomator.hub.api.cipherduck.StorageProfileDto; import org.cryptomator.hub.api.cipherduck.StorageDto; import java.io.ByteArrayInputStream; @@ -20,20 +20,20 @@ public class S3Storage { public static void makeS3Bucket( - final StorageConfig storageConfig, + final StorageProfileDto storageConfig, final StorageDto dto ) { - final String bucketName = storageConfig.bucketPrefix().get() + dto.vaultId(); + final String bucketName = storageConfig.bucketPrefix() + dto.vaultId(); // https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/java/example_code/s3/src/main/java/aws/example/s3/CreateBucket.java final String region = dto.region(); AmazonS3ClientBuilder s3Builder = AmazonS3ClientBuilder .standard() .withCredentials(new AWSStaticCredentialsProvider(new BasicSessionCredentials(dto.awsAccessKey(), dto.awsSecretKey(), dto.sessionToken()))); - if (storageConfig.stsEndpoint().isPresent()) { + if (storageConfig.stsEndpoint()!=null) { s3Builder = s3Builder - .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(storageConfig.stsEndpoint().get(), region)) - .withPathStyleAccessEnabled(storageConfig.withPathStyleAccessEnabled().orElse(false)); + .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(storageConfig.stsEndpoint(), region)) + .withPathStyleAccessEnabled(storageConfig.withPathStyleAccessEnabled() != null ?storageConfig.withPathStyleAccessEnabled() : false); } else if (region != null) { s3Builder = s3Builder.withRegion(region); } diff --git a/backend/src/main/java/org/cryptomator/hub/cipherduck/KeycloakGrantAccessToVault.java b/backend/src/main/java/org/cryptomator/hub/cipherduck/KeycloakGrantAccessToVault.java index a59dfd4d8..3d012b778 100644 --- a/backend/src/main/java/org/cryptomator/hub/cipherduck/KeycloakGrantAccessToVault.java +++ b/backend/src/main/java/org/cryptomator/hub/cipherduck/KeycloakGrantAccessToVault.java @@ -4,7 +4,7 @@ import jakarta.ws.rs.core.Response; import org.cryptomator.hub.SyncerConfig; import org.cryptomator.hub.api.VaultResource; -import org.cryptomator.hub.api.cipherduck.StorageConfig; +import org.cryptomator.hub.api.cipherduck.StorageProfileDto; import org.cryptomator.hub.entities.Group; import org.cryptomator.hub.entities.Vault; import org.jboss.logging.Logger; @@ -19,7 +19,6 @@ import org.keycloak.representations.idm.RoleRepresentation; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -31,7 +30,7 @@ public class KeycloakGrantAccessToVault { private static final Logger LOG = Logger.getLogger(KeycloakGrantAccessToVault.class); - public static void keycloakPrepareVault(final SyncerConfig syncerConfig, final String vaultId, final StorageConfig storageConfig, final String userOrGroupId, final String clientId) { + public static void keycloakPrepareVault(final SyncerConfig syncerConfig, final String vaultId, final StorageProfileDto storageConfig, final String userOrGroupId, final String clientId) { // N.B. quarkus has no means to provide empty string as value, interpreted as no value, see https://github.com/quarkusio/quarkus/issues/2765 // TODO review better solution than using sentinel string "empty"? @@ -45,8 +44,8 @@ public static void keycloakPrepareVault(final SyncerConfig syncerConfig, final S // https://www.keycloak.org/docs-api/21.1.1/rest-api final RealmResource realm = keycloak.realm(syncerConfig.getKeycloakRealm()); - final boolean minio = storageConfig.jwe().stsRoleArn().isPresent() && storageConfig.jwe().stsRoleArn2().isEmpty(); - final boolean aws = storageConfig.jwe().stsRoleArn().isPresent() && storageConfig.jwe().stsRoleArn2().isPresent(); + final boolean minio = storageConfig.stsRoleArn()!=null && storageConfig.stsRoleArn2() ==null; + final boolean aws = storageConfig.stsRoleArn() !=null && storageConfig.stsRoleArn2() != null; ClientScopeResource clientScopeResource = realm.clientScopes().get(vaultId); diff --git a/frontend/src/common/backend.ts b/frontend/src/common/backend.ts index 36b101fed..adb1269d0 100644 --- a/frontend/src/common/backend.ts +++ b/frontend/src/common/backend.ts @@ -218,7 +218,7 @@ export type ConfigDto = { apiLevel: number; } -export type BackendsConfigDto = { +export type StorageProfileDto = { hubId: string; backends: StorageConfig[]; } @@ -441,9 +441,9 @@ class StorageService { return axiosAuth.put(`/storage/${vaultId}/`, dto); } } -class BackendsConfigService { - public async get(): Promise { - return axiosAuth.get('/backendsconfig/') +class StorageProfileService { + public async get(): Promise { + return axiosAuth.get('/storageprofile/') .then(response => response.data); } } @@ -473,7 +473,7 @@ const services = { // / start cipherduck extension ,storage: new StorageService() - ,backendsconfig: new BackendsConfigService() + ,storageprofiles: new StorageProfileService() ,config: new ConfigService() // \ end cipherduck extension }; diff --git a/frontend/src/components/CreateVault.vue b/frontend/src/components/CreateVault.vue index 9d3198ee8..16e10c19d 100644 --- a/frontend/src/components/CreateVault.vue +++ b/frontend/src/components/CreateVault.vue @@ -439,9 +439,9 @@ async function initialize() { state.value = State.EnterVaultDetails; } // / start cipherduck extension - const backendsconfig = await backend.backendsconfig.get(); - hubId.value = backendsconfig.hubId; - backends.value = backendsconfig.backends; + const storageprofile = await backend.storageprofile.get(); + hubId.value = storageprofile.hubId; + backends.value = storageprofile.backends; selectedBackend.value = backends.value[0]; setRegionsOnSelectStorage(selectedBackend.value); selectedRegion.value = selectedBackend.value.region;