Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
13.16% covered (danger)
13.16%
5 / 38
40.00% covered (danger)
40.00%
2 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ScopeRepository
13.16% covered (danger)
13.16%
5 / 38
40.00% covered (danger)
40.00%
2 / 5
106.31
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getAllowedScopes
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getScopeEntityByIdentifier
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 finalizeScopes
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
42
 replaceDefaultScope
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace MediaWiki\Extension\OAuth\Repository;
4
5use League\OAuth2\Server\Entities\ClientEntityInterface;
6use League\OAuth2\Server\Entities\ScopeEntityInterface;
7use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
8use MediaWiki\Extension\OAuth\Backend\MWOAuthException;
9use MediaWiki\Extension\OAuth\Backend\Utils;
10use MediaWiki\Extension\OAuth\Entity\ClientEntity;
11use MediaWiki\Extension\OAuth\Entity\ScopeEntity;
12use MediaWiki\Extension\OAuth\Entity\UserEntity;
13use MediaWiki\MediaWikiServices;
14use MediaWiki\WikiMap\WikiMap;
15
16class ScopeRepository implements ScopeRepositoryInterface {
17    /**
18     * @var string[]
19     */
20    protected $allowedScopes = [
21        '#default',
22        'mwoauth-authonly',
23        'mwoauth-authonlyprivate'
24    ];
25
26    public function __construct() {
27        $grantsInfo = MediaWikiServices::getInstance()->getGrantsInfo();
28        $this->allowedScopes = array_merge( $this->allowedScopes, $grantsInfo->getValidGrants() );
29    }
30
31    /**
32     * @return string[]
33     */
34    public function getAllowedScopes() {
35        return $this->allowedScopes;
36    }
37
38    /**
39     * Return information about a scope.
40     *
41     * @param string $identifier The scope identifier
42     *
43     * @return ScopeEntityInterface|null
44     */
45    public function getScopeEntityByIdentifier( $identifier ) {
46        if ( in_array( $identifier, $this->allowedScopes, true ) ) {
47            return new ScopeEntity( $identifier );
48        }
49
50        return null;
51    }
52
53    /**
54     * Given a client, grant type and optional user identifier
55     * validate the set of scopes requested are valid and optionally
56     * append additional scopes or remove requested scopes.
57     *
58     * @param ScopeEntityInterface[] $scopes
59     * @param string $grantType
60     * @param ClientEntityInterface|ClientEntity $clientEntity
61     * @param null|string $userIdentifier
62     *
63     * @return ScopeEntityInterface[]
64     */
65    public function finalizeScopes( array $scopes, $grantType,
66        ClientEntityInterface $clientEntity, $userIdentifier = null ) {
67        $scopes = $this->replaceDefaultScope( $scopes, $clientEntity );
68
69        if ( $grantType !== 'authorization_code' ) {
70            // For grants that do not require approval,
71            // just filter out the scopes that are not allowed for the client
72            return array_filter(
73                $scopes,
74                static function ( ScopeEntityInterface $scope ) use ( $clientEntity ) {
75                    return in_array( $scope->getIdentifier(), $clientEntity->getGrants(), true );
76                }
77            );
78        }
79        if ( !is_numeric( $userIdentifier ) ) {
80            return [];
81        }
82
83        $mwUser = Utils::getLocalUserFromCentralId( (int)$userIdentifier );
84        if ( !$mwUser ) {
85            return [];
86        }
87        $userEntity = UserEntity::newFromMWUser( $mwUser );
88        if ( $userEntity === null ) {
89            return [];
90        }
91
92        // Filter out not approved scopes
93        try {
94            $approval = $clientEntity->getCurrentAuthorization( $mwUser, WikiMap::getCurrentWikiId() );
95            $approvedScopeIds = $approval->getGrants();
96        } catch ( MWOAuthException $ex ) {
97            $approvedScopeIds = [];
98        }
99
100        return array_filter(
101            $scopes,
102            static function ( ScopeEntityInterface $scope ) use ( $approvedScopeIds ) {
103                return in_array( $scope->getIdentifier(), $approvedScopeIds, true );
104            }
105        );
106    }
107
108    /**
109     * Detect "#default" scope and replace it with all client's allowed scopes
110     *
111     * @param ScopeEntityInterface[] $scopes
112     * @param ClientEntityInterface|ClientEntity $client
113     * @return ScopeEntityInterface[]
114     */
115    private function replaceDefaultScope( array $scopes, ClientEntityInterface $client ) {
116        // Normally, #default scope would be an only scope set, but go through whole array in case
117        // someone explicitly made a request with that scope set
118        $index = array_search( '#default', array_map( static function ( ScopeEntityInterface $scope ) {
119            return $scope->getIdentifier();
120        }, $scopes ) );
121
122        if ( $index === false ) {
123            return $scopes;
124        }
125
126        return $client->getScopes();
127    }
128}