This commit is contained in:
crschnick
2025-03-08 22:02:02 +00:00
parent e8f4145ed6
commit 0689a27bfe
14 changed files with 88 additions and 128 deletions

View File

@@ -76,7 +76,7 @@ public class ContextualFileReference {
var start = getDataDir();
var normalizedPath = FilePath.of(path).normalize().toUnix();
if (normalizedPath.startsWith(start) && !normalizedPath.equals(start)) {
return "<DATA>" + "/" + start.relativize(normalizedPath);
return "<DATA>" + "/" + normalizedPath.relativize(start);
}
return path;
}

View File

@@ -133,11 +133,17 @@ public class DataStorageNode {
}
public boolean hasAccess() {
return !perUser || availableForUser;
// In this case the loading failed
// We have access to it, we just can't read it
if (!perUser && !readableForUser) {
return true;
}
return !perUser || readableForUser;
}
JsonNode contentNode;
boolean perUser;
boolean availableForUser;
boolean readableForUser;
boolean encrypted;
}

View File

@@ -1,5 +1,6 @@
package io.xpipe.app.storage;
import com.fasterxml.jackson.core.JacksonException;
import io.xpipe.app.ext.DataStoreProvider;
import io.xpipe.app.ext.DataStoreProviders;
import io.xpipe.app.ext.UserScopeStore;
@@ -285,8 +286,15 @@ public class DataStoreEntry extends StorageElement {
notes = null;
}
var fileNode = mapper.readTree(storeFile.toFile());
var node = DataStorageNode.readPossiblyEncryptedNode(fileNode);
DataStorageNode node = null;
try {
var fileNode = mapper.readTree(storeFile.toFile());
node = DataStorageNode.readPossiblyEncryptedNode(fileNode);
} catch (JacksonException ex) {
ErrorEvent.fromThrowable(ex).omit().expected().handle();
node = DataStorageNode.fail();
}
var store = node.parseStore();
return Optional.of(new DataStoreEntry(
dir,
@@ -481,7 +489,7 @@ public class DataStoreEntry extends StorageElement {
var notesNode = JsonNodeFactory.instance.objectNode();
notesNode.put("markdown", notes);
var storageNode = DataStorageNode.encryptNodeIfNeeded(new DataStorageNode(
notesNode, storeNode.isPerUser(), storeNode.isAvailableForUser(), storeNode.isEncrypted()));
notesNode, storeNode.isPerUser(), storeNode.isReadableForUser(), storeNode.isEncrypted()));
var string = mapper.writeValueAsString(storageNode);
Files.writeString(encryptedNotesFile, string);
} else if (notes != null) {

View File

@@ -161,16 +161,6 @@ public class StandardStorage extends DataStorage {
}
storeEntries.put(entry.get(), entry.get());
} catch (JacksonException ex) {
// Data corruption and schema changes are expected
// We only keep invalid entries in developer mode as there's no point in keeping them in
// production.
if (AppPrefs.get().isDevelopmentEnvironment()) {
directoriesToKeep.add(path);
}
ErrorEvent.fromThrowable(ex).expected().omit().build().handle();
} catch (IOException ex) {
// IO exceptions are not expected
exception.set(new IOException("Unable to load data from " + path + ". Is it corrupted?", ex));

View File

@@ -238,7 +238,7 @@ public class AppJacksonModule extends SimpleModule {
var e = DataStorage.get()
.getStoreEntryIfPresent(id)
.filter(dataStoreEntry -> dataStoreEntry.getValidity() != DataStoreEntry.Validity.LOAD_FAILED
|| !dataStoreEntry.getStoreNode().isAvailableForUser())
|| !dataStoreEntry.getStoreNode().isReadableForUser())
.orElse(null);
if (e == null) {
return null;

View File

@@ -204,7 +204,7 @@ public class SshLocalBridge {
var exec = CommandSupport.findProgram(sc, "sshd");
if (exec.isEmpty()) {
throw ErrorEvent.expected(new IllegalStateException(
"No sshd executable found in PATH. The SSH terminal bridge requires a local ssh server"));
"No sshd executable found in PATH. The SSH terminal bridge requires a local ssh server to connect to an SSH terminal"));
}
return exec.get();
}

View File

