Currently SpecialPage has stable to override methods related to permission checks:
- SpecialPage::userCanExecute - User is passed in, but should probably always be the context user for the special page, it doesn't make too much sense to check for some other user. In practice SpecialPage's context user seem to be always provided, except SpecialPageFactory, where theoretically another user can be passed, but AFAIK never is.
- SpecialPage::checkPermissions- a wrapper over "userCanExecute" which throws an exception instead of returning false.
Additionally, FormSpecialPage defines a FormSpecialPage::checkExecutePermissions, another wrapper over SpecialPage::checkPermissions which is called from FormSpecialPage::execute, and can be overridden to make more checks.
The logic behind all these methods escapes me, there's no documentation on which methods to override under which circumstances, plus when permission check method is called, it doesn't know the context, so has to apply the most restrictive RIGOR level possible, which results in reading block status from DB_PRIMARY.
Since for Authority we need to make backwards-incompatible changes anyway, so it might be a good time to clean up the interface. I propose to replace this with 3 methods, following Authority. Names TBD.
/** * Check whether the context user has appropriate to execute the special page if it's restricted. * @stable to override * @return bool */ public function isExecutionAllowed(): bool; /** * Check whether the context user can probably execute the special page if they submit the form. * This method offers a fast, lightweight check, and may produce false positives. * It is intended for determining which UI elements should be offered to the user. * @stable to override * @throws PermissionException * @return void */ protected function probablyCanExecute(): void; /** * Check whether the context user definitely has appropriate rights to execute the special page. * Should be used immediately before performing the special page action. * @stable to override * @throws PermissionException * @return void */ protected function authorizeExecution(): void;
With PermissionException being a new exception, a wrapper over the PermissionStatus (derived from StatusValue), and a restriction that has been checked. Thus PermissionException can be converted into ErrorPageError derivative really easily for backwards compatibility.