CoolCMS API

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
Result

sendPasswordReset()

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
Result
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
Result

passesResetLimit()

private passesResetLimit(string|null $ip) : bool
Parameters
$ip : string|null
Return values
bool

passesSendLimit()

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
Return values
bool
On this page

Search results