Default to sole saved remote binding
This commit is contained in:
@@ -2143,31 +2143,55 @@ func (u *ui) selectVaultRemoteCredentialEntry(id string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) selectedVaultRemoteProfile() (vault.RemoteProfile, bool) {
|
func (u *ui) selectedVaultRemoteProfile() (vault.RemoteProfile, bool) {
|
||||||
for _, profile := range u.availableRemoteProfiles() {
|
selectedID := strings.TrimSpace(u.selectedVaultRemoteProfileID)
|
||||||
if profile.ID == strings.TrimSpace(u.selectedVaultRemoteProfileID) {
|
profiles := u.availableRemoteProfiles()
|
||||||
|
for _, profile := range profiles {
|
||||||
|
if profile.ID == selectedID {
|
||||||
return profile, true
|
return profile, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if selectedID == "" && len(profiles) == 1 {
|
||||||
|
return profiles[0], true
|
||||||
|
}
|
||||||
return vault.RemoteProfile{}, false
|
return vault.RemoteProfile{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) selectedVaultRemoteCredentialEntry() (vault.Entry, bool) {
|
func (u *ui) selectedVaultRemoteCredentialEntry() (vault.Entry, bool) {
|
||||||
for _, entry := range u.availableRemoteCredentialEntries() {
|
selectedID := strings.TrimSpace(u.selectedVaultRemoteCredentialEntryID)
|
||||||
if entry.ID == strings.TrimSpace(u.selectedVaultRemoteCredentialEntryID) {
|
entries := u.availableRemoteCredentialEntries()
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.ID == selectedID {
|
||||||
return entry, true
|
return entry, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if selectedID == "" && len(entries) == 1 {
|
||||||
|
return entries[0], true
|
||||||
|
}
|
||||||
return vault.Entry{}, false
|
return vault.Entry{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) selectedVaultRemoteBinding() (appstate.RemoteBinding, bool) {
|
func (u *ui) selectedVaultRemoteBinding() (appstate.RemoteBinding, bool) {
|
||||||
if strings.TrimSpace(u.selectedVaultRemoteProfileID) == "" || strings.TrimSpace(u.selectedVaultRemoteCredentialEntryID) == "" {
|
profileID := strings.TrimSpace(u.selectedVaultRemoteProfileID)
|
||||||
|
entryID := strings.TrimSpace(u.selectedVaultRemoteCredentialEntryID)
|
||||||
|
if profileID != "" && entryID != "" {
|
||||||
|
return appstate.RemoteBinding{
|
||||||
|
LocalVaultPath: strings.TrimSpace(u.vaultPath.Text()),
|
||||||
|
RemoteProfileID: profileID,
|
||||||
|
CredentialEntryID: entryID,
|
||||||
|
}, true
|
||||||
|
}
|
||||||
|
profile, ok := u.selectedVaultRemoteProfile()
|
||||||
|
if !ok {
|
||||||
|
return appstate.RemoteBinding{}, false
|
||||||
|
}
|
||||||
|
entry, ok := u.selectedVaultRemoteCredentialEntry()
|
||||||
|
if !ok {
|
||||||
return appstate.RemoteBinding{}, false
|
return appstate.RemoteBinding{}, false
|
||||||
}
|
}
|
||||||
return appstate.RemoteBinding{
|
return appstate.RemoteBinding{
|
||||||
LocalVaultPath: strings.TrimSpace(u.vaultPath.Text()),
|
LocalVaultPath: strings.TrimSpace(u.vaultPath.Text()),
|
||||||
RemoteProfileID: strings.TrimSpace(u.selectedVaultRemoteProfileID),
|
RemoteProfileID: profile.ID,
|
||||||
CredentialEntryID: strings.TrimSpace(u.selectedVaultRemoteCredentialEntryID),
|
CredentialEntryID: entry.ID,
|
||||||
}, true
|
}, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+119
@@ -1804,6 +1804,50 @@ func TestUIOpenRemoteActionUsesSelectedVaultBinding(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUIOpenRemoteActionUsesImplicitSingleVaultBinding(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
sess := &remoteOpenCaptureSession{
|
||||||
|
model: vault.Model{
|
||||||
|
Entries: []vault.Entry{{
|
||||||
|
ID: "remote-creds-1",
|
||||||
|
Title: "WebDAV Sign-In",
|
||||||
|
Username: "tjulian",
|
||||||
|
Password: "token-1",
|
||||||
|
Path: []string{"Crew", "Internet"},
|
||||||
|
}},
|
||||||
|
RemoteProfiles: []vault.RemoteProfile{{
|
||||||
|
ID: "family-webdav",
|
||||||
|
Name: "Family Vault",
|
||||||
|
Backend: vault.RemoteBackendWebDAV,
|
||||||
|
BaseURL: "https://dav.example.invalid/remote.php/dav",
|
||||||
|
Path: "files/family/keepass.kdbx",
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
u := newUIWithSession("desktop", sess)
|
||||||
|
u.masterPassword.SetText("correct horse battery staple")
|
||||||
|
u.vaultPath.SetText("/vaults/family.kdbx")
|
||||||
|
|
||||||
|
if err := u.openRemoteAction(); err != nil {
|
||||||
|
t.Fatalf("openRemoteAction() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := sess.remoteClient.BaseURL; got != "https://dav.example.invalid/remote.php/dav" {
|
||||||
|
t.Fatalf("remoteClient.BaseURL = %q, want remote.php/dav URL", got)
|
||||||
|
}
|
||||||
|
if got := sess.remoteClient.Username; got != "tjulian" {
|
||||||
|
t.Fatalf("remoteClient.Username = %q, want tjulian", got)
|
||||||
|
}
|
||||||
|
if got := sess.remoteClient.Password; got != "token-1" {
|
||||||
|
t.Fatalf("remoteClient.Password = %q, want token-1", got)
|
||||||
|
}
|
||||||
|
if got := sess.remotePath; got != "files/family/keepass.kdbx" {
|
||||||
|
t.Fatalf("remotePath = %q, want files/family/keepass.kdbx", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUIOpenRemoteActionBootstrapsFromLocalVaultBinding(t *testing.T) {
|
func TestUIOpenRemoteActionBootstrapsFromLocalVaultBinding(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -1885,6 +1929,7 @@ func TestUIStartOpenRemoteActionUsesSelectedVaultBinding(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
localKey := vault.MasterKey{Password: "correct horse battery staple"}
|
localKey := vault.MasterKey{Password: "correct horse battery staple"}
|
||||||
|
localPath := filepath.Join(t.TempDir(), "family.kdbx")
|
||||||
localModel := vault.Model{
|
localModel := vault.Model{
|
||||||
Entries: []vault.Entry{{
|
Entries: []vault.Entry{{
|
||||||
ID: "remote-creds-1",
|
ID: "remote-creds-1",
|
||||||
@@ -1935,6 +1980,7 @@ func TestUIStartOpenRemoteActionUsesSelectedVaultBinding(t *testing.T) {
|
|||||||
|
|
||||||
u := newUIWithSession("desktop", manager)
|
u := newUIWithSession("desktop", manager)
|
||||||
u.masterPassword.SetText(localKey.Password)
|
u.masterPassword.SetText(localKey.Password)
|
||||||
|
u.vaultPath.SetText(localPath)
|
||||||
u.selectedVaultRemoteProfileID = "family-webdav"
|
u.selectedVaultRemoteProfileID = "family-webdav"
|
||||||
u.selectedVaultRemoteCredentialEntryID = "remote-creds-1"
|
u.selectedVaultRemoteCredentialEntryID = "remote-creds-1"
|
||||||
|
|
||||||
@@ -1954,6 +2000,79 @@ func TestUIStartOpenRemoteActionUsesSelectedVaultBinding(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUIStartOpenRemoteActionUsesImplicitSingleVaultBinding(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
localKey := vault.MasterKey{Password: "correct horse battery staple"}
|
||||||
|
localPath := filepath.Join(t.TempDir(), "family.kdbx")
|
||||||
|
localModel := vault.Model{
|
||||||
|
Entries: []vault.Entry{{
|
||||||
|
ID: "remote-creds-1",
|
||||||
|
Title: "WebDAV Sign-In",
|
||||||
|
Username: "tjulian",
|
||||||
|
Password: "token-1",
|
||||||
|
Path: []string{"Crew", "Internet"},
|
||||||
|
}},
|
||||||
|
RemoteProfiles: []vault.RemoteProfile{{
|
||||||
|
ID: "family-webdav",
|
||||||
|
Name: "Family Vault",
|
||||||
|
Backend: vault.RemoteBackendWebDAV,
|
||||||
|
BaseURL: "",
|
||||||
|
Path: "files/family/keepass.kdbx",
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteModel := vault.Model{
|
||||||
|
Entries: []vault.Entry{{
|
||||||
|
ID: "vault-console",
|
||||||
|
Title: "Vault Console",
|
||||||
|
Username: "dannyocean",
|
||||||
|
Password: "token-1",
|
||||||
|
URL: "https://vault.crew.example.invalid",
|
||||||
|
Path: []string{"Root", "Internet"},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodGet {
|
||||||
|
t.Fatalf("unexpected method %s", r.Method)
|
||||||
|
}
|
||||||
|
var encoded bytes.Buffer
|
||||||
|
if err := vault.SaveKDBXWithKey(&encoded, remoteModel, localKey); err != nil {
|
||||||
|
t.Fatalf("SaveKDBXWithKey() error = %v", err)
|
||||||
|
}
|
||||||
|
w.Header().Set("ETag", "\"v1\"")
|
||||||
|
_, _ = w.Write(encoded.Bytes())
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
localModel.RemoteProfiles[0].BaseURL = server.URL
|
||||||
|
|
||||||
|
manager := &session.Manager{}
|
||||||
|
if err := manager.Create(localModel, localKey); err != nil {
|
||||||
|
t.Fatalf("manager.Create() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
u := newUIWithSession("desktop", manager)
|
||||||
|
u.masterPassword.SetText(localKey.Password)
|
||||||
|
u.vaultPath.SetText(localPath)
|
||||||
|
|
||||||
|
u.startOpenRemoteAction()
|
||||||
|
|
||||||
|
result := waitForBackgroundResult(t, u)
|
||||||
|
u.applyBackgroundResult(result)
|
||||||
|
|
||||||
|
if got := u.state.ErrorMessage; got != "" {
|
||||||
|
t.Fatalf("ErrorMessage after apply = %q, want empty", got)
|
||||||
|
}
|
||||||
|
if got := u.remoteBaseURL.Text(); got != server.URL {
|
||||||
|
t.Fatalf("remoteBaseURL = %q, want server URL from implicit profile", got)
|
||||||
|
}
|
||||||
|
if got := u.remotePath.Text(); got != "files/family/keepass.kdbx" {
|
||||||
|
t.Fatalf("remotePath = %q, want implicit profile path", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUIStartOpenRemoteActionBootstrapsFromLocalVaultBinding(t *testing.T) {
|
func TestUIStartOpenRemoteActionBootstrapsFromLocalVaultBinding(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user