]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/specials/SpecialBotPasswords.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / includes / specials / SpecialBotPasswords.php
1 <?php
2 /**
3  * Implements Special:BotPasswords
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  * http://www.gnu.org/copyleft/gpl.html
19  *
20  * @file
21  * @ingroup SpecialPage
22  */
23
24 /**
25  * Let users manage bot passwords
26  *
27  * @ingroup SpecialPage
28  */
29 class SpecialBotPasswords extends FormSpecialPage {
30
31         /** @var int Central user ID */
32         private $userId = 0;
33
34         /** @var BotPassword|null Bot password being edited, if any */
35         private $botPassword = null;
36
37         /** @var string Operation being performed: create, update, delete */
38         private $operation = null;
39
40         /** @var string New password set, for communication between onSubmit() and onSuccess() */
41         private $password = null;
42
43         public function __construct() {
44                 parent::__construct( 'BotPasswords', 'editmyprivateinfo' );
45         }
46
47         /**
48          * @return bool
49          */
50         public function isListed() {
51                 return $this->getConfig()->get( 'EnableBotPasswords' );
52         }
53
54         protected function getLoginSecurityLevel() {
55                 return $this->getName();
56         }
57
58         /**
59          * Main execution point
60          * @param string|null $par
61          */
62         function execute( $par ) {
63                 $this->getOutput()->disallowUserJs();
64                 $this->requireLogin();
65
66                 $par = trim( $par );
67                 if ( strlen( $par ) === 0 ) {
68                         $par = null;
69                 } elseif ( strlen( $par ) > BotPassword::APPID_MAXLENGTH ) {
70                         throw new ErrorPageError( 'botpasswords', 'botpasswords-bad-appid',
71                                 [ htmlspecialchars( $par ) ] );
72                 }
73
74                 parent::execute( $par );
75         }
76
77         protected function checkExecutePermissions( User $user ) {
78                 parent::checkExecutePermissions( $user );
79
80                 if ( !$this->getConfig()->get( 'EnableBotPasswords' ) ) {
81                         throw new ErrorPageError( 'botpasswords', 'botpasswords-disabled' );
82                 }
83
84                 $this->userId = CentralIdLookup::factory()->centralIdFromLocalUser( $this->getUser() );
85                 if ( !$this->userId ) {
86                         throw new ErrorPageError( 'botpasswords', 'botpasswords-no-central-id' );
87                 }
88         }
89
90         protected function getFormFields() {
91                 $fields = [];
92
93                 if ( $this->par !== null ) {
94                         $this->botPassword = BotPassword::newFromCentralId( $this->userId, $this->par );
95                         if ( !$this->botPassword ) {
96                                 $this->botPassword = BotPassword::newUnsaved( [
97                                         'centralId' => $this->userId,
98                                         'appId' => $this->par,
99                                 ] );
100                         }
101
102                         $sep = BotPassword::getSeparator();
103                         $fields[] = [
104                                 'type' => 'info',
105                                 'label-message' => 'username',
106                                 'default' => $this->getUser()->getName() . $sep . $this->par
107                         ];
108
109                         if ( $this->botPassword->isSaved() ) {
110                                 $fields['resetPassword'] = [
111                                         'type' => 'check',
112                                         'label-message' => 'botpasswords-label-resetpassword',
113                                 ];
114                                 if ( $this->botPassword->isInvalid() ) {
115                                         $fields['resetPassword']['default'] = true;
116                                 }
117                         }
118
119                         $lang = $this->getLanguage();
120                         $showGrants = MWGrants::getValidGrants();
121                         $fields['grants'] = [
122                                 'type' => 'checkmatrix',
123                                 'label-message' => 'botpasswords-label-grants',
124                                 'help-message' => 'botpasswords-help-grants',
125                                 'columns' => [
126                                         $this->msg( 'botpasswords-label-grants-column' )->escaped() => 'grant'
127                                 ],
128                                 'rows' => array_combine(
129                                         array_map( 'MWGrants::getGrantsLink', $showGrants ),
130                                         $showGrants
131                                 ),
132                                 'default' => array_map(
133                                         function ( $g ) {
134                                                 return "grant-$g";
135                                         },
136                                         $this->botPassword->getGrants()
137                                 ),
138                                 'tooltips' => array_combine(
139                                         array_map( 'MWGrants::getGrantsLink', $showGrants ),
140                                         array_map(
141                                                 function ( $rights ) use ( $lang ) {
142                                                         return $lang->semicolonList( array_map( 'User::getRightDescription', $rights ) );
143                                                 },
144                                                 array_intersect_key( MWGrants::getRightsByGrant(), array_flip( $showGrants ) )
145                                         )
146                                 ),
147                                 'force-options-on' => array_map(
148                                         function ( $g ) {
149                                                 return "grant-$g";
150                                         },
151                                         MWGrants::getHiddenGrants()
152                                 ),
153                         ];
154
155                         $fields['restrictions'] = [
156                                 'class' => 'HTMLRestrictionsField',
157                                 'required' => true,
158                                 'default' => $this->botPassword->getRestrictions(),
159                         ];
160
161                 } else {
162                         $linkRenderer = $this->getLinkRenderer();
163                         $passwordFactory = new PasswordFactory();
164                         $passwordFactory->init( $this->getConfig() );
165
166                         $dbr = BotPassword::getDB( DB_REPLICA );
167                         $res = $dbr->select(
168                                 'bot_passwords',
169                                 [ 'bp_app_id', 'bp_password' ],
170                                 [ 'bp_user' => $this->userId ],
171                                 __METHOD__
172                         );
173                         foreach ( $res as $row ) {
174                                 try {
175                                         $password = $passwordFactory->newFromCiphertext( $row->bp_password );
176                                         $passwordInvalid = $password instanceof InvalidPassword;
177                                         unset( $password );
178                                 } catch ( PasswordError $ex ) {
179                                         $passwordInvalid = true;
180                                 }
181
182                                 $text = $linkRenderer->makeKnownLink(
183                                         $this->getPageTitle( $row->bp_app_id ),
184                                         $row->bp_app_id
185                                 );
186                                 if ( $passwordInvalid ) {
187                                         $text .= $this->msg( 'word-separator' )->escaped()
188                                                 . $this->msg( 'botpasswords-label-needsreset' )->parse();
189                                 }
190
191                                 $fields[] = [
192                                         'section' => 'existing',
193                                         'type' => 'info',
194                                         'raw' => true,
195                                         'default' => $text,
196                                 ];
197                         }
198
199                         $fields['appId'] = [
200                                 'section' => 'createnew',
201                                 'type' => 'textwithbutton',
202                                 'label-message' => 'botpasswords-label-appid',
203                                 'buttondefault' => $this->msg( 'botpasswords-label-create' )->text(),
204                                 'buttonflags' => [ 'progressive', 'primary' ],
205                                 'required' => true,
206                                 'size' => BotPassword::APPID_MAXLENGTH,
207                                 'maxlength' => BotPassword::APPID_MAXLENGTH,
208                                 'validation-callback' => function ( $v ) {
209                                         $v = trim( $v );
210                                         return $v !== '' && strlen( $v ) <= BotPassword::APPID_MAXLENGTH;
211                                 },
212                         ];
213
214                         $fields[] = [
215                                 'type' => 'hidden',
216                                 'default' => 'new',
217                                 'name' => 'op',
218                         ];
219                 }
220
221                 return $fields;
222         }
223
224         protected function alterForm( HTMLForm $form ) {
225                 $form->setId( 'mw-botpasswords-form' );
226                 $form->setTableId( 'mw-botpasswords-table' );
227                 $form->addPreText( $this->msg( 'botpasswords-summary' )->parseAsBlock() );
228                 $form->suppressDefaultSubmit();
229
230                 if ( $this->par !== null ) {
231                         if ( $this->botPassword->isSaved() ) {
232                                 $form->setWrapperLegendMsg( 'botpasswords-editexisting' );
233                                 $form->addButton( [
234                                         'name' => 'op',
235                                         'value' => 'update',
236                                         'label-message' => 'botpasswords-label-update',
237                                         'flags' => [ 'primary', 'progressive' ],
238                                 ] );
239                                 $form->addButton( [
240                                         'name' => 'op',
241                                         'value' => 'delete',
242                                         'label-message' => 'botpasswords-label-delete',
243                                         'flags' => [ 'destructive' ],
244                                 ] );
245                         } else {
246                                 $form->setWrapperLegendMsg( 'botpasswords-createnew' );
247                                 $form->addButton( [
248                                         'name' => 'op',
249                                         'value' => 'create',
250                                         'label-message' => 'botpasswords-label-create',
251                                         'flags' => [ 'primary', 'progressive' ],
252                                 ] );
253                         }
254
255                         $form->addButton( [
256                                 'name' => 'op',
257                                 'value' => 'cancel',
258                                 'label-message' => 'botpasswords-label-cancel'
259                         ] );
260                 }
261         }
262
263         public function onSubmit( array $data ) {
264                 $op = $this->getRequest()->getVal( 'op', '' );
265
266                 switch ( $op ) {
267                         case 'new':
268                                 $this->getOutput()->redirect( $this->getPageTitle( $data['appId'] )->getFullURL() );
269                                 return false;
270
271                         case 'create':
272                                 $this->operation = 'insert';
273                                 return $this->save( $data );
274
275                         case 'update':
276                                 $this->operation = 'update';
277                                 return $this->save( $data );
278
279                         case 'delete':
280                                 $this->operation = 'delete';
281                                 $bp = BotPassword::newFromCentralId( $this->userId, $this->par );
282                                 if ( $bp ) {
283                                         $bp->delete();
284                                 }
285                                 return Status::newGood();
286
287                         case 'cancel':
288                                 $this->getOutput()->redirect( $this->getPageTitle()->getFullURL() );
289                                 return false;
290                 }
291
292                 return false;
293         }
294
295         private function save( array $data ) {
296                 $bp = BotPassword::newUnsaved( [
297                         'centralId' => $this->userId,
298                         'appId' => $this->par,
299                         'restrictions' => $data['restrictions'],
300                         'grants' => array_merge(
301                                 MWGrants::getHiddenGrants(),
302                                 preg_replace( '/^grant-/', '', $data['grants'] )
303                         )
304                 ] );
305
306                 if ( $this->operation === 'insert' || !empty( $data['resetPassword'] ) ) {
307                         $this->password = BotPassword::generatePassword( $this->getConfig() );
308                         $passwordFactory = new PasswordFactory();
309                         $passwordFactory->init( RequestContext::getMain()->getConfig() );
310                         $password = $passwordFactory->newFromPlaintext( $this->password );
311                 } else {
312                         $password = null;
313                 }
314
315                 if ( $bp->save( $this->operation, $password ) ) {
316                         return Status::newGood();
317                 } else {
318                         // Messages: botpasswords-insert-failed, botpasswords-update-failed
319                         return Status::newFatal( "botpasswords-{$this->operation}-failed", $this->par );
320                 }
321         }
322
323         public function onSuccess() {
324                 $out = $this->getOutput();
325
326                 $username = $this->getUser()->getName();
327                 switch ( $this->operation ) {
328                         case 'insert':
329                                 $out->setPageTitle( $this->msg( 'botpasswords-created-title' )->text() );
330                                 $out->addWikiMsg( 'botpasswords-created-body', $this->par, $username );
331                                 break;
332
333                         case 'update':
334                                 $out->setPageTitle( $this->msg( 'botpasswords-updated-title' )->text() );
335                                 $out->addWikiMsg( 'botpasswords-updated-body', $this->par, $username );
336                                 break;
337
338                         case 'delete':
339                                 $out->setPageTitle( $this->msg( 'botpasswords-deleted-title' )->text() );
340                                 $out->addWikiMsg( 'botpasswords-deleted-body', $this->par, $username );
341                                 $this->password = null;
342                                 break;
343                 }
344
345                 if ( $this->password !== null ) {
346                         $sep = BotPassword::getSeparator();
347                         $out->addWikiMsg(
348                                 'botpasswords-newpassword',
349                                 htmlspecialchars( $username . $sep . $this->par ),
350                                 htmlspecialchars( $this->password ),
351                                 htmlspecialchars( $username ),
352                                 htmlspecialchars( $this->par . $sep . $this->password )
353                         );
354                         $this->password = null;
355                 }
356
357                 $out->addReturnTo( $this->getPageTitle() );
358         }
359
360         protected function getGroupName() {
361                 return 'users';
362         }
363
364         protected function getDisplayFormat() {
365                 return 'ooui';
366         }
367 }