mirror of
https://github.com/xpipe-io/xpipe.git
synced 2026-04-22 07:29:05 -04:00
Rework
This commit is contained in:
@@ -46,8 +46,12 @@ public class AskpassExchangeImpl extends AskpassExchange {
|
||||
return Response.builder().value(InPlaceSecretValue.of("")).build();
|
||||
}
|
||||
|
||||
var prompt = msg.getPrompt();
|
||||
// sudo-rs uses a different prefix which we don't really need
|
||||
prompt = prompt.replace("[sudo: authenticate]", "[sudo]");
|
||||
|
||||
if (msg.getRequest() == null) {
|
||||
var r = AskpassAlert.queryRaw(msg.getPrompt(), null, true);
|
||||
var r = AskpassAlert.queryRaw(prompt, null, true);
|
||||
return Response.builder().value(r.getSecret()).build();
|
||||
}
|
||||
|
||||
@@ -59,7 +63,7 @@ public class AskpassExchangeImpl extends AskpassExchange {
|
||||
}
|
||||
|
||||
var p = found.get();
|
||||
var secret = p.process(msg.getPrompt());
|
||||
var secret = p.process(prompt);
|
||||
if (p.getState() != SecretQueryState.NORMAL) {
|
||||
var ex = new BeaconClientException(SecretQueryState.toErrorMessage(p.getState()));
|
||||
ErrorEventFactory.preconfigure(ErrorEventFactory.fromThrowable(ex).ignore());
|
||||
|
||||
@@ -3,13 +3,13 @@ package io.xpipe.app.comp.base;
|
||||
import io.xpipe.app.comp.Comp;
|
||||
import io.xpipe.app.comp.CompStructure;
|
||||
import io.xpipe.app.comp.SimpleCompStructure;
|
||||
import io.xpipe.app.core.AppCache;
|
||||
import io.xpipe.app.core.AppProperties;
|
||||
import io.xpipe.app.core.AppResources;
|
||||
import io.xpipe.app.issue.ErrorEventFactory;
|
||||
import io.xpipe.app.platform.MarkdownHelper;
|
||||
import io.xpipe.app.platform.PlatformThread;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.app.process.ShellTemp;
|
||||
import io.xpipe.app.util.Hyperlinks;
|
||||
import io.xpipe.core.OsType;
|
||||
|
||||
@@ -34,7 +34,7 @@ import java.util.function.UnaryOperator;
|
||||
public class MarkdownComp extends Comp<CompStructure<StackPane>> {
|
||||
|
||||
private static Boolean WEB_VIEW_SUPPORTED;
|
||||
private static Path TEMP;
|
||||
private static Path DIR;
|
||||
private final ObservableValue<String> markdown;
|
||||
private final UnaryOperator<String> htmlTransformation;
|
||||
private final boolean bodyPadding;
|
||||
@@ -53,8 +53,8 @@ public class MarkdownComp extends Comp<CompStructure<StackPane>> {
|
||||
}
|
||||
|
||||
private Path getHtmlFile(String markdown) {
|
||||
if (TEMP == null) {
|
||||
TEMP = ShellTemp.getLocalTempDataDirectory("webview");
|
||||
if (DIR == null) {
|
||||
DIR = AppCache.getBasePath().resolve("md");
|
||||
}
|
||||
|
||||
if (markdown == null) {
|
||||
@@ -68,7 +68,7 @@ public class MarkdownComp extends Comp<CompStructure<StackPane>> {
|
||||
} else {
|
||||
hash = markdown.hashCode();
|
||||
}
|
||||
var file = TEMP.resolve("md-" + hash + ".html");
|
||||
var file = DIR.resolve("md-" + hash + ".html");
|
||||
if (Files.exists(file)) {
|
||||
return file;
|
||||
}
|
||||
@@ -94,7 +94,7 @@ public class MarkdownComp extends Comp<CompStructure<StackPane>> {
|
||||
wv.setPageFill(Color.TRANSPARENT);
|
||||
wv.getEngine()
|
||||
.setUserDataDirectory(
|
||||
AppProperties.get().getDataDir().resolve("webview").toFile());
|
||||
AppCache.getBasePath().resolve("webview").toFile());
|
||||
var theme = AppPrefs.get() != null
|
||||
&& AppPrefs.get().theme().getValue() != null
|
||||
&& AppPrefs.get().theme().getValue().isDark()
|
||||
|
||||
@@ -94,16 +94,16 @@ public class SecretFieldComp extends Comp<SecretFieldComp.Structure> {
|
||||
}
|
||||
|
||||
field.setText(n != null ? n.getSecretValue() : null);
|
||||
});
|
||||
|
||||
var capslock = Platform.isKeyLocked(KeyCode.CAPS);
|
||||
if (!capslock.orElse(false)) {
|
||||
capsPopover.hide();
|
||||
return;
|
||||
}
|
||||
if (!capsPopover.isShowing()) {
|
||||
capsPopover.show(field);
|
||||
}
|
||||
var capslock = Platform.isKeyLocked(KeyCode.CAPS);
|
||||
if (!capslock.orElse(false)) {
|
||||
capsPopover.hide();
|
||||
return;
|
||||
}
|
||||
if (!capsPopover.isShowing()) {
|
||||
capsPopover.show(field);
|
||||
}
|
||||
});
|
||||
});
|
||||
HBox.setHgrow(field, Priority.ALWAYS);
|
||||
|
||||
|
||||
@@ -52,6 +52,10 @@ public class AppI18n {
|
||||
return INSTANCE.observableImpl(s, vars);
|
||||
}
|
||||
|
||||
public static ObservableValue<String> observable(ObservableValue<String> s, Object... vars) {
|
||||
return BindingsHelper.flatMap(s, v -> INSTANCE.observableImpl(v, vars));
|
||||
}
|
||||
|
||||
public static String get(String s, Object... vars) {
|
||||
return INSTANCE.getLocalised(s, vars);
|
||||
}
|
||||
|
||||
@@ -123,6 +123,7 @@ public class AppBaseMode extends AppOperationMode {
|
||||
syncPrefsLoaded.countDown();
|
||||
AppMainWindow.loadingText("loadingConnections");
|
||||
DataStorage.init();
|
||||
AppPrefs.initStorage();
|
||||
storageLoaded.countDown();
|
||||
AppMcpServer.init();
|
||||
StoreViewState.init();
|
||||
|
||||
@@ -126,6 +126,10 @@ public class OptionsBuilder {
|
||||
return name(key).description(key + "Description");
|
||||
}
|
||||
|
||||
public OptionsBuilder nameAndDescription(ObservableValue<String> key) {
|
||||
return name(AppI18n.observable(key)).description(AppI18n.observable(BindingsHelper.map(key, k -> k + "Description")));
|
||||
}
|
||||
|
||||
public OptionsBuilder subAdvanced(OptionsBuilder builder) {
|
||||
name("advanced");
|
||||
subExpandable("showAdvancedOptions", builder);
|
||||
@@ -326,6 +330,13 @@ public class OptionsBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public OptionsBuilder name(ObservableValue<String> name) {
|
||||
finishCurrent();
|
||||
this.name = name;
|
||||
lastNameReference = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OptionsBuilder description(String descriptionKey) {
|
||||
finishCurrent();
|
||||
description = AppI18n.observable(descriptionKey);
|
||||
|
||||
@@ -15,6 +15,7 @@ import io.xpipe.app.pwman.PasswordManager;
|
||||
import io.xpipe.app.rdp.ExternalRdpClient;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStorageGroupStrategy;
|
||||
import io.xpipe.app.storage.DataStorageUserHandler;
|
||||
import io.xpipe.app.terminal.ExternalTerminalType;
|
||||
import io.xpipe.app.terminal.TerminalMultiplexer;
|
||||
import io.xpipe.app.terminal.TerminalPrompt;
|
||||
@@ -41,6 +42,7 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
import org.w3c.dom.UserDataHandler;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
@@ -68,6 +70,10 @@ public final class AppPrefs {
|
||||
});
|
||||
}
|
||||
|
||||
public static void initStorage() {
|
||||
INSTANCE.vaultAuthentication.set(DataStorageUserHandler.getInstance().getVaultAuthenticationType());
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
INSTANCE.save();
|
||||
|
||||
@@ -270,11 +276,14 @@ public final class AppPrefs {
|
||||
.log(false)
|
||||
.documentationLink(DocumentationLink.TERMINAL_PROMPT)
|
||||
.build());
|
||||
final ObjectProperty<VaultAuthentication> vaultAuthentication = new GlobalObjectProperty<>();
|
||||
|
||||
final ObjectProperty<DataStorageGroupStrategy> groupSecretStrategy = map(Mapping.builder()
|
||||
.property(new GlobalObjectProperty<>(new DataStorageGroupStrategy.None()))
|
||||
.key("groupSecretStrategy")
|
||||
.valueClass(DataStorageGroupStrategy.class)
|
||||
.requiresRestart(true)
|
||||
.vaultSpecific(true)
|
||||
.licenseFeatureId("team")
|
||||
.build());
|
||||
final ObjectProperty<StartupBehaviour> startupBehaviour = map(Mapping.builder()
|
||||
@@ -423,6 +432,10 @@ public final class AppPrefs {
|
||||
|
||||
private AppPrefs() {}
|
||||
|
||||
public ObservableValue<VaultAuthentication> vaultAuthentication() {
|
||||
return vaultAuthentication;
|
||||
}
|
||||
|
||||
public ObservableValue<DataStorageGroupStrategy> groupSecretStrategy() {
|
||||
return groupSecretStrategy;
|
||||
}
|
||||
@@ -721,7 +734,10 @@ public final class AppPrefs {
|
||||
PlatformThread.runLaterIfNeededBlocking(() -> {
|
||||
writable.setValue(newValue);
|
||||
});
|
||||
save();
|
||||
|
||||
if (mapping.stream().anyMatch(m -> m.property == prop)) {
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
private void fixLocalValues() {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package io.xpipe.app.prefs;
|
||||
|
||||
import io.xpipe.app.core.mode.AppOperationMode;
|
||||
import io.xpipe.app.ext.PrefsChoiceValue;
|
||||
import io.xpipe.core.XPipeDaemonMode;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum VaultAuthentication implements PrefsChoiceValue {
|
||||
USER("userAuth"),
|
||||
GROUP("groupAuth");
|
||||
|
||||
private final String id;
|
||||
}
|
||||
@@ -2,10 +2,12 @@ package io.xpipe.app.prefs;
|
||||
|
||||
import io.xpipe.app.comp.Comp;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.comp.base.ChoiceComp;
|
||||
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.platform.GlobalObjectProperty;
|
||||
import io.xpipe.app.platform.LabelGraphic;
|
||||
import io.xpipe.app.platform.OptionsBuilder;
|
||||
import io.xpipe.app.platform.OptionsChoiceBuilder;
|
||||
@@ -17,6 +19,7 @@ import io.xpipe.app.util.LicenseProvider;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
@@ -62,40 +65,50 @@ public class VaultCategory extends AppPrefsCategory {
|
||||
var uh = DataStorageUserHandler.getInstance();
|
||||
var vaultTypeKey = uh.getUserCount() == 0
|
||||
? "Default"
|
||||
: uh.getUserCount() == 1
|
||||
: uh.getUserCount() == 1 && uh.getVaultAuthenticationType() != VaultAuthentication.GROUP
|
||||
? (uh.getActiveUser() != null && uh.getActiveUser().equals("legacy") ? "Legacy" : "Personal")
|
||||
: "Team";
|
||||
|
||||
var authChoice = ChoiceComp.ofTranslatable(prefs.vaultAuthentication, Arrays.asList(VaultAuthentication.values()), false);
|
||||
authChoice.apply(struc -> struc.get().setOpacity(1.0));
|
||||
authChoice.maxWidth(600);
|
||||
authChoice.disable(Bindings.createBooleanBinding(() -> {
|
||||
return uh.getUserCount() > 0 && prefs.vaultAuthentication.get() == VaultAuthentication.USER ||
|
||||
prefs.groupSecretStrategy.get().requiresUnlock() && prefs.vaultAuthentication.get() == VaultAuthentication.GROUP;
|
||||
}, prefs.vaultAuthentication, prefs.groupSecretStrategy));
|
||||
|
||||
builder.addTitle("vault")
|
||||
.sub(new OptionsBuilder()
|
||||
.name("vaultTypeName" + vaultTypeKey)
|
||||
.description("vaultTypeContent" + vaultTypeKey)
|
||||
.documentationLink(DocumentationLink.TEAM_VAULTS)
|
||||
.addComp(Comp.empty())
|
||||
.pref( uh.getActiveUser() != null
|
||||
? "userManagement"
|
||||
: "userManagementEmpty", true, null, null)
|
||||
.addComp(uh.createOverview().maxWidth(getCompWidth()))
|
||||
.nameAndDescription("syncTeamVaults")
|
||||
.addComp(new ButtonComp(AppI18n.observable("enableGitSync"), () -> AppPrefs.get()
|
||||
.selectCategory("vaultSync")))
|
||||
.licenseRequirement("team")
|
||||
.disable(!LicenseProvider.get().getFeature("team").isSupported())
|
||||
.hide(new SimpleBooleanProperty(
|
||||
DataStorageSyncHandler.getInstance().supportsSync()))
|
||||
.nameAndDescription(Bindings.createStringBinding(() -> {
|
||||
var empty = uh.getUserCount() == 0;
|
||||
if (prefs.vaultAuthentication.get() == VaultAuthentication.GROUP) {
|
||||
return empty ? "groupManagementEmpty" : "groupManagement";
|
||||
}
|
||||
|
||||
return empty ? "userManagementEmpty" : "userManagement";
|
||||
}, prefs.vaultAuthentication))
|
||||
.addComp(uh.createOverview().maxWidth(getCompWidth()))
|
||||
.pref(prefs.groupSecretStrategy)
|
||||
.addComp(OptionsChoiceBuilder.builder().property(prefs.groupSecretStrategy)
|
||||
.allowNull(false).available(DataStorageGroupStrategy.getClasses())
|
||||
.build().build().buildComp().maxWidth(getCompWidth()),
|
||||
prefs.groupSecretStrategy)
|
||||
.licenseRequirement("team")
|
||||
.hide(prefs.vaultAuthentication.isNotEqualTo(VaultAuthentication.GROUP))
|
||||
.nameAndDescription("syncVault")
|
||||
.addComp(new ButtonComp(AppI18n.observable("enableGitSync"), () -> AppPrefs.get()
|
||||
.selectCategory("vaultSync")))
|
||||
.hide(new SimpleBooleanProperty(
|
||||
DataStorageSyncHandler.getInstance().supportsSync()))
|
||||
.nameAndDescription("teamVaults")
|
||||
.addComp(Comp.empty())
|
||||
.licenseRequirement("team")
|
||||
.disable(!LicenseProvider.get().getFeature("team").isSupported())
|
||||
.hide(Bindings.createBooleanBinding(() -> {
|
||||
return uh.getUserCount() > 1 || !(prefs.groupSecretStrategy.get() instanceof DataStorageGroupStrategy.None);
|
||||
}, prefs.groupSecretStrategy))
|
||||
.hide(uh.getUserCount() > 1)
|
||||
);
|
||||
builder.sub(new OptionsBuilder().pref(prefs.encryptAllVaultData).addToggle(encryptVault));
|
||||
return builder.buildComp();
|
||||
|
||||
@@ -48,6 +48,10 @@ public interface DataStorageGroupStrategy {
|
||||
return l;
|
||||
}
|
||||
|
||||
default boolean requiresUnlock() {
|
||||
return true;
|
||||
}
|
||||
|
||||
default void checkComplete() throws ValidationException {}
|
||||
|
||||
byte[] queryEncryptionSecret() throws Exception;
|
||||
@@ -56,6 +60,11 @@ public interface DataStorageGroupStrategy {
|
||||
@Value
|
||||
public class None implements DataStorageGroupStrategy {
|
||||
|
||||
@Override
|
||||
public boolean requiresUnlock() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] queryEncryptionSecret() throws Exception {
|
||||
throw new UnsupportedOperationException();
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.xpipe.app.storage;
|
||||
|
||||
import io.xpipe.app.comp.Comp;
|
||||
import io.xpipe.app.ext.ProcessControlProvider;
|
||||
import io.xpipe.app.prefs.VaultAuthentication;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.crypto.SecretKey;
|
||||
@@ -25,4 +26,6 @@ public interface DataStorageUserHandler {
|
||||
Comp<?> createOverview();
|
||||
|
||||
String getActiveUser();
|
||||
|
||||
VaultAuthentication getVaultAuthenticationType();
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package io.xpipe.app.storage;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.xpipe.app.secret.EncryptionToken;
|
||||
|
||||
public enum DataStoreScope {
|
||||
|
||||
@JsonProperty("vault")
|
||||
VAULT() {
|
||||
@Override
|
||||
public EncryptionToken getToken() {
|
||||
return EncryptionToken.ofVaultKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "vault";
|
||||
}
|
||||
},
|
||||
@JsonProperty("group")
|
||||
GROUP() {
|
||||
@Override
|
||||
public EncryptionToken getToken() {
|
||||
return EncryptionToken.ofVaultKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "group";
|
||||
}
|
||||
},
|
||||
@JsonProperty("user")
|
||||
USER() {
|
||||
@Override
|
||||
public EncryptionToken getToken() {
|
||||
return EncryptionToken.ofVaultKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "user";
|
||||
}
|
||||
};
|
||||
|
||||
public abstract String getId();
|
||||
|
||||
public abstract EncryptionToken getToken();
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package io.xpipe.ext.base.identity;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.deser.std.DelegatingDeserializer;
|
||||
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
|
||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.fasterxml.jackson.databind.node.TreeTraversingParser;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class SyncedIdentityMigrationDeserializer extends DelegatingDeserializer {
|
||||
|
||||
public SyncedIdentityMigrationDeserializer(JsonDeserializer<?> d) {
|
||||
super(d);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonDeserializer<?> newDelegatingInstance(JsonDeserializer<?> newDelegatee) {
|
||||
return new SyncedIdentityMigrationDeserializer(newDelegatee);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
return super.deserialize(restructure(p), ctxt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object deserialize(JsonParser p, DeserializationContext ctxt, Object intoValue) throws IOException {
|
||||
return super.deserialize(restructure(p), ctxt, intoValue);
|
||||
}
|
||||
|
||||
public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer)
|
||||
throws IOException {
|
||||
return super.deserializeWithType(restructure(jp), ctxt, typeDeserializer);
|
||||
}
|
||||
|
||||
public JsonParser restructure(JsonParser p) throws IOException {
|
||||
var node = p.readValueAsTree();
|
||||
if (node == null) {
|
||||
return p;
|
||||
}
|
||||
|
||||
if (!node.isObject()) {
|
||||
var newJsonParser = new TreeTraversingParser((ObjectNode) node, p.getCodec());
|
||||
newJsonParser.nextToken();
|
||||
return newJsonParser;
|
||||
}
|
||||
|
||||
migrate((ObjectNode) node);
|
||||
var newJsonParser = new TreeTraversingParser((ObjectNode) node, p.getCodec());
|
||||
newJsonParser.nextToken();
|
||||
return newJsonParser;
|
||||
}
|
||||
|
||||
private void migrate(ObjectNode containerNode) {
|
||||
if (containerNode.has("perUser")) {
|
||||
containerNode.remove("perUser");
|
||||
containerNode.put("scope", "user");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,6 @@ public class SyncedIdentityStore extends IdentityStore implements UserScopeStore
|
||||
// per user stores are additionally encrypted on the entry level
|
||||
EncryptedValue.VaultKey<SecretRetrievalStrategy> password;
|
||||
EncryptedValue.VaultKey<SshIdentityStrategy> sshIdentity;
|
||||
DataStoreScope scope;
|
||||
boolean perUser;
|
||||
|
||||
public UsernameStrategy.Fixed getUsername() {
|
||||
|
||||
27
lang/strings/translations_en.properties
generated
27
lang/strings/translations_en.properties
generated
@@ -570,6 +570,8 @@ identitiesIntroBottomContent=You can add identities locally or also sync them up
|
||||
identitiesIntroBottomButton=Setup sync
|
||||
identitiesIntroButton=Create identity
|
||||
userName=Username
|
||||
userAuth=User-based password authentication
|
||||
groupAuth=Group-based secret authentication
|
||||
team=Team
|
||||
teamSettings=Team settings
|
||||
teamVaults=Team vaults
|
||||
@@ -585,6 +587,10 @@ vaultTypeContentLegacy=You are currently using a legacy personal vault for your
|
||||
vaultTypeContentPersonal=You are currently using a personal vault for your user. Secrets are encrypted with your personal passphrase. You can upgrade to a team vault by adding additional vault users or add a group-based access configuration.
|
||||
#force
|
||||
vaultTypeContentTeam=You are currently using a team vault, which allows multiple users to have secure access to a shared vault. You can configure connections and identities to either be shared for all users or only have them available for your personal user or group by encrypting them with your personal or group key. Other vault users can't access your personal and group-based connections and identities if they don't have access to the key.
|
||||
groupManagement=Group management
|
||||
groupManagementEmpty=Group management
|
||||
groupManagementDescription=Manage existing vault groups or create new ones. Each vault group has its own individual secret key which is used to encrypt connections and identities that should only be available to the group and not to others.
|
||||
groupManagementEmptyDescription=Manage existing vault groups or create new ones. Each vault group has its own individual secret key which is used to encrypt connections and identities that should only be available to the group and not to others.\n\nGroup-based accounts for a team are supported in the professional plan.
|
||||
#force
|
||||
userManagement=User-based access control
|
||||
userManagementEmpty=User-based access control
|
||||
@@ -596,8 +602,8 @@ userIntroHeader=User management
|
||||
userIntroContent=Create the first user account for yourself to get started. This allows you to lock this workspace with a password.
|
||||
addReusableIdentity=Add reusable identity
|
||||
users=Users
|
||||
syncTeamVaults=Team vault synchronization
|
||||
syncTeamVaultsDescription=To synchronize your vault with multiple team members, enable the git synchronization.
|
||||
syncVault=Vault synchronization
|
||||
syncVaultDescription=To synchronize your vault with across multiple systems or with multiple team members, enable the git synchronization for this vault.
|
||||
enableGitSync=Enable git sync
|
||||
browseVault=Vault data
|
||||
browseVaultDescription=You can take a look at the vault directory yourself in your native file manager. Note that external edits are not recommended and can cause a variety of issues.
|
||||
@@ -615,6 +621,8 @@ loadingUserInterface=Loading user interface ...
|
||||
ptbNotice=Notice for the public test build
|
||||
userDeletionTitle=User deletion
|
||||
userDeletionContent=Do you want to delete this vault user? This will reencrypt all your personal identities and connection secrets using the vault key that is available to all users. XPipe will restart to apply the user changes.
|
||||
groupDeletionTitle=Group deletion
|
||||
groupDeletionContent=Do you want to delete this vault group? This will reencrypt all group-only identities and connection secrets using the vault key that is available to all users. XPipe will restart to apply the group changes.
|
||||
killTransfer=Kill transfer
|
||||
destination=Destination
|
||||
configuration=Configuration
|
||||
@@ -1217,20 +1225,25 @@ libvirt=libvirt domains
|
||||
customIp=Custom IP
|
||||
customIpDescription=Override the default local VM IP detection if you use advanced networking
|
||||
automaticallyDetect=Automatically detect
|
||||
lockCreationAlertTitle=User creation
|
||||
userAddDialogTitle=User creation
|
||||
groupAddDialogTitle=Group creation
|
||||
passphrase=Passphrase
|
||||
repeatPassphrase=Repeat passphrase
|
||||
lockCreationAlertHeader=Create new vault user
|
||||
groupSecret=Group secret
|
||||
repeatGroupSecret=Repeat group secret
|
||||
vaultGroup=Vault group
|
||||
loginAlertTitle=Login required
|
||||
loginAlertHeader=Unlock vault to access your personal connections
|
||||
vaultUser=Vault user
|
||||
#context: dative case
|
||||
me=Me
|
||||
addGroup=Add group ...
|
||||
addGroupDescription=Create a new group for this vault
|
||||
addUser=Add user ...
|
||||
addUserDescription=Create a new user for this vault
|
||||
skip=Skip
|
||||
userChangePasswordAlertTitle=Password change
|
||||
userChangePasswordAlertHeader=Set new password for user
|
||||
groupChangeSecretAlertTitle=Secret change
|
||||
docs=Documentation
|
||||
lxd.displayName=LXD Container
|
||||
lxd.displayDescription=Connect to a LXD container via lxc
|
||||
@@ -1702,7 +1715,7 @@ openSftp=Open in SFTP session
|
||||
capslockWarning=You have capslock enabled
|
||||
inherit=Inherit
|
||||
groupSecretStrategy=Group-based access control
|
||||
groupSecretStrategyDescription=In addition to individual users, you can also configure group-based encryption for connections and identities, meaning that people of a certain group can share access to them.\n\nHere you can choose how to retrieve the group secret used for encryption. The retrieval method you choose will be run when a user logs into the vault. Their access level to certain connections is determined by the raw key that the retrieval method returns.
|
||||
groupSecretStrategyDescription=Here you can choose how to retrieve the group secret used for encryption. The retrieval method you choose will be run when a user logs into the vault. Their access level to certain connections is determined by the raw key that the retrieval method returns.
|
||||
fileSecret=File-based secret
|
||||
commandSecret=Secret retrieval command
|
||||
httpRequestSecret=Secret HTTP response
|
||||
@@ -1711,3 +1724,5 @@ fileSecretChoiceDescription=The path to the file containing the group encryption
|
||||
commandSecretField=Retrieval script
|
||||
commandSecretFieldDescription=The command or script that will return the secret encryption key for the current group. The key should be printed to stdout.
|
||||
httpRequestSecretField=Request URI
|
||||
vaultAuthentication=Vault authentication
|
||||
vaultAuthenticationDescription=How to authenticate / unlock the vault data. There are multiple different ways of encrypting and unlocking vault data, depending on who you want to share the vault data with.
|
||||
|
||||
Reference in New Issue
Block a user