From dd4c9c120e77530bd2d17efb702e0cde4867fd79 Mon Sep 17 00:00:00 2001 From: crschnick Date: Sat, 28 Feb 2026 15:11:06 +0000 Subject: [PATCH] Rework --- .../ext/base/identity/MultiIdentityStore.java | 90 ++++++++++++++++++ .../identity/MultiIdentityStoreProvider.java | 68 +++++++++++++ .../MultiIdentitySwitchBranchProvider.java | 89 +++++++++++++++++ ext/base/src/main/java/module-info.java | 2 + .../img/multiIdentity_icon-16-dark.png | Bin 0 -> 589 bytes .../resources/img/multiIdentity_icon-16.png | Bin 0 -> 564 bytes .../img/multiIdentity_icon-24-dark.png | Bin 0 -> 878 bytes .../resources/img/multiIdentity_icon-24.png | Bin 0 -> 819 bytes .../img/multiIdentity_icon-40-dark.png | Bin 0 -> 1503 bytes .../resources/img/multiIdentity_icon-40.png | Bin 0 -> 1395 bytes .../img/multiIdentity_icon-80-dark.png | Bin 0 -> 3043 bytes .../resources/img/multiIdentity_icon-80.png | Bin 0 -> 2862 bytes img/base/multiIdentity_icon-dark.svg | 53 +++++++++++ img/base/multiIdentity_icon.svg | 53 +++++++++++ lang/strings/translations_en.properties | 5 + 15 files changed, 360 insertions(+) create mode 100644 ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentityStore.java create mode 100644 ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentityStoreProvider.java create mode 100644 ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentitySwitchBranchProvider.java create mode 100644 ext/base/src/main/resources/io/xpipe/ext/base/resources/img/multiIdentity_icon-16-dark.png create mode 100644 ext/base/src/main/resources/io/xpipe/ext/base/resources/img/multiIdentity_icon-16.png create mode 100644 ext/base/src/main/resources/io/xpipe/ext/base/resources/img/multiIdentity_icon-24-dark.png create mode 100644 ext/base/src/main/resources/io/xpipe/ext/base/resources/img/multiIdentity_icon-24.png create mode 100644 ext/base/src/main/resources/io/xpipe/ext/base/resources/img/multiIdentity_icon-40-dark.png create mode 100644 ext/base/src/main/resources/io/xpipe/ext/base/resources/img/multiIdentity_icon-40.png create mode 100644 ext/base/src/main/resources/io/xpipe/ext/base/resources/img/multiIdentity_icon-80-dark.png create mode 100644 ext/base/src/main/resources/io/xpipe/ext/base/resources/img/multiIdentity_icon-80.png create mode 100644 img/base/multiIdentity_icon-dark.svg create mode 100644 img/base/multiIdentity_icon.svg diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentityStore.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentityStore.java new file mode 100644 index 000000000..d899ee17d --- /dev/null +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentityStore.java @@ -0,0 +1,90 @@ +package io.xpipe.ext.base.identity; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import io.xpipe.app.core.AppCache; +import io.xpipe.app.cred.SshIdentityStrategy; +import io.xpipe.app.cred.UsernameStrategy; +import io.xpipe.app.ext.ValidationException; +import io.xpipe.app.issue.ErrorEventFactory; +import io.xpipe.app.secret.EncryptedValue; +import io.xpipe.app.secret.SecretRetrievalStrategy; +import io.xpipe.app.storage.DataStorage; +import io.xpipe.app.storage.DataStoreEntry; +import io.xpipe.app.storage.DataStoreEntryRef; +import io.xpipe.app.util.Validators; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import lombok.Value; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@SuperBuilder +@JsonTypeName("multiIdentity") +@Jacksonized +@Value +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MultiIdentityStore extends IdentityStore { + + List> identities; + + public List> getAvailableIdentities() { + return identities.stream().filter(ref -> ref != null && ref.get().getValidity().isUsable()).toList(); + } + + public Optional> getSelected() { + UUID cached = AppCache.getNonNull("id-default-" + getSelfEntry().getUuid(), UUID.class, () -> null); + if (cached != null) { + var entry = DataStorage.get().getStoreEntryIfPresent(cached); + if (entry.isPresent() && entry.get().getValidity().isUsable() && getAvailableIdentities().contains(entry.get().ref())) { + return Optional.of(entry.get().ref()); + } + } + + var fallback = getAvailableIdentities().stream().findFirst(); + if (fallback.isPresent()) { + return fallback; + } + + return Optional.empty(); + } + + public void select(DataStoreEntryRef entry) { + if (!getAvailableIdentities().contains(entry)) { + return; + } + + AppCache.update("id-default-" + getSelfEntry().getUuid(), entry.get().getUuid()); + } + + private DataStoreEntryRef getSelectedOrThrow() { + var found = getSelected(); + if (found.isPresent()) { + return found.get(); + } + throw ErrorEventFactory.expected(new IllegalStateException("No available identity for multi identity " + getSelfEntry().getName())); + } + + @Override + public void checkComplete() throws Throwable { + getSelectedOrThrow(); + } + + public UsernameStrategy getUsername() { + return getSelectedOrThrow().getStore().getUsername(); + } + + @Override + public SecretRetrievalStrategy getPassword() { + return getSelectedOrThrow().getStore().getPassword(); + } + + @Override + public SshIdentityStrategy getSshIdentity() { + return getSelectedOrThrow().getStore().getSshIdentity(); + } +} diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentityStoreProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentityStoreProvider.java new file mode 100644 index 000000000..3d5605604 --- /dev/null +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentityStoreProvider.java @@ -0,0 +1,68 @@ +package io.xpipe.ext.base.identity; + +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.StoreListChoiceComp; +import io.xpipe.app.hub.comp.StoreViewState; +import io.xpipe.app.platform.OptionsBuilder; +import io.xpipe.app.platform.OptionsChoiceBuilder; +import io.xpipe.app.secret.EncryptedValue; +import io.xpipe.app.secret.SecretNoneStrategy; +import io.xpipe.app.secret.SecretRetrievalStrategy; +import io.xpipe.app.secret.SecretStrategyChoiceConfig; +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 javafx.beans.property.Property; +import javafx.beans.property.SimpleListProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.collections.FXCollections; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class MultiIdentityStoreProvider extends IdentityStoreProvider { + + @Override + public GuiDialog guiDialog(DataStoreEntry entry, Property store) { + MultiIdentityStore st = (MultiIdentityStore) store.getValue(); + + var identities = new SimpleListProperty<>(FXCollections.observableArrayList(st.getIdentities())); + + return new OptionsBuilder() + .nameAndDescription("multiIdentityList") + .addComp(new StoreListChoiceComp<>(identities, IdentityStore.class, + ref -> !(ref.getStore() instanceof MultiIdentityStore) && !identities.contains(ref), + StoreViewState.get().getAllIdentitiesCategory())) + .bind( + () -> { + return MultiIdentityStore.builder() + .identities(identities) + .build(); + }, + store) + .buildDialog(); + } + + @Override + public DataStore defaultStore(DataStoreCategory category) { + return MultiIdentityStore.builder() + .identities(new ArrayList<>()) + .build(); + } + + @Override + public String getId() { + return "multiIdentity"; + } + + @Override + public List> getStoreClasses() { + return List.of(MultiIdentityStore.class); + } +} diff --git a/ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentitySwitchBranchProvider.java b/ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentitySwitchBranchProvider.java new file mode 100644 index 000000000..721068a58 --- /dev/null +++ b/ext/base/src/main/java/io/xpipe/ext/base/identity/MultiIdentitySwitchBranchProvider.java @@ -0,0 +1,89 @@ +package io.xpipe.ext.base.identity; + +import io.xpipe.app.core.AppI18n; +import io.xpipe.app.hub.action.HubBranchProvider; +import io.xpipe.app.hub.action.HubLeafProvider; +import io.xpipe.app.hub.action.HubMenuItemProvider; +import io.xpipe.app.hub.action.StoreActionCategory; +import io.xpipe.app.platform.LabelGraphic; +import io.xpipe.app.storage.DataStorage; +import io.xpipe.app.storage.DataStoreEntryRef; +import javafx.beans.property.ReadOnlyStringWrapper; +import javafx.beans.value.ObservableValue; + +import java.util.List; +import java.util.stream.Collectors; + +public class MultiIdentitySwitchBranchProvider implements HubBranchProvider { + + @Override + public boolean isMajor() { + return true; + } + + @Override + public List> getChildren(DataStoreEntryRef store) { + var selected = store.getStore().getSelected(); + return store.getStore().getAvailableIdentities().stream() + .map(is -> { + return new IdentityProvider(is, selected.map(ref -> ref.equals(is)).orElse(false)); + }) + .collect(Collectors.toList()); + } + + @Override + public StoreActionCategory getCategory() { + return StoreActionCategory.CONFIGURATION; + } + + @Override + public boolean isApplicable(DataStoreEntryRef o) { + return o.getStore().getAvailableIdentities().size() > 1; + } + + @Override + public ObservableValue getName(DataStoreEntryRef store) { + return AppI18n.observable("switchIdentity"); + } + + @Override + public LabelGraphic getIcon(DataStoreEntryRef store) { + return new LabelGraphic.IconGraphic("mdi2f-format-list-group"); + } + + @Override + public Class getApplicableClass() { + return MultiIdentityStore.class; + } + + private static class IdentityProvider implements HubLeafProvider { + + private final DataStoreEntryRef identity; + private final boolean active; + + private IdentityProvider(DataStoreEntryRef identity, boolean active) { + this.identity = identity; + this.active = active; + } + + @Override + public void execute(DataStoreEntryRef ref) { + ref.getStore().select(identity); + } + + @Override + public ObservableValue getName(DataStoreEntryRef store) { + return new ReadOnlyStringWrapper((active ? "> " : "") + identity.get().getName()); + } + + @Override + public LabelGraphic getIcon(DataStoreEntryRef store) { + return new LabelGraphic.ImageGraphic(identity.get().getEffectiveIconFile(), 16); + } + + @Override + public Class getApplicableClass() { + return MultiIdentityStore.class; + } + } +} diff --git a/ext/base/src/main/java/module-info.java b/ext/base/src/main/java/module-info.java index 73ecd2810..1c42fe5bb 100644 --- a/ext/base/src/main/java/module-info.java +++ b/ext/base/src/main/java/module-info.java @@ -39,6 +39,7 @@ open module io.xpipe.ext.base { AbstractHostCreationActionProvider, HostAddressSwitchBranchProvider, LocalIdentityConvertHubLeafProvider, + MultiIdentitySwitchBranchProvider, RunBackgroundScriptActionProvider, RunHubBatchScriptActionProvider, RunHubScriptActionProvider, @@ -68,6 +69,7 @@ open module io.xpipe.ext.base { LocalIdentityStoreProvider, SyncedIdentityStoreProvider, PasswordManagerIdentityStoreProvider, + MultiIdentityStoreProvider, AbstractHostStoreProvider; provides DataStorageExtensionProvider with ScriptDataStorageProvider; diff --git a/ext/base/src/main/resources/io/xpipe/ext/base/resources/img/multiIdentity_icon-16-dark.png b/ext/base/src/main/resources/io/xpipe/ext/base/resources/img/multiIdentity_icon-16-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..a4bd2d65bf1aa22e7af7d32a77e93dbc98337ea7 GIT binary patch literal 589 zcmV-T03{PyVnV=$Y64sY*lk- z%vuP#3WYWr`X}T@GMIlLXyfSMII9*#o0?4&(Jqic6cI$%(GQSjIG(Xpg>zbrmLfU# zp3jYLu2)gd?!4!DAHH+oP^D72&&;og=n_t`L}Zhhv+jO*D4kAMP9xA-cUo&_NGVm% zWHOh-!1w&^Slnq0kw_~uEyTxGe}x&PRGp*jzw_5x;?C|e2>l>R1|sujD%~5!x{;xK z{*ee~6Jx6d05F9cA4HtD0Pcp_$O$aOW5$<*eOvB_25>csjTEq8*#;PuQg;&otWSCm z0DL{M;7X=C4MMvyf2-?-)F&dm7Pb>766hDHb4Gcy0IC3uI*cp`<0Smi6uqu6f9Me} zZejw0Z;5gJ%RdoAOcX(C=$s^ z&204nSd0`%{+?T-pU-03&4U*HV+Q7|$U6`v5yV~w&i@`){C`JYf$~c}fahj1^$?J^z*<{6 b>v!G*4GgheB8p0p00000NkvXXu0mjfLb>^` literal 0 HcmV?d00001 diff --git a/ext/base/src/main/resources/io/xpipe/ext/base/resources/img/multiIdentity_icon-16.png b/ext/base/src/main/resources/io/xpipe/ext/base/resources/img/multiIdentity_icon-16.png new file mode 100644 index 0000000000000000000000000000000000000000..2b5a8bcbd3093f272c9f2c22c848a70b5c8a0927 GIT binary patch literal 564 zcmV-40?Yl0P)6y%7>1ws+^a$@*k&>y+?<#g zEeZ~uih_$3`vV;8B363q;-srZ#L1y-d*1^`g+k$+q#AI5-K}px97?JsNix;#b{o679YxVt5CqTUz3+88 zozXP#@q+e-qfE&$V({`v)jp@0VzJnBS?XV64TU+D5ogWFW4vg_I+ewSS|PjM&7@zElr1cT|J{Tyw?$~*BuFo_^=+yOoeJ5B?yGX>t62=kVw{>u4-ul43)vLO!gc0D7#9Fy#cLy`JyrTEMXJ(|UHOt|w{^s)D z_Z+YuMbTJVxKy`KTc&M3FO`l1>+;@D0(JiLJO2gPE~>J0sHSHC0000#D#9sMbm|#2?aHanNdMB zCU=60!}!Q!{^KHQ@ zofJO;;3COnHPj`oAbB(x48q04_D5#!6vHrX)2u6$-EPJEH$Md1hpsbS9G>_B$I-=sHX}ZrKKd-ioiBJbM5T;07zL z0@)6+)lWvxmE*hR0Ce*SNH4MrupxRdJu;c@_dBCcDO}HVEWZ#=JSn>3vJNPG1Mt`A z!AB$0^Wo#^zW~0ma@T&A0}ukBnTxWZiqs~el84*_VTUfQ4opw_U5(oTEOuafN|DPt zu;Zr9G5P;8!`a)J_NF%2{2&~&Nm3WcU&8U?`>&|ME0`A{+G~*ZiCPciU~*$`Xzi79 z{E{p3yOwcj0Dz3`hBJPH(kTk(ht9Ulo0S7m zyCGZ(B~kBY8*Ofb1)Mt@t`YsTde=nr$?*O*gQMiga&NygM2L)p5 z{kJc@#?506u0;~*t);NVqU=$z#Cw!QZS4=AB3@`PKr&hi6luNxcDBNjVgR1IlCF|$ zO(v75=0_jEev+r%TrSrZ3_KmY&$07*qoM6N<$ Ef>}P65C8xG literal 0 HcmV?d00001 diff --git a/ext/base/src/main/resources/io/xpipe/ext/base/resources/img/multiIdentity_icon-24.png b/ext/base/src/main/resources/io/xpipe/ext/base/resources/img/multiIdentity_icon-24.png new file mode 100644 index 0000000000000000000000000000000000000000..4deb2ad948a65e13fa3a44a2fd198986171175de GIT binary patch literal 819 zcmV-31I+x1P)pxPW?4Q8A-}A|CV-!Q~(*K@hjhgov29VrvAq znCbE|(FmC(VG903|F7!3|5Y`Aw_>r_FyHqZB-NN%8O4cC;H>9)dtBf58-Q(Qb^>TC z2D_O}mbAn7eca5+Pjqy2Y%Kp!!1koak$m-T zzJyS~P7`mOF!Rfg*{{s@(^S1)nQ{4fT*s#*vbQ*|0~2#(oa;vpKs3QsL3hZQovXf( z85qI_1_&$*HW-1s1}j_ZG_3z-TOG>*FEgL4k#`1xXmpH6rQ&9CgBnBPZ8~6)U_a25 ztN(=weBzDcaRJysz})mdsM`@|^3~^T(aUuj?|R$=R?0XOh(!*}0&0NPz<7|X*QOyk zcvh{j>v0_@K}?G#tg9b&n6x?FtcC>vCjml(Jbs14@*5R0!s)pu@A3Eupm{( z>uACb0|c;%5Ch(>1Gnh1rK8aDc$G#nf^k3yBk~t-K+)Z{ukUe6lRX)bC#ygvs+#Rv z`(`Z;dbm)Lu%)?jvqj#kqWqYB{rR00Lm>xnC3OMOTuD{U_H78}1ONa48FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H101k9T zSaeirbZlh+X=G(?bZK;X$zwi3000G2Nkl(Y-1RP z2d9F{Id`3;l|X9=a+&3FxvjubGwU559=^G)t?l!vfGaDT%{H0Y9w07htE2eN0lYZX zZ~zMzE<9sq8v)Gh7DwrWkaTED$wo<1f2la5ZXz5WT7OML{TG-0ND9AWX0s7KXnRY` z<^Qym9}bVl_qYLrRN`R`@kdb1Fi^S22!g@=LE3w?rL*UqTK&86z#UI{Yh}Cy#DD|ROhfbUfstEMIP zgU^3pFb^1vL|w;)?95PDsQWb#0=N3q4>UTOu9ak$!8H35dG{59Shq;E)E%=Xw*t#<(P zfv=)<`S+`hRTJP@6w1KcdJEyyH%Kb`nE z$R$n~yiM_3cMRv2&PnbMCKWJ9woNy5JLmrFn}If9=BT#mg!<`)UV}@irkx~6#TSK^ zf3x(n2I$A^?`F#r@SA>l0Bcg&@BayVCD_GX-`%fv*IN_mhqM#-3x?~h#tJ6>)>aj`~RDa&xl0x zkZj-Pe6|dxkGDFUO zS#tCmfQh>+iCfVU_dXYoa++%aMkwqqw+HFOIzxHj-!fh|*gaZiRQB6b2h0dN+?*5vm3>BK9* zfT9h{+dB5<0h}vzTUhtL;Ov9}mNxk%<+`6vbVBhyBDf!P|2YFfZ3dRh^g(@H_{gls z_70W1VrvfgqkT;X;AlyhpRpV5>-*&eHK(KR*=qfdRn>rINE@1d76gOe0`38O7qR6W zBk90+!DtP)aSH#_($%+dqJYM#=F?1Da$i0y46VT2j)?>st8t}1 zu1X&L0;oByQHdNd2w-OOE@3-qGPC)m;-CX|0Z95qHk)mlI;^6y*=$oO%Zj9(-}Dsl zUElm9dby>yxA(3v3|9lUP|6%kfzC+!mvipVp6C4o;D4VfG4aFncg+9*002ovPDHLk FV1k{3(OLii literal 0 HcmV?d00001 diff --git a/ext/base/src/main/resources/io/xpipe/ext/base/resources/img/multiIdentity_icon-40.png b/ext/base/src/main/resources/io/xpipe/ext/base/resources/img/multiIdentity_icon-40.png new file mode 100644 index 0000000000000000000000000000000000000000..582504ef353727ffd1fbf18db0a31e588dcf681b GIT binary patch literal 1395 zcmV-(1&sQMP)}1ONa48FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H101k9T zSaeirbZlh+X=G(?bZK;X$zwi3000E$NklfU!K z71T1#5{h(1Qj%zf3H2e*IhzTYfgc)z5SF1x;$9RJDo7V;Wr#`;10~z?s&oBJokP7pX>T@U(fyW+>gu*fb8t-B1vxnj{+Ga#x4Vg zfOSnxP5S|4X5{4Llme?01l5xnP}O%I2|$ALUxzeD7i1K5&iIDopkx)vf?1eoR!9S~;r zTf(TinVFrAUS=c>gu^q-HLSIbXD}WCMo65M%YjSq-IPje?~m_(1BL>6)r|Lm`#bB* zzy#^g81!pdL51y!*T2&rxL`nQ%-9x%YsUCQ##*2XxCG<^yX$=l#2X{-9t`>QGO!W= zu`Xp8r4OvPw$Anw#kzN>%SMAm5*vd)ohYcZ1A_wigHJ=!#rpszJ}fM^72P!x%IqT2 z;-HUIV6KU^z+-)l6<3PW9G(Ei0GA2#&Mk#z=887~S1`T7r`$mSl*=RlgX+Sq*3z5S zTW+U-UjVKva^D>X#33C(;zEpfoCiRC--N_Fihl>d$caU4k((km>BKhQ5dV*TA3Fr)(iP%)^tY!>jRo0V;_)eaN2>D1*6%)Ds#JQ zqf6rJz-oi>xE!u8*5id`Rv&A;KBc&Jsm96J38VrCJz*ZsudvU0f&<8O$tf67kNVjWt$Qw(urZ~$VVRPFaRBZRj&#=7`&cUo13wDBFmAR1 zU_6D$6YOjMi;_qcM-=(*bq((mU|R<%$>JDBxV4=>Ao4yUSb@v-&=Nfj98%<51viQ2 z!ZQ0g(R4UzgA6aN(VOPCeb^`NU zD$LLCH z9Sw0GYs6v>kz-dRjk%M7`eKsCMlUZrz~N|NiP!5*PY}}$^LoAMX6B0)58c&Az>;R) z{fFqCpCNK`a%KZ7fN4OYQr!Z4FX@BE#>Q^}{sof~)1d@G6zl*1002ovPDHLkV1oHm BgpB|I literal 0 HcmV?d00001 diff --git a/ext/base/src/main/resources/io/xpipe/ext/base/resources/img/multiIdentity_icon-80-dark.png b/ext/base/src/main/resources/io/xpipe/ext/base/resources/img/multiIdentity_icon-80-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..8bc62c1440303ea5b1c20275916fb82d8b4a22a5 GIT binary patch literal 3043 zcmV<93mo)`P)dvH|M9ml`Fb9VzO5?+IBiB70W9LH`y3`AX4mDN2fEWV>^S6P_0_4LL47c ztyQb8>}EGWHIVFv07(!{cJKN1k3`se_a@mSckdF&XC{B_@0{~Hzx&PEbM8Io+_MA| zm|29w;j5VWS^)EisF0Z_o8>)OA)-Uf+ydZTBHHfpcwQkQnUc?F2!%pd0N4ni+=e(u z6Q3~ia-Yw)BiSgDKLmrpI|2L?W8#@nPDHPVLLoKTc&r5mgTa+Vbf4{EjY61trO)Sk z0O%92v9WO>GrtN$i|petGyhiz5wkPMY$K&~5z+Yoa?O&Nd6CcO+u>klrLnPbBT~id z0I;H~tLw?xvuDRM9`OlCAP~rLI2<^O zMh)?Jd<%NJwPfZA0EGtQM`dMYJ+?$OiYO~9>mj0#4Cd(qsiTZhbO9)4i5PT66 zF9-0JNty8=da~8;xjM5^r;?REFk0vSIha?PB)ve;R=BEmhfUI20DK5wQ%gvu8Dk}iMY+My#?-9z0<(-h{4PNs$G@grTvJfqi|i*PSSGb0=(Uj zG*M`H+_GTPz;*#03RlAL^h1n$^Q*#sqV#X#U25QnODnz};2ruDx+LoNnoW>o1X~3> zy#70r8TeJQjev$c8D2LWf21}f;)>%20PTjP%HhU1RBRQnTg$l``Vp5UV-=}LS?x-f z;x)l`5Y_9gd{L{v^z7mK918Y+g_(Y6u)JST9e!`5ZiSPc0HB*e5|Flx$Tkbey45}g zUbMn(-pWuH2yg1GbeOK<{Smt}6YVD{50NTIOQdjm^n_!Z4cu1USuykAIdjM<`uuKi~0O(A%F^L&g z`8WC^;DA-W2g2qXY5Gi8Iq?-%c;()C!|x~CB-oZ!MZ2FwbQ1v#RJ5z^iT!0xm$rL7 zmjalcY!eV~kJN8E*eW11Z!?&3HIcgBI&{DA8%!M?x$=#X`b`I01uUozzsKMYl5I@5 zyQS7UXShD2weH^lxJ+kc{3Ni^3h0tW2nm zb9CR@t&KY$jB}FAQy}Oct+k$)n0P~hR{S=p4TYY%z(-38R#|6C!!Cc ze)sS4t0I50I({>36R=e+aS28Ioq_&tnp?p9S4nvB8!hp#v(>%dm}UA>Et`_#h|dS( zI)Hx!;0!~S@c=y<^?SbOa!tIAZvRh4_jlVOV7uxrP6)igfY&6siHQ7pd1wA5or|5J z1`Yz)24LIa_1~G?of!9904reFzYO4}XtZ-$c(Z$1CZ9?&-Ul|<`=%xYy$T|ucp5S4 zj%x8)uFA$IhtpCft^VRrepT2ngu@4h@i`krSTeCgJk4t6t}~-8khel)V7o3`EviQJ0a7w!m1(UCGP~^j2z> z%$JQ+yp~$;oCEb4GXB>)NpMAB zbtGU~Xb32bX`05b8`pnbm62zOahoB#fP%-q+*o|tNb5C{F#(07O)xy@6ZjU_+VIB~ zRx_O-Uu#oKbt`bL>hKl@^!slZ=j42K+6oKn29O~EqqXk2U>LqK^t7ug@>~Y0E0KzP zwYlCmHQCse%lCk}(GbIOD`vox$;==^Wc*JbVNFZxzu)>``5EIAhjRwY*Q3b^;A8jG zvvM@;m3Gy=aH<;V1OU$+k?xq^yPUx<9WTX`;|gv8@Nhb@q=V%xaKGxE20*{9g7H|o zJM5|Z>pylU#vL5&kD0dWtH7LuB3|iRe+B@!sv2JgpxZh^pqqw$aVA(Upb%IJAh`uG zlX%qXdd%T@)cy6g@Mib%SsNx}-RHIWOG~Y)(Ru+%E;N|lDX0qV=`VjkEz9ApL)u(E zFpNdH9dv9;m8A1|7#7(jYnCjh0vQmHL34Em;@A4kyA?{F(*4wdJ*B5x7{X^gigK)v zbE$zN0KcP;Nz6?tHB2lOFs7DGK<{kHF^Ktnv%IWA#6sRJ4El+MAv6i$SX{g&XxMNm z{%y!N)2a$B9fhUyo3ls?-BgpNIfKT0%%E7E=TU%DUQLjaHwY5X3|1Eba~Pbwv9eie z%EJ7rT`ehj3Ax#v- zz>wdEUxU zmz}X#h3yOL1!_}F(ReWkyX~$!6J%v)!c;ZVtR=Js0RJx=^;=2S_Qn*cyT2gFMd*DG zc+`^BGZS1@yTi=Nl}Enr9<`^#W&PBSg6iO?KIF`-1)6T$83TZcHUfsTvaHyzcd|l6 z#*>UY1q_`yGhYx01O{CdJJkpT0y)flfx)~_5Yf8^V{YHEmt!R1bUJTF>PdI+3L-L| zd;tI(g2CX}QOJD+gTZoUe!yraqV3do2<=AzzH3PDB%*ub@%WZg^+3izAdus9I&bb1 z&$y%E6R+2MK|04EcQEtEW5*!tlfOL%`Pi@=3WZh!s5i+xh5=??<@5RO2a;M`X=&+t zX1)t)Oe>wjFo2rU($Zu7VShhH@Gw&Q#8Vy9A0se*>R@IOiA0u2DX#@E4?r=Hawfsa lP8Wb?9RE3tm(6|-e{fXQ6~{m4Z4zQcF_Ez*=!}~U zS)dVrm8sw;QUQx-r83g#*y=b4yFmvmqZ_w0Ay-uLc3_uUm$#Y9A` z^78U4RP|~R8KbJDKyg~dW1Br9vQt&x5s@|9wrzVwRc*79m;wd@fy-3&Az-9ek@gWb zT9!2@7K^P-Hd@Iq{C@wfs`_{O#dBh$ZQHL00)d6e#zSYI-|wF%BKPMavc86@&TDLJ zd=L@>1_FWcs`@I%8JXZ~;3MF3FM~V^I1M-#801i9Tb4B;7K^PFz~}e-KLE})Dm4Lf zOG-+f*sx(kYnFpODygcfDmZZ9zzx7+;AErtqsGR@^TqG?Uj@8i6gG*-#SIM&n>>m1 zD5t!3=u`-N=sFzI2<##U%bO?+qP{ZaHnZk)hU*Uj4|wA7Znvf=}l;ThoYjQ zRkU|&hY*o57Eo#!KiIr^^Fa?|>k}k2`5@V*szWT&uQE;a{mPGkVeB!x+K0T0!O<>n)oq>maAal`>1YK$SVP52;KF1)iv{mMgOy zb{aY91EbaQQ(&HH7v+UEVear+t#>$VC-6S-NL@(AS;7*9s-$vDm8WB28C_AUTe1{- z8lInl(U1&AdDJkgD!&}Mf+?MdmqIY2>soz`2G$rwC)=>XwO}*Aa{)z|k1_gD_;oO% zi*QQNbi#hRk7>YH zDL&gjBv0^EzyZpYq+W3q2$7YeT3ZU}sA z7|J@S>MEWIxbb>95g3?kQx`LRs(hAe;B>cq9|_L{%=?D4%$^#8_6m!W3`>h^zK__f zM~rwz4+48r8n-#qwFc6Ced(I-Bj9ZxC~P(?m$~Ct1mSyz)sw8E*S9gL69K}c*laNq z*4?H)#Kpi6!~9ln{bquAZF60&Pn^Zp+OwFfYzzf%(Q{Y6okA5tdM!)$miuiGXJ>(><|} zOtghJNxyndwv@?bJK0@VEt@Q5hpiMMl;y$szyPO7LxJ@JmFv1Qr|1Sb%ZXG(w84sV zA;v`|i4sx9DqJVRRDscSo_u;{pfb5Mq6e}QZRg}nz%A3Hz|Lb5$|)|m8?sLs>w=$d zlM?~!LNYF|m=94_AXK>E@**r=uB(>yv6=F17yM<6r+wh|8>O(dkcUL5?xJcd&rB@-Un{IMNYMS zyh^%wafM$Mv`|$U(IJs1&kIm9~fttcUd-<1S7hx zSH5S;sAx!rCEJ3v`a4yA45W-;Q0}S^%Y@#v*BMz8Fcy{(2){7Qd&Oo_#R~n{@u2Q8 z7~yp@{~L_xvm(qe>RJl5J8qW~Txd7dvD*$iu-j)z%z^&&p)2%57n>O(K3=PvE-4wf zq04m@q^-XxZ*FF;i){m05-=8)(Lm}~MtLe&tIuVjxqy|=t6Oi8QlJG&(}9OGdnaAv`WYAv$zVubR-sUv=?eS8A|4xHv%9N55uQmoP#I;n@=7BA z3_xYAz6`u;)Vral@6Cy9e1)e#${D1@S(b%%(ox0~7O^@uTassO%(l;|ni!E zp`lHgm{>D-2!VeYuZO$UpsQD5u{jH4s!4^d=@mulX0l9#v_}&xo5c!Gch#0!j3RYm zQ&W@T&#t_s*D)aMEJizdPq_LbJA<|*n!@&Lje$u`Mn zPF+~KoO1HPiw=)4G+8#^uUM|1batH8#s<59`y|n2DI8!S+Ow6}skkcOh^4vWNXI1}O8j|>(WTU?LgO8L}hc)D#V-i(0cQW2>?eul=H?qo-|6lhOGM0_F8~%Nw!Q3!_ANj~Y6h}3M~zWq>t*x8Q}JWP6@ zc)a8IV+3geLi^8Q`2GILz}3JQ;0#FHli*lqAFu=H_&JQ1oqi7E|DnOO?|>Q&*Z=?k M07*qoM6N<$g5H} +identityidentity diff --git a/img/base/multiIdentity_icon.svg b/img/base/multiIdentity_icon.svg new file mode 100644 index 000000000..820ecf43b --- /dev/null +++ b/img/base/multiIdentity_icon.svg @@ -0,0 +1,53 @@ + +identityidentity diff --git a/lang/strings/translations_en.properties b/lang/strings/translations_en.properties index 453612f25..7895b5dd8 100644 --- a/lang/strings/translations_en.properties +++ b/lang/strings/translations_en.properties @@ -2016,3 +2016,8 @@ passwordManagerSshAgentSocketDescription=Override the default agent socket locat passwordManagerSshKeysNotSupported=The current password manager configuration does not support retrieving SSH keys passwordManagerIdentityAgentKey=Additional SSH key passwordManagerIdentityAgentKeyDescription=The SSH key to use from the password manager SSH agent +multiIdentity.displayName=Multi identity +multiIdentity.displayDescription=Choose from multiple different identities to connect to a system +multiIdentityList=Identity list +multiIdentityListDescription=The list of identities to switch between +switchIdentity=Switch identity \ No newline at end of file