Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 324 |
|
0.00% |
0 / 11 |
CRAP | |
0.00% |
0 / 1 |
SpecialMWOAuthManageConsumers | |
0.00% |
0 / 324 |
|
0.00% |
0 / 11 |
2862 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
doesWrites | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 27 |
|
0.00% |
0 / 1 |
110 | |||
addQueueSubtitleLinks | |
0.00% |
0 / 23 |
|
0.00% |
0 / 1 |
30 | |||
showMainHub | |
0.00% |
0 / 36 |
|
0.00% |
0 / 1 |
20 | |||
handleConsumerForm | |
0.00% |
0 / 98 |
|
0.00% |
0 / 1 |
132 | |||
getInfoTableOptions | |
0.00% |
0 / 68 |
|
0.00% |
0 / 1 |
132 | |||
formatCallbackUrl | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
showConsumerList | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
formatRow | |
0.00% |
0 / 52 |
|
0.00% |
0 / 1 |
12 | |||
getGroupName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\OAuth\Frontend\SpecialPages; |
4 | |
5 | /** |
6 | * (c) Aaron Schulz 2013, GPL |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. |
12 | * |
13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU General Public License along |
19 | * with this program; if not, write to the Free Software Foundation, Inc., |
20 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
21 | * http://www.gnu.org/copyleft/gpl.html |
22 | */ |
23 | |
24 | use ErrorPageError; |
25 | use HTMLForm; |
26 | use IContextSource; |
27 | use LogEventsList; |
28 | use LogPage; |
29 | use MediaWiki\Extension\OAuth\Backend\Consumer; |
30 | use MediaWiki\Extension\OAuth\Backend\Utils; |
31 | use MediaWiki\Extension\OAuth\Control\ConsumerAccessControl; |
32 | use MediaWiki\Extension\OAuth\Control\ConsumerSubmitControl; |
33 | use MediaWiki\Extension\OAuth\Entity\ClientEntity; |
34 | use MediaWiki\Extension\OAuth\Frontend\Pagers\ManageConsumersPager; |
35 | use MediaWiki\Extension\OAuth\Frontend\UIUtils; |
36 | use MediaWiki\Html\Html; |
37 | use MediaWiki\MediaWikiServices; |
38 | use MediaWiki\Permissions\GrantsLocalization; |
39 | use MediaWiki\SpecialPage\SpecialPage; |
40 | use MediaWiki\Status\Status; |
41 | use MediaWiki\Title\Title; |
42 | use MWRestrictions; |
43 | use OOUI\HtmlSnippet; |
44 | use PermissionsError; |
45 | use stdClass; |
46 | use Wikimedia\Rdbms\IDatabase; |
47 | use Xml; |
48 | |
49 | /** |
50 | * Special page for listing the queue of consumer requests and managing |
51 | * their approval/rejection and also for listing approved/disabled consumers |
52 | */ |
53 | class SpecialMWOAuthManageConsumers extends SpecialPage { |
54 | /** @var bool|int An Consumer::STAGE_* constant on queue/list subpages, false otherwise */ |
55 | protected $stage = false; |
56 | /** @var string A stage key from Consumer::$stageNames */ |
57 | protected $stageKey; |
58 | |
59 | /** |
60 | * Stages which are shown in a queue (they are in an actionable state and can form a backlog) |
61 | * @var int[] |
62 | */ |
63 | public static $queueStages = [ Consumer::STAGE_PROPOSED, |
64 | Consumer::STAGE_REJECTED, Consumer::STAGE_EXPIRED ]; |
65 | |
66 | /** |
67 | * Stages which cannot form a backlog and are shown in a list |
68 | * @var int[] |
69 | */ |
70 | public static $listStages = [ Consumer::STAGE_APPROVED, |
71 | Consumer::STAGE_DISABLED ]; |
72 | |
73 | /** @var GrantsLocalization */ |
74 | private $grantsLocalization; |
75 | |
76 | /** |
77 | * @param GrantsLocalization $grantsLocalization |
78 | */ |
79 | public function __construct( GrantsLocalization $grantsLocalization ) { |
80 | parent::__construct( 'OAuthManageConsumers', 'mwoauthmanageconsumer' ); |
81 | $this->grantsLocalization = $grantsLocalization; |
82 | } |
83 | |
84 | public function doesWrites() { |
85 | return true; |
86 | } |
87 | |
88 | public function execute( $par ) { |
89 | $user = $this->getUser(); |
90 | $permissionManager = MediaWikiServices::getInstance()->getPermissionManager(); |
91 | |
92 | $this->setHeaders(); |
93 | $this->getOutput()->disallowUserJs(); |
94 | $this->addHelpLink( 'Help:OAuth' ); |
95 | $this->requireNamedUser( 'mwoauth-available-only-to-registered' ); |
96 | |
97 | if ( !$permissionManager->userHasRight( $user, 'mwoauthmanageconsumer' ) ) { |
98 | throw new PermissionsError( 'mwoauthmanageconsumer' ); |
99 | } |
100 | |
101 | if ( $this->getConfig()->get( 'MWOAuthReadOnly' ) ) { |
102 | throw new ErrorPageError( 'mwoauth-error', 'mwoauth-db-readonly' ); |
103 | } |
104 | |
105 | // Format is Special:OAuthManageConsumers[/<stage>|/<consumer key>] |
106 | // B/C format is Special:OAuthManageConsumers/<stage>/<consumer key> |
107 | $consumerKey = null; |
108 | $navigation = $par !== null ? explode( '/', $par ) : []; |
109 | if ( count( $navigation ) === 2 ) { |
110 | $this->stage = false; |
111 | $consumerKey = $navigation[1]; |
112 | } elseif ( count( $navigation ) === 1 && $navigation[0] ) { |
113 | $this->stage = array_search( $navigation[0], Consumer::$stageNames, true ); |
114 | if ( $this->stage !== false ) { |
115 | $this->stageKey = $navigation[0]; |
116 | } else { |
117 | $consumerKey = $navigation[0]; |
118 | } |
119 | } |
120 | |
121 | if ( $consumerKey ) { |
122 | $this->handleConsumerForm( $consumerKey ); |
123 | } elseif ( $this->stage !== false ) { |
124 | $this->showConsumerList(); |
125 | } else { |
126 | $this->showMainHub(); |
127 | } |
128 | |
129 | $this->addQueueSubtitleLinks( $consumerKey ); |
130 | |
131 | $this->getOutput()->addModuleStyles( 'ext.MWOAuth.styles' ); |
132 | } |
133 | |
134 | /** |
135 | * Show other sub-queue links. Grey out the current one. |
136 | * When viewing a request, show them all and a link to current consumer view. |
137 | * |
138 | * @param string|null $consumerKey |
139 | * @return void |
140 | */ |
141 | protected function addQueueSubtitleLinks( $consumerKey ) { |
142 | $linkRenderer = $this->getLinkRenderer(); |
143 | $listLinks = []; |
144 | foreach ( self::$queueStages as $stage ) { |
145 | $stageKey = Consumer::$stageNames[$stage]; |
146 | if ( $consumerKey || $this->stageKey !== $stageKey ) { |
147 | $listLinks[] = $linkRenderer->makeKnownLink( |
148 | $this->getPageTitle( $stageKey ), |
149 | // Messages: mwoauthmanageconsumers-showproposed, |
150 | // mwoauthmanageconsumers-showrejected, mwoauthmanageconsumers-showexpired, |
151 | $this->msg( 'mwoauthmanageconsumers-show' . $stageKey )->text() |
152 | ); |
153 | } else { |
154 | $listLinks[] = $this->msg( 'mwoauthmanageconsumers-show' . $stageKey )->escaped(); |
155 | } |
156 | } |
157 | |
158 | if ( $consumerKey ) { |
159 | $consumerViewLink = "[" . $linkRenderer->makeKnownLink( |
160 | SpecialPage::getTitleFor( 'OAuthListConsumers', "view/$consumerKey" ), |
161 | $this->msg( 'mwoauthconsumer-consumer-view' )->text() ) . "]"; |
162 | } else { |
163 | $consumerViewLink = ''; |
164 | } |
165 | |
166 | $linkHtml = $this->getLanguage()->pipeList( $listLinks ); |
167 | |
168 | $viewall = $this->msg( 'parentheses' )->rawParams( $linkRenderer->makeKnownLink( |
169 | $this->getPageTitle(), |
170 | $this->msg( 'mwoauthmanageconsumers-main' )->text() |
171 | ) )->escaped(); |
172 | |
173 | $this->getOutput()->setSubtitle( |
174 | "<strong>" . $this->msg( 'mwoauthmanageconsumers-type' )->escaped() . |
175 | "</strong> [{$linkHtml}] {$consumerViewLink} <strong>{$viewall}</strong>" ); |
176 | } |
177 | |
178 | /** |
179 | * Show the links to all the queues and how many requests are in each. |
180 | * Also show the list of enabled and disabled consumers and how many there are of each. |
181 | * |
182 | * @return void |
183 | */ |
184 | protected function showMainHub() { |
185 | $keyStageMapQ = array_intersect( array_flip( Consumer::$stageNames ), |
186 | self::$queueStages ); |
187 | $keyStageMapL = array_intersect( array_flip( Consumer::$stageNames ), |
188 | self::$listStages ); |
189 | |
190 | $linkRenderer = $this->getLinkRenderer(); |
191 | $out = $this->getOutput(); |
192 | |
193 | $out->addWikiMsg( 'mwoauthmanageconsumers-maintext' ); |
194 | |
195 | $counts = Utils::getConsumerStateCounts( Utils::getCentralDB( DB_REPLICA ) ); |
196 | |
197 | $out->wrapWikiMsg( "<p><strong>$1</strong></p>", 'mwoauthmanageconsumers-queues' ); |
198 | $out->addHTML( '<ul>' ); |
199 | foreach ( $keyStageMapQ as $stageKey => $stage ) { |
200 | $tag = ( $stage === Consumer::STAGE_EXPIRED ) ? 'i' : 'b'; |
201 | $out->addHTML( |
202 | '<li>' . |
203 | "<$tag>" . |
204 | $linkRenderer->makeKnownLink( |
205 | $this->getPageTitle( $stageKey ), |
206 | // Messages: mwoauthmanageconsumers-q-proposed, mwoauthmanageconsumers-q-rejected, |
207 | // mwoauthmanageconsumers-q-expired |
208 | $this->msg( 'mwoauthmanageconsumers-q-' . $stageKey )->text() |
209 | ) . |
210 | "</$tag> [$counts[$stage]]" . |
211 | '</li>' |
212 | ); |
213 | } |
214 | $out->addHTML( '</ul>' ); |
215 | |
216 | $out->wrapWikiMsg( "<p><strong>$1</strong></p>", 'mwoauthmanageconsumers-lists' ); |
217 | $out->addHTML( '<ul>' ); |
218 | foreach ( $keyStageMapL as $stageKey => $stage ) { |
219 | $out->addHTML( |
220 | '<li>' . |
221 | $linkRenderer->makeKnownLink( |
222 | $this->getPageTitle( $stageKey ), |
223 | // Messages: mwoauthmanageconsumers-l-approved, mwoauthmanageconsumers-l-disabled |
224 | $this->msg( 'mwoauthmanageconsumers-l-' . $stageKey )->text() |
225 | ) . |
226 | " [$counts[$stage]]" . |
227 | '</li>' |
228 | ); |
229 | } |
230 | $out->addHTML( '</ul>' ); |
231 | } |
232 | |
233 | /** |
234 | * Show the form to approve/reject/disable/re-enable consumers |
235 | * |
236 | * @param string $consumerKey |
237 | * @throws PermissionsError |
238 | */ |
239 | protected function handleConsumerForm( $consumerKey ) { |
240 | $user = $this->getUser(); |
241 | $dbr = Utils::getCentralDB( DB_REPLICA ); |
242 | $cmrAc = ConsumerAccessControl::wrap( |
243 | Consumer::newFromKey( $dbr, $consumerKey ), $this->getContext() ); |
244 | $permissionManager = MediaWikiServices::getInstance()->getPermissionManager(); |
245 | |
246 | if ( !$cmrAc ) { |
247 | $this->getOutput()->addWikiMsg( 'mwoauth-invalid-consumer-key' ); |
248 | return; |
249 | } elseif ( $cmrAc->getDeleted() |
250 | && !$permissionManager->userHasRight( $user, 'mwoauthviewsuppressed' ) ) { |
251 | throw new PermissionsError( 'mwoauthviewsuppressed' ); |
252 | } |
253 | $startingStage = $cmrAc->getStage(); |
254 | $pending = !in_array( $startingStage, [ |
255 | Consumer::STAGE_APPROVED, Consumer::STAGE_DISABLED ] ); |
256 | |
257 | if ( $pending ) { |
258 | $opts = [ |
259 | $this->msg( 'mwoauthmanageconsumers-approve' )->escaped() => 'approve', |
260 | $this->msg( 'mwoauthmanageconsumers-reject' )->escaped() => 'reject' |
261 | ]; |
262 | if ( $permissionManager->userHasRight( $this->getUser(), 'mwoauthsuppress' ) ) { |
263 | $msg = $this->msg( 'mwoauthmanageconsumers-rsuppress' )->escaped(); |
264 | $opts["<strong>$msg</strong>"] = 'rsuppress'; |
265 | } |
266 | } else { |
267 | $opts = [ |
268 | $this->msg( 'mwoauthmanageconsumers-disable' )->escaped() => 'disable', |
269 | $this->msg( 'mwoauthmanageconsumers-reenable' )->escaped() => 'reenable' |
270 | ]; |
271 | if ( $permissionManager->userHasRight( $this->getUser(), 'mwoauthsuppress' ) ) { |
272 | $msg = $this->msg( 'mwoauthmanageconsumers-dsuppress' )->escaped(); |
273 | $opts["<strong>$msg</strong>"] = 'dsuppress'; |
274 | } |
275 | } |
276 | |
277 | $dbw = Utils::getCentralDB( DB_PRIMARY ); |
278 | $control = new ConsumerSubmitControl( $this->getContext(), [], $dbw ); |
279 | $form = HTMLForm::factory( 'ooui', |
280 | $control->registerValidators( [ |
281 | 'info' => [ |
282 | 'type' => 'info', |
283 | 'raw' => true, |
284 | 'default' => UIUtils::generateInfoTable( |
285 | $this->getInfoTableOptions( $cmrAc ), |
286 | $this->getContext() |
287 | ), |
288 | ], |
289 | 'action' => [ |
290 | 'type' => 'radio', |
291 | 'label-message' => 'mwoauthmanageconsumers-action', |
292 | 'required' => true, |
293 | 'options' => $opts, |
294 | // no validate on GET |
295 | 'default' => '', |
296 | ], |
297 | 'reason' => [ |
298 | 'type' => 'text', |
299 | 'label-message' => 'mwoauthmanageconsumers-reason', |
300 | 'required' => true, |
301 | ], |
302 | 'consumerKey' => [ |
303 | 'type' => 'hidden', |
304 | 'default' => $cmrAc->getConsumerKey(), |
305 | ], |
306 | 'changeToken' => [ |
307 | 'type' => 'hidden', |
308 | 'default' => $cmrAc->getDAO()->getChangeToken( $this->getContext() ), |
309 | ], |
310 | ] ), |
311 | $this->getContext() |
312 | ); |
313 | $form->setSubmitCallback( |
314 | static function ( array $data, IContextSource $context ) use ( $control ) { |
315 | $data['suppress'] = 0; |
316 | if ( $data['action'] === 'dsuppress' ) { |
317 | $data = [ 'action' => 'disable', 'suppress' => 1 ] + $data; |
318 | } elseif ( $data['action'] === 'rsuppress' ) { |
319 | $data = [ 'action' => 'reject', 'suppress' => 1 ] + $data; |
320 | } |
321 | $control->setInputParameters( $data ); |
322 | return $control->submit(); |
323 | } |
324 | ); |
325 | |
326 | $form->setWrapperLegendMsg( 'mwoauthmanageconsumers-confirm-legend' ); |
327 | $form->setSubmitTextMsg( 'mwoauthmanageconsumers-confirm-submit' ); |
328 | $form->addPreHtml( |
329 | $this->msg( 'mwoauthmanageconsumers-confirm-text' )->parseAsBlock() ); |
330 | |
331 | $status = $form->show(); |
332 | if ( $status instanceof Status && $status->isOK() ) { |
333 | /** @var Consumer $cmr */ |
334 | // @phan-suppress-next-line PhanTypeArraySuspiciousNullable |
335 | $cmr = $status->value['result']; |
336 | '@phan-var Consumer $cmr'; |
337 | $oldStageKey = Consumer::$stageNames[$startingStage]; |
338 | $newStageKey = Consumer::$stageNames[$cmr->getStage()]; |
339 | // Messages: mwoauthmanageconsumers-success-approved, mwoauthmanageconsumers-success-rejected, |
340 | // mwoauthmanageconsumers-success-disabled |
341 | $this->getOutput()->addWikiMsg( "mwoauthmanageconsumers-success-$newStageKey" ); |
342 | $returnTo = Title::newFromText( 'Special:OAuthManageConsumers/' . $oldStageKey ); |
343 | $this->getOutput()->addReturnTo( $returnTo, [], |
344 | // Messages: mwoauthmanageconsumers-linkproposed, |
345 | // mwoauthmanageconsumers-linkrejected, mwoauthmanageconsumers-linkexpired, |
346 | // mwoauthmanageconsumers-linkapproved, mwoauthmanageconsumers-linkdisabled |
347 | $this->msg( 'mwoauthmanageconsumers-link' . $oldStageKey )->text() ); |
348 | } else { |
349 | $out = $this->getOutput(); |
350 | // Show all of the status updates |
351 | $logPage = new LogPage( 'mwoauthconsumer' ); |
352 | $out->addHTML( Xml::element( 'h2', null, $logPage->getName()->text() ) ); |
353 | LogEventsList::showLogExtract( $out, 'mwoauthconsumer', '', '', [ |
354 | 'conds' => [ |
355 | 'ls_field' => 'OAuthConsumer', |
356 | 'ls_value' => $cmrAc->getConsumerKey(), |
357 | ], |
358 | ] ); |
359 | } |
360 | } |
361 | |
362 | /** |
363 | * @param ConsumerAccessControl $cmrAc |
364 | * @return array |
365 | */ |
366 | protected function getInfoTableOptions( $cmrAc ) { |
367 | $owner = $cmrAc->getUserName(); |
368 | $lang = $this->getLanguage(); |
369 | |
370 | $link = $this->getLinkRenderer()->makeKnownLink( |
371 | $title = SpecialPage::getTitleFor( 'OAuthListConsumers' ), |
372 | $this->msg( 'mwoauthmanageconsumers-search-publisher' )->text(), |
373 | [], |
374 | [ 'publisher' => $owner ] |
375 | ); |
376 | $ownerLink = $cmrAc->escapeForHtml( $owner ) . ' ' . |
377 | $this->msg( 'parentheses' )->rawParams( $link )->escaped(); |
378 | $ownerOnly = $cmrAc->getDAO()->getOwnerOnly(); |
379 | $restrictions = $cmrAc->getRestrictions(); |
380 | |
381 | $options = [ |
382 | // Messages: mwoauth-consumer-stage-proposed, mwoauth-consumer-stage-rejected, |
383 | // mwoauth-consumer-stage-expired, mwoauth-consumer-stage-approved, |
384 | // mwoauth-consumer-stage-disabled |
385 | 'mwoauth-consumer-stage' => $cmrAc->getDeleted() |
386 | ? $this->msg( 'mwoauth-consumer-stage-suppressed' ) |
387 | : $this->msg( 'mwoauth-consumer-stage-' . |
388 | Consumer::$stageNames[$cmrAc->getStage()] ), |
389 | 'mwoauth-consumer-key' => $cmrAc->getConsumerKey(), |
390 | 'mwoauth-consumer-name' => new HtmlSnippet( $cmrAc->get( 'name', function ( $s ) { |
391 | $link = $this->getLinkRenderer()->makeKnownLink( |
392 | SpecialPage::getTitleFor( 'OAuthListConsumers' ), |
393 | $this->msg( 'mwoauthmanageconsumers-search-name' )->text(), |
394 | [], |
395 | [ 'name' => $s ] |
396 | ); |
397 | return htmlspecialchars( $s ) . ' ' . |
398 | $this->msg( 'parentheses' )->rawParams( $link )->escaped(); |
399 | } ) ), |
400 | 'mwoauth-consumer-version' => $cmrAc->getVersion(), |
401 | 'mwoauth-oauth-version' => $cmrAc->getOAuthVersion() === Consumer::OAUTH_VERSION_2 |
402 | ? $this->msg( 'mwoauth-oauth-version-2' ) |
403 | : $this->msg( 'mwoauth-oauth-version-1' ), |
404 | 'mwoauth-consumer-user' => new HtmlSnippet( $ownerLink ), |
405 | 'mwoauth-consumer-description' => $cmrAc->getDescription(), |
406 | 'mwoauth-consumer-owner-only-label' => $ownerOnly ? |
407 | $this->msg( 'mwoauth-consumer-owner-only', $owner ) : null, |
408 | 'mwoauth-consumer-callbackurl' => $ownerOnly ? |
409 | null : $this->formatCallbackUrl( $cmrAc ), |
410 | 'mwoauth-consumer-callbackisprefix' => $ownerOnly ? |
411 | null : ( $cmrAc->getCallbackIsPrefix() ? |
412 | $this->msg( 'htmlform-yes' ) : $this->msg( 'htmlform-no' ) ), |
413 | 'mwoauth-consumer-grantsneeded' => $cmrAc->get( 'grants', |
414 | function ( $grants ) use ( $lang ) { |
415 | return $lang->semicolonList( $this->grantsLocalization->getGrantDescriptions( $grants, $lang ) ); |
416 | } ), |
417 | 'mwoauth-consumer-email' => $cmrAc->getEmail(), |
418 | 'mwoauth-consumer-wiki' => $cmrAc->getWiki() |
419 | ]; |
420 | |
421 | // Add OAuth2 specific parameters |
422 | if ( $cmrAc->getOAuthVersion() === Consumer::OAUTH_VERSION_2 ) { |
423 | /** @var ClientEntity $consumer */ |
424 | $consumer = $cmrAc->getDAO(); |
425 | $options += [ |
426 | 'mwoauth-oauth2-is-confidential' => $consumer->isConfidential() ? |
427 | $this->msg( 'htmlform-yes' ) : $this->msg( 'htmlform-no' ), |
428 | 'mwoauth-oauth2-granttypes' => implode( ', ', array_map( function ( $grant ) { |
429 | $map = [ |
430 | 'authorization_code' => 'mwoauth-oauth2-granttype-auth-code', |
431 | 'refresh_token' => 'mwoauth-oauth2-granttype-refresh-token', |
432 | 'client_credentials' => 'mwoauth-oauth2-granttype-client-credentials' |
433 | ]; |
434 | return isset( $map[$grant] ) ? $this->msg( $map[$grant] ) : ''; |
435 | }, $consumer->getAllowedGrants() ) ) |
436 | ]; |
437 | } |
438 | |
439 | // Add optional parameters |
440 | $options += [ |
441 | 'mwoauth-consumer-restrictions-json' => $restrictions instanceof MWRestrictions ? |
442 | $restrictions->toJson( true ) : $restrictions, |
443 | 'mwoauth-consumer-rsakey' => $cmrAc->getRsaKey(), |
444 | ]; |
445 | |
446 | return $options; |
447 | } |
448 | |
449 | /** |
450 | * Format a callback URL. Usually this doesn't do anything nontrivial, but it adds a warning |
451 | * to callback URLs with a special meaning. |
452 | * @param ConsumerAccessControl $cmrAc |
453 | * @return HtmlSnippet|string Formatted callback URL, as a plaintext or HTML string |
454 | */ |
455 | protected function formatCallbackUrl( ConsumerAccessControl $cmrAc ) { |
456 | $url = $cmrAc->getCallbackUrl(); |
457 | if ( $cmrAc->getDAO()->getCallbackIsPrefix() ) { |
458 | $urlParts = wfParseUrl( $cmrAc->getDAO()->getCallbackUrl() ); |
459 | if ( ( $urlParts['port'] ?? null ) === 1 ) { |
460 | $warning = Html::element( 'span', [ 'class' => 'warning' ], |
461 | $this->msg( 'mwoauth-consumer-callbackurl-warning' )->text() ); |
462 | $url = new HtmlSnippet( $url . ' ' . $warning ); |
463 | } |
464 | } |
465 | return $url; |
466 | } |
467 | |
468 | /** |
469 | * Show a paged list of consumers with links to details |
470 | */ |
471 | protected function showConsumerList() { |
472 | $pager = new ManageConsumersPager( $this, [], $this->stage ); |
473 | if ( $pager->getNumRows() ) { |
474 | $this->getOutput()->addHTML( $pager->getNavigationBar() ); |
475 | $this->getOutput()->addHTML( $pager->getBody() ); |
476 | $this->getOutput()->addHTML( $pager->getNavigationBar() ); |
477 | } else { |
478 | // Messages: mwoauthmanageconsumers-none-proposed, mwoauthmanageconsumers-none-rejected, |
479 | // mwoauthmanageconsumers-none-expired, mwoauthmanageconsumers-none-approved, |
480 | // mwoauthmanageconsumers-none-disabled |
481 | $this->getOutput()->addWikiMsg( "mwoauthmanageconsumers-none-{$this->stageKey}" ); |
482 | } |
483 | # Every 30th view, prune old deleted items |
484 | if ( mt_rand( 0, 29 ) == 0 ) { |
485 | Utils::runAutoMaintenance( Utils::getCentralDB( DB_PRIMARY ) ); |
486 | } |
487 | } |
488 | |
489 | /** |
490 | * @param IDatabase $db |
491 | * @param stdClass $row |
492 | * @return string |
493 | */ |
494 | public function formatRow( IDatabase $db, $row ) { |
495 | $cmrAc = ConsumerAccessControl::wrap( |
496 | Consumer::newFromRow( $db, $row ), $this->getContext() |
497 | ); |
498 | |
499 | $cmrKey = $cmrAc->getConsumerKey(); |
500 | $stageKey = Consumer::$stageNames[$cmrAc->getStage()]; |
501 | |
502 | $link = $this->getLinkRenderer()->makeKnownLink( |
503 | $this->getPageTitle( $cmrKey ), |
504 | $this->msg( 'mwoauthmanageconsumers-review' )->text() |
505 | ); |
506 | |
507 | $time = $this->getLanguage()->timeanddate( |
508 | wfTimestamp( TS_MW, $cmrAc->getRegistration() ), true ); |
509 | |
510 | $encStageKey = htmlspecialchars( $stageKey ); |
511 | $r = "<li class='mw-mwoauthmanageconsumers-{$encStageKey}'>"; |
512 | |
513 | $r .= $time . " (<strong>{$link}</strong>)"; |
514 | |
515 | // Show last log entry (@TODO: title namespace?) |
516 | // @TODO: inject DB |
517 | $logHtml = ''; |
518 | LogEventsList::showLogExtract( $logHtml, 'mwoauthconsumer', '', '', [ |
519 | 'action' => Consumer::$stageActionNames[$cmrAc->getStage()], |
520 | 'conds' => [ |
521 | 'ls_field' => 'OAuthConsumer', |
522 | 'ls_value' => $cmrAc->getConsumerKey(), |
523 | ], |
524 | 'lim' => 1, |
525 | 'flags' => LogEventsList::NO_EXTRA_USER_LINKS, |
526 | ] ); |
527 | |
528 | $lang = $this->getLanguage(); |
529 | $data = [ |
530 | 'mwoauthmanageconsumers-name' => $cmrAc->escapeForHtml( $cmrAc->getNameAndVersion() ), |
531 | 'mwoauthmanageconsumers-user' => $cmrAc->escapeForHtml( $cmrAc->getUserName() ), |
532 | 'mwoauth-oauth-version' => $cmrAc->escapeForHtml( |
533 | $cmrAc->getOAuthVersion() === Consumer::OAUTH_VERSION_2 ? |
534 | $this->msg( 'mwoauth-oauth-version-2' ) : |
535 | $this->msg( 'mwoauth-oauth-version-1' ) |
536 | ), |
537 | 'mwoauthmanageconsumers-description' => $cmrAc->escapeForHtml( |
538 | $cmrAc->get( 'description', static function ( $s ) use ( $lang ) { |
539 | return $lang->truncateForVisual( $s, 10024 ); |
540 | } ) |
541 | ), |
542 | 'mwoauthmanageconsumers-email' => $cmrAc->escapeForHtml( $cmrAc->getEmail() ), |
543 | 'mwoauthmanageconsumers-consumerkey' => $cmrAc->escapeForHtml( $cmrAc->getConsumerKey() ), |
544 | 'mwoauthmanageconsumers-lastchange' => $logHtml, |
545 | ]; |
546 | |
547 | $r .= "<table class='mw-mwoauthmanageconsumers-body' " . |
548 | "cellspacing='1' cellpadding='3' border='1' width='100%'>"; |
549 | foreach ( $data as $msg => $encValue ) { |
550 | $r .= '<tr>' . |
551 | '<td><strong>' . $this->msg( $msg )->escaped() . '</strong></td>' . |
552 | '<td width=\'90%\'>' . $encValue . '</td>' . |
553 | '</tr>'; |
554 | } |
555 | $r .= '</table>'; |
556 | |
557 | $r .= '</li>'; |
558 | |
559 | return $r; |
560 | } |
561 | |
562 | protected function getGroupName() { |
563 | return 'users'; |
564 | } |
565 | |
566 | } |