Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
SsoHookHandler
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 4
156
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onSetupAfterCache
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 onGetUserPermissionsErrors
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
30
 onApiCheckCanExecute
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace MediaWiki\Extension\CentralAuth\Hooks\Handlers;
4
5use MediaWiki\Api\Hook\ApiCheckCanExecuteHook;
6use MediaWiki\Extension\CentralAuth\SharedDomainUtils;
7use MediaWiki\Hook\SetupAfterCacheHook;
8use MediaWiki\Permissions\Hook\GetUserPermissionsErrorsHook;
9
10/**
11 * Ensure that the SSO domain cannot be used for anything that is unrelated to its purpose.
12 */
13class SsoHookHandler implements
14    SetupAfterCacheHook,
15    GetUserPermissionsErrorsHook,
16    ApiCheckCanExecuteHook
17{
18
19    private SharedDomainUtils $sharedDomainUtils;
20
21    public function __construct(
22        SharedDomainUtils $sharedDomainUtils
23    ) {
24        $this->sharedDomainUtils = $sharedDomainUtils;
25    }
26
27    /** @inheritDoc */
28    public function onSetupAfterCache() {
29        if ( $this->sharedDomainUtils->isSharedDomain() ) {
30            // FIXME The REST API does not provide a hook for disabling APIs. No rest APIs
31            //   should be needed for login and signup so we can just throw unconditionally,
32            //   but this should be improved in the future.
33            // FIXME should not log a production error
34            if ( !in_array( MW_ENTRY_POINT, [ 'index', 'api', 'load' ], true ) ) {
35                throw new \RuntimeException( MW_ENTRY_POINT . ' endpoint is not allowed on the SSO domain' );
36            }
37        }
38    }
39
40    /** @inheritDoc */
41    public function onGetUserPermissionsErrors( $title, $user, $action, &$result ) {
42        if ( $this->sharedDomainUtils->isSharedDomain() ) {
43            if ( !$title->isSpecialPage() ) {
44                $result = wfMessage( 'badaccess-group0' );
45                return false;
46            }
47            // FIXME this should be an extension attribute eventually
48            $allowlist = [ 'Userlogin', 'Userlogout', 'CreateAccount', 'PasswordReset', 'Captcha' ];
49            foreach ( $allowlist as $name ) {
50                if ( $title->isSpecial( $name ) ) {
51                    return true;
52                }
53            }
54            $result = wfMessage( 'badaccess-group0' );
55            return false;
56        }
57    }
58
59    /** @inheritDoc */
60    public function onApiCheckCanExecute( $module, $user, &$message ) {
61        if ( $this->sharedDomainUtils->isSharedDomain() ) {
62            // FIXME this should be an extension attribute eventually
63            $allowlist = [
64                // needed for allowing any query API, even if we only want meta modules; it can be
65                // used to check page existence (which is unwanted functionality on the SSO domain),
66                // which is unfortunate but permissions will still be checked, so it's not a risk.
67                'query',
68                // allow login/signup directly via the API + help for those APIs
69                'clientlogin', 'createaccount', 'authmanagerinfo', 'paraminfo', 'help',
70                // APIs used during web login
71                'validatepassword', 'userinfo', 'webauthn', 'fancycaptchareload',
72                // generic meta APIs, there's a good chance something somewhere will use them
73                'siteinfo', 'globaluserinfo', 'tokens',
74            ];
75
76            if ( !in_array( $module->getModuleName(), $allowlist ) ) {
77                $message = 'apierror-moduledisabled';
78                return false;
79            }
80        }
81    }
82
83}