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