Stop storing remote credentials in recent state
This commit is contained in:
+61
-98
@@ -3853,11 +3853,11 @@ func TestUIRecentRemoteConnectionsPersistAndReload(t *testing.T) {
|
||||
first.recentRemotesPath = configPath
|
||||
first.recentRemotes = nil
|
||||
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.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.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.recentRemotesPath = configPath
|
||||
@@ -3867,20 +3867,47 @@ func TestUIRecentRemoteConnectionsPersistAndReload(t *testing.T) {
|
||||
if got := len(second.recentRemotes); got != 2 {
|
||||
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" {
|
||||
t.Fatalf("recentRemotes[0] = %#v, want updated remembered credentials", got)
|
||||
if got := second.recentRemotes[0]; got.BaseURL != "https://dav.example.com" || got.Path != "vaults/home.kdbx" {
|
||||
t.Fatalf("recentRemotes[0] = %#v, want updated location-only record", got)
|
||||
}
|
||||
if got := second.recentRemotes[0].LastGroup; !slices.Equal(got, []string{"Root", "Finance"}) {
|
||||
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"}) {
|
||||
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) {
|
||||
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.noteRecentVault("/tmp/local.kdbx")
|
||||
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)
|
||||
|
||||
@@ -3911,11 +3938,11 @@ func TestUIStartupPreselectsNewestTargetAcrossLocalAndRemote(t *testing.T) {
|
||||
if got := second.remotePath.Text(); got != "vaults/home.kdbx" {
|
||||
t.Fatalf("remotePath = %q, want vaults/home.kdbx", got)
|
||||
}
|
||||
if got := second.remoteUsername.Text(); got != "alice" {
|
||||
t.Fatalf("remoteUsername = %q, want alice", got)
|
||||
if got := second.remoteUsername.Text(); got != "" {
|
||||
t.Fatalf("remoteUsername = %q, want empty for location-only recent remote", got)
|
||||
}
|
||||
if got := second.remotePassword.Text(); got != "secret-1" {
|
||||
t.Fatalf("remotePassword = %q, want secret-1", got)
|
||||
if got := second.remotePassword.Text(); 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.recentRemotes = []recentRemoteRecord{{
|
||||
BaseURL: "https://dav.example.com",
|
||||
Path: "vaults/home.kdbx",
|
||||
Username: "alice",
|
||||
Password: "secret-1",
|
||||
BaseURL: "https://dav.example.com",
|
||||
Path: "vaults/home.kdbx",
|
||||
}}
|
||||
u.recentRemoteClicks = make([]widget.Clickable, 1)
|
||||
|
||||
u.remoteUsername.SetText("alice")
|
||||
u.remotePassword.SetText("secret-1")
|
||||
u.remotePassword.Mask = 0
|
||||
u.recentRemoteClicks[0].Click()
|
||||
|
||||
@@ -4326,15 +4353,18 @@ func TestSelectingRecentRemoteConnectionKeepsPasswordMasked(t *testing.T) {
|
||||
record := u.recentRemotes[0]
|
||||
u.remoteBaseURL.SetText(record.BaseURL)
|
||||
u.remotePath.SetText(record.Path)
|
||||
u.remoteUsername.SetText(record.Username)
|
||||
u.remotePassword.SetText(record.Password)
|
||||
u.remotePassword.Mask = '•'
|
||||
u.rememberRemoteAuth.Value = true
|
||||
}
|
||||
|
||||
if got := u.remotePassword.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) {
|
||||
@@ -4460,10 +4490,8 @@ func TestApplyingRecentRemoteRecordMarksSelectedRemoteConnection(t *testing.T) {
|
||||
}
|
||||
|
||||
u.applyRecentRemoteRecord(recentRemoteRecord{
|
||||
BaseURL: "https://dav.crew.example.invalid",
|
||||
Path: "vaults/bellagio.kdbx",
|
||||
Username: "dannyocean",
|
||||
Password: "topsecret",
|
||||
BaseURL: "https://dav.crew.example.invalid",
|
||||
Path: "vaults/bellagio.kdbx",
|
||||
})
|
||||
|
||||
if !u.hasSelectedRemoteTarget() {
|
||||
@@ -4481,7 +4509,6 @@ func TestSwitchToLifecycleSelectionResetsLockedLocalSession(t *testing.T) {
|
||||
u.remotePath.SetText("vaults/remote.kdbx")
|
||||
u.remoteUsername.SetText("dannyocean")
|
||||
u.remotePassword.SetText("topsecret")
|
||||
u.rememberRemoteAuth.Value = true
|
||||
u.masterPassword.SetText("correct horse battery staple")
|
||||
u.keyFilePath.SetText("/vaults/keyfile.keyx")
|
||||
u.search.SetText("crew")
|
||||
@@ -4513,9 +4540,6 @@ func TestSwitchToLifecycleSelectionResetsLockedLocalSession(t *testing.T) {
|
||||
if got := u.remotePassword.Text(); got != "" {
|
||||
t.Fatalf("remotePassword = %q, want empty", got)
|
||||
}
|
||||
if u.rememberRemoteAuth.Value {
|
||||
t.Fatal("rememberRemoteAuth = true, want false")
|
||||
}
|
||||
if got := u.masterPassword.Text(); got != "" {
|
||||
t.Fatalf("masterPassword = %q, want empty", got)
|
||||
}
|
||||
@@ -4549,7 +4573,6 @@ func TestSwitchToLifecycleSelectionResetsLockedRemoteSession(t *testing.T) {
|
||||
u.remotePath.SetText("vaults/remote.kdbx")
|
||||
u.remoteUsername.SetText("rustyryan")
|
||||
u.remotePassword.SetText("topsecret")
|
||||
u.rememberRemoteAuth.Value = true
|
||||
|
||||
u.switchToLifecycleSelection("remote")
|
||||
|
||||
@@ -4574,9 +4597,6 @@ func TestSwitchToLifecycleSelectionResetsLockedRemoteSession(t *testing.T) {
|
||||
if got := u.remotePassword.Text(); got != "" {
|
||||
t.Fatalf("remotePassword = %q, want empty", got)
|
||||
}
|
||||
if u.rememberRemoteAuth.Value {
|
||||
t.Fatal("rememberRemoteAuth = true, want false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelectingRecentRemoteSwitchesToRemoteMode(t *testing.T) {
|
||||
@@ -4656,90 +4676,33 @@ func TestFriendlyRecentRemoteLabelUsesVaultNameBeforeHost(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecentRemoteStoredAuthSummaryDescribesSavedCredentialState(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) {
|
||||
func TestUIRemotePreferencesCurrentSummaryExplainsVaultBackedCredentialFlow(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
u := newUIWithSession("desktop", &session.Manager{})
|
||||
u.remoteBaseURL.SetText("https://dav.example.com")
|
||||
u.remotePath.SetText("vaults/home.kdbx")
|
||||
|
||||
if got := u.remotePreferencesCurrentSummary(); got != "Current choice: KeePassGO will remember only the WebDAV location for this connection." {
|
||||
t.Fatalf("remotePreferencesCurrentSummary() = %q, want location-only 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)
|
||||
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 vault guidance", got)
|
||||
}
|
||||
|
||||
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." {
|
||||
t.Fatalf("remotePreferencesCurrentSummary() = %q, want pending-save 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)
|
||||
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 vault-storage guidance", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUIRemotePreferencesHelpExplainsSavedFieldsAndRetention(t *testing.T) {
|
||||
func TestUIRemotePreferencesHelpExplainsLocationOnlyRetention(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
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)
|
||||
}
|
||||
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." {
|
||||
t.Fatalf("remotePreferencesRetentionSummary() = %q, want retention guidance", got)
|
||||
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 vault retention guidance", got)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user