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;

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 59 KiB

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="50.4mm" height="54.4mm" inkscape:version="1.0.2 (e86c870879, 2021-01-15, custom)" sodipodi:docname="Endeavouros_Logo.svg" version="1.1" viewBox="0 0 50.4 54.4" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd">
<sodipodi:namedview id="cvfa" bordercolor="#666666" borderopacity="1.0" inkscape:current-layer="g1230-2" inkscape:cx="196.53533" inkscape:cy="181.7514" inkscape:document-rotation="0" inkscape:document-units="mm" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:window-height="1014" inkscape:window-maximized="1" inkscape:window-width="1920" inkscape:window-x="0" inkscape:window-y="36" inkscape:zoom="1.743335" pagecolor="#ffffff" showgrid="false"/>
<title>EndeavourOS Logo</title>
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<g transform="translate(-66.5 -122)" inkscape:groupmode="layer" inkscape:label="Layer 1">
<g transform="translate(76.2 -12.7)">
<g transform="matrix(1.47 0 0 1.47 -519 105)">
<g transform="matrix(.963 0 0 .983 13.5 .76)">
<g transform="matrix(.678 0 0 .678 452 49.2)">
<path d="m-127-42.3c4.57 6.45 23.8 31.4 10.7 36.6-6.12 2.81-34-1.65-33.6-0.921-2 3.28-3.59 5.92-3.59 5.92s21.5 0.967 38.1-1.27c23.7-3.18-4.88-33.5-11.6-40.3z" style="fill:#7f7fff;stroke-width:.585" inkscape:connector-curvature="0"/>
<path d="m-127-42.3c-1.52 0.209-29.4 34.5-29.4 34.5s2.01 0.57 6.58 1.23c1.48-1.15 22.3-36.2 22.9-35.7-0.0107-0.0141-0.028-0.0193-0.0522-0.016z" style="fill:#ff7f7f;stroke-width:.585" inkscape:connector-curvature="0"/>
<path d="m-127-42.3c-0.96-0.156-22.9 35.7-22.9 35.7s19.9 2.1 28.1 1.96c23.1-0.39 0.176-30.6-5.16-37.7-7e-3 -7e-3 -0.0151-0.0108-0.0248-0.0124z" style="fill:#7f3fbf;stroke-width:.585" inkscape:connector-curvature="0"/>
</g>
<path transform="matrix(.179 0 0 .179 345 18.5)" d="m116 183c-2.54 0-4.6 2.05-4.6 4.55v24.1c0.0501 2.46 2.1 4.5 4.6 4.5h2.49c2.44-0.102 4.39-2.05 4.44-4.5v-24.1c0-2.46-2-4.45-4.44-4.55h-0.0117zm-107 0.314c-0.818 0-1.48 0.638-1.54 1.41l-0.00195 0.0254v31.1h7.74c1.2 0 1.93-0.586 2.24-1.14l0.0059-0.0117 0.0059-0.01c0.179-0.359 0.689-1.56 1.04-2.36l0.35-0.789h-7.04v-10.3c0-0.0711 0.131-0.203 0.203-0.203h1.35c1.2 0 1.9-0.613 2.2-1.14l0.0059-0.0117 0.0059-0.01c0.0958-0.191 0.249-0.559 0.445-1.02 0.196-0.46 0.421-0.986 0.598-1.38l0.35-0.789h-5.15v-8.78c0-0.186 0.0165-0.203 0.203-0.203h2.6c1.21 0 1.95-0.631 2.22-1.23l1.44-3.12zm15 0v32.5h4.35v-13l2.35 10.6c0.0657 0.307 0.172 0.902 0.561 1.45 0.39 0.549 1.12 1.01 2.14 1.01h2.04v-32.5h-4.35v13.1l-2.21-10.9v-2e-3c-0.0953-0.477-0.241-1.05-0.693-1.5-0.453-0.453-1.16-0.701-2.15-0.701zm18.1 0v32.5h6.8c2.5 0 4.5-2.01 4.55-4.5v-23.5c-0.0501-2.49-2.06-4.5-4.55-4.5zm21.1 0c-0.818 0-1.48 0.637-1.55 1.41l-2e-3 0.0254v31.1h7.74c1.2 0 1.93-0.586 2.24-1.14l0.0059-0.0117 0.0059-0.01c0.179-0.358 0.689-1.56 1.04-2.36l0.35-0.789h-7.04v-10.3c0-0.073 0.13-0.203 0.201-0.203h1.35c1.2 0 1.9-0.614 2.2-1.14l0.0059-0.0117 0.0059-0.01c0.0953-0.19 0.251-0.559 0.447-1.02 0.197-0.46 0.421-0.986 0.598-1.38l0.35-0.789h-5.15v-8.78c0-0.188 0.0168-0.203 0.201-0.203h2.6c1.21 0 1.95-0.625 2.22-1.22l2e-3 -4e-3 1.44-3.12zm17.9 0-0.0723 0.477-4.86 32.1h4.69l2.34-17.9 2.34 17.9h4.69l-4.63-30.4v-2e-3c-0.0702-0.445-0.159-0.964-0.504-1.42-0.345-0.458-0.964-0.756-1.76-0.756zm11.7 0 0.0996 0.646 4.49 29.2v4e-3c0.0167 0.101 0.104 0.75 0.436 1.37 0.332 0.623 1.02 1.29 2.05 1.29h2.1l4.97-32.5h-4.37l-2.71 17.8-2.45-16.1-2e-3 -6e-3c-0.211-1.21-0.974-1.68-1.5-1.68zm38.7 0c-0.799 0-1.43 0.638-1.5 1.41l-2e-3 0.0254v26.9c0 2.5 2.07 4.42 4.55 4.42h3.03v-0.16c2.13-0.31 3.86-2 3.86-4.26v-28.3h-4.35v27.7c0 0.639-0.506 1.14-1.15 1.14h-0.492c-0.64 0-1.1-0.488-1.1-1.14v-27.7zm16.7 0v32.5h4.35v-13.5h1.15c0.318 0 0.566 0.084 0.75 0.271 0.184 0.187 0.35 0.525 0.35 1.19v12.1h4.35v-12.6s0.0279-0.642-0.123-1.38c-0.107-0.525-0.385-1.09-0.807-1.57 0.125-0.12 0.325-0.173 0.41-0.311 0.224-0.362 0.342-0.759 0.414-1.13 0.145-0.74 0.105-1.41 0.105-1.41v-9.66c0-2.51-2.05-4.51-4.51-4.51zm-30.8 3.63h0.467c0.64 0 1.14 0.491 1.14 1.19v22.9c0 0.699-0.505 1.19-1.14 1.19h-0.467c-0.655-0.0312-1.17-0.549-1.17-1.19v-22.9c0-0.64 0.516-1.16 1.17-1.19zm35.2 0.492h1.15c0.328 0 0.609 0.0895 0.789 0.24 0.18 0.151 0.311 0.362 0.311 0.816v8.44c0 0.613-0.151 0.867-0.318 1.01-0.167 0.146-0.434 0.223-0.781 0.223h-1.15zm-106 0.225h1.55c0.355 0 0.65 0.0931 0.834 0.244 0.184 0.151 0.312 0.359 0.312 0.812v21.7c0 0.481-0.133 0.7-0.316 0.854-0.183 0.153-0.475 0.246-0.83 0.246h-1.55z" style="color-rendering:auto;color:#000000;dominant-baseline:auto;fill:#280b0b;font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;image-rendering:auto;isolation:auto;mix-blend-mode:normal;shape-padding:0;shape-rendering:auto;solid-color:#000000;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/>
<path transform="matrix(.179 0 0 .179 345 18.5)" d="m169 183c-2.54 0-4.6 2.05-4.6 4.55v24.1c0.0501 2.46 2.1 4.5 4.6 4.5h2.49c2.44-0.102 4.39-2.05 4.44-4.5v-24.1c0-2.46-2-4.45-4.44-4.55h-0.0117zm18.3 0c-2.57 0-4.6 2.23-4.6 4.73v3.2s-0.128 2.3 1.51 5.29l3.86 7.23 4e-3 8e-3s0.33 0.577 0.664 1.38c0.334 0.802 0.654 1.83 0.654 2.61v3.59c0 0.698-0.506 1.19-1.15 1.19-0.64 0-1.14-0.504-1.14-1.14v-6.34l-0.789 0.35c-0.405 0.18-0.931 0.416-1.39 0.617-0.457 0.201-0.894 0.381-0.939 0.396l-0.0508 0.0176-0.043 0.0274c-0.539 0.3-1.19 1.03-1.19 2.24v3.24c0.0501 2.44 2 4.44 4.44 4.54h2.04c2.54 0 4.6-2.05 4.6-4.55v-3.96s0.129-2.3-1.5-5.29l-3.86-7.28-4e-3 -0.01s-0.329-0.563-0.662-1.35c-0.333-0.79-0.654-1.8-0.654-2.58v-3.01c0-0.699 0.507-1.19 1.15-1.19 0.64 0 1.15 0.491 1.15 1.19v4.48l0.775-0.328c0.871-0.367 2.1-0.934 2.41-1.09l0.0117-8e-3 0.0117-6e-3c0.54-0.3 1.18-1.03 1.18-2.24v-1.21c0-2.46-1.96-4.68-4.45-4.73h-6e-3zm-17.4 3.95h0.467c0.64 0 1.14 0.491 1.14 1.19v22.9c0 0.699-0.505 1.19-1.14 1.19h-0.467c-0.656-0.0312-1.17-0.549-1.17-1.19v-22.9c0-0.64 0.516-1.16 1.17-1.19z" style="fill:#7f7fff;font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

