Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
134 commits
Select commit Hold shift + click to select a range
4be72b6
Require controller constructor argument.
dkocher Jun 5, 2025
da18bdc
Add implementation using alerts.
dkocher Jun 11, 2025
2755a7c
Save account key in keychain.
dkocher Jun 12, 2025
42dac15
Add implementation using login callback.
dkocher Jun 13, 2025
cbd7b91
Attach sources.
dkocher Jun 13, 2025
adf2a9b
Require controller constructor argument.
dkocher Jun 13, 2025
65e7efe
Lookup device setup prompt from login feature.
dkocher Jun 13, 2025
505d20f
Add custom buttons.
dkocher Jun 16, 2025
2954905
Remove confirmation input.
dkocher Jun 16, 2025
511de65
Implement home interface.
dkocher Jun 17, 2025
67e7a13
Fix initialization.
dkocher Jun 17, 2025
ac514cf
Fix default return value for fallback to parent profile.
dkocher Jun 17, 2025
a913446
Extract variable.
dkocher Jun 17, 2025
40a390d
Explicitly require PKCE.
dkocher Jun 19, 2025
30e6980
Add password store as constructor parameter.
dkocher Jul 2, 2025
4274e3b
Add find feature for vaults.
dkocher Jul 11, 2025
7db4b1f
Add null check for feature.
dkocher Jul 12, 2025
50b847d
Add details to exception.
dkocher Aug 10, 2025
acf1856
Logging.
dkocher Aug 26, 2025
8677628
Fix logger name.
dkocher Aug 29, 2025
0015cd7
Deny read/write access to root in preflight checks.
dkocher Aug 29, 2025
80a9d3c
Delete redundant override.
dkocher Aug 29, 2025
55aebb5
Set nickname of vault as display name.
dkocher Aug 29, 2025
4716add
Add missing listener notification for chunks retrieved.
dkocher Aug 29, 2025
f958e82
Remove logger.
dkocher Aug 29, 2025
fce0259
Fail with exception when storage does not support feature.
dkocher Aug 29, 2025
0270b44
Disable custom versioning option.
dkocher Aug 29, 2025
5ac4790
Allow multiple attempts to recover with account key until canceled by…
dkocher Aug 31, 2025
22bb26d
Use default application icon in prompt.
dkocher Aug 31, 2025
3ed5042
Add null check for token expiry.
dkocher Aug 31, 2025
f5764a0
Standard wrapping for error handler to limit retries.
dkocher Sep 1, 2025
2389723
Remove redundant override of registry.
dkocher Sep 1, 2025
f87fd8a
Use storage specific comparison service.
dkocher Sep 1, 2025
d131a87
Extract class.
dkocher Sep 1, 2025
51c2956
Prune imports.
dkocher Sep 1, 2025
f191521
Return storage profile name as default nickname.
dkocher May 12, 2025
d6e9109
Must not cache storage profile with reference to Hub connection.
dkocher Sep 2, 2025
8a5ebce
No need to register profile.
dkocher Sep 2, 2025
5412f4f
Reduce required parameters.
dkocher Sep 2, 2025
2a57408
No custom cleanup.
dkocher Sep 2, 2025
6e55170
Simplify assertion.
dkocher Sep 2, 2025
14e4520
Extract class.
dkocher Sep 2, 2025
716d282
Extract method.
dkocher Sep 2, 2025
357eb1c
Remove redundant override.
dkocher Sep 2, 2025
3dddc43
Delete unused property.
dkocher Sep 4, 2025
d589135
Use factory.
dkocher Sep 4, 2025
97171e3
Set vault ID as vendor key in profile.
dkocher Sep 4, 2025
fa61288
Log failures.
dkocher Sep 4, 2025
7ba91ea
Add overloaded constructor.
dkocher Sep 4, 2025
b4fcf21
Refactor to load vaults only once with short-lived user keys.
dkocher Sep 4, 2025
69eba9a
Resolve deprecated usages.
dkocher Sep 1, 2025
154156b
Default path is bucket name.
dkocher Sep 4, 2025
cca05cf
Logging.
dkocher Sep 4, 2025
91b323c
Revert "Set vault ID as vendor key in profile."
dkocher Sep 4, 2025
edc25b1
Logging.
dkocher Sep 4, 2025
c86b6e4
Handle load failure.
dkocher Sep 11, 2025
7963bb4
Add required argument.
dkocher Aug 29, 2025
5bb434d
Fix test. Attributes retrieved for bucket have region set.
dkocher Aug 29, 2025
d0c91fb
Implement location service as combo of storage profile configuration …
dkocher May 12, 2025
88e05b8
Log failure.
dkocher Jun 26, 2025
fb91071
Adtop updated interface.
dkocher Sep 1, 2025
b343090
Default to allow configuration of transfer acceleration.
dkocher Sep 4, 2025
34ff3dd
Numbered formatting.
dkocher Sep 4, 2025
b58cd4b
Add constructors.
dkocher Sep 4, 2025
b39e4d7
Javadoc.
dkocher Sep 4, 2025
0da90c9
Add accessor.
dkocher Sep 4, 2025
2194d6a
Allow to create vault.
dkocher Sep 4, 2025
0f05379
Show display name.
dkocher Sep 4, 2025
4e66e13
Use delimiter different from regions.
dkocher Sep 4, 2025
b1e5797
Reuse OAuth credentials.
dkocher Sep 4, 2025
255b1bc
Only reload when expired.
dkocher Sep 9, 2025
01614b6
Logging.
dkocher Sep 9, 2025
ccbdfc9
Skip vault when token exchange fails.
dkocher Sep 9, 2025
51c0885
Rename field.
dkocher Oct 12, 2025
d248046
Pass location including storage profile id and region.
dkocher Oct 12, 2025
9e5c759
Fix return value.
dkocher Oct 12, 2025
9fa6495
Register storage configurations on login.
dkocher Oct 12, 2025
a6dba37
Rename variable.
dkocher Oct 12, 2025
4d9657e
Revert "Fail with exception when storage does not support feature."
dkocher Oct 12, 2025
ff2ff77
Adopt reusable authentication with temporeary credentials assuming ro…
dkocher Oct 12, 2025
ba602db
Add new parameters.
dkocher Oct 13, 2025
056b171
Require test to create bucket for vault.
dkocher Oct 13, 2025
56459f9
Assign bucket.
dkocher Oct 13, 2025
7fca9af
No preloading of buckets.
dkocher Oct 13, 2025
0c67e00
Unused.
dkocher Oct 13, 2025
24ce8d4
Require storage credentials.
dkocher Oct 13, 2025
31f7cae
Extract region name.
dkocher Oct 14, 2025
56ff305
Use cached keys.
dkocher Oct 14, 2025
7b9e652
Empty initialization.
dkocher Oct 14, 2025
a749fe2
Throw failure when attempting to list non-root.
dkocher Oct 14, 2025
bd9da57
Determine access grant options depeding on storage profile.
dkocher Oct 14, 2025
19ff164
Revert "Resolve deprecated usages."
dkocher Oct 14, 2025
a6d0fe3
Set role configurable key.
dkocher Oct 14, 2025
4a06d84
Fix region name.
dkocher Oct 14, 2025
378bf99
Review ARN property names.
dkocher Oct 14, 2025
f3d5159
Add support to create vault from client.
dkocher Oct 14, 2025
35ae8ee
Use provided bucket name when available.
dkocher Oct 14, 2025
44c9327
Handle null.
dkocher Oct 14, 2025
6a2ba62
Create random bucket in tests.
dkocher Oct 14, 2025
e5db877
Workaround #189.
dkocher Oct 14, 2025
92cffe2
Delete unused.
dkocher Oct 14, 2025
852e7b5
No duplicate transcript.
dkocher Oct 15, 2025
0dae67f
Delete test.
dkocher Oct 15, 2025
a5cd29b
Revert "No preloading of buckets."
dkocher Oct 15, 2025
aeef089
Ensure listener notification.
dkocher Oct 15, 2025
5ae6d55
Ignore vaults that fail to load.
dkocher Oct 15, 2025
91e6bcc
Delete unused accessor.
dkocher Oct 16, 2025
682d812
Keep user keys alive.
dkocher Oct 16, 2025
98c3095
Make user keys short-lived.
dkocher Oct 16, 2025
2e36d89
Inline configuration.
dkocher Oct 16, 2025
717850d
Inline configuration.
dkocher Oct 16, 2025
3108aac
Wrap with expiring holders.
dkocher Oct 16, 2025
6526cf8
Obtain hub session as feature.
dkocher Oct 16, 2025
f13cc40
Logging.
dkocher Oct 17, 2025
1d2801d
Adopt interface changes.
dkocher Oct 17, 2025
19a5005
Merge implementations.
dkocher Oct 17, 2025
833f56b
Share OAuth tokens. Resolves #29.
dkocher Oct 17, 2025
f0cbfce
Read cached setting.
dkocher Oct 17, 2025
e098e7c
Extract field.
dkocher Oct 17, 2025
67d76ff
Isolate storage configurations per hub.
dkocher Oct 18, 2025
d07f6b4
Extract builder method.
dkocher Oct 19, 2025
448b3d6
Delete unused.
dkocher Oct 19, 2025
548ee66
Remove duplicate field.
dkocher Oct 19, 2025
a76a555
Remove redundant constructor.
dkocher Oct 19, 2025
a696a75
Use service loader.
dkocher Oct 20, 2025
746b1cb
Fail on missing configuration.
dkocher Oct 20, 2025
7bef746
Move to class.
dkocher Oct 20, 2025
16e832b
Move to class.
dkocher Oct 20, 2025
b03cde1
Remove profile properties to be configured from storage profile confi…
dkocher Oct 20, 2025
8bed1ae
Inject reference to Hub connection/Oauth explicitly. Load storage ses…
dkocher Oct 20, 2025
3dde8a6
Remove option to save account key.
dkocher Oct 24, 2025
e33210f
Add comment on usage of "Role Configurable" boolean for storage profile.
dkocher Oct 24, 2025
9694c95
Remove unused bucket name parameter.
dkocher Oct 24, 2025
6657882
Merge pull request #205 from shift7-ch/issues/204
dkocher Oct 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions hub/src/main/java/cloud/katta/core/DefaultDeviceSetupCallback.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (c) 2025 shift7 GmbH. All rights reserved.
*/

