This commit is contained in:
crschnick
2026-03-15 00:17:57 +00:00
parent 3add2eac21
commit 0eeaea2c8c
27 changed files with 106 additions and 18 deletions
@@ -39,22 +39,26 @@ public class PasswordManagerAgentStrategy implements SshIdentityAgentStrategy {
var identifier =
new SimpleStringProperty(p.getValue() != null ? p.getValue().getIdentifier() : null);
var pwmanBinding = Bindings.createObjectBinding(() -> {
var pwmanError = Bindings.createObjectBinding(() -> {
var pwman = AppPrefs.get().passwordManager().getValue();
if (pwman == null) {
return AppI18n.get("passwordManagerEmpty");
}
if (!pwman.getKeyConfiguration().useAgent()) {
if (!pwman.supportsKeyConfiguration()) {
return AppI18n.get("passwordManagerNoAgentSupport");
}
if (!pwman.getKeyConfiguration().useAgent()) {
return AppI18n.get("passwordManagerNoAgentConfigured");
}
return null;
}, AppPrefs.get().passwordManager(), AppI18n.activeLanguage());
var pwmanProp = new SimpleStringProperty();
pwmanProp.bind(pwmanBinding);
var pwmanErrorProp = new SimpleStringProperty();
pwmanErrorProp.bind(pwmanError);
var pwmanDisplay = new HorizontalComp(List.of(
new LabelComp(pwmanProp)
new LabelComp(pwmanErrorProp)
.maxWidth(10000)
.apply(label -> label.setAlignment(Pos.CENTER_LEFT))
.hgrow(),
@@ -67,9 +71,10 @@ public class PasswordManagerAgentStrategy implements SshIdentityAgentStrategy {
return new OptionsBuilder()
.nameAndDescription("passwordManagerSshKeyConfig")
.addComp(pwmanDisplay)
.hide(pwmanProp.isNull())
.hide(pwmanErrorProp.isNull())
.nameAndDescription(useKeyName() ? "agentKeyName" : "publicKey")
.addComp(new SshAgentKeyListComp(config.getFileSystem(), p, identifier, useKeyName()), identifier)
.disable(pwmanErrorProp.isNotNull())
.nonNull()
.hide(!config.isAllowAgentForward())
.bind(
@@ -61,7 +61,7 @@ public class SshAgentKeyList {
var type = matcher.group(1);
var publicKey = matcher.group(2);
var name = matcher.groupCount() > 3 ? matcher.group(3) : null;
var name = matcher.groupCount() > 2 ? matcher.group(3) : null;
list.add(new Entry(type, publicKey, name));
}
return list;
@@ -74,6 +74,7 @@ public class SshAgentKeyListComp extends SimpleRegionBuilder {
});
entryButton.setMinWidth(400);
entryButton.setAlignment(Pos.CENTER_LEFT);
entryButton.setMnemonicParsing(false);
content.getChildren().add(entryButton);
}
popover.setContentNode(content);
@@ -52,6 +52,7 @@ public class SshAgentTestComp extends SimpleRegionBuilder {
entryButton.getStyleClass().add(Styles.FLAT);
entryButton.setMinWidth(400);
entryButton.setAlignment(Pos.CENTER_LEFT);
entryButton.setMnemonicParsing(false);
content.getChildren().add(entryButton);
}
popover.setContentNode(content);
@@ -234,6 +234,7 @@ public class StoreCategoryComp extends SimpleRegionBuilder {
newCategory.setOnAction(event -> {
StoreViewState.get().createNewCategory(category);
});
newCategory.setDisable(!DataStorage.get().canCreateStoreCategoryWithin(category.getCategory()));
contextMenu.getItems().add(newCategory);
contextMenu.getItems().add(new SeparatorMenuItem());
@@ -107,12 +107,11 @@ public class PasswordManagerTestComp extends SimpleRegionBuilder {
}
List<String> elements = new ArrayList<>();
if (r.getCredentials() != null && r.getCredentials().getUsername() != null) {
elements.add(r.getCredentials().getUsername());
}
if (r.getCredentials() != null && r.getCredentials().getPassword() != null) {
elements.add("[" + r.getCredentials().getPassword().getSecretValue() + "]");
if (r.getCredentials() != null) {
elements.add(r.getCredentials().getUsername() != null ? r.getCredentials().getUsername() : "<no user>");
elements.add(r.getCredentials().getPassword() != null ? ("[" + r.getCredentials().getPassword().getSecretValue() + "]") : "<no password>");
} else {
elements.add("<no credentials>");
}
if (r.getSshKey() != null) {
@@ -273,6 +273,11 @@ public class BitwardenPasswordManager implements PasswordManager {
return "https://bitwarden.com/";
}
@Override
public boolean supportsKeyConfiguration() {
return true;
}
@Override
public PasswordManagerKeyConfiguration getKeyConfiguration() {
return PasswordManagerKeyConfiguration.of(true, false, true, keyStrategy, getSocketLocation());
@@ -24,6 +24,11 @@ public class DashlanePasswordManager implements PasswordManager {
private static ShellControl SHELL;
@Override
public boolean supportsKeyConfiguration() {
return false;
}
@Override
public boolean selectInitial() throws Exception {
return LocalShell.getShell().view().findProgram("dcli").isPresent();
@@ -47,6 +47,11 @@ public class EnpassPasswordManager implements PasswordManager {
private static ShellControl SHELL;
private final FilePath vaultPath;
@Override
public boolean supportsKeyConfiguration() {
return false;
}
@Override
public boolean selectInitial() throws Exception {
return LocalShell.getShell().view().findProgram("enpass-cli").isPresent();
@@ -38,6 +38,11 @@ import java.util.*;
@JsonTypeName("hashicorpVault")
public class HashicorpVaultPasswordManager implements PasswordManager {
@Override
public boolean supportsKeyConfiguration() {
return false;
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
public interface VaultAuth {
@@ -43,6 +43,11 @@ public class KeePassXcPasswordManager implements PasswordManager {
private final List<KeePassXcAssociationKey> associationKeys;
private final PasswordManagerKeyStrategy keyStrategy;
@Override
public boolean supportsKeyConfiguration() {
return true;
}
@Override
public boolean selectInitial() throws Exception {
return findKeePassProxy().isPresent();
@@ -51,6 +51,11 @@ public class KeeperPasswordManager implements PasswordManager {
return socket;
}
@Override
public boolean supportsKeyConfiguration() {
return true;
}
@Override
public boolean selectInitial() throws Exception {
return LocalShell.getShell().view().findProgram("keeper-commander").isPresent();
@@ -37,6 +37,11 @@ public class LastpassPasswordManager implements PasswordManager {
private static ShellControl SHELL;
@Override
public boolean supportsKeyConfiguration() {
return false;
}
@SuppressWarnings("unused")
public static OptionsBuilder createOptions(Property<LastpassPasswordManager> p) {
return new OptionsBuilder()
@@ -31,6 +31,11 @@ import java.util.regex.Pattern;
@Getter
public class OnePasswordManager implements PasswordManager {
@Override
public boolean supportsKeyConfiguration() {
return true;
}
@Override
public PasswordManagerKeyConfiguration getKeyConfiguration() {
return PasswordManagerKeyConfiguration.of(true, false, true, keyStrategy, getSocketLocation());
@@ -47,6 +47,11 @@ public class PassboltPasswordManager implements PasswordManager {
private final InPlaceSecretValue passphrase;
private final Path privateKey;
@Override
public boolean supportsKeyConfiguration() {
return false;
}
@Override
public boolean selectInitial() throws Exception {
return LocalShell.getShell().view().findProgram("passbolt").isPresent();
@@ -92,6 +92,8 @@ public interface PasswordManager {
String getWebsite();
boolean supportsKeyConfiguration();
PasswordManagerKeyConfiguration getKeyConfiguration();
boolean selectInitial() throws Exception;
@@ -42,6 +42,11 @@ public class PasswordManagerCommand implements PasswordManager {
return PasswordManagerKeyConfiguration.none();
}
@Override
public boolean supportsKeyConfiguration() {
return false;
}
@SuppressWarnings("unused")
static OptionsBuilder createOptions(Property<PasswordManagerCommand> property) {
var template = new SimpleObjectProperty<PasswordManagerCommandTemplate>();
@@ -25,6 +25,11 @@ import java.util.List;
@Getter
public class ProtonPasswordManager implements PasswordManager {
@Override
public boolean supportsKeyConfiguration() {
return true;
}
@Override
public PasswordManagerKeyConfiguration getKeyConfiguration() {
return PasswordManagerKeyConfiguration.of(false, false, true, keyStrategy, getSocketLocation());
@@ -39,6 +39,11 @@ public class PsonoPasswordManager implements PasswordManager {
private final InPlaceSecretValue apiSecretKey;
private final String serverUrl;
@Override
public boolean supportsKeyConfiguration() {
return false;
}
@Override
public boolean selectInitial() throws Exception {
return LocalShell.getShell().view().findProgram("psonoci").isPresent();
@@ -17,6 +17,11 @@ public class WindowsCredentialManager implements PasswordManager {
private static boolean loaded = false;
@Override
public boolean supportsKeyConfiguration() {
return false;
}
@Override
public PasswordManagerKeyConfiguration getKeyConfiguration() {
return PasswordManagerKeyConfiguration.none();
@@ -423,6 +423,7 @@ public abstract class DataStorage {
other.finalizeEntry();
}
}
entry.finalizeEntry();
}
private Collection<DataStoreEntryRef<?>> getDependencies(DataStoreEntry entry) {
@@ -1028,6 +1029,10 @@ public abstract class DataStorage {
saveAsync();
}
public boolean canCreateStoreCategoryWithin(@NonNull DataStoreCategory cat) {
return !cat.getUuid().equals(ALL_IDENTITIES_CATEGORY_UUID);
}
public boolean canDeleteStoreCategory(@NonNull DataStoreCategory cat) {
if (cat.getParentCategory() == null) {
return false;
+1 -1
View File
@@ -6,7 +6,7 @@ import java.util.stream.Stream
plugins {
id("io.github.gradle-nexus.publish-plugin") version "2.0.0"
id 'org.gradlex.extra-java-module-info' version '1.14' apply false
id("com.diffplug.spotless") version "8.2.1" apply false
id("com.diffplug.spotless") version "8.3.0" apply false
}
allprojects { subproject ->
@@ -3,6 +3,7 @@ package io.xpipe.ext.base.identity;
import io.xpipe.app.cred.UsernameStrategy;
import io.xpipe.app.ext.DataStore;
import io.xpipe.app.ext.SelfReferentialStore;
import io.xpipe.app.ext.ValidationException;
import io.xpipe.app.secret.SecretRetrievalStrategy;
import io.xpipe.app.cred.SshIdentityStrategy;
@@ -24,7 +25,7 @@ public abstract class IdentityStore implements SelfReferentialStore, DataStore {
public abstract SshIdentityStrategy getSshIdentity();
@Override
public void checkComplete() throws Throwable {
public void checkComplete() throws ValidationException {
if (getPassword() != null) {
getPassword().checkComplete();
}
@@ -160,6 +160,7 @@ public interface IdentityValue {
public void checkComplete() throws ValidationException {
Validators.nonNull(ref);
Validators.isType(ref, IdentityStore.class);
ref.getStore().checkComplete();
}
@Override
@@ -4,6 +4,7 @@ import io.xpipe.app.cred.*;
import io.xpipe.app.ext.InternalCacheDataStore;
import io.xpipe.app.ext.UserScopeStore;
import io.xpipe.app.ext.ValidatableStore;
import io.xpipe.app.ext.ValidationException;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.process.CommandBuilder;
@@ -73,6 +74,11 @@ public class PasswordManagerIdentityStore extends IdentityStore
new UnsupportedOperationException("Credentials were requested but not supplied"));
}
if (r.getSshKey() != null && r.getCredentials() == null) {
throw ErrorEventFactory.expected(
new UnsupportedOperationException("Identity " + key + " does not provide credentials, only a key. Use another credentials entry as a base and reference the key via the password manager agent option instead"));
}
if (r.getCredentials() == null) {
throw ErrorEventFactory.expected(
new UnsupportedOperationException("Identity " + key + " does not provide credentials"));
@@ -219,7 +225,7 @@ public class PasswordManagerIdentityStore extends IdentityStore
}
@Override
public void checkComplete() throws Throwable {
public void checkComplete() throws ValidationException {
Validators.nonNull(key);
if (sshKey != null) {
sshKey.checkComplete();
@@ -53,7 +53,7 @@ public class SyncedIdentityStore extends IdentityStore implements UserScopeStore
}
@Override
public void checkComplete() throws Throwable {
public void checkComplete() throws ValidationException {
super.checkComplete();
if (getSshIdentity() instanceof KeyFileStrategy f) {
if (!f.getFile().isInDataDirectory()) {
+2 -1
View File
@@ -2007,7 +2007,8 @@ passwordManagerKeyStrategy=SSH key retrieval method
passwordManagerKeyStrategyDescription=How to retrieve SSH keys stored in the password manager. When disabled, no keys can be retrieved. When an SSH agent is configured, the agent must be properly started in the password manager.
sshAgentNoKeys=The agent does not have any keys at the moment
sshAgentHasKeys=The agent currently offers the following keys:
passwordManagerNoAgentSupport=Password manager does not support retrieving SSH keys
passwordManagerNoAgentSupport=Password manager does not support retrieving SSH keys via an agent
passwordManagerNoAgentConfigured=XPipe is not configured for retrieving SSH keys from the agent
passwordManagerEmpty=No password manager is configured
passwordManagerSshKeyConfig=Password manager configuration
passwordManagerSshKeyConfigDescription=Check the status of the password manager