diff --git a/app/src/main/java/io/xpipe/app/fxcomps/impl/OptionsComp.java b/app/src/main/java/io/xpipe/app/fxcomps/impl/OptionsComp.java index c4e9d318a..e69027dc9 100644 --- a/app/src/main/java/io/xpipe/app/fxcomps/impl/OptionsComp.java +++ b/app/src/main/java/io/xpipe/app/fxcomps/impl/OptionsComp.java @@ -152,18 +152,15 @@ public class OptionsComp extends Comp> { if (entries.stream().anyMatch(entry -> entry.name() != null && entry.description() == null)) { var nameWidthBinding = Bindings.createDoubleBinding( () -> { - if (nameRegions.stream().anyMatch(r -> r.getWidth() == 0)) { - return Region.USE_COMPUTED_SIZE; - } - var m = nameRegions.stream() .map(Region::getWidth) + .filter(aDouble -> aDouble > 0.0) .max(Double::compareTo) - .orElse(0.0); + .orElse(Region.USE_COMPUTED_SIZE); return m; }, nameRegions.stream().map(Region::widthProperty).toList().toArray(new Observable[0])); - nameRegions.forEach(r -> r.prefWidthProperty().bind(nameWidthBinding)); + nameRegions.forEach(r -> r.minWidthProperty().bind(nameWidthBinding)); } return new SimpleCompStructure<>(pane); diff --git a/app/src/main/java/io/xpipe/app/util/PasswordRetrievalMethod.java b/app/src/main/java/io/xpipe/app/util/PasswordRetrievalMethod.java deleted file mode 100644 index 8e6129c9b..000000000 --- a/app/src/main/java/io/xpipe/app/util/PasswordRetrievalMethod.java +++ /dev/null @@ -1,75 +0,0 @@ -package io.xpipe.app.util; - -import io.xpipe.core.impl.LocalStore; -import io.xpipe.core.util.SecretValue; - -import java.util.function.Supplier; - -public abstract class PasswordRetrievalMethod { - - public abstract SecretValue retrieve(String displayName) throws Exception; - - public static class None extends PasswordRetrievalMethod { - - @Override - public SecretValue retrieve(String displayName) { - return null; - } - } - - public static class Unsupported extends PasswordRetrievalMethod { - - @Override - public SecretValue retrieve(String displayName) { - throw new UnsupportedOperationException(); - } - } - - public static class Reference extends PasswordRetrievalMethod { - - private final Supplier supplier; - - public Reference(Supplier supplier) { - this.supplier = supplier; - } - - @Override - public SecretValue retrieve(String displayName) { - return supplier.get(); - } - } - - public static class Prompt extends PasswordRetrievalMethod { - - @Override - public SecretValue retrieve(String displayName) { - return AskpassAlert.query(displayName); - } - } - - public static class Command extends PasswordRetrievalMethod { - - String command; - - @Override - public SecretValue retrieve(String displayName) throws Exception { - try (var cc = new LocalStore().createBasicControl().command(command).start()) { - var read = cc.readStdoutDiscardErr(); - return SecretHelper.encrypt(read); - } - } - } - - public static class KeePass extends PasswordRetrievalMethod { - - String command; - - @Override - public SecretValue retrieve(String displayName) throws Exception { - try (var cc = new LocalStore().createBasicControl().command(command).start()) { - var read = cc.readStdoutDiscardErr(); - return SecretHelper.encrypt(read); - } - } - } -} diff --git a/app/src/main/java/io/xpipe/app/util/SecretRetrievalStrategy.java b/app/src/main/java/io/xpipe/app/util/SecretRetrievalStrategy.java new file mode 100644 index 000000000..3bf9391a4 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/util/SecretRetrievalStrategy.java @@ -0,0 +1,113 @@ +package io.xpipe.app.util; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import io.xpipe.core.impl.LocalStore; +import io.xpipe.core.util.SecretValue; +import lombok.Getter; + +import java.util.function.Supplier; + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = SecretRetrievalStrategy.None.class), + @JsonSubTypes.Type(value = SecretRetrievalStrategy.Unsupported.class), + @JsonSubTypes.Type(value = SecretRetrievalStrategy.Reference.class), + @JsonSubTypes.Type(value = SecretRetrievalStrategy.InPlace.class), + @JsonSubTypes.Type(value = SecretRetrievalStrategy.Prompt.class), + @JsonSubTypes.Type(value = SecretRetrievalStrategy.Command.class), + @JsonSubTypes.Type(value = SecretRetrievalStrategy.KeePass.class) +}) +public abstract class SecretRetrievalStrategy { + + public abstract SecretValue retrieve(String displayName) throws Exception; + + @JsonTypeName("none") + public static class None extends SecretRetrievalStrategy { + + @Override + public SecretValue retrieve(String displayName) { + return null; + } + } + + @JsonTypeName("unsupported") + public static class Unsupported extends SecretRetrievalStrategy { + + @Override + public SecretValue retrieve(String displayName) { + throw new UnsupportedOperationException(); + } + } + + @JsonTypeName("reference") + public static class Reference extends SecretRetrievalStrategy { + + @JsonIgnore + private final Supplier supplier; + + public Reference(Supplier supplier) { + this.supplier = supplier; + } + + @Override + public SecretValue retrieve(String displayName) { + return supplier.get(); + } + } + + @JsonTypeName("inPlace") + @Getter + public static class InPlace extends SecretRetrievalStrategy { + + private final SecretValue value; + + public InPlace(SecretValue value) { + this.value = value; + } + + @Override + public SecretValue retrieve(String displayName) { + return value; + } + } + + @JsonTypeName("prompt") + public static class Prompt extends SecretRetrievalStrategy { + + @Override + public SecretValue retrieve(String displayName) { + return AskpassAlert.query(displayName); + } + } + + @JsonTypeName("command") + public static class Command extends SecretRetrievalStrategy { + + String command; + + @Override + public SecretValue retrieve(String displayName) throws Exception { + try (var cc = new LocalStore().createBasicControl().command(command).start()) { + var read = cc.readStdoutDiscardErr(); + return SecretHelper.encrypt(read); + } + } + } + + @JsonTypeName("keepass") + public static class KeePass extends SecretRetrievalStrategy { + + String command; + + @Override + public SecretValue retrieve(String displayName) throws Exception { + try (var cc = new LocalStore().createBasicControl().command(command).start()) { + var read = cc.readStdoutDiscardErr(); + return SecretHelper.encrypt(read); + } + } + } +} diff --git a/app/src/main/java/io/xpipe/app/util/SecretRetrievalStrategyHelper.java b/app/src/main/java/io/xpipe/app/util/SecretRetrievalStrategyHelper.java new file mode 100644 index 000000000..04298263b --- /dev/null +++ b/app/src/main/java/io/xpipe/app/util/SecretRetrievalStrategyHelper.java @@ -0,0 +1,51 @@ +package io.xpipe.app.util; + +import io.xpipe.app.fxcomps.impl.SecretFieldComp; +import javafx.beans.property.Property; +import javafx.beans.property.SimpleObjectProperty; + +import java.util.LinkedHashMap; + +public class SecretRetrievalStrategyHelper { + + private static OptionsBuilder inPlace(Property p) { + var secretProperty = new SimpleObjectProperty<>( + p.getValue() != null ? p.getValue().getValue() : null); + return new OptionsBuilder() + .name("keyPassword") + .addComp(new SecretFieldComp(secretProperty), secretProperty) + .bind( + () -> { + return new SecretRetrievalStrategy.InPlace(secretProperty.getValue()); + }, + p); + } + + public static OptionsBuilder comp(Property s) { + var inPlace = new SimpleObjectProperty<>(s.getValue() instanceof SecretRetrievalStrategy.InPlace i ? i : null); + var command = new SimpleObjectProperty<>(s.getValue() instanceof SecretRetrievalStrategy.Command c ? c : null); + var map = new LinkedHashMap(); + map.put("none", new OptionsBuilder()); + map.put("password", inPlace(inPlace)); + map.put("command", new OptionsBuilder()); + map.put("keepass", new OptionsBuilder()); + var selected = new SimpleObjectProperty(); + return new OptionsBuilder() + .name("keyAuthentication") + .description("keyAuthenticationDescription") + .longDescription("proc:sshKey") + .choice(selected, map) + .nonNull() + .bindChoice( + () -> { + return switch (selected.get()) { + case 0 -> new SimpleObjectProperty<>(new SecretRetrievalStrategy.None()); + case 1 -> inPlace; + case 2 -> command; + case 3 -> new SimpleObjectProperty<>(new SecretRetrievalStrategy.KeePass()); + case null, default -> new SimpleObjectProperty<>(); + }; + }, + s); + } +}