Skip to content

users.previous_accounts and users.merge - #4

Merged
imit merged 1 commit into
mainfrom
previous-accounts
Sep 28, 2026
Merged

imit merged 1 commit into
mainfrom
previous-accounts

Conversation

@imit

@imit imit commented Sep 28, 2026

Copy link
Copy Markdown
Contributor

What this adds

Someone deleted who signs in again now gets a fresh account (a new id). Their earlier account stays deleted, with its history. Two secret-key calls cover it:

  • scute.users.previous_accounts(user_id) lists the person's earlier, deleted accounts in the app, newest first. Each has id, status, created_at, deleted_at, merged_into (once merged), and what it still holds: roles and passkeys counts, and mfa_methods.
  • scute.users.merge(user_id, from_id) brings one of those into the live account.
    • Roles, resource roles, passkeys, MFA methods and unused backup codes move over.
    • For meta and attributes the live account wins.
    • History stays on the old account.
    • It answers {user_id, merged, moved: {...}}. Merging the same account again raises APIError (422, already_merged).

Both are typed and documented, with a README entry. The README also says that someone deactivated and then deleted can't sign in again (403, account_deactivated).

Tests

  • Unit: the fake API serves the two routes. The test covers listing, merging (the body, the secret key) and the second merge's 422 already_merged, and checks that the POST isn't retried.
  • Live, fresh account:
    1. A test identity signs in, gets a role and is deleted with the secret key.
    2. It signs in again and gets a new id; the old id answers 404.
    3. previous_accounts lists the old account with its role.
    4. merge moves the role, and the check now allows what the role grants.
    5. The old account shows merged_into, and a second merge is already_merged.
    6. Both accounts are cleaned up.
  • Live, deprovisioned: a user is deactivated and then deleted. Sign-in then answers 403 account_deactivated, and find_by_identifier returns None.

Results

  • Unit suite: 141 passed.
  • ruff, mypy --strict src tests/live: clean.
  • pytest -m live against scute-api-v2 v24: 55 passed, 0 failed. It used seven sign-ins plus one refused.
  • After the run: the app has no users, roles, resources, agents or properties, and its settings are back as they were.

Someone deleted who signs in again gets a fresh account (a new id).
users.previous_accounts(user_id) lists the person's earlier, deleted accounts
in the app, and users.merge(user_id, from_id) brings one into the live
account: roles, resource roles, passkeys, MFA methods and unused backup codes
move over, the live account wins on meta and attributes, history stays on the
old account. A second merge raises APIError (422, already_merged).

Unit tests against the fake API; README entry. Live tests: a test identity
signed in, given a role and deleted signs in again to a new id (the old one is
404), previous_accounts lists the old account, merge moves the role and a
second merge is already_merged; someone deactivated and then deleted gets 403
account_deactivated at sign-in. 55 passed against scute-api-v2 v24.
@imit
imit merged commit e97d348 into main Sep 28, 2026
4 checks passed
@imit
imit deleted the previous-accounts branch September 28, 2026 16:48
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