mirror of
https://github.com/xpipe-io/xpipe.git
synced 2026-06-22 06:19:02 -04:00
Rework password handling
This commit is contained in:
@@ -14,7 +14,7 @@ public class AskpassExchangeImpl extends AskpassExchange
|
||||
OperationMode.switchTo(OperationMode.TRAY);
|
||||
}
|
||||
|
||||
var r = AskpassAlert.query(msg.getPrompt(), msg.getRequest(), msg.getId());
|
||||
var r = AskpassAlert.query(msg.getPrompt(), msg.getRequest(), msg.getStoreId(), msg.getSubId());
|
||||
return Response.builder().value(r != null ? r.getSecretValue() : null).build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,17 @@ public class AskpassAlert {
|
||||
private static final Set<UUID> cancelledRequests = new HashSet<>();
|
||||
private static final Set<UUID> requests = new HashSet<>();
|
||||
|
||||
public static SecretValue query(String prompt, UUID requestId, UUID secretId) {
|
||||
public static SecretValue query(String prompt, UUID requestId, UUID secretId, int sub) {
|
||||
if (cancelledRequests.contains(requestId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (SecretCache.get(secretId).isPresent() && requests.contains(requestId)) {
|
||||
SecretCache.clear(secretId);
|
||||
var ref = new SecretManager.SecretReference(secretId, sub);
|
||||
if (SecretManager.get(ref).isPresent() && requests.contains(requestId)) {
|
||||
SecretManager.clear(ref);
|
||||
}
|
||||
|
||||
var found = SecretCache.get(secretId);
|
||||
var found = SecretManager.get(ref);
|
||||
if (found.isPresent()) {
|
||||
return found.get();
|
||||
}
|
||||
@@ -49,7 +50,7 @@ public class AskpassAlert {
|
||||
// If the result is null, assume that the operation was aborted by the user
|
||||
if (r != null) {
|
||||
requests.add(requestId);
|
||||
SecretCache.set(secretId, r);
|
||||
SecretManager.set(ref, r);
|
||||
} else {
|
||||
cancelledRequests.add(requestId);
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
package io.xpipe.app.util;
|
||||
|
||||
import io.xpipe.core.util.SecretValue;
|
||||
import io.xpipe.core.util.UuidHelper;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SecretCache {
|
||||
|
||||
private static final Map<UUID, SecretValue> passwords = new HashMap<>();
|
||||
|
||||
public static SecretValue retrieve(SecretRetrievalStrategy strategy, String prompt, Object key) throws Exception {
|
||||
var id = UuidHelper.generateFromObject(key);
|
||||
if (passwords.containsKey(id)) {
|
||||
return passwords.get(id);
|
||||
}
|
||||
|
||||
if (strategy == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var pass = strategy.retrieve(prompt, id);
|
||||
if (pass == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
passwords.put(id, pass);
|
||||
return pass;
|
||||
}
|
||||
|
||||
public static void clear(UUID id) {
|
||||
passwords.remove(id);
|
||||
}
|
||||
|
||||
public static void set(UUID id, SecretValue value) {
|
||||
passwords.put(id, value);
|
||||
}
|
||||
|
||||
public static Optional<SecretValue> get(UUID id) {
|
||||
return Optional.ofNullable(passwords.get(id));
|
||||
}
|
||||
}
|
||||
72
app/src/main/java/io/xpipe/app/util/SecretManager.java
Normal file
72
app/src/main/java/io/xpipe/app/util/SecretManager.java
Normal file
@@ -0,0 +1,72 @@
|
||||
package io.xpipe.app.util;
|
||||
|
||||
import io.xpipe.core.util.SecretValue;
|
||||
import io.xpipe.core.util.UuidHelper;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class SecretManager {
|
||||
|
||||
@Value
|
||||
@AllArgsConstructor
|
||||
public static class SecretReference {
|
||||
|
||||
UUID secretId;
|
||||
int subId;
|
||||
|
||||
public SecretReference(Object store) {
|
||||
this.secretId = UuidHelper.generateFromObject(store);
|
||||
this.subId = 0;
|
||||
}
|
||||
|
||||
public SecretReference(Object store, int sub) {
|
||||
this.secretId = UuidHelper.generateFromObject(store);
|
||||
this.subId = sub;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Map<SecretReference, SecretValue> passwords = new HashMap<>();
|
||||
|
||||
public static SecretValue retrieve(SecretRetrievalStrategy strategy, String prompt, Object key) throws Exception {
|
||||
return retrieve(strategy, prompt,key, 0);
|
||||
}
|
||||
public static SecretValue retrieve(SecretRetrievalStrategy strategy, String prompt, Object key, int sub) throws Exception {
|
||||
var ref = new SecretReference(key, sub);
|
||||
if (strategy == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (strategy.shouldCache() && passwords.containsKey(ref)) {
|
||||
return passwords.get(ref);
|
||||
}
|
||||
|
||||
var pass = strategy.retrieve(prompt, ref.getSecretId(), ref.getSubId());
|
||||
if (pass == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (strategy.shouldCache()) {
|
||||
passwords.put(ref, pass);
|
||||
}
|
||||
return pass;
|
||||
}
|
||||
|
||||
public static void clearAll(Object store) {
|
||||
var id = UuidHelper.generateFromObject(store);
|
||||
passwords.entrySet().removeIf(secretReferenceSecretValueEntry -> secretReferenceSecretValueEntry.getKey().getSecretId().equals(id));
|
||||
}
|
||||
|
||||
public static void clear(SecretReference ref) {
|
||||
passwords.remove(ref);
|
||||
}
|
||||
|
||||
public static void set(SecretReference ref, SecretValue value) {
|
||||
passwords.put(ref, value);
|
||||
}
|
||||
|
||||
public static Optional<SecretValue> get(SecretReference ref) {
|
||||
return Optional.ofNullable(passwords.get(ref));
|
||||
}
|
||||
}
|
||||
@@ -26,15 +26,17 @@ import java.util.function.Supplier;
|
||||
})
|
||||
public interface SecretRetrievalStrategy {
|
||||
|
||||
SecretValue retrieve(String displayName, UUID id) throws Exception;
|
||||
SecretValue retrieve(String displayName, UUID id, int sub) throws Exception;
|
||||
|
||||
boolean isLocalAskpassCompatible();
|
||||
|
||||
boolean shouldCache();
|
||||
|
||||
@JsonTypeName("none")
|
||||
public static class None implements SecretRetrievalStrategy {
|
||||
|
||||
@Override
|
||||
public SecretValue retrieve(String displayName, UUID id) {
|
||||
public SecretValue retrieve(String displayName, UUID id, int sub) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -42,6 +44,11 @@ public interface SecretRetrievalStrategy {
|
||||
public boolean isLocalAskpassCompatible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCache() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonTypeName("reference")
|
||||
@@ -55,7 +62,7 @@ public interface SecretRetrievalStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretValue retrieve(String displayName, UUID id) {
|
||||
public SecretValue retrieve(String displayName, UUID id, int sub) {
|
||||
return supplier.get();
|
||||
}
|
||||
|
||||
@@ -63,6 +70,11 @@ public interface SecretRetrievalStrategy {
|
||||
public boolean isLocalAskpassCompatible() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCache() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonTypeName("inPlace")
|
||||
@@ -79,10 +91,14 @@ public interface SecretRetrievalStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretValue retrieve(String displayName, UUID id) {
|
||||
public SecretValue retrieve(String displayName, UUID id, int sub) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCache() {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean isLocalAskpassCompatible() {
|
||||
return false;
|
||||
@@ -93,10 +109,14 @@ public interface SecretRetrievalStrategy {
|
||||
public static class Prompt implements SecretRetrievalStrategy {
|
||||
|
||||
@Override
|
||||
public SecretValue retrieve(String displayName, UUID id) {
|
||||
return AskpassAlert.query(displayName, UUID.randomUUID(), id);
|
||||
public SecretValue retrieve(String displayName, UUID id, int sub) {
|
||||
return AskpassAlert.query(displayName, UUID.randomUUID(), id, sub);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCache() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean isLocalAskpassCompatible() {
|
||||
return true;
|
||||
@@ -112,7 +132,7 @@ public interface SecretRetrievalStrategy {
|
||||
String key;
|
||||
|
||||
@Override
|
||||
public SecretValue retrieve(String displayName, UUID id) throws Exception {
|
||||
public SecretValue retrieve(String displayName, UUID id, int sub) throws Exception {
|
||||
var cmd = AppPrefs.get().passwordManagerString(key);
|
||||
if (cmd == null) {
|
||||
return null;
|
||||
@@ -123,6 +143,11 @@ public interface SecretRetrievalStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCache() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLocalAskpassCompatible() {
|
||||
return false;
|
||||
@@ -138,12 +163,17 @@ public interface SecretRetrievalStrategy {
|
||||
String command;
|
||||
|
||||
@Override
|
||||
public SecretValue retrieve(String displayName, UUID id) throws Exception {
|
||||
public SecretValue retrieve(String displayName, UUID id, int sub) throws Exception {
|
||||
try (var cc = new LocalStore().createBasicControl().command(command).start()) {
|
||||
return SecretHelper.encrypt(cc.readStdoutOrThrow());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCache() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLocalAskpassCompatible() {
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user