Skip to content

feat: add command to release a retired learner's email for reuse - #39311

Open
rahulkanneri-2u wants to merge 2 commits into
openedx:masterfrom
rahulkanneri-2u:lp-1228-release-retired-email
Open

rahulkanneri-2u wants to merge 2 commits into
openedx:masterfrom
rahulkanneri-2u:lp-1228-release-retired-email

Conversation

@rahulkanneri-2u

Copy link
Copy Markdown
Contributor

Description

A retired learner's original email is permanently blocked from re-registration, because is_email_retired() still finds a User row whose email equals the salted retired hash of that address.

This adds:

  • release_retired_learner_email(user) in openedx/core/djangoapps/user_api/accounts/utils.py. It replaces the retired-hash email on the learner's auth_user row with a placeholder, retired__uid_<user_id>@<RETIRED_EMAIL_DOMAIN>. That clears the match, so the original address can be used for a new account.

    • It only changes the email column. The UserRetirementStatus row and retirement history are left untouched.
    • It runs under select_for_update(), so it can't interleave with a concurrent handle_retirement_cancellation() or another release for the same user.
    • It requires the retirement to be in the COMPLETE state. If the status row has already been archived and deleted, it falls back to checking that the email is still in the retired-email domain. Otherwise it raises RetirementStateError.
    • It is idempotent: releasing an already-released user is a no-op.
  • A release_retired_user_email management command that wraps the function for operator/support use:

    ./manage.py lms release_retired_user_email --user_id <id>
    

    The command takes --user_id only, not a username, to avoid taking PII as input for a retired-learner tool.

There is no automatic trigger. This is manual only.

Impacted roles: Operator / Support (the command), and indirectly Learners who want to reuse a previously retired email.

Supporting information

Testing instructions

  1. Run the tests:

    pytest openedx/core/djangoapps/user_api/accounts/tests/test_utils.py -k ReleaseRetiredLearnerEmail
    pytest openedx/core/djangoapps/user_api/management/tests/test_release_retired_user_email.py
    
  2. Manual check in an LMS shell:

    • Retire a test user through the normal retirement pipeline until COMPLETE.
    • Confirm is_email_retired('<original email>') is True.
    • Run ./manage.py lms release_retired_user_email --user_id <id>.
    • Confirm the user's email is now retired__uid_<id>@retired.invalid and is_email_retired('<original email>') is False.
    • Confirm the original email can register a new account.
  3. Run the command against a user whose retirement is still in progress (for example RETIRING_LMS), or one that isn't retired. It should fail with a CommandError and leave the email unchanged.

Deadline

None

Other information

  • No migrations or settings changes.
  • The placeholder domain comes from settings.RETIRED_EMAIL_DOMAIN, so it stays consistent with what the retirement code treats as "retired" on a given deployment.
  • Limitation: the original email can be reused, but the retired account keeps its auth_user row, as retirement intends.

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.

1 participant