Skip to content

Validate a password set through the API before hashing it; a password change no longer verifies the email (#570) - #577

Merged
silverbackdan merged 3 commits into
mainfrom
fix/570-password-validation
Oct 31, 2026
Merged

silverbackdan merged 3 commits into
mainfrom
fix/570-password-validation

Conversation

@silverbackdan

Copy link
Copy Markdown
Collaborator

Fixes #570.

Validation. UserNormalizer called processChanges(), which hashed plainPassword and erased it during denormalization, so API Platform's validation never saw it: PATCH /users/{id} {"plainPassword": "abc"} was a 200, and a password over 4096 characters was a 500 from the hasher.

  • processChanges() no longer hashes. UserDataProcessor::hashPassword() is public and called after validation: on the API by a new UserPasswordStateProcessor (decorates api_platform.state_processor.locator at 16, before the persist and inside the user notification decorator, so the password changed email still compares the new hash), and in EntityPersistFormListener after processChanges(), after the form has validated.
  • The plainPassword Length(min: 6, max: 4096) is now in Default as well as User:password:create. It ignores null, so a user write without a password is unaffected; NotBlank stays in User:password:create, so the forms still require a password and the API does not. A constraint in both groups is validated once, so the register form (which validates Default and User:password:create) reports one violation.

Side effect. hashPassword() no longer sets emailAddressVerified. Only passwordReset() sets it, since the reset link proves the inbox (and the unverified login message tells users to reset their password). A password set through the API, the change password form or any custom path leaves verification as it was.

Tests:

  • Functional User\UserPasswordTest: a short or over-long password on PATCH is a 422 on plainPassword with the password unchanged and no email; a super admin's POST with a short password is a 422; a valid password is saved hashed and the password changed email sent; setting a password on an unverified user leaves them unverified.
  • Unit: UserPasswordStateProcessorTest, UserDataProcessorTest (hashing, no verify, processChanges() does not hash, reset verifies), EntityPersistFormListenerTest (the form path hashes), StateDecoratorServicesTest.

🤖 Generated with Claude Code

@codecov

codecov Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.04%. Comparing base (43f3702) to head (3396609).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##               main     #577   +/-   ##
=========================================
  Coverage     97.04%   97.04%           
- Complexity     5298     5302    +4     
=========================================
  Files           432      433    +1     
  Lines         13719    13724    +5     
=========================================
+ Hits          13313    13318    +5     
  Misses          406      406           
Flag Coverage Δ
phpunit 97.04% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@silverbackdan
silverbackdan merged commit 6f07272 into main Oct 31, 2026
14 of 15 checks passed
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.

Password set via PATCH /users/{id} skips validation (hashed during denormalization) and marks the email verified

1 participant