Problem:
Some key functionality attached to user relies on global state to gain access to the current user's session as well as HTTP request information such as the client's IP address and cookies. This is needed in particular for abuse prevention via auto-blocking (based on IP and cookies) as well as rate limit (per user, per session, or per IP). We need a way to provide access to this information without having to rely on global state.
Examples of this can be found in PermissionManager and RequestManager, or by looking for usages of User::getRequest or User::pingLimiter. Callers of RequestContent::getMain() are also problematic, though that method is often called for other reasons as well.
## Proposal 2020 (Authority):
Define a new kind of object that represents the acting agent's authority. That object would encapsulate the access to any necessary information and state, and would provide methods for authorizing actions. That is, instead of having PermissionManager act on a value object like UserIdentity (or ActingUser, see below), the Authority object would internally access the PermissionManager, and any other services needed.
Rough draft of the Authority interface:
```lang=php
interface Authority {
function authorizeAction( $action );
function authorizeActionOn( $action, $page );
function getAuthorizationErrors( $action, $rigor );
function getAuthorizationErrorsOn( $action, $page, $rigor );
}
```
The `getAuthorizationErrors` methods check authorization, which may or may not include checks for IP blocks and cascading protection, depending on $rigor. They are idempotent. The `authorizeAction` methods throw an exception upon failure, and may modify state - in particular, they can update counters and enforce rate limits.
We would create multiple implementations of this interface, likely including one that fails all authorization as well as one that allows all authorization. But most importantly, there would be one that covers the logic currently implemented in `PermissionManager::userCan` and friends: it would be instantiated based on the current request, session, and user identity, and would perform checks based on the user group, IP blocks, rate limits, etc.
The corresponding methods in PermissionManager would be deprecated and delegate to the new class. PermissionManager would only retain logic about group based permissions, not about blocks or other session based checks.
The User class would implement the Authority interface as well, delegating to the session based Authority instance if the User object is the user for the session. `getAuthorizationErrors` could still function for a user that isn't the acting user, but `authorizeAction` would always fail for a user based Authority that doesn't correspond to the current request.
Jobs can use an implementation of Authority that is based on the user identity and checks blocks, but does not use session or IP based information.
Since User implements the new interface, the new functionality becomes available everywhere immediately. Type hints against user can be narrowed to Authority when and where possible, allowing for alternative light weight implementations of the interface to be used.
## Original Proposal (ActingUser):
Introduce ActingUser as an interface that extends UserIdentity. Critical checks, such as calls to PermissionManager::userCan with RIGOR_SECURE, must fail if presented with a UserIdentity that is not an ActingUser, or should require an ActingUser in the signature. The ActingUser represents not a user as such, but a user acting withing a certain context (a device, a session, oath token, address, etc). This context represents the execution context of the entire application (thanks th PHP's per-request execution model). For maintenance scripts and async jobs, the execution context would essentially just be "CLI" or "JOB".
Code that needs to check for context-based blocks or limits (session, IP address, cookies, device) will need access to some aspects of the http request. ActingUser could provide methods to access this information, but it's unclear how exactly these methods should behave in a non-web execution context. The relevant calling code will probably have to know about web-mode and cli-mode ActingUsers in any case.
Code that makes direct use of ActingUsers should be rare. However, it's likely to be used via many code paths for many use cases.
NOTE: this supersedes T218555