Rework password handling

This commit is contained in:
crschnick
2023-08-08 04:24:07 +00:00
parent f8b56ab774
commit 3d32d6cd84
12 changed files with 179 additions and 68 deletions

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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));
}
}

View 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));
}
}

View File

@@ -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;