View file

@ -40,6 +40,13 @@ dragFiles=Drag files within browser
dragLocalFiles=Drag local files from here
null=$VALUE$ must be not null
roots=Roots
terminator=Terminator
kitty=Kitty
terminology=Terminology
coolRetroTerm=Cool Retro Term
guake=Guake
alacritty=Alacritty
tilda=Tilda
recent=Recent
hostFeatureUnsupported=$FEATURE$ is not installed on the host
missingStore=$NAME$ does not exist

View file

@ -21,7 +21,9 @@ public class AskpassExchange implements MessageExchange {
@Value
public static class Request implements RequestMessage {
@NonNull
UUID id;
UUID storeId;
int subId;
@NonNull
UUID request;

View file

@ -0,0 +1,9 @@
package io.xpipe.core.process;
import lombok.Value;
@Value
public class ElevationConfig {
boolean requiresPassword;
}

View file

@ -28,6 +28,8 @@ public interface ShellControl extends ProcessControl {
ShellControl onExit(Consumer<ShellControl> pc);
ShellControl onFail(Consumer<Throwable> t);
ElevationResult elevateCommand(String input) throws Exception;
ShellControl withExceptionConverter(ExceptionConverter converter);
ShellControl withErrorFormatter(Function<String, String> formatter);
@ -90,12 +92,14 @@ public interface ShellControl extends ProcessControl {
}
}
ElevationResult elevateCommand(String input) throws Exception;
ElevationResult buildElevatedCommand(String input) throws Exception;
void restart() throws Exception;
OsType getOsType();
ElevationConfig getElevationConfig();
ShellControl elevated(String message, FailableFunction<ShellControl, Boolean, Exception> elevationFunction);
default ShellControl elevationPassword(SecretValue value) {

View file

@ -17,13 +17,6 @@ createShortcut=Create desktop shortcut
browseFiles=Browse Files
targetPath=Target path
newDirectory=New directory
terminator=Terminator
kitty=Kitty
terminology=Terminology
coolRetroTerm=Cool Retro Term
guake=Guake
alacritty=Alacritty
tilda=Tilda
copyShareLink=Copy share link
selectStore=Select Store
saveSource=Save for later