This commit is contained in:
crschnick
2025-05-28 10:23:40 +00:00
parent 7e6a8a8fb5
commit 54bfea6779
9 changed files with 78 additions and 26 deletions
@@ -15,7 +15,7 @@ import io.xpipe.app.ext.DataStoreProviders;
import io.xpipe.app.ext.ProcessControlProvider;
import io.xpipe.app.icon.SystemIconManager;
import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.password.KeePassXcManager;
import io.xpipe.app.password.KeePassXcPasswordManager;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.resources.*;
import io.xpipe.app.storage.DataStorage;
@@ -180,7 +180,7 @@ public class BaseMode extends OperationMode {
ProcessControlProvider.get().reset();
AppPrefs.reset();
AppBeaconServer.reset();
KeePassXcManager.reset();
KeePassXcPasswordManager.reset();
StoreViewState.reset();
AppLayoutModel.reset();
AppTheme.reset();
@@ -9,6 +9,8 @@ import io.xpipe.core.process.ShellControl;
import io.xpipe.core.process.ShellScript;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.core.util.InPlaceSecretValue;
import io.xpipe.core.util.JacksonMapper;
@JsonTypeName("dashlane")
public class DashlanePasswordManager implements PasswordManager {
@@ -45,10 +47,13 @@ public class DashlanePasswordManager implements PasswordManager {
}
var out = sc.command(CommandBuilder.of()
.add("dcli", "password", "--output", "console")
.add("dcli", "password", "--output", "console", "-o", "json")
.addLiteral(key))
.readStdoutOrThrow();
return null;
var tree = JacksonMapper.getDefault().readTree(out);
var login = tree.get("login");
var password = tree.get("password");
return new CredentialResult(login != null ? login.asText() : null, password != null ? InPlaceSecretValue.of(password.asText()) : null);
} catch (Exception ex) {
ErrorEvent.fromThrowable(ex).handle();
return null;
@@ -13,6 +13,7 @@ import io.xpipe.core.process.CommandBuilder;
import io.xpipe.core.process.ShellControl;
import io.xpipe.core.process.ShellScript;
import io.xpipe.core.store.FilePath;
import io.xpipe.core.util.InPlaceSecretValue;
import io.xpipe.core.util.JacksonMapper;
import javafx.application.Platform;
import javafx.beans.property.Property;
@@ -135,8 +136,9 @@ public class EnpassPasswordManager implements PasswordManager {
throw ErrorEvent.expected(new IllegalArgumentException("Ambiguous item name, multiple password entries match: " + String.join(", ", matches)));
}
var secret = json.get(0).get("password").asText();
return null;
var login = json.get(0).required("login").asText();
var secret = json.get(0).required("password").asText();
return new CredentialResult(!login.isEmpty() ? login : null, !secret.isEmpty() ? InPlaceSecretValue.of(secret) : null);
}
} catch (Exception ex) {
ErrorEvent.fromThrowable(ex).handle();
@@ -28,13 +28,13 @@ import java.util.regex.Pattern;
@ToString
@Jacksonized
@JsonTypeName("keePassXc")
public class KeePassXcManager implements PasswordManager {
public class KeePassXcPasswordManager implements PasswordManager {
private static KeePassXcProxyClient client;
private final KeePassXcAssociationKey associationKey;
public static OptionsBuilder createOptions(Property<KeePassXcManager> p) {
public static OptionsBuilder createOptions(Property<KeePassXcPasswordManager> p) {
var prop = new SimpleObjectProperty<KeePassXcAssociationKey>();
p.subscribe(keePassXcManager -> {
prop.set(keePassXcManager != null ? keePassXcManager.getAssociationKey() : null);
@@ -63,7 +63,7 @@ public class KeePassXcManager implements PasswordManager {
.addProperty(prop)
.bind(
() -> {
return new KeePassXcManager(prop.getValue());
return new KeePassXcPasswordManager(prop.getValue());
},
p);
}
@@ -114,7 +114,7 @@ public class KeePassXcManager implements PasswordManager {
c.exchangeKeys();
var pref = AppPrefs.get().passwordManager();
KeePassXcAssociationKey cached =
pref.getValue() instanceof KeePassXcManager kpm ? kpm.getAssociationKey() : null;
pref.getValue() instanceof KeePassXcPasswordManager kpm ? kpm.getAssociationKey() : null;
if (cached != null) {
c.useExistingAssociationKey(cached);
try {
@@ -128,7 +128,7 @@ public class KeePassXcManager implements PasswordManager {
if (cached == null) {
c.associate();
c.testAssociation();
if (pref.getValue() instanceof KeePassXcManager kpm
if (pref.getValue() instanceof KeePassXcPasswordManager kpm
&& !c.getAssociationKey().equals(kpm.getAssociationKey())) {
AppPrefs.get()
.setFromExternal(
@@ -192,7 +192,7 @@ public class KeePassXcManager implements PasswordManager {
@Override
public CredentialResult retrieveCredentials(String key) {
try {
return KeePassXcManager.receive(key);
return KeePassXcPasswordManager.receive(key);
} catch (Exception e) {
ErrorEvent.fromThrowable(e).handle();
return null;
@@ -1,5 +1,6 @@
package io.xpipe.app.password;
import com.fasterxml.jackson.databind.JsonNode;
import io.xpipe.app.ext.ProcessControlProvider;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.terminal.TerminalLauncher;
@@ -9,6 +10,8 @@ import io.xpipe.app.util.SecretRetrievalStrategy;
import io.xpipe.core.process.*;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.core.util.InPlaceSecretValue;
import io.xpipe.core.util.JacksonMapper;
import java.util.UUID;
@@ -67,10 +70,35 @@ public class KeeperPasswordManager implements PasswordManager {
var out = sc.command(CommandBuilder.of()
.add(getExecutable(sc), "get")
.addLiteral(key)
.add("--format", "password", "--unmask", "--password")
.add("--format", "json", "--unmask")
.add("--password")
.addLiteral(r.getSecretValue()))
.readStdoutOrThrow();
return null;
var tree = JacksonMapper.getDefault().readTree(out);
var fields = tree.required("fields");
if (!fields.isArray()) {
return null;
}
String login = null;
String password = null;
for (JsonNode field : fields) {
var type = field.required("type").asText();
if (type.equals("login")) {
var v = field.required("value");
if (v.size() > 0) {
login = v.get(0).asText();
}
}
if (type.equals("password")) {
var v = field.required("value");
if (v.size() > 0) {
password = v.get(0).asText();
}
}
}
return new CredentialResult(login, password != null ? InPlaceSecretValue.of(password) : null);
} catch (Exception ex) {
ErrorEvent.fromThrowable(ex).handle();
return null;
@@ -7,6 +7,10 @@ import io.xpipe.app.util.*;
import io.xpipe.core.process.*;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.core.util.InPlaceSecretValue;
import io.xpipe.core.util.JacksonMapper;
import java.util.ArrayList;
@JsonTypeName("lastpass")
public class LastpassPasswordManager implements PasswordManager {
@@ -47,10 +51,25 @@ public class LastpassPasswordManager implements PasswordManager {
var out = sc.command(CommandBuilder.of()
.add("lpass", "show")
.add("--fixed-strings", "--password")
.add("--fixed-strings", "--json")
.addLiteral(key))
.readStdoutOrThrow();
return null;
var tree = JacksonMapper.getDefault().readTree(out);
if (tree.size() > 1) {
var matches = new ArrayList<String>();
tree.iterator().forEachRemaining(item -> {
var title = item.get("name");
if (title != null) {
matches.add(title.asText());
}
});
throw ErrorEvent.expected(new IllegalArgumentException("Ambiguous item name, multiple password entries match: " + String.join(", ", matches)));
}
var username = tree.get(0).required("username").asText();
var password = tree.get(0).required("password").asText();
return new CredentialResult(!username.isEmpty() ? username : null, !password.isEmpty() ? InPlaceSecretValue.of(password) : null);
} catch (Exception ex) {
ErrorEvent.fromThrowable(ex).handle();
return null;
@@ -2,7 +2,6 @@ package io.xpipe.app.password;
import io.xpipe.core.process.OsType;
import io.xpipe.core.util.SecretValue;
import io.xpipe.core.util.ValidationException;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Value;
@@ -16,7 +15,7 @@ public interface PasswordManager {
static List<Class<?>> getClasses() {
var l = new ArrayList<Class<?>>();
l.add(OnePasswordManager.class);
l.add(KeePassXcManager.class);
l.add(KeePassXcPasswordManager.class);
l.add(BitwardenPasswordManager.class);
l.add(DashlanePasswordManager.class);
if (OsType.getLocal() != OsType.WINDOWS) {
@@ -84,20 +84,21 @@ public class PsonoPasswordManager implements PasswordManager {
}
try {
getOrStartShell().view().setSensitiveEnvironmentVariable("PSONO_CI_API_KEY_ID", apiKey.getSecretValue());
getOrStartShell().view().setSensitiveEnvironmentVariable("PSONO_CI_API_SECRET_KEY_HEX", apiSecretKey.getSecretValue());
var cmd = getOrStartShell()
.command(CommandBuilder.of()
.add("psonoci", "--api-key-id")
.addLiteral(apiKey.getSecretValue())
.add("--api-secret-key-hex")
.addLiteral(apiSecretKey.getSecretValue())
.add("psonoci")
.add("--server-url")
.addLiteral(serverUrl)
.add("secret", "get")
.addLiteral(key)
.add("json"));
cmd.setSensitive();;
cmd.setSensitive();
var r = JacksonMapper.getDefault().readTree(cmd.readStdoutOrThrow());
return null;
var username = r.required("username");
var password = r.required("password");
return new CredentialResult(username.isNull() ? null : username.asText(), password.isNull() ? null : InPlaceSecretValue.of(password.asText()));
} catch (Exception e) {
ErrorEvent.fromThrowable(e).handle();
return null;
@@ -77,8 +77,6 @@ public interface CommandControl extends ProcessControl {
String readStdoutOrThrow() throws Exception;
SecretValue readStdoutSecretOrThrow() throws Exception;
Optional<String> readStdoutIfPossible() throws Exception;
default boolean discardAndCheckExit() throws ProcessOutputException {