История коммитов

simba77
feat(auth): add the authentication and authorization skeleton
Introduces Johncms\Auth: the layer the new sign-in and permission system is
built on. Nothing calls it yet, so behaviour is unchanged.

Authentication is a chain: every AuthenticatorInterface tagged
johncms.auth.authenticator is asked in turn and the first one to recognise the
request wins; a request nobody recognises belongs to a guest. CurrentUser
resolves that once per request, lazily, and is reset between requests.

Authorization is a chain of voters: any Deny refuses, a single Allow is enough,
and a check nobody voted on is refused. The asymmetry is what later lets a ban,
an impersonation limit or the abilities of an API token take away what a role
grants. Refusing by default means a permission whose voter was forgotten closes
the door instead of opening it.

Identity carries ids, role slugs and permission keys and nothing that needs a
query, so authorization is testable without a container or a schema —
tests/Support/IdentityFactory is what that buys.

The can() helper and its Twig function are not here: with no voters registered
they would answer false to everything, and a silent denial is the worst kind of
placeholder. They arrive with the role voter.
simba77
chore(mail): drop the unused smtp name option
A leftover of laminas-mail, where it was the HELO hostname. MailFactory builds the DSN from host, port, username, password, encryption and auth_mode and never reads this key, so it only misled whoever configured SMTP.
simba77
docs: update the documentation submodule
simba77
docs: update the documentation submodule
simba77
docs: describe the CSRF middleware and the validator replacement in the changelog
simba77
refactor: remove laminas-validator
The legacy validator, its rules and its message map are gone, and with the package go laminas-servicemanager, laminas-stdlib and laminas-escaper. No laminas package is left in the dependency tree.

Not one msgid was lost: the strings the rules need are registered by literal d__() calls in their constructors and factories, so twenty catalogs survive the migration untouched. What the .pot loses is the detailed laminas hostname diagnostics and the CSRF message, none of which the code produces any more.

.agents/validation.md documents the API, the null policy, how to add a rule and where its messages live.
simba77
refactor(install): move the installer to the new validator
The last caller of the legacy validator and the last reference to Laminas\Validator\Hostname. The email stays mandatory: it was only rejected as empty because the previous EmailAddress failed on an empty value, and the new rule requires a value outright.
simba77
chore: drop the orphaned legacy validator imports
Left behind when the CSRF-only calls were removed.
simba77
refactor(profile,registration): move the last forms to the new validator
The optional profile fields now say so: everything a visitor may leave blank carries allowEmpty, including the email, which the previous ruleset left optional only as a side effect of ModelNotExists finding nothing for an empty value.

The MX check of the address survives as the MxRecord rule, and the consent message again belongs to the rule that needs it rather than to every Identical of the form.
simba77
refactor(admin,consent): move the remaining admin forms to the new validator
The forum structure form uses ValidationResult::withError() for the cycle check, so a domain failure found outside the ruleset joins the result instead of unpacking it into an array.