Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
7.54% |
15 / 199 |
|
3.85% |
1 / 26 |
CRAP | |
0.00% |
0 / 1 |
CentralAuthHooks | |
7.54% |
15 / 199 |
|
3.85% |
1 / 26 |
5528.66 | |
0.00% |
0 / 1 |
onRegistration | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
30 | |||
onGetPreferences | |
0.00% |
0 / 41 |
|
0.00% |
0 / 1 |
72 | |||
onSpecialPasswordResetOnSubmit | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
20 | |||
getAuthIconHtml | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
42 | |||
getAutoLoginWikis | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
isMobileDomain | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
onUserArrayFromResult | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
onUserGetEmail | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
onUserGetEmailAuthenticationTimestamp | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 | |||
onInvalidateEmailComplete | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
onUserSetEmail | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
onUserSaveSettings | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
onUserSetEmailAuthenticationTimestamp | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
onUserGetRights | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
4 | |||
onUserIsLocked | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
30 | |||
onUserIsBot | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
56 | |||
onMakeGlobalVariablesScript | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
20 | |||
getCentralautologinJsData | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
30 | |||
getEdgeLoginHTML | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
20 | |||
isUIReloadRecommended | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
12 | |||
onTestCanonicalRedirect | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
onUserGetReservedNames | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
onApiQueryTokensRegisterTypes | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
onResourceLoaderForeignApiModules | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
onSessionCheckInfo | |
42.86% |
3 / 7 |
|
0.00% |
0 / 1 |
4.68 | |||
onGetLogTypesOnUser | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | */ |
20 | |
21 | namespace MediaWiki\Extension\CentralAuth; |
22 | |
23 | use CentralAuthSessionProvider; |
24 | use ExtensionRegistry; |
25 | use MediaWiki\Api\Hook\ApiQueryTokensRegisterTypesHook; |
26 | use MediaWiki\Extension\CentralAuth\Hooks\CentralAuthHookRunner; |
27 | use MediaWiki\Extension\CentralAuth\Hooks\Handlers\PageDisplayHookHandler; |
28 | use MediaWiki\Extension\CentralAuth\Special\SpecialCentralAutoLogin; |
29 | use MediaWiki\Extension\CentralAuth\User\CentralAuthUser; |
30 | use MediaWiki\Extension\CentralAuth\User\CentralAuthUserArrayFromResult; |
31 | use MediaWiki\Hook\GetLogTypesOnUserHook; |
32 | use MediaWiki\Hook\MakeGlobalVariablesScriptHook; |
33 | use MediaWiki\Hook\TestCanonicalRedirectHook; |
34 | use MediaWiki\Html\Html; |
35 | use MediaWiki\MediaWikiServices; |
36 | use MediaWiki\Output\OutputPage; |
37 | use MediaWiki\Permissions\Hook\UserGetRightsHook; |
38 | use MediaWiki\Preferences\Hook\GetPreferencesHook; |
39 | use MediaWiki\Request\ContentSecurityPolicy; |
40 | use MediaWiki\Request\WebRequest; |
41 | use MediaWiki\ResourceLoader as RL; |
42 | use MediaWiki\ResourceLoader\Hook\ResourceLoaderForeignApiModulesHook; |
43 | use MediaWiki\Session\CookieSessionProvider; |
44 | use MediaWiki\Session\Hook\SessionCheckInfoHook; |
45 | use MediaWiki\Session\SessionInfo; |
46 | use MediaWiki\SpecialPage\SpecialPage; |
47 | use MediaWiki\Title\Title; |
48 | use MediaWiki\User\Hook\InvalidateEmailCompleteHook; |
49 | use MediaWiki\User\Hook\SpecialPasswordResetOnSubmitHook; |
50 | use MediaWiki\User\Hook\UserArrayFromResultHook; |
51 | use MediaWiki\User\Hook\UserGetEmailAuthenticationTimestampHook; |
52 | use MediaWiki\User\Hook\UserGetEmailHook; |
53 | use MediaWiki\User\Hook\UserGetReservedNamesHook; |
54 | use MediaWiki\User\Hook\UserIsBotHook; |
55 | use MediaWiki\User\Hook\UserIsLockedHook; |
56 | use MediaWiki\User\Hook\UserSaveSettingsHook; |
57 | use MediaWiki\User\Hook\UserSetEmailAuthenticationTimestampHook; |
58 | use MediaWiki\User\Hook\UserSetEmailHook; |
59 | use MediaWiki\User\User; |
60 | use MediaWiki\User\UserArrayFromResult; |
61 | use MediaWiki\User\UserIdentity; |
62 | use MediaWiki\WikiMap\WikiMap; |
63 | use MobileContext; |
64 | use OOUI\ButtonWidget; |
65 | use OOUI\HorizontalLayout; |
66 | use OOUI\IconWidget; |
67 | use Wikimedia\Rdbms\IResultWrapper; |
68 | |
69 | class CentralAuthHooks implements |
70 | ApiQueryTokensRegisterTypesHook, |
71 | MakeGlobalVariablesScriptHook, |
72 | TestCanonicalRedirectHook, |
73 | UserGetRightsHook, |
74 | GetPreferencesHook, |
75 | ResourceLoaderForeignApiModulesHook, |
76 | SessionCheckInfoHook, |
77 | GetLogTypesOnUserHook, |
78 | InvalidateEmailCompleteHook, |
79 | SpecialPasswordResetOnSubmitHook, |
80 | UserArrayFromResultHook, |
81 | UserGetEmailAuthenticationTimestampHook, |
82 | UserGetEmailHook, |
83 | UserGetReservedNamesHook, |
84 | UserIsBotHook, |
85 | UserIsLockedHook, |
86 | UserSaveSettingsHook, |
87 | UserSetEmailAuthenticationTimestampHook, |
88 | UserSetEmailHook |
89 | { |
90 | |
91 | /** |
92 | * Called right after configuration variables have been set. |
93 | */ |
94 | public static function onRegistration() { |
95 | global $wgCentralAuthDatabase, $wgDBname, $wgSessionProviders, |
96 | $wgCentralIdLookupProvider; |
97 | |
98 | // Override $wgCentralAuthDatabase for Wikimedia Jenkins. |
99 | if ( defined( 'MW_QUIBBLE_CI' ) ) { |
100 | $wgCentralAuthDatabase = $wgDBname; |
101 | } |
102 | |
103 | // CentralAuthSessionProvider is supposed to replace core |
104 | // CookieSessionProvider, so remove the latter if both are configured |
105 | if ( isset( $wgSessionProviders[CookieSessionProvider::class] ) && |
106 | isset( $wgSessionProviders[CentralAuthSessionProvider::class] ) |
107 | ) { |
108 | unset( $wgSessionProviders[CookieSessionProvider::class] ); |
109 | } |
110 | |
111 | // Assume they want CentralAuth as the default central ID provider, unless |
112 | // already configured otherwise. |
113 | if ( $wgCentralIdLookupProvider === 'local' ) { |
114 | $wgCentralIdLookupProvider = 'CentralAuth'; |
115 | } |
116 | } |
117 | |
118 | /** |
119 | * Add a little pretty to the preferences user info section |
120 | * |
121 | * @param User $user |
122 | * @param array &$preferences |
123 | * @return bool |
124 | */ |
125 | public function onGetPreferences( $user, &$preferences ) { |
126 | // Possible states: |
127 | // - account not merged at all |
128 | // - global accounts exists, but this local account is unattached |
129 | // - this local account is attached, but migration incomplete |
130 | // - all local accounts are attached (no $message shown) |
131 | |
132 | $global = CentralAuthUser::getInstance( $user ); |
133 | $unattached = count( $global->listUnattached() ); |
134 | if ( $global->exists() ) { |
135 | if ( $global->isAttached() && $unattached ) { |
136 | // Migration incomplete - unattached accounts at other wikis |
137 | $attached = count( $global->listAttached() ); |
138 | $message = wfMessage( 'centralauth-prefs-unattached' )->parse() . |
139 | '<br />' . |
140 | wfMessage( 'centralauth-prefs-count-attached' ) |
141 | ->numParams( $attached )->parse() . |
142 | '<br />' . |
143 | wfMessage( 'centralauth-prefs-count-unattached' ) |
144 | ->numParams( $unattached )->parse(); |
145 | } elseif ( !$global->isAttached() ) { |
146 | // Global account exists but the local account is not attached |
147 | $message = wfMessage( 'centralauth-prefs-detail-unattached' )->parse(); |
148 | } |
149 | } else { |
150 | // No global account |
151 | $message = wfMessage( 'centralauth-prefs-not-managed' )->parse(); |
152 | } |
153 | |
154 | $manageButtons = []; |
155 | |
156 | if ( $unattached && $user->isAllowed( 'centralauth-merge' ) ) { |
157 | // Add "Manage your global account" button |
158 | $manageButtons[] = new ButtonWidget( [ |
159 | 'href' => SpecialPage::getTitleFor( 'MergeAccount' )->getLinkURL(), |
160 | 'label' => wfMessage( 'centralauth-prefs-manage' )->text(), |
161 | ] ); |
162 | } |
163 | |
164 | // Add "View your global account info" button |
165 | $manageButtons[] = new ButtonWidget( [ |
166 | 'href' => SpecialPage::getTitleFor( 'CentralAuth', $user->getName() )->getLinkURL(), |
167 | 'label' => wfMessage( 'centralauth-prefs-view' )->text(), |
168 | ] ); |
169 | |
170 | $manageLinkList = (string)( new HorizontalLayout( [ 'items' => $manageButtons ] ) ); |
171 | |
172 | $preferences['globalaccountstatus'] = [ |
173 | 'section' => 'personal/info', |
174 | 'label-message' => 'centralauth-prefs-status', |
175 | 'type' => 'info', |
176 | 'raw' => true, |
177 | 'default' => $manageLinkList |
178 | ]; |
179 | |
180 | // Display a notice about the user account status with an alert icon |
181 | if ( isset( $message ) ) { |
182 | $messageIconWidget = (string)new IconWidget( [ |
183 | 'icon' => 'alert', |
184 | 'flags' => [ 'destructive' ] |
185 | ] ); |
186 | $preferences['globalaccountstatus']['default'] = $messageIconWidget |
187 | . "$message<br>$manageLinkList"; |
188 | } |
189 | |
190 | return true; |
191 | } |
192 | |
193 | /** |
194 | * Show a nicer error when the user account does not exist on the local wiki, but |
195 | * does exist globally |
196 | * @param User[] &$users |
197 | * @param array $data |
198 | * @param string &$error |
199 | * @return bool |
200 | */ |
201 | public function onSpecialPasswordResetOnSubmit( &$users, $data, &$error ) { |
202 | $firstUser = reset( $users ); |
203 | if ( !( $firstUser instanceof UserIdentity ) ) { |
204 | // We can't handle this |
205 | return true; |
206 | } |
207 | |
208 | if ( !$firstUser->getId() ) { |
209 | $centralUser = CentralAuthUser::getInstance( $firstUser ); |
210 | if ( $centralUser->exists() ) { |
211 | $error = [ 'centralauth-account-exists-reset', $centralUser->getName() ]; |
212 | return false; |
213 | } |
214 | } |
215 | |
216 | return true; |
217 | } |
218 | |
219 | /** |
220 | * Get the HTML for an <img> element used to perform edge login, autologin (no-JS), or central logout. |
221 | * |
222 | * @param string $wikiID Target wiki |
223 | * @param string $page Target page, should be a Special:CentralAutoLogin subpage |
224 | * @param array $params URL query parameters. Some also affect the generated HTML: |
225 | * - 'type': when set to '1x1', generate an invisible pixel image, instead of a visible icon |
226 | * - 'mobile': when set, use target wiki's mobile domain URL instead of canonical URL |
227 | * @param ContentSecurityPolicy|null $csp If provided, it will be modified to allow requests to |
228 | * the target wiki. Otherwise, that must be done in 'ContentSecurityPolicyDefaultSource' hook. |
229 | * @return string HTML |
230 | */ |
231 | public static function getAuthIconHtml( |
232 | string $wikiID, string $page, array $params, ?ContentSecurityPolicy $csp |
233 | ): string { |
234 | // Use WikiMap to avoid localization of the 'Special' namespace, see T56195. |
235 | $wiki = WikiMap::getWiki( $wikiID ); |
236 | $url = wfAppendQuery( |
237 | $wiki->getCanonicalUrl( $page ), |
238 | $params |
239 | ); |
240 | if ( isset( $params['mobile'] ) ) { |
241 | // Do autologin on the mobile domain for each wiki |
242 | $url = MobileContext::singleton()->getMobileUrl( $url ); |
243 | } |
244 | if ( $csp ) { |
245 | $csp->addDefaultSrc( wfParseUrl( $url )['host'] ); |
246 | } |
247 | |
248 | $type = $params['type']; |
249 | return Html::element( 'img', [ |
250 | 'src' => $url, |
251 | 'alt' => '', |
252 | 'width' => $type === '1x1' ? 1 : 20, |
253 | 'height' => $type === '1x1' ? 1 : 20, |
254 | 'style' => $type === '1x1' ? 'border: none; position: absolute;' : 'border: 1px solid #ccc;', |
255 | ] ); |
256 | } |
257 | |
258 | /** |
259 | * Get autologin wikis, in the same format as $wgCentralAuthAutoLoginWikis, but with the |
260 | * current domain removed. |
261 | * @return string[] |
262 | */ |
263 | public static function getAutoLoginWikis(): array { |
264 | global $wgServer, $wgCentralAuthAutoLoginWikis, $wgCentralAuthCookieDomain; |
265 | $autoLoginWikis = $wgCentralAuthAutoLoginWikis; |
266 | if ( $wgCentralAuthCookieDomain ) { |
267 | unset( $autoLoginWikis[$wgCentralAuthCookieDomain] ); |
268 | } else { |
269 | $serverParts = MediaWikiServices::getInstance()->getUrlUtils()->parse( $wgServer ); |
270 | // @phan-suppress-next-line PhanTypeArraySuspiciousNullable |
271 | unset( $autoLoginWikis[ $serverParts['host'] ] ); |
272 | } |
273 | return $autoLoginWikis; |
274 | } |
275 | |
276 | /** |
277 | * @return bool |
278 | */ |
279 | public static function isMobileDomain() { |
280 | return ExtensionRegistry::getInstance()->isLoaded( 'MobileFrontend' ) |
281 | && MobileContext::singleton()->usingMobileDomain(); |
282 | } |
283 | |
284 | /** |
285 | * @param UserArrayFromResult|null &$userArray |
286 | * @param IResultWrapper $res |
287 | * @return bool |
288 | */ |
289 | public function onUserArrayFromResult( &$userArray, $res ) { |
290 | $userArray = new CentralAuthUserArrayFromResult( $res ); |
291 | return true; |
292 | } |
293 | |
294 | /** |
295 | * @param User $user |
296 | * @param string &$email |
297 | * @return bool |
298 | */ |
299 | public function onUserGetEmail( $user, &$email ) { |
300 | $ca = CentralAuthUser::getInstance( $user ); |
301 | if ( $ca->isAttached() ) { |
302 | $email = $ca->getEmail(); |
303 | } |
304 | return true; |
305 | } |
306 | |
307 | /** |
308 | * @param User $user |
309 | * @param string|null &$timestamp |
310 | * @return bool |
311 | */ |
312 | public function onUserGetEmailAuthenticationTimestamp( $user, &$timestamp ) { |
313 | $ca = CentralAuthUser::getInstance( $user ); |
314 | if ( $ca->isAttached() ) { |
315 | if ( $ca->isLocked() ) { |
316 | // Locked users shouldn't be receiving email (T87559) |
317 | $timestamp = null; |
318 | } else { |
319 | $timestamp = $ca->getEmailAuthenticationTimestamp(); |
320 | } |
321 | } |
322 | return true; |
323 | } |
324 | |
325 | /** |
326 | * @param User $user |
327 | * @return bool |
328 | */ |
329 | public function onInvalidateEmailComplete( $user ) { |
330 | $ca = CentralAuthUser::getPrimaryInstance( $user ); |
331 | if ( $ca->isAttached() ) { |
332 | $ca->setEmail( '' ); |
333 | $ca->setEmailAuthenticationTimestamp( null ); |
334 | $ca->saveSettings(); |
335 | } |
336 | return true; |
337 | } |
338 | |
339 | /** |
340 | * @param User $user |
341 | * @param string &$email |
342 | * @return bool |
343 | */ |
344 | public function onUserSetEmail( $user, &$email ) { |
345 | $ca = CentralAuthUser::getPrimaryInstance( $user ); |
346 | if ( $ca->isAttached() ) { |
347 | $ca->setEmail( $email ); |
348 | $ca->saveSettings(); |
349 | } |
350 | return true; |
351 | } |
352 | |
353 | /** |
354 | * @param User $user |
355 | * @return bool |
356 | */ |
357 | public function onUserSaveSettings( $user ) { |
358 | $ca = CentralAuthUser::getPrimaryInstance( $user ); |
359 | if ( $ca->isAttached() ) { |
360 | $ca->saveSettings(); |
361 | } |
362 | |
363 | return true; |
364 | } |
365 | |
366 | /** |
367 | * @param User $user |
368 | * @param ?string &$timestamp |
369 | * @return bool |
370 | */ |
371 | public function onUserSetEmailAuthenticationTimestamp( $user, &$timestamp ) { |
372 | $ca = CentralAuthUser::getInstance( $user ); |
373 | if ( $ca->isAttached() ) { |
374 | $latestCa = CentralAuthUser::newPrimaryInstanceFromId( $ca->getId() ); |
375 | if ( $latestCa->isAttached() ) { |
376 | $latestCa->setEmailAuthenticationTimestamp( $timestamp ); |
377 | $latestCa->saveSettings(); |
378 | } |
379 | } |
380 | |
381 | return true; |
382 | } |
383 | |
384 | /** |
385 | * @param User $user |
386 | * @param string[] &$rights |
387 | * @return bool |
388 | */ |
389 | public function onUserGetRights( $user, &$rights ) { |
390 | if ( $user->isRegistered() ) { |
391 | $centralUser = CentralAuthUser::getInstance( $user ); |
392 | |
393 | if ( $centralUser->exists() && $centralUser->isAttached() ) { |
394 | $extraRights = $centralUser->getGlobalRights(); |
395 | |
396 | $rights = array_merge( $extraRights, $rights ); |
397 | } |
398 | } |
399 | |
400 | return true; |
401 | } |
402 | |
403 | /** |
404 | * @param User $user |
405 | * @param bool &$isLocked |
406 | * @return bool |
407 | */ |
408 | public function onUserIsLocked( $user, &$isLocked ) { |
409 | $centralUser = CentralAuthUser::getInstance( $user ); |
410 | if ( $centralUser->exists() |
411 | && ( $centralUser->isAttached() || !$user->isRegistered() ) |
412 | && $centralUser->isLocked() |
413 | ) { |
414 | $isLocked = true; |
415 | return false; |
416 | } |
417 | |
418 | return true; |
419 | } |
420 | |
421 | /** |
422 | * @param User $user |
423 | * @param bool &$isBot |
424 | * @return bool |
425 | */ |
426 | public function onUserIsBot( $user, &$isBot ) { |
427 | // No need to check global groups if the user is already marked as a bot, |
428 | // and no global groups for unregistered user |
429 | if ( !$isBot && $user->isRegistered() ) { |
430 | $centralUser = CentralAuthUser::getInstance( $user ); |
431 | if ( $centralUser->exists() |
432 | && $centralUser->isAttached() |
433 | && array_intersect( [ 'bot', 'global-bot' ], $centralUser->getGlobalGroups() ) |
434 | && in_array( 'bot', $centralUser->getGlobalRights() ) |
435 | ) { |
436 | $isBot = true; |
437 | } |
438 | } |
439 | |
440 | return true; |
441 | } |
442 | |
443 | /** |
444 | * @param array &$vars |
445 | * @param OutputPage $out |
446 | */ |
447 | public function onMakeGlobalVariablesScript( &$vars, $out ): void { |
448 | $user = $out->getUser(); |
449 | if ( $user->isRegistered() ) { |
450 | $centralUser = CentralAuthUser::getInstance( $user ); |
451 | $vars['wgGlobalGroups'] = ( $centralUser->exists() && $centralUser->isAttached() ) |
452 | ? $centralUser->getActiveGlobalGroups() |
453 | : []; |
454 | } |
455 | } |
456 | |
457 | /** |
458 | * Data to be serialised as JSON for the 'ext.centralauth.centralautologin' module. |
459 | * @return array |
460 | */ |
461 | public static function getCentralautologinJsData() { |
462 | global $wgCentralAuthLoginWiki; |
463 | $data = []; |
464 | if ( $wgCentralAuthLoginWiki && $wgCentralAuthLoginWiki !== WikiMap::getCurrentWikiId() ) { |
465 | $url = WikiMap::getForeignURL( |
466 | $wgCentralAuthLoginWiki, 'Special:CentralAutoLogin/checkLoggedIn' |
467 | ); |
468 | if ( $url !== false ) { |
469 | $params = [ |
470 | 'type' => 'script', |
471 | 'wikiid' => WikiMap::getCurrentWikiId(), |
472 | ]; |
473 | if ( self::isMobileDomain() ) { |
474 | $params['mobile'] = 1; |
475 | } |
476 | $data['checkLoggedInURL'] = wfAppendQuery( $url, $params ); |
477 | } |
478 | } |
479 | return $data; |
480 | } |
481 | |
482 | /** |
483 | * Get a HTML fragment that will trigger central autologin, i.e. try to log in the user on |
484 | * each of $wgCentralAuthAutoLoginWikis in the background by embedding invisible pixel images |
485 | * which point to Special:CentralAutoLogin on each of those wikis. |
486 | * |
487 | * It also calls Special:CentralAutoLogin/refreshCookies on the central wiki, to refresh |
488 | * central session cookies if needed (e.g. because the "remember me" setting changed). |
489 | * |
490 | * This is typically used on the next page view after a successful login (by setting the |
491 | * CentralAuthDoEdgeLogin session flag). |
492 | * |
493 | * @return string |
494 | * |
495 | * @see SpecialCentralAutoLogin |
496 | * @see PageDisplayHookHandler::onBeforePageDisplay() |
497 | */ |
498 | public static function getEdgeLoginHTML() { |
499 | global $wgCentralAuthLoginWiki; |
500 | |
501 | $html = ''; |
502 | |
503 | foreach ( self::getAutoLoginWikis() as $domain => $wikiID ) { |
504 | $params = [ |
505 | 'type' => '1x1', |
506 | 'from' => WikiMap::getCurrentWikiId(), |
507 | ]; |
508 | if ( self::isMobileDomain() ) { |
509 | $params['mobile'] = 1; |
510 | } |
511 | $html .= self::getAuthIconHtml( $wikiID, 'Special:CentralAutoLogin/start', $params, null ); |
512 | } |
513 | |
514 | if ( $wgCentralAuthLoginWiki ) { |
515 | $html .= self::getAuthIconHtml( $wgCentralAuthLoginWiki, 'Special:CentralAutoLogin/refreshCookies', [ |
516 | 'type' => '1x1', |
517 | 'wikiid' => WikiMap::getCurrentWikiId(), |
518 | ], null ); |
519 | } |
520 | |
521 | return $html; |
522 | } |
523 | |
524 | /** |
525 | * Check whether the user's preferences are such that a UI reload is |
526 | * recommended. |
527 | * @param User $user |
528 | * @return bool |
529 | */ |
530 | public static function isUIReloadRecommended( User $user ) { |
531 | global $wgCentralAuthPrefsForUIReload; |
532 | $userFactory = MediaWikiServices::getInstance()->getUserFactory(); |
533 | $userOptionsLookup = MediaWikiServices::getInstance()->getUserOptionsLookup(); |
534 | |
535 | foreach ( $wgCentralAuthPrefsForUIReload as $pref ) { |
536 | if ( |
537 | $userOptionsLookup->getOption( $user, $pref ) !== |
538 | $userOptionsLookup->getDefaultOption( $pref, $userFactory->newAnonymous() ) |
539 | ) { |
540 | return true; |
541 | } |
542 | } |
543 | |
544 | $hookRunner = new CentralAuthHookRunner( MediaWikiServices::getInstance()->getHookContainer() ); |
545 | |
546 | $recommendReload = false; |
547 | $hookRunner->onCentralAuthIsUIReloadRecommended( $user, $recommendReload ); |
548 | return $recommendReload; |
549 | } |
550 | |
551 | /** |
552 | * Prevent "canonicalization" of Special:CentralAutoLogin to a localized |
553 | * Special namespace name. See T56195. |
554 | * @param WebRequest $request |
555 | * @param Title $title |
556 | * @param OutputPage $output |
557 | * @return bool |
558 | */ |
559 | public function onTestCanonicalRedirect( $request, $title, $output ) { |
560 | return $title->getNamespace() !== NS_SPECIAL || |
561 | !str_starts_with( $request->getVal( 'title', '' ), 'Special:CentralAutoLogin/' ); |
562 | } |
563 | |
564 | /** |
565 | * Handler for UserGetReservedNames |
566 | * @param array &$reservedUsernames |
567 | */ |
568 | public function onUserGetReservedNames( &$reservedUsernames ) { |
569 | $reservedUsernames[] = 'Global rename script'; |
570 | } |
571 | |
572 | /** |
573 | * @param array &$salts |
574 | * @return bool |
575 | */ |
576 | public function onApiQueryTokensRegisterTypes( &$salts ) { |
577 | $salts += [ |
578 | 'setglobalaccountstatus' => 'setglobalaccountstatus', |
579 | 'deleteglobalaccount' => 'deleteglobalaccount', |
580 | ]; |
581 | return true; |
582 | } |
583 | |
584 | /** |
585 | * @param string[] &$dependencies |
586 | * @param RL\Context|null $context |
587 | * @return void |
588 | */ |
589 | public function onResourceLoaderForeignApiModules( |
590 | &$dependencies, |
591 | $context = null |
592 | ): void { |
593 | $dependencies[] = 'ext.centralauth.ForeignApi'; |
594 | } |
595 | |
596 | /** |
597 | * Hook function to prevent logged-in sessions when a user is being |
598 | * renamed. |
599 | * @param string &$reason Failure reason to log |
600 | * @param SessionInfo $info |
601 | * @param WebRequest $request |
602 | * @param array|bool $metadata |
603 | * @param array|bool $data |
604 | * @return bool |
605 | */ |
606 | public function onSessionCheckInfo( |
607 | &$reason, |
608 | $info, |
609 | $request, |
610 | $metadata, |
611 | $data |
612 | ) { |
613 | $name = $info->getUserInfo()->getName(); |
614 | if ( $name !== null ) { |
615 | $centralUser = CentralAuthUser::getInstanceByName( $name ); |
616 | if ( $centralUser->renameInProgress() ) { |
617 | $reason = 'CentralAuth rename in progress'; |
618 | return false; |
619 | } |
620 | } |
621 | return true; |
622 | } |
623 | |
624 | /** |
625 | * @param array &$types |
626 | */ |
627 | public function onGetLogTypesOnUser( &$types ) { |
628 | $types[] = 'gblrights'; |
629 | } |
630 | } |