CoolCMS API

ContentCreationLimiter
in package

FinalYes

Throttles content creation in the API layer, equally for the front end (`ApiBridge`) and the external `:Api:*:save` with a JWT.

IMPORTANT: the throttle lives here, not in the form. reCAPTCHA and front limits sit in components, so a logged-in user with a token (any registered user can call /api/system/auth/login) would bypass them by calling the API directly. The manager is the only place every insert passes through.

The IP comes from IRequest (the request of the visitor or client, the same PHP process), never from the payload. In CLI it is null and only the user is counted.

Fail-open like the whole RateLimit: a storage outage does not block creation.

Table of Contents

Properties

$audience  : FieldAudienceContext
$httpRequest  : IRequest
$rateLimit  : RateLimit
$rateLimitConfig  : RateLimitConfig

Methods

__construct()  : mixed
isActivationMailAllowed()  : bool
Returns whether another activation or verification email may be sent to `$subject` (normalized email).
isAllowed()  : bool
Returns whether the user `$userId` may create another record of kind `$scope`.
isAnonymousInsertAllowed()  : bool
Returns whether an anonymous visitor may create another record of kind `$scope`.
resolveRemoteIp()  : string|null
hit()  : bool
isExemptCaller()  : bool
Returns whether the throttle does not apply.

Properties

Methods

isActivationMailAllowed()

Returns whether another activation or verification email may be sent to `$subject` (normalized email).

public isActivationMailAllowed(string $scope, string $subject) : bool

Applies to anonymous callers too, since it protects the recipient, not the sender identity. Two dimensions, as in sendActivationEmail*: per IP (one attacker spraying many addresses) and per address (a botnet targeting one mailbox).

Parameters
$scope : string
$subject : string
Return values
bool

isAllowed()

Returns whether the user `$userId` may create another record of kind `$scope`.

public isAllowed(string $scope, int|null $userId) : bool

An anonymous user ($userId null) always passes: anonymous paths have their own protection (reCAPTCHA, per-IP limits of individual managers) and this throttle does not count them. The external API cannot create without a token (jwt.guardMode: enforce, save actions are not #[PublicAction]).

Parameters
$scope : string

content kind that separates quotas (bazaar-response, discussion-message)

$userId : int|null
Return values
bool

isAnonymousInsertAllowed()

Returns whether an anonymous visitor may create another record of kind `$scope`.

public isAnonymousInsertAllowed(string $scope, string $subject) : bool

Complements isAllowed(), which does not count anonymous users, for anonymous paths where reCAPTCHA is not enough (it can be disabled in settings or bypassed), typically the contact form. Two dimensions: per IP (from the server, never from the payload; only a hash is in the key) and per normalized $subject (form email, lowercase and trimmed). An empty subject is not counted, because its key would be shared by the whole site. Uses its own config keys ratelimit.anonymousInsert; the activation email quota is not borrowed. Exemptions (privileged or unknown context) and fail-open are the same as in the other methods.

Parameters
$scope : string
$subject : string
Return values
bool

hit()

private hit(string $key, int $limit, int $window) : bool
Parameters
$key : string
$limit : int
$window : int
Return values
bool

isExemptCaller()

Returns whether the throttle does not apply.

private isExemptCaller() : bool

Decided by the context that only the guard fills (FieldAudienceContext), never by the request:

  • a privileged caller (an admin holding a role with privileged = 1): manual admin work (address import, support replies) must not hit a throttle meant for a script with a registered user's token;
  • an unknown context (isResolved() === false): the guard was not evaluated at all, e.g. the emergency mode apiRbac.internalMode: off (admin without limits), cron, CLI. Same boundary as the field filter: unknown means no filtering.

In production both modes are enforce, so on the API path the context is always set and every unprivileged caller is throttled.

Return values
bool
On this page

Search results