Skip to content

Commit d40fa6b

Browse files
committed
Add support to create vault from client.
1 parent 3550e1b commit d40fa6b

File tree

4 files changed

+149
-148
lines changed

4 files changed

+149
-148
lines changed

hub/src/main/java/cloud/katta/protocols/hub/HubUVFVault.java

Lines changed: 77 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,17 @@
44

55
package cloud.katta.protocols.hub;
66

7-
import ch.cyberduck.core.Credentials;
8-
import ch.cyberduck.core.CredentialsConfigurator;
9-
import ch.cyberduck.core.DisabledCancelCallback;
10-
import ch.cyberduck.core.DisabledHostKeyCallback;
11-
import ch.cyberduck.core.DisabledLoginCallback;
12-
import ch.cyberduck.core.Host;
13-
import ch.cyberduck.core.HostUrlProvider;
14-
import ch.cyberduck.core.PasswordCallback;
15-
import ch.cyberduck.core.Path;
16-
import ch.cyberduck.core.PathAttributes;
17-
import ch.cyberduck.core.Protocol;
18-
import ch.cyberduck.core.ProtocolFactory;
19-
import ch.cyberduck.core.Session;
20-
import ch.cyberduck.core.SessionFactory;
7+
import ch.cyberduck.core.*;
218
import ch.cyberduck.core.cryptomator.ContentWriter;
229
import ch.cyberduck.core.cryptomator.UVFVault;
2310
import ch.cyberduck.core.exception.BackgroundException;
11+
import ch.cyberduck.core.exception.ConnectionCanceledException;
2412
import ch.cyberduck.core.exception.InteroperabilityException;
2513
import ch.cyberduck.core.features.AttributesFinder;
2614
import ch.cyberduck.core.features.Directory;
2715
import ch.cyberduck.core.features.Write;
2816
import ch.cyberduck.core.preferences.PreferencesFactory;
17+
import ch.cyberduck.core.preferences.ProxyPreferencesReader;
2918
import ch.cyberduck.core.proxy.ProxyFactory;
3019
import ch.cyberduck.core.s3.S3Session;
3120
import ch.cyberduck.core.ssl.X509KeyManager;
@@ -34,7 +23,6 @@
3423
import ch.cyberduck.core.vault.VaultCredentials;
3524
import ch.cyberduck.core.vault.VaultUnlockCancelException;
3625

37-
import org.apache.http.HttpStatus;
3826
import org.apache.logging.log4j.LogManager;
3927
import org.apache.logging.log4j.Logger;
4028
import org.joda.time.DateTime;
@@ -45,22 +33,16 @@
4533
import java.util.UUID;
4634

4735
import cloud.katta.client.ApiException;
48-
import cloud.katta.client.api.UsersResourceApi;
4936
import cloud.katta.client.api.VaultResourceApi;
5037
import cloud.katta.client.model.UserDto;
5138
import cloud.katta.client.model.VaultDto;
52-
import cloud.katta.crypto.DeviceKeys;
5339
import cloud.katta.crypto.UserKeys;
5440
import cloud.katta.crypto.uvf.UvfMetadataPayload;
5541
import cloud.katta.crypto.uvf.UvfMetadataPayloadPasswordCallback;
5642
import cloud.katta.crypto.uvf.VaultMetadataJWEAutomaticAccessGrantDto;
5743
import cloud.katta.crypto.uvf.VaultMetadataJWEBackendDto;
5844
import cloud.katta.protocols.hub.exceptions.HubExceptionMappingService;
59-
import cloud.katta.workflows.DeviceKeysServiceImpl;
60-
import cloud.katta.workflows.UserKeysServiceImpl;
61-
import cloud.katta.workflows.VaultServiceImpl;
62-
import cloud.katta.workflows.exceptions.AccessException;
63-
import cloud.katta.workflows.exceptions.SecurityFailure;
45+
import cloud.katta.protocols.s3.S3AssumeRoleProtocol;
6446
import com.fasterxml.jackson.core.JsonProcessingException;
6547
import com.nimbusds.jose.JOSEException;
6648

