AdvertPasswordResetTrait
The "forgot advert password" for adverts placed without an account (Bazaar adverts, Comcat company profiles). One implementation for both modules; the using manager-tier class supplies the module specifics.
Previously the front component generated a new password, e-mailed it in plain text and
then tried to write it over the Api (as a guest, which the updateColumns guard refused —
the e-mailed password never worked). The limit lived only in the session, so dropping the
cookie bypassed it. Now:
- self::sendPasswordReset() stores a one-time token hash + expiry on the advert and e-mails a link to the edit page; the current password stays valid,
- self::resetPassword() checks the token from the link and sets the chosen password,
- both are rate limited on the server (advert + IP), the request answers the same way for a matching and a non-matching e-mail (the advert e-mail is often public on the detail).
Requires in the using class:
$this->appContext(AppContext),$this->emailManager(Api System EmailManager),$this->passwordHasher,$this->rateLimit,$this->rateLimitConfig,findAdvertForReset(int): ?object(the module Advert entity, fresh from the DB),updateAdvertColumns(int|array, array): Result(array id = compound WHERE[[0 => id, column => value]], used for the conditional token restore),getResetModule()(bazaar|comcat),getResetEmailKey(),getFrontPresenter()(:Front:Bazaar:Advert),getDetailSlug(),getEditSlug().
Table of Contents
Methods
- resetPassword() : Result
- `USER_PASSWORD_RESET_KEY_INVALID` for an unknown advert and for a wrong, expired or used token alike; `INVALID_LENGTH` for a short password (the token is not consumed then).
- sendPasswordReset() : Result
- Always `success(OK)` — whether the advert exists, the e-mail matches, the limit was hit or the delivery failed. Internal failures go to the `email` / `auth` log only.
- deliverPasswordResetLink() : Result
- passesResetLimit() : bool
- passesSendLimit() : bool
- Two dimensions, both must pass (values of `ratelimit.sendNewPassword`): per advert stops a botnet flooding one owner, per IP stops one client walking many adverts. The IP is hashed.
Methods
resetPassword()
`USER_PASSWORD_RESET_KEY_INVALID` for an unknown advert and for a wrong, expired or used token alike; `INVALID_LENGTH` for a short password (the token is not consumed then).
public
resetPassword(int $advertId, string $key, string $password, string|null $ip) : Result
Success writes the hash and clears the token — the link works once.
Parameters
- $advertId : int
- $key : string
- $password : string
- $ip : string|null
Return values
ResultsendPasswordReset()
Always `success(OK)` — whether the advert exists, the e-mail matches, the limit was hit or the delivery failed. Internal failures go to the `email` / `auth` log only.
public
sendPasswordReset(int $advertId, string $email, string|null $ip) : Result
Parameters
- $advertId : int
- $email : string
- $ip : string|null
Return values
ResultdeliverPasswordResetLink()
private
deliverPasswordResetLink(int $advertId, string $email, string $advertName, array<string, mixed> $previous) : Result
Parameters
- $advertId : int
- $email : string
- $advertName : string
- $previous : array<string, mixed>
-
token columns before this request (fresh from the DB)
Return values
ResultpassesResetLimit()
private
passesResetLimit(string|null $ip) : bool
Parameters
- $ip : string|null
Return values
boolpassesSendLimit()
Two dimensions, both must pass (values of `ratelimit.sendNewPassword`): per advert stops a botnet flooding one owner, per IP stops one client walking many adverts. The IP is hashed.
private
passesSendLimit(int $advertId, string|null $ip) : bool
Parameters
- $advertId : int
- $ip : string|null