Keeper fixes

This commit is contained in:
crschnick
2026-01-27 13:15:49 +00:00
parent 7b6e6ed0ab
commit 180cbb722d
3 changed files with 66 additions and 24 deletions
@@ -1,11 +1,9 @@
package io.xpipe.app.pwman;
import io.xpipe.app.comp.base.ButtonComp;
import io.xpipe.app.comp.base.ListBoxViewComp;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.ProcessControlProvider;
import io.xpipe.app.issue.ErrorEventFactory;
import io.xpipe.app.platform.DerivedObservableList;
import io.xpipe.app.platform.OptionsBuilder;
import io.xpipe.app.process.*;
import io.xpipe.app.secret.SecretManager;
@@ -13,26 +11,22 @@ import io.xpipe.app.secret.SecretPromptStrategy;
import io.xpipe.app.secret.SecretQueryState;
import io.xpipe.app.terminal.TerminalLaunch;
import io.xpipe.app.util.AskpassAlert;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.*;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.beans.property.SimpleStringProperty;
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;
import lombok.extern.jackson.Jacksonized;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.Duration;
import java.util.List;
import java.util.Random;
import java.util.UUID;
@JsonTypeName("keeper")
@@ -45,6 +39,10 @@ public class KeeperPasswordManager implements PasswordManager {
private static final UUID KEEPER_PASSWORD_ID = UUID.randomUUID();
private static ShellControl SHELL;
private final Boolean mfa;
private final String totpDuration;
@JsonIgnore
private boolean hasCompletedRequestInSession;
private static synchronized ShellControl getOrStartShell() throws Exception {
if (SHELL == null) {
@@ -60,13 +58,18 @@ public class KeeperPasswordManager implements PasswordManager {
@SuppressWarnings("unused")
public static OptionsBuilder createOptions(Property<KeeperPasswordManager> p) {
var mfa = new SimpleObjectProperty<>(p.getValue().getMfa());
var mfa = new SimpleBooleanProperty(p.getValue().getMfa() != null ? p.getValue().getMfa() : false);
var duration = new SimpleStringProperty(p.getValue().getTotpDuration());
return new OptionsBuilder()
.nameAndDescription("keeperUseMfa")
.addToggle(mfa)
.name("keeperTotpDuration")
.description(AppI18n.observable("keeperTotpDurationDescription", "login | 12_hours | 24_hours | 30_days | forever"))
.addString(duration)
.hide(mfa.not())
.bind(
() -> {
return KeeperPasswordManager.builder().mfa(mfa.get()).build();
return KeeperPasswordManager.builder().mfa(mfa.get()).totpDuration(duration.get()).build();
},
p);
}
@@ -122,22 +125,33 @@ public class KeeperPasswordManager implements PasswordManager {
.add("--format", "json", "--unmask")
.add("--password")
.addLiteral(r.getSecretValue());
FilePath file = null;
FilePath file = sc.getSystemTemporaryDirectory().join("keeper" + Math.abs(new Random().nextInt()) + ".txt");
CommandBuilder fullB;
if (mfa != null && mfa) {
var totp = AskpassAlert.queryRaw("Enter Keeper 2FA Code", null, true);
if (totp.getState() != SecretQueryState.NORMAL) {
return null;
}
var input = """
var index = getTotpDurationIndex();
if (hasCompletedRequestInSession && index > 0) {
var input = """
1
%s
""".formatted(totp.getSecret().getSecretValue());
file = sc.getSystemTemporaryDirectory().join("keeper.txt");
sc.view().writeTextFile(file, input);
fullB = CommandBuilder.of().add(sc.getShellDialect() == ShellDialects.CMD ? "type" : "cat").addFile(file).add("|").add(b);
""";
sc.view().writeTextFile(file, input);
fullB = CommandBuilder.of().add(sc.getShellDialect() == ShellDialects.CMD ? "type" : "cat").addFile(file).add("|").add(b);
} else {
var totp = AskpassAlert.queryRaw("Enter Keeper 2FA Code", null, true);
if (totp.getState() != SecretQueryState.NORMAL) {
return null;
}
var input = """
1%s
%s
""".formatted(index != -1 ? "\n" + getTotpDurationValues().get(index) : "", totp.getSecret().getSecretValue());
sc.view().writeTextFile(file, input);
fullB = CommandBuilder.of().add(sc.getShellDialect() == ShellDialects.CMD ? "type" : "cat").addFile(file).add("|").add(b);
}
} else {
fullB = b;
}
@@ -192,6 +206,9 @@ public class KeeperPasswordManager implements PasswordManager {
}
var message = !err.isEmpty() ? outPrefix + "\n" + err : outPrefix;
if (message.isEmpty()) {
message = result[0] + "\n" + result[1];
}
ErrorEventFactory.fromMessage(message).expected().handle();
return null;
}
@@ -205,6 +222,8 @@ public class KeeperPasswordManager implements PasswordManager {
return null;
}
hasCompletedRequestInSession = true;
var fields = tree.get("fields");
// There multiple schemas
if (fields == null || !fields.isArray()) {
@@ -255,6 +274,17 @@ public class KeeperPasswordManager implements PasswordManager {
}
}
private List<String> getTotpDurationValues() {
var values = List.of("login", "12_hours", "24_hours", "30_days", "forever");
return values;
}
private int getTotpDurationIndex() {
var values = getTotpDurationValues();
var index = totpDuration != null ? values.indexOf(totpDuration) : -1;
return index;
}
@Override
public String getKeyPlaceholder() {
return "Record UID";
@@ -264,4 +294,9 @@ public class KeeperPasswordManager implements PasswordManager {
public String getWebsite() {
return "https://www.keepersecurity.com";
}
@Override
public Duration getCacheDuration() {
return (mfa != null && mfa && getTotpDurationIndex() < 1) ? Duration.ofDays(10) : Duration.ofSeconds(3);
}
}
@@ -6,6 +6,7 @@ import io.xpipe.core.SecretValue;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Value;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
@@ -38,6 +39,10 @@ public interface PasswordManager {
String getWebsite();
default Duration getCacheDuration() {
return Duration.ofSeconds(30);
}
@Value
class CredentialResult {
+2
View File
@@ -1906,6 +1906,8 @@ testingConnection=Testing connection ...
openManagementConsole=Open management console
keeperUseMfa=Use 2FA authenticator app
keeperUseMfaDescription=Enable this if your Keeper account requires an 2FA TOTP to access passwords.
keeperTotpDuration=Custom 2FA code duration
keeperTotpDurationDescription=Override the default duration on how long a 2FA code is valid. Only applies if your organization policy allows changing the duration.\n\nPossible values are: $VALUES$
extractReusableIdentities=Extract reusable identities
identitiesAdded=Identities added
syncMode=Sync mode