AdvertScopeGuardTrait
Single definition of the rule "may this caller see the data of one advert?".
Used by the response tab and the statistics tab of the advert edit page, in two modules, so four presenters. If the rule lived in each of them, the four copies would drift apart.
A caller is allowed for any of three independent reasons, checked cheapest first:
- a privileged role (
isGrantedByPrivilegedRole()reads an in-memory map, so an admin costs no extra SELECT); - classic ownership:
insertUserIdof the advert loaded from the DB equalsjwt.userId; - a capability: the token carries a valid claim for this
advertId, with the right scope and a password fingerprint equal to the hash in the DB (JwtCapability::grants()).
IMPORTANT: $advertId from the request is only the caller's claim of which advert,
never proof of ownership. Proof comes from comparing against the entity loaded from the
DB by that id; a foreign advertId yields false and the caller returns an empty
collection.
Password-based advert editing runs without identity (jwt.userId is 0, a guest).
Ownership is proven by a capability claim issued by
CapabilityTokenIssuer only after the front session passed
against the password hash in the DB. The fingerprint is compared again against the DB,
so a password change by the owner invalidates access immediately.
Table of Contents
Methods
- advertScopeAllows() : bool
- Returns true when the caller may see the data of advert `$advertId`.
- advertScopeCapability() : string
- Capability scope of this module (`bazaar.advert.edit` or `comcat.advert.edit`).
- advertScopePrivilegeKey() : string
- Privilege key to ask the privileged role about: always `getAll` of this presenter, i.e. "may the caller read this collection without restriction?".
- loadScopedAdvert() : object|null
- Loads the advert from the DB by id; returns `null` when it does not exist.
Methods
advertScopeAllows()
Returns true when the caller may see the data of advert `$advertId`.
protected
advertScopeAllows(int|null $advertId) : bool
Fail-closed on every axis: a missing or non-positive id, a nonexistent advert, a
foreign owner and an invalid capability all end in false.
Parameters
- $advertId : int|null
Return values
booladvertScopeCapability()
Capability scope of this module (`bazaar.advert.edit` or `comcat.advert.edit`).
protected
abstract advertScopeCapability() : string
Without the distinction a capability for Bazaar advert 5 would unlock company 5.
Return values
stringadvertScopePrivilegeKey()
Privilege key to ask the privileged role about: always `getAll` of this presenter, i.e. "may the caller read this collection without restriction?".
protected
abstract advertScopePrivilegeKey() : string
Return values
stringloadScopedAdvert()
Loads the advert from the DB by id; returns `null` when it does not exist.
protected
abstract loadScopedAdvert(int $advertId) : object|null
IMPORTANT: never build the entity from request data; the ownership decision relies on DB state.
The return type is ?object on purpose: both shared classes are generic, so a union
would need seven type parameters. instanceof in self::advertScopeAllows()
narrows it and is fail-closed: anything other than an advert or company means false.
Parameters
- $advertId : int