@@ -73,28 +55,76 @@ public class HubUVFVault extends UVFVault {
7355
private static final Logger log = LogManager.getLogger(HubUVFVault.class);
7456

7557
private final UUID vaultId;
58+
private final UvfMetadataPayload vaultMetadata;
7659

7760
/**
7861
* Storage connection only available after loading vault
7962
*/
80-
private Session<?> storage;
63+
private final Session<?> storage;
64+
8165
private final Path home;
82-
private final Credentials credentials;
66+
67+
public HubUVFVault(final HubSession hub, final Path bucket, final HubStorageLocationService.StorageLocation location) throws ConnectionCanceledException {
68+
this(hub, UUID.fromString(new UUIDRandomStringService().random()), bucket, location);
69+
}
70+
71+
/**
72+
* Constructor for factory creating new vault
73+
*
74+
* @param bucket Bucket
75+
*/
76+
public HubUVFVault(final HubSession hub, final UUID vaultId, final Path bucket, final HubStorageLocationService.StorageLocation location) throws ConnectionCanceledException {
77+
this(hub, vaultId, bucket,
78+
UvfMetadataPayload.create()
79+
.withStorage(new VaultMetadataJWEBackendDto()
80+
.provider(location.getProfile())
81+
.defaultPath(bucket.getAbsolute())
82+
.region(location.getRegion())
83+
.nickname(null != bucket.attributes().getDisplayname() ? bucket.attributes().getDisplayname() : "Vault"))
84+
.withAutomaticAccessGrant(new VaultMetadataJWEAutomaticAccessGrantDto()
85+
.enabled(true)
86+
.maxWotDepth(null)));
87+
}
8388

8489
/**
8590
* Open from existing metadata
8691
*
87-
* @param vaultId Vault ID Used to lookup profile
88-
* @param bucket Bucket name
89-
* @param credentials Storage access credentials
92+
* @param vaultId Vault ID Used to lookup profile
9093
*/
91-
public HubUVFVault(final UUID vaultId, final Path bucket, final Credentials credentials) {
94+
public HubUVFVault(final HubSession hub, final UUID vaultId, final UvfMetadataPayload vaultMetadata) throws ConnectionCanceledException {
95+
this(hub, vaultId, new Path(vaultMetadata.storage().getDefaultPath(), EnumSet.of(Path.Type.directory, Path.Type.volume),
96+
new PathAttributes().setDisplayname(vaultMetadata.storage().getNickname())), vaultMetadata);
97+
}
98+
99+
public HubUVFVault(final HubSession hub, final UUID vaultId, final Path bucket, final UvfMetadataPayload vaultMetadata) throws ConnectionCanceledException {
92100
super(bucket);
93101
this.vaultId = vaultId;
102+
this.vaultMetadata = vaultMetadata;
94103
this.home = bucket;
95-
this.credentials = credentials;
104+
105+
final VaultMetadataJWEBackendDto vaultStorageMetadata = vaultMetadata.storage();
106+
final Protocol profile = ProtocolFactory.get().forName(vaultStorageMetadata.getProvider());
107+
log.debug("Loaded profile {} for UVF metadata {}", profile, vaultMetadata);
108+
final Credentials credentials =
109+
hub.getFeature(CredentialsConfigurator.class).reload().configure(hub.getHost());
110+
log.debug("Copy credentials {}", credentials);
111+
if(vaultStorageMetadata.getUsername() != null) {
112+
credentials.setUsername(vaultStorageMetadata.getUsername());
113+
}
114+
if(vaultStorageMetadata.getPassword() != null) {
115+
credentials.setPassword(vaultStorageMetadata.getPassword());
116+
}
117+
final Host storageProvider = new Host(profile, credentials);
118+
storageProvider.setProperty(OAUTH_TOKENEXCHANGE_VAULT, vaultId.toString());
119+
storageProvider.setRegion(vaultStorageMetadata.getRegion());
120+
log.debug("Configured {} for vault {}", storageProvider, this);
121+
this.storage = SessionFactory.create(storageProvider, hub.getFeature(X509TrustManager.class), hub.getFeature(X509KeyManager.class));
96122
}
97123

124+
/**
125+
*
126+
* @return Storage provider configuration
127+
*/
98128
public Session<?> getStorage() {
99129
return storage;
100130
}
@@ -126,32 +156,21 @@ public synchronized void close() {
126156
@Override
127157
public Path create(final Session<?> session, final String region, final VaultCredentials noop) throws BackgroundException {
128158
try {
129-
final HubStorageLocationService.StorageLocation location = HubStorageLocationService.StorageLocation.fromIdentifier(region);
130-
final String storageProfileId = location.getProfile();
131-
final UvfMetadataPayload metadataPayload = UvfMetadataPayload.create()
132-
.withStorage(new VaultMetadataJWEBackendDto()
133-
.provider(storageProfileId)
134-
.defaultPath(home.getAbsolute())
135-
.region(location.getRegion())
136-
.nickname(null != home.attributes().getDisplayname() ? home.attributes().getDisplayname() : "Vault"))
137-
.withAutomaticAccessGrant(new VaultMetadataJWEAutomaticAccessGrantDto()
138-
.enabled(true)
139-
.maxWotDepth(null));
140-
log.debug("Created metadata JWE {}", metadataPayload);
159+
final HubSession hub = HubSession.coerce(session);
160+
log.debug("Created metadata JWE {}", vaultMetadata);
141161
final UvfMetadataPayload.UniversalVaultFormatJWKS jwks = UvfMetadataPayload.createKeys();
142162
final VaultDto vaultDto = new VaultDto()
143163
.id(vaultId)
144-
.name(metadataPayload.storage().getNickname())
164+
.name(vaultMetadata.storage().getNickname())
145165
.description(null)
146166
.archived(false)
147167
.creationTime(DateTime.now())
148-
.uvfMetadataFile(metadataPayload.encrypt(
168+
.uvfMetadataFile(vaultMetadata.encrypt(
149169
String.format("%s/api", new HostUrlProvider(false, true).get(session.getHost())),
150170
vaultId,
151171
jwks.toJWKSet()
152172
))
153173
.uvfKeySet(jwks.serializePublicRecoverykey());
154-
final HubSession hub = (HubSession) session;
155174
// Create vault in Hub
156175
final VaultResourceApi vaultResourceApi = new VaultResourceApi(hub.getClient());
157176
log.debug("Create vault {}", vaultDto);
@@ -165,20 +184,19 @@ public Path create(final Session<?> session, final String region, final VaultCre
165184
vaultResourceApi.apiVaultsVaultIdAccessTokensPost(vaultDto.getId(),
166185
Collections.singletonMap(userDto.getId(), jwks.toOwnerAccessToken().encryptForUser(userKeys.ecdhKeyPair().getPublic())));
167186
// Upload vault template to storage
168-
final Protocol profile = ProtocolFactory.get().forName(storageProfileId);
169-
log.debug("Loaded profile {} for vault {}", profile, this);
170-
final Host bookmark = new Host(profile, credentials);
171-
bookmark.setProperty(OAUTH_TOKENEXCHANGE_VAULT, vaultId.toString());
172-
bookmark.setRegion(location.getRegion());
173-
bookmark.setDefaultPath(home.getAbsolute());
174-
log.debug("Configured {} for vault {}", bookmark, this);
175-
storage = SessionFactory.create(bookmark, session.getFeature(X509TrustManager.class), session.getFeature(X509KeyManager.class));
176187
log.debug("Connect to {}", storage);
188+
final Host configuration = storage.getHost();
189+
// No token exchange with Katta Server
190+
configuration.setProperty(S3AssumeRoleProtocol.OAUTH_TOKENEXCHANGE, null);
191+
// Assume role with policy attached to create vault
192+
configuration.setProperty(S3AssumeRoleProtocol.S3_ASSUMEROLE_ROLEARN_WEBIDENTITY,
193+
new ProxyPreferencesReader(storage.getHost()).getProperty(S3AssumeRoleProtocol.S3_ASSUMEROLE_ROLEARN_CREATE_BUCKET));
194+
// No role chaining when creating vault
195+
configuration.setProperty(S3AssumeRoleProtocol.S3_ASSUMEROLE_ROLEARN_TAG, null);
177196
storage.open(ProxyFactory.get(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback());
178-
storage.login(new DisabledLoginCallback(), new DisabledCancelCallback());
179-
log.debug("Upload vault template to {}", storage);
180197
final Path vault;
181198
if(false) {
199+
log.debug("Upload vault template to {}", storage);
182200
return super.create(storage,
183201
HubStorageLocationService.StorageLocation.fromIdentifier(region).getRegion(), noop);
184202
}
@@ -188,7 +206,7 @@ public Path create(final Session<?> session, final String region, final VaultCre
188206
final TransferStatus status = (new TransferStatus()).setRegion(HubStorageLocationService.StorageLocation.fromIdentifier(region).getRegion());
189207
vault = directory.mkdir(storage._getFeature(Write.class), home, status);
190208

191-
final String hashedRootDirId = metadataPayload.computeRootDirIdHash();
209+
final String hashedRootDirId = vaultMetadata.computeRootDirIdHash();
192210
final Path dataDir = new Path(vault, "d", EnumSet.of(Path.Type.directory));
193211
final Path firstLevel = new Path(dataDir, hashedRootDirId.substring(0, 2), EnumSet.of(Path.Type.directory));
194212
final Path secondLevel = new Path(firstLevel, hashedRootDirId.substring(2), EnumSet.of(Path.Type.directory));
@@ -202,11 +220,11 @@ public Path create(final Session<?> session, final String region, final VaultCre
202220
EnumSet.of(Path.Type.file, Path.Type.vault)), vaultDto.getUvfMetadataFile().getBytes(StandardCharsets.US_ASCII));
203221
// dir.uvf
204222
new ContentWriter(storage).write(new Path(secondLevel, "dir.uvf", EnumSet.of(Path.Type.file)),
205-
metadataPayload.computeRootDirUvf());
223+
vaultMetadata.computeRootDirUvf());
206224
}
207225
return vault;
208226
}
209-
catch(JOSEException | JsonProcessingException | AccessException | SecurityFailure e) {
227+
catch(JOSEException | JsonProcessingException e) {
210228
throw new InteroperabilityException(e.getMessage(), e);
211229
}
212230
catch(ApiException e) {
@@ -223,35 +241,9 @@ public Path create(final Session<?> session, final String region, final VaultCre
223241
@Override
224242
public HubUVFVault load(final Session<?> session, final PasswordCallback prompt) throws BackgroundException {
225243
try {
226-
final HubSession hub = HubSession.coerce(session);
227-
// Find storage configuration in vault metadata
228-
final VaultServiceImpl vaultService = new VaultServiceImpl(hub);
229-
final UvfMetadataPayload vaultMetadata = vaultService.getVaultMetadataJWE(vaultId, hub.getUserKeys());
230-
final Protocol profile = ProtocolFactory.get().forName(vaultMetadata.storage().getProvider());
231-
log.debug("Loaded profile {} for vault {}", profile, this);
232-
final Credentials credentials =
233-
session.getFeature(CredentialsConfigurator.class).reload().configure(session.getHost());
234-
log.debug("Copy credentials {}", credentials);
235-
final VaultMetadataJWEBackendDto vaultStorageMetadata = vaultMetadata.storage();
236-
if(vaultStorageMetadata.getUsername() != null) {
237-
credentials.setUsername(vaultStorageMetadata.getUsername());
238-
}
239-
if(vaultStorageMetadata.getPassword() != null) {
240-
credentials.setPassword(vaultStorageMetadata.getPassword());
241-
}
242-
final Host bookmark = new Host(profile, credentials);
243-
log.debug("Configure bookmark for vault {}", vaultStorageMetadata);
244-
bookmark.setNickname(vaultStorageMetadata.getNickname());
245-
bookmark.setDefaultPath(vaultStorageMetadata.getDefaultPath());
246-
bookmark.setProperty(OAUTH_TOKENEXCHANGE_VAULT, vaultId.toString());
247-
// region as chosen by user upon vault creation (STS) or as retrieved from bucket (permanent)
248-
bookmark.setRegion(vaultStorageMetadata.getRegion());
249-
log.debug("Configured {} for vault {}", bookmark, this);
250-
storage = SessionFactory.create(bookmark, session.getFeature(X509TrustManager.class), session.getFeature(X509KeyManager.class));
251244
log.debug("Connect to {}", storage);
252245
try {
253246
storage.open(ProxyFactory.get(), new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback());
254-
storage.login(new DisabledLoginCallback(), new DisabledCancelCallback());
255247
}
256248
catch(BackgroundException e) {
257249
log.warn("Skip loading vault with failure {} connecting to storage", e.toString());
@@ -265,13 +257,7 @@ public HubUVFVault load(final Session<?> session, final PasswordCallback prompt)
265257
super.load(storage, new UvfMetadataPayloadPasswordCallback(vaultMetadata.toJSON()));
266258
return this;
267259
}
268-
catch(ApiException e) {
269-
if(HttpStatus.SC_FORBIDDEN == e.getCode()) {
270-
throw new VaultUnlockCancelException(this, e);
271-
}
272-
throw new HubExceptionMappingService().map(e);
273-
}
274-
catch(JsonProcessingException | SecurityFailure | AccessException e) {
260+
catch(JsonProcessingException e) {
275261
throw new InteroperabilityException(e.getMessage(), e);
276262
}
277263
}
@@ -280,8 +266,8 @@ public HubUVFVault load(final Session<?> session, final PasswordCallback prompt)
280266
public String toString() {
281267
final StringBuilder sb = new StringBuilder("HubUVFVault{");
282268
sb.append("vaultId=").append(vaultId);
269+
sb.append(", vaultMetadata=").append(vaultMetadata);
283270
sb.append(", home=").append(home);
284-
sb.append(", credentials=").append(credentials);
285271
sb.append('}');
286272
return sb.toString();
287273
}

0 commit comments

Comments
 (0)