ResponseMarkReadTrait
Marks a response as read from an e-mail link, without login, using only the key stored on the response (column `read_key`, random, set by {@see self::generateReadKey()} in `beforeSave()` when the response is created).
The key is verified on the Api side in the receiver check of the response
(ResponseReceiverTrait::isResponseReceiver(), key
branch; the comparison is done by self::readKeyMatches()). The front presenter and
the front layers do not check it; they only pass id and key from the URL. The RBAC key
:Api:{Bazaar,Comcat}:Response:markReadByKey (guest = 1) allows access without identity,
so security rests entirely on that check: without a valid key nothing is written
(self::markRead() must not be called without it).
IMPORTANT: contract:
- the action only switches
read = 1; it returns nothing from the response or the advert, - an invalid key, a response without a key (older ones, no backfill), a missing response
and a deleted response all return the same result (
INVALID_KEY); otherwise the action would be an oracle for the existence of a response, - modules stay separated because each manager looks in its own response table (a Bazaar key does not work for a response with the same id in Comcat, and vice versa),
- the key is not deleted after use: a valid key on an already read response is a success again, so the link can be opened repeatedly and shows the confirmation again.
Table of Contents
Methods
- generateReadKey() : string
- 24 random bytes = 48 hex characters (column `read_key` is `varchar(64)`).
- markRead() : Result
- Writes `read = 1` after the presenter decided that the caller is the receiver ({@see \App\Core\Traits\Api\Presenters\ResponseReceiverTrait::isResponseReceiver()}).
- readKeyMatches() : bool
- Tells whether the given key matches the stored one. A null or empty stored key (older responses) and an empty given key never match; the comparison runs in constant time.
Methods
generateReadKey()
24 random bytes = 48 hex characters (column `read_key` is `varchar(64)`).
public
static generateReadKey() : string
Return values
stringmarkRead()
Writes `read = 1` after the presenter decided that the caller is the receiver ({@see \App\Core\Traits\Api\Presenters\ResponseReceiverTrait::isResponseReceiver()}).
public
markRead(int $id) : Result
The manager decides nothing itself, so there is no second copy of the check. An already read response is a success again without another write; the key is not deleted.
Parameters
- $id : int
Return values
ResultreadKeyMatches()
Tells whether the given key matches the stored one. A null or empty stored key (older responses) and an empty given key never match; the comparison runs in constant time.
public
static readKeyMatches(string|null $stored, string $given) : bool
Parameters
- $stored : string|null
- $given : string