Skip to content

Commit

Permalink
Refactoring (R3) storage profile service persistence (#4 #6).
Browse files Browse the repository at this point in the history
  • Loading branch information
chenkins committed Jan 10, 2024
1 parent 1be0876 commit 20a6262
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 45 deletions.
1 change: 1 addition & 0 deletions backend/setup/aws_static/aws_static_profile.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"id": "72736C19-283C-49D3-80A5-AB74B5202543",
"name": "AWS S3 static",
"region": "eu-central-1",
"protocol": "s3"
Expand Down
1 change: 1 addition & 0 deletions backend/setup/aws_sts/aws_sts_profile.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"id": "844BD517-96D4-4787-BCFA-238E103149F6",
"name": "AWS S3 STS",
"bucketPrefix": "cipherduck",
"stsRoleArnHub": "arn:aws:iam::930717317329:role/cipherduck-createbucket",
Expand Down
2 changes: 1 addition & 1 deletion backend/setup/minio_static/minio_static_profile.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id":"71B910E0-2ECC-46DE-A871-8DB28549677E",
"name": "MinIO S3 static",
"s3Endpoint": "http://minio:9000",
"protocol": "s3",
"withPathStyleAccessEnabled": "true",
"hostname": "minio",
Expand Down
1 change: 1 addition & 0 deletions backend/setup/minio_sts/minio_sts_profile.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"id": "732D43FA-3716-46C4-B931-66EA5405EF1C",
"name": "MinIO S3 STS",
"bucketPrefix": "cipherduck",
"region": "eu-central-1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ public class CipherduckConfig {
@Inject
OidcConfigurationMetadata oidcConfData;

public List<StorageProfileDto> inMemoryStorageConfigs = new ArrayList<>();

String replacePrefix(String str, String prefix, String replacement) {
int index = str.indexOf(prefix);
if (index == 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,29 @@
import com.amazonaws.regions.Regions;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

import java.util.Arrays;
import java.util.List;
import java.util.UUID;


// TODO https://github.com/shift7-ch/cipherduck-hub/issues/4 (R3) update documentation
public class StorageProfileDto {
@Entity
@Table(name = "storage_profile")
public class StorageProfileDto extends PanacheEntityBase {
public enum Protocol {
s3("s3"),
s3sts("s3-sts");
private final String protocol;

private Protocol(final String protocol){
private Protocol(final String protocol) {
this.protocol = protocol;
}

@JsonValue
public String getProtocol() {
return protocol;
Expand All @@ -27,53 +34,52 @@ public String getProtocol() {
}


// (1) bucket creation
// TODO https://github.com/shift7-ch/cipherduck-hub/issues/4 (R3) auto-generate in DB

@Id
@Column(name = "id", nullable = false)
@JsonProperty(value = "id", required = true)
UUID id; // clients will use this as vendor in profile and provider in vault bookmark

@JsonProperty(value = "name", required = true)
String name;

@JsonProperty(value = "bucketPrefix")
String bucketPrefix; // not required if we do not create bucket (non-STS case)

@JsonProperty("stsRoleArnClient")
String stsRoleArnClient; // not required if we do not create bucket (non-STS case)
// (1) bucket creation, template upload and client profile
@JsonProperty("scheme")
String scheme; // defaults to AWS

@JsonProperty("stsRoleArnHub") // not required if we do not create bucket (non-STS case)
String stsRoleArnHub;
@JsonProperty("hostname")
String hostname; // defaults to AWS

@JsonProperty("stsEndpoint")
String stsEndpoint; // not required if we do not create bucket (non-STS case)
@JsonProperty("port")
Integer port; // defaults to AWS

@JsonProperty(value = "region")
String region = "us-east-1"; // default region selected in the frontend/client

@JsonProperty(value = "regions")
List<String> regions = Arrays.stream(Regions.values()).map(r -> r.getName()).toList(); // defaults to full list injected

List<String> regions = Arrays.stream(Regions.values()).map(r -> r.getName()).toList(); // defaults to full AWS list

@JsonProperty(value = "withPathStyleAccessEnabled")
Boolean withPathStyleAccessEnabled = false; // not required if we do not create bucket (non-STS case)
Boolean withPathStyleAccessEnabled = false;


// (2) bucket creation and client profile
@JsonProperty("scheme")
String scheme; // defaults to AWS
// (2) bucket creation only (i.e. STS-case)
@JsonProperty(value = "bucketPrefix")
String bucketPrefix;

@JsonProperty("hostname")
String hostname; // defaults to AWS
@JsonProperty("stsRoleArnClient")
String stsRoleArnClient;

@JsonProperty("port") // defaults to AWS
Integer port;
@JsonProperty("stsRoleArnHub")
String stsRoleArnHub;

@JsonProperty(value = "protocol")
Protocol protocol = Protocol.s3sts;
@JsonProperty("stsEndpoint")
String stsEndpoint; // defaults to AWS


// (3) client profile
// (3a) client profile attributes
@JsonProperty(value = "protocol")
Protocol protocol = Protocol.s3sts;
@JsonProperty(value = "oauthClientId")
String oauthClientId; // injected from hub config if STS

Expand All @@ -84,7 +90,7 @@ public String getProtocol() {
String oauthAuthorizationUrl; // injected from hub config if STS


// (3) client profile custom properties
// (3b) client profile custom properties
@JsonProperty(value = "stsRoleArn")
String stsRoleArn; // token exchange

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
package org.cryptomator.hub.api.cipherduck;

import jakarta.annotation.security.PermitAll;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.ClientErrorException;
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 jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.hibernate.exception.ConstraintViolationException;

import java.net.URI;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

@Path("/storageprofile")
public class StorageProfileResource {
Expand All @@ -25,33 +30,40 @@ public class StorageProfileResource {

@PUT
@Path("/")
// TODO https://github.com/shift7-ch/cipherduck-hub/issues/4 (R3) restrict to admin after refctoring is done
// @RolesAllowed("admin")
@PermitAll
@Transactional
@Consumes(MediaType.APPLICATION_JSON)
public void uploadStorageProfile(StorageProfileDto c) {
c.withId(UUID.randomUUID());
// TODO https://github.com/shift7-ch/cipherduck-hub/issues/4 (R3) StorageProfileDto is not very transparent from the admin perspective - which fields are required for S3 static and which for S3 STS - we need some kind of meta-model (which could also be used for configuring which fields to show/hide in the ui).
public Response uploadStorageProfile(StorageProfileDto c) {
// TODO https://github.com/shift7-ch/cipherduck-hub/issues/4 (R3) protocol-specific validations?
switch (c.protocol) {
case s3:
break;
case s3sts:
break;
}
cipherduckConfig.inMemoryStorageConfigs.add(c);

try {
c.persistAndFlush();
return Response.created(URI.create(".")).build();
} catch (ConstraintViolationException e) {
throw new ClientErrorException(Response.Status.CONFLICT, e);
}
}


@GET
@Path("/")
// TODO https://github.com/shift7-ch/cipherduck-hub/issues/4 (R3) restrict to admin after refctoring is done
// @RolesAllowed("admin")
// TODO https://github.com/shift7-ch/cipherduck-hub/issues/4 (R3) restrict to user after refctoring is done
// @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<StorageProfileDto> getStorageProfiles() {
for (StorageProfileDto storageProfileDto : cipherduckConfig.inMemoryStorageConfigs) {
List<StorageProfileDto> storageProfiles = StorageProfileDto.findAll().<StorageProfileDto>stream().collect(Collectors.toList());
for (StorageProfileDto storageProfileDto : storageProfiles) {
// inject OAuth endpoints if STS
if (storageProfileDto.stsRoleArn() != null) {
storageProfileDto
Expand All @@ -62,7 +74,20 @@ public List<StorageProfileDto> getStorageProfiles() {
;
}
}
return cipherduckConfig.inMemoryStorageConfigs;
return storageProfiles;
}

// TODO https://github.com/shift7-ch/cipherduck-hub/issues/4 (R3) refactor into meta service for getting which values are required for which protocol?
@GET
@Path("/meta")
@RolesAllowed("admin")
@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 VaultJWEBackendDto getVaultJWEBackendDto(final StorageProfileDto.Protocol protocol) {
// N.B. temporary workaround to have VaultJWEBackendDto exposed in openapi.json for now....
return null;
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public class StorageResource {
@APIResponse(responseCode = "400", description = "Could not create bucket")
public Response createBucket(@PathParam("vaultId") UUID vaultId, StorageDto storage) {

final Map<UUID, StorageProfileDto> storageConfigs = cipherduckConfig.inMemoryStorageConfigs.stream().collect(Collectors.toMap(StorageProfileDto::id, Function.identity()));
final Map<UUID, StorageProfileDto> storageConfigs = StorageProfileDto.findAll().<StorageProfileDto>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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
public record VaultJWEBackendDto(

@JsonProperty(value = "provider", required = true)
// references vendor in VaultJWEBackendDto (coming from id in StorageProfileDto)
// references id in StorageProfileDto (aka. vendor in client profile)
String provider,
@JsonProperty(value = "defaultPath", required = true)
String defaultPath,
@JsonProperty(value = "nickname", required = true)
String nickname,
@JsonProperty(value = "uuid", required = true)
String uuid,
String uuid, // vault UUID, will be used as bookmark UUID
@JsonProperty(value = "region", required = true)
String region,

Expand Down
1 change: 1 addition & 0 deletions backend/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ quarkus.hibernate-orm.database.globally-quoted-identifiers=true
quarkus.flyway.migrate-at-start=true
quarkus.flyway.locations=classpath:org/cryptomator/hub/flyway
%dev.quarkus.flyway.ignore-missing-migrations=true
%dev.quarkus.flyway.validate-migration-naming=true
# https://quarkus.io/guides/databases-dev-services
# https://stackoverflow.com/questions/44654216/correct-way-to-install-psql-without-full-postgres-on-macos
# psql -h localhost -p 54082 -U quarkus -d quarkus
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
CREATE TABLE "storage_profile"
(
"id" UUID NOT NULL,
"name" VARCHAR,

-- (1) bucket creation, template upload and client profile
"scheme" VARCHAR,
"hostname" VARCHAR,
"port" INT4,
"region" VARCHAR,
"regions" text[],
"withPathStyleAccessEnabled"
bool,

-- (2) bucket creation only (i.e. STS-case)
"bucketPrefix" VARCHAR,
"stsRoleArnClient" VARCHAR,
"stsRoleArnHub" VARCHAR,
"stsEndpoint" VARCHAR,


-- (3) client profile
-- (3a) client profile attributes
"protocol" VARCHAR NOT NULL,
"oauthClientId" VARCHAR,
"oauthTokenUrl" VARCHAR,
"oauthAuthorizationUrl"
VARCHAR,
-- (3b) client profile custom properties
"stsRoleArn" VARCHAR,
"stsRoleArn2" VARCHAR,
"stsDurationSeconds" INT4,
"oAuthTokenExchangeAudience"
VARCHAR,

CONSTRAINT "STORAGE_PROFILE_PK" PRIMARY KEY ("id")
-- TODO https://github.com/shift7-ch/cipherduck-hub/issues/4 (R3) regions in region contraint?
);
2 changes: 1 addition & 1 deletion frontend/src/components/CreateVault.vue
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ async function uploadVaultTemplate() {
const client = new S3Client({
region: selectedRegion.value,
endpoint: endpoint,
forcePathStyle: true,
forcePathStyle: selectedBackend.value.withPathStyleAccessEnabled,
credentials:{
accessKeyId: vaultAccessKeyId.value,
secretAccessKey: vaultSecretKey.value
Expand Down

0 comments on commit 20a6262

Please sign in to comment.