Rework [stage]

This commit is contained in:
crschnick
2026-08-19 06:21:59 +00:00
parent a5ebaf91c0
commit 91754e190f
11 changed files with 78 additions and 25 deletions
@@ -1,13 +1,19 @@
package io.xpipe.app.comp.base;
import io.xpipe.app.core.AppFontSizes;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.core.mode.AppOperationMode;
import io.xpipe.app.platform.PlatformThread;
import javafx.beans.binding.Bindings;
import javafx.beans.property.Property;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.Button;
import lombok.Value;
import lombok.experimental.NonFinal;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
@Value
@@ -16,9 +22,7 @@ public class ModalButton {
Runnable action;
boolean close;
boolean defaultButton;
@NonFinal
Consumer<Button> augment;
List<Consumer<Button>> augments = new ArrayList<>();
public ModalButton(String key, Runnable action, boolean close, boolean defaultButton) {
this.key = key;
@@ -68,7 +72,35 @@ public class ModalButton {
}
public ModalButton augment(Consumer<Button> augment) {
this.augment = augment;
this.augments.add(augment);
return this;
}
public ModalButton loadingIndicator(ObservableValue<Boolean> busy) {
return augment(button -> {
button.graphicProperty()
.bind(Bindings.createObjectBinding(
() -> {
return busy.getValue()
? new LoadingIconComp(busy, AppFontSizes::base)
.style("busy-loading-icon")
.build()
: null;
},
PlatformThread.sync(busy)));
button.textProperty()
.bind(Bindings.createStringBinding(
() -> {
return !busy.getValue() ? AppI18n.get(key) : null;
},
PlatformThread.sync(busy),
AppI18n.activeLanguage()));
});
}
public ModalButton disable(ObservableValue<Boolean> b) {
return augment(button -> {
button.disableProperty().bind(PlatformThread.sync(b));
});
}
}
@@ -34,6 +34,7 @@ import atlantafx.base.util.Animations;
import net.synedra.validatorfx.GraphicDecorationStackPane;
import java.time.Instant;
import java.util.function.Consumer;
public class ModalOverlayComp extends RegionBuilder<Region> {
@@ -321,8 +322,8 @@ public class ModalOverlayComp extends RegionBuilder<Region> {
if (mb.isDefaultButton()) {
button.getStyleClass().add(Styles.ACCENT);
}
if (mb.getAugment() != null) {
mb.getAugment().accept(button);
for (Consumer<Button> augment : mb.getAugments()) {
augment.accept(button);
}
button.managedProperty().bind(button.visibleProperty());
button.setOnAction(event -> {
@@ -200,7 +200,7 @@ public class StoreEntryWrapper {
.findFirst()
.orElse(StoreViewState.get().getAllConnectionsCategory());
category.setValue(newCat);
accessScopeRestricted.setValue(entry.getAccessScope().isAccessRestricted());
accessScopeRestricted.setValue(entry.getAccessScope().isAccessSubRestricted());
pinToTop.setValue(entry.isPinToTop());
var orderedTags = entry.getTags().stream().sorted().toList();
@@ -17,9 +17,14 @@ import java.util.stream.Collectors;
public class DataStoreAccessScope {
public static DataStoreAccessScope merge(List<DataStoreAccessScope> scopes) {
var effectiveScopes = scopes.stream().filter(s -> !s.equals(vault())).collect(Collectors.toSet());
if (effectiveScopes.isEmpty()) {
return DataStoreAccessScope.vault();
}
var matching = DataStorageAccessHandler.getInstance().getAllEncryptionPrincipals().stream()
.filter(encryptionPrincipal -> {
return scopes.stream().allMatch(s -> s.getPrincipals().contains(encryptionPrincipal));
return effectiveScopes.stream().allMatch(s -> s.getPrincipals().contains(encryptionPrincipal));
}).collect(Collectors.toSet());
return !matching.isEmpty() ? of(matching) : DataStoreAccessScope.of(Set.of(EncryptionPrincipal.inaccessible()));
}
@@ -55,7 +60,7 @@ public class DataStoreAccessScope {
this.principals = principals;
}
public boolean isAccessRestricted() {
public boolean isAccessSubRestricted() {
var all = this.equals(encryption()) || this.equals(vault());
return !all;
}
@@ -57,9 +57,10 @@ public class DataStoreAccessScopeComp extends SimpleRegionBuilder {
allPrincipals.removeIf(
encryptionPrincipal -> encryptionPrincipal.getName().equals("vault"));
allPrincipals.remove(handler.getEncryptAllPrincipal());
allPrincipals.sort(Comparator.comparing(encryptionPrincipal -> encryptionPrincipal.getName()));
var selectedPrincipals = FXCollections.observableArrayList(
scope.getValue().isAccessRestricted() ? scope.getValue().getPrincipals() : List.of());
scope.getValue().isAccessSubRestricted() ? scope.getValue().getPrincipals() : List.of());
selectedPrincipals.addListener((ListChangeListener<? super EncryptionPrincipal>) c -> {
if (selectedPrincipals.isEmpty()) {
scope.setValue(DataStoreAccessScope.encryption());
@@ -370,12 +370,15 @@ 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()
var currentScope = store instanceof AccessScopeStore s ? s.getAccessScope()
: DataStoreAccessScope.encryption();
var shouldEncrypt = (encryptIfRestricted && scope.isAccessRestricted())
var targetScope = DataStoreAccessScope.getTargetScope(currentScope);
var shouldEncrypt = (encryptIfRestricted && currentScope.isAccessSubRestricted())
|| AppPrefs.get().encryptAllVaultData().get();
var encryptionChange = shouldEncrypt && !node.isEncrypted() || !shouldEncrypt && node.isEncrypted();
var scopeTargetChange = !DataStoreAccessScope.getTargetScope(scope).equals(scope);
var scopeTargetChange = !targetScope.equals(currentScope);
return encryptionChange || scopeTargetChange;
}
@@ -790,6 +793,17 @@ public class DataStoreEntry extends DataStorageElement {
return;
}
var newStore = getStore() instanceof AccessScopeStore s ? s.withUpdatedPrincipals() : getStore();
var changedStore = !Objects.equals(getStore(), newStore);
if (changedStore) {
// This will take care of the encryption change for the node
// we don't have to do this further down below
storeNode = DataStoreEntryNode.of(newStore);
dirty = true;
notifyUpdate(false, false);
return;
}
var newNode = storeNode.withUpdatedEncryption(this, true);
if (!newNode.equals(storeNode)) {
storeNode = newNode;
@@ -70,10 +70,10 @@ public class DataStoreEntryNode<T> {
return null;
}
var currentScope = enc.getSecret() != null ? enc.getSecret().getScope() : DataStoreAccessScope.vault();
var targetScope = DataStoreAccessScope.getTargetScope(entry.getAccessScope());
var currentScope = entry.getAccessScope();
var targetScope = DataStoreAccessScope.getTargetScope(currentScope);
var shouldEncrypt = (encryptIfRestricted && targetScope.isAccessRestricted())
var shouldEncrypt = (encryptIfRestricted && targetScope.isAccessSubRestricted())
|| AppPrefs.get().encryptAllVaultData().get();
var encryptionChange = shouldEncrypt && !enc.isEncrypted() || !shouldEncrypt && enc.isEncrypted();
var scopeTargetChange = !targetScope.equals(currentScope);
@@ -111,10 +111,10 @@ public class DataStoreEntryNode<T> {
return this;
}
var currentScope = enc.getSecret() != null ? enc.getSecret().getScope() : DataStoreAccessScope.vault();
var targetScope = DataStoreAccessScope.getTargetScope(entry.getAccessScope());
var currentScope = entry.getAccessScope();
var targetScope = DataStoreAccessScope.getTargetScope(currentScope);
var shouldEncrypt = (encryptIfRestricted && targetScope.isAccessRestricted())
var shouldEncrypt = (encryptIfRestricted && targetScope.isAccessSubRestricted())
|| AppPrefs.get().encryptAllVaultData().get();
var encryptionChange = shouldEncrypt && !enc.isEncrypted() || !shouldEncrypt && enc.isEncrypted();
var scopeTargetChange = !targetScope.equals(currentScope);
@@ -174,7 +174,7 @@ public class IdentitySelectComp extends RegionBuilder<HBox> {
: id.getClass().getSimpleName().equals("PasswordManagerIdentityStore")
? AppI18n.get("passwordManagerIdentity")
: id instanceof SyncedIdentityStore
&& storeEntry.getAccessScope().isAccessRestricted()
&& storeEntry.getAccessScope().isAccessSubRestricted()
? (DataStorageAccessHandler.getInstance().getType() == DataStorageAccessType.ROLE
? AppI18n.get("roleIdentity")
: AppI18n.get("userIdentity"))
@@ -1,7 +1,6 @@
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;
@@ -130,7 +129,7 @@ public class SyncedIdentityStoreProvider extends IdentityStoreProvider {
@Override
public String summaryString(StoreEntryWrapper wrapper) {
if (!wrapper.getEntry().getAccessScope().isAccessRestricted()) {
if (!wrapper.getEntry().getAccessScope().isAccessSubRestricted()) {
return AppI18n.get("globalIdentity");
}
+2 -1
View File
@@ -559,7 +559,8 @@ ptbNotice=Notice for the public test build
userDeletionTitle=User deletion
userDeletionContent=Do you want to delete this vault user? This will reencrypt all data using the default vault key. This will take a while.
roleDeletionTitle=Role deletion
roleDeletionContent=Do you want to delete this vault role? This will reencrypt all role-only identities and connection secrets and potentially make some entries which still use this role inaccessible. This will take a while.
#force
roleDeletionContent=Do you want to delete this vault role? This will reencrypt all identities and connection secrets that use this role. Any entries that are only accessible to this role will become available to all roles. This will take a while.
killTransfer=Kill transfer
destination=Destination
configuration=Configuration
+1 -1
View File
@@ -1 +1 @@
24.0-40
24.0-41