mirror of
https://github.com/xpipe-io/xpipe.git
synced 2026-09-26 18:35:43 +00:00
Merge branch 'security-keys' into 23-release
This commit is contained in:
@@ -4,12 +4,14 @@ 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.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.util.DocumentationLink;
|
||||
import io.xpipe.app.util.Validators;
|
||||
import io.xpipe.core.FilePath;
|
||||
import io.xpipe.core.KeyValue;
|
||||
import io.xpipe.core.OsType;
|
||||
@@ -95,6 +97,11 @@ public class CustomAgentStrategy implements SshIdentityAgentStrategy {
|
||||
|
||||
String publicKey;
|
||||
|
||||
@Override
|
||||
public void checkComplete() throws ValidationException {
|
||||
Validators.nonNull(AppPrefs.get().defaultSshAgentSocket().getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareParent(ShellControl parent) throws Exception {
|
||||
if (parent.isLocal()) {
|
||||
@@ -107,7 +114,7 @@ public class CustomAgentStrategy implements SshIdentityAgentStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilePath determinetAgentSocketLocation(ShellControl sc) throws Exception {
|
||||
public FilePath determineAgentSocketLocation(ShellControl sc) throws Exception {
|
||||
if (!sc.isLocal() || sc.getOsType() == OsType.WINDOWS) {
|
||||
return null;
|
||||
}
|
||||
@@ -136,7 +143,7 @@ public class CustomAgentStrategy implements SshIdentityAgentStrategy {
|
||||
KeyValue.raw("IdentityFile", file.isPresent() ? file.get().toString() : "none"),
|
||||
KeyValue.raw("PKCS11Provider", "none")));
|
||||
|
||||
var agent = determinetAgentSocketLocation(sc);
|
||||
var agent = determineAgentSocketLocation(sc);
|
||||
if (agent != null) {
|
||||
l.add(KeyValue.escape("IdentityAgent", agent));
|
||||
}
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
package io.xpipe.app.cred;
|
||||
|
||||
import io.xpipe.app.comp.base.ContextualFileReferenceChoiceComp;
|
||||
import io.xpipe.app.ext.ValidationException;
|
||||
import io.xpipe.app.issue.ErrorEventFactory;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.process.CommandBuilder;
|
||||
import io.xpipe.app.process.ShellControl;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.util.LicenseProvider;
|
||||
import io.xpipe.app.util.Validators;
|
||||
import io.xpipe.core.FilePath;
|
||||
import io.xpipe.core.KeyValue;
|
||||
import io.xpipe.core.OsType;
|
||||
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@JsonTypeName("customPkcs11")
|
||||
@AllArgsConstructor
|
||||
public class CustomPkcs11LibraryStrategy implements SshIdentityStrategy {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static String getOptionsNameKey() {
|
||||
return "customPkcs11Library";
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static OptionsBuilder createOptions(
|
||||
Property<CustomPkcs11LibraryStrategy> p, SshIdentityStrategyChoiceConfig config) {
|
||||
|
||||
var file =
|
||||
new SimpleObjectProperty<>(p.getValue() != null ? p.getValue().getFile() : null);
|
||||
return new OptionsBuilder()
|
||||
.nameAndDescription("pkcs11Library")
|
||||
.addComp(
|
||||
new ContextualFileReferenceChoiceComp(
|
||||
config.getFileSystem() != null
|
||||
? config.getFileSystem()
|
||||
: new ReadOnlyObjectWrapper<>(
|
||||
DataStorage.get().local().ref()),
|
||||
file,
|
||||
null,
|
||||
List.of(),
|
||||
e -> {
|
||||
if (config.getFileSystem() == null) {
|
||||
return e.equals(DataStorage.get().local());
|
||||
}
|
||||
|
||||
var fs = config.getFileSystem().getValue();
|
||||
if (fs == null) {
|
||||
return e.equals(DataStorage.get().local());
|
||||
} else {
|
||||
return e.equals(fs.get());
|
||||
}
|
||||
},
|
||||
false),
|
||||
file)
|
||||
.nonNull()
|
||||
.bind(
|
||||
() -> {
|
||||
return new CustomPkcs11LibraryStrategy(file.get());
|
||||
},
|
||||
p);
|
||||
}
|
||||
|
||||
FilePath file;
|
||||
|
||||
@Override
|
||||
public void checkComplete() throws ValidationException {
|
||||
Validators.nonNull(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareParent(ShellControl parent) throws Exception {
|
||||
parent.requireLicensedFeature(LicenseProvider.get().getFeature("pkcs11Identity"));
|
||||
|
||||
if (!parent.getShellDialect()
|
||||
.createFileExistsCommand(parent, file.toString())
|
||||
.executeAndCheck()) {
|
||||
throw ErrorEventFactory.expected(new IOException("PKCS11 library at " + file + " not found"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildCommand(CommandBuilder builder) {
|
||||
builder.setup(sc -> {
|
||||
var dir = file.getParent();
|
||||
if (sc.getOsType() == OsType.WINDOWS) {
|
||||
builder.addToPath(dir, true);
|
||||
} else {
|
||||
builder.addToEnvironmentPath("LD_LIBRARY_PATH", dir, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyValue> configOptions(ShellControl sc) {
|
||||
return List.of(
|
||||
KeyValue.raw("IdentitiesOnly", "no"),
|
||||
KeyValue.escape("PKCS11Provider", file),
|
||||
KeyValue.raw("IdentityFile", "none"),
|
||||
KeyValue.raw("IdentityAgent", "none"));
|
||||
}
|
||||
|
||||
public PublicKeyStrategy getPublicKeyStrategy() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package io.xpipe.app.cred;
|
||||
|
||||
import io.xpipe.app.comp.base.TextFieldComp;
|
||||
import io.xpipe.app.core.AppSystemInfo;
|
||||
import io.xpipe.app.ext.ValidationException;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.process.CommandBuilder;
|
||||
import io.xpipe.app.process.ShellControl;
|
||||
@@ -67,6 +68,11 @@ public class GpgAgentStrategy implements SshIdentityAgentStrategy {
|
||||
|
||||
String publicKey;
|
||||
|
||||
@Override
|
||||
public void checkComplete() throws ValidationException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareParent(ShellControl parent) throws Exception {
|
||||
parent.requireLicensedFeature(LicenseProvider.get().getFeature("gpgAgent"));
|
||||
@@ -76,7 +82,7 @@ public class GpgAgentStrategy implements SshIdentityAgentStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilePath determinetAgentSocketLocation(ShellControl sc) throws Exception {
|
||||
public FilePath determineAgentSocketLocation(ShellControl sc) throws Exception {
|
||||
if (sc.getOsType() == OsType.WINDOWS) {
|
||||
return null;
|
||||
}
|
||||
@@ -100,7 +106,7 @@ public class GpgAgentStrategy implements SshIdentityAgentStrategy {
|
||||
KeyValue.escape("IdentityFile", file.isPresent() ? file.get() : "none"),
|
||||
KeyValue.raw("PKCS11Provider", "none")));
|
||||
|
||||
var agent = determinetAgentSocketLocation(sc);
|
||||
var agent = determineAgentSocketLocation(sc);
|
||||
if (agent != null) {
|
||||
l.add(KeyValue.escape("IdentityAgent", agent));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.xpipe.app.cred;
|
||||
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.ext.ValidationException;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.process.CommandBuilder;
|
||||
@@ -58,7 +59,12 @@ public class OpenSshAgentStrategy implements SshIdentityAgentStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilePath determinetAgentSocketLocation(ShellControl sc) throws Exception {
|
||||
public void checkComplete() throws ValidationException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilePath determineAgentSocketLocation(ShellControl sc) throws Exception {
|
||||
if (sc.getOsType() == OsType.WINDOWS) {
|
||||
return null;
|
||||
}
|
||||
@@ -84,7 +90,7 @@ public class OpenSshAgentStrategy implements SshIdentityAgentStrategy {
|
||||
KeyValue.escape("IdentityFile", file.isPresent() ? file.get() : "none"),
|
||||
KeyValue.raw("PKCS11Provider", "none")));
|
||||
|
||||
var agent = determinetAgentSocketLocation(sc);
|
||||
var agent = determineAgentSocketLocation(sc);
|
||||
if (agent != null) {
|
||||
l.add(KeyValue.escape("IdentityAgent", agent));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.xpipe.app.cred;
|
||||
|
||||
import io.xpipe.app.ext.ValidationException;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.process.CommandBuilder;
|
||||
import io.xpipe.app.process.ShellControl;
|
||||
@@ -49,7 +50,12 @@ public class OtherExternalAgentStrategy implements SshIdentityAgentStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilePath determinetAgentSocketLocation(ShellControl parent) {
|
||||
public void checkComplete() throws ValidationException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilePath determineAgentSocketLocation(ShellControl parent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.xpipe.app.cred;
|
||||
|
||||
import io.xpipe.app.core.AppSystemInfo;
|
||||
import io.xpipe.app.ext.ValidationException;
|
||||
import io.xpipe.app.issue.ErrorEventFactory;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
@@ -87,7 +88,7 @@ public class PageantStrategy implements SshIdentityAgentStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilePath determinetAgentSocketLocation(ShellControl sc) {
|
||||
public FilePath determineAgentSocketLocation(ShellControl sc) {
|
||||
if (sc.isLocal() && sc.getOsType() == OsType.WINDOWS) {
|
||||
return FilePath.of(getPageantWindowsPipe());
|
||||
}
|
||||
@@ -95,6 +96,11 @@ public class PageantStrategy implements SshIdentityAgentStrategy {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkComplete() throws ValidationException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildCommand(CommandBuilder builder) {}
|
||||
|
||||
@@ -106,7 +112,7 @@ public class PageantStrategy implements SshIdentityAgentStrategy {
|
||||
KeyValue.escape("IdentityFile", file.isPresent() ? file.get() : "none"),
|
||||
KeyValue.raw("PKCS11Provider", "none")));
|
||||
|
||||
var agent = determinetAgentSocketLocation(sc);
|
||||
var agent = determineAgentSocketLocation(sc);
|
||||
if (agent != null) {
|
||||
l.add(KeyValue.escape("IdentityAgent", agent));
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ 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.storage.DataStoreEntry;
|
||||
import io.xpipe.app.util.DocumentationLink;
|
||||
import io.xpipe.app.util.Validators;
|
||||
import io.xpipe.core.FilePath;
|
||||
@@ -126,7 +125,7 @@ public class PasswordManagerAgentStrategy implements SshIdentityAgentStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilePath determinetAgentSocketLocation(ShellControl parent) {
|
||||
public FilePath determineAgentSocketLocation(ShellControl parent) {
|
||||
var config = getConfig();
|
||||
return config != null ? FilePath.of(config.getDefaultSocketLocation()) : null;
|
||||
}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package io.xpipe.app.cred;
|
||||
|
||||
import io.xpipe.app.comp.base.*;
|
||||
import io.xpipe.app.core.App;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.process.CommandBuilder;
|
||||
import io.xpipe.app.process.ShellControl;
|
||||
import io.xpipe.core.FilePath;
|
||||
import io.xpipe.core.KeyValue;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.Property;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@JsonTypeName("passwordManagerInPlaceKey")
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
public class PasswordManagerInPlaceKeyStrategy implements SshIdentityAgentStrategy {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static OptionsBuilder createOptions(
|
||||
Property<PasswordManagerInPlaceKeyStrategy> p, SshIdentityStrategyChoiceConfig config) {
|
||||
var options = new OptionsBuilder();
|
||||
var prefs = AppPrefs.get();
|
||||
var keyProperty = options.map(p, PasswordManagerInPlaceKeyStrategy::getKey);
|
||||
var field = new TextFieldComp(keyProperty).apply(struc -> struc.promptTextProperty()
|
||||
.bind(Bindings.createStringBinding(
|
||||
() -> {
|
||||
return prefs.passwordManager().getValue() != null
|
||||
? prefs.passwordManager().getValue().getKeyPlaceholder()
|
||||
: "?";
|
||||
},
|
||||
prefs.passwordManager())));
|
||||
var button = new ButtonComp(null, new FontIcon("mdomz-settings"), () -> {
|
||||
AppPrefs.get().selectCategory("passwordManager");
|
||||
App.getApp().getStage().requestFocus();
|
||||
});
|
||||
var content = new InputGroupComp(List.of(field, button));
|
||||
content.setMainReference(field);
|
||||
return options.nameAndDescription("passwordManagerInPlaceKeyKey")
|
||||
.addComp(content, keyProperty)
|
||||
.nonNull()
|
||||
.bind(
|
||||
() -> {
|
||||
return PasswordManagerInPlaceKeyStrategy.builder()
|
||||
.key(keyProperty.get())
|
||||
.build();
|
||||
},
|
||||
p);
|
||||
}
|
||||
|
||||
String key;
|
||||
|
||||
@Override
|
||||
public void prepareParent(ShellControl parent) {}
|
||||
|
||||
@Override
|
||||
public void buildCommand(CommandBuilder builder) {}
|
||||
|
||||
@Override
|
||||
public List<KeyValue> configOptions(ShellControl sc) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKeyStrategy getPublicKeyStrategy() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilePath determinetAgentSocketLocation(ShellControl parent) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
package io.xpipe.app.cred;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.comp.base.ContextualFileReferenceChoiceComp;
|
||||
import io.xpipe.app.comp.base.HorizontalComp;
|
||||
import io.xpipe.app.comp.base.LabelComp;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.ext.ValidationException;
|
||||
import io.xpipe.app.issue.ErrorEventFactory;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.process.ShellControl;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.util.DocumentationLink;
|
||||
import io.xpipe.app.util.Validators;
|
||||
import io.xpipe.core.FilePath;
|
||||
import io.xpipe.core.OsType;
|
||||
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;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
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;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||
public interface SecurityKeyImpl {
|
||||
|
||||
static List<Class<?>> getClasses() {
|
||||
var l = new ArrayList<Class<?>>();
|
||||
l.add(OpenSc.class);
|
||||
l.add(YubikeyPiv.class);
|
||||
l.add(MacOsKeychain.class);
|
||||
l.add(Custom.class);
|
||||
return l;
|
||||
}
|
||||
|
||||
static List<Class<?>> getAvailable() {
|
||||
var l = new ArrayList<Class<?>>();
|
||||
l.add(OpenSc.class);
|
||||
l.add(YubikeyPiv.class);
|
||||
if (OsType.ofLocal() == OsType.MACOS) {
|
||||
l.add(MacOsKeychain.class);
|
||||
}
|
||||
l.add(Custom.class);
|
||||
return l;
|
||||
}
|
||||
|
||||
default void checkComplete() throws ValidationException {}
|
||||
|
||||
FilePath determineLibraryPath(ShellControl sc) throws Exception;
|
||||
|
||||
@JsonTypeName("yubikeyPiv")
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
class YubikeyPiv implements SecurityKeyImpl {
|
||||
|
||||
@Override
|
||||
public FilePath determineLibraryPath(ShellControl sc) throws Exception {
|
||||
var file =
|
||||
switch (sc.getOsType()) {
|
||||
case OsType.MacOs ignored -> FilePath.of("/usr/local/lib/libykcs11.dylib");
|
||||
case OsType.Windows ignored -> {
|
||||
var x64 = FilePath.of(
|
||||
sc.view().getEnvironmentVariableOrThrow("ProgramFiles"),
|
||||
"Yubico\\Yubico PIV Tool\\bin\\libykcs11.dll");
|
||||
if (sc.view().fileExists(x64)) {
|
||||
yield x64;
|
||||
}
|
||||
|
||||
var x86 = FilePath.of(
|
||||
sc.view().getEnvironmentVariableOrThrow("ProgramFiles(x86)"),
|
||||
"Yubico\\Yubico PIV Tool\\bin\\libykcs11.dll");
|
||||
if (sc.view().fileExists(x86)) {
|
||||
yield x86;
|
||||
}
|
||||
|
||||
yield x64;
|
||||
}
|
||||
default -> FilePath.of("/usr/local/lib/libykcs11.so");
|
||||
};
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@JsonTypeName("openSc")
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
class OpenSc implements SecurityKeyImpl {
|
||||
|
||||
@Override
|
||||
public FilePath determineLibraryPath(ShellControl sc) throws Exception {
|
||||
var file =
|
||||
switch (sc.getOsType()) {
|
||||
case OsType.MacOs ignored -> FilePath.of("/Library/OpenSC/lib/opensc-pkcs11.so");
|
||||
case OsType.Windows ignored -> {
|
||||
var x64 = FilePath.of(
|
||||
sc.view().getEnvironmentVariableOrThrow("ProgramFiles"),
|
||||
"OpenSC Project\\OpenSC\\pkcs11\\opensc-pkcs11.dll");
|
||||
if (sc.view().fileExists(x64)) {
|
||||
yield x64;
|
||||
}
|
||||
|
||||
var x86 = FilePath.of(
|
||||
sc.view().getEnvironmentVariableOrThrow("ProgramFiles(x86)"),
|
||||
"OpenSC Project\\OpenSC\\pkcs11\\opensc-pkcs11.dll");
|
||||
if (sc.view().fileExists(x86)) {
|
||||
yield x86;
|
||||
}
|
||||
|
||||
yield x64;
|
||||
}
|
||||
default -> FilePath.of("/usr/lib/pkcs11/opensc-pkcs11.so");
|
||||
};
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@JsonTypeName("macOsKeychain")
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
class MacOsKeychain implements SecurityKeyImpl {
|
||||
|
||||
@Override
|
||||
public FilePath determineLibraryPath(ShellControl sc) throws Exception {
|
||||
var file =
|
||||
switch (sc.getOsType()) {
|
||||
case OsType.MacOs ignored -> FilePath.of("/usr/lib/ssh-keychain.dylib");
|
||||
default -> throw ErrorEventFactory.expected(new UnsupportedOperationException("macOS keychain is not supported on other operating systems"));
|
||||
};
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@JsonTypeName("customLibrary")
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
class Custom implements SecurityKeyImpl {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static OptionsBuilder createOptions(
|
||||
Property<Custom> p, SshIdentityStrategyChoiceConfig config) {
|
||||
var file = new SimpleObjectProperty<>(p.getValue().getFile());
|
||||
|
||||
return new OptionsBuilder()
|
||||
.nameAndDescription("pkcs11Library")
|
||||
.addComp(
|
||||
new ContextualFileReferenceChoiceComp(
|
||||
config.getFileSystem() != null
|
||||
? config.getFileSystem()
|
||||
: new ReadOnlyObjectWrapper<>(
|
||||
DataStorage.get().local().ref()),
|
||||
file,
|
||||
null,
|
||||
List.of(),
|
||||
e -> {
|
||||
if (config.getFileSystem() == null) {
|
||||
return e.equals(DataStorage.get().local());
|
||||
}
|
||||
|
||||
var fs = config.getFileSystem().getValue();
|
||||
if (fs == null) {
|
||||
return e.equals(DataStorage.get().local());
|
||||
} else {
|
||||
return e.equals(fs.get());
|
||||
}
|
||||
},
|
||||
false),
|
||||
file)
|
||||
.nonNull()
|
||||
.bind(
|
||||
() -> {
|
||||
return new Custom(file.get());
|
||||
},
|
||||
p);
|
||||
}
|
||||
|
||||
FilePath file;
|
||||
|
||||
@Override
|
||||
public FilePath determineLibraryPath(ShellControl sc) {
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkComplete() throws ValidationException {
|
||||
Validators.nonNull(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package io.xpipe.app.cred;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.xpipe.app.comp.base.ContextualFileReferenceChoiceComp;
|
||||
import io.xpipe.app.core.AppInstallation;
|
||||
import io.xpipe.app.ext.ProcessControlProvider;
|
||||
import io.xpipe.app.ext.ValidationException;
|
||||
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.ShellControl;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.util.DocumentationLink;
|
||||
import io.xpipe.app.util.LicenseProvider;
|
||||
import io.xpipe.app.util.Validators;
|
||||
import io.xpipe.core.FilePath;
|
||||
import io.xpipe.core.KeyValue;
|
||||
import io.xpipe.core.OsType;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@JsonTypeName("hardwareSecurityKey")
|
||||
@AllArgsConstructor
|
||||
public class SecurityKeyStrategy implements SshIdentityKeyListStrategy {
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static OptionsBuilder createOptions(
|
||||
Property<SecurityKeyStrategy> p, SshIdentityStrategyChoiceConfig config) {
|
||||
var publicKey = new SimpleStringProperty(p.getValue().getPublicKey());
|
||||
var securityKey = new SimpleObjectProperty<>(p.getValue().getSecurityKey());
|
||||
|
||||
var choice = OptionsChoiceBuilder.builder().property(securityKey).available(SecurityKeyImpl.getAvailable()).customConfiguration(config).build().build();
|
||||
|
||||
return new OptionsBuilder()
|
||||
.nameAndDescription("pkcs11Impl")
|
||||
.sub(choice, securityKey)
|
||||
.nonNull()
|
||||
.nameAndDescription("publicKey")
|
||||
.documentationLink(DocumentationLink.SSH_AGENT_PUBLIC_KEYS)
|
||||
.addComp(new SshAgentKeyListComp(config.getFileSystem(), p, publicKey, false), publicKey)
|
||||
.bind(
|
||||
() -> {
|
||||
return SecurityKeyStrategy.builder().securityKey(securityKey.get()).publicKey(publicKey.get()).build();
|
||||
},
|
||||
p);
|
||||
}
|
||||
|
||||
SecurityKeyImpl securityKey;
|
||||
String publicKey;
|
||||
|
||||
@Override
|
||||
public void checkComplete() throws ValidationException {
|
||||
Validators.nonNull(securityKey);
|
||||
securityKey.checkComplete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareParent(ShellControl parent) throws Exception {
|
||||
parent.requireLicensedFeature(LicenseProvider.get().getFeature("pkcs11Identity"));
|
||||
|
||||
var file = securityKey.determineLibraryPath(parent);
|
||||
if (!parent.view().fileExists(file)) {
|
||||
throw ErrorEventFactory.expected(new IOException("PKCS11 library at " + file + " not found"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandBuilder createListCommand() {
|
||||
var cmd = CommandBuilder.of().add("ssh-keygen", "-D")
|
||||
.addFile(sc -> securityKey.determineLibraryPath(sc).toUnix()).add("-e")
|
||||
.fixedEnvironment("SSH_ASKPASS", AppInstallation.ofCurrent().getCliExecutablePath().toString())
|
||||
.fixedEnvironment("SSH_ASKPASS_REQUIRE", "force");
|
||||
ProcessControlProvider.get().addAskpassEnvironment(cmd, "[ssh-keygen]", null, null);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildCommand(CommandBuilder builder) {
|
||||
builder.setup(sc -> {
|
||||
var dir = securityKey.determineLibraryPath(sc).getParent();
|
||||
if (sc.getOsType() == OsType.WINDOWS) {
|
||||
builder.addToPath(dir, true);
|
||||
} else {
|
||||
builder.addToEnvironmentPath("LD_LIBRARY_PATH", dir, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyValue> configOptions(ShellControl sc) throws Exception {
|
||||
var file = securityKey.determineLibraryPath(sc);
|
||||
var key = SshIdentityStrategy.getPublicKeyPath(sc, publicKey);
|
||||
return List.of(
|
||||
new KeyValue("PKCS11Provider", "\"" + file.toString() + "\""),
|
||||
new KeyValue("IdentitiesOnly", key.isPresent() ? "yes" : "no"),
|
||||
new KeyValue("IdentityFile", key.isPresent() ? key.get().toString() : "none"),
|
||||
new KeyValue("IdentityAgent", "none"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKeyStrategy getPublicKeyStrategy() {
|
||||
return PublicKeyStrategy.Fixed.of(publicKey);
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public class SshAgentKeyList {
|
||||
}
|
||||
|
||||
public static Entry findAgentIdentity(
|
||||
DataStoreEntryRef<ShellStore> ref, SshIdentityAgentStrategy strategy, String identifier) throws Exception {
|
||||
DataStoreEntryRef<ShellStore> ref, SshIdentityKeyListStrategy strategy, String identifier) throws Exception {
|
||||
var all = listAgentIdentities(ref, strategy);
|
||||
var list = all.stream()
|
||||
.filter(entry -> {
|
||||
@@ -73,16 +73,14 @@ public class SshAgentKeyList {
|
||||
return list.getFirst();
|
||||
}
|
||||
|
||||
public static List<Entry> listAgentIdentities(DataStoreEntryRef<ShellStore> ref, SshIdentityAgentStrategy strategy)
|
||||
public static List<Entry> listAgentIdentities(DataStoreEntryRef<ShellStore> ref, SshIdentityKeyListStrategy strategy)
|
||||
throws Exception {
|
||||
var session = ref != null ? ref.getStore().getOrStartSession() : LocalShell.getShell();
|
||||
strategy.prepareParent(session);
|
||||
|
||||
var socket = strategy.determinetAgentSocketLocation(session);
|
||||
var out = session.command(CommandBuilder.of()
|
||||
.add("ssh-add", "-L")
|
||||
.fixedEnvironment("SSH_AUTH_SOCK", socket != null ? socket.toString() : null))
|
||||
.readStdoutOrThrow();
|
||||
var cmd = strategy.createListCommand();
|
||||
strategy.buildCommand(cmd);
|
||||
var out = session.command(cmd).readStdoutOrThrow();
|
||||
var pattern = Pattern.compile("([^ ]+) ([^ ]+)\\s*(?: (.+))?");
|
||||
var lines = out.lines().toList();
|
||||
var list = new ArrayList<Entry>();
|
||||
|
||||
@@ -12,6 +12,7 @@ import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.geometry.Insets;
|
||||
@@ -29,13 +30,13 @@ import java.util.List;
|
||||
public class SshAgentKeyListComp extends SimpleRegionBuilder {
|
||||
|
||||
private final ObservableValue<DataStoreEntryRef<ShellStore>> ref;
|
||||
private final ObservableValue<? extends SshIdentityAgentStrategy> sshIdentityStrategy;
|
||||
private final ObservableValue<? extends SshIdentityKeyListStrategy> sshIdentityStrategy;
|
||||
private final StringProperty value;
|
||||
private final boolean useKeyNames;
|
||||
|
||||
public SshAgentKeyListComp(
|
||||
ObservableValue<DataStoreEntryRef<ShellStore>> ref,
|
||||
ObservableValue<? extends SshIdentityAgentStrategy> sshIdentityStrategy,
|
||||
ObservableValue<? extends SshIdentityKeyListStrategy> sshIdentityStrategy,
|
||||
StringProperty value,
|
||||
boolean useKeyNames) {
|
||||
this.ref = ref;
|
||||
@@ -50,6 +51,14 @@ public class SshAgentKeyListComp extends SimpleRegionBuilder {
|
||||
field.apply(struc -> struc.setPromptText(
|
||||
useKeyNames ? "<name>" : "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIBmhLUTJiP...== <key comment>"));
|
||||
var button = new ButtonComp(null, new LabelGraphic.IconGraphic("mdi2m-magnify-scan"), null);
|
||||
button.disable(Bindings.createBooleanBinding(() -> {
|
||||
try {
|
||||
sshIdentityStrategy.getValue().checkComplete();
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
return true;
|
||||
}
|
||||
}, sshIdentityStrategy));
|
||||
button.apply(struc -> {
|
||||
struc.setOnAction(event -> {
|
||||
DataStoreEntryRef<ShellStore> refToUse = ref != null && ref.getValue() != null
|
||||
|
||||
@@ -24,10 +24,10 @@ import atlantafx.base.theme.Styles;
|
||||
public class SshAgentTestComp extends SimpleRegionBuilder {
|
||||
|
||||
private final Runnable beforeTest;
|
||||
private final ObservableValue<? extends SshIdentityAgentStrategy> sshIdentityStrategy;
|
||||
private final ObservableValue<? extends SshIdentityKeyListStrategy> sshIdentityStrategy;
|
||||
|
||||
public SshAgentTestComp(
|
||||
Runnable beforeTest, ObservableValue<? extends SshIdentityAgentStrategy> sshIdentityStrategy) {
|
||||
Runnable beforeTest, ObservableValue<? extends SshIdentityKeyListStrategy> sshIdentityStrategy) {
|
||||
this.beforeTest = beforeTest;
|
||||
this.sshIdentityStrategy = sshIdentityStrategy;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
package io.xpipe.app.cred;
|
||||
|
||||
import io.xpipe.app.process.CommandBuilder;
|
||||
import io.xpipe.app.process.ShellControl;
|
||||
import io.xpipe.core.FilePath;
|
||||
|
||||
public interface SshIdentityAgentStrategy extends SshIdentityStrategy {
|
||||
public interface SshIdentityAgentStrategy extends SshIdentityKeyListStrategy {
|
||||
|
||||
void prepareParent(ShellControl parent) throws Exception;
|
||||
@Override
|
||||
default CommandBuilder createListCommand() {
|
||||
return CommandBuilder.of().add("ssh-add", "-L")
|
||||
.environment("SSH_AUTH_SOCK", sc -> {
|
||||
var socket = determineAgentSocketLocation(sc);
|
||||
return socket != null ? socket.toString() : null;
|
||||
});
|
||||
}
|
||||
|
||||
FilePath determinetAgentSocketLocation(ShellControl parent) throws Exception;
|
||||
FilePath determineAgentSocketLocation(ShellControl parent) throws Exception;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package io.xpipe.app.cred;
|
||||
|
||||
import io.xpipe.app.ext.ValidationException;
|
||||
import io.xpipe.app.process.CommandBuilder;
|
||||
import io.xpipe.app.process.ShellControl;
|
||||
import io.xpipe.core.FilePath;
|
||||
|
||||
public interface SshIdentityKeyListStrategy extends SshIdentityStrategy {
|
||||
|
||||
void checkComplete() throws ValidationException;
|
||||
|
||||
void prepareParent(ShellControl parent) throws Exception;
|
||||
|
||||
CommandBuilder createListCommand();
|
||||
}
|
||||
@@ -36,8 +36,7 @@ public interface SshIdentityStrategy {
|
||||
if (PageantStrategy.isSupported()) {
|
||||
l.add(PageantStrategy.class);
|
||||
}
|
||||
l.add(YubikeyPivStrategy.class);
|
||||
l.add(CustomPkcs11LibraryStrategy.class);
|
||||
l.add(SecurityKeyStrategy.class);
|
||||
l.add(OtherExternalAgentStrategy.class);
|
||||
|
||||
return l;
|
||||
@@ -50,12 +49,10 @@ public interface SshIdentityStrategy {
|
||||
l.add(KeyFileStrategy.class);
|
||||
l.add(OpenSshAgentStrategy.class);
|
||||
l.add(PasswordManagerAgentStrategy.class);
|
||||
l.add(PasswordManagerInPlaceKeyStrategy.class);
|
||||
l.add(CustomAgentStrategy.class);
|
||||
l.add(GpgAgentStrategy.class);
|
||||
l.add(PageantStrategy.class);
|
||||
l.add(YubikeyPivStrategy.class);
|
||||
l.add(CustomPkcs11LibraryStrategy.class);
|
||||
l.add(SecurityKeyStrategy.class);
|
||||
l.add(OtherExternalAgentStrategy.class);
|
||||
|
||||
return l;
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
package io.xpipe.app.cred;
|
||||
|
||||
import io.xpipe.app.issue.ErrorEventFactory;
|
||||
import io.xpipe.app.process.CommandBuilder;
|
||||
import io.xpipe.app.process.ShellControl;
|
||||
import io.xpipe.app.util.LicenseProvider;
|
||||
import io.xpipe.core.FilePath;
|
||||
import io.xpipe.core.KeyValue;
|
||||
import io.xpipe.core.OsType;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
@Value
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@JsonTypeName("yubikeyPiv")
|
||||
@AllArgsConstructor
|
||||
public class YubikeyPivStrategy implements SshIdentityStrategy {
|
||||
|
||||
private String getFile(ShellControl sc) {
|
||||
var file =
|
||||
switch (sc.getOsType()) {
|
||||
case OsType.MacOs ignored -> "/usr/local/lib/libykcs11.dylib";
|
||||
case OsType.Windows ignored -> {
|
||||
var x64 = "C:\\Program Files\\Yubico\\Yubico PIV Tool\\bin\\libykcs11.dll";
|
||||
if (Files.exists(Path.of(x64))) {
|
||||
yield x64;
|
||||
}
|
||||
|
||||
var x86 = "C:\\Program Files (x86)\\Yubico\\Yubico PIV Tool\\bin\\libykcs11.dll";
|
||||
if (Files.exists(Path.of(x86))) {
|
||||
yield x86;
|
||||
}
|
||||
|
||||
yield x64;
|
||||
}
|
||||
default -> "/usr/local/lib/libykcs11.so";
|
||||
};
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareParent(ShellControl parent) throws Exception {
|
||||
parent.requireLicensedFeature(LicenseProvider.get().getFeature("pkcs11Identity"));
|
||||
|
||||
var file = getFile(parent);
|
||||
if (!parent.getShellDialect().createFileExistsCommand(parent, file).executeAndCheck()) {
|
||||
throw ErrorEventFactory.expected(new IOException("Yubikey PKCS11 library at " + file + " not found"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildCommand(CommandBuilder builder) {
|
||||
builder.setup(sc -> {
|
||||
var file = getFile(sc);
|
||||
var dir = FilePath.of(file).getParent();
|
||||
if (sc.getOsType() == OsType.WINDOWS) {
|
||||
builder.addToPath(dir, true);
|
||||
} else {
|
||||
builder.addToEnvironmentPath("LD_LIBRARY_PATH", dir, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyValue> configOptions(ShellControl sc) {
|
||||
return List.of(
|
||||
KeyValue.raw("IdentitiesOnly", "no"),
|
||||
KeyValue.escape("PKCS11Provider", getFile(sc)),
|
||||
KeyValue.raw("IdentityFile", "none"),
|
||||
KeyValue.raw("IdentityAgent", "none"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKeyStrategy getPublicKeyStrategy() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class ProcessControlProvider {
|
||||
|
||||
@@ -87,4 +88,5 @@ public abstract class ProcessControlProvider {
|
||||
public abstract void pullRepository(Path target) throws Exception;
|
||||
|
||||
public abstract Optional<HttpProxy> getHttpProxy(DataStoreEntryRef<?> store);
|
||||
public abstract void addAskpassEnvironment(CommandBuilder b, String prefix, UUID requestId, UUID secretId, String... askpassName);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.xpipe.app.pwman;
|
||||
|
||||
import io.xpipe.app.cred.SshIdentityAgentStrategy;
|
||||
import io.xpipe.app.cred.SshIdentityKeyListStrategy;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
@@ -29,7 +29,7 @@ public interface PasswordManagerKeyConfiguration {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SshIdentityAgentStrategy getSshIdentityStrategy(String publicKey, boolean forward) {
|
||||
public SshIdentityKeyListStrategy getSshIdentityStrategy(String publicKey, boolean forward) {
|
||||
return strategy.getSshIdentityStrategy(publicKey, forward);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public interface PasswordManagerKeyConfiguration {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SshIdentityAgentStrategy getSshIdentityStrategy(String publicKey, boolean forward) {
|
||||
public SshIdentityKeyListStrategy getSshIdentityStrategy(String publicKey, boolean forward) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ public interface PasswordManagerKeyConfiguration {
|
||||
|
||||
boolean supportsAgentKeyNames();
|
||||
|
||||
SshIdentityAgentStrategy getSshIdentityStrategy(String publicKey, boolean forward);
|
||||
SshIdentityKeyListStrategy getSshIdentityStrategy(String publicKey, boolean forward);
|
||||
|
||||
Path getDefaultSocketLocation();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.xpipe.app.pwman;
|
||||
import io.xpipe.app.comp.base.ContextualFileReferenceChoiceComp;
|
||||
import io.xpipe.app.cred.*;
|
||||
import io.xpipe.app.ext.ProcessControlProvider;
|
||||
import io.xpipe.app.ext.ValidationException;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.process.CommandBuilder;
|
||||
@@ -47,7 +48,7 @@ public interface PasswordManagerKeyStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SshIdentityAgentStrategy getSshIdentityStrategy(String publicKey, boolean forward) {
|
||||
public SshIdentityKeyListStrategy getSshIdentityStrategy(String publicKey, boolean forward) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -113,7 +114,7 @@ public interface PasswordManagerKeyStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SshIdentityAgentStrategy getSshIdentityStrategy(String publicKey, boolean forward) {
|
||||
public SshIdentityKeyListStrategy getSshIdentityStrategy(String publicKey, boolean forward) {
|
||||
return PasswordManagerKeyStrategy.getAgentSshIdentityStrategy(
|
||||
publicKey, forward, (socket) -> SshIdentityStateManager.prepareLocalExternalAgent(socket));
|
||||
}
|
||||
@@ -175,7 +176,7 @@ public interface PasswordManagerKeyStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SshIdentityAgentStrategy getSshIdentityStrategy(String publicKey, boolean forward) {
|
||||
public SshIdentityKeyListStrategy getSshIdentityStrategy(String publicKey, boolean forward) {
|
||||
return PasswordManagerKeyStrategy.getAgentSshIdentityStrategy(publicKey, forward, (socket) -> {
|
||||
try {
|
||||
SshIdentityStateManager.prepareLocalExternalAgent(socket);
|
||||
@@ -191,12 +192,15 @@ public interface PasswordManagerKeyStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
private static SshIdentityAgentStrategy getAgentSshIdentityStrategy(
|
||||
private static SshIdentityKeyListStrategy getAgentSshIdentityStrategy(
|
||||
String publicKey, boolean forward, FailableConsumer<FilePath, Exception> con) {
|
||||
var pwman = AppPrefs.get().passwordManager().getValue();
|
||||
var socket = pwman != null ? FilePath.of(pwman.getKeyConfiguration().getDefaultSocketLocation()) : null;
|
||||
|
||||
return new SshIdentityAgentStrategy() {
|
||||
@Override
|
||||
public void checkComplete() throws ValidationException {}
|
||||
|
||||
@Override
|
||||
public void prepareParent(ShellControl parent) throws Exception {
|
||||
if (parent.isLocal()) {
|
||||
@@ -205,7 +209,7 @@ public interface PasswordManagerKeyStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilePath determinetAgentSocketLocation(ShellControl parent) throws Exception {
|
||||
public FilePath determineAgentSocketLocation(ShellControl parent) throws Exception {
|
||||
return socket != null ? socket.resolveTildeHome(parent.view().userHome()) : null;
|
||||
}
|
||||
|
||||
@@ -258,7 +262,7 @@ public interface PasswordManagerKeyStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SshIdentityAgentStrategy getSshIdentityStrategy(String publicKey, boolean forward) {
|
||||
public SshIdentityKeyListStrategy getSshIdentityStrategy(String publicKey, boolean forward) {
|
||||
return OpenSshAgentStrategy.builder().build();
|
||||
}
|
||||
}
|
||||
@@ -288,14 +292,14 @@ public interface PasswordManagerKeyStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SshIdentityAgentStrategy getSshIdentityStrategy(String publicKey, boolean forward) {
|
||||
public SshIdentityKeyListStrategy getSshIdentityStrategy(String publicKey, boolean forward) {
|
||||
return PageantStrategy.builder().build();
|
||||
}
|
||||
}
|
||||
|
||||
boolean useAgent();
|
||||
|
||||
SshIdentityAgentStrategy getSshIdentityStrategy(String publicKey, boolean forward);
|
||||
SshIdentityKeyListStrategy getSshIdentityStrategy(String publicKey, boolean forward);
|
||||
|
||||
static List<Class<?>> getClasses() {
|
||||
var l = new ArrayList<Class<?>>();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.xpipe.app.util;
|
||||
|
||||
import io.xpipe.app.cred.SecurityKeyImpl;
|
||||
import io.xpipe.app.cred.SshIdentityStrategy;
|
||||
import io.xpipe.app.ext.HostAddress;
|
||||
import io.xpipe.app.process.ShellDialect;
|
||||
@@ -78,6 +79,7 @@ public class AppJacksonModule extends SimpleModule {
|
||||
context.registerSubtypes(new NamedType(t.getClass()));
|
||||
}
|
||||
|
||||
context.registerSubtypes(SecurityKeyImpl.getClasses());
|
||||
context.registerSubtypes(SshIdentityStrategy.getClasses());
|
||||
context.registerSubtypes(PasswordManagerKeyStrategy.getClasses());
|
||||
context.registerSubtypes(PasswordManager.getClasses());
|
||||
|
||||
Generated
+1
@@ -169,4 +169,5 @@ protonPass=Proton Pass
|
||||
passwork=Passwork
|
||||
passworkPlaceholder=Item ID
|
||||
socks5=SOCKS5
|
||||
openSc=OpenSC
|
||||
|
||||
|
||||
Generated
+7
-1
@@ -841,7 +841,8 @@ sshAgent=OpenSSH agent
|
||||
#context: nothing selected
|
||||
none=None
|
||||
index=Index ...
|
||||
otherExternal=Other external agent
|
||||
#force
|
||||
otherExternal=Other external auth
|
||||
sync=Sync
|
||||
vaultSync=Vault sync
|
||||
customUsername=Username
|
||||
@@ -1640,6 +1641,8 @@ k8sEditPodTitle=Apply changes
|
||||
k8sEditPodContent=Do you want to apply the changes made via the command kubectl apply? A restart is likely required for changes to apply.
|
||||
virshEditDomainTitle=Apply changes
|
||||
virshEditDomainContent=Do you want to apply the changes to the domain? A restart is likely required for changes to apply.
|
||||
pkcs11Impl=PKCS#11 implementation
|
||||
pkcs11ImplDescription=The PKCS#11 implementation to use to retrieve hardware keys
|
||||
pkcs11Library=PKCS#11 library
|
||||
pkcs11LibraryDescription=The path of the dynamically linked library file
|
||||
sshAgentSocket=Custom SSH agent socket
|
||||
@@ -2100,3 +2103,6 @@ addProxy=Add proxy ...
|
||||
httpProxyError=Configure HTTP proxy settings
|
||||
httpProxyErrorDescription=Add HTTP proxy authentication details in the settings menu
|
||||
systemDefault=System default
|
||||
hardwareSecurityKey=Hardware security key (PKCS#11)
|
||||
macOsKeychain=macOS keychain
|
||||
customLibrary=Custom library
|
||||
|
||||
Reference in New Issue
Block a user