Skip to content

fix(credentials): delete the keyring entry when removing credentials - #344

Open
iabaako wants to merge 5 commits into
mainfrom
fix/342-remove-credentials
Open

iabaako wants to merge 5 commits into
mainfrom
fix/342-remove-credentials

Conversation

@iabaako

@iabaako iabaako commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Pull Request Summary 🚀

What does this PR do? 📝

Makes Remove Credentials actually delete the SurveyCTO password from the OS keyring, and report failure when it can't.

  • Remove button (import_view.py): passes the selected server and credential type, which it used to mix up. It shows a success message only when the deletion succeeded, and an error otherwise.
  • delete_stored_credentials (secure_credentials.py): reads the username from the right place in the metadata file. It used to always get None, so keyring.delete_password never ran.
  • Keyring errors: a password confirmed gone from the keyring still clears the entry. Any other keyring error is reported as a failure and keeps the entry, so the user can retry.

Closes #342

Why is this change needed? 🤔

Users were told their credentials were deleted while the password stayed in the keyring, where anyone using the same OS account could retrieve it. Three defects stacked (see #342): wrong arguments from the view, a wrong metadata lookup, and swallowed errors. Release blocker for v1.2.0 (#339).

How was this implemented? 🛠️

  • The view passes selected_server and the credential_type key that list_stored_credentials actually returns.
  • delete_stored_credentials builds the service name the same way saving does and reads the username from metadata[service_name].
  • A PasswordDeleteError counts as success only after keyring.get_password confirms the password is absent, so an entry whose password was removed outside DataSure can still be cleared from the dropdown. The macOS Keychain and KWallet backends also raise PasswordDeleteError for access errors and cancelled unlocks, so if the password is still there, or the check itself fails, the result is a failure and the entry is kept.

Merge danger: two-way door, easy to revert. Blast radius: narrow, limited to the Remove Credentials button. Saving and retrieving credentials are unchanged.

How to test or reproduce ? 🧪

Before: the existing tests asserted only that delete_stored_credentials was called, and asserted success when the keyring delete failed.
After:

  • test_delete_credentials_success asserts the exact service name and username passed to keyring.delete_password.
  • test_delete_credentials_keyring_error_is_failure: a keyring error returns failure and keeps the entry.
  • test_delete_credentials_already_removed_from_keyring: a password confirmed missing returns success and removes the entry.
  • test_delete_credentials_password_delete_error_not_confirmed: a PasswordDeleteError with the password still stored, or with a failing check, returns failure and keeps the entry.
  • test_delete_credentials_button and test_delete_credentials_failure_shows_error: the view passes the server and type, and shows an error on failure.

All failed before the fix. just test: 3910 passed, 6 skipped. The keyring is mocked throughout.

Manual check: save credentials, click Remove, then confirm the datasure_scto_login_<server>_<project> entry is gone from Windows Credential Manager or macOS Keychain, and from the dropdown.

Screenshots (if applicable) 📷

N/A

Checklist ✅

  • I have run and tested my changes locally
  • I have limit this PR to less than 1000 lines of code change (if not, explain why)
  • I have updated/added tests to cover my changes (if applicable)
  • I have updated/added requirements to cover my changes (if applicable) (N/A, no new requirements)
  • I have run linting and formatting on any code changes (if applicable)
  • I have updated the documentation (README, etc.) accordingly (CHANGELOG)
  • I have reviewed and resolved any merge conflict

Reviewer Emoji Legend

:code: Meaning
😃👍💯 :smiley: :+1: :100: I like this...

...and I want the author to know it! This is a way to highlight positive parts of a code review.
⭐⭐⭐ :star: :star: :star: Important to fix before PR can be approved...

And I am providing reasons why it needs to be addressed as well as suggested improvements.
⭐⭐ :star: :star: Important to fix but non-blocking for PR approval...

And I am providing suggestions where it could be improved either in this PR or later.
⭐ :star: Give this some thought but non-blocking for PR approval...

...and consider this a suggestion, not a requirement.
❓ :question: I have a question.

This should be a fully formed question with sufficient information and context that requires a response.
📝 :memo: This is an explanatory note, fun fact, or relevant commentary that does not require any action.
⛏ :pick: This is a nitpick.

This does not require any changes and is often better left unsaid. This may include stylistic, formatting, or organization suggestions and should likely be prevented/enforced by linting if they really matter
♻️ :recycle: Suggestion for refactoring.

Should include enough context to be actionable and not be considered a nitpick.

🤖 Generated with Claude Code

iabaako and others added 3 commits October 15, 2026 21:54
Remove Credentials reported success but left the password in the OS
keyring because of three stacked defects:

- The view read a non-existent "type" key and passed it as the server.
  It now passes the selected server and the credential_type returned by
  list_stored_credentials.
- delete_stored_credentials read the username from the top level of the
  metadata file, which is keyed by service name, so
  keyring.delete_password never ran. It now reads metadata[service_name].
- A KeyringError was logged and success still returned, and the view
  ignored the result. A keyring failure now returns a keyring_error
  result (metadata is kept so the entry can be retried) and the view
  shows st.error instead of st.success.

Tests now assert the exact delete_password arguments, the view's call
arguments, and that keyring failures surface as errors.

Closes #342

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y gone

keyring raises PasswordDeleteError when there is nothing to delete. Treat
that as success and remove the metadata entry, so a credential removed
outside DataSure can still be cleared from the Remove dropdown.

Refs #342

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@iabaako
iabaako requested a review from a team as a code owner October 15, 2026 21:57
@iabaako iabaako added this to the v1.2.0 milestone Oct 15, 2026
@iabaako iabaako self-assigned this Oct 15, 2026
@iabaako iabaako mentioned this pull request Oct 15, 2026
13 of 54 tasks
@iabaako
iabaako requested a balanced review from Copilot October 15, 2026 22:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

PasswordDeleteError can still trigger false success and remove retry metadata while the password remains stored.

1 open finding
What changed in this PR

Fixes DataSure’s credential-removal flow for #342, a v1.2.0 release blocker.

Changes:

  • Passes the correct server and credential type from the view.
  • Fixes the username lookup and reports keyring deletion failures.
  • Adds regression tests and a changelog entry.
File Description
tests/​views/​test_import_view.py Tests deletion arguments and success/error messages.
tests/​utils/​test_secure_credentials.py Tests keyring calls and deletion outcomes.
src/​datasure/​views/​import_view.py Corrects deletion arguments and displays failures.
src/​datasure/​utils/​secure_credentials.py Fixes metadata lookup and keyring error handling.
CHANGELOG.md Documents the credential-removal fix.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/datasure/utils/secure_credentials.py Outdated
…dDeleteError

The macOS Keychain and KWallet keyring backends raise PasswordDeleteError
for access errors and cancelled unlocks, not only for missing passwords.
Treating it as "already deleted" could report success and drop the
metadata entry while the password stayed in the keyring.

Now the metadata entry is cleared only when keyring.get_password confirms
the password is absent; otherwise the deletion is reported as a failure
and the entry is kept for retry.

Addresses Copilot review on #344.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The narrow changes address the reported defects and prior feedback with targeted regression coverage and no unresolved findings.

0 open findings

1 resolved since last review

🧠 Review effort: Balanced

@iabaako
iabaako requested a review from NKeleher October 15, 2026 22:47
@sonarqubecloud

Copy link
Copy Markdown

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove Credentials reports success but the keyring entry survives

2 participants