diff --git a/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryComp.java b/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryComp.java index 9e46f7bbf..1967cfc99 100644 --- a/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryComp.java +++ b/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryComp.java @@ -107,7 +107,7 @@ public class StoreCategoryComp extends SimpleRegionBuilder { return new LabelGraphic.IconGraphic("mdomz-settings"); } - if (!DataStorage.get().supportsSync() + if (!DataStorage.get().syncEnabled() || (!category.getCategory().canShare())) { return new LabelGraphic.IconGraphic("mdi2g-git"); } @@ -155,7 +155,7 @@ public class StoreCategoryComp extends SimpleRegionBuilder { var dragOver = new SimpleBooleanProperty(); var dragIntoIndicator = createDragIntoIndicator(dragOver); - var showStatus = hover.or(new SimpleBooleanProperty(DataStorage.get().supportsSync())) + var showStatus = hover.or(new SimpleBooleanProperty(DataStorage.get().syncEnabled())) .or(showing) .or(focus); var h = new HorizontalComp(List.of( diff --git a/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryConfigComp.java b/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryConfigComp.java index e113856a4..0015de7d7 100644 --- a/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryConfigComp.java +++ b/app/src/main/java/io/xpipe/app/hub/category/StoreCategoryConfigComp.java @@ -86,7 +86,7 @@ public class StoreCategoryConfigComp extends SimpleRegionBuilder { var options = new OptionsBuilder(); var specialCategorySync = !wrapper.getCategory().canShare(); - var syncDisable = !DataStorage.get().supportsSync() + var syncDisable = !DataStorage.get().syncEnabled() || ((sync.getValue() == null || !sync.getValue()) && !wrapper.getCategory().canShare()); options.title("sync") diff --git a/app/src/main/java/io/xpipe/app/hub/entry/StoreEntryBadge.java b/app/src/main/java/io/xpipe/app/hub/entry/StoreEntryBadge.java index f1d60d850..1c138b266 100644 --- a/app/src/main/java/io/xpipe/app/hub/entry/StoreEntryBadge.java +++ b/app/src/main/java/io/xpipe/app/hub/entry/StoreEntryBadge.java @@ -167,7 +167,7 @@ public interface StoreEntryBadge { return null; } - return of("mdi2c-console", s).withCopyAction(); + return of("mdi2c-console", s); } static StoreEntryBadge ofKey(String s) { diff --git a/app/src/main/java/io/xpipe/app/hub/list/StoreIdentitiesIntroComp.java b/app/src/main/java/io/xpipe/app/hub/list/StoreIdentitiesIntroComp.java index ad6cc2d1a..696d90ded 100644 --- a/app/src/main/java/io/xpipe/app/hub/list/StoreIdentitiesIntroComp.java +++ b/app/src/main/java/io/xpipe/app/hub/list/StoreIdentitiesIntroComp.java @@ -22,7 +22,7 @@ public class StoreIdentitiesIntroComp extends SimpleRegionBuilder { top.setButtonDefault(true); top.setButtonGraphic(new LabelGraphic.IconGraphic("mdi2p-play-circle")); top.setButtonAction(() -> { - var canSync = DataStorage.get().supportsSync(); + var canSync = DataStorage.get().syncEnabled(); var prov = canSync ? DataStoreProvider.byId("syncedIdentity").orElseThrow() : DataStoreProvider.byId("localIdentity").orElseThrow(); diff --git a/app/src/main/java/io/xpipe/app/issue/ErrorEventFactory.java b/app/src/main/java/io/xpipe/app/issue/ErrorEventFactory.java index 4ade7243a..3148f2669 100644 --- a/app/src/main/java/io/xpipe/app/issue/ErrorEventFactory.java +++ b/app/src/main/java/io/xpipe/app/issue/ErrorEventFactory.java @@ -6,6 +6,7 @@ import io.xpipe.app.util.OsType; import java.nio.file.AccessDeniedException; import java.nio.file.FileSystemException; +import java.nio.file.InvalidPathException; import java.nio.file.NoSuchFileException; import java.util.*; @@ -114,6 +115,10 @@ public class ErrorEventFactory { b.expected(); } + if (t instanceof InvalidPathException ipe) { + b.description("Invalid file path: " + ipe.getMessage()); + } + return b; } } diff --git a/app/src/main/java/io/xpipe/app/secret/EncryptionPrincipal.java b/app/src/main/java/io/xpipe/app/secret/EncryptionPrincipal.java index 63aa36e14..5a8fb6e04 100644 --- a/app/src/main/java/io/xpipe/app/secret/EncryptionPrincipal.java +++ b/app/src/main/java/io/xpipe/app/secret/EncryptionPrincipal.java @@ -5,6 +5,32 @@ import javax.crypto.SecretKey; public interface EncryptionPrincipal { + static EncryptionPrincipal inaccessible() { + var dummyId = UUID.fromString("73e2d533-6fa4-497e-87a8-24da4fdf4d63"); + return new EncryptionPrincipal() { + + @Override + public UUID getUuid() { + return dummyId; + } + + @Override + public String getName() { + return "inaccessible"; + } + + @Override + public boolean isAccessible() { + return false; + } + + @Override + public SecretKey getSecretKey() { + throw new UnsupportedOperationException(); + } + }; + } + static EncryptionPrincipal getTargetPrincipal(EncryptionPrincipal principal) { if (!principal.isAccessible()) { return principal; diff --git a/app/src/main/java/io/xpipe/app/storage/DataStorage.java b/app/src/main/java/io/xpipe/app/storage/DataStorage.java index 1fadcfdfd..dcb9436d6 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStorage.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStorage.java @@ -132,7 +132,7 @@ public abstract class DataStorage { } private void dispose() { - save(true); + save(true, true); var finalizing = false; for (DataStoreEntry entry : getStoreEntries()) { // Prevent blocking of shutdown @@ -202,7 +202,7 @@ public abstract class DataStorage { localIdentities.get().setParentCategory(ALL_IDENTITIES_CATEGORY_UUID); } - if (supportsSync()) { + if (syncEnabled()) { var sharedIdentities = getStoreCategoryIfPresent(SYNCED_IDENTITIES_CATEGORY_UUID); if (sharedIdentities.isEmpty()) { var cat = DataStoreCategory.createNew( @@ -267,9 +267,9 @@ public abstract class DataStorage { public abstract void saveAsync(); - public abstract void save(boolean dispose); + public abstract void save(boolean dispose, boolean forceSync); - public abstract boolean supportsSync(); + public abstract boolean syncEnabled(); public boolean shouldSync(DataStoreCategory category) { // Don't sync lone identities category diff --git a/app/src/main/java/io/xpipe/app/storage/DataStorageSecret.java b/app/src/main/java/io/xpipe/app/storage/DataStorageSecret.java index 2c07403a4..a49ccfc03 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStorageSecret.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStorageSecret.java @@ -207,6 +207,10 @@ public class DataStorageSecret { newEntries.add(newEntry); } + if (newEntries.equals(entries)) { + return this; + } + return new DataStorageSecret(newEntries, secret); } diff --git a/app/src/main/java/io/xpipe/app/storage/DataStoreAccessScope.java b/app/src/main/java/io/xpipe/app/storage/DataStoreAccessScope.java index acb04f86f..93c62794f 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStoreAccessScope.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStoreAccessScope.java @@ -7,6 +7,7 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; +import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -15,6 +16,14 @@ import java.util.stream.Collectors; @ToString public class DataStoreAccessScope { + public static DataStoreAccessScope merge(List scopes) { + var matching = DataStorageAccessHandler.getInstance().getAllEncryptionPrincipals().stream() + .filter(encryptionPrincipal -> { + return scopes.stream().allMatch(s -> s.getPrincipals().contains(encryptionPrincipal)); + }).collect(Collectors.toSet()); + return !matching.isEmpty() ? of(matching) : DataStoreAccessScope.of(Set.of(EncryptionPrincipal.inaccessible())); + } + public static DataStoreAccessScope getTargetScope(DataStoreAccessScope scope) { var newPrincipals = scope.getPrincipals().stream() .map(encryptionPrincipal -> EncryptionPrincipal.getTargetPrincipal(encryptionPrincipal)) @@ -32,11 +41,6 @@ public class DataStoreAccessScope { Set.of(DataStorageAccessHandler.getInstance().getEncryptAllPrincipal())); } - public static DataStoreAccessScope current() { - var handler = DataStorageAccessHandler.getInstance(); - return new DataStoreAccessScope(handler.getCurrentEncryptionPrincipals()); - } - public static DataStoreAccessScope of(Set encryptionPrincipals) { return new DataStoreAccessScope(encryptionPrincipals); } diff --git a/app/src/main/java/io/xpipe/app/storage/DataStoreEntry.java b/app/src/main/java/io/xpipe/app/storage/DataStoreEntry.java index 97afcdedb..d87922cfb 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStoreEntry.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStoreEntry.java @@ -370,8 +370,7 @@ public class DataStoreEntry extends DataStorageElement { } // Check whether we need to write the node due to external changes - var scope = store instanceof AccessScopeStore s && s.getAccessScope() != null - ? s.getAccessScope() + var scope = store instanceof AccessScopeStore s ? s.getAccessScope() : DataStoreAccessScope.encryption(); var shouldEncrypt = (encryptIfRestricted && scope.isAccessRestricted()) || AppPrefs.get().encryptAllVaultData().get(); @@ -425,7 +424,7 @@ public class DataStoreEntry extends DataStorageElement { public DataStoreAccessScope getAccessScope() { try { - if (getStore() instanceof AccessScopeStore s && s.getAccessScope() != null) { + if (getStore() instanceof AccessScopeStore s) { return s.getAccessScope(); } } catch (Exception ignored) { @@ -768,7 +767,7 @@ public class DataStoreEntry extends DataStorageElement { DataStoreAccessScope newAccessScope = null; try { - if (newStore instanceof AccessScopeStore u && u.getAccessScope() != null) { + if (newStore instanceof AccessScopeStore u) { newAccessScope = u.getAccessScope(); } } catch (Exception ignored) { diff --git a/app/src/main/java/io/xpipe/app/storage/DataStoreEntryNode.java b/app/src/main/java/io/xpipe/app/storage/DataStoreEntryNode.java index 80aa05dca..2ff9fa154 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStoreEntryNode.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStoreEntryNode.java @@ -70,15 +70,17 @@ public class DataStoreEntryNode { return null; } - var scope = entry.getAccessScope(); - var shouldEncrypt = (encryptIfRestricted && scope.isAccessRestricted()) + var currentScope = enc.getSecret() != null ? enc.getSecret().getScope() : DataStoreAccessScope.vault(); + var targetScope = DataStoreAccessScope.getTargetScope(entry.getAccessScope()); + + var shouldEncrypt = (encryptIfRestricted && targetScope.isAccessRestricted()) || AppPrefs.get().encryptAllVaultData().get(); var encryptionChange = shouldEncrypt && !enc.isEncrypted() || !shouldEncrypt && enc.isEncrypted(); - var scopeTargetChange = !DataStoreAccessScope.getTargetScope(scope).equals(scope); + var scopeTargetChange = !targetScope.equals(currentScope); var valueChange = !getValue().equals(newValue); if (encryptionChange || valueChange || scopeTargetChange) { return new DataStoreEntryNode<>( - shouldEncrypt ? EncryptedValue.of(newValue, scope) : EncryptedValue.ofRaw(newValue), false); + shouldEncrypt ? EncryptedValue.of(newValue, targetScope) : EncryptedValue.ofRaw(newValue), false); } else { return this; } @@ -99,25 +101,27 @@ public class DataStoreEntryNode { } if (getValue() instanceof AccessScopeStore s - && (s.getAccessScope() == null || !s.getAccessScope().isAccessible())) { + && !s.getAccessScope().isAccessible()) { return this; } T newValue = getValue() instanceof AccessScopeStore s ? (T) s.withUpdatedPrincipals() : getValue(); if (newValue instanceof AccessScopeStore s - && (s.getAccessScope() == null || !s.getAccessScope().isAccessible())) { + && !s.getAccessScope().isAccessible()) { return this; } - var scope = entry.getAccessScope(); - var shouldEncrypt = (encryptIfRestricted && scope.isAccessRestricted()) + var currentScope = enc.getSecret() != null ? enc.getSecret().getScope() : DataStoreAccessScope.vault(); + var targetScope = DataStoreAccessScope.getTargetScope(entry.getAccessScope()); + + var shouldEncrypt = (encryptIfRestricted && targetScope.isAccessRestricted()) || AppPrefs.get().encryptAllVaultData().get(); var encryptionChange = shouldEncrypt && !enc.isEncrypted() || !shouldEncrypt && enc.isEncrypted(); - var scopeTargetChange = !DataStoreAccessScope.getTargetScope(scope).equals(scope); + var scopeTargetChange = !targetScope.equals(currentScope); var valueChange = !getValue().equals(newValue); if (encryptionChange || scopeTargetChange || valueChange) { return new DataStoreEntryNode<>( - shouldEncrypt ? EncryptedValue.of(newValue, scope) : EncryptedValue.ofRaw(newValue), false); + shouldEncrypt ? EncryptedValue.of(newValue, targetScope) : EncryptedValue.ofRaw(newValue), false); } else { return this; } diff --git a/app/src/main/java/io/xpipe/app/storage/ImpersistentStorage.java b/app/src/main/java/io/xpipe/app/storage/ImpersistentStorage.java index f178b1e97..cb75a1da9 100644 --- a/app/src/main/java/io/xpipe/app/storage/ImpersistentStorage.java +++ b/app/src/main/java/io/xpipe/app/storage/ImpersistentStorage.java @@ -48,10 +48,10 @@ public class ImpersistentStorage extends DataStorage { public void saveAsync() {} @Override - public synchronized void save(boolean dispose) {} + public synchronized void save(boolean dispose, boolean forceSync) {} @Override - public boolean supportsSync() { + public boolean syncEnabled() { return false; } diff --git a/app/src/main/java/io/xpipe/app/storage/StandardStorage.java b/app/src/main/java/io/xpipe/app/storage/StandardStorage.java index 42c053b23..4e21030c0 100644 --- a/app/src/main/java/io/xpipe/app/storage/StandardStorage.java +++ b/app/src/main/java/io/xpipe/app/storage/StandardStorage.java @@ -392,11 +392,11 @@ public class StandardStorage extends DataStorage { } ThreadHelper.runAsync(() -> { - save(false); + save(false, false); }); } - public void save(boolean dispose) { + public void save(boolean dispose, boolean forceSync) { try { // If another save operation is in progress, we have to wait on dispose // Otherwise the application may quit and kill the daemon thread that is performing the other save operation @@ -408,8 +408,11 @@ public class StandardStorage extends DataStorage { return; } - // We don't need to wait on normal saves though - if (!dispose && !busyIo.tryLock()) { + // Wait for sync lock + if (forceSync) { + busyIo.lock(); + } else if (!dispose && !busyIo.tryLock()) { + // We don't need to wait on normal saves though saveQueued = true; return; } @@ -426,8 +429,10 @@ public class StandardStorage extends DataStorage { if (syncEnabled) { GlobalTimer.delay( () -> { - if (saveActive.get()) { - AppLayoutModel.get().showQueueEntry(queueEntry, null, false); + synchronized (queueEntry) { + if (saveActive.get()) { + AppLayoutModel.get().showQueueEntry(queueEntry, null, false); + } } }, Duration.ofSeconds(5)); @@ -499,8 +504,10 @@ public class StandardStorage extends DataStorage { disposed = true; } - saveActive.set(false); - queueEntry.hide(); + synchronized (queueEntry) { + saveActive.set(false); + queueEntry.hide(); + } busyIo.unlock(); if (!dispose && saveQueued) { @@ -545,7 +552,7 @@ public class StandardStorage extends DataStorage { } @Override - public boolean supportsSync() { + public boolean syncEnabled() { return dataStorageSyncHandler.supportsSync(); } diff --git a/app/src/main/java/io/xpipe/app/store/AccessScopeDependencyStore.java b/app/src/main/java/io/xpipe/app/store/AccessScopeDependencyStore.java new file mode 100644 index 000000000..be7afcb6d --- /dev/null +++ b/app/src/main/java/io/xpipe/app/store/AccessScopeDependencyStore.java @@ -0,0 +1,18 @@ +package io.xpipe.app.store; + +import io.xpipe.app.storage.DataStoreAccessScope; + +public interface AccessScopeDependencyStore extends AccessScopeStore, DependentDataStore { + + default DataStoreAccessScope getAccessScope() { + var deps = getDependencies(); + var scopes = deps.stream().map(ref -> ref.getStore() instanceof AccessScopeStore s ? s.getAccessScope() : null) + .filter(s -> s != null) + .toList(); + if (scopes.isEmpty()) { + return DataStoreAccessScope.encryption(); + } + + return DataStoreAccessScope.merge(scopes); + } +} diff --git a/app/src/main/java/io/xpipe/app/util/AppJacksonModule.java b/app/src/main/java/io/xpipe/app/util/AppJacksonModule.java index c48e4da0d..b80caf89e 100644 --- a/app/src/main/java/io/xpipe/app/util/AppJacksonModule.java +++ b/app/src/main/java/io/xpipe/app/util/AppJacksonModule.java @@ -466,7 +466,7 @@ public class AppJacksonModule extends SimpleModule { var e = DataStorage.get() .getStoreEntryIfPresent(id) .filter(dataStoreEntry -> dataStoreEntry.getValidity() != DataStoreEntry.Validity.LOAD_FAILED - || !dataStoreEntry.getStoreNode().isAccessible()) + || (dataStoreEntry.getStoreNode() != null && !dataStoreEntry.getStoreNode().isAccessible())) .orElse(null); if (e == null) { return null; diff --git a/app/src/main/resources/io/xpipe/app/resources/style/scrollbar.css b/app/src/main/resources/io/xpipe/app/resources/style/scrollbar.css index a2a44f9b7..042bc9b27 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/scrollbar.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/scrollbar.css @@ -39,9 +39,9 @@ the calculation of the default scrollbar skin. Without this, the min height will be too small */ -.root .scroll-bar >.decrement-button >.decrement-arrow { +.root .scroll-bar:vertical >.decrement-button >.decrement-arrow { -fx-padding: 0 3 0 4; } -.root .scroll-bar >.increment-button >.increment-arrow { +.root .scroll-bar:vertical >.increment-button >.increment-arrow { -fx-padding: 0 3 0 4; } diff --git a/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css b/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css index 00bc987b3..06056ad7a 100644 --- a/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css +++ b/app/src/main/resources/io/xpipe/app/resources/style/store-entry-comp.css @@ -298,7 +298,14 @@ -fx-background-radius: 4; -fx-font-size: 1.0em; -fx-padding: 2 5 2 5; - -fx-background-color: -color-bg-default-transparent; +} + +.root:light .store-entry-badge { + -fx-background-color: derive(-color-bg-default-transparent, -5%); +} + +.root:dark .store-entry-badge { + -fx-background-color: derive(-color-bg-default-transparent, 5%); } .store-entry-badge:focused { diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/IdentityChoiceBuilder.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/IdentityChoiceBuilder.java index 1ade08792..c38885977 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/IdentityChoiceBuilder.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/IdentityChoiceBuilder.java @@ -77,7 +77,7 @@ public class IdentityChoiceBuilder { private void addSyncCheckListener() { identity.addListener((observable, oldValue, newValue) -> { - if (DataStorage.get().supportsSync() + if (DataStorage.get().syncEnabled() && syncedBase.getValue() && newValue instanceof IdentityValue.Ref r && r.unwrap() instanceof LocalIdentityStore) { diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/LocalIdentityConvertHubLeafProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/LocalIdentityConvertHubLeafProvider.java index f703c31d1..6cb9cf005 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/LocalIdentityConvertHubLeafProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/LocalIdentityConvertHubLeafProvider.java @@ -28,7 +28,7 @@ public class LocalIdentityConvertHubLeafProvider implements HubLeafProvider o) { - return DataStorage.get().supportsSync() && !MultiIdentityStore.isExclusivelyHeld(o.asNeeded()); + return DataStorage.get().syncEnabled() && !MultiIdentityStore.isExclusivelyHeld(o.asNeeded()); } @Override diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/LocalIdentityStore.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/LocalIdentityStore.java index deb13a4b4..53ec74d9b 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/LocalIdentityStore.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/LocalIdentityStore.java @@ -1,14 +1,18 @@ package io.xpipe.ext.base.identity; +import io.xpipe.app.beacon.BeaconAuthMethod; import io.xpipe.app.identity.NoIdentityStrategy; import io.xpipe.app.identity.SshIdentityStrategy; import io.xpipe.app.identity.UsernameStrategy; import io.xpipe.app.secret.EncryptedValue; import io.xpipe.app.secret.SecretNoneStrategy; import io.xpipe.app.secret.SecretRetrievalStrategy; +import io.xpipe.app.storage.DataStoreAccessScope; import io.xpipe.app.storage.DataStoreEntryRef; import com.fasterxml.jackson.annotation.JsonTypeName; +import io.xpipe.app.store.AccessScopeStore; +import io.xpipe.app.store.DataStore; import lombok.EqualsAndHashCode; import lombok.ToString; import lombok.Value; @@ -23,12 +27,26 @@ import java.util.List; @Value @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class LocalIdentityStore extends IdentityStore { +public class LocalIdentityStore extends IdentityStore implements AccessScopeStore { String username; EncryptedValue password; EncryptedValue sshIdentity; + @Override + public DataStoreAccessScope getAccessScope() { + return DataStoreAccessScope.encryption(); + } + + @Override + public DataStore withUpdatedPrincipals() { + return LocalIdentityStore.builder() + .username(username) + .password(password != null ? password.withUpdatedPrincipals() : null) + .sshIdentity(sshIdentity != null ? sshIdentity.withUpdatedPrincipals() : null) + .build(); + } + @Override public String toSummary() { var user = getUsername().hasUser() diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/LocalIdentityStoreProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/LocalIdentityStoreProvider.java index 2d916882b..c2b58362c 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/LocalIdentityStoreProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/LocalIdentityStoreProvider.java @@ -5,10 +5,7 @@ import io.xpipe.app.identity.NoIdentityStrategy; import io.xpipe.app.identity.SshIdentityStrategyChoiceConfig; import io.xpipe.app.platform.OptionsBuilder; import io.xpipe.app.platform.OptionsChoiceBuilder; -import io.xpipe.app.secret.EncryptedValue; -import io.xpipe.app.secret.SecretNoneStrategy; -import io.xpipe.app.secret.SecretRetrievalStrategy; -import io.xpipe.app.secret.SecretStrategyChoiceConfig; +import io.xpipe.app.secret.*; import io.xpipe.app.storage.*; import io.xpipe.app.store.DataStore; import io.xpipe.app.util.*; @@ -39,9 +36,10 @@ public class LocalIdentityStoreProvider extends IdentityStoreProvider { var pass = new SimpleObjectProperty<>(st.getPassword()); var identity = new SimpleObjectProperty<>(st.getSshIdentity()); + var current = DataStoreAccessScope.encryption(); var sshIdentityChoiceConfig = SshIdentityStrategyChoiceConfig.builder() .allowKeyFileSync(false) - .scopeCheck(() -> DataStoreAccessScope.current()) + .scopeCheck(() -> current) .build(); var passwordChoice = OptionsChoiceBuilder.builder() @@ -70,12 +68,12 @@ public class LocalIdentityStoreProvider extends IdentityStoreProvider { .password( st.getEncryptedPassword() != null ? st.getEncryptedPassword().with(pass.get()) - : EncryptedValue.of(pass.get(), DataStoreAccessScope.current())) + : EncryptedValue.of(pass.get(), current)) .sshIdentity( st.getEncryptedSshIdentity() != null ? st.getEncryptedSshIdentity() .with(identity.get()) - : EncryptedValue.of(identity.get(), DataStoreAccessScope.current())) + : EncryptedValue.of(identity.get(), current)) .build(); }, store) @@ -84,9 +82,10 @@ public class LocalIdentityStoreProvider extends IdentityStoreProvider { @Override public DataStore defaultStore(DataStoreCategory category) { + var current = DataStoreAccessScope.encryption(); return LocalIdentityStore.builder() - .password(EncryptedValue.of(new SecretNoneStrategy(), DataStoreAccessScope.current())) - .sshIdentity(EncryptedValue.of(new NoIdentityStrategy(), DataStoreAccessScope.current())) + .password(EncryptedValue.of(new SecretNoneStrategy(), current)) + .sshIdentity(EncryptedValue.of(new NoIdentityStrategy(), current)) .build(); } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentityConvertHubLeafProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentityConvertHubLeafProvider.java index 871678186..53717d029 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentityConvertHubLeafProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentityConvertHubLeafProvider.java @@ -31,7 +31,7 @@ public class MultiIdentityConvertHubLeafProvider implements HubLeafProvider o) { - return DataStorage.get().supportsSync() + return DataStorage.get().syncEnabled() && o.getStore().areAnyChildrenLocal() && o.getStore().areAllIdentitiesAccessible() && !o.getStore().hasNestedMultiIdentities(); diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentityStore.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentityStore.java index 1682141bd..2c3e8d13b 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentityStore.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentityStore.java @@ -53,6 +53,11 @@ public class MultiIdentityStore extends IdentityStore Boolean exclusive; DataStoreAccessScope accessScope; + @Override + public DataStoreAccessScope getAccessScope() { + return accessScope != null ? accessScope : DataStoreAccessScope.encryption(); + } + @Override public String toSummary() { var selected = getSelected(); diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/SyncedIdentityStore.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/SyncedIdentityStore.java index 21c49a51c..6d391b7fa 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/SyncedIdentityStore.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/SyncedIdentityStore.java @@ -32,12 +32,15 @@ import java.util.List; public class SyncedIdentityStore extends IdentityStore implements AccessScopeStore { String username; - // We can encrypt it with only the vault key as - // per user stores are additionally encrypted on the entry level EncryptedValue password; EncryptedValue sshIdentity; DataStoreAccessScope accessScope; + @Override + public DataStoreAccessScope getAccessScope() { + return accessScope != null ? accessScope : DataStoreAccessScope.encryption(); + } + @Override public String toSummary() { var user = getUsername().hasUser() diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/SyncedIdentityStoreProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/SyncedIdentityStoreProvider.java index 802d23af9..d215c26fd 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/SyncedIdentityStoreProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/SyncedIdentityStoreProvider.java @@ -1,6 +1,7 @@ package io.xpipe.ext.base.identity; import io.xpipe.app.core.AppI18n; +import io.xpipe.app.core.AppProperties; import io.xpipe.app.hub.creation.StoreCreationModel; import io.xpipe.app.hub.entry.StoreEntryWrapper; import io.xpipe.app.identity.KeyFileStrategy; @@ -10,6 +11,7 @@ import io.xpipe.app.identity.SshIdentityStrategyChoiceConfig; import io.xpipe.app.platform.OptionsBuilder; import io.xpipe.app.platform.OptionsChoiceBuilder; import io.xpipe.app.platform.Validator; +import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.prefs.DataStorageAccessType; import io.xpipe.app.secret.*; import io.xpipe.app.storage.*; @@ -27,7 +29,7 @@ public class SyncedIdentityStoreProvider extends IdentityStoreProvider { @Override public boolean allowCreation() { - return DataStorage.get().supportsSync(); + return AppPrefs.get().enableGitStorage().get(); } @Override diff --git a/ext/base/src/main/java/io/xpipe/ext/base/store/SyncConfigHubLeafProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/store/SyncConfigHubLeafProvider.java index a0c292cee..5bc8cab49 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/store/SyncConfigHubLeafProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/store/SyncConfigHubLeafProvider.java @@ -33,7 +33,7 @@ public class SyncConfigHubLeafProvider implements HubLeafProvider o) { - return DataStorage.get().supportsSync() && !o.get().getProvider().isSyncable(o.get()); + return DataStorage.get().syncEnabled() && !o.get().getProvider().isSyncable(o.get()); } @Override diff --git a/lang/strings/translations_en.properties b/lang/strings/translations_en.properties index 73eb141ca..eed0280b8 100644 --- a/lang/strings/translations_en.properties +++ b/lang/strings/translations_en.properties @@ -2081,7 +2081,8 @@ certificateRole=Role name certificateRoleDescription=The role with which to sign the public key testConfig=Test configuration testConfigDescription=Verify that the configuration works -applyingVaultChanges=Applying vault changes ... +#force +applyingVaultChanges=Applying vault encryption changes ... allowExternalApiRequests=Allow external API requests allowExternalApiRequestsDescription=By default, the API server is only listening on localhost. If you want to accept connections from outside localhost as well, you can enable this option. certificateRenewCommand=Certificate renewal command @@ -2337,3 +2338,6 @@ vaultMigratedTitle=Vault migration vaultMigratedContent=Your vault was migrated. Note that the migration removed any kind of vault users/groups, so you will have to recreate them in the settings menu. vaultMigratedGitContent=Your vault was migrated. Note that the migration removed any kind of vault users/groups, so you will have to recreate them in the settings menu.\n\nOnce you have done this, you can also reenable the git git sync as well. orderDisabledNotice=Reordering not possible in current sort mode +skipAllRoles=Skip all roles +skipRole=Skip role +disableRole=Disable role