Rework encryption

This commit is contained in:
crschnick
2025-01-07 10:10:12 +00:00
parent c3165b806c
commit 1090c8d0d6
23 changed files with 300 additions and 156 deletions

View File

@@ -2,6 +2,7 @@ package io.xpipe.ext.base.identity;
import io.xpipe.app.ext.ShellStore;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.util.EncryptedValue;
import io.xpipe.app.util.OptionsBuilder;
import io.xpipe.app.util.SecretRetrievalStrategyHelper;
@@ -58,10 +59,10 @@ public class IdentityChoice {
var options = new OptionsBuilder()
.nameAndDescription(userChoiceTranslationKey)
.addComp(new IdentitySelectComp(ref, user, pass, identityStrategy, allowCustomUserInput), user)
.nonNullIf(inPlaceSelected.and(new SimpleBooleanProperty(requireUserInput)))
.nonNullIf(inPlaceSelected)
.nameAndDescription(passwordChoiceTranslationKey)
.sub(SecretRetrievalStrategyHelper.comp(pass, true, true), pass)
.nonNullIf(inPlaceSelected.and(new SimpleBooleanProperty(requirePassword)))
.sub(SecretRetrievalStrategyHelper.comp(pass, true), pass)
.nonNullIf(inPlaceSelected)
.disable(refSelected)
.hide(refSelected)
.name("keyAuthentication")
@@ -72,8 +73,7 @@ public class IdentityChoice {
gateway != null ? gateway : new SimpleObjectProperty<>(),
identityStrategy,
null,
false,
true),
false),
identityStrategy)
.nonNullIf(inPlaceSelected.and(new SimpleBooleanProperty(keyInput)))
.disable(refSelected)
@@ -89,8 +89,8 @@ public class IdentityChoice {
return IdentityValue.InPlace.builder()
.identityStore(LocalIdentityStore.builder()
.username(user.get())
.password(pass.get())
.sshIdentity(identityStrategy.get())
.password(EncryptedValue.CurrentKey.of(pass.get()))
.sshIdentity(EncryptedValue.CurrentKey.of(identityStrategy.get()))
.build())
.build();
}

View File

