Persist vault binding refs in recent remotes
This commit is contained in:
@@ -154,6 +154,9 @@ type recentVaultRecord struct {
|
|||||||
type recentRemoteRecord struct {
|
type recentRemoteRecord struct {
|
||||||
BaseURL string `json:"baseUrl"`
|
BaseURL string `json:"baseUrl"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
|
LocalVaultPath string `json:"localVaultPath,omitempty"`
|
||||||
|
RemoteProfileID string `json:"remoteProfileId,omitempty"`
|
||||||
|
CredentialEntryID string `json:"credentialEntryId,omitempty"`
|
||||||
LastGroup []string `json:"lastGroup,omitempty"`
|
LastGroup []string `json:"lastGroup,omitempty"`
|
||||||
UsedAt string `json:"usedAt,omitempty"`
|
UsedAt string `json:"usedAt,omitempty"`
|
||||||
}
|
}
|
||||||
@@ -1077,6 +1080,7 @@ func (u *ui) openRemoteAction() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if u.hasOpenVault() {
|
||||||
if binding, resolved, ok, err := u.resolvedSelectedVaultRemoteBinding(); err != nil {
|
if binding, resolved, ok, err := u.resolvedSelectedVaultRemoteBinding(); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if ok {
|
} else if ok {
|
||||||
@@ -1093,6 +1097,7 @@ func (u *ui) openRemoteAction() error {
|
|||||||
u.filter()
|
u.filter()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
client := webdav.Client{
|
client := webdav.Client{
|
||||||
BaseURL: strings.TrimSpace(u.remoteBaseURL.Text()),
|
BaseURL: strings.TrimSpace(u.remoteBaseURL.Text()),
|
||||||
Username: strings.TrimSpace(u.remoteUsername.Text()),
|
Username: strings.TrimSpace(u.remoteUsername.Text()),
|
||||||
@@ -1132,6 +1137,7 @@ func (u *ui) startOpenRemoteAction() {
|
|||||||
Password: u.remotePassword.Text(),
|
Password: u.remotePassword.Text(),
|
||||||
}
|
}
|
||||||
remotePath := strings.TrimSpace(u.remotePath.Text())
|
remotePath := strings.TrimSpace(u.remotePath.Text())
|
||||||
|
if u.hasOpenVault() {
|
||||||
if _, resolved, ok, err := u.resolvedSelectedVaultRemoteBinding(); err != nil {
|
if _, resolved, ok, err := u.resolvedSelectedVaultRemoteBinding(); err != nil {
|
||||||
u.state.ErrorMessage = u.describeActionError("open remote vault", err)
|
u.state.ErrorMessage = u.describeActionError("open remote vault", err)
|
||||||
u.requestMasterPassFocus = true
|
u.requestMasterPassFocus = true
|
||||||
@@ -1146,6 +1152,7 @@ func (u *ui) startOpenRemoteAction() {
|
|||||||
u.remoteBaseURL.SetText(resolved.Profile.BaseURL)
|
u.remoteBaseURL.SetText(resolved.Profile.BaseURL)
|
||||||
u.remotePath.SetText(resolved.Profile.Path)
|
u.remotePath.SetText(resolved.Profile.Path)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
u.lastLifecycleAction = "open remote vault"
|
u.lastLifecycleAction = "open remote vault"
|
||||||
u.runBackgroundAction("open remote vault", func() (func() error, error) {
|
u.runBackgroundAction("open remote vault", func() (func() error, error) {
|
||||||
prepared, err := session.PrepareRemoteOpen(client, remotePath, key)
|
prepared, err := session.PrepareRemoteOpen(client, remotePath, key)
|
||||||
@@ -1518,6 +1525,9 @@ func (u *ui) loadRecentRemotes() {
|
|||||||
for _, record := range records {
|
for _, record := range records {
|
||||||
record.BaseURL = strings.TrimSpace(record.BaseURL)
|
record.BaseURL = strings.TrimSpace(record.BaseURL)
|
||||||
record.Path = strings.TrimSpace(record.Path)
|
record.Path = strings.TrimSpace(record.Path)
|
||||||
|
record.LocalVaultPath = strings.TrimSpace(record.LocalVaultPath)
|
||||||
|
record.RemoteProfileID = strings.TrimSpace(record.RemoteProfileID)
|
||||||
|
record.CredentialEntryID = strings.TrimSpace(record.CredentialEntryID)
|
||||||
if record.BaseURL == "" || record.Path == "" {
|
if record.BaseURL == "" || record.Path == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -1750,6 +1760,11 @@ func (u *ui) noteRecentRemote(baseURL, path string) {
|
|||||||
LastGroup: append([]string(nil), u.currentPath...),
|
LastGroup: append([]string(nil), u.currentPath...),
|
||||||
UsedAt: u.now().Format(time.RFC3339Nano),
|
UsedAt: u.now().Format(time.RFC3339Nano),
|
||||||
}
|
}
|
||||||
|
if binding, ok := u.selectedVaultRemoteBinding(); ok {
|
||||||
|
record.LocalVaultPath = binding.LocalVaultPath
|
||||||
|
record.RemoteProfileID = binding.RemoteProfileID
|
||||||
|
record.CredentialEntryID = binding.CredentialEntryID
|
||||||
|
}
|
||||||
if len(record.LastGroup) == 0 {
|
if len(record.LastGroup) == 0 {
|
||||||
record.LastGroup = u.recentRemoteGroup(baseURL, path)
|
record.LastGroup = u.recentRemoteGroup(baseURL, path)
|
||||||
}
|
}
|
||||||
@@ -1905,6 +1920,9 @@ func (u *ui) selectedRecentRemoteRecord() (recentRemoteRecord, bool) {
|
|||||||
func (u *ui) applyRecentRemoteRecord(record recentRemoteRecord) {
|
func (u *ui) applyRecentRemoteRecord(record recentRemoteRecord) {
|
||||||
u.remoteBaseURL.SetText(record.BaseURL)
|
u.remoteBaseURL.SetText(record.BaseURL)
|
||||||
u.remotePath.SetText(record.Path)
|
u.remotePath.SetText(record.Path)
|
||||||
|
u.vaultPath.SetText(strings.TrimSpace(record.LocalVaultPath))
|
||||||
|
u.selectedVaultRemoteProfileID = strings.TrimSpace(record.RemoteProfileID)
|
||||||
|
u.selectedVaultRemoteCredentialEntryID = strings.TrimSpace(record.CredentialEntryID)
|
||||||
u.remotePassword.Mask = '•'
|
u.remotePassword.Mask = '•'
|
||||||
u.selectedRemoteConnection = true
|
u.selectedRemoteConnection = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4018,6 +4018,40 @@ func TestUIRecentRemoteConnectionsPersistAndReload(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUIRecentRemoteConnectionsPersistVaultBindingMetadata(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
configPath := filepath.Join(t.TempDir(), "recent-remotes.json")
|
||||||
|
|
||||||
|
first := newUIWithSession("desktop", &session.Manager{})
|
||||||
|
first.recentRemotesPath = configPath
|
||||||
|
first.recentRemotes = nil
|
||||||
|
first.currentPath = []string{"Root", "Internet"}
|
||||||
|
first.vaultPath.SetText("/vaults/family.kdbx")
|
||||||
|
first.selectedVaultRemoteProfileID = "remote-profile-1"
|
||||||
|
first.selectedVaultRemoteCredentialEntryID = "remote-creds-1"
|
||||||
|
first.noteRecentRemote("https://dav.example.com", "vaults/home.kdbx")
|
||||||
|
|
||||||
|
second := newUIWithSession("desktop", &session.Manager{})
|
||||||
|
second.recentRemotesPath = configPath
|
||||||
|
second.recentRemotes = nil
|
||||||
|
second.loadRecentRemotes()
|
||||||
|
|
||||||
|
if got := len(second.recentRemotes); got != 1 {
|
||||||
|
t.Fatalf("len(recentRemotes) = %d, want 1", got)
|
||||||
|
}
|
||||||
|
record := second.recentRemotes[0]
|
||||||
|
if record.LocalVaultPath != "/vaults/family.kdbx" {
|
||||||
|
t.Fatalf("recentRemotes[0].LocalVaultPath = %q, want /vaults/family.kdbx", record.LocalVaultPath)
|
||||||
|
}
|
||||||
|
if record.RemoteProfileID != "remote-profile-1" {
|
||||||
|
t.Fatalf("recentRemotes[0].RemoteProfileID = %q, want remote-profile-1", record.RemoteProfileID)
|
||||||
|
}
|
||||||
|
if record.CredentialEntryID != "remote-creds-1" {
|
||||||
|
t.Fatalf("recentRemotes[0].CredentialEntryID = %q, want remote-creds-1", record.CredentialEntryID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUILoadRecentRemotesIgnoresLegacySavedCredentials(t *testing.T) {
|
func TestUILoadRecentRemotesIgnoresLegacySavedCredentials(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -4048,6 +4082,30 @@ func TestUILoadRecentRemotesIgnoresLegacySavedCredentials(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUIApplyRecentRemoteRecordRestoresVaultBindingSelection(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
u := newUIWithSession("desktop", &session.Manager{})
|
||||||
|
|
||||||
|
u.applyRecentRemoteRecord(recentRemoteRecord{
|
||||||
|
BaseURL: "https://dav.example.com",
|
||||||
|
Path: "vaults/home.kdbx",
|
||||||
|
LocalVaultPath: "/vaults/family.kdbx",
|
||||||
|
RemoteProfileID: "remote-profile-1",
|
||||||
|
CredentialEntryID: "remote-creds-1",
|
||||||
|
})
|
||||||
|
|
||||||
|
if got := u.vaultPath.Text(); got != "/vaults/family.kdbx" {
|
||||||
|
t.Fatalf("vaultPath = %q, want /vaults/family.kdbx", got)
|
||||||
|
}
|
||||||
|
if got := u.selectedVaultRemoteProfileID; got != "remote-profile-1" {
|
||||||
|
t.Fatalf("selectedVaultRemoteProfileID = %q, want remote-profile-1", got)
|
||||||
|
}
|
||||||
|
if got := u.selectedVaultRemoteCredentialEntryID; got != "remote-creds-1" {
|
||||||
|
t.Fatalf("selectedVaultRemoteCredentialEntryID = %q, want remote-creds-1", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUIStartupPreselectsNewestTargetAcrossLocalAndRemote(t *testing.T) {
|
func TestUIStartupPreselectsNewestTargetAcrossLocalAndRemote(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user