This commit is contained in:
crschnick
2025-06-02 22:33:38 +00:00
parent 6018a7bf76
commit 633b98f119
9 changed files with 146 additions and 33 deletions
@@ -1,6 +1,10 @@
package io.xpipe.app.ext;
import io.xpipe.app.browser.BrowserAbstractSessionModel;
import io.xpipe.app.browser.BrowserFullSessionModel;
import io.xpipe.app.browser.BrowserStoreSessionTab;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.vnc.VncBaseStore;
import io.xpipe.core.process.*;
import io.xpipe.core.store.DataStore;
@@ -16,6 +20,7 @@ public abstract class ProcessControlProvider {
.findFirst()
.orElseThrow();
}
public abstract BrowserStoreSessionTab<?> createVncSession(BrowserFullSessionModel model, DataStoreEntryRef<VncBaseStore> ref);
public static ProcessControlProvider get() {
return INSTANCE;
@@ -17,6 +17,9 @@ import io.xpipe.app.update.AppDistributionType;
import io.xpipe.app.util.OptionsBuilder;
import io.xpipe.app.util.PlatformState;
import io.xpipe.app.util.PlatformThread;
import io.xpipe.app.vnc.ExternalVncClient;
import io.xpipe.app.vnc.InternalVncClient;
import io.xpipe.app.vnc.VncCategory;
import io.xpipe.core.process.ShellScript;
import javafx.beans.property.*;
@@ -101,6 +104,11 @@ public class AppPrefs {
mapVaultShared(new SimpleObjectProperty<>(false), "alwaysConfirmElevation", Boolean.class, false);
public final BooleanProperty dontCachePasswords =
mapVaultShared(new SimpleBooleanProperty(false), "dontCachePasswords", Boolean.class, false);
public final Property<ExternalVncClient> vncClient = map(Mapping.builder()
.property(new SimpleObjectProperty<>(InternalVncClient.builder().build()))
.key("vncClient")
.valueClass(ExternalVncClient.class)
.build());
final Property<PasswordManager> passwordManager = map(Mapping.builder()
.property(new SimpleObjectProperty<>())
.key("passwordManager")
@@ -141,8 +149,6 @@ public class AppPrefs {
return terminalAlwaysPauseOnExit;
}
public final StringProperty passwordManagerCommand =
mapLocal(new SimpleStringProperty(null), "passwordManagerCommand", String.class, false);
final ObjectProperty<StartupBehaviour> startupBehaviour = mapLocal(
new SimpleObjectProperty<>(StartupBehaviour.GUI), "startupBehaviour", StartupBehaviour.class, true);
public final BooleanProperty enableGitStorage =
@@ -305,6 +311,7 @@ public class AppPrefs {
new LoggingCategory(),
new EditorCategory(),
new RdpCategory(),
new VncCategory(),
new SshCategory(),
new ConnectionHubCategory(),
new FileBrowserCategory(),
@@ -589,15 +596,6 @@ public class AppPrefs {
var f = PlatformState.determineDefaultScalingFactor();
uiScale.setValue(f.isPresent() ? f.getAsInt() : null);
}
// Migrate legacy password manager
if (passwordManagerCommand.get() != null
&& !passwordManagerCommand.get().isBlank()
&& passwordManager.getValue() == null) {
passwordManager.setValue(PasswordManagerCommand.builder()
.script(new ShellScript(passwordManagerCommand.get()))
.build());
}
}
private void loadSharedRemote() {
@@ -6,6 +6,7 @@ import io.xpipe.app.storage.*;
import io.xpipe.app.terminal.ExternalTerminalType;
import io.xpipe.app.terminal.TerminalMultiplexer;
import io.xpipe.app.terminal.TerminalPrompt;
import io.xpipe.app.vnc.ExternalVncClient;
import io.xpipe.core.util.InPlaceSecretValue;
import io.xpipe.core.util.JacksonMapper;
@@ -47,6 +48,7 @@ public class AppJacksonModule extends SimpleModule {
context.registerSubtypes(PasswordManager.getClasses());
context.registerSubtypes(TerminalMultiplexer.getClasses());
context.registerSubtypes(TerminalPrompt.getClasses());
context.registerSubtypes(ExternalVncClient.getClasses());
context.addSerializers(_serializers);
context.addDeserializers(_deserializers);
@@ -2,7 +2,10 @@ package io.xpipe.app.vnc;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.xpipe.app.pwman.*;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.core.process.OsType;
import io.xpipe.core.process.ShellControl;
import io.xpipe.core.util.SecretValue;
import lombok.Value;
@@ -14,20 +17,8 @@ public interface ExternalVncClient {
static List<Class<?>> getClasses() {
var l = new ArrayList<Class<?>>();
l.add(OnePasswordManager.class);
l.add(KeePassXcPasswordManager.class);
l.add(BitwardenPasswordManager.class);
l.add(DashlanePasswordManager.class);
if (OsType.getLocal() != OsType.WINDOWS) {
l.add(LastpassPasswordManager.class);
l.add(EnpassPasswordManager.class);
}
l.add(KeeperPasswordManager.class);
l.add(PsonoPasswordManager.class);
if (OsType.getLocal() == OsType.WINDOWS) {
l.add(WindowsCredentialManager.class);
}
l.add(PasswordManagerCommand.class);
l.add(InternalVncClient.class);
l.add(TightVncClient.class);
return l;
}
@@ -36,11 +27,10 @@ public interface ExternalVncClient {
String title;
String host;
int port;
SecretValue password;
DataStoreEntryRef<VncBaseStore> entry;
}
boolean isAvailable();
void launch(LaunchConfiguration configuration) throws Exception;
}
@@ -0,0 +1,44 @@
package io.xpipe.app.vnc;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.app.browser.BrowserFullSessionModel;
import io.xpipe.app.browser.BrowserStoreSessionTab;
import io.xpipe.app.core.AppLayoutModel;
import io.xpipe.app.ext.ProcessControlProvider;
import io.xpipe.app.prefs.ExternalApplicationType;
import io.xpipe.app.util.LocalShell;
import io.xpipe.core.process.CommandBuilder;
import lombok.Builder;
import lombok.extern.jackson.Jacksonized;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
@Builder
@Jacksonized
@JsonTypeName("xpipe")
public class InternalVncClient implements ExternalVncClient {
@Override
public boolean isAvailable() {
return true;
}
@Override
public void launch(LaunchConfiguration configuration) throws Exception {
var open = BrowserFullSessionModel.DEFAULT.getSessionEntriesSnapshot().stream()
.filter(browserSessionTab -> browserSessionTab instanceof BrowserStoreSessionTab<?> st
&& st.getEntry().get().equals(configuration.getEntry().get()))
.findFirst()
.orElse(null);
if (open != null) {
AppLayoutModel.get().selectBrowser();
BrowserFullSessionModel.DEFAULT.getSelectedEntry().setValue(open);
return;
}
BrowserFullSessionModel.DEFAULT.openSync(ProcessControlProvider.get().createVncSession(BrowserFullSessionModel.DEFAULT, configuration.getEntry()), null);
AppLayoutModel.get().selectBrowser();
}
}
@@ -21,6 +21,11 @@ public class TightVncClient implements ExternalApplicationType.WindowsType, Exte
return true;
}
@Override
public String getId() {
return null;
}
@Override
public String getExecutable() {
return "tvnviewer.exe";
@@ -36,12 +41,7 @@ public class TightVncClient implements ExternalApplicationType.WindowsType, Exte
@Override
public void launch(LaunchConfiguration configuration) throws Exception {
var command = CommandBuilder.of().addFile(findExecutable()).add("-host").addLiteral(configuration.getHost()).add("-port").add("" + configuration.getPort());
var command = CommandBuilder.of().addFile(findExecutable()).addQuoted(configuration.getHost() + "::" + configuration.getPort());
LocalShell.getShell().command(command).execute();
}
@Override
public String getId() {
return "";
}
}
@@ -0,0 +1,18 @@
package io.xpipe.app.vnc;
import io.xpipe.app.util.SecretRetrievalStrategy;
import io.xpipe.core.process.ShellControl;
import io.xpipe.core.store.DataStore;
import java.util.Optional;
public interface VncBaseStore extends DataStore {
String getEffectiveHost();
int getEffectivePort();
String getUser();
SecretRetrievalStrategy getPassword();
}
@@ -0,0 +1,55 @@
package io.xpipe.app.vnc;
import io.xpipe.app.comp.Comp;
import io.xpipe.app.comp.base.ButtonComp;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.prefs.AppPrefsCategory;
import io.xpipe.app.pwman.PasswordManager;
import io.xpipe.app.util.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import org.kordamp.ikonli.javafx.FontIcon;
public class VncCategory extends AppPrefsCategory {
@Override
protected String getId() {
return "vnc";
}
@Override
protected Comp<?> create() {
var prefs = AppPrefs.get();
var choiceBuilder = OptionsChoiceBuilder.builder()
.property(prefs.vncClient)
.subclasses(ExternalVncClient.getClasses())
.allowNull(false)
.transformer(entryComboBox -> {
var docsLinkButton = new ButtonComp(
AppI18n.observable("docs"), new FontIcon("mdi2h-help-circle-outline"), () -> {
Hyperlinks.open(DocumentationLink.PASSWORD_MANAGER.getLink());
});
docsLinkButton.minWidth(Region.USE_PREF_SIZE);
var hbox = new HBox(entryComboBox, docsLinkButton.createRegion());
HBox.setHgrow(entryComboBox, Priority.ALWAYS);
hbox.setSpacing(10);
hbox.setMaxWidth(getCompWidth());
return hbox;
})
.build();
prefs.vncClient.addListener((observable, oldValue, newValue) -> {
int a = 0;
});
var choice = choiceBuilder.build().buildComp();
return new OptionsBuilder()
.addTitle("vncClient")
.sub(new OptionsBuilder()
.pref(prefs.vncClient)
.addComp(choice)
)
.buildComp();
}
}
+1
View File
@@ -42,6 +42,7 @@ open module io.xpipe.app {
exports io.xpipe.app.icon;
exports io.xpipe.app.pwman;
exports io.xpipe.app.rdp;
exports io.xpipe.app.vnc;
requires com.sun.jna;
requires com.sun.jna.platform;