diff --git a/app/src/main/java/io/xpipe/app/exchange/AskpassExchangeImpl.java b/app/src/main/java/io/xpipe/app/exchange/AskpassExchangeImpl.java index 17332e410..24ae0e1aa 100644 --- a/app/src/main/java/io/xpipe/app/exchange/AskpassExchangeImpl.java +++ b/app/src/main/java/io/xpipe/app/exchange/AskpassExchangeImpl.java @@ -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(); } } diff --git a/app/src/main/java/io/xpipe/app/util/AskpassAlert.java b/app/src/main/java/io/xpipe/app/util/AskpassAlert.java index d5fa4b11b..131142d27 100644 --- a/app/src/main/java/io/xpipe/app/util/AskpassAlert.java +++ b/app/src/main/java/io/xpipe/app/util/AskpassAlert.java @@ -15,16 +15,17 @@ public class AskpassAlert { private static final Set cancelledRequests = new HashSet<>(); private static final Set 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); } diff --git a/app/src/main/java/io/xpipe/app/util/SecretCache.java b/app/src/main/java/io/xpipe/app/util/SecretCache.java deleted file mode 100644 index da37f11f0..000000000 --- a/app/src/main/java/io/xpipe/app/util/SecretCache.java +++ /dev/null @@ -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 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 get(UUID id) { - return Optional.ofNullable(passwords.get(id)); - } -} diff --git a/app/src/main/java/io/xpipe/app/util/SecretManager.java b/app/src/main/java/io/xpipe/app/util/SecretManager.java new file mode 100644 index 000000000..392134bb2 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/util/SecretManager.java @@ -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 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 get(SecretReference ref) { + return Optional.ofNullable(passwords.get(ref)); + } +} diff --git a/app/src/main/java/io/xpipe/app/util/SecretRetrievalStrategy.java b/app/src/main/java/io/xpipe/app/util/SecretRetrievalStrategy.java index 0cfb37aa3..58d12828a 100644 --- a/app/src/main/java/io/xpipe/app/util/SecretRetrievalStrategy.java +++ b/app/src/main/java/io/xpipe/app/util/SecretRetrievalStrategy.java @@ -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; diff --git a/app/src/main/resources/io/xpipe/app/resources/img/os/endeavour-dark.svg b/app/src/main/resources/io/xpipe/app/resources/img/os/endeavour-dark.svg new file mode 100644 index 000000000..2fb99e60d --- /dev/null +++ b/app/src/main/resources/io/xpipe/app/resources/img/os/endeavour-dark.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/resources/io/xpipe/app/resources/img/os/endeavour.svg b/app/src/main/resources/io/xpipe/app/resources/img/os/endeavour.svg new file mode 100644 index 000000000..1eea6a89e --- /dev/null +++ b/app/src/main/resources/io/xpipe/app/resources/img/os/endeavour.svg @@ -0,0 +1,29 @@ + + + + EndeavourOS Logo + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/resources/io/xpipe/app/resources/lang/translations_en.properties b/app/src/main/resources/io/xpipe/app/resources/lang/translations_en.properties index 4bc8ece7e..c974304d3 100644 --- a/app/src/main/resources/io/xpipe/app/resources/lang/translations_en.properties +++ b/app/src/main/resources/io/xpipe/app/resources/lang/translations_en.properties @@ -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 diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/AskpassExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/AskpassExchange.java index 6d3b3b8c2..442e9ccef 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/AskpassExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/AskpassExchange.java @@ -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; diff --git a/core/src/main/java/io/xpipe/core/process/ElevationConfig.java b/core/src/main/java/io/xpipe/core/process/ElevationConfig.java new file mode 100644 index 000000000..72874c089 --- /dev/null +++ b/core/src/main/java/io/xpipe/core/process/ElevationConfig.java @@ -0,0 +1,9 @@ +package io.xpipe.core.process; + +import lombok.Value; + +@Value +public class ElevationConfig { + + boolean requiresPassword; +} diff --git a/core/src/main/java/io/xpipe/core/process/ShellControl.java b/core/src/main/java/io/xpipe/core/process/ShellControl.java index 1f03f4825..dc4aa65c1 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellControl.java +++ b/core/src/main/java/io/xpipe/core/process/ShellControl.java @@ -28,6 +28,8 @@ public interface ShellControl extends ProcessControl { ShellControl onExit(Consumer pc); + ShellControl onFail(Consumer t); + ElevationResult elevateCommand(String input) throws Exception; ShellControl withExceptionConverter(ExceptionConverter converter); ShellControl withErrorFormatter(Function 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 elevationFunction); default ShellControl elevationPassword(SecretValue value) { diff --git a/ext/base/src/main/resources/io/xpipe/ext/base/resources/lang/translations_en.properties b/ext/base/src/main/resources/io/xpipe/ext/base/resources/lang/translations_en.properties index 207dd0d18..57f897c4c 100644 --- a/ext/base/src/main/resources/io/xpipe/ext/base/resources/lang/translations_en.properties +++ b/ext/base/src/main/resources/io/xpipe/ext/base/resources/lang/translations_en.properties @@ -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