Persist remote binding after setup push
This commit is contained in:
@@ -1523,14 +1523,21 @@ func (u *ui) startImportSharedVaultAction() {
|
||||
func (u *ui) advancedSyncToAction() error {
|
||||
switch u.syncSourceMode {
|
||||
case syncSourceRemote:
|
||||
baseURL := strings.TrimSpace(u.syncRemoteBaseURL.Text())
|
||||
remotePath := strings.TrimSpace(u.syncRemotePath.Text())
|
||||
client := webdav.Client{
|
||||
BaseURL: strings.TrimSpace(u.syncRemoteBaseURL.Text()),
|
||||
BaseURL: baseURL,
|
||||
Username: strings.TrimSpace(u.syncRemoteUsername.Text()),
|
||||
Password: u.syncRemotePassword.Text(),
|
||||
}
|
||||
if err := u.state.SynchronizeToRemote(client, strings.TrimSpace(u.syncRemotePath.Text())); err != nil {
|
||||
if err := u.state.SynchronizeToRemote(client, remotePath); err != nil {
|
||||
return err
|
||||
}
|
||||
if u.syncDialogPurpose == syncDialogPurposeRemoteSetup {
|
||||
if err := u.persistSyncDialogRemoteBinding(baseURL, remotePath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
path := strings.TrimSpace(u.syncLocalPath.Text())
|
||||
if path == "" {
|
||||
@@ -1546,6 +1553,45 @@ func (u *ui) advancedSyncToAction() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ui) persistSyncDialogRemoteBinding(baseURL, remotePath string) error {
|
||||
baseURL = strings.TrimSpace(baseURL)
|
||||
remotePath = strings.TrimSpace(remotePath)
|
||||
if baseURL == "" || remotePath == "" {
|
||||
return fmt.Errorf("remote setup requires base URL and path")
|
||||
}
|
||||
input := appstate.RemoteBindingInput{
|
||||
LocalVaultPath: strings.TrimSpace(u.vaultPath.Text()),
|
||||
RemoteProfileID: "remote-profile-" + remoteBindingSuffix(baseURL, remotePath, strings.TrimSpace(u.syncRemoteUsername.Text())),
|
||||
RemoteProfileName: friendlyRecentRemoteLabel(recentRemoteRecord{BaseURL: baseURL, Path: remotePath}),
|
||||
BaseURL: baseURL,
|
||||
RemotePath: remotePath,
|
||||
CredentialEntryID: "remote-credential-" + remoteBindingSuffix(baseURL, remotePath, strings.TrimSpace(u.syncRemoteUsername.Text())),
|
||||
CredentialTitle: "WebDAV Sign-In" + func() string {
|
||||
if user := strings.TrimSpace(u.syncRemoteUsername.Text()); user != "" {
|
||||
return " · " + user
|
||||
}
|
||||
return ""
|
||||
}(),
|
||||
Username: strings.TrimSpace(u.syncRemoteUsername.Text()),
|
||||
Password: u.syncRemotePassword.Text(),
|
||||
CredentialPath: append([]string(nil), u.currentPath...),
|
||||
SyncMode: u.newRemoteBindingSyncMode(),
|
||||
}
|
||||
binding, err := u.state.ConfigureRemoteBinding(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.selectedVaultRemoteProfileID = binding.RemoteProfileID
|
||||
u.selectedVaultRemoteCredentialEntryID = binding.CredentialEntryID
|
||||
u.selectedVaultRemoteSyncMode = binding.SyncMode
|
||||
u.remoteBaseURL.SetText(baseURL)
|
||||
u.remotePath.SetText(remotePath)
|
||||
u.remoteUsername.SetText(strings.TrimSpace(u.syncRemoteUsername.Text()))
|
||||
u.remotePassword.SetText(u.syncRemotePassword.Text())
|
||||
u.noteRecentRemote(baseURL, remotePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ui) saveAsTargetPath() string {
|
||||
path := strings.TrimSpace(u.saveAsPath.Text())
|
||||
if path != "" {
|
||||
|
||||
@@ -5974,6 +5974,72 @@ func TestUISyncDialogUsesAdvancedCopy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUIRemoteSyncSetupPersistsBindingAfterSuccessfulPush(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
key := vault.MasterKey{Password: "correct horse battery staple"}
|
||||
currentPath := filepath.Join(t.TempDir(), "current.kdbx")
|
||||
writeKDBXMainTestFile(t, currentPath, vault.Model{
|
||||
Entries: []vault.Entry{{
|
||||
ID: "entry-current",
|
||||
Title: "Vault Console",
|
||||
Username: "dannyocean",
|
||||
Password: "token-current",
|
||||
URL: "https://vault.crew.example.invalid",
|
||||
Path: []string{"Root", "Internet"},
|
||||
}},
|
||||
}, key)
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
case http.MethodPut:
|
||||
w.Header().Set("ETag", "\"v1\"")
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
default:
|
||||
t.Fatalf("unexpected method %s", r.Method)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
u := newUIWithSession("desktop", &session.Manager{})
|
||||
u.masterPassword.SetText(key.Password)
|
||||
u.vaultPath.SetText(currentPath)
|
||||
if err := u.openVaultAction(); err != nil {
|
||||
t.Fatalf("openVaultAction() error = %v", err)
|
||||
}
|
||||
|
||||
u.openRemoteSyncSetupDialog()
|
||||
u.syncRemoteBaseURL.SetText(server.URL)
|
||||
u.syncRemotePath.SetText("vaults/other.kdbx")
|
||||
u.syncRemoteUsername.SetText("tjulian")
|
||||
u.syncRemotePassword.SetText("token-1")
|
||||
|
||||
if err := u.advancedSyncAction(); err != nil {
|
||||
t.Fatalf("advancedSyncAction() error = %v", err)
|
||||
}
|
||||
|
||||
if got := u.selectedVaultRemoteProfileID; got == "" {
|
||||
t.Fatal("selectedVaultRemoteProfileID = empty, want saved binding")
|
||||
}
|
||||
if got := u.selectedVaultRemoteCredentialEntryID; got == "" {
|
||||
t.Fatal("selectedVaultRemoteCredentialEntryID = empty, want saved credential binding")
|
||||
}
|
||||
if got := u.remoteBaseURL.Text(); got != server.URL {
|
||||
t.Fatalf("remoteBaseURL = %q, want %q", got, server.URL)
|
||||
}
|
||||
if got := u.remotePath.Text(); got != "vaults/other.kdbx" {
|
||||
t.Fatalf("remotePath = %q, want vaults/other.kdbx", got)
|
||||
}
|
||||
if got := len(u.recentRemotes); got != 1 {
|
||||
t.Fatalf("len(recentRemotes) = %d, want 1", got)
|
||||
}
|
||||
if got := u.recentRemotes[0].LocalVaultPath; got != currentPath {
|
||||
t.Fatalf("recentRemotes[0].LocalVaultPath = %q, want %q", got, currentPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUISaveCurrentRemoteBindingActionPersistsBindingIntoVault(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user