@@ -71,8 +71,6 @@ public class IdentityMigrationDeserializer extends DelegatingDeserializer {
identityStore.put("type", "localIdentity");
if (user != null && user.isTextual()) {
identityStore.set("username", user);
} else {
int a = 0;
}
identityStore.set("password", password);
identityStore.set("sshIdentity", identity);

View File

@@ -15,6 +15,7 @@ import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.util.EncryptedValue;
import io.xpipe.app.util.PlatformThread;
import io.xpipe.app.util.SecretRetrievalStrategy;
@@ -63,13 +64,13 @@ public class IdentitySelectComp extends Comp<CompStructure<HBox>> {
var id = canSync
? SyncedIdentityStore.builder()
.username(inPlaceUser.getValue())
.password(password.getValue())
.sshIdentity(identityStrategy.getValue())
.password(EncryptedValue.VaultKey.of(password.getValue()))
.sshIdentity(EncryptedValue.VaultKey.of(identityStrategy.getValue()))
.build()
: LocalIdentityStore.builder()
.username(inPlaceUser.getValue())
.password(password.getValue())
.sshIdentity(identityStrategy.getValue())
.password(EncryptedValue.CurrentKey.of(password.getValue()))
.sshIdentity(EncryptedValue.CurrentKey.of(identityStrategy.getValue()))
.build();
StoreCreationComp.showCreation(
id,

View File

@@ -16,16 +16,16 @@ import lombok.experimental.SuperBuilder;
public abstract class IdentityStore implements SelfReferentialStore, DataStore {
String username;
SecretRetrievalStrategy password;
SshIdentityStrategy sshIdentity;
public abstract SecretRetrievalStrategy getPassword();
public abstract SshIdentityStrategy getSshIdentity();
@Override
public void checkComplete() throws Throwable {
if (password != null) {
password.checkComplete();
}
if (sshIdentity != null) {
sshIdentity.checkComplete();
}
Validators.nonNull(getPassword());
Validators.nonNull(getSshIdentity());
getPassword().checkComplete();
getSshIdentity().checkComplete();
}
}

View File

@@ -21,7 +21,7 @@ public interface IdentityValue {
return new InPlace(identityStore);
}
void checkComplete(boolean requireUser, boolean requirePassword, boolean requireKey) throws Throwable;
void checkComplete(boolean requireUser) throws Throwable;
IdentityStore unwrap();
@@ -36,24 +36,12 @@ public interface IdentityValue {
LocalIdentityStore identityStore;
@Override
public void checkComplete(boolean requireUser, boolean requirePassword, boolean requireKey) throws Throwable {
public void checkComplete(boolean requireUser) throws Throwable {
Validators.nonNull(identityStore);
identityStore.checkComplete();
if (requireUser) {
Validators.nonNull(identityStore.getUsername());
}
if (requirePassword) {
Validators.nonNull(identityStore.getPassword());
}
if (identityStore.getPassword() != null) {
identityStore.getPassword().checkComplete();
}
if (requireKey) {
Validators.nonNull(identityStore.getSshIdentity());
}
if (identityStore.getSshIdentity() != null) {
identityStore.getSshIdentity().checkComplete();
}
identityStore.checkComplete();
}
@Override
@@ -76,25 +64,13 @@ public interface IdentityValue {
DataStoreEntryRef<IdentityStore> ref;
@Override
public void checkComplete(boolean requireUser, boolean requirePassword, boolean requireKey) throws Throwable {
public void checkComplete(boolean requireUser) throws Throwable {
Validators.nonNull(ref);
Validators.isType(ref, IdentityStore.class);
ref.getStore().checkComplete();
if (requireUser) {
Validators.nonNull(ref.getStore().getUsername());
}
if (requirePassword) {
Validators.nonNull(ref.getStore().getPassword());
}
if (ref.getStore().getPassword() != null) {
ref.getStore().getPassword().checkComplete();
}
if (requireKey) {
Validators.nonNull(ref.getStore().getSshIdentity());
}
if (ref.getStore().getSshIdentity() != null) {
ref.getStore().getSshIdentity().checkComplete();
}
ref.getStore().checkComplete();
}
@Override

View File

@@ -7,6 +7,7 @@ import io.xpipe.app.ext.DataStoreCreationCategory;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.util.EncryptedValue;
import javafx.beans.value.ObservableValue;
import lombok.Value;
@@ -58,8 +59,9 @@ public class LocalIdentityConvertAction implements ActionProvider {
var st = ref.getStore();
var synced = SyncedIdentityStore.builder()
.username(st.getUsername())
.password(st.getPassword())
.sshIdentity(st.getSshIdentity())
.password(EncryptedValue.VaultKey.of(st.getPassword()))
.sshIdentity(EncryptedValue.VaultKey.of(st.getSshIdentity()))
.perUser(false)
.build();
StoreCreationComp.showCreation(synced, DataStoreCreationCategory.IDENTITY, dataStoreEntry -> {}, true);
}

View File

@@ -1,6 +1,10 @@
package io.xpipe.ext.base.identity;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.app.util.EncryptedValue;
import io.xpipe.app.util.SecretRetrievalStrategy;
import io.xpipe.app.util.Validators;
import io.xpipe.core.util.ValidationException;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.Value;
@@ -13,4 +17,18 @@ import lombok.extern.jackson.Jacksonized;
@Value
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class LocalIdentityStore extends IdentityStore {}
public class LocalIdentityStore extends IdentityStore {
EncryptedValue.CurrentKey<SecretRetrievalStrategy> password;
EncryptedValue.CurrentKey<SshIdentityStrategy> sshIdentity;
@Override
public SecretRetrievalStrategy getPassword() {
return password != null ? password.getValue() : null;
}
@Override
public SshIdentityStrategy getSshIdentity() {
return sshIdentity != null ? sshIdentity.getValue() : null;
}
}

View File

@@ -4,6 +4,7 @@ import io.xpipe.app.comp.store.StoreEntryWrapper;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.GuiDialog;
import io.xpipe.app.storage.*;
import io.xpipe.app.util.EncryptedValue;
import io.xpipe.app.util.OptionsBuilder;
import io.xpipe.app.util.SecretRetrievalStrategyHelper;
import io.xpipe.core.store.DataStore;
@@ -39,19 +40,19 @@ public class LocalIdentityStoreProvider extends IdentityStoreProvider {
.addString(user)
.name("passwordAuthentication")
.description("passwordAuthenticationDescription")
.sub(SecretRetrievalStrategyHelper.comp(pass, true, true), pass)
.sub(SecretRetrievalStrategyHelper.comp(pass, true), pass)
.name("keyAuthentication")
.description("keyAuthenticationDescription")
.longDescription("base:sshKey")
.sub(
SshIdentityStrategyHelper.identity(new SimpleObjectProperty<>(), identity, null, false, true),
SshIdentityStrategyHelper.identity(new SimpleObjectProperty<>(), identity, null, false),
identity)
.bind(
() -> {
return LocalIdentityStore.builder()
.username(user.get())
.password(pass.get())
.sshIdentity(identity.get())
.password(EncryptedValue.CurrentKey.of(pass.get()))
.sshIdentity(EncryptedValue.CurrentKey.of(identity.get()))
.build();
},
store)

View File

@@ -49,8 +49,7 @@ public class SshIdentityStrategyHelper {
Property<DataStoreEntryRef<ShellStore>> proxy,
Property<SshIdentityStrategy.File> fileProperty,
Predicate<Path> perUserFile,
boolean allowSync,
boolean allowUserSecretKey) {
boolean allowSync) {
var keyPath = new SimpleStringProperty(
fileProperty.getValue() != null && fileProperty.getValue().getFile() != null
? fileProperty.getValue().getFile().toAbsoluteFilePath(null)
@@ -77,7 +76,7 @@ public class SshIdentityStrategyHelper {
.name("keyPassword")
.description("sshConfigHost.identityPassphraseDescription")
.sub(
SecretRetrievalStrategyHelper.comp(keyPasswordProperty, true, allowUserSecretKey),
SecretRetrievalStrategyHelper.comp(keyPasswordProperty, true),
keyPasswordProperty)
.nonNull()
.bind(
@@ -92,8 +91,7 @@ public class SshIdentityStrategyHelper {
Property<DataStoreEntryRef<ShellStore>> proxy,
Property<SshIdentityStrategy> strategyProperty,
Predicate<Path> perUserFile,
boolean allowSync,
boolean allowUserSecretKey) {
boolean allowSync) {
SshIdentityStrategy strat = strategyProperty.getValue();
var file = new SimpleObjectProperty<>(
strat instanceof SshIdentityStrategy.File f
@@ -110,7 +108,7 @@ public class SshIdentityStrategyHelper {
map.put(AppI18n.observable("base.none"), new OptionsBuilder());
map.put(
AppI18n.observable("base.keyFile"),
fileIdentity(proxy, file, perUserFile, allowSync, allowUserSecretKey));
fileIdentity(proxy, file, perUserFile, allowSync));
map.put(AppI18n.observable("base.sshAgent"), agent(agent));
map.put(AppI18n.observable("base.pageant"), new OptionsBuilder());
map.put(gpgFeature.suffixObservable("base.gpgAgent"), new OptionsBuilder());

View File

@@ -1,6 +1,9 @@
package io.xpipe.ext.base.identity;
import io.xpipe.app.ext.UserScopeStore;
import io.xpipe.app.util.EncryptedValue;
import io.xpipe.app.util.SecretRetrievalStrategy;
import io.xpipe.app.util.Validators;
import io.xpipe.core.util.ValidationException;
import com.fasterxml.jackson.annotation.JsonTypeName;
@@ -18,8 +21,20 @@ import lombok.extern.jackson.Jacksonized;
@ToString(callSuper = true)
public class SyncedIdentityStore extends IdentityStore implements UserScopeStore {
EncryptedValue<SecretRetrievalStrategy> password;
EncryptedValue<SshIdentityStrategy> sshIdentity;
boolean perUser;
@Override
public SecretRetrievalStrategy getPassword() {
return password != null ? password.getValue() : null;
}
@Override
public SshIdentityStrategy getSshIdentity() {
return sshIdentity != null ? sshIdentity.getValue() : null;
}
@Override
public void checkComplete() throws Throwable {
super.checkComplete();

View File

@@ -5,6 +5,7 @@ import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.DataStoreCreationCategory;
import io.xpipe.app.ext.GuiDialog;
import io.xpipe.app.storage.*;
import io.xpipe.app.util.EncryptedValue;
import io.xpipe.app.util.OptionsBuilder;
import io.xpipe.app.util.SecretRetrievalStrategyHelper;
import io.xpipe.app.util.Validator;
@@ -59,13 +60,13 @@ public class SyncedIdentityStoreProvider extends IdentityStoreProvider {
.addString(user)
.name("passwordAuthentication")
.description("passwordAuthenticationDescription")
.sub(SecretRetrievalStrategyHelper.comp(pass, true, true), pass)
.sub(SecretRetrievalStrategyHelper.comp(pass, true), pass)
.name("keyAuthentication")
.description("keyAuthenticationDescription")
.longDescription("base:sshKey")
.sub(
SshIdentityStrategyHelper.identity(
new SimpleObjectProperty<>(), identity, path -> perUser.get(), true, true),
new SimpleObjectProperty<>(), identity, path -> perUser.get(), true),
identity)
.check(val -> Validator.create(val, AppI18n.observable("keyNotSynced"), identity, i -> {
var wrong = i instanceof SshIdentityStrategy.File f
@@ -83,8 +84,8 @@ public class SyncedIdentityStoreProvider extends IdentityStoreProvider {
() -> {
return SyncedIdentityStore.builder()
.username(user.get())
.password(pass.get())
.sshIdentity(identity.get())
.password(perUser.get() ? EncryptedValue.CurrentKey.of(pass.get()) : EncryptedValue.VaultKey.of(pass.get()))
.sshIdentity(perUser.get() ? EncryptedValue.CurrentKey.of(identity.get()) : EncryptedValue.VaultKey.of(identity.get()))
.perUser(perUser.get())
.build();
},

View File

@@ -54,7 +54,7 @@ public class IncusContainerStore
install.checkComplete();
Validators.nonNull(containerName);
if (identity != null) {
identity.checkComplete(false, false, false);
identity.checkComplete(false);
}
}

View File

@@ -52,7 +52,7 @@ public class LxdContainerStore
cmd.checkComplete();
Validators.nonNull(containerName);
if (identity != null) {
identity.checkComplete(false, false, false);
identity.checkComplete(false);
}
}