Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 128
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
UIHooks
0.00% covered (danger)
0.00%
0 / 128
0.00% covered (danger)
0.00%
0 / 8
552
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onGetPreferences
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
12
 onMessagesPreLoad
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
30
 onSpecialPageAfterExecute
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 1
30
 onSpecialPageBeforeFormDisplay
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 onBeforeCreateEchoEvent
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
 onSpecialPage_initList
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
6
 onLoginFormValidErrorMessages
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\OAuth\Frontend;
4
5use DerivativeContext;
6use HTMLForm;
7use MediaWiki\Cache\Hook\MessagesPreLoadHook;
8use MediaWiki\Extension\OAuth\Backend\Consumer;
9use MediaWiki\Extension\OAuth\Backend\Utils;
10use MediaWiki\Extension\OAuth\Control\ConsumerAccessControl;
11use MediaWiki\Extension\OAuth\Control\ConsumerSubmitControl;
12use MediaWiki\Extension\OAuth\Frontend\SpecialPages\SpecialMWOAuthConsumerRegistration;
13use MediaWiki\Extension\OAuth\Frontend\SpecialPages\SpecialMWOAuthManageConsumers;
14use MediaWiki\Hook\LoginFormValidErrorMessagesHook;
15use MediaWiki\Html\Html;
16use MediaWiki\Parser\Sanitizer;
17use MediaWiki\Permissions\PermissionManager;
18use MediaWiki\Preferences\Hook\GetPreferencesHook;
19use MediaWiki\SpecialPage\Hook\SpecialPage_initListHook;
20use MediaWiki\SpecialPage\Hook\SpecialPageAfterExecuteHook;
21use MediaWiki\SpecialPage\Hook\SpecialPageBeforeFormDisplayHook;
22use MediaWiki\SpecialPage\SpecialPage;
23use MediaWiki\User\User;
24use MediaWiki\WikiMap\WikiMap;
25use MWException;
26use OOUI\ButtonWidget;
27use RequestContext;
28
29/**
30 * Class containing GUI even handler functions for an OAuth environment
31 *
32 * @phpcs:disable MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName
33 */
34class UIHooks implements
35    GetPreferencesHook,
36    LoginFormValidErrorMessagesHook,
37    MessagesPreLoadHook,
38    SpecialPageAfterExecuteHook,
39    SpecialPageBeforeFormDisplayHook,
40    SpecialPage_initListHook
41{
42
43    /** @var PermissionManager */
44    private $permissionManager;
45
46    /**
47     * @param PermissionManager $permissionManager
48     */
49    public function __construct( PermissionManager $permissionManager ) {
50        $this->permissionManager = $permissionManager;
51    }
52
53    /**
54     * @param User $user
55     * @param array &$preferences
56     * @return bool
57     * @throws MWException
58     */
59    public function onGetPreferences( $user, &$preferences ) {
60        $dbr = Utils::getCentralDB( DB_REPLICA );
61        $conds = [
62            'oaac_user_id' => Utils::getCentralIdFromLocalUser( $user ),
63        ];
64
65        if ( !$this->permissionManager->userHasRight( $user, 'mwoauthviewsuppressed' ) ) {
66            $conds['oarc_deleted'] = 0;
67        }
68        $count = $dbr->newSelectQueryBuilder()
69            ->select( 'COUNT(*)' )
70            ->from( 'oauth_accepted_consumer' )
71            ->join( 'oauth_registered_consumer', null, 'oaac_consumer_id = oarc_id' )
72            ->where( $conds )
73            ->caller( __METHOD__ )
74            ->fetchField();
75
76        $control = new ButtonWidget( [
77            'href' => SpecialPage::getTitleFor( 'OAuthManageMyGrants' )->getLinkURL(),
78            'label' => wfMessage( 'mwoauth-prefs-managegrantslink' )->numParams( $count )->text()
79        ] );
80
81        $prefInsert = [ 'mwoauth-prefs-managegrants' =>
82            [
83                'section' => 'personal/info',
84                'label-message' => 'mwoauth-prefs-managegrants',
85                'type' => 'info',
86                'raw' => true,
87                'default' => (string)$control
88            ],
89        ];
90
91        if ( array_key_exists( 'usergroups', $preferences ) ) {
92            $preferences = wfArrayInsertAfter( $preferences, $prefInsert, 'usergroups' );
93        } else {
94            $preferences += $prefInsert;
95        }
96
97        return true;
98    }
99
100    /**
101     * Override MediaWiki namespace for a message
102     * @param string $title Message name (no prefix)
103     * @param string &$message Message wikitext
104     * @param string $code Language code
105     * @return bool false if we replaced $message
106     */
107    public function onMessagesPreLoad( $title, &$message, $code ) {
108        // Quick fail check
109        if ( substr( $title, 0, 15 ) !== 'Tag-OAuth_CID:_' ) {
110            return true;
111        }
112
113        // More expensive check
114        if ( !preg_match( '!^Tag-OAuth_CID:_(\d+)((?:-description)?)(?:/|$)!', $title, $m ) ) {
115            return true;
116        }
117
118        // Put the correct language in the context, so that later uses of $context->msg() will use it
119        $context = new DerivativeContext( RequestContext::getMain() );
120        $context->setLanguage( $code );
121
122        $dbr = Utils::getCentralDB( DB_REPLICA );
123        $cmrAc = ConsumerAccessControl::wrap(
124            Consumer::newFromId( $dbr, (int)$m[1] ),
125            $context
126        );
127        if ( !$cmrAc ) {
128            // Invalid consumer, skip it
129            return true;
130        }
131
132        if ( $m[2] ) {
133            $message = $cmrAc->escapeForWikitext( $cmrAc->getDescription() );
134        } else {
135            $target = SpecialPage::getTitleFor( 'OAuthListConsumers',
136                'view/' . $cmrAc->getConsumerKey()
137            );
138            $encName = $cmrAc->escapeForWikitext( $cmrAc->getNameAndVersion() );
139            $message = "[[$target|$encName]]";
140        }
141        return false;
142    }
143
144    /**
145     * Append OAuth-specific grants to Special:ListGrants
146     * @param SpecialPage $special
147     * @param string $par
148     * @return bool
149     */
150    public function onSpecialPageAfterExecute( $special, $par ) {
151        if ( $special->getName() !== 'Listgrants' ) {
152            return true;
153        }
154
155        $out = $special->getOutput();
156
157        $out->addWikiMsg( 'mwoauth-listgrants-extra-summary' );
158
159        $out->addHTML(
160            Html::openElement( 'table',
161            [ 'class' => 'wikitable mw-listgrouprights-table' ] ) .
162            '<tr>' .
163            Html::element( 'th', [], $special->msg( 'listgrants-grant' )->text() ) .
164            Html::element( 'th', [], $special->msg( 'listgrants-rights' )->text() ) .
165            '</tr>'
166        );
167
168        $grants = [
169            'mwoauth-authonly' => [],
170            'mwoauth-authonlyprivate' => [],
171        ];
172
173        foreach ( $grants as $grant => $rights ) {
174            $descs = [];
175            $rights = array_filter( $rights );
176            foreach ( $rights as $permission => $granted ) {
177                $descs[] = $special->msg(
178                    'listgrouprights-right-display',
179                    User::getRightDescription( $permission ),
180                    '<span class="mw-listgrants-right-name">' . $permission . '</span>'
181                )->parse();
182            }
183            if ( !count( $descs ) ) {
184                $grantCellHtml = '';
185            } else {
186                sort( $descs );
187                $grantCellHtml = '<ul><li>' . implode( "</li>\n<li>", $descs ) . '</li></ul>';
188            }
189
190            $id = Sanitizer::escapeIdForAttribute( $grant );
191            $out->addHTML( Html::rawElement( 'tr', [ 'id' => $id ],
192                "<td>" . $special->msg( "grant-$grant" )->escaped() . "</td>" .
193                "<td>" . $grantCellHtml . '</td>'
194            ) );
195        }
196
197        $out->addHTML( Html::closeElement( 'table' ) );
198
199        return true;
200    }
201
202    /**
203     * Add additional text to Special:BotPasswords
204     * @param string $name Special page name
205     * @param HTMLForm $form
206     * @return bool
207     */
208    public function onSpecialPageBeforeFormDisplay( $name, $form ) {
209        global $wgMWOAuthCentralWiki;
210
211        if ( $name === 'BotPasswords' ) {
212            if ( Utils::isCentralWiki() ) {
213                $url = SpecialPage::getTitleFor( 'OAuthConsumerRegistration' )->getFullURL();
214            } else {
215                $url = WikiMap::getForeignURL(
216                    $wgMWOAuthCentralWiki,
217                    // Cross-wiki, so don't localize
218                    'Special:OAuthConsumerRegistration'
219                );
220            }
221            $form->addPreHtml( $form->msg( 'mwoauth-botpasswords-note', $url )->parseAsBlock() );
222        }
223        return true;
224    }
225
226    /**
227     * @param array &$notifications
228     * @param array &$notificationCategories
229     * @param array &$icons
230     */
231    public static function onBeforeCreateEchoEvent(
232        &$notifications, &$notificationCategories, &$icons
233    ) {
234        global $wgOAuthGroupsToNotify;
235
236        if ( !Utils::isCentralWiki() ) {
237            return;
238        }
239
240        $notificationCategories['oauth-owner'] = [
241            'tooltip' => 'echo-pref-tooltip-oauth-owner',
242        ];
243        $notificationCategories['oauth-admin'] = [
244            'tooltip' => 'echo-pref-tooltip-oauth-admin',
245            'usergroups' => $wgOAuthGroupsToNotify,
246        ];
247
248        foreach ( ConsumerSubmitControl::$actions as $eventName ) {
249            // oauth-app-propose and oauth-app-update notifies admins of the app.
250            // oauth-app-approve, oauth-app-reject, oauth-app-disable and oauth-app-reenable
251            // notify owner of the change.
252            $notifications["oauth-app-$eventName"] =
253                EchoOAuthStageChangePresentationModel::getDefinition( $eventName );
254        }
255
256        $icons['oauth'] = [ 'path' => 'OAuth/resources/assets/echo-icon.png' ];
257    }
258
259    /**
260     * @param array &$specialPages
261     */
262    public function onSpecialPage_initList( &$specialPages ) {
263        if ( Utils::isCentralWiki() ) {
264            $specialPages['OAuthConsumerRegistration'] = [
265                'class' => SpecialMWOAuthConsumerRegistration::class,
266                'services' => [
267                    'PermissionManager',
268                    'GrantsInfo',
269                    'GrantsLocalization',
270                ],
271            ];
272            $specialPages['OAuthManageConsumers'] = [
273                'class' => SpecialMWOAuthManageConsumers::class,
274                'services' => [
275                    'GrantsLocalization',
276                ],
277            ];
278        }
279    }
280
281    /**
282     * Show help text when a user is redirected to provider login page
283     * @param array &$messages
284     * @return bool
285     */
286    public function onLoginFormValidErrorMessages( &$messages ) {
287        $messages[] = 'mwoauth-named-account-required-reason';
288        $messages[] = 'mwoauth-available-only-to-registered';
289        return true;
290    }
291}