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