package cloud.katta.core;

import ch.cyberduck.core.Credentials;
import ch.cyberduck.core.Host;
import ch.cyberduck.core.LocaleFactory;
import ch.cyberduck.core.LoginCallback;
import ch.cyberduck.core.LoginOptions;
import ch.cyberduck.core.StringAppender;
import ch.cyberduck.core.exception.LoginCanceledException;

import cloud.katta.model.AccountKeyAndDeviceName;
import cloud.katta.workflows.exceptions.AccessException;

public class DefaultDeviceSetupCallback implements DeviceSetupCallback {

private final LoginCallback prompt;

public DefaultDeviceSetupCallback(final LoginCallback prompt) {
this.prompt = prompt;
}

@Override
public AccountKeyAndDeviceName displayAccountKeyAndAskDeviceName(final Host bookmark, final AccountKeyAndDeviceName accountKeyAndDeviceName) throws AccessException {
try {
final Credentials input = prompt.prompt(bookmark, accountKeyAndDeviceName.accountKey(),
LocaleFactory.localizedString("Account Key", "Hub"),
new StringAppender()
.append(LocaleFactory.localizedString("On first login, every user gets a unique Account Key", "Hub"))
.append(LocaleFactory.localizedString("Your Account Key is required to login from other apps or browsers", "Hub"))
.append(LocaleFactory.localizedString("You can see a list of authorized apps on your profile page", "Hub")).toString(),
new LoginOptions()
.usernamePlaceholder(LocaleFactory.localizedString("Account Key", "Hub"))
// Account key not editable
.user(false)
.passwordPlaceholder(accountKeyAndDeviceName.deviceName())
// Input device name
.password(true)
.keychain(false)
);
return new AccountKeyAndDeviceName()
.withDeviceName(input.getUsername())
.withAccountKey(input.getPassword());
}
catch(LoginCanceledException e) {
throw new AccessException(e);
}
}

@Override
public AccountKeyAndDeviceName askForAccountKeyAndDeviceName(final Host bookmark, final String initialDeviceName) throws AccessException {
try {
final Credentials input = prompt.prompt(bookmark, initialDeviceName,
LocaleFactory.localizedString("Authorization Required", "Hub"),
new StringAppender()
.append(LocaleFactory.localizedString("This is your first login on this device.", "Hub"))
.append(LocaleFactory.localizedString("Your Account Key is required to link this browser to your account.", "Hub")).toString(),
new LoginOptions()
.usernamePlaceholder(LocaleFactory.localizedString("Device Name", "Hub"))
// Customize device name
.user(true)
.passwordPlaceholder(LocaleFactory.localizedString("Account Key", "Hub"))
// Input account key
.password(true)
.keychain(false)
);
return new AccountKeyAndDeviceName()
.withDeviceName(input.getUsername())
.withAccountKey(input.getPassword());
}
catch(LoginCanceledException e) {
throw new AccessException(e);
}
}
}
6 changes: 3 additions & 3 deletions hub/src/main/java/cloud/katta/core/DeviceSetupCallback.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ public interface DeviceSetupCallback {
/**
* Prompt user for device name
*
* @return Device name
* @return Account key and device name
* @throws AccessException Canceled prompt by user
*/
String displayAccountKeyAndAskDeviceName(Host bookmark, AccountKeyAndDeviceName accountKeyAndDeviceName) throws AccessException;
AccountKeyAndDeviceName displayAccountKeyAndAskDeviceName(Host bookmark, AccountKeyAndDeviceName accountKeyAndDeviceName) throws AccessException;

/**
* Prompt user for existing account key
Expand Down Expand Up @@ -50,7 +50,7 @@ default UserKeys generateUserKeys() {

DeviceSetupCallback disabled = new DeviceSetupCallback() {
@Override
public String displayAccountKeyAndAskDeviceName(final Host bookmark, final AccountKeyAndDeviceName accountKeyAndDeviceName) throws AccessException {
public AccountKeyAndDeviceName displayAccountKeyAndAskDeviceName(final Host bookmark, final AccountKeyAndDeviceName accountKeyAndDeviceName) throws AccessException {
throw new AccessException("Disabled");
}

Expand Down
51 changes: 0 additions & 51 deletions hub/src/main/java/cloud/katta/core/DeviceSetupCallbackFactory.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,18 @@

public class UvfMetadataPayloadPasswordCallback extends DisabledPasswordCallback {

private final UvfMetadataPayload payload;
private final String payloadJson;

public UvfMetadataPayloadPasswordCallback(final UvfMetadataPayload payload) {
this.payload = payload;
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 {
try {
return new VaultCredentials(payload.toJSON());
}
catch(JsonProcessingException e) {
throw new LoginCanceledException(e);
}
return new VaultCredentials(payloadJson);
}
}
20 changes: 20 additions & 0 deletions hub/src/main/java/cloud/katta/protocols/hub/HubAwareProfile.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2025 shift7 GmbH. All rights reserved.
*/

package cloud.katta.protocols.hub;

import ch.cyberduck.core.Profile;
import ch.cyberduck.core.Protocol;

import cloud.katta.client.model.ConfigDto;
import cloud.katta.model.StorageProfileDtoWrapper;
import cloud.katta.protocols.hub.serializer.HubConfigDtoDeserializer;
import cloud.katta.protocols.hub.serializer.StorageProfileDtoWrapperDeserializer;

public final class HubAwareProfile extends Profile {

public HubAwareProfile(final Protocol parent, final ConfigDto configDto, final StorageProfileDtoWrapper storageProfile) {
super(parent, new HubConfigDtoDeserializer(configDto, new StorageProfileDtoWrapperDeserializer(storageProfile)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import ch.cyberduck.core.HostPasswordStore;
import ch.cyberduck.core.PasswordCallback;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.preferences.HostPreferences;
import ch.cyberduck.core.preferences.HostPreferencesFactory;
import ch.cyberduck.core.shared.ThreadPoolSchedulerFeature;

import org.apache.logging.log4j.LogManager;
Expand All @@ -18,14 +18,14 @@

import cloud.katta.client.ApiException;
import cloud.katta.client.api.DeviceResourceApi;
import cloud.katta.client.api.StorageProfileResourceApi;
import cloud.katta.client.api.UsersResourceApi;
import cloud.katta.client.api.VaultResourceApi;
import cloud.katta.client.model.Role;
import cloud.katta.client.model.VaultDto;
import cloud.katta.crypto.UserKeys;
import cloud.katta.protocols.hub.exceptions.HubExceptionMappingService;
import cloud.katta.workflows.DeviceKeysServiceImpl;
import cloud.katta.workflows.GrantAccessService;
import cloud.katta.workflows.GrantAccessServiceImpl;
import cloud.katta.workflows.UserKeysServiceImpl;
import cloud.katta.workflows.exceptions.AccessException;
Expand All @@ -36,40 +36,32 @@ public class HubGrantAccessSchedulerService extends ThreadPoolSchedulerFeature<H

private final HubSession session;
private final HostPasswordStore keychain;
private final VaultResourceApi vaults;
private final UsersResourceApi users;
private final DeviceResourceApi devices;
private final GrantAccessService service;

public HubGrantAccessSchedulerService(final HubSession session, final HostPasswordStore keychain) {
this(session, keychain, new VaultResourceApi(session.getClient()), new UsersResourceApi(session.getClient()), new DeviceResourceApi(session.getClient()), new GrantAccessServiceImpl(session));
}

public HubGrantAccessSchedulerService(final HubSession session, final HostPasswordStore keychain, final VaultResourceApi vaults, final UsersResourceApi users, final DeviceResourceApi devices, final GrantAccessService service) {
super(new HostPreferences(session.getHost()).getLong("hub.protocol.scheduler.period"));
super(HostPreferencesFactory.get(session.getHost()).getLong("hub.protocol.scheduler.period"));
this.session = session;
this.keychain = keychain;
this.vaults = vaults;
this.users = users;
this.devices = devices;
this.service = service;
}

@Override
public Host operate(final PasswordCallback callback) throws BackgroundException {
log.info("Scheduler for {}", session.getHost());
try {
final UserKeys userKeys = new UserKeysServiceImpl(users, devices).getUserKeys(session.getHost(), session.getMe(),
final UserKeys userKeys = new UserKeysServiceImpl(
new UsersResourceApi(session.getClient()),
new DeviceResourceApi(session.getClient())).getUserKeys(session.getHost(), session.getMe(),
new DeviceKeysServiceImpl(keychain).getDeviceKeys(session.getHost()));

final List<VaultDto> accessibleVaults = vaults.apiVaultsAccessibleGet(Role.OWNER);

final List<VaultDto> accessibleVaults = new VaultResourceApi(session.getClient()).apiVaultsAccessibleGet(Role.OWNER);
for(final VaultDto accessibleVault : accessibleVaults) {
if(Boolean.TRUE.equals(accessibleVault.getArchived())) {
log.debug("Skip archived vault {}", accessibleVault);
continue;
}
service.grantAccessToUsersRequiringAccessGrant(accessibleVault.getId(), userKeys);
new GrantAccessServiceImpl(
new VaultResourceApi(session.getClient()),
new StorageProfileResourceApi(session.getClient()),
new UsersResourceApi(session.getClient())
).grantAccessToUsersRequiringAccessGrant(accessibleVault.getId(), userKeys);
}
userKeys.destroy();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2025 shift7 GmbH. All rights reserved.
*/

package cloud.katta.protocols.hub;

import ch.cyberduck.core.Credentials;
import ch.cyberduck.core.CredentialsConfigurator;
import ch.cyberduck.core.Host;
import ch.cyberduck.core.HostPasswordStore;
import ch.cyberduck.core.OAuthTokens;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class HubOAuthTokensCredentialsConfigurator implements CredentialsConfigurator {
private static final Logger log = LogManager.getLogger(HubOAuthTokensCredentialsConfigurator.class);

private final HostPasswordStore keychain;
private final Host host;

private OAuthTokens tokens = OAuthTokens.EMPTY;

public HubOAuthTokensCredentialsConfigurator(final HostPasswordStore keychain, final Host host) {
this.keychain = keychain;
this.host = host;
}

@Override
public Credentials configure(final Host host) {
return new Credentials(host.getCredentials()).setOauth(tokens);
}

@Override
public CredentialsConfigurator reload() {
if(tokens.isExpired()) {
log.debug("Reload expired tokens from keychain for {}", host);
tokens = keychain.findOAuthTokens(host);
log.debug("Retrieved tokens {}", tokens);
}
return this;
}
}
5 changes: 5 additions & 0 deletions hub/src/main/java/cloud/katta/protocols/hub/HubProtocol.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,9 @@ public boolean isUsernameConfigurable() {
public boolean isPasswordConfigurable() {
return false;
}

@Override
public VersioningMode getVersioningMode() {
return VersioningMode.storage;
}
}
Loading