diff --git a/app/src/main/java/io/xpipe/app/cred/PasswordManagerAgentStrategy.java b/app/src/main/java/io/xpipe/app/cred/PasswordManagerAgentStrategy.java index 17a2e6b05..93308d917 100644 --- a/app/src/main/java/io/xpipe/app/cred/PasswordManagerAgentStrategy.java +++ b/app/src/main/java/io/xpipe/app/cred/PasswordManagerAgentStrategy.java @@ -14,6 +14,7 @@ import io.xpipe.app.pwman.PasswordManagerKeyConfiguration; import io.xpipe.core.KeyValue; import javafx.beans.binding.Bindings; import javafx.beans.property.Property; +import javafx.beans.property.ReadOnlyBooleanWrapper; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleStringProperty; import javafx.geometry.Insets; @@ -45,7 +46,7 @@ public class PasswordManagerAgentStrategy implements SshIdentityStrategy { return AppI18n.get("passwordManagerEmpty"); } - if (!pwman.getKeyConfiguration().supportsAgent()) { + if (!pwman.getKeyConfiguration().useAgent()) { return AppI18n.get("passwordManagerNoAgentSupport"); } @@ -67,9 +68,10 @@ public class PasswordManagerAgentStrategy implements SshIdentityStrategy { return new OptionsBuilder() .nameAndDescription("passwordManagerSshKeyConfig") .addComp(pwmanDisplay) - .hide(pwmanProp.isNull()) + .hide(Bindings.or(pwmanProp.isNull(), new ReadOnlyBooleanWrapper(!config.isAllowPasswordAgentKeyChoice()))) .nameAndDescription("publicKey") .addComp(new SshAgentKeyListComp(config.getFileSystem(), p, publicKey), publicKey) + .hide(!config.isAllowPasswordAgentKeyChoice()) .nameAndDescription("forwardAgent") .addToggle(forward) .nonNull() @@ -86,7 +88,7 @@ public class PasswordManagerAgentStrategy implements SshIdentityStrategy { private PasswordManagerKeyConfiguration getConfig() { var pwman = AppPrefs.get().passwordManager().getValue(); - return pwman != null && pwman.getKeyConfiguration() != null && pwman.getKeyConfiguration().supportsAgent() ? pwman.getKeyConfiguration() : null; + return pwman != null && pwman.getKeyConfiguration() != null && pwman.getKeyConfiguration().useAgent() ? pwman.getKeyConfiguration() : null; } @Override diff --git a/app/src/main/java/io/xpipe/app/cred/SshAgentKeyList.java b/app/src/main/java/io/xpipe/app/cred/SshAgentKeyList.java index fca3a5368..d4804cd4a 100644 --- a/app/src/main/java/io/xpipe/app/cred/SshAgentKeyList.java +++ b/app/src/main/java/io/xpipe/app/cred/SshAgentKeyList.java @@ -19,6 +19,10 @@ public class SshAgentKeyList { String name; } + public static void validate(DataStoreEntryRef ref, SshIdentityStrategy strategy, String publicKey) { + + } + public static List listAgentIdentities(DataStoreEntryRef ref, SshIdentityStrategy strategy) throws Exception { var session = ref.getStore().getOrStartSession(); strategy.prepareParent(session); diff --git a/app/src/main/java/io/xpipe/app/cred/SshIdentityStrategyChoiceConfig.java b/app/src/main/java/io/xpipe/app/cred/SshIdentityStrategyChoiceConfig.java index dead1746e..7357f9005 100644 --- a/app/src/main/java/io/xpipe/app/cred/SshIdentityStrategyChoiceConfig.java +++ b/app/src/main/java/io/xpipe/app/cred/SshIdentityStrategyChoiceConfig.java @@ -17,5 +17,6 @@ public class SshIdentityStrategyChoiceConfig { Supplier perUserKeyFileCheck; boolean allowKeyFileSync; boolean allowAgentForward; + boolean allowPasswordAgentKeyChoice; ObservableValue> fileSystem; } diff --git a/app/src/main/java/io/xpipe/app/ext/DataStoreProvider.java b/app/src/main/java/io/xpipe/app/ext/DataStoreProvider.java index 26f4c4c0c..e1018adcf 100644 --- a/app/src/main/java/io/xpipe/app/ext/DataStoreProvider.java +++ b/app/src/main/java/io/xpipe/app/ext/DataStoreProvider.java @@ -27,6 +27,10 @@ import java.util.UUID; public interface DataStoreProvider { + default boolean allowCreation() { + return true; + } + default boolean showIncompleteInfo() { return false; } diff --git a/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationDialog.java b/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationDialog.java index e5ef37475..5edd81fc2 100644 --- a/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationDialog.java +++ b/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationDialog.java @@ -122,7 +122,8 @@ public class StoreCreationDialog { name, prov, base, - dataStoreProvider -> (category != null && category.equals(dataStoreProvider.getCreationCategory())) + dataStoreProvider -> (category != null && dataStoreProvider.allowCreation() && + category.equals(dataStoreProvider.getCreationCategory())) || dataStoreProvider.equals(prov), consumer, false, diff --git a/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationMenu.java b/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationMenu.java index abaf5d605..f2fda216f 100644 --- a/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationMenu.java +++ b/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationMenu.java @@ -153,7 +153,7 @@ public class StoreCreationMenu { }); int lastOrder = providers.getFirst().getOrderPriority(); - for (io.xpipe.app.ext.DataStoreProvider dataStoreProvider : providers) { + for (var dataStoreProvider : providers) { if (dataStoreProvider.getOrderPriority() != lastOrder) { menu.getItems().add(new SeparatorMenuItem()); lastOrder = dataStoreProvider.getOrderPriority(); @@ -167,6 +167,7 @@ public class StoreCreationMenu { StoreCreationDialog.showCreation(dataStoreProvider, category); event.consume(); }); + item.setDisable(!dataStoreProvider.allowCreation()); menu.getItems().add(item); } return menu; diff --git a/app/src/main/java/io/xpipe/app/pwman/BitwardenPasswordManager.java b/app/src/main/java/io/xpipe/app/pwman/BitwardenPasswordManager.java index 6d3e644de..a04536854 100644 --- a/app/src/main/java/io/xpipe/app/pwman/BitwardenPasswordManager.java +++ b/app/src/main/java/io/xpipe/app/pwman/BitwardenPasswordManager.java @@ -61,7 +61,7 @@ public class BitwardenPasswordManager implements PasswordManager { @SuppressWarnings("unused") public static OptionsBuilder createOptions(Property p) { - var agentStrategy = new SimpleObjectProperty<>(p.getValue().keyStrategy); + var keyStrategy = new SimpleObjectProperty<>(p.getValue().keyStrategy); AtomicReference button = new AtomicReference<>(); var testButton = new ButtonComp(AppI18n.observable("sync"), new FontIcon("mdi2r-refresh"), () -> { @@ -76,18 +76,18 @@ public class BitwardenPasswordManager implements PasswordManager { testButton.apply(struc -> button.set(struc)); testButton.padding(new Insets(6, 10, 6, 6)); - var agentStrategyChoice = OptionsChoiceBuilder.builder() + var keyStrategyChoice = OptionsChoiceBuilder.builder() .allowNull(true) .available(List.of(PasswordManagerKeyStrategy.Agent.class)) - .property(agentStrategy) + .property(keyStrategy) .build(); return new OptionsBuilder() .addComp(testButton) .nameAndDescription("passwordManagerKeyStrategy") - .sub(agentStrategyChoice.build(), agentStrategy) + .sub(keyStrategyChoice.build(), keyStrategy) .bind(() -> { - return BitwardenPasswordManager.builder().keyStrategy(agentStrategy.getValue()).build(); + return BitwardenPasswordManager.builder().keyStrategy(keyStrategy.getValue()).build(); }, p); } @@ -260,26 +260,6 @@ public class BitwardenPasswordManager implements PasswordManager { @Override public PasswordManagerKeyConfiguration getKeyConfiguration() { - return new PasswordManagerKeyConfiguration() { - @Override - public boolean supportsInlineSshKeys() { - return true; - } - - @Override - public boolean supportsAgent() { - return keyStrategy != null; - } - - @Override - public boolean supportsJoinedEntries() { - return false; - } - - @Override - public SshIdentityStrategy getSshIdentityStrategy(String publicKey, boolean forward) { - return keyStrategy.getSshIdentityStrategy(publicKey, forward); - } - }; + return PasswordManagerKeyConfiguration.of(true, false, keyStrategy); } } diff --git a/app/src/main/java/io/xpipe/app/pwman/KeePassXcPasswordManager.java b/app/src/main/java/io/xpipe/app/pwman/KeePassXcPasswordManager.java index 7f0c74a9b..a7a066826 100644 --- a/app/src/main/java/io/xpipe/app/pwman/KeePassXcPasswordManager.java +++ b/app/src/main/java/io/xpipe/app/pwman/KeePassXcPasswordManager.java @@ -51,7 +51,7 @@ public class KeePassXcPasswordManager implements PasswordManager { @SuppressWarnings("unused") public static OptionsBuilder createOptions(Property p) { var keyStrategy = new SimpleObjectProperty<>(p.getValue().getKeyStrategy()); - var agentStrategyChoice = OptionsChoiceBuilder.builder() + var keyStrategyChoice = OptionsChoiceBuilder.builder() .allowNull(true) .available(List.of(PasswordManagerKeyStrategy.KeePassXcOpenSshAgent.class, PasswordManagerKeyStrategy.KeePassXcPageant.class)) .property(keyStrategy) @@ -95,7 +95,7 @@ public class KeePassXcPasswordManager implements PasswordManager { .hide(Bindings.isEmpty(prop)) .addProperty(prop) .nameAndDescription("passwordManagerKeyStrategy") - .sub(agentStrategyChoice.build(), keyStrategy) + .sub(keyStrategyChoice.build(), keyStrategy) .bind( () -> { return new KeePassXcPasswordManager(prop, keyStrategy.getValue()); diff --git a/app/src/main/java/io/xpipe/app/pwman/KeeperPasswordManager.java b/app/src/main/java/io/xpipe/app/pwman/KeeperPasswordManager.java index 298b4b54e..10891423e 100644 --- a/app/src/main/java/io/xpipe/app/pwman/KeeperPasswordManager.java +++ b/app/src/main/java/io/xpipe/app/pwman/KeeperPasswordManager.java @@ -404,7 +404,7 @@ public class KeeperPasswordManager implements PasswordManager { @SuppressWarnings("unused") public static OptionsBuilder createOptions(Property p) { - var agentStrategy = new SimpleObjectProperty<>(p.getValue().getKeyStrategy()); + var keyStrategy = new SimpleObjectProperty<>(p.getValue().getKeyStrategy()); var mfa = new SimpleObjectProperty<>(p.getValue().getTwoFactorAuth() != null ? p.getValue().getTwoFactorAuth() : new KeeperAuth.None()); var choice = OptionsChoiceBuilder.builder() @@ -412,10 +412,10 @@ public class KeeperPasswordManager implements PasswordManager { .available(KeeperAuth.getClasses()) .property(mfa) .build(); - var agentStrategyChoice = OptionsChoiceBuilder.builder() + var keyStrategyChoice = OptionsChoiceBuilder.builder() .allowNull(true) - .available(List.of(PasswordManagerKeyStrategy.Agent.class)) - .property(agentStrategy) + .available(List.of(PasswordManagerKeyStrategy.Inline.class, PasswordManagerKeyStrategy.Agent.class)) + .property(keyStrategy) .build(); @@ -423,11 +423,12 @@ public class KeeperPasswordManager implements PasswordManager { .nameAndDescription("keeper2fa") .sub(choice.build(), mfa) .nameAndDescription("passwordManagerKeyStrategy") - .sub(agentStrategyChoice.build(), agentStrategy) + .sub(keyStrategyChoice.build(), keyStrategy) .bind( () -> { return KeeperPasswordManager.builder() .twoFactorAuth(mfa.get()) + .keyStrategy(keyStrategy.get()) .build(); }, p); diff --git a/app/src/main/java/io/xpipe/app/pwman/OnePasswordManager.java b/app/src/main/java/io/xpipe/app/pwman/OnePasswordManager.java index 48e3cfdaa..d48fa3e67 100644 --- a/app/src/main/java/io/xpipe/app/pwman/OnePasswordManager.java +++ b/app/src/main/java/io/xpipe/app/pwman/OnePasswordManager.java @@ -39,19 +39,19 @@ public class OnePasswordManager implements PasswordManager { @SuppressWarnings("unused") public static OptionsBuilder createOptions(Property p) { - var agentStrategy = new SimpleObjectProperty<>(p.getValue().getKeyStrategy()); + var keyStrategy = new SimpleObjectProperty<>(p.getValue().getKeyStrategy()); - var agentStrategyChoice = OptionsChoiceBuilder.builder() + var keyStrategyChoice = OptionsChoiceBuilder.builder() .allowNull(true) .available(List.of(PasswordManagerKeyStrategy.Agent.class)) - .property(agentStrategy) + .property(keyStrategy) .build(); return new OptionsBuilder() .nameAndDescription("passwordManagerKeyStrategy") - .sub(agentStrategyChoice.build(), agentStrategy) + .sub(keyStrategyChoice.build(), keyStrategy) .bind(() -> { - return OnePasswordManager.builder().keyStrategy(agentStrategy.getValue()).build(); + return OnePasswordManager.builder().keyStrategy(keyStrategy.getValue()).build(); }, p); } diff --git a/app/src/main/java/io/xpipe/app/pwman/PasswordManagerKeyConfiguration.java b/app/src/main/java/io/xpipe/app/pwman/PasswordManagerKeyConfiguration.java index 7657aebe6..c1e254432 100644 --- a/app/src/main/java/io/xpipe/app/pwman/PasswordManagerKeyConfiguration.java +++ b/app/src/main/java/io/xpipe/app/pwman/PasswordManagerKeyConfiguration.java @@ -7,18 +7,13 @@ public interface PasswordManagerKeyConfiguration { static PasswordManagerKeyConfiguration of(boolean inline, boolean joined, PasswordManagerKeyStrategy strategy) { return new PasswordManagerKeyConfiguration() { @Override - public boolean supportsInlineSshKeys() { - return inline; + public boolean useInline() { + return (strategy == null || !strategy.useAgent()) && inline && joined; } @Override - public boolean supportsAgent() { - return strategy != null; - } - - @Override - public boolean supportsJoinedEntries() { - return joined; + public boolean useAgent() { + return strategy != null && strategy.useAgent(); } @Override @@ -31,17 +26,12 @@ public interface PasswordManagerKeyConfiguration { static PasswordManagerKeyConfiguration none() { return new PasswordManagerKeyConfiguration() { @Override - public boolean supportsInlineSshKeys() { + public boolean useInline() { return false; } @Override - public boolean supportsAgent() { - return false; - } - - @Override - public boolean supportsJoinedEntries() { + public boolean useAgent() { return false; } @@ -52,11 +42,9 @@ public interface PasswordManagerKeyConfiguration { }; } - boolean supportsInlineSshKeys(); + boolean useInline(); - boolean supportsAgent(); - - boolean supportsJoinedEntries(); + boolean useAgent(); SshIdentityStrategy getSshIdentityStrategy(String publicKey, boolean forward); } diff --git a/app/src/main/java/io/xpipe/app/pwman/PasswordManagerKeyStrategy.java b/app/src/main/java/io/xpipe/app/pwman/PasswordManagerKeyStrategy.java index 44c99a391..df8ecab4f 100644 --- a/app/src/main/java/io/xpipe/app/pwman/PasswordManagerKeyStrategy.java +++ b/app/src/main/java/io/xpipe/app/pwman/PasswordManagerKeyStrategy.java @@ -38,6 +38,11 @@ public interface PasswordManagerKeyStrategy { return "inlineKey"; } + @Override + public boolean useAgent() { + return false; + } + @Override public SshIdentityStrategy getSshIdentityStrategy(String publicKey, boolean forward) { return null; @@ -52,6 +57,11 @@ public interface PasswordManagerKeyStrategy { FilePath customSocket; + @Override + public boolean useAgent() { + return true; + } + @SuppressWarnings("unused") public static String getOptionsNameKey() { return "keyAgent"; @@ -134,6 +144,11 @@ public interface PasswordManagerKeyStrategy { property); } + @Override + public boolean useAgent() { + return true; + } + @Override public SshIdentityStrategy getSshIdentityStrategy(String publicKey, boolean forward) { return OpenSshAgentStrategy.builder().build(); @@ -157,12 +172,19 @@ public interface PasswordManagerKeyStrategy { property); } + @Override + public boolean useAgent() { + return true; + } + @Override public SshIdentityStrategy getSshIdentityStrategy(String publicKey, boolean forward) { return PageantStrategy.builder().build(); } } + boolean useAgent(); + SshIdentityStrategy getSshIdentityStrategy(String publicKey, boolean forward); static List> getClasses() { 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 246b68cc6..29460a035 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 @@ -152,6 +152,7 @@ public class IdentityChoiceBuilder { .allowAgentForward(allowAgentForward) .allowKeyFileSync(true) .perUserKeyFileCheck(() -> false) + .allowPasswordAgentKeyChoice(true) .fileSystem(fileSystem) .build(); 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 2bd1c5d96..febc294c8 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 @@ -42,6 +42,7 @@ public class LocalIdentityStoreProvider extends IdentityStoreProvider { var sshIdentityChoiceConfig = SshIdentityStrategyChoiceConfig.builder() .allowAgentForward(true) .allowKeyFileSync(false) + .allowPasswordAgentKeyChoice(true) .perUserKeyFileCheck(() -> false) .build(); diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/PasswordManagerIdentityStore.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/PasswordManagerIdentityStore.java index 6658aaa89..f583a8cb4 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/PasswordManagerIdentityStore.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/PasswordManagerIdentityStore.java @@ -51,7 +51,7 @@ public class PasswordManagerIdentityStore extends IdentityStore return true; } - private PasswordManager.Result retrieveCredentials() { + private PasswordManager.Result retrieve() { if (!checkOutdatedOrRefresh()) { var r = getCache("result", PasswordManager.Result.class, null); if (r != null) { @@ -79,6 +79,18 @@ public class PasswordManagerIdentityStore extends IdentityStore new UnsupportedOperationException("Identity " + key + " does not provide a username")); } + if (sshKey != null) { + var pwman = AppPrefs.get().passwordManager().getValue(); + if (pwman.getKeyConfiguration().useInline() && r.getSshKey() == null) { + throw ErrorEventFactory.expected( + new UnsupportedOperationException("Identity " + key + " does not provide an SSH key")); + } + + if (pwman.getKeyConfiguration().useAgent()) { + SshAgentKeyList.validate(); + } + } + setCache("lastQueried", Instant.now()); setCache("result", r); @@ -87,7 +99,7 @@ public class PasswordManagerIdentityStore extends IdentityStore public UsernameStrategy getUsername() { return new UsernameStrategy.Dynamic(() -> { - var r = retrieveCredentials(); + var r = retrieve(); var effective = r != null && r.getCredentials() != null && r.getCredentials().getUsername() != null ? r.getCredentials().getUsername() : "unknown"; return effective; }); @@ -102,7 +114,7 @@ public class PasswordManagerIdentityStore extends IdentityStore return new SecretQuery() { @Override public SecretQueryResult query(String prompt) { - var r = retrieveCredentials(); + var r = retrieve(); if (r == null || r.getCredentials() == null || r.getCredentials().getPassword() == null) { return new SecretQueryResult(null, SecretQueryState.RETRIEVAL_FAILURE); } @@ -138,15 +150,15 @@ public class PasswordManagerIdentityStore extends IdentityStore } var strat = r.getKeyConfiguration(); - if (strat == null || (!strat.supportsInlineSshKeys() && !strat.supportsAgent())) { + if (strat == null || (!strat.useInline() && !strat.useAgent())) { return def; } - if (strat.supportsInlineSshKeys() && strat.supportsJoinedEntries()) { + if (strat.useInline()) { return new SshIdentityStrategy() { @Override public void prepareParent(ShellControl parent) throws Exception { - var r = retrieveCredentials(); + var r = retrieve(); if (r == null || r.getSshKey() == null || r.getSshKey().getPrivateKey() == null) { return; } @@ -157,7 +169,7 @@ public class PasswordManagerIdentityStore extends IdentityStore @Override public void buildCommand(CommandBuilder builder) { - var r = retrieveCredentials(); + var r = retrieve(); if (r == null || r.getSshKey() == null || r.getSshKey().getPrivateKey() == null) { return; } @@ -168,7 +180,7 @@ public class PasswordManagerIdentityStore extends IdentityStore @Override public List configOptions(ShellControl sc) throws Exception { - var r = retrieveCredentials(); + var r = retrieve(); if (r == null || r.getSshKey() == null || r.getSshKey().getPrivateKey() == null) { return List.of(); } @@ -179,7 +191,7 @@ public class PasswordManagerIdentityStore extends IdentityStore @Override public String getPublicKey() { - var r = retrieveCredentials(); + var r = retrieve(); if (r == null || r.getSshKey() == null || r.getSshKey().getPublicKey() == null) { return null; } @@ -189,17 +201,23 @@ public class PasswordManagerIdentityStore extends IdentityStore }; } - var agentStrat = strat.getSshIdentityStrategy(null, false); + if (strat.useAgent() && sshKey != null) { + return sshKey; + } + return new NoIdentityStrategy(); } @Override public void checkComplete() throws Throwable { Validators.nonNull(key); + if (sshKey != null) { + sshKey.checkComplete(); + } } @Override public void validate() { - retrieveCredentials(); + retrieve(); } } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/PasswordManagerIdentityStoreProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/PasswordManagerIdentityStoreProvider.java index 622f2a928..0d2fbc270 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/identity/PasswordManagerIdentityStoreProvider.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/PasswordManagerIdentityStoreProvider.java @@ -1,30 +1,28 @@ package io.xpipe.ext.base.identity; -import io.xpipe.app.cred.SshIdentityStrategy; +import io.xpipe.app.cred.PasswordManagerAgentStrategy; +import io.xpipe.app.cred.SshIdentityStrategyChoiceConfig; import io.xpipe.app.ext.DataStore; -import io.xpipe.app.ext.DataStoreCreationCategory; import io.xpipe.app.ext.GuiDialog; import io.xpipe.app.platform.OptionsBuilder; import io.xpipe.app.platform.OptionsChoiceBuilder; import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.prefs.PasswordManagerTestComp; +import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStorageUserHandler; import io.xpipe.app.storage.DataStoreCategory; import io.xpipe.app.storage.DataStoreEntry; import javafx.beans.binding.Bindings; -import javafx.beans.property.Property; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.*; import java.util.List; public class PasswordManagerIdentityStoreProvider extends IdentityStoreProvider { @Override - public DataStoreCreationCategory getCreationCategory() { - return AppPrefs.get().passwordManager().getValue() != null ? DataStoreCreationCategory.IDENTITY : null; + public boolean allowCreation() { + return AppPrefs.get().passwordManager().getValue() != null; } @Override @@ -35,21 +33,30 @@ public class PasswordManagerIdentityStoreProvider extends IdentityStoreProvider var sshKey = new SimpleObjectProperty<>(st.getSshKey()); var perUser = new SimpleBooleanProperty(st.isPerUser()); + var pwMan = AppPrefs.get().passwordManager().getValue(); + var showKeyChoice = pwMan == null || !pwMan.getKeyConfiguration().useInline(); + var sshIdentityChoiceConfig = SshIdentityStrategyChoiceConfig.builder() + .allowAgentForward(true) + .allowKeyFileSync(true) + .allowPasswordAgentKeyChoice(showKeyChoice) + .perUserKeyFileCheck(() -> false) + .fileSystem(new ReadOnlyObjectWrapper<>(DataStorage.get().local().ref())) + .build(); var sshKeyChoice = OptionsChoiceBuilder.builder().allowNull(true) - .available(SshIdentityStrategy.getClasses()).property(sshKey).build(); + .customConfiguration(sshIdentityChoiceConfig) + .available(List.of(PasswordManagerAgentStrategy.class)).property(sshKey).build(); var hideSshKeyChoice = Bindings.createBooleanBinding(() -> { var pwman = AppPrefs.get().passwordManager().getValue(); var strat = pwman.getKeyConfiguration(); - return !strat.supportsAgent() || - (strat.supportsJoinedEntries() && strat.supportsInlineSshKeys()); - }); + return !strat.useAgent() && !strat.useInline(); + }, AppPrefs.get().passwordManager()); - var comp = new PasswordManagerTestComp(key, false); + var testComp = new PasswordManagerTestComp(key, false); return new OptionsBuilder() .nameAndDescription("passwordManagerKey") - .addComp(comp.hgrow(), key) + .addComp(testComp.hgrow(), key) .nonNull() - .nameAndDescription("sshKey") + .nameAndDescription("passwordManagerIdentityAgentKey") .sub(sshKeyChoice.build(), sshKey) .hide(hideSshKeyChoice) .nameAndDescription( 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 b5fe41217..0773173d4 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 @@ -8,6 +8,7 @@ import io.xpipe.app.hub.comp.StoreEntryWrapper; 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.VaultAuthentication; import io.xpipe.app.secret.EncryptedValue; import io.xpipe.app.secret.SecretNoneStrategy; @@ -29,8 +30,8 @@ import java.util.UUID; public class SyncedIdentityStoreProvider extends IdentityStoreProvider { @Override - public DataStoreCreationCategory getCreationCategory() { - return DataStorage.get().supportsSync() ? DataStoreCreationCategory.IDENTITY : null; + public boolean allowCreation() { + return DataStorage.get().supportsSync(); } @Override @@ -75,6 +76,7 @@ public class SyncedIdentityStoreProvider extends IdentityStoreProvider { var sshIdentityChoiceConfig = SshIdentityStrategyChoiceConfig.builder() .allowAgentForward(true) .allowKeyFileSync(true) + .allowPasswordAgentKeyChoice(true) .perUserKeyFileCheck(() -> perUser.get()) .build(); diff --git a/lang/strings/translations_en.properties b/lang/strings/translations_en.properties index a4ac88b95..c862dc90f 100644 --- a/lang/strings/translations_en.properties +++ b/lang/strings/translations_en.properties @@ -1454,7 +1454,8 @@ terminalPromptConfiguration=Terminal prompt configuration terminalPromptConfig=Config file terminalPromptConfigDescription=The custom config file to apply to the prompt. This config will be automatically set up on the target system when the terminal is initialized and used as the default prompt config.\n\nIf you want to use the existing default config file on each system, you can leave this field empty. passwordManagerKey=Password manager key -passwordManagerKeyDescription=The password manager identifier of the secret +#force +passwordManagerKeyDescription=The password manager identifier of the credentials entry passwordManagerAgent=Password manager agent dockerComposeProject.displayName=Docker compose project dockerComposeProject.displayDescription=Group containers of a compose project together @@ -1532,7 +1533,8 @@ customVncCommandDescription=The custom command to execute to launch VNC sessions vncConnections=VNC connections passwordManagerIdentity=Password manager identity passwordManagerIdentity.displayName=Password manager identity -passwordManagerIdentity.displayDescription=Retrieve username and password of an identity from your password manager +#force +passwordManagerIdentity.displayDescription=Retrieve credentials of an identity from your password manager passwordCopied=Connection password copied to clipboard errorOccurred=Error occurred actionMacro.displayName=Action macro @@ -2010,3 +2012,5 @@ keePassXcPageant=Use Pageant passwordManagerSshAgentSocket=SSH agent socket passwordManagerSshAgentSocketDescription=Override the default agent socket location that should be used. passwordManagerSshKeysNotSupported=The current password manager configuration does not support retrieving SSH keys +passwordManagerIdentityAgentKey=Additional SSH key +passwordManagerIdentityAgentKeyDescription=The SSH key to use from the password manager SSH agent