Issue #3207974: Clear password expiration for users whose policy role was removed
Problem
When a user has their password policy role removed while field_password_expiration is set to 1, they are permanently locked in the password reset redirect loop. Cron never clears the flag because it only queries users who currently have a policy role — users who lost the role are invisible to the expiration logic but still affected by the redirect.
Steps to reproduce:
- Create a password policy targeting a specific role (e.g. password_policy) with password_reset: 60
- Assign the role to a user and let cron expire their password (field_password_expiration = 1)
- Remove the password_policy role from the user
- The user is still redirected to the password change form on every page load, with no way out
Fix
Adds a cleanup pass at the end of password_policy_cron() that:
- Collects all roles from active policies with password_reset > 0
- Queries users who have field_password_expiration = 1
- Clears field_password_expiration and field_pending_expire_sent for users who no longer have any role covered by an active policy
- Skips cleanup entirely if any policy targets the authenticated role (all users would be covered)
- Respects the existing cron_threshold setting to avoid memory issues
Testing
Tested on Drupal 11 / PHP 8.3 / password_policy 4.0.3. Verified that:
- Users with the policy role are still expired normally by cron
- Users whose policy role was removed have their expiration flag cleared
- Externally authenticated (SSO) users are unaffected
- The cleanup respects the cron threshold limit
Closes #3207974