This commit is contained in:
crschnick
2026-03-05 20:19:54 +00:00
parent ab8941b826
commit 09cab9308b
19 changed files with 57 additions and 52 deletions
@@ -38,12 +38,10 @@ public class OptionsComp extends RegionBuilder<VBox> {
private final List<Entry> entries;
private final List<Check> checks;
private final boolean autoFocus;
public OptionsComp(List<Entry> entries, List<Check> checks, boolean autoFocus) {
public OptionsComp(List<Entry> entries, List<Check> checks) {
this.entries = entries;
this.checks = checks;
this.autoFocus = autoFocus;
}
@Override
@@ -244,10 +242,6 @@ public class OptionsComp extends RegionBuilder<VBox> {
return;
}
if (!autoFocus) {
return;
}
var failed = checks.stream()
.filter(check -> check.getValidationResult().getMessages().size() > 0)
.findFirst();
@@ -7,11 +7,13 @@ import io.xpipe.app.comp.base.LabelComp;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.ValidationException;
import io.xpipe.app.platform.OptionsBuilder;
import io.xpipe.app.platform.Validator;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.process.CommandBuilder;
import io.xpipe.app.process.ShellControl;
import io.xpipe.app.pwman.PasswordManagerKeyConfiguration;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.Validators;
import io.xpipe.core.KeyValue;
import javafx.beans.binding.Bindings;
import javafx.beans.property.Property;
@@ -27,7 +29,7 @@ import org.kordamp.ikonli.javafx.FontIcon;
import java.util.List;
@JsonTypeName("passwordManager")
@JsonTypeName("passwordManagerAgent")
@Value
@Jacksonized
@Builder
@@ -36,8 +38,6 @@ public class PasswordManagerAgentStrategy implements SshIdentityStrategy {
@SuppressWarnings("unused")
public static OptionsBuilder createOptions(
Property<PasswordManagerAgentStrategy> p, SshIdentityStrategyChoiceConfig config) {
var forward =
new SimpleBooleanProperty(p.getValue() != null && p.getValue().isForwardAgent());
var identifier =
new SimpleStringProperty(p.getValue() != null ? p.getValue().getIdentifier() : null);
@@ -69,22 +69,18 @@ public class PasswordManagerAgentStrategy implements SshIdentityStrategy {
return new OptionsBuilder()
.nameAndDescription("passwordManagerSshKeyConfig")
.addComp(pwmanDisplay)
.hide(Bindings.or(pwmanProp.isNull(), new ReadOnlyBooleanWrapper(!config.isAllowPasswordAgentKeyChoice())))
.hide(pwmanProp.isNull())
.nameAndDescription(useKeyName() ? "agentKeyName" : "publicKey")
.addComp(new SshAgentKeyListComp(config.getFileSystem(), p, identifier, useKeyName()), identifier)
.hide(!config.isAllowPasswordAgentKeyChoice())
.nameAndDescription("forwardAgent")
.addToggle(forward)
.nonNull()
.hide(!config.isAllowAgentForward())
.bind(
() -> {
return new PasswordManagerAgentStrategy(forward.get(), identifier.get());
return new PasswordManagerAgentStrategy(identifier.get());
},
p);
}
boolean forwardAgent;
String identifier;
private static PasswordManagerKeyConfiguration getConfig() {
@@ -99,6 +95,7 @@ public class PasswordManagerAgentStrategy implements SshIdentityStrategy {
@Override
public void checkComplete() throws ValidationException {
Validators.nonNull(identifier);
var config = getConfig();
if (config == null) {
throw new ValidationException(AppI18n.get("passwordManagerSshKeysNotSupported"));
@@ -127,7 +124,7 @@ public class PasswordManagerAgentStrategy implements SshIdentityStrategy {
public List<KeyValue> configOptions(ShellControl sc) throws Exception {
var config = getConfig();
if (config != null) {
var strat = config.getSshIdentityStrategy(getPublicKeyStrategy().retrievePublicKey(), forwardAgent);
var strat = config.getSshIdentityStrategy(getPublicKeyStrategy().retrievePublicKey(), false);
return strat.configOptions(sc);
} else {
return List.of();
@@ -70,6 +70,7 @@ public class SshAgentKeyListComp extends SimpleRegionBuilder {
popover.hide();
e.consume();
});
entryButton.setMinWidth(600);
content.getChildren().add(entryButton);
}
popover.setContentNode(content);
@@ -17,6 +17,5 @@ public class SshIdentityStrategyChoiceConfig {
Supplier<Boolean> perUserKeyFileCheck;
boolean allowKeyFileSync;
boolean allowAgentForward;
boolean allowPasswordAgentKeyChoice;
ObservableValue<DataStoreEntryRef<ShellStore>> fileSystem;
}
@@ -84,11 +84,6 @@ public class OptionsBuilder {
return this;
}
public OptionsBuilder disableAutoFocus() {
focusEnabled = false;
return this;
}
public OptionsBuilder() {
this.ownValidator = new SimpleValidator();
this.allValidators.add(ownValidator);
@@ -496,7 +491,7 @@ public class OptionsBuilder {
public OptionsComp buildComp() {
finishCurrent();
var comp = new OptionsComp(entries, focusFirstIncomplete ? allChecks : List.of(), focusEnabled);
var comp = new OptionsComp(entries, focusFirstIncomplete ? allChecks : List.of());
return comp;
}
@@ -84,7 +84,6 @@ public class BitwardenPasswordManager implements PasswordManager {
.build();
return new OptionsBuilder()
.disableAutoFocus()
.addComp(syncButton)
.nameAndDescription("passwordManagerTest")
.addComp(new PasswordManagerTestComp(true))
@@ -37,7 +37,6 @@ public class DashlanePasswordManager implements PasswordManager {
@SuppressWarnings("unused")
public static OptionsBuilder createOptions(Property<DashlanePasswordManager> p) {
return new OptionsBuilder()
.disableAutoFocus()
.nameAndDescription("passwordManagerTest")
.addComp(new PasswordManagerTestComp(true));
}
@@ -66,7 +66,7 @@ public class KeeperPasswordManager implements PasswordManager {
return values;
}
String constructKeeperInput(KeeperPasswordManager passwordManager) throws Exception;
String constructKeeperInput(KeeperPasswordManager passwordManager, SecretValue password) throws Exception;
Duration getCacheDuration();
@@ -105,11 +105,13 @@ public class KeeperPasswordManager implements PasswordManager {
return index;
}
private void sendInitialSms() throws Exception {
private boolean sendInitialSms(SecretValue password) throws Exception {
var sc = getOrStartShell();
var b = CommandBuilder.of()
.add(getExecutable(), "get")
.addLiteral("test");
.addLiteral("xpipe-test")
.add("--password")
.addLiteral(password.getSecretValue());
var file = sc.getSystemTemporaryDirectory().join("keeper" + Math.abs(new Random().nextInt()) + ".txt");
var input = """
@@ -119,20 +121,30 @@ public class KeeperPasswordManager implements PasswordManager {
""";
sc.view().writeTextFile(file, input);
var fullCommand = CommandBuilder.of()
var fullB = CommandBuilder.of()
.add(sc.getShellDialect() == ShellDialects.CMD ? "type" : "cat")
.addFile(file)
.add("|")
.add(b);
sc.command(fullCommand).sensitive().execute();
var command = sc.command(fullB);
command.killOnTimeout(CountDown.of().start(30_000));
command.sensitive();
var success = command.executeAndCheck();
// A fail indicates the query went through but the entry was not found
if (!success) {
return false;
} else {
return true;
}
}
@Override
public String constructKeeperInput(KeeperPasswordManager passwordManager) throws Exception {
sendInitialSms();
public String constructKeeperInput(KeeperPasswordManager passwordManager, SecretValue password) throws Exception {
var sent = sendInitialSms(password);
var index = getTotpDurationIndex();
if (passwordManager.isHasCompletedRequestInSession() && index > 0) {
if (!sent || (passwordManager.isHasCompletedRequestInSession() && index > 0)) {
var input = """
1
@@ -215,7 +227,7 @@ public class KeeperPasswordManager implements PasswordManager {
}
@Override
public String constructKeeperInput(KeeperPasswordManager passwordManager) {
public String constructKeeperInput(KeeperPasswordManager passwordManager, SecretValue password) {
var index = getTotpDurationIndex();
if (passwordManager.isHasCompletedRequestInSession() && index > 0) {
var input = """
@@ -284,7 +296,7 @@ public class KeeperPasswordManager implements PasswordManager {
class SecurityKey implements KeeperAuth {
@Override
public String constructKeeperInput(KeeperPasswordManager passwordManager) {
public String constructKeeperInput(KeeperPasswordManager passwordManager, SecretValue password) {
var input = """
1
@@ -337,7 +349,7 @@ public class KeeperPasswordManager implements PasswordManager {
}
@Override
public String constructKeeperInput(KeeperPasswordManager passwordManager) {
public String constructKeeperInput(KeeperPasswordManager passwordManager, SecretValue password) {
var input = """
1
@@ -369,7 +381,7 @@ public class KeeperPasswordManager implements PasswordManager {
}
@Override
public String constructKeeperInput(KeeperPasswordManager passwordManager) {
public String constructKeeperInput(KeeperPasswordManager passwordManager, SecretValue password) {
var input = """
1
@@ -493,7 +505,7 @@ public class KeeperPasswordManager implements PasswordManager {
FilePath file = sc.getSystemTemporaryDirectory().join("keeper" + Math.abs(new Random().nextInt()) + ".txt");
var effectiveTwoFactor = twoFactorAuth != null ? twoFactorAuth : new KeeperAuth.None();
var input = effectiveTwoFactor.constructKeeperInput(this);
var input = effectiveTwoFactor.constructKeeperInput(this, r);
if (input == null) {
return null;
}
@@ -32,7 +32,6 @@ public class LastpassPasswordManager implements PasswordManager {
@SuppressWarnings("unused")
public static OptionsBuilder createOptions(Property<LastpassPasswordManager> p) {
return new OptionsBuilder()
.disableAutoFocus()
.nameAndDescription("passwordManagerTest")
.addComp(new PasswordManagerTestComp(true));
}
@@ -55,7 +55,6 @@ public class OnePasswordManager implements PasswordManager {
.build();
return new OptionsBuilder()
.disableAutoFocus()
.nameAndDescription("onePasswordManagerAccount")
.addString(account)
.hide(account.isNull().and(availableAccounts.emptyProperty()))
@@ -67,7 +67,6 @@ public class PassboltPasswordManager implements PasswordManager {
chooser.setPrompt(new ReadOnlyObjectWrapper<>(FilePath.of("passbolt_private.asc")));
return new OptionsBuilder()
.disableAutoFocus()
.nameAndDescription("passboltServerUrl")
.addComp(
new TextFieldComp(serverUrl)
@@ -189,10 +189,10 @@ public interface PasswordManagerKeyStrategy {
static List<Class<?>> getClasses() {
var l = new ArrayList<Class<?>>();
l.add(Inline.class);
l.add(Agent.class);
l.add(KeePassXcOpenSshAgent.class);
l.add(KeePassXcPageant.class);
l.add(Inline.class);
return l;
}
}
@@ -23,7 +23,6 @@ public class WindowsCredentialManager implements PasswordManager {
@SuppressWarnings("unused")
public static OptionsBuilder createOptions(Property<WindowsCredentialManager> p) {
return new OptionsBuilder()
.disableAutoFocus()
.nameAndDescription("passwordManagerTest")
.addComp(new PasswordManagerTestComp(true));
}
@@ -234,3 +234,7 @@
.ikonli-font-icon.graphic.terminal-dock-button {
-fx-icon-color: -color-accent-fg;
}
.text-field:focused {
-fx-prompt-text-fill: -color-fg-subtle;
}
@@ -152,7 +152,6 @@ public class IdentityChoiceBuilder {
.allowAgentForward(allowAgentForward)
.allowKeyFileSync(true)
.perUserKeyFileCheck(() -> false)
.allowPasswordAgentKeyChoice(true)
.fileSystem(fileSystem)
.build();
@@ -42,7 +42,6 @@ public class LocalIdentityStoreProvider extends IdentityStoreProvider {
var sshIdentityChoiceConfig = SshIdentityStrategyChoiceConfig.builder()
.allowAgentForward(true)
.allowKeyFileSync(false)
.allowPasswordAgentKeyChoice(true)
.perUserKeyFileCheck(() -> false)
.build();
@@ -17,9 +17,24 @@ import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import java.util.List;
import java.util.UUID;
public class PasswordManagerIdentityStoreProvider extends IdentityStoreProvider {
@Override
public UUID getTargetCategory(DataStore store, UUID target) {
PasswordManagerIdentityStore st = (PasswordManagerIdentityStore) store;
if (!st.isPerUser()) {
return target;
}
var cat = DataStorage.get().getStoreCategoryIfPresent(target).orElseThrow();
var inSynced = DataStorage.get().getCategoryParentHierarchy(cat).stream()
.anyMatch(dataStoreCategory ->
dataStoreCategory.getUuid().equals(DataStorage.SYNCED_IDENTITIES_CATEGORY_UUID));
return inSynced ? target : DataStorage.SYNCED_IDENTITIES_CATEGORY_UUID;
}
@Override
public boolean allowCreation() {
return AppPrefs.get().passwordManager().getValue() != null;
@@ -33,12 +48,9 @@ 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();
@@ -48,7 +60,7 @@ public class PasswordManagerIdentityStoreProvider extends IdentityStoreProvider
var hideSshKeyChoice = Bindings.createBooleanBinding(() -> {
var pwman = AppPrefs.get().passwordManager().getValue();
var strat = pwman.getKeyConfiguration();
return !strat.useAgent() && !strat.useInline();
return strat.useInline();
}, AppPrefs.get().passwordManager());
var testComp = new PasswordManagerTestComp(key, false);
@@ -76,7 +76,6 @@ public class SyncedIdentityStoreProvider extends IdentityStoreProvider {
var sshIdentityChoiceConfig = SshIdentityStrategyChoiceConfig.builder()
.allowAgentForward(true)
.allowKeyFileSync(true)
.allowPasswordAgentKeyChoice(true)
.perUserKeyFileCheck(() -> perUser.get())
.build();
+1 -1
View File
@@ -1639,7 +1639,7 @@ sshAgentSocketDescription=The custom socket to use to communicate with the SSH a
publicKey=Key selector
publicKeyDescription=The optional public key to force the agent to only offer the matching private key
agentKeyName=Key selector
agentKeyNameDescription=The optional key name to force the agent to only offer the matching key
agentKeyNameDescription=The name of the key entry in the password manager
actions=Actions
hcloudServer.displayName=Hetzner cloud server
hcloudServer.displayDescription=Access a server hosted on Hetzner cloud via SSH