Repository navigation
Conversation
Shared service method that disables 2FA for a user: clears two_factor_enabled and two_factor_enforced_at, deletes every recovery code and revokes every trusted device in a single transaction, then records a device_revoked audit event best-effort. The actor decides the password rule: null (console) and another user (admin) need no password, the user acting on their own account must provide the current one. The settings_changed audit event is left to the caller, which knows the context (reason, actor, operator). Group-enforced users are still challenged at login afterwards, as enforcement is derived from group membership.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
📘 OpenAPI / Swagger preview ➡️ https://OpenStackweb.github.io/openstackid/openapi/pr-169/ This page is automatically updated on each push to this PR. |
Stacked on #166 (base:
feat/mfa-admin-enforcement-and-reset-commands), which is stacked on #125. Merge bottom-up: #125, #166, then this one.Summary
Provisional implementation of the shared disable/reset service method that ClickUp ticket 12 (
idp:reset-2fa) needs and ticket 14 (profile toggle and admin CRUD) will reuse. The ticket allows implementing it in ticket 12 when ticket 14 has not landed, with this exact contract:IRecoveryCodeService::disableTwoFactor(User $user, ?string $currentPassword, ?User $actor): voiddisable2FA()(clearstwo_factor_enabledandtwo_factor_enforced_at), deletes every recovery code, and revokes every trusted device (is_revoked = 1, rows are kept).device_revokedaudit event, best-effort: a failure there does not fail the operation.$actordecides the password rule:null(console, e.g.idp:reset-2fa): no password.$currentPasswordmust match, otherwiseValidationExceptionand nothing changes.settings_changed: the caller knows the context (reason, actor, operator) and emits it, so there is exactly one row.User::shouldRequire2FA()).Things to know
DeviceTrustService::removeTrustedDevicesas the ticket suggests: that method's audit call opens its own transaction, and nesting transactions can close the entity manager if the inner one fails (the existing code inRecoveryCodeServicewarns about this). It usesIUserTrustedDeviceRepository::revokeAllForUserinside the same transaction instead, which is whatremoveTrustedDevicesdoes for the DB part.RecoveryCodeServicegets a new constructor dependency (IUserTrustedDeviceRepository). Nothing instantiates it withnew; the container resolves it.disableTwoFactor, the two need to be reconciled; ticket 14 can adopt this one directly.Test plan
tests/unit/RecoveryCodeServiceDisableTwoFactorTest.php: 6 tests (console, admin, self-service with correct / wrong / missing password, audit failure).tests/DisableTwoFactorIntegrationTest.php: 2 tests against the DB (everything cleared, 2 devices revoked, 1device_revokedrow and nosettings_changed; wrong password leaves state untouched).tests/RecoveryCodeRegenerationTest.phpalready fails on the base branch (Class "Strategies\MFA\MFAChallengeStrategyFactory" not found), unrelated to this change.