Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 59
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
AbstractClientHandler
0.00% covered (danger)
0.00%
0 / 59
0.00% covered (danger)
0.00%
0 / 4
182
0.00% covered (danger)
0.00%
0 / 1
 execute
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
42
 getFixedParams
n/a
0 / 0
n/a
0 / 0
0
 getParamMapping
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 getUnifiedParams
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
30
 getSupportedRequestTypes
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\OAuth\Rest\Handler;
4
5use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
6use MediaWiki\Context\RequestContext;
7use MediaWiki\Extension\OAuth\Backend\Utils;
8use MediaWiki\Extension\OAuth\Control\ConsumerSubmitControl;
9use MediaWiki\Extension\OAuth\Entity\ClientEntity;
10use MediaWiki\Rest\Handler;
11use MediaWiki\Rest\HttpException;
12use MediaWiki\Rest\LocalizedHttpException;
13use MediaWiki\Rest\ResponseInterface;
14use Wikimedia\Message\MessageValue;
15
16/**
17 * This class serves as the base class for all operations
18 * on OAuth 2.0 clients over the REST API.
19 * It provides client initialization and basic checks on it,
20 * as well as parameter name mapping between OAuth 2.0 and 1.0 terminology
21 */
22abstract class AbstractClientHandler extends Handler {
23
24    /**
25     * @return ResponseInterface
26     * @throws HttpException
27     */
28    public function execute(): ResponseInterface {
29        // At this point we assume user is authenticated and has valid session
30        // Authentication can be achieved over CentralAuth or Access token in authorization header
31        $responseFactory = $this->getResponseFactory();
32        $params = $this->getUnifiedParams();
33
34        $control = new ConsumerSubmitControl(
35            RequestContext::getMain(),
36            $params,
37            Utils::getCentralDB( DB_PRIMARY )
38        );
39
40        $status = $control->submit();
41        if ( $status->isGood() ) {
42            $value = $status->getValue();
43            if ( isset( $value['result']['consumer'] ) ) {
44                /** @var ClientEntity $client */
45                $client = $value['result']['consumer'];
46                $data = [
47                    'name' => $client->getName(),
48                    'client_key' => $client->getConsumerKey(),
49                    'secret' => Utils::hmacDBSecret( $client->getSecretKey() )
50                ];
51                if ( $client->getOwnerOnly() ) {
52                    $accessToken = $value['result']['accessToken'];
53                    if ( $accessToken instanceof AccessTokenEntityInterface ) {
54                        $data['access_token'] = (string)$accessToken;
55                    }
56                }
57
58                return $responseFactory->createJson( $data );
59            }
60
61            throw new LocalizedHttpException(
62                MessageValue::new( 'mwoauth-consumer-submit-error' ), 400
63            );
64        }
65        $value = $status->getValue();
66        if ( isset( $value['error'] ) ) {
67            throw new HttpException( $value['error'], 400 );
68        }
69
70        throw new HttpException( $status->getMessage() );
71    }
72
73    /**
74     * Get params that have fixed values and cannot be
75     * changed by the request params
76     *
77     * @return array
78     */
79    abstract protected function getFixedParams(): array;
80
81    /**
82     * Maps modern OAuth2 param names to the ones
83     * expected by the SubmitControl
84     *
85     * @return string[]
86     */
87    protected function getParamMapping(): array {
88        return [
89            'oauth2IsConfidential' => 'is_confidential',
90            'ownerOnly' => 'owner_only',
91            'callbackUrl' => 'callback_url',
92            'callbackIsPrefix' => 'callback_is_prefix',
93            'oauth2GrantTypes' => 'grant_types',
94            'grants' => 'scopes',
95            'consumerKey' => 'client_key',
96        ];
97    }
98
99    /**
100     * Merge and adjust all params
101     *
102     * @return array
103     */
104    protected function getUnifiedParams(): array {
105        $finalParams = [];
106
107        $requestParams = $this->getValidatedParams();
108        $mapping = array_flip( $this->getParamMapping() );
109        foreach ( $requestParams as $name => $value ) {
110            if ( isset( $mapping[$name] ) ) {
111                $finalParams[$mapping[$name]] = $value;
112            } else {
113                $finalParams[$name] = $value;
114            }
115        }
116
117        $bodyParams = $this->getValidatedBody();
118        foreach ( $bodyParams as $name => $value ) {
119            if ( isset( $mapping[$name] ) ) {
120                $finalParams[$mapping[$name]] = $value;
121            } else {
122                $finalParams[$name] = $value;
123            }
124        }
125
126        $finalParams = array_merge(
127            $finalParams,
128            $this->getFixedParams()
129        );
130
131        return $finalParams;
132    }
133
134    /**
135     * @return string[]
136     */
137    public function getSupportedRequestTypes(): array {
138        return [
139            'application/json',
140            'application/x-www-form-urlencoded'
141        ];
142    }
143}