Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 43
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
Resource
0.00% covered (danger)
0.00%
0 / 43
0.00% covered (danger)
0.00%
0 / 10
182
0.00% covered (danger)
0.00%
0 / 1
 factory
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 needsReadAccess
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 needsWriteAccess
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 doExecuteProtected
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 getProfile
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 getScopes
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 respond
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getParamSettings
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\OAuth\Rest\Handler;
4
5use FormatJson;
6use GuzzleHttp\Psr7\ServerRequest;
7use MediaWiki\Extension\OAuth\Backend\MWOAuthException;
8use MediaWiki\Extension\OAuth\ResourceServer;
9use MediaWiki\Extension\OAuth\Response;
10use MediaWiki\Extension\OAuth\UserStatementProvider;
11use MediaWiki\Rest\Handler;
12use MediaWiki\Rest\HttpException;
13use MWException;
14use Psr\Http\Message\ResponseInterface;
15use Psr\Http\Message\ServerRequestInterface;
16use Wikimedia\ParamValidator\ParamValidator;
17
18/**
19 * Handles the oauth2/resource/profile and oauth2/resource/scope endpoints, which return
20 * information about the user and the grants of the application, respectively.
21 */
22class Resource extends Handler {
23
24    /**
25     * (string) TYPE_PROFILE constant to specify the profile type of the resource.
26     */
27    private const TYPE_PROFILE = 'profile';
28
29    /**
30     * (string) TYPE_SCOPES constant to specify the scopes type of the resource.
31     */
32    private const TYPE_SCOPES = 'scopes';
33
34    /** @var ResourceServer */
35    protected $resourceServer;
36
37    /**
38     * @return static
39     */
40    public static function factory() {
41        return new static(
42            ResourceServer::factory()
43        );
44    }
45
46    /**
47     * @param ResourceServer $resourceServer
48     */
49    protected function __construct( $resourceServer ) {
50        $this->resourceServer = $resourceServer;
51    }
52
53    /**
54     * All access controls are handled over OAuth2
55     *
56     * @return bool
57     */
58    public function needsReadAccess() {
59        return false;
60    }
61
62    /**
63     * @return bool
64     */
65    public function needsWriteAccess() {
66        return false;
67    }
68
69    /**
70     * @return ResponseInterface
71     */
72    public function execute() {
73        $response = new Response();
74        $request = ServerRequest::fromGlobals()->withHeader(
75            'authorization',
76            $this->getRequest()->getHeader( 'authorization' )
77        );
78
79        $callback = [ $this, 'doExecuteProtected' ];
80        return $this->resourceServer->verify( $request, $response, $callback );
81    }
82
83    /**
84     * @param ServerRequestInterface $request
85     * @param ResponseInterface $response
86     * @throws HttpException
87     * @return ResponseInterface
88     * @throws MWOAuthException
89     */
90    public function doExecuteProtected( $request, $response ) {
91        $type = $this->getRequest()->getPathParam( 'type' );
92
93        switch ( $type ) {
94            case self::TYPE_PROFILE:
95                return $this->getProfile( $response );
96            case self::TYPE_SCOPES:
97                return $this->getScopes( $response );
98        }
99
100        throw new HttpException( 'Invalid resource type', 400 );
101    }
102
103    /**
104     * Return appropriate profile info based on approved scopes
105     *
106     * @param ResponseInterface $response
107     * @return ResponseInterface
108     * @throws HttpException
109     * @throws MWOAuthException
110     */
111    private function getProfile( $response ) {
112        // Intersection between approved and requested scopes
113        $scopes = array_keys( $this->resourceServer->getScopes() );
114        $userStatementProvider = UserStatementProvider::factory(
115            $this->resourceServer->getUser(),
116            $this->resourceServer->getClient(),
117            $scopes
118        );
119
120        try {
121            $profile = $userStatementProvider->getUserProfile();
122        } catch ( MWException $ex ) {
123            throw new HttpException( $ex->getMessage(), $ex->getCode() );
124        }
125
126        return $this->respond( $response, $profile );
127    }
128
129    /**
130     * Get all available scopes client application can use
131     *
132     * @param ResponseInterface $response
133     * @return ResponseInterface
134     * @throws MWOAuthException
135     */
136    private function getScopes( $response ) {
137        $grants = $this->resourceServer->getClient()->getGrants();
138        return $this->respond( $response, [
139            self::TYPE_SCOPES => $grants
140        ] );
141    }
142
143    /**
144     * @param ResponseInterface $response
145     * @param array $data
146     * @return ResponseInterface
147     */
148    private function respond( $response, $data = [] ) {
149        $response->getBody()->write( FormatJson::encode( $data ) );
150        return $response;
151    }
152
153    public function getParamSettings() {
154        return [
155            'type' => [
156                self::PARAM_SOURCE => 'path',
157                ParamValidator::PARAM_TYPE => [ self::TYPE_PROFILE, self::TYPE_SCOPES ],
158                ParamValidator::PARAM_REQUIRED => true,
159            ],
160        ];
161    }
162}