mirror of
https://github.com/xpipe-io/xpipe.git
synced 2026-09-27 02:45:36 +00:00
Merge branch 'identity-sync' into 23-release
This commit is contained in:
@@ -5,6 +5,7 @@ import io.xpipe.app.comp.BaseRegionBuilder;
|
||||
import io.xpipe.app.comp.RegionBuilder;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.core.AppImages;
|
||||
import io.xpipe.app.hub.comp.StoreCreationModel;
|
||||
import io.xpipe.app.hub.comp.StoreEntryComp;
|
||||
import io.xpipe.app.hub.comp.StoreEntryWrapper;
|
||||
import io.xpipe.app.hub.comp.StoreSection;
|
||||
@@ -160,7 +161,7 @@ public interface DataStoreProvider {
|
||||
return null;
|
||||
}
|
||||
|
||||
default GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
|
||||
default GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ public class StoreCreationComp extends ModalOverlayContentComp {
|
||||
var activeDialog = new SimpleObjectProperty<GuiDialog>();
|
||||
model.getProvider().subscribe(n -> {
|
||||
if (n != null) {
|
||||
var d = n.guiDialog(model.getExistingEntry(), model.getStore());
|
||||
var d = n.guiDialog(model, model.getStore());
|
||||
activeDialog.set(d);
|
||||
if (d == null) {
|
||||
return;
|
||||
|
||||
@@ -31,11 +31,11 @@ public class StoreCreationDialog {
|
||||
}
|
||||
|
||||
public static StoreCreationModel showEdit(DataStoreEntry e, Consumer<DataStoreEntry> c) {
|
||||
return showEdit(e, e.getStore(), true, c);
|
||||
return showEdit(e, e.getStore(), true, true, c);
|
||||
}
|
||||
|
||||
public static StoreCreationModel showEdit(
|
||||
DataStoreEntry e, DataStore base, boolean addToStorage, Consumer<DataStoreEntry> c) {
|
||||
DataStoreEntry e, DataStore base, boolean addToStorage, boolean switchCategory, Consumer<DataStoreEntry> c) {
|
||||
StoreCreationConsumer consumer = (newE, validated) -> {
|
||||
ThreadHelper.runAsync(() -> {
|
||||
if (!addToStorage) {
|
||||
@@ -66,14 +66,13 @@ public class StoreCreationDialog {
|
||||
}
|
||||
}
|
||||
|
||||
// Select new category if needed
|
||||
var cat = DataStorage.get()
|
||||
.getStoreCategoryIfPresent(e.getCategoryUuid())
|
||||
.orElseThrow();
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
StoreViewState.get()
|
||||
.selectCategoryIntoViewIfNeeded(StoreViewState.get().getCategoryWrapper(cat));
|
||||
});
|
||||
if (switchCategory) {
|
||||
// Select new category if needed
|
||||
var cat = DataStorage.get().getStoreCategoryIfPresent(e.getCategoryUuid()).orElseThrow();
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
StoreViewState.get().selectCategoryIntoViewIfNeeded(StoreViewState.get().getCategoryWrapper(cat));
|
||||
});
|
||||
}
|
||||
|
||||
c.accept(e);
|
||||
});
|
||||
|
||||
@@ -46,6 +46,7 @@ public class StoreCreationModel {
|
||||
DataStoreEntry existingEntry;
|
||||
boolean staticDisplay;
|
||||
StoreCreationConsumer consumer;
|
||||
ObservableBooleanValue syncable;
|
||||
|
||||
public StoreCreationModel(
|
||||
Property<DataStoreProvider> provider,
|
||||
@@ -67,7 +68,7 @@ public class StoreCreationModel {
|
||||
store.unbind();
|
||||
store.setValue(null);
|
||||
if (n != null) {
|
||||
store.setValue(n.defaultStore(getTargetCategory(existingEntry)));
|
||||
store.setValue(n.defaultStore(getTargetCategory(existingEntry != null ? existingEntry.getCategoryUuid() : null)));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -82,6 +83,7 @@ public class StoreCreationModel {
|
||||
newValue.validate();
|
||||
});
|
||||
});
|
||||
|
||||
this.entry = Bindings.createObjectBinding(
|
||||
() -> {
|
||||
if (name.getValue() == null
|
||||
@@ -98,13 +100,21 @@ public class StoreCreationModel {
|
||||
var entryRef = existingEntry != null
|
||||
? existingEntry
|
||||
: DataStorage.get().getDefaultDisplayParent(initial).orElse(initial);
|
||||
var targetCategory = getTargetCategory(entryRef);
|
||||
var targetCategory = getTargetCategory(entryRef.getCategoryUuid());
|
||||
return DataStoreEntry.createNew(
|
||||
UUID.randomUUID(), targetCategory.getUuid(), name.getValue(), store.getValue());
|
||||
},
|
||||
name,
|
||||
store);
|
||||
|
||||
this.syncable = Bindings.createBooleanBinding(() -> {
|
||||
var targetCategory = getTargetCategory(existingEntry != null ? existingEntry.getCategoryUuid() :
|
||||
StoreViewState.get().getActiveCategory().getValue().getCategory().getUuid());
|
||||
var entry = DataStoreEntry.createNew(
|
||||
UUID.randomUUID(), targetCategory.getUuid(), "Temp", store.getValue());
|
||||
return DataStorage.get().shouldSync(entry);
|
||||
}, store, StoreViewState.get().getActiveCategory());
|
||||
|
||||
skippable.bind(Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
if (entry.getValue() != null
|
||||
@@ -124,9 +134,9 @@ public class StoreCreationModel {
|
||||
name));
|
||||
}
|
||||
|
||||
private DataStoreCategory getTargetCategory(DataStoreEntry base) {
|
||||
var targetCategory = base != null
|
||||
? base.getCategoryUuid()
|
||||
private DataStoreCategory getTargetCategory(UUID baseCategory) {
|
||||
var targetCategory = baseCategory != null
|
||||
? baseCategory
|
||||
: DataStorage.get().getSelectedCategory().getUuid();
|
||||
var rootCategory = DataStorage.get()
|
||||
.getRootCategory(DataStorage.get()
|
||||
|
||||
@@ -81,7 +81,7 @@ public class StoreQuickConnect {
|
||||
return true;
|
||||
}
|
||||
|
||||
StoreCreationDialog.showEdit(quickConnectEntry, newStore, false, finished -> {
|
||||
StoreCreationDialog.showEdit(quickConnectEntry, newStore, false, true, finished -> {
|
||||
update(finished.getStore());
|
||||
ThreadHelper.runAsync(() -> {
|
||||
try {
|
||||
|
||||
+3
-7
@@ -3,10 +3,7 @@ package io.xpipe.ext.base.desktop;
|
||||
import io.xpipe.app.browser.BrowserFullSessionModel;
|
||||
import io.xpipe.app.comp.BaseRegionBuilder;
|
||||
import io.xpipe.app.ext.*;
|
||||
import io.xpipe.app.hub.comp.StoreChoiceComp;
|
||||
import io.xpipe.app.hub.comp.StoreEntryWrapper;
|
||||
import io.xpipe.app.hub.comp.StoreViewState;
|
||||
import io.xpipe.app.hub.comp.SystemStateComp;
|
||||
import io.xpipe.app.hub.comp.*;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.storage.DataStoreCategory;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
@@ -63,7 +60,7 @@ public class DesktopApplicationStoreProvider implements DataStoreProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
|
||||
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
|
||||
DesktopApplicationStore st = (DesktopApplicationStore) store.getValue();
|
||||
var host = new SimpleObjectProperty<>(st.getDesktop());
|
||||
var path = new SimpleStringProperty(st.getPath());
|
||||
@@ -71,8 +68,7 @@ public class DesktopApplicationStoreProvider implements DataStoreProvider {
|
||||
return new OptionsBuilder()
|
||||
.nameAndDescription("desktopBase")
|
||||
.addComp(
|
||||
new StoreChoiceComp<>(
|
||||
entry,
|
||||
new StoreChoiceComp<>(model.getExistingEntry(),
|
||||
host,
|
||||
DesktopBaseStore.class,
|
||||
desktopStoreDataStoreEntryRef ->
|
||||
|
||||
@@ -6,7 +6,6 @@ import io.xpipe.app.ext.*;
|
||||
import io.xpipe.app.hub.comp.*;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.storage.DataStoreCategory;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.util.DocumentationLink;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
@@ -69,7 +68,7 @@ public class AbstractHostStoreProvider implements DataStoreProvider {
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
|
||||
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
|
||||
AbstractHostStore st = store.getValue().asNeeded();
|
||||
|
||||
var host = new SimpleObjectProperty<>(st.getHost());
|
||||
@@ -81,8 +80,7 @@ public class AbstractHostStoreProvider implements DataStoreProvider {
|
||||
.nonNull()
|
||||
.nameAndDescription("abstractHostGateway")
|
||||
.addComp(
|
||||
new StoreChoiceComp<>(
|
||||
entry,
|
||||
new StoreChoiceComp<>(model.getExistingEntry(),
|
||||
gateway,
|
||||
NetworkTunnelStore.class,
|
||||
null,
|
||||
|
||||
@@ -3,7 +3,10 @@ package io.xpipe.ext.base.identity;
|
||||
import io.xpipe.app.comp.RegionBuilder;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.comp.base.InputGroupComp;
|
||||
import io.xpipe.app.comp.base.ModalButton;
|
||||
import io.xpipe.app.comp.base.ModalOverlay;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.core.window.AppDialog;
|
||||
import io.xpipe.app.cred.SshIdentityStrategy;
|
||||
import io.xpipe.app.cred.SshIdentityStrategyChoiceConfig;
|
||||
import io.xpipe.app.ext.ProcessControlProvider;
|
||||
@@ -11,16 +14,20 @@ import io.xpipe.app.ext.ShellStore;
|
||||
import io.xpipe.app.platform.LabelGraphic;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.platform.OptionsChoiceBuilder;
|
||||
import io.xpipe.app.platform.PlatformThread;
|
||||
import io.xpipe.app.secret.EncryptedValue;
|
||||
import io.xpipe.app.secret.SecretRetrievalStrategy;
|
||||
import io.xpipe.app.secret.SecretStrategyChoiceConfig;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStorageUserHandler;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.app.util.*;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.beans.value.ObservableBooleanValue;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -35,6 +42,7 @@ import java.util.List;
|
||||
public class IdentityChoiceBuilder {
|
||||
|
||||
ObjectProperty<IdentityValue> identity;
|
||||
ObservableBooleanValue syncedBase;
|
||||
boolean allowCustomUserInput;
|
||||
boolean requireUserInput;
|
||||
boolean requirePassword;
|
||||
@@ -45,7 +53,7 @@ public class IdentityChoiceBuilder {
|
||||
ObservableValue<DataStoreEntryRef<ShellStore>> fileSystem;
|
||||
|
||||
public IdentityChoiceBuilder(
|
||||
ObjectProperty<IdentityValue> identity,
|
||||
ObjectProperty<IdentityValue> identity, ObservableBooleanValue syncedBase,
|
||||
boolean allowCustomUserInput,
|
||||
boolean requireUserInput,
|
||||
boolean requirePassword,
|
||||
@@ -53,6 +61,7 @@ public class IdentityChoiceBuilder {
|
||||
boolean requireKeyInput,
|
||||
String userChoiceTranslationKey,
|
||||
String passwordChoiceTranslationKey) {
|
||||
this.syncedBase = syncedBase;
|
||||
this.identity = identity;
|
||||
this.allowCustomUserInput = allowCustomUserInput;
|
||||
this.requireUserInput = requireUserInput;
|
||||
@@ -62,17 +71,46 @@ public class IdentityChoiceBuilder {
|
||||
this.userChoiceTranslationKey = userChoiceTranslationKey;
|
||||
this.passwordChoiceTranslationKey = new ReadOnlyStringWrapper(passwordChoiceTranslationKey);
|
||||
this.fileSystem = new ReadOnlyObjectWrapper<>(DataStorage.get().local().ref());
|
||||
|
||||
addSyncCheckListener();
|
||||
}
|
||||
|
||||
public static OptionsBuilder ssh(ObjectProperty<IdentityValue> identity, boolean requireUser) {
|
||||
var i = new IdentityChoiceBuilder(
|
||||
identity, true, requireUser, true, true, true, "identityChoice", "passwordAuthentication");
|
||||
private void addSyncCheckListener() {
|
||||
identity.addListener((observable, oldValue, newValue) -> {
|
||||
if (DataStorage.get().supportsSync() && syncedBase.getValue() && newValue instanceof IdentityValue.Ref r && r.unwrap() instanceof LocalIdentityStore) {
|
||||
var modal = ModalOverlay.of("unsyncedIdentityTitle", AppDialog.dialogTextKey("unsyncedIdentityContent").prefWidth(600));
|
||||
modal.addButton(new ModalButton("documentation", () -> {
|
||||
DocumentationLink.IDENTITIES.open();
|
||||
}, false, false));
|
||||
modal.addButtonBarComp(RegionBuilder.hspacer());
|
||||
modal.addButton(new ModalButton("syncIdentity", () -> {
|
||||
IdentityConvert.syncLocal(r.getRef().asNeeded(), false, updated -> {
|
||||
Platform.runLater(() -> {
|
||||
identity.set(null);
|
||||
identity.set(IdentityValue.Ref.builder().ref(updated.asNeeded()).build());
|
||||
});
|
||||
});
|
||||
}, true, false));
|
||||
modal.addButton(new ModalButton("convertToMulti", () -> {
|
||||
IdentityConvert.createMulti(r, true, created -> {
|
||||
Platform.runLater(() -> {
|
||||
identity.set(IdentityValue.Ref.builder().ref(created.asNeeded()).build());
|
||||
});
|
||||
});
|
||||
}, true, false));
|
||||
modal.addButton(new ModalButton("ignore", null, true, false));
|
||||
modal.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static OptionsBuilder ssh(ObjectProperty<IdentityValue> identity, ObservableBooleanValue syncedBase, boolean requireUser) {
|
||||
var i = new IdentityChoiceBuilder(identity, syncedBase, true, requireUser, true, true, true, "identityChoice", "passwordAuthentication");
|
||||
return i.build();
|
||||
}
|
||||
|
||||
public static OptionsBuilder container(ObjectProperty<IdentityValue> identity) {
|
||||
var i = new IdentityChoiceBuilder(
|
||||
identity, true, false, false, false, false, "customUsername", "customUsernamePassword");
|
||||
public static OptionsBuilder container(ObjectProperty<IdentityValue> identity, ObservableBooleanValue syncedBase) {
|
||||
var i = new IdentityChoiceBuilder(identity, syncedBase, true, false, false, false, false, "customUsername", "customUsernamePassword");
|
||||
return i.build();
|
||||
}
|
||||
|
||||
@@ -98,23 +136,27 @@ public class IdentityChoiceBuilder {
|
||||
}
|
||||
|
||||
public OptionsBuilder build() {
|
||||
var existing = identity.getValue();
|
||||
var user = new SimpleStringProperty(
|
||||
existing instanceof IdentityValue.InPlace inPlace && inPlace.unwrap() != null
|
||||
? inPlace.unwrap().getUsername().get()
|
||||
: null);
|
||||
var pass = new SimpleObjectProperty<>(
|
||||
existing instanceof IdentityValue.InPlace inPlace && inPlace.unwrap() != null
|
||||
? inPlace.unwrap().getPassword()
|
||||
: null);
|
||||
var identityStrategy = new SimpleObjectProperty<>(
|
||||
existing instanceof IdentityValue.InPlace inPlace && inPlace.unwrap() != null
|
||||
? inPlace.unwrap().getSshIdentity()
|
||||
: null);
|
||||
var ref = new SimpleObjectProperty<>(existing instanceof IdentityValue.Ref r ? r.getRef() : null);
|
||||
var user = new SimpleStringProperty();
|
||||
var pass = new SimpleObjectProperty<SecretRetrievalStrategy>();
|
||||
var identityStrategy = new SimpleObjectProperty<SshIdentityStrategy>();
|
||||
var ref = new SimpleObjectProperty<DataStoreEntryRef<IdentityStore>>();
|
||||
var inPlaceSelected = ref.isNull();
|
||||
var refSelected = ref.isNotNull();
|
||||
|
||||
identity.subscribe(existing -> {
|
||||
user.set(existing instanceof IdentityValue.InPlace inPlace && inPlace.unwrap() != null
|
||||
? inPlace.unwrap().getUsername().get()
|
||||
: null);
|
||||
pass.set(existing instanceof IdentityValue.InPlace inPlace && inPlace.unwrap() != null
|
||||
? inPlace.unwrap().getPassword()
|
||||
: null);
|
||||
identityStrategy.set(
|
||||
existing instanceof IdentityValue.InPlace inPlace && inPlace.unwrap() != null
|
||||
? inPlace.unwrap().getSshIdentity()
|
||||
: null);
|
||||
ref.set(existing instanceof IdentityValue.Ref r ? r.getRef() : null);
|
||||
});
|
||||
|
||||
var passwordChoice = OptionsChoiceBuilder.builder()
|
||||
.allowNull(false)
|
||||
.property(pass)
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package io.xpipe.ext.base.identity;
|
||||
|
||||
import io.xpipe.app.core.window.AppMainWindow;
|
||||
import io.xpipe.app.ext.DataStoreCreationCategory;
|
||||
import io.xpipe.app.hub.comp.StoreCreationDialog;
|
||||
import io.xpipe.app.secret.EncryptedValue;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreCategory;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.Button;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class IdentityConvert {
|
||||
|
||||
public static void syncLocal(DataStoreEntryRef<LocalIdentityStore> ref, boolean selectCategory, Consumer<DataStoreEntryRef<SyncedIdentityStore>> consumer) {
|
||||
var st = ref.getStore();
|
||||
var synced = SyncedIdentityStore.builder()
|
||||
.username(st.getUsername().get())
|
||||
.password(EncryptedValue.VaultKey.of(st.getPassword()))
|
||||
.sshIdentity(EncryptedValue.VaultKey.of(st.getSshIdentity()))
|
||||
.perUser(false)
|
||||
.build();
|
||||
StoreCreationDialog.showEdit(ref.get(), synced, true, selectCategory, updated -> {
|
||||
if (updated.getStore() instanceof SyncedIdentityStore) {
|
||||
consumer.accept(updated.ref());
|
||||
}
|
||||
});
|
||||
|
||||
// Ugly solution to sync key file if needed
|
||||
Platform.runLater(() -> {
|
||||
var found = AppMainWindow.get().getStage().getScene().getRoot().lookupAll(".git-sync-file-button");
|
||||
if (found.size() != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var first = found.iterator().next();
|
||||
if (first instanceof Button b) {
|
||||
b.fire();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void createMulti(IdentityValue val, boolean moveToSync, Consumer<DataStoreEntryRef<MultiIdentityStore>> consumer) {
|
||||
if (!(val instanceof IdentityValue.Ref ref)) {
|
||||
throw new IllegalArgumentException("Not a identity reference");
|
||||
}
|
||||
|
||||
var synced = MultiIdentityStore.builder()
|
||||
.identities(List.of(ref.getRef().get().getUuid()))
|
||||
.perUser(val.isPerUser())
|
||||
.build();
|
||||
StoreCreationDialog.showCreation(ref.getRef().get().getName() + "-multi", synced, DataStoreCreationCategory.IDENTITY, created -> {
|
||||
if (created.getStore() instanceof MultiIdentityStore) {
|
||||
if (moveToSync) {
|
||||
var cat = DataStorage.get().getStoreCategoryIfPresent(created.getCategoryUuid()).orElseThrow();
|
||||
var inSynced = DataStorage.get().getCategoryParentHierarchy(cat).stream().anyMatch(
|
||||
dataStoreCategory -> dataStoreCategory.getUuid().equals(DataStorage.SYNCED_IDENTITIES_CATEGORY_UUID));
|
||||
var targetCategory = DataStorage.get().getStoreCategoryIfPresent(
|
||||
inSynced ? created.getCategoryUuid() : DataStorage.SYNCED_IDENTITIES_CATEGORY_UUID).orElseThrow();
|
||||
DataStorage.get().moveEntryToCategory(created, targetCategory);
|
||||
}
|
||||
consumer.accept(created.ref());
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
@@ -200,16 +200,24 @@ public class IdentitySelectComp extends RegionBuilder<HBox> {
|
||||
}
|
||||
}
|
||||
|
||||
Runnable updateMap = () -> {
|
||||
map.clear();
|
||||
for (DataStoreEntry storeEntry : DataStorage.get().getStoreEntries()) {
|
||||
if (storeEntry.getValidity().isUsable() && storeEntry.getStore() instanceof IdentityStore) {
|
||||
map.put(formatName(storeEntry), storeEntry.ref());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
StoreViewState.get().getAllEntries().getList().addListener((ListChangeListener<? super StoreEntryWrapper>)
|
||||
c -> {
|
||||
map.clear();
|
||||
for (DataStoreEntry storeEntry : DataStorage.get().getStoreEntries()) {
|
||||
if (storeEntry.getValidity().isUsable() && storeEntry.getStore() instanceof IdentityStore) {
|
||||
map.put(formatName(storeEntry), storeEntry.ref());
|
||||
}
|
||||
}
|
||||
updateMap.run();
|
||||
});
|
||||
|
||||
selectedReference.addListener((observable, oldValue, newValue) -> {
|
||||
updateMap.run();
|
||||
});
|
||||
|
||||
var prop = new SimpleStringProperty();
|
||||
if (inPlaceUser.getValue() != null) {
|
||||
prop.setValue(inPlaceUser.getValue());
|
||||
|
||||
+1
-21
@@ -67,27 +67,7 @@ public class LocalIdentityConvertHubLeafProvider implements HubLeafProvider<Loca
|
||||
|
||||
@Override
|
||||
public void executeImpl() {
|
||||
var st = ref.getStore();
|
||||
var synced = SyncedIdentityStore.builder()
|
||||
.username(st.getUsername().get())
|
||||
.password(EncryptedValue.VaultKey.of(st.getPassword()))
|
||||
.sshIdentity(EncryptedValue.VaultKey.of(st.getSshIdentity()))
|
||||
.perUser(false)
|
||||
.build();
|
||||
StoreCreationDialog.showEdit(ref.get(), synced, true, ignored -> {});
|
||||
|
||||
// Ugly solution to sync key file if needed
|
||||
Platform.runLater(() -> {
|
||||
var found = AppMainWindow.get().getStage().getScene().getRoot().lookupAll(".git-sync-file-button");
|
||||
if (found.size() != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var first = found.iterator().next();
|
||||
if (first instanceof Button b) {
|
||||
b.fire();
|
||||
}
|
||||
});
|
||||
IdentityConvert.syncLocal(ref, true, ignored -> {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.xpipe.app.cred.NoIdentityStrategy;
|
||||
import io.xpipe.app.cred.SshIdentityStrategyChoiceConfig;
|
||||
import io.xpipe.app.ext.DataStore;
|
||||
import io.xpipe.app.ext.GuiDialog;
|
||||
import io.xpipe.app.hub.comp.StoreCreationModel;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.platform.OptionsChoiceBuilder;
|
||||
import io.xpipe.app.secret.EncryptedValue;
|
||||
@@ -32,7 +33,7 @@ public class LocalIdentityStoreProvider extends IdentityStoreProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
|
||||
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
|
||||
LocalIdentityStore st = (LocalIdentityStore) store.getValue();
|
||||
|
||||
var user = new SimpleStringProperty(st.getUsername().get());
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package io.xpipe.ext.base.identity;
|
||||
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.core.AppInstallation;
|
||||
import io.xpipe.app.ext.DataStore;
|
||||
import io.xpipe.app.ext.GuiDialog;
|
||||
import io.xpipe.app.hub.comp.StoreCreationModel;
|
||||
import io.xpipe.app.hub.comp.StoreListChoiceComp;
|
||||
import io.xpipe.app.hub.comp.StoreViewState;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
@@ -15,14 +15,13 @@ import javafx.beans.property.SimpleListProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class MultiIdentityStoreProvider extends IdentityStoreProvider {
|
||||
|
||||
@Override
|
||||
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
|
||||
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
|
||||
MultiIdentityStore st = (MultiIdentityStore) store.getValue();
|
||||
|
||||
var initialAvailableIdentities = st.getAvailableIdentities();
|
||||
|
||||
+2
-2
@@ -5,6 +5,7 @@ import io.xpipe.app.cred.PasswordManagerAgentStrategy;
|
||||
import io.xpipe.app.cred.SshIdentityStrategyChoiceConfig;
|
||||
import io.xpipe.app.ext.DataStore;
|
||||
import io.xpipe.app.ext.GuiDialog;
|
||||
import io.xpipe.app.hub.comp.StoreCreationModel;
|
||||
import io.xpipe.app.hub.comp.StoreEntryWrapper;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.platform.OptionsChoiceBuilder;
|
||||
@@ -13,7 +14,6 @@ import io.xpipe.app.prefs.PasswordManagerTestComp;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStorageUserHandler;
|
||||
import io.xpipe.app.storage.DataStoreCategory;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.*;
|
||||
@@ -43,7 +43,7 @@ public class PasswordManagerIdentityStoreProvider extends IdentityStoreProvider
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
|
||||
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
|
||||
PasswordManagerIdentityStore st = (PasswordManagerIdentityStore) store.getValue();
|
||||
|
||||
var key = new SimpleStringProperty(st.getKey());
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.xpipe.app.cred.NoIdentityStrategy;
|
||||
import io.xpipe.app.cred.SshIdentityStrategyChoiceConfig;
|
||||
import io.xpipe.app.ext.DataStore;
|
||||
import io.xpipe.app.ext.GuiDialog;
|
||||
import io.xpipe.app.hub.comp.StoreCreationModel;
|
||||
import io.xpipe.app.hub.comp.StoreEntryWrapper;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.platform.OptionsChoiceBuilder;
|
||||
@@ -42,7 +43,7 @@ public class SyncedIdentityStoreProvider extends IdentityStoreProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
|
||||
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
|
||||
SyncedIdentityStore st = (SyncedIdentityStore) store.getValue();
|
||||
|
||||
var user = new SimpleStringProperty(st.getUsername().get());
|
||||
|
||||
+1
-2
@@ -8,7 +8,6 @@ import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.platform.OptionsChoiceBuilder;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreCategory;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.util.DocumentationLink;
|
||||
import io.xpipe.app.util.StoreStateFormat;
|
||||
|
||||
@@ -56,7 +55,7 @@ public class ScriptCollectionSourceStoreProvider implements DataStoreProvider {
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
|
||||
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
|
||||
ScriptCollectionSourceStore st = store.getValue().asNeeded();
|
||||
|
||||
var source = new SimpleObjectProperty<>(st.getSource());
|
||||
|
||||
@@ -83,7 +83,7 @@ public class ScriptStoreProvider implements DataStoreProvider {
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
|
||||
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
|
||||
ScriptStore st = store.getValue().asNeeded();
|
||||
|
||||
var textSource = new SimpleObjectProperty<>(
|
||||
@@ -155,7 +155,7 @@ public class ScriptStoreProvider implements DataStoreProvider {
|
||||
new StoreListChoiceComp<>(
|
||||
others,
|
||||
ScriptStore.class,
|
||||
scriptStore -> !scriptStore.get().equals(entry) && !others.contains(scriptStore),
|
||||
scriptStore -> !scriptStore.get().equals(model.getExistingEntry()) && !others.contains(scriptStore),
|
||||
StoreViewState.get().getAllScriptsCategory()),
|
||||
others)
|
||||
.bind(
|
||||
|
||||
@@ -3,6 +3,7 @@ package io.xpipe.ext.base.service;
|
||||
import io.xpipe.app.ext.*;
|
||||
import io.xpipe.app.hub.comp.StoreChoiceComp;
|
||||
import io.xpipe.app.hub.comp.StoreComboChoiceComp;
|
||||
import io.xpipe.app.hub.comp.StoreCreationModel;
|
||||
import io.xpipe.app.hub.comp.StoreViewState;
|
||||
import io.xpipe.app.platform.BindingsHelper;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
@@ -53,7 +54,7 @@ public class CustomServiceStoreProvider extends AbstractServiceStoreProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
|
||||
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
|
||||
CustomServiceStore st = store.getValue().asNeeded();
|
||||
|
||||
var comboHost = new SimpleObjectProperty<>(StoreComboChoiceComp.ComboValue.of(st.getAddress(), st.getHost()));
|
||||
@@ -91,15 +92,13 @@ public class CustomServiceStoreProvider extends AbstractServiceStoreProvider {
|
||||
hostStore -> {
|
||||
HostAddress addr = hostStore.getHostAddress();
|
||||
return addr != null && !addr.isEmpty() ? addr.get() : null;
|
||||
},
|
||||
entry,
|
||||
}, model.getExistingEntry(),
|
||||
comboHost,
|
||||
HostAddressStore.class,
|
||||
n -> true,
|
||||
StoreViewState.get().getAllConnectionsCategory(),
|
||||
false);
|
||||
var gatewayChoice = new StoreChoiceComp<>(
|
||||
entry,
|
||||
var gatewayChoice = new StoreChoiceComp<>(model.getExistingEntry(),
|
||||
gateway,
|
||||
NetworkTunnelStore.class,
|
||||
ref -> !ref.get().equals(DataStorage.get().local()),
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.xpipe.app.ext.DataStore;
|
||||
import io.xpipe.app.ext.GuiDialog;
|
||||
import io.xpipe.app.ext.LocalStore;
|
||||
import io.xpipe.app.hub.comp.StoreChoiceComp;
|
||||
import io.xpipe.app.hub.comp.StoreCreationModel;
|
||||
import io.xpipe.app.hub.comp.StoreViewState;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
@@ -35,7 +36,7 @@ public class FixedServiceStoreProvider extends AbstractServiceStoreProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
|
||||
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
|
||||
FixedServiceStore st = store.getValue().asNeeded();
|
||||
var host = new ReadOnlyObjectWrapper<>(st.getHost());
|
||||
var localPort = new SimpleObjectProperty<>(st.getLocalPort());
|
||||
@@ -60,8 +61,7 @@ public class FixedServiceStoreProvider extends AbstractServiceStoreProvider {
|
||||
var q = new OptionsBuilder()
|
||||
.nameAndDescription("serviceHost")
|
||||
.addComp(
|
||||
new StoreChoiceComp<>(
|
||||
entry,
|
||||
new StoreChoiceComp<>(model.getExistingEntry(),
|
||||
host,
|
||||
DataStore.class,
|
||||
null,
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.xpipe.app.ext.DataStore;
|
||||
import io.xpipe.app.ext.GuiDialog;
|
||||
import io.xpipe.app.ext.LocalStore;
|
||||
import io.xpipe.app.hub.comp.StoreChoiceComp;
|
||||
import io.xpipe.app.hub.comp.StoreCreationModel;
|
||||
import io.xpipe.app.hub.comp.StoreViewState;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
@@ -36,7 +37,7 @@ public class MappedServiceStoreProvider extends FixedServiceStoreProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
|
||||
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
|
||||
MappedServiceStore st = store.getValue().asNeeded();
|
||||
|
||||
var host = new SimpleObjectProperty<>(st.getHost());
|
||||
@@ -62,8 +63,7 @@ public class MappedServiceStoreProvider extends FixedServiceStoreProvider {
|
||||
var q = new OptionsBuilder()
|
||||
.nameAndDescription("serviceHost")
|
||||
.addComp(
|
||||
new StoreChoiceComp<>(
|
||||
entry,
|
||||
new StoreChoiceComp<>(model.getExistingEntry(),
|
||||
host,
|
||||
DataStore.class,
|
||||
null,
|
||||
|
||||
@@ -71,7 +71,7 @@ public class IncusContainerStoreProvider implements ShellStoreProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
|
||||
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
|
||||
IncusContainerStore st = (IncusContainerStore) store.getValue();
|
||||
var identity = new SimpleObjectProperty<>(st.getIdentity());
|
||||
|
||||
@@ -79,7 +79,7 @@ public class IncusContainerStoreProvider implements ShellStoreProvider {
|
||||
.name("container")
|
||||
.description("containerDescription")
|
||||
.addStaticString((st.getProjectName() != null ? st.getProjectName() + "/" : "") + st.getContainerName())
|
||||
.sub(IdentityChoiceBuilder.container(identity), identity)
|
||||
.sub(IdentityChoiceBuilder.container(identity, model.getSyncable()), identity)
|
||||
.bind(
|
||||
() -> {
|
||||
return IncusContainerStore.builder()
|
||||
|
||||
@@ -71,7 +71,7 @@ public class LxdContainerStoreProvider implements ShellStoreProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
|
||||
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
|
||||
LxdContainerStore st = (LxdContainerStore) store.getValue();
|
||||
var identity = new SimpleObjectProperty<>(st.getIdentity());
|
||||
|
||||
@@ -79,7 +79,7 @@ public class LxdContainerStoreProvider implements ShellStoreProvider {
|
||||
.name("container")
|
||||
.description("containerDescription")
|
||||
.addStaticString((st.getProjectName() != null ? st.getProjectName() + "/" : "") + st.getContainerName())
|
||||
.sub(IdentityChoiceBuilder.container(identity), identity)
|
||||
.sub(IdentityChoiceBuilder.container(identity, model.getSyncable()), identity)
|
||||
.bind(
|
||||
() -> {
|
||||
return LxdContainerStore.builder()
|
||||
|
||||
+2
-3
@@ -55,14 +55,13 @@ public class PodmanContainerStoreProvider implements ShellStoreProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
|
||||
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
|
||||
PodmanContainerStore st = (PodmanContainerStore) store.getValue();
|
||||
|
||||
return new OptionsBuilder()
|
||||
.name("host")
|
||||
.description("podmanHostDescription")
|
||||
.addComp(new StoreChoiceComp<>(
|
||||
entry,
|
||||
.addComp(new StoreChoiceComp<>(model.getExistingEntry(),
|
||||
new ReadOnlyObjectWrapper<>(
|
||||
st.getCmd() != null ? st.getCmd().getStore().getHost() : null),
|
||||
ShellStore.class,
|
||||
|
||||
Generated
+2
@@ -2108,3 +2108,5 @@ systemDefault=System default
|
||||
hardwareSecurityKey=Hardware security key (PKCS#11)
|
||||
macOsKeychain=macOS keychain
|
||||
customLibrary=Custom library
|
||||
unsyncedIdentityTitle=Unsynced identity
|
||||
unsyncedIdentityContent=This connection is marked to sync across multiple systems, but the identity you selected is local-only. This will lead to a missing identity on other systems.\n\nYou can either convert this local identity to a synced identity, or convert it to a multi identity to switch between multiple local identities on each synced system.
|
||||
|
||||
Reference in New Issue
Block a user