Stop storing remote credentials in recent state

This commit is contained in:
Joe Julian
2026-04-06 11:53:16 -07:00
parent 5e1e9f82fb
commit 79c5176487
3 changed files with 68 additions and 167 deletions
+7 -33
View File
@@ -152,8 +152,6 @@ 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"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
LastGroup []string `json:"lastGroup,omitempty"` LastGroup []string `json:"lastGroup,omitempty"`
UsedAt string `json:"usedAt,omitempty"` UsedAt string `json:"usedAt,omitempty"`
} }
@@ -359,7 +357,6 @@ type ui struct {
cancelLifecycleProgress widget.Clickable cancelLifecycleProgress widget.Clickable
retryLifecycleOpen widget.Clickable retryLifecycleOpen widget.Clickable
approvalPermanent widget.Bool approvalPermanent widget.Bool
rememberRemoteAuth widget.Bool
apiPolicyAllow widget.Bool apiPolicyAllow widget.Bool
apiPolicyGroupScopeW widget.Bool apiPolicyGroupScopeW widget.Bool
apiTokenDisabled widget.Bool apiTokenDisabled widget.Bool
@@ -1083,9 +1080,6 @@ func (u *ui) openRemoteAction() error {
u.noteRecentRemote( u.noteRecentRemote(
strings.TrimSpace(u.remoteBaseURL.Text()), strings.TrimSpace(u.remoteBaseURL.Text()),
strings.TrimSpace(u.remotePath.Text()), strings.TrimSpace(u.remotePath.Text()),
strings.TrimSpace(u.remoteUsername.Text()),
u.remotePassword.Text(),
u.rememberRemoteAuth.Value,
) )
u.resetPasswordPeek() u.resetPasswordPeek()
u.restoreRecentRemoteGroup(strings.TrimSpace(u.remoteBaseURL.Text()), strings.TrimSpace(u.remotePath.Text())) u.restoreRecentRemoteGroup(strings.TrimSpace(u.remoteBaseURL.Text()), strings.TrimSpace(u.remotePath.Text()))
@@ -1125,9 +1119,6 @@ func (u *ui) startOpenRemoteAction() {
u.noteRecentRemote( u.noteRecentRemote(
strings.TrimSpace(u.remoteBaseURL.Text()), strings.TrimSpace(u.remoteBaseURL.Text()),
remotePath, remotePath,
strings.TrimSpace(u.remoteUsername.Text()),
u.remotePassword.Text(),
u.rememberRemoteAuth.Value,
) )
u.resetPasswordPeek() u.resetPasswordPeek()
u.restoreRecentRemoteGroup(strings.TrimSpace(u.remoteBaseURL.Text()), remotePath) u.restoreRecentRemoteGroup(strings.TrimSpace(u.remoteBaseURL.Text()), remotePath)
@@ -1709,7 +1700,7 @@ func (u *ui) setAutofillNoticePreference(value autofillNoticeMode) {
u.saveUIPreferences() u.saveUIPreferences()
} }
func (u *ui) noteRecentRemote(baseURL, path, username, password string, rememberAuth bool) { func (u *ui) noteRecentRemote(baseURL, path string) {
baseURL = strings.TrimSpace(baseURL) baseURL = strings.TrimSpace(baseURL)
path = strings.TrimSpace(path) path = strings.TrimSpace(path)
if baseURL == "" || path == "" { if baseURL == "" || path == "" {
@@ -1724,10 +1715,6 @@ func (u *ui) noteRecentRemote(baseURL, path, username, password string, remember
if len(record.LastGroup) == 0 { if len(record.LastGroup) == 0 {
record.LastGroup = u.recentRemoteGroup(baseURL, path) record.LastGroup = u.recentRemoteGroup(baseURL, path)
} }
if rememberAuth {
record.Username = strings.TrimSpace(username)
record.Password = password
}
next := []recentRemoteRecord{record} next := []recentRemoteRecord{record}
for _, existing := range u.recentRemotes { for _, existing := range u.recentRemotes {
if existing.BaseURL == baseURL && existing.Path == path { if existing.BaseURL == baseURL && existing.Path == path {
@@ -1830,7 +1817,6 @@ func (u *ui) switchToLifecycleSelection(mode string) {
u.remotePath.SetText("") u.remotePath.SetText("")
u.remoteUsername.SetText("") u.remoteUsername.SetText("")
u.remotePassword.SetText("") u.remotePassword.SetText("")
u.rememberRemoteAuth.Value = false
u.selectedRemoteConnection = false u.selectedRemoteConnection = false
default: default:
u.vaultPath.SetText("") u.vaultPath.SetText("")
@@ -1838,7 +1824,6 @@ func (u *ui) switchToLifecycleSelection(mode string) {
u.remotePath.SetText("") u.remotePath.SetText("")
u.remoteUsername.SetText("") u.remoteUsername.SetText("")
u.remotePassword.SetText("") u.remotePassword.SetText("")
u.rememberRemoteAuth.Value = false
u.selectedRemoteConnection = false u.selectedRemoteConnection = false
} }
u.requestMasterPassFocus = u.hasSelectedLifecycleTarget() u.requestMasterPassFocus = u.hasSelectedLifecycleTarget()
@@ -1861,10 +1846,8 @@ func (u *ui) latestRecentRemote() (recentRemoteRecord, bool, time.Time) {
func (u *ui) currentRemoteRecord() recentRemoteRecord { func (u *ui) currentRemoteRecord() recentRemoteRecord {
return recentRemoteRecord{ return recentRemoteRecord{
BaseURL: strings.TrimSpace(u.remoteBaseURL.Text()), BaseURL: strings.TrimSpace(u.remoteBaseURL.Text()),
Path: strings.TrimSpace(u.remotePath.Text()), Path: strings.TrimSpace(u.remotePath.Text()),
Username: strings.TrimSpace(u.remoteUsername.Text()),
Password: u.remotePassword.Text(),
} }
} }
@@ -1884,33 +1867,25 @@ 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.remoteUsername.SetText(record.Username)
u.remotePassword.SetText(record.Password)
u.remotePassword.Mask = '•' u.remotePassword.Mask = '•'
u.rememberRemoteAuth.Value = strings.TrimSpace(record.Username) != "" || record.Password != ""
u.selectedRemoteConnection = true u.selectedRemoteConnection = true
} }
func (u *ui) remotePreferencesCurrentSummary() string { func (u *ui) remotePreferencesCurrentSummary() string {
selected, hasSelected := u.selectedRecentRemoteRecord()
switch { switch {
case !u.rememberRemoteAuth.Value:
return "Current choice: KeePassGO will remember only the WebDAV location for this connection."
case hasSelected && (strings.TrimSpace(selected.Username) != "" || selected.Password != ""):
return "Current choice: a successful open will update the saved sign-in for this connection on this device."
case strings.TrimSpace(u.remoteUsername.Text()) != "" || u.remotePassword.Text() != "": case strings.TrimSpace(u.remoteUsername.Text()) != "" || u.remotePassword.Text() != "":
return "Current choice: a successful open will save the entered sign-in for this connection on this device." return "Current choice: the entered WebDAV sign-in is used for this open. To persist it, store it in the vault and bind this vault to the remote profile."
default: default:
return "Current choice: sign-in retention is enabled, but no username or password is entered yet." return "Current choice: KeePassGO remembers this connection's location only. Remote credentials belong in the vault, not device state."
} }
} }
func (u *ui) remotePreferencesAlwaysSavedSummary() string { func (u *ui) remotePreferencesAlwaysSavedSummary() string {
return "Recent Connections always stores the WebDAV base URL, remote path, and the last group you opened for that connection." return "Recent Connections stores only the WebDAV base URL, remote path, and the last group you opened for that connection."
} }
func (u *ui) remotePreferencesRetentionSummary() string { func (u *ui) remotePreferencesRetentionSummary() string {
return "KeePassGO keeps up to six recent connections. Turning off Remember sign-in and reopening rewrites that connection without the saved username or password." return "KeePassGO keeps up to six recent connections. Store remote credentials in the vault if this connection should persist across devices or reinstalls."
} }
func (u *ui) noteCurrentRemotePath() { func (u *ui) noteCurrentRemotePath() {
@@ -3257,7 +3232,6 @@ func (u *ui) layout(gtx layout.Context) layout.Dimensions {
u.remotePath.SetText("") u.remotePath.SetText("")
u.remoteUsername.SetText("") u.remoteUsername.SetText("")
u.remotePassword.SetText("") u.remotePassword.SetText("")
u.rememberRemoteAuth.Value = false
u.state.ErrorMessage = "" u.state.ErrorMessage = ""
u.state.StatusMessage = "" u.state.StatusMessage = ""
u.requestMasterPassFocus = true u.requestMasterPassFocus = true
+61 -98
View File
@@ -3853,11 +3853,11 @@ func TestUIRecentRemoteConnectionsPersistAndReload(t *testing.T) {
first.recentRemotesPath = configPath first.recentRemotesPath = configPath
first.recentRemotes = nil first.recentRemotes = nil
first.currentPath = []string{"Root", "Internet"} first.currentPath = []string{"Root", "Internet"}
first.noteRecentRemote("https://dav.example.com", "vaults/home.kdbx", "alice", "secret-1", true) first.noteRecentRemote("https://dav.example.com", "vaults/home.kdbx")
first.currentPath = []string{"Root", "Home"} first.currentPath = []string{"Root", "Home"}
first.noteRecentRemote("https://dav.example.com", "vaults/team.kdbx", "bob", "secret-2", false) first.noteRecentRemote("https://dav.example.com", "vaults/team.kdbx")
first.currentPath = []string{"Root", "Finance"} first.currentPath = []string{"Root", "Finance"}
first.noteRecentRemote("https://dav.example.com", "vaults/home.kdbx", "alice", "secret-3", true) first.noteRecentRemote("https://dav.example.com", "vaults/home.kdbx")
second := newUIWithSession("desktop", &session.Manager{}) second := newUIWithSession("desktop", &session.Manager{})
second.recentRemotesPath = configPath second.recentRemotesPath = configPath
@@ -3867,20 +3867,47 @@ func TestUIRecentRemoteConnectionsPersistAndReload(t *testing.T) {
if got := len(second.recentRemotes); got != 2 { if got := len(second.recentRemotes); got != 2 {
t.Fatalf("len(recentRemotes) = %d, want 2", got) t.Fatalf("len(recentRemotes) = %d, want 2", got)
} }
if got := second.recentRemotes[0]; got.BaseURL != "https://dav.example.com" || got.Path != "vaults/home.kdbx" || got.Username != "alice" || got.Password != "secret-3" { if got := second.recentRemotes[0]; got.BaseURL != "https://dav.example.com" || got.Path != "vaults/home.kdbx" {
t.Fatalf("recentRemotes[0] = %#v, want updated remembered credentials", got) t.Fatalf("recentRemotes[0] = %#v, want updated location-only record", got)
} }
if got := second.recentRemotes[0].LastGroup; !slices.Equal(got, []string{"Root", "Finance"}) { if got := second.recentRemotes[0].LastGroup; !slices.Equal(got, []string{"Root", "Finance"}) {
t.Fatalf("recentRemotes[0].LastGroup = %v, want [Root Finance]", got) t.Fatalf("recentRemotes[0].LastGroup = %v, want [Root Finance]", got)
} }
if got := second.recentRemotes[1]; got.Username != "" || got.Password != "" {
t.Fatalf("recentRemotes[1] = %#v, want credentials omitted when remember disabled", got)
}
if got := second.recentRemotes[1].LastGroup; !slices.Equal(got, []string{"Root", "Home"}) { if got := second.recentRemotes[1].LastGroup; !slices.Equal(got, []string{"Root", "Home"}) {
t.Fatalf("recentRemotes[1].LastGroup = %v, want [Root Home]", got) t.Fatalf("recentRemotes[1].LastGroup = %v, want [Root Home]", got)
} }
} }
func TestUILoadRecentRemotesIgnoresLegacySavedCredentials(t *testing.T) {
t.Parallel()
configPath := filepath.Join(t.TempDir(), "recent-remotes.json")
content := `[
{
"baseUrl": "https://dav.example.com",
"path": "vaults/home.kdbx",
"username": "alice",
"password": "secret-1",
"lastGroup": ["Root", "Internet"]
}
]`
if err := os.WriteFile(configPath, []byte(content), 0o600); err != nil {
t.Fatalf("WriteFile(recent-remotes.json) error = %v", err)
}
u := newUIWithSession("desktop", &session.Manager{})
u.recentRemotesPath = configPath
u.recentRemotes = nil
u.loadRecentRemotes()
if got := len(u.recentRemotes); got != 1 {
t.Fatalf("len(recentRemotes) = %d, want 1", got)
}
if got := u.recentRemotes[0]; got.BaseURL != "https://dav.example.com" || got.Path != "vaults/home.kdbx" {
t.Fatalf("recentRemotes[0] = %#v, want location-only record", got)
}
}
func TestUIStartupPreselectsNewestTargetAcrossLocalAndRemote(t *testing.T) { func TestUIStartupPreselectsNewestTargetAcrossLocalAndRemote(t *testing.T) {
t.Parallel() t.Parallel()
@@ -3898,7 +3925,7 @@ func TestUIStartupPreselectsNewestTargetAcrossLocalAndRemote(t *testing.T) {
first.now = func() time.Time { return time.Date(2026, 3, 30, 12, 0, 0, 0, time.UTC) } first.now = func() time.Time { return time.Date(2026, 3, 30, 12, 0, 0, 0, time.UTC) }
first.noteRecentVault("/tmp/local.kdbx") first.noteRecentVault("/tmp/local.kdbx")
first.now = func() time.Time { return time.Date(2026, 3, 30, 13, 0, 0, 0, time.UTC) } first.now = func() time.Time { return time.Date(2026, 3, 30, 13, 0, 0, 0, time.UTC) }
first.noteRecentRemote("https://dav.example.com", "vaults/home.kdbx", "alice", "secret-1", true) first.noteRecentRemote("https://dav.example.com", "vaults/home.kdbx")
second := newUIWithSession("desktop", &session.Manager{}, paths) second := newUIWithSession("desktop", &session.Manager{}, paths)
@@ -3911,11 +3938,11 @@ func TestUIStartupPreselectsNewestTargetAcrossLocalAndRemote(t *testing.T) {
if got := second.remotePath.Text(); got != "vaults/home.kdbx" { if got := second.remotePath.Text(); got != "vaults/home.kdbx" {
t.Fatalf("remotePath = %q, want vaults/home.kdbx", got) t.Fatalf("remotePath = %q, want vaults/home.kdbx", got)
} }
if got := second.remoteUsername.Text(); got != "alice" { if got := second.remoteUsername.Text(); got != "" {
t.Fatalf("remoteUsername = %q, want alice", got) t.Fatalf("remoteUsername = %q, want empty for location-only recent remote", got)
} }
if got := second.remotePassword.Text(); got != "secret-1" { if got := second.remotePassword.Text(); got != "" {
t.Fatalf("remotePassword = %q, want secret-1", got) t.Fatalf("remotePassword = %q, want empty for location-only recent remote", got)
} }
} }
@@ -4311,13 +4338,13 @@ func TestSelectingRecentRemoteConnectionKeepsPasswordMasked(t *testing.T) {
u := newUIWithSession("desktop", &session.Manager{}) u := newUIWithSession("desktop", &session.Manager{})
u.recentRemotes = []recentRemoteRecord{{ u.recentRemotes = []recentRemoteRecord{{
BaseURL: "https://dav.example.com", BaseURL: "https://dav.example.com",
Path: "vaults/home.kdbx", Path: "vaults/home.kdbx",
Username: "alice",
Password: "secret-1",
}} }}
u.recentRemoteClicks = make([]widget.Clickable, 1) u.recentRemoteClicks = make([]widget.Clickable, 1)
u.remoteUsername.SetText("alice")
u.remotePassword.SetText("secret-1")
u.remotePassword.Mask = 0 u.remotePassword.Mask = 0
u.recentRemoteClicks[0].Click() u.recentRemoteClicks[0].Click()
@@ -4326,15 +4353,18 @@ func TestSelectingRecentRemoteConnectionKeepsPasswordMasked(t *testing.T) {
record := u.recentRemotes[0] record := u.recentRemotes[0]
u.remoteBaseURL.SetText(record.BaseURL) u.remoteBaseURL.SetText(record.BaseURL)
u.remotePath.SetText(record.Path) u.remotePath.SetText(record.Path)
u.remoteUsername.SetText(record.Username)
u.remotePassword.SetText(record.Password)
u.remotePassword.Mask = '•' u.remotePassword.Mask = '•'
u.rememberRemoteAuth.Value = true
} }
if got := u.remotePassword.Mask; got != '•' { if got := u.remotePassword.Mask; got != '•' {
t.Fatalf("remotePassword.Mask = %q, want bullet mask", got) t.Fatalf("remotePassword.Mask = %q, want bullet mask", got)
} }
if got := u.remoteUsername.Text(); got != "alice" {
t.Fatalf("remoteUsername = %q, want preserved manual username", got)
}
if got := u.remotePassword.Text(); got != "secret-1" {
t.Fatalf("remotePassword = %q, want preserved manual password", got)
}
} }
func TestSelectingRecentVaultSwitchesToLocalMode(t *testing.T) { func TestSelectingRecentVaultSwitchesToLocalMode(t *testing.T) {
@@ -4460,10 +4490,8 @@ func TestApplyingRecentRemoteRecordMarksSelectedRemoteConnection(t *testing.T) {
} }
u.applyRecentRemoteRecord(recentRemoteRecord{ u.applyRecentRemoteRecord(recentRemoteRecord{
BaseURL: "https://dav.crew.example.invalid", BaseURL: "https://dav.crew.example.invalid",
Path: "vaults/bellagio.kdbx", Path: "vaults/bellagio.kdbx",
Username: "dannyocean",
Password: "topsecret",
}) })
if !u.hasSelectedRemoteTarget() { if !u.hasSelectedRemoteTarget() {
@@ -4481,7 +4509,6 @@ func TestSwitchToLifecycleSelectionResetsLockedLocalSession(t *testing.T) {
u.remotePath.SetText("vaults/remote.kdbx") u.remotePath.SetText("vaults/remote.kdbx")
u.remoteUsername.SetText("dannyocean") u.remoteUsername.SetText("dannyocean")
u.remotePassword.SetText("topsecret") u.remotePassword.SetText("topsecret")
u.rememberRemoteAuth.Value = true
u.masterPassword.SetText("correct horse battery staple") u.masterPassword.SetText("correct horse battery staple")
u.keyFilePath.SetText("/vaults/keyfile.keyx") u.keyFilePath.SetText("/vaults/keyfile.keyx")
u.search.SetText("crew") u.search.SetText("crew")
@@ -4513,9 +4540,6 @@ func TestSwitchToLifecycleSelectionResetsLockedLocalSession(t *testing.T) {
if got := u.remotePassword.Text(); got != "" { if got := u.remotePassword.Text(); got != "" {
t.Fatalf("remotePassword = %q, want empty", got) t.Fatalf("remotePassword = %q, want empty", got)
} }
if u.rememberRemoteAuth.Value {
t.Fatal("rememberRemoteAuth = true, want false")
}
if got := u.masterPassword.Text(); got != "" { if got := u.masterPassword.Text(); got != "" {
t.Fatalf("masterPassword = %q, want empty", got) t.Fatalf("masterPassword = %q, want empty", got)
} }
@@ -4549,7 +4573,6 @@ func TestSwitchToLifecycleSelectionResetsLockedRemoteSession(t *testing.T) {
u.remotePath.SetText("vaults/remote.kdbx") u.remotePath.SetText("vaults/remote.kdbx")
u.remoteUsername.SetText("rustyryan") u.remoteUsername.SetText("rustyryan")
u.remotePassword.SetText("topsecret") u.remotePassword.SetText("topsecret")
u.rememberRemoteAuth.Value = true
u.switchToLifecycleSelection("remote") u.switchToLifecycleSelection("remote")
@@ -4574,9 +4597,6 @@ func TestSwitchToLifecycleSelectionResetsLockedRemoteSession(t *testing.T) {
if got := u.remotePassword.Text(); got != "" { if got := u.remotePassword.Text(); got != "" {
t.Fatalf("remotePassword = %q, want empty", got) t.Fatalf("remotePassword = %q, want empty", got)
} }
if u.rememberRemoteAuth.Value {
t.Fatal("rememberRemoteAuth = true, want false")
}
} }
func TestSelectingRecentRemoteSwitchesToRemoteMode(t *testing.T) { func TestSelectingRecentRemoteSwitchesToRemoteMode(t *testing.T) {
@@ -4656,90 +4676,33 @@ func TestFriendlyRecentRemoteLabelUsesVaultNameBeforeHost(t *testing.T) {
} }
} }
func TestRecentRemoteStoredAuthSummaryDescribesSavedCredentialState(t *testing.T) { func TestUIRemotePreferencesCurrentSummaryExplainsVaultBackedCredentialFlow(t *testing.T) {
t.Parallel()
tests := []struct {
name string
record recentRemoteRecord
want string
}{
{
name: "location_only",
record: recentRemoteRecord{},
want: "location only",
},
{
name: "username_only",
record: recentRemoteRecord{Username: "alice"},
want: "saved username",
},
{
name: "password_only",
record: recentRemoteRecord{Password: "token-1"},
want: "saved password",
},
{
name: "full_sign_in",
record: recentRemoteRecord{Username: "alice", Password: "token-1"},
want: "saved username and password",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
if got := recentRemoteStoredAuthSummary(tt.record); got != tt.want {
t.Fatalf("recentRemoteStoredAuthSummary(%+v) = %q, want %q", tt.record, got, tt.want)
}
})
}
}
func TestUIRemotePreferencesCurrentSummaryExplainsWhatWillBeRemembered(t *testing.T) {
t.Parallel() t.Parallel()
u := newUIWithSession("desktop", &session.Manager{}) u := newUIWithSession("desktop", &session.Manager{})
u.remoteBaseURL.SetText("https://dav.example.com") u.remoteBaseURL.SetText("https://dav.example.com")
u.remotePath.SetText("vaults/home.kdbx") u.remotePath.SetText("vaults/home.kdbx")
if got := u.remotePreferencesCurrentSummary(); got != "Current choice: KeePassGO will remember only the WebDAV location for this connection." { if got := u.remotePreferencesCurrentSummary(); got != "Current choice: KeePassGO remembers this connection's location only. Remote credentials belong in the vault, not device state." {
t.Fatalf("remotePreferencesCurrentSummary() = %q, want location-only guidance", got) t.Fatalf("remotePreferencesCurrentSummary() = %q, want location-only vault guidance", got)
}
u.rememberRemoteAuth.Value = true
if got := u.remotePreferencesCurrentSummary(); got != "Current choice: sign-in retention is enabled, but no username or password is entered yet." {
t.Fatalf("remotePreferencesCurrentSummary() = %q, want empty-sign-in guidance", got)
} }
u.remoteUsername.SetText("alice") u.remoteUsername.SetText("alice")
if got := u.remotePreferencesCurrentSummary(); got != "Current choice: a successful open will save the entered sign-in for this connection on this device." { if got := u.remotePreferencesCurrentSummary(); got != "Current choice: the entered WebDAV sign-in is used for this open. To persist it, store it in the vault and bind this vault to the remote profile." {
t.Fatalf("remotePreferencesCurrentSummary() = %q, want pending-save guidance", got) t.Fatalf("remotePreferencesCurrentSummary() = %q, want vault-storage guidance", got)
}
u.recentRemotes = []recentRemoteRecord{{
BaseURL: "https://dav.example.com",
Path: "vaults/home.kdbx",
Username: "alice",
Password: "secret-1",
}}
if got := u.remotePreferencesCurrentSummary(); got != "Current choice: a successful open will update the saved sign-in for this connection on this device." {
t.Fatalf("remotePreferencesCurrentSummary() = %q, want saved-sign-in guidance", got)
} }
} }
func TestUIRemotePreferencesHelpExplainsSavedFieldsAndRetention(t *testing.T) { func TestUIRemotePreferencesHelpExplainsLocationOnlyRetention(t *testing.T) {
t.Parallel() t.Parallel()
u := newUIWithSession("desktop", &session.Manager{}) u := newUIWithSession("desktop", &session.Manager{})
if got := u.remotePreferencesAlwaysSavedSummary(); got != "Recent Connections always stores the WebDAV base URL, remote path, and the last group you opened for that connection." { if got := u.remotePreferencesAlwaysSavedSummary(); got != "Recent Connections stores only the WebDAV base URL, remote path, and the last group you opened for that connection." {
t.Fatalf("remotePreferencesAlwaysSavedSummary() = %q, want saved-fields guidance", got) t.Fatalf("remotePreferencesAlwaysSavedSummary() = %q, want saved-fields guidance", got)
} }
if got := u.remotePreferencesRetentionSummary(); got != "KeePassGO keeps up to six recent connections. Turning off Remember sign-in and reopening rewrites that connection without the saved username or password." { if got := u.remotePreferencesRetentionSummary(); got != "KeePassGO keeps up to six recent connections. Store remote credentials in the vault if this connection should persist across devices or reinstalls." {
t.Fatalf("remotePreferencesRetentionSummary() = %q, want retention guidance", got) t.Fatalf("remotePreferencesRetentionSummary() = %q, want vault retention guidance", got)
} }
} }
-36
View File
@@ -155,14 +155,6 @@ func (u *ui) lifecycleControls(gtx layout.Context) layout.Dimensions {
} }
return layout.Spacer{Height: unit.Dp(6)}.Layout(gtx) return layout.Spacer{Height: unit.Dp(6)}.Layout(gtx)
}), }),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
if !showRemoteChooser {
return layout.Dimensions{}
}
box := material.CheckBox(u.theme, &u.rememberRemoteAuth, "Remember sign-in on this device")
box.Color = accentColor
return box.Layout(gtx)
}),
layout.Rigid(func(gtx layout.Context) layout.Dimensions { layout.Rigid(func(gtx layout.Context) layout.Dimensions {
if !showRemoteChooser { if !showRemoteChooser {
return layout.Dimensions{} return layout.Dimensions{}
@@ -388,14 +380,6 @@ func (u *ui) selectedRemoteConnectionCard(gtx layout.Context) layout.Dimensions
lbl.Color = mutedColor lbl.Color = mutedColor
return lbl.Layout(gtx) return lbl.Layout(gtx)
}), }),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
lbl := material.Label(u.theme, unit.Sp(11), "Auth: "+recentRemoteStoredAuthSummary(recentRemoteRecord{
Username: strings.TrimSpace(u.remoteUsername.Text()),
Password: u.remotePassword.Text(),
}))
lbl.Color = mutedColor
return lbl.Layout(gtx)
}),
layout.Rigid(func(gtx layout.Context) layout.Dimensions { layout.Rigid(func(gtx layout.Context) layout.Dimensions {
if len(lastGroup) == 0 { if len(lastGroup) == 0 {
return layout.Dimensions{} return layout.Dimensions{}
@@ -617,11 +601,6 @@ func (u *ui) recentRemoteList(gtx layout.Context) layout.Dimensions {
lbl.Color = mutedColor lbl.Color = mutedColor
return lbl.Layout(gtx) return lbl.Layout(gtx)
}), }),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
lbl := material.Label(u.theme, unit.Sp(11), "Auth: "+recentRemoteStoredAuthSummary(record))
lbl.Color = mutedColor
return lbl.Layout(gtx)
}),
layout.Rigid(func(gtx layout.Context) layout.Dimensions { layout.Rigid(func(gtx layout.Context) layout.Dimensions {
if len(record.LastGroup) == 0 { if len(record.LastGroup) == 0 {
return layout.Dimensions{} return layout.Dimensions{}
@@ -750,21 +729,6 @@ func normalizedRemoteHost(baseURL string) string {
return strings.TrimSuffix(host, "/") return strings.TrimSuffix(host, "/")
} }
func recentRemoteStoredAuthSummary(record recentRemoteRecord) string {
username := strings.TrimSpace(record.Username)
hasPassword := record.Password != ""
switch {
case username != "" && hasPassword:
return "saved username and password"
case username != "":
return "saved username"
case hasPassword:
return "saved password"
default:
return "location only"
}
}
func (u *ui) attachmentList(gtx layout.Context) layout.Dimensions { func (u *ui) attachmentList(gtx layout.Context) layout.Dimensions {
items := u.selectedAttachmentItems() items := u.selectedAttachmentItems()
if len(items) == 0 { if len(items) == 0 {