Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.90% covered (success)
98.90%
90 / 91
83.33% covered (warning)
83.33%
5 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
ListClients
98.90% covered (success)
98.90%
90 / 91
83.33% covered (warning)
83.33%
5 / 6
13
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 needsWriteAccess
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 run
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 getParamSettings
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
1
 getDbResults
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
1 / 1
2
 processDbResults
96.97% covered (success)
96.97%
32 / 33
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace MediaWiki\Extension\OAuth\Rest\Handler;
4
5use MediaWiki\Extension\OAuth\Backend\Consumer;
6use MediaWiki\Extension\OAuth\Backend\Utils;
7use MediaWiki\Extension\OAuth\Control\ConsumerAccessControl;
8use MediaWiki\Rest\LocalizedHttpException;
9use MediaWiki\Rest\ResponseInterface;
10use MediaWiki\Rest\SimpleHandler;
11use RequestContext;
12use Wikimedia\Message\MessageValue;
13use Wikimedia\ParamValidator\ParamValidator;
14use Wikimedia\Rdbms\ILoadBalancer;
15use Wikimedia\Rdbms\IResultWrapper;
16use Wikimedia\Rdbms\SelectQueryBuilder;
17
18/**
19 * Handles the oauth2/consumers endpoint, which returns
20 * a list of registered consumers for the user
21 */
22class ListClients extends SimpleHandler {
23
24    /** @var string[] */
25    protected $propertyMapping = [
26        'id' => 'oarc_id',
27        'client_key' => 'oarc_consumer_key',
28        'name' => 'oarc_name',
29        'version' => 'oarc_version',
30        'email' => 'oarc_email',
31        'callback_url' => 'oarc_callback_url',
32        'scopes' => 'oarc_grants',
33        'registration' => 'oarc_registration',
34        'stage' => 'oarc_stage',
35        'oauth_version' => 'oarc_oauth_version',
36        'description' => 'oarc_description',
37        'allowed_grants' => 'oarc_oauth2_allowed_grants',
38        'restrictions' => 'oarc_restrictions',
39        'user_id' => 'oarc_user_id',
40        'callback_is_prefix' => 'oarc_callback_is_prefix',
41        'email_authenticated' => 'oarc_email_authenticated',
42        'developer_agreement' => 'oarc_developer_agreement',
43        'owner_only' => 'oarc_owner_only',
44        'wiki' => 'oarc_wiki',
45        'secret_key' => 'oarc_secret_key',
46        'rsa_key' => 'oarc_rsa_key',
47        'stage_timestamp' => 'oarc_stage_timestamp',
48        'deleted' => 'oarc_deleted',
49        'oauth2_is_confidential' => 'oarc_oauth2_is_confidential',
50    ];
51
52    /**
53     *
54     * @var ILoadBalancer
55     */
56    private $loadBalancer;
57
58    /**
59     * @param ILoadBalancer $loadBalancer
60     */
61    public function __construct( ILoadBalancer $loadBalancer ) {
62        $this->loadBalancer = $loadBalancer;
63    }
64
65    /**
66     * @return bool
67     */
68    public function needsWriteAccess() {
69        return false;
70    }
71
72    /**
73     * @return ResponseInterface
74     * @throws LocalizedHttpException
75     */
76    public function run(): ResponseInterface {
77        // @todo Inject this, when there is a good way to do that, see T239753
78        $user = RequestContext::getMain()->getUser();
79
80        $centralId = Utils::getCentralIdFromUserName( $user->getName() );
81        $responseFactory = $this->getResponseFactory();
82
83        if ( !$centralId ) {
84            throw new LocalizedHttpException(
85                new MessageValue( 'rest-nonexistent-user', [ $user->getName() ] ), 404
86            );
87        }
88        $response = $this->getDbResults( $centralId );
89
90        return $responseFactory->createJson( $response );
91    }
92
93    /** @inheritDoc */
94    public function getParamSettings() {
95        return [
96            'limit' => [
97                self::PARAM_SOURCE => 'query',
98                ParamValidator::PARAM_TYPE => 'integer',
99                ParamValidator::PARAM_REQUIRED => false,
100                ParamValidator::PARAM_DEFAULT => 25
101            ],
102            'offset' => [
103                self::PARAM_SOURCE => 'query',
104                ParamValidator::PARAM_TYPE => 'integer',
105                ParamValidator::PARAM_REQUIRED => false,
106                ParamValidator::PARAM_DEFAULT => 0
107            ],
108            'oauth_version' => [
109                self::PARAM_SOURCE => 'query',
110                ParamValidator::PARAM_TYPE => [ '1', '2' ],
111                ParamValidator::PARAM_REQUIRED => false,
112                ParamValidator::PARAM_DEFAULT => '2'
113            ]
114        ];
115    }
116
117    /**
118     * @param int $centralId the user id of calling user
119     * @return array the results
120     */
121    private function getDbResults( int $centralId ) {
122        $dbr = $this->loadBalancer->getConnection( DB_REPLICA );
123
124        $params = $this->getValidatedParams();
125        $limit = $params['limit'];
126        $offset = $params['offset'];
127
128        $oauthVersion = $params['oauth_version'];
129        $conds = [ 'oarc_user_id' => $centralId ];
130        if ( $oauthVersion !== null ) {
131            $conds['oarc_oauth_version'] = (int)$oauthVersion;
132        }
133
134        $res = $dbr->newSelectQueryBuilder()
135            ->select( array_values( $this->propertyMapping ) )
136            ->from( 'oauth_registered_consumer' )
137            ->where( $conds )
138            ->orderBy( 'oarc_id', SelectQueryBuilder::SORT_DESC )
139            ->limit( $limit )
140            ->offset( $offset )
141            ->caller( __METHOD__ )
142            ->fetchResultSet();
143
144        $total = $dbr->newSelectQueryBuilder()
145            ->select( 'oarc_consumer_key' )
146            ->from( 'oauth_registered_consumer' )
147            ->where( $conds )
148            ->caller( __METHOD__ )
149            ->fetchRowCount();
150
151        return [
152            'clients' => $this->processDbResults( $res ),
153            'total' => $total
154        ];
155    }
156
157    /**
158     * @param IResultWrapper $res database results, or an empty array if none
159     * @return array consumer data
160     */
161    private function processDbResults( $res ) {
162        $consumers = [];
163        $requestContext = RequestContext::getMain();
164        $user = $requestContext->getUser();
165
166        foreach ( $res as $row ) {
167
168            $consumer = [];
169
170            $cmrAc = ConsumerAccessControl::wrap(
171                Consumer::newFromRow( Utils::getCentralDB( DB_REPLICA ), $row ),
172                $requestContext
173            );
174
175            if ( !$cmrAc ) {
176                continue;
177            }
178
179            $consumer['email'] = $cmrAc->getEmail();
180            $consumer['name'] = $cmrAc->getName();
181            $consumer['version'] = $cmrAc->getVersion();
182            $consumer['callback_url'] = $cmrAc->getCallbackUrl();
183            $consumer['description'] = $cmrAc->getDescription();
184            $consumer['client_key'] = $cmrAc->getConsumerKey();
185            $consumer['owner_only'] = $cmrAc->getOwnerOnly();
186
187            $consumer['stage'] = (int)$cmrAc->getStage();
188            $consumer['oauth_version'] = $cmrAc->getOAuthVersion();
189            $consumer['registration_formatted'] = $requestContext->getLanguage()->userTimeAndDate(
190                $cmrAc->getRegistration(),
191                $user
192            );
193
194            if ( $consumer['oauth_version'] === Consumer::OAUTH_VERSION_2 ) {
195                $consumer['allowed_grants'] = $cmrAc->get( 'oauth2GrantTypes' );
196            }
197
198            $consumer['scopes'] = $cmrAc->getGrants();
199            $consumer['restrictions'] = $cmrAc->getRestrictions();
200
201            foreach ( $consumer as $key => $value ) {
202                if ( is_object( $consumer[$key] ) ) {
203                    unset( $consumer[$key] );
204                }
205            }
206
207            $consumers[] = $consumer;
208        }
209
210        return $consumers;
211    }
212}