@@ -65,7 +65,7 @@ public interface ShellControl extends ProcessControl {
ShellControl withSourceStore(DataStore store);
List<ShellInitCommand> getInitCommands();
List<ShellTerminalInitCommand> getInitCommands();
ParentSystemAccess getParentSystemAccess();
@@ -186,7 +186,7 @@ public interface ShellControl extends ProcessControl {
ShellControl elevated(ElevationFunction elevationFunction);
ShellControl withInitSnippet(ShellInitCommand snippet);
ShellControl withInitSnippet(ShellTerminalInitCommand snippet);
default ShellControl subShell(@NonNull ShellDialect type) {
var o = new ShellOpenFunction() {

View File

@@ -138,7 +138,7 @@ public interface ShellDialect {
String runScriptCommand(ShellControl parent, String file);
String sourceScriptCommand(ShellControl parent, String file);
String sourceScriptCommand(String file);
String executeCommandWithShell(String cmd);

View File

@@ -1,79 +0,0 @@
package io.xpipe.core.process;
import lombok.NonNull;
import java.util.Optional;
public interface ShellInitCommand {
default void runDumb(ShellControl shellControl) throws Exception {
throw new UnsupportedOperationException();
}
default Optional<String> terminalContent(ShellControl shellControl) throws Exception {
throw new UnsupportedOperationException();
}
default boolean runInDumb() {
return false;
}
boolean canPotentiallyRunInDialect(ShellDialect dialect);
default boolean runInTerminal() {
return false;
}
interface Terminal extends ShellInitCommand {
Optional<String> terminalContent(ShellControl shellControl);
default boolean runInTerminal() {
return true;
}
}
class Simple implements ShellInitCommand {
@NonNull
private final String content;
private final ShellDialect dialect;
private final boolean dumb;
private final boolean terminal;
public Simple(@NonNull String content, ShellDialect dialect, boolean dumb, boolean terminal) {
this.content = content;
this.dialect = dialect;
this.dumb = dumb;
this.terminal = terminal;
}
@Override
public void runDumb(ShellControl shellControl) throws Exception {
shellControl.executeSimpleCommand(content);
}
@Override
public Optional<String> terminalContent(ShellControl shellControl) {
return Optional.of(content);
}
@Override
public boolean runInDumb() {
return dumb;
}
@Override
public boolean canPotentiallyRunInDialect(ShellDialect dialect) {
return this.dialect == null || this.dialect.isCompatibleTo(dialect);
}
@Override
public boolean runInTerminal() {
return terminal;
}
}
}

View File

@@ -0,0 +1,40 @@
package io.xpipe.core.process;
import lombok.NonNull;
import java.util.Optional;
public interface ShellTerminalInitCommand {
boolean isStatic();
Optional<String> content(ShellControl sc);
boolean canPotentiallyRunInDialect(ShellDialect dialect);
class Static implements ShellTerminalInitCommand {
private final String content;
private final ShellDialect dialect;
public Static(String content, ShellDialect dialect) {
this.content = content;
this.dialect = dialect;
}
@Override
public boolean isStatic() {
return true;
}
@Override
public Optional<String> content(ShellControl sc) {
return Optional.of(content);
}
@Override
public boolean canPotentiallyRunInDialect(ShellDialect dialect) {
return this.dialect == null || this.dialect.isCompatibleTo(dialect);
}
}
}

View File

@@ -95,7 +95,7 @@ public class WrapperShellControl implements ShellControl {
}
@Override
public List<ShellInitCommand> getInitCommands() {
public List<ShellTerminalInitCommand> getInitCommands() {
return parent.getInitCommands();
}
@@ -334,7 +334,7 @@ public class WrapperShellControl implements ShellControl {
}
@Override
public ShellControl withInitSnippet(ShellInitCommand snippet) {
public ShellControl withInitSnippet(ShellTerminalInitCommand snippet) {
return parent.withInitSnippet(snippet);
}

View File

@@ -1,6 +1,8 @@
package io.xpipe.ext.base.identity;
import io.xpipe.app.core.AppProperties;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.storage.ContextualFileReference;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.SecretRetrievalStrategy;
@@ -176,7 +178,7 @@ public interface SshIdentityStrategy {
@Override
public boolean isConnectionAttemptCostly() {
return false;
return password.expectsQuery() && AppPrefs.get().dontCachePasswords().get();
}
public void checkComplete() throws ValidationException {

View File

@@ -8,13 +8,12 @@ import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.util.ShellTemp;
import io.xpipe.core.process.ShellControl;
import io.xpipe.core.process.ShellDialect;
import io.xpipe.core.process.ShellInitCommand;
import io.xpipe.core.process.ShellTerminalInitCommand;
import io.xpipe.core.store.FileNames;
import io.xpipe.core.store.FilePath;
import lombok.Value;
import java.util.*;
import java.util.stream.Collectors;
public class ScriptStoreSetup {
@@ -72,7 +71,7 @@ public class ScriptStoreSetup {
var source = pc.getSourceStoreId();
if (source.isPresent()) {
var useCached = pc.getSourceStore().map(dataStore -> dataStore instanceof ShellStore s && s.isConnectionAttemptCostly()).orElse(false);
var useCached = true || pc.getSourceStore().map(dataStore -> dataStore instanceof ShellStore s && s.isConnectionAttemptCostly()).orElse(false);
if (useCached) {
var cached = hasGeneratedScriptsCached(source.get(), initFlattened) && hasGeneratedScriptsCached(source.get(), bringFlattened);
if (cached) {
@@ -81,11 +80,22 @@ public class ScriptStoreSetup {
}
}
initFlattened.forEach(simpleScriptStore -> {
pc.withInitSnippet(simpleScriptStore.getStore());
initFlattened.forEach(s -> {
pc.withInitSnippet(new ShellTerminalInitCommand.Static(s.getStore().))
pc.withInitSnippet(new ShellTerminalInitCommand() {
@Override
public Optional<String> terminalContent(ShellControl shellControl) {
return Optional.ofNullable(s.getStore().assembleScriptChain(shellControl));
}
@Override
public boolean canPotentiallyRunInDialect(ShellDialect dialect) {
return s.getStore().getMinimumDialect().isCompatibleTo(dialect);
}
});
});
if (!bringFlattened.isEmpty() || source.isPresent()) {
pc.withInitSnippet(new ShellInitCommand() {
pc.withInitSnippet(new ShellTerminalInitCommand.Terminal() {
String dir;
@@ -116,11 +126,6 @@ public class ScriptStoreSetup {
public boolean canPotentiallyRunInDialect(ShellDialect dialect) {
return true;
}
@Override
public boolean runInTerminal() {
return true;
}
});
}
return pc;

View File

@@ -6,7 +6,6 @@ import io.xpipe.app.util.ScriptHelper;
import io.xpipe.app.util.Validators;
import io.xpipe.core.process.ShellControl;
import io.xpipe.core.process.ShellDialect;
import io.xpipe.core.process.ShellInitCommand;
import io.xpipe.core.util.ValidationException;
import io.xpipe.ext.base.SelfReferentialStore;
@@ -20,7 +19,6 @@ import lombok.extern.jackson.Jacksonized;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
@SuperBuilder(toBuilder = true)
@@ -29,7 +27,7 @@ import java.util.stream.Collectors;
@JsonTypeName("script")
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SimpleScriptStore extends ScriptStore implements ShellInitCommand.Terminal, SelfReferentialStore {
public class SimpleScriptStore extends ScriptStore implements SelfReferentialStore {
ShellDialect minimumDialect;
String commands;
@@ -62,7 +60,7 @@ public class SimpleScriptStore extends ScriptStore implements ShellInitCommand.T
shellControl.getShellDialect().getNewLine().getNewLineString()));
var targetType = shellControl.getOriginalShellDialect();
var script = ScriptHelper.createExecScript(targetType, shellControl, fixedCommands);
return targetType.sourceScriptCommand(shellControl, script.toString());
return targetType.sourceScriptCommand(script.toString());
}
return null;
@@ -115,14 +113,4 @@ public class SimpleScriptStore extends ScriptStore implements ShellInitCommand.T
.toList()
: List.of();
}
@Override
public Optional<String> terminalContent(ShellControl shellControl) {
return Optional.ofNullable(assembleScriptChain(shellControl));
}
@Override
public boolean canPotentiallyRunInDialect(ShellDialect dialect) {
return this.minimumDialect.isCompatibleTo(dialect);
}
}