Add legacy remote credential migration notices
This commit is contained in:
@@ -159,8 +159,11 @@ type recentRemoteRecord struct {
|
||||
LocalVaultPath string `json:"localVaultPath,omitempty"`
|
||||
RemoteProfileID string `json:"remoteProfileId,omitempty"`
|
||||
CredentialEntryID string `json:"credentialEntryId,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
LastGroup []string `json:"lastGroup,omitempty"`
|
||||
UsedAt string `json:"usedAt,omitempty"`
|
||||
NeedsMigration bool `json:"-"`
|
||||
}
|
||||
|
||||
type uiPreferences struct {
|
||||
@@ -668,6 +671,9 @@ func newUIWithState(mode string, sess appstate.CurrentSession, paths statePaths)
|
||||
u.setCustomFieldRows(nil)
|
||||
u.loadRecentVaults()
|
||||
u.loadRecentRemotes()
|
||||
if u.hasLegacyRecentRemoteCredentialMigration() {
|
||||
u.showStatusMessage("Some saved remote sign-ins came from an older KeePassGO build. Reopen those remotes and save them in the vault to migrate them.")
|
||||
}
|
||||
u.consumePendingSharedVaultImport()
|
||||
u.restoreStartupLifecycleTarget()
|
||||
u.requestMasterPassFocus = u.hasSelectedLifecycleTarget()
|
||||
@@ -1695,9 +1701,16 @@ func (u *ui) loadRecentRemotes() {
|
||||
record.LocalVaultPath = strings.TrimSpace(record.LocalVaultPath)
|
||||
record.RemoteProfileID = strings.TrimSpace(record.RemoteProfileID)
|
||||
record.CredentialEntryID = strings.TrimSpace(record.CredentialEntryID)
|
||||
record.Username = strings.TrimSpace(record.Username)
|
||||
record.Password = strings.TrimSpace(record.Password)
|
||||
if record.BaseURL == "" || record.Path == "" {
|
||||
continue
|
||||
}
|
||||
if record.Username != "" || record.Password != "" {
|
||||
record.NeedsMigration = true
|
||||
record.Username = ""
|
||||
record.Password = ""
|
||||
}
|
||||
key := record.BaseURL + "|" + record.Path
|
||||
if seen[key] {
|
||||
continue
|
||||
@@ -1715,6 +1728,15 @@ func (u *ui) loadRecentRemotes() {
|
||||
}
|
||||
}
|
||||
|
||||
func (u *ui) hasLegacyRecentRemoteCredentialMigration() bool {
|
||||
for _, record := range u.recentRemotes {
|
||||
if record.NeedsMigration {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *ui) saveRecentVaults() {
|
||||
if strings.TrimSpace(u.recentVaultsPath) == "" {
|
||||
return
|
||||
@@ -2092,6 +2114,9 @@ func (u *ui) applyRecentRemoteRecord(record recentRemoteRecord) {
|
||||
u.selectedVaultRemoteCredentialEntryID = strings.TrimSpace(record.CredentialEntryID)
|
||||
u.remotePassword.Mask = '•'
|
||||
u.selectedRemoteConnection = true
|
||||
if record.NeedsMigration && strings.TrimSpace(record.RemoteProfileID) == "" && strings.TrimSpace(record.CredentialEntryID) == "" {
|
||||
u.showStatusMessage("This saved remote came from an older local-sign-in format. Open it again, then save the remote in the vault to migrate it.")
|
||||
}
|
||||
}
|
||||
|
||||
func (u *ui) remotePreferencesCurrentSummary() string {
|
||||
|
||||
@@ -4560,6 +4560,44 @@ func TestUILoadRecentRemotesIgnoresLegacySavedCredentials(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
if !u.recentRemotes[0].NeedsMigration {
|
||||
t.Fatal("recentRemotes[0].NeedsMigration = false, want true for legacy saved credentials")
|
||||
}
|
||||
if got := u.recentRemotes[0].Username; got != "" {
|
||||
t.Fatalf("recentRemotes[0].Username = %q, want empty after migration strip", got)
|
||||
}
|
||||
if got := u.recentRemotes[0].Password; got != "" {
|
||||
t.Fatalf("recentRemotes[0].Password = %q, want empty after migration strip", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUINewUIShowsMigrationStatusForLegacyRecentRemoteCredentials(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dir := t.TempDir()
|
||||
recentRemotesPath := filepath.Join(dir, "recent-remotes.json")
|
||||
content := `[
|
||||
{
|
||||
"baseUrl": "https://dav.example.com",
|
||||
"path": "vaults/home.kdbx",
|
||||
"username": "alice",
|
||||
"password": "secret-1"
|
||||
}
|
||||
]`
|
||||
if err := os.WriteFile(recentRemotesPath, []byte(content), 0o600); err != nil {
|
||||
t.Fatalf("WriteFile(recent-remotes.json) error = %v", err)
|
||||
}
|
||||
|
||||
u := newUIWithState("desktop", &session.Manager{}, statePaths{
|
||||
DefaultSaveAsPath: filepath.Join(dir, "default.kdbx"),
|
||||
RecentVaultsPath: filepath.Join(dir, "recent-vaults.json"),
|
||||
RecentRemotesPath: recentRemotesPath,
|
||||
UIPreferencesPath: filepath.Join(dir, "ui-prefs.json"),
|
||||
})
|
||||
|
||||
if got := u.state.StatusMessage; got != "This saved remote came from an older local-sign-in format. Open it again, then save the remote in the vault to migrate it." {
|
||||
t.Fatalf("StatusMessage = %q, want legacy recent-remote migration notice for the selected startup remote", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUIApplyRecentRemoteRecordRestoresVaultBindingSelection(t *testing.T) {
|
||||
@@ -4586,6 +4624,22 @@ func TestUIApplyRecentRemoteRecordRestoresVaultBindingSelection(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUIApplyRecentRemoteRecordShowsMigrationNoticeForLegacySavedCredentials(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
u := newUIWithSession("desktop", &session.Manager{})
|
||||
|
||||
u.applyRecentRemoteRecord(recentRemoteRecord{
|
||||
BaseURL: "https://dav.example.com",
|
||||
Path: "vaults/home.kdbx",
|
||||
NeedsMigration: true,
|
||||
})
|
||||
|
||||
if got := u.state.StatusMessage; got != "This saved remote came from an older local-sign-in format. Open it again, then save the remote in the vault to migrate it." {
|
||||
t.Fatalf("StatusMessage = %q, want legacy per-record migration notice", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUIStartupPreselectsNewestTargetAcrossLocalAndRemote(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user