mirror of
https://github.com/xpipe-io/xpipe.git
synced 2026-09-24 01:15:35 +00:00
Rework
This commit is contained in:
@@ -75,16 +75,12 @@ public class CustomAgentStrategy implements SshIdentityStrategy {
|
||||
i -> {
|
||||
return i != null;
|
||||
}))
|
||||
.nameAndDescription("publicKey")
|
||||
.addComp(new SshAgentKeyListComp(config.getFileSystem(), p, publicKey), publicKey)
|
||||
.nameAndDescription("forwardAgent")
|
||||
.addToggle(forward)
|
||||
.nonNull()
|
||||
.hide(!config.isAllowAgentForward())
|
||||
.nameAndDescription("publicKey")
|
||||
.addComp(
|
||||
new TextFieldComp(publicKey)
|
||||
.apply(struc -> struc.setPromptText(
|
||||
"ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIBmhLUTJiP...== Your Comment")),
|
||||
publicKey)
|
||||
.bind(
|
||||
() -> {
|
||||
return new CustomAgentStrategy(forward.get(), publicKey.get());
|
||||
|
||||
@@ -36,11 +36,11 @@ public class GpgAgentStrategy implements SshIdentityStrategy {
|
||||
var publicKey =
|
||||
new SimpleStringProperty(p.getValue() != null ? p.getValue().getPublicKey() : null);
|
||||
return new OptionsBuilder()
|
||||
.nameAndDescription("publicKey")
|
||||
.addComp(new SshAgentKeyListComp(config.getFileSystem(), p, publicKey), publicKey)
|
||||
.nameAndDescription("forwardAgent")
|
||||
.addToggle(forward)
|
||||
.nonNull()
|
||||
.hide(!config.isAllowAgentForward())
|
||||
.nameAndDescription("publicKey")
|
||||
.addComp(
|
||||
new TextFieldComp(publicKey)
|
||||
.apply(struc -> struc.setPromptText(
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
package io.xpipe.app.cred;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.comp.base.HorizontalComp;
|
||||
import io.xpipe.app.comp.base.TextFieldComp;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
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.core.KeyValue;
|
||||
import io.xpipe.core.OsType;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.geometry.Insets;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@JsonTypeName("customAgent")
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
public class InternalCustomAgentStrategy implements SshIdentityStrategy {
|
||||
|
||||
boolean forwardAgent;
|
||||
String publicKey;
|
||||
|
||||
@Override
|
||||
public void prepareParent(ShellControl parent) throws Exception {
|
||||
if (parent.isLocal()) {
|
||||
var agent = AppPrefs.get().sshAgentSocket().getValue();
|
||||
if (agent == null) {
|
||||
agent = AppPrefs.get().defaultSshAgentSocket().getValue();
|
||||
}
|
||||
SshIdentityStateManager.prepareLocalCustomAgent(
|
||||
parent, agent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildCommand(CommandBuilder builder) {}
|
||||
|
||||
private String getIdentityAgent(ShellControl sc) throws Exception {
|
||||
if (!sc.isLocal() || sc.getOsType() == OsType.WINDOWS) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (AppPrefs.get() != null) {
|
||||
var agent = AppPrefs.get().sshAgentSocket().getValue();
|
||||
if (agent == null) {
|
||||
agent = AppPrefs.get().defaultSshAgentSocket().getValue();
|
||||
}
|
||||
if (agent != null) {
|
||||
return agent.resolveTildeHome(sc.view().userHome()).toString();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyValue> configOptions(ShellControl sc) throws Exception {
|
||||
var file = SshIdentityStrategy.getPublicKeyPath(sc, publicKey);
|
||||
var l = new ArrayList<>(List.of(
|
||||
new KeyValue("IdentitiesOnly", file.isPresent() ? "yes" : "no"),
|
||||
new KeyValue("ForwardAgent", forwardAgent ? "yes" : "no"),
|
||||
new KeyValue("IdentityFile", file.isPresent() ? file.get().toString() : "none"),
|
||||
new KeyValue("PKCS11Provider", "none")));
|
||||
|
||||
var agent = getIdentityAgent(sc);
|
||||
if (agent != null) {
|
||||
l.add(new KeyValue("IdentityAgent", "\"" + agent + "\""));
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
}
|
||||
@@ -39,16 +39,12 @@ public class OpenSshAgentStrategy implements SshIdentityStrategy {
|
||||
.nameAndDescription("agentSocket")
|
||||
.addStaticString(socket != null ? socket : AppI18n.get("agentSocketNotFound"))
|
||||
.hide(OsType.ofLocal() == OsType.WINDOWS)
|
||||
.nameAndDescription("publicKey")
|
||||
.addComp(new SshAgentKeyListComp(config.getFileSystem(), p, publicKey), publicKey)
|
||||
.nameAndDescription("forwardAgent")
|
||||
.addToggle(forward)
|
||||
.nonNull()
|
||||
.hide(!config.isAllowAgentForward())
|
||||
.nameAndDescription("publicKey")
|
||||
.addComp(
|
||||
new TextFieldComp(publicKey)
|
||||
.apply(struc -> struc.setPromptText(
|
||||
"ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIBmhLUTJiP...== Your Comment")),
|
||||
publicKey)
|
||||
.bind(
|
||||
() -> {
|
||||
return new OpenSshAgentStrategy(forward.get(), publicKey.get());
|
||||
|
||||
@@ -31,16 +31,13 @@ public class OtherExternalAgentStrategy implements SshIdentityStrategy {
|
||||
var publicKey =
|
||||
new SimpleStringProperty(p.getValue() != null ? p.getValue().getPublicKey() : null);
|
||||
return new OptionsBuilder()
|
||||
.nameAndDescription("publicKey")
|
||||
.addComp(new SshAgentKeyListComp(config.getFileSystem(), p, publicKey), publicKey)
|
||||
.nameAndDescription("forwardAgent")
|
||||
.addToggle(forward)
|
||||
.nonNull()
|
||||
.hide(!config.isAllowAgentForward())
|
||||
.nameAndDescription("publicKey")
|
||||
.addComp(
|
||||
new TextFieldComp(publicKey)
|
||||
.apply(struc -> struc.setPromptText(
|
||||
"ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIBmhLUTJiP...== Your Comment")),
|
||||
publicKey)
|
||||
.bind(
|
||||
() -> {
|
||||
return new OtherExternalAgentStrategy(forward.get(), publicKey.get());
|
||||
|
||||
@@ -39,16 +39,12 @@ public class PageantStrategy implements SshIdentityStrategy {
|
||||
var publicKey =
|
||||
new SimpleStringProperty(p.getValue() != null ? p.getValue().getPublicKey() : null);
|
||||
return new OptionsBuilder()
|
||||
.nameAndDescription("publicKey")
|
||||
.addComp(new SshAgentKeyListComp(config.getFileSystem(), p, publicKey), publicKey)
|
||||
.nameAndDescription("forwardAgent")
|
||||
.addToggle(forward)
|
||||
.nonNull()
|
||||
.hide(!config.isAllowAgentForward())
|
||||
.nameAndDescription("publicKey")
|
||||
.addComp(
|
||||
new TextFieldComp(publicKey)
|
||||
.apply(struc -> struc.setPromptText(
|
||||
"ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIBmhLUTJiP...== Your Comment")),
|
||||
publicKey)
|
||||
.bind(
|
||||
() -> {
|
||||
return new PageantStrategy(forward.get(), publicKey.get());
|
||||
@@ -92,6 +88,9 @@ public class PageantStrategy implements SshIdentityStrategy {
|
||||
throw ErrorEventFactory.expected(new IllegalStateException(
|
||||
"Pageant is not running as the primary agent via the $SSH_AUTH_SOCK variable."));
|
||||
}
|
||||
} else if (parent.isLocal()) {
|
||||
// Check if it exists
|
||||
getPageantWindowsPipe();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package io.xpipe.app.cred;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.comp.base.HorizontalComp;
|
||||
import io.xpipe.app.comp.base.TextFieldComp;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
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.core.KeyValue;
|
||||
import io.xpipe.core.OsType;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.geometry.Insets;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@JsonTypeName("passwordManager")
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
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 publicKey =
|
||||
new SimpleStringProperty(p.getValue() != null ? p.getValue().getPublicKey() : null);
|
||||
|
||||
var pwmanBinding = Bindings.createObjectBinding(() -> {
|
||||
var pwman = AppPrefs.get().passwordManager().getValue();
|
||||
if (pwman == null) {
|
||||
return AppI18n.get("passwordManagerEmpty");
|
||||
}
|
||||
|
||||
if (!pwman.getKeyStrategy().supportsAgent()) {
|
||||
return AppI18n.get("passwordManagerNoAgentSupport");
|
||||
}
|
||||
|
||||
return null;
|
||||
}, AppPrefs.get().passwordManager(), AppI18n.activeLanguage());
|
||||
var pwmanProp = new SimpleStringProperty();
|
||||
pwmanProp.bind(pwmanBinding);
|
||||
var pwmanDisplay = new HorizontalComp(List.of(
|
||||
new TextFieldComp(pwmanProp)
|
||||
.apply(struc -> struc.setEditable(false))
|
||||
.hgrow(),
|
||||
new ButtonComp(null, new FontIcon("mdomz-settings"), () -> {
|
||||
AppPrefs.get().selectCategory("passwordManager");
|
||||
})
|
||||
.padding(new Insets(7))))
|
||||
.spacing(9);
|
||||
|
||||
return new OptionsBuilder()
|
||||
.nameAndDescription("passwordManagerSshKeyConfig")
|
||||
.addComp(pwmanDisplay)
|
||||
.nameAndDescription("publicKey")
|
||||
.addComp(new SshAgentKeyListComp(config.getFileSystem(), p, publicKey), publicKey)
|
||||
.nameAndDescription("forwardAgent")
|
||||
.addToggle(forward)
|
||||
.nonNull()
|
||||
.hide(!config.isAllowAgentForward())
|
||||
.bind(
|
||||
() -> {
|
||||
return new PasswordManagerAgentStrategy(forward.get(), publicKey.get());
|
||||
},
|
||||
p);
|
||||
}
|
||||
|
||||
boolean forwardAgent;
|
||||
String publicKey;
|
||||
|
||||
@Override
|
||||
public void prepareParent(ShellControl parent) throws Exception {
|
||||
var pwman = AppPrefs.get().passwordManager().getValue();
|
||||
var strat = pwman.getKeyStrategy().getSshIdentityStrategy();
|
||||
strat.prepareParent(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildCommand(CommandBuilder builder) {
|
||||
var pwman = AppPrefs.get().passwordManager().getValue();
|
||||
var strat = pwman.getKeyStrategy().getSshIdentityStrategy();
|
||||
strat.buildCommand(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyValue> configOptions(ShellControl sc) throws Exception {
|
||||
var pwman = AppPrefs.get().passwordManager().getValue();
|
||||
var strat = pwman.getKeyStrategy().getSshIdentityStrategy();
|
||||
return strat.configOptions(sc);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package io.xpipe.app.cred;
|
||||
|
||||
import io.xpipe.app.ext.ShellStore;
|
||||
import io.xpipe.app.process.CommandBuilder;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class SshAgentKeyList {
|
||||
|
||||
@Value
|
||||
public static class Entry {
|
||||
|
||||
String type;
|
||||
String publicKey;
|
||||
String name;
|
||||
}
|
||||
|
||||
public static List<Entry> listAgentIdentities(DataStoreEntryRef<ShellStore> ref, SshIdentityStrategy strategy) throws Exception {
|
||||
var session = ref.getStore().getOrStartSession();
|
||||
strategy.prepareParent(session);
|
||||
|
||||
var out = session.command(CommandBuilder.of().add("ssh-add", "-L")).readStdoutOrThrow();
|
||||
var pattern = Pattern.compile("([^ ]+) ([^ ]+) (.+)");
|
||||
var lines = out.lines().toList();
|
||||
var list = new ArrayList<Entry>();
|
||||
for (String line : lines) {
|
||||
var matcher = pattern.matcher(line);
|
||||
if (!matcher.matches()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var type = matcher.group(1);
|
||||
var publicKey = matcher.group(2);
|
||||
var name = matcher.group(3);
|
||||
list.add(new Entry(type, publicKey, name));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package io.xpipe.app.cred;
|
||||
|
||||
import atlantafx.base.controls.Popover;
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.comp.SimpleRegionBuilder;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.comp.base.IconButtonComp;
|
||||
import io.xpipe.app.comp.base.InputGroupComp;
|
||||
import io.xpipe.app.comp.base.TextFieldComp;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.ext.ShellStore;
|
||||
import io.xpipe.app.platform.LabelGraphic;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SshAgentKeyListComp extends SimpleRegionBuilder {
|
||||
|
||||
private final ObservableValue<DataStoreEntryRef<ShellStore>> ref;
|
||||
private final ObservableValue<? extends SshIdentityStrategy> sshIdentityStrategy;
|
||||
private final StringProperty value;
|
||||
|
||||
public SshAgentKeyListComp(ObservableValue<DataStoreEntryRef<ShellStore>> ref, ObservableValue<? extends SshIdentityStrategy> sshIdentityStrategy, StringProperty value) {
|
||||
this.ref = ref;
|
||||
this.sshIdentityStrategy = sshIdentityStrategy;
|
||||
this.value = value;}
|
||||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
var field = new TextFieldComp(value);
|
||||
field.apply(struc -> struc.setPromptText(
|
||||
"ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIBmhLUTJiP...== <key comment>"));
|
||||
var button = new ButtonComp(null, new LabelGraphic.IconGraphic("mdi2m-magnify-scan"), null);
|
||||
button.apply(struc -> {
|
||||
struc.setOnAction(event -> {
|
||||
DataStoreEntryRef<ShellStore> refToUse = ref.getValue() != null ? ref.getValue() : DataStorage.get().local().ref();
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
var list = SshAgentKeyList.listAgentIdentities(refToUse, sshIdentityStrategy.getValue());
|
||||
Platform.runLater(() -> {
|
||||
var popover = new Popover();
|
||||
popover.setArrowLocation(Popover.ArrowLocation.TOP_CENTER);
|
||||
|
||||
if (list.size() > 0) {
|
||||
var content = new VBox();
|
||||
content.setPadding(new Insets(10));
|
||||
content.setFillWidth(true);
|
||||
content.getChildren().add(new Label(AppI18n.get("sshAgentHasKeys")));
|
||||
for (SshAgentKeyList.Entry entry : list) {
|
||||
var entryButton = new Button(entry.getName() + " (" + entry.getType() + ")");
|
||||
entryButton.maxWidth(10000);
|
||||
entryButton.getStyleClass().add(Styles.FLAT);
|
||||
entryButton.setOnAction(e -> {
|
||||
value.setValue(entry.getPublicKey());
|
||||
popover.hide();
|
||||
e.consume();
|
||||
});
|
||||
content.getChildren().add(entryButton);
|
||||
}
|
||||
popover.setContentNode(content);
|
||||
} else {
|
||||
popover.setContentNode(new Label(AppI18n.get("sshAgentNoKeys")));
|
||||
}
|
||||
|
||||
var target = struc.getParent().getChildrenUnmodifiable().getFirst();
|
||||
popover.show(target);
|
||||
});
|
||||
});
|
||||
event.consume();
|
||||
});
|
||||
});
|
||||
var inputGroup = new InputGroupComp(List.of(field, button));
|
||||
inputGroup.setMainReference(field);
|
||||
return inputGroup.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package io.xpipe.app.cred;
|
||||
|
||||
import atlantafx.base.controls.Popover;
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.comp.SimpleRegionBuilder;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.comp.base.InputGroupComp;
|
||||
import io.xpipe.app.comp.base.TextFieldComp;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.ext.ShellStore;
|
||||
import io.xpipe.app.platform.LabelGraphic;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SshAgentTestComp extends SimpleRegionBuilder {
|
||||
|
||||
private final ObservableValue<? extends SshIdentityStrategy> sshIdentityStrategy;
|
||||
|
||||
public SshAgentTestComp(ObservableValue<? extends SshIdentityStrategy> sshIdentityStrategy) {this.sshIdentityStrategy = sshIdentityStrategy;}
|
||||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
var button = new ButtonComp(AppI18n.observable("test"), new LabelGraphic.IconGraphic("mdi2p-play"), null);
|
||||
button.apply(struc -> {
|
||||
struc.setOnAction(event -> {
|
||||
DataStoreEntryRef<ShellStore> refToUse = DataStorage.get().local().ref();
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
var list = SshAgentKeyList.listAgentIdentities(refToUse, sshIdentityStrategy.getValue());
|
||||
Platform.runLater(() -> {
|
||||
var popover = new Popover();
|
||||
popover.setArrowLocation(Popover.ArrowLocation.LEFT_CENTER);
|
||||
|
||||
if (list.size() > 0) {
|
||||
var content = new VBox();
|
||||
content.setPadding(new Insets(10));
|
||||
content.setFillWidth(true);
|
||||
content.getChildren().add(new Label(AppI18n.get("sshAgentHasKeys")));
|
||||
for (SshAgentKeyList.Entry entry : list) {
|
||||
var entryButton = new Button(entry.getName() + " (" + entry.getType() + ")");
|
||||
entryButton.maxWidth(10000);
|
||||
entryButton.getStyleClass().add(Styles.FLAT);
|
||||
content.getChildren().add(entryButton);
|
||||
}
|
||||
popover.setContentNode(content);
|
||||
} else {
|
||||
popover.setContentNode(new Label(AppI18n.get("sshAgentNoKeys")));
|
||||
}
|
||||
|
||||
var target = struc;
|
||||
popover.show(target);
|
||||
});
|
||||
});
|
||||
event.consume();
|
||||
});
|
||||
});
|
||||
return button.build();
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ import io.xpipe.core.FilePath;
|
||||
import io.xpipe.core.KeyValue;
|
||||
import io.xpipe.core.OsType;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -19,26 +18,15 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||
@JsonSubTypes({
|
||||
@JsonSubTypes.Type(value = NoIdentityStrategy.class),
|
||||
@JsonSubTypes.Type(value = KeyFileStrategy.class),
|
||||
@JsonSubTypes.Type(value = InPlaceKeyStrategy.class),
|
||||
@JsonSubTypes.Type(value = OpenSshAgentStrategy.class),
|
||||
@JsonSubTypes.Type(value = PageantStrategy.class),
|
||||
@JsonSubTypes.Type(value = CustomAgentStrategy.class),
|
||||
@JsonSubTypes.Type(value = GpgAgentStrategy.class),
|
||||
@JsonSubTypes.Type(value = YubikeyPivStrategy.class),
|
||||
@JsonSubTypes.Type(value = CustomPkcs11LibraryStrategy.class),
|
||||
@JsonSubTypes.Type(value = OtherExternalAgentStrategy.class)
|
||||
})
|
||||
public interface SshIdentityStrategy {
|
||||
|
||||
static List<Class<?>> getSubclasses() {
|
||||
static List<Class<?>> getClasses() {
|
||||
var l = new ArrayList<Class<?>>();
|
||||
l.add(NoIdentityStrategy.class);
|
||||
l.add(InPlaceKeyStrategy.class);
|
||||
l.add(KeyFileStrategy.class);
|
||||
l.add(OpenSshAgentStrategy.class);
|
||||
l.add(PasswordManagerAgentStrategy.class);
|
||||
if (OsType.ofLocal() != OsType.WINDOWS) {
|
||||
l.add(CustomAgentStrategy.class);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import io.xpipe.app.process.ShellDialect;
|
||||
import io.xpipe.app.secret.SecretRetrievalStrategy;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.app.vnc.VncBaseStore;
|
||||
import io.xpipe.core.FilePath;
|
||||
import io.xpipe.core.SecretValue;
|
||||
|
||||
import javafx.beans.property.Property;
|
||||
@@ -80,5 +79,4 @@ public abstract class ProcessControlProvider {
|
||||
|
||||
public abstract void pullRepository(Path target) throws Exception;
|
||||
|
||||
public abstract void checkSshAgent(ShellControl sc, FilePath agent) throws Exception;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ import io.xpipe.app.comp.BaseRegionBuilder;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.comp.base.ContextualFileReferenceChoiceComp;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.cred.CustomAgentStrategy;
|
||||
import io.xpipe.app.cred.SshAgentTestComp;
|
||||
import io.xpipe.app.cred.SshIdentityStateManager;
|
||||
import io.xpipe.app.ext.ProcessControlProvider;
|
||||
import io.xpipe.app.issue.ErrorEventFactory;
|
||||
import io.xpipe.app.platform.LabelGraphic;
|
||||
@@ -18,6 +21,7 @@ import io.xpipe.core.OsType;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.layout.Region;
|
||||
@@ -46,35 +50,7 @@ public class SshCategory extends AppPrefsCategory {
|
||||
options.addComp(prefs.getCustomOptions("x11WslInstance").buildComp());
|
||||
}
|
||||
|
||||
AtomicReference<Region> button = new AtomicReference<>();
|
||||
var agentTest = new ButtonComp(AppI18n.observable("test"), new FontIcon("mdi2p-play"), () -> {
|
||||
ThreadHelper.runFailableAsync(() -> {
|
||||
var agent = prefs.sshAgentSocket().getValue();
|
||||
if (agent == null) {
|
||||
agent = prefs.defaultSshAgentSocket().getValue();
|
||||
}
|
||||
|
||||
if (agent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ProcessControlProvider.get().checkSshAgent(LocalShell.getShell(), agent);
|
||||
} catch (Exception e) {
|
||||
ErrorEventFactory.fromThrowable(e).expected().handle();
|
||||
return;
|
||||
}
|
||||
|
||||
Platform.runLater(() -> {
|
||||
button.get().getStyleClass().add(Styles.SUCCESS);
|
||||
});
|
||||
});
|
||||
})
|
||||
.padding(new Insets(6, 11, 6, 5))
|
||||
.apply(struc -> struc.setAlignment(Pos.CENTER_LEFT));
|
||||
agentTest.apply(struc -> button.set(struc));
|
||||
|
||||
|
||||
var agentTest = new SshAgentTestComp(new SimpleObjectProperty<>(CustomAgentStrategy.builder().build()));
|
||||
if (OsType.ofLocal() != OsType.WINDOWS) {
|
||||
var choice = new ContextualFileReferenceChoiceComp(
|
||||
new ReadOnlyObjectWrapper<>(DataStorage.get().local().ref()),
|
||||
|
||||
@@ -22,22 +22,28 @@ import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.layout.Region;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@JsonTypeName("bitwarden")
|
||||
@Builder
|
||||
@Jacksonized
|
||||
public class BitwardenPasswordManager implements PasswordManager {
|
||||
|
||||
private static ShellControl SHELL;
|
||||
private static boolean copied;
|
||||
private PasswordManagerAgentStrategy agentStrategy;
|
||||
private final PasswordManagerAgentStrategy agentStrategy;
|
||||
|
||||
private static synchronized ShellControl getOrStartShell() throws Exception {
|
||||
if (SHELL == null) {
|
||||
@@ -75,7 +81,7 @@ public class BitwardenPasswordManager implements PasswordManager {
|
||||
|
||||
var agentStrategyChoice = OptionsChoiceBuilder.builder()
|
||||
.allowNull(true)
|
||||
.available(PasswordManagerAgentStrategy.getClasses())
|
||||
.available(List.of(PasswordManagerAgentStrategy.Agent.class))
|
||||
.property(agentStrategy)
|
||||
.build();
|
||||
|
||||
@@ -83,7 +89,10 @@ public class BitwardenPasswordManager implements PasswordManager {
|
||||
.addComp(testButton)
|
||||
.nameAndDescription("passwordManagerAgentStrategy")
|
||||
.sub(agentStrategyChoice.build(), agentStrategy)
|
||||
.nonNull();
|
||||
.nonNull()
|
||||
.bind(() -> {
|
||||
return BitwardenPasswordManager.builder().agentStrategy(agentStrategy.getValue()).build();
|
||||
}, p);
|
||||
}
|
||||
|
||||
|
||||
@@ -258,12 +267,12 @@ public class BitwardenPasswordManager implements PasswordManager {
|
||||
return new PasswordManagerKeyStrategy() {
|
||||
@Override
|
||||
public boolean supportsInlineSshKeys() {
|
||||
return agentStrategy != null && agentStrategy.getSshIdentityStrategy() == null;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAgent() {
|
||||
return agentStrategy != null && agentStrategy.getSshIdentityStrategy() != null;
|
||||
return agentStrategy != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -273,7 +282,7 @@ public class BitwardenPasswordManager implements PasswordManager {
|
||||
|
||||
@Override
|
||||
public SshIdentityStrategy getSshIdentityStrategy() {
|
||||
return agentStrategy != null ? agentStrategy.getSshIdentityStrategy() : null;
|
||||
return agentStrategy.getSshIdentityStrategy();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ package io.xpipe.app.pwman;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.comp.base.ListBoxViewComp;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.cred.SshIdentityStrategy;
|
||||
import io.xpipe.app.issue.ErrorEventFactory;
|
||||
import io.xpipe.app.platform.DerivedObservableList;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.platform.OptionsChoiceBuilder;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.process.LocalShell;
|
||||
import io.xpipe.app.util.*;
|
||||
@@ -14,6 +16,7 @@ import io.xpipe.core.OsType;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
@@ -38,14 +41,42 @@ public class KeePassXcPasswordManager implements PasswordManager {
|
||||
private static KeePassXcProxyClient client;
|
||||
|
||||
private final List<KeePassXcAssociationKey> associationKeys;
|
||||
private final PasswordManagerAgentStrategy agentStrategy;
|
||||
|
||||
@Override
|
||||
public PasswordManagerKeyStrategy getKeyStrategy() {
|
||||
return PasswordManagerKeyStrategy.none();
|
||||
return new PasswordManagerKeyStrategy() {
|
||||
@Override
|
||||
public boolean supportsInlineSshKeys() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAgent() {
|
||||
return agentStrategy != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsJoinedEntries() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SshIdentityStrategy getSshIdentityStrategy() {
|
||||
return agentStrategy.getSshIdentityStrategy();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static OptionsBuilder createOptions(Property<KeePassXcPasswordManager> p) {
|
||||
var agentStrategy = new SimpleObjectProperty<>(p.getValue().getAgentStrategy());
|
||||
var agentStrategyChoice = OptionsChoiceBuilder.builder()
|
||||
.allowNull(true)
|
||||
.available(List.of(PasswordManagerAgentStrategy.KeePassXcOpenSshAgent.class, PasswordManagerAgentStrategy.KeePassXcPageant.class))
|
||||
.property(agentStrategy)
|
||||
.build();
|
||||
|
||||
var prop = FXCollections.<KeePassXcAssociationKey>observableArrayList();
|
||||
p.subscribe(keePassXcManager -> {
|
||||
DerivedObservableList.wrap(prop, true)
|
||||
@@ -83,9 +114,11 @@ public class KeePassXcPasswordManager implements PasswordManager {
|
||||
}))
|
||||
.hide(Bindings.isEmpty(prop))
|
||||
.addProperty(prop)
|
||||
.nameAndDescription("passwordManagerAgentStrategy")
|
||||
.sub(agentStrategyChoice.build(), agentStrategy)
|
||||
.bind(
|
||||
() -> {
|
||||
return new KeePassXcPasswordManager(prop);
|
||||
return new KeePassXcPasswordManager(prop, agentStrategy.getValue());
|
||||
},
|
||||
p);
|
||||
}
|
||||
|
||||
@@ -41,9 +41,11 @@ public class KeeperPasswordManager implements PasswordManager {
|
||||
|
||||
@Override
|
||||
public PasswordManagerKeyStrategy getKeyStrategy() {
|
||||
return PasswordManagerKeyStrategy.none();
|
||||
return PasswordManagerKeyStrategy.of(true, true, agentStrategy);
|
||||
}
|
||||
|
||||
private final PasswordManagerAgentStrategy agentStrategy;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||
public interface KeeperAuth {
|
||||
|
||||
@@ -402,6 +404,7 @@ public class KeeperPasswordManager implements PasswordManager {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static OptionsBuilder createOptions(Property<KeeperPasswordManager> p) {
|
||||
var agentStrategy = new SimpleObjectProperty<>(p.getValue().getAgentStrategy());
|
||||
var mfa = new SimpleObjectProperty<>(p.getValue().getTwoFactorAuth() != null ? p.getValue().getTwoFactorAuth() : new KeeperAuth.None());
|
||||
|
||||
var choice = OptionsChoiceBuilder.builder()
|
||||
@@ -409,10 +412,19 @@ public class KeeperPasswordManager implements PasswordManager {
|
||||
.available(KeeperAuth.getClasses())
|
||||
.property(mfa)
|
||||
.build();
|
||||
var agentStrategyChoice = OptionsChoiceBuilder.builder()
|
||||
.allowNull(true)
|
||||
.available(List.of(PasswordManagerAgentStrategy.Agent.class))
|
||||
.property(agentStrategy)
|
||||
.build();
|
||||
|
||||
|
||||
return new OptionsBuilder()
|
||||
.nameAndDescription("keeper2fa")
|
||||
.sub(choice.build(), mfa)
|
||||
.nameAndDescription("passwordManagerAgentStrategy")
|
||||
.sub(agentStrategyChoice.build(), agentStrategy)
|
||||
.nonNull()
|
||||
.bind(
|
||||
() -> {
|
||||
return KeeperPasswordManager.builder()
|
||||
|
||||
@@ -1,29 +1,68 @@
|
||||
package io.xpipe.app.pwman;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.ext.ProcessControlProvider;
|
||||
import io.xpipe.app.issue.ErrorEventFactory;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.platform.OptionsChoiceBuilder;
|
||||
import io.xpipe.app.process.CommandBuilder;
|
||||
import io.xpipe.app.process.CommandSupport;
|
||||
import io.xpipe.app.process.ProcessOutputException;
|
||||
import io.xpipe.app.process.ShellControl;
|
||||
import io.xpipe.app.util.DocumentationLink;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.JacksonMapper;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.layout.Region;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@JsonTypeName("onePassword")
|
||||
@Builder
|
||||
@Jacksonized
|
||||
@Getter
|
||||
public class OnePasswordManager implements PasswordManager {
|
||||
|
||||
@Override
|
||||
public PasswordManagerKeyStrategy getKeyStrategy() {
|
||||
return PasswordManagerKeyStrategy.none();
|
||||
return PasswordManagerKeyStrategy.of(true, false, agentStrategy);
|
||||
}
|
||||
|
||||
private static ShellControl SHELL;
|
||||
private final PasswordManagerAgentStrategy agentStrategy;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static OptionsBuilder createOptions(Property<OnePasswordManager> p) {
|
||||
var agentStrategy = new SimpleObjectProperty<>(p.getValue().getAgentStrategy());
|
||||
|
||||
var agentStrategyChoice = OptionsChoiceBuilder.builder()
|
||||
.allowNull(true)
|
||||
.available(List.of(PasswordManagerAgentStrategy.Agent.class))
|
||||
.property(agentStrategy)
|
||||
.build();
|
||||
|
||||
return new OptionsBuilder()
|
||||
.nameAndDescription("passwordManagerAgentStrategy")
|
||||
.sub(agentStrategyChoice.build(), agentStrategy)
|
||||
.nonNull()
|
||||
.bind(() -> {
|
||||
return OnePasswordManager.builder().agentStrategy(agentStrategy.getValue()).build();
|
||||
}, p);
|
||||
}
|
||||
|
||||
private static synchronized ShellControl getOrStartShell() throws Exception {
|
||||
if (SHELL == null) {
|
||||
|
||||
@@ -23,12 +23,12 @@ public interface PasswordManager {
|
||||
l.add(OnePasswordManager.class);
|
||||
l.add(KeePassXcPasswordManager.class);
|
||||
l.add(BitwardenPasswordManager.class);
|
||||
l.add(DashlanePasswordManager.class);
|
||||
l.add(KeeperPasswordManager.class);
|
||||
if (OsType.ofLocal() != OsType.WINDOWS) {
|
||||
l.add(LastpassPasswordManager.class);
|
||||
l.add(EnpassPasswordManager.class);
|
||||
}
|
||||
l.add(KeeperPasswordManager.class);
|
||||
l.add(DashlanePasswordManager.class);
|
||||
l.add(PsonoPasswordManager.class);
|
||||
l.add(PassboltPasswordManager.class);
|
||||
if (OsType.ofLocal() == OsType.WINDOWS) {
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package io.xpipe.app.pwman;
|
||||
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.comp.base.ContextualFileReferenceChoiceComp;
|
||||
import io.xpipe.app.comp.base.InputGroupComp;
|
||||
import io.xpipe.app.comp.base.IntegratedTextAreaComp;
|
||||
import io.xpipe.app.core.AppCache;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.core.window.AppDialog;
|
||||
import io.xpipe.app.cred.OpenSshAgentStrategy;
|
||||
import io.xpipe.app.cred.PageantStrategy;
|
||||
import io.xpipe.app.cred.SshAgentTestComp;
|
||||
import io.xpipe.app.cred.SshIdentityStrategy;
|
||||
import io.xpipe.app.ext.ShellDialectChoiceComp;
|
||||
import io.xpipe.app.ext.ValidationException;
|
||||
@@ -17,15 +20,18 @@ import io.xpipe.app.platform.LabelGraphic;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.process.ShellDialect;
|
||||
import io.xpipe.app.process.ShellScript;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.app.util.DocumentationLink;
|
||||
import io.xpipe.app.util.HttpHelper;
|
||||
import io.xpipe.app.util.Validators;
|
||||
import io.xpipe.core.FilePath;
|
||||
import io.xpipe.core.OsType;
|
||||
import io.xpipe.core.UuidHelper;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
|
||||
@@ -41,10 +47,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||
@JsonSubTypes({
|
||||
@JsonSubTypes.Type(value = PasswordManagerAgentStrategy.Inline.class),
|
||||
@JsonSubTypes.Type(value = PasswordManagerAgentStrategy.Agent.class)
|
||||
})
|
||||
public interface PasswordManagerAgentStrategy {
|
||||
|
||||
@JsonTypeName("inline")
|
||||
@@ -58,9 +60,6 @@ public interface PasswordManagerAgentStrategy {
|
||||
return "inlineKey";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkComplete() {}
|
||||
|
||||
@Override
|
||||
public SshIdentityStrategy getSshIdentityStrategy() {
|
||||
return null;
|
||||
@@ -73,6 +72,8 @@ public interface PasswordManagerAgentStrategy {
|
||||
@Builder
|
||||
class Agent implements PasswordManagerAgentStrategy {
|
||||
|
||||
FilePath customSocket;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static String getOptionsNameKey() {
|
||||
return "keyAgent";
|
||||
@@ -80,24 +81,82 @@ public interface PasswordManagerAgentStrategy {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static OptionsBuilder createOptions(Property<Agent> property) {
|
||||
var customSocket = new SimpleObjectProperty<>(property.getValue().getCustomSocket());
|
||||
|
||||
var choice = new ContextualFileReferenceChoiceComp(
|
||||
new ReadOnlyObjectWrapper<>(DataStorage.get().local().ref()),
|
||||
customSocket,
|
||||
null,
|
||||
List.of(),
|
||||
e -> e.equals(DataStorage.get().local()),
|
||||
false);
|
||||
|
||||
return new OptionsBuilder()
|
||||
.addComp(new SshAgentTestComp(Bindings.createObjectBinding(() -> {
|
||||
return property.getValue().getSshIdentityStrategy();
|
||||
}, property)))
|
||||
.nameAndDescription("passwordManagerSshAgentSocket")
|
||||
.addComp(choice, customSocket)
|
||||
.hide(OsType.ofLocal() == OsType.WINDOWS)
|
||||
.bind(
|
||||
() -> Agent.builder()
|
||||
.customSocket(customSocket.get())
|
||||
.build(),
|
||||
property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkComplete() throws ValidationException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SshIdentityStrategy getSshIdentityStrategy() {
|
||||
return OpenSshAgentStrategy.builder().build();
|
||||
}
|
||||
}
|
||||
|
||||
void checkComplete() throws ValidationException;
|
||||
|
||||
@JsonTypeName("keePassXcOpenSshAgent")
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
class KeePassXcOpenSshAgent implements PasswordManagerAgentStrategy {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static OptionsBuilder createOptions(Property<KeePassXcOpenSshAgent> property) {
|
||||
return new OptionsBuilder()
|
||||
.addComp(new SshAgentTestComp(Bindings.createObjectBinding(() -> {
|
||||
return property.getValue().getSshIdentityStrategy();
|
||||
}, property)))
|
||||
.bind(
|
||||
() -> KeePassXcOpenSshAgent.builder().build(),
|
||||
property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SshIdentityStrategy getSshIdentityStrategy() {
|
||||
return OpenSshAgentStrategy.builder().build();
|
||||
}
|
||||
}
|
||||
|
||||
@JsonTypeName("keePassXcPageant")
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
class KeePassXcPageant implements PasswordManagerAgentStrategy {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static OptionsBuilder createOptions(Property<KeePassXcPageant> property) {
|
||||
return new OptionsBuilder()
|
||||
.addComp(new SshAgentTestComp(Bindings.createObjectBinding(() -> {
|
||||
return property.getValue().getSshIdentityStrategy();
|
||||
}, property)))
|
||||
.bind(
|
||||
() -> KeePassXcPageant.builder().build(),
|
||||
property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SshIdentityStrategy getSshIdentityStrategy() {
|
||||
return PageantStrategy.builder().build();
|
||||
}
|
||||
}
|
||||
|
||||
SshIdentityStrategy getSshIdentityStrategy();
|
||||
|
||||
@@ -105,6 +164,8 @@ public interface PasswordManagerAgentStrategy {
|
||||
var l = new ArrayList<Class<?>>();
|
||||
l.add(Inline.class);
|
||||
l.add(Agent.class);
|
||||
l.add(KeePassXcOpenSshAgent.class);
|
||||
l.add(KeePassXcPageant.class);
|
||||
return l;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,30 @@ import io.xpipe.app.cred.SshIdentityStrategy;
|
||||
|
||||
public interface PasswordManagerKeyStrategy {
|
||||
|
||||
static PasswordManagerKeyStrategy of(boolean inline, boolean joined, PasswordManagerAgentStrategy strategy) {
|
||||
return new PasswordManagerKeyStrategy() {
|
||||
@Override
|
||||
public boolean supportsInlineSshKeys() {
|
||||
return inline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAgent() {
|
||||
return strategy != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsJoinedEntries() {
|
||||
return joined;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SshIdentityStrategy getSshIdentityStrategy() {
|
||||
return strategy.getSshIdentityStrategy();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static PasswordManagerKeyStrategy none() {
|
||||
return new PasswordManagerKeyStrategy() {
|
||||
@Override
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package io.xpipe.app.util;
|
||||
|
||||
import io.xpipe.app.cred.SshIdentityStrategy;
|
||||
import io.xpipe.app.ext.HostAddress;
|
||||
import io.xpipe.app.process.ShellDialect;
|
||||
import io.xpipe.app.process.ShellDialects;
|
||||
import io.xpipe.app.process.ShellScript;
|
||||
import io.xpipe.app.pwman.KeePassXcAssociationKey;
|
||||
import io.xpipe.app.pwman.KeePassXcPasswordManager;
|
||||
import io.xpipe.app.pwman.KeeperPasswordManager;
|
||||
import io.xpipe.app.pwman.PasswordManager;
|
||||
import io.xpipe.app.pwman.*;
|
||||
import io.xpipe.app.rdp.ExternalRdpClient;
|
||||
import io.xpipe.app.secret.*;
|
||||
import io.xpipe.app.spice.ExternalSpiceClient;
|
||||
@@ -80,6 +78,8 @@ public class AppJacksonModule extends SimpleModule {
|
||||
context.registerSubtypes(new NamedType(t.getClass()));
|
||||
}
|
||||
|
||||
context.registerSubtypes(SshIdentityStrategy.getClasses());
|
||||
context.registerSubtypes(PasswordManagerAgentStrategy.getClasses());
|
||||
context.registerSubtypes(PasswordManager.getClasses());
|
||||
context.registerSubtypes(TerminalMultiplexer.getClasses());
|
||||
context.registerSubtypes(TerminalPrompt.getClasses());
|
||||
|
||||
@@ -85,7 +85,7 @@ public class IdentityChoiceBuilder {
|
||||
.allowNull(false)
|
||||
.property(identity)
|
||||
.customConfiguration(config)
|
||||
.available(SshIdentityStrategy.getSubclasses())
|
||||
.available(SshIdentityStrategy.getClasses())
|
||||
.transformer(entryComboBox -> {
|
||||
var button = new ButtonComp(null, new LabelGraphic.IconGraphic("mdi2k-key-plus"), () -> {
|
||||
ProcessControlProvider.get().showSshKeygenDialog(null, identity);
|
||||
|
||||
Generated
+12
-1
@@ -1627,7 +1627,8 @@ pkcs11Library=PKCS#11 library
|
||||
pkcs11LibraryDescription=The path of the dynamically linked library file
|
||||
sshAgentSocket=Custom SSH agent socket
|
||||
sshAgentSocketDescription=The custom socket to use to communicate with the SSH agent. This custom agent can be used for a connection by selecting the custom agent option for it.
|
||||
publicKey=Public key identifier
|
||||
#force
|
||||
publicKey=Key selector
|
||||
publicKeyDescription=The optional public key to force the agent to only offer the matching private key
|
||||
actions=Actions
|
||||
hcloudServer.displayName=Hetzner cloud server
|
||||
@@ -1998,3 +1999,13 @@ inlineKey=Retrieve directly from CLI
|
||||
keyAgent=Use agent integration
|
||||
passwordManagerAgentStrategy=SSH key retrieval method
|
||||
passwordManagerAgentStrategyDescription=How and where to retrieve any stored SSH keys from.
|
||||
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
|
||||
passwordManagerEmpty=No password manager is configured
|
||||
passwordManagerSshKeyConfig=Password manager configuration
|
||||
passwordManagerSshKeyConfigDescription=Check the status of the password manager
|
||||
keePassXcOpenSshAgent=Use OpenSSH agent
|
||||
keePassXcPageant=Use Pageant
|
||||
passwordManagerSshAgentSocket=SSH agent socket
|
||||
passwordManagerSshAgentSocketDescription=Override the default agent socket location that should be used.
|
||||
|
||||
Reference in New Issue
Block a user