X-Git-Url: https://scripts.mit.edu/gitweb/autoinstallsdev/mediawiki.git/blobdiff_plain/19e297c21b10b1b8a3acad5e73fc71dcb35db44a..6932310fd58ebef145fa01eb76edf7150284d8ea:/includes/specials/SpecialBotPasswords.php diff --git a/includes/specials/SpecialBotPasswords.php b/includes/specials/SpecialBotPasswords.php new file mode 100644 index 00000000..056ce657 --- /dev/null +++ b/includes/specials/SpecialBotPasswords.php @@ -0,0 +1,367 @@ +getConfig()->get( 'EnableBotPasswords' ); + } + + protected function getLoginSecurityLevel() { + return $this->getName(); + } + + /** + * Main execution point + * @param string|null $par + */ + function execute( $par ) { + $this->getOutput()->disallowUserJs(); + $this->requireLogin(); + + $par = trim( $par ); + if ( strlen( $par ) === 0 ) { + $par = null; + } elseif ( strlen( $par ) > BotPassword::APPID_MAXLENGTH ) { + throw new ErrorPageError( 'botpasswords', 'botpasswords-bad-appid', + [ htmlspecialchars( $par ) ] ); + } + + parent::execute( $par ); + } + + protected function checkExecutePermissions( User $user ) { + parent::checkExecutePermissions( $user ); + + if ( !$this->getConfig()->get( 'EnableBotPasswords' ) ) { + throw new ErrorPageError( 'botpasswords', 'botpasswords-disabled' ); + } + + $this->userId = CentralIdLookup::factory()->centralIdFromLocalUser( $this->getUser() ); + if ( !$this->userId ) { + throw new ErrorPageError( 'botpasswords', 'botpasswords-no-central-id' ); + } + } + + protected function getFormFields() { + $fields = []; + + if ( $this->par !== null ) { + $this->botPassword = BotPassword::newFromCentralId( $this->userId, $this->par ); + if ( !$this->botPassword ) { + $this->botPassword = BotPassword::newUnsaved( [ + 'centralId' => $this->userId, + 'appId' => $this->par, + ] ); + } + + $sep = BotPassword::getSeparator(); + $fields[] = [ + 'type' => 'info', + 'label-message' => 'username', + 'default' => $this->getUser()->getName() . $sep . $this->par + ]; + + if ( $this->botPassword->isSaved() ) { + $fields['resetPassword'] = [ + 'type' => 'check', + 'label-message' => 'botpasswords-label-resetpassword', + ]; + if ( $this->botPassword->isInvalid() ) { + $fields['resetPassword']['default'] = true; + } + } + + $lang = $this->getLanguage(); + $showGrants = MWGrants::getValidGrants(); + $fields['grants'] = [ + 'type' => 'checkmatrix', + 'label-message' => 'botpasswords-label-grants', + 'help-message' => 'botpasswords-help-grants', + 'columns' => [ + $this->msg( 'botpasswords-label-grants-column' )->escaped() => 'grant' + ], + 'rows' => array_combine( + array_map( 'MWGrants::getGrantsLink', $showGrants ), + $showGrants + ), + 'default' => array_map( + function ( $g ) { + return "grant-$g"; + }, + $this->botPassword->getGrants() + ), + 'tooltips' => array_combine( + array_map( 'MWGrants::getGrantsLink', $showGrants ), + array_map( + function ( $rights ) use ( $lang ) { + return $lang->semicolonList( array_map( 'User::getRightDescription', $rights ) ); + }, + array_intersect_key( MWGrants::getRightsByGrant(), array_flip( $showGrants ) ) + ) + ), + 'force-options-on' => array_map( + function ( $g ) { + return "grant-$g"; + }, + MWGrants::getHiddenGrants() + ), + ]; + + $fields['restrictions'] = [ + 'class' => 'HTMLRestrictionsField', + 'required' => true, + 'default' => $this->botPassword->getRestrictions(), + ]; + + } else { + $linkRenderer = $this->getLinkRenderer(); + $passwordFactory = new PasswordFactory(); + $passwordFactory->init( $this->getConfig() ); + + $dbr = BotPassword::getDB( DB_REPLICA ); + $res = $dbr->select( + 'bot_passwords', + [ 'bp_app_id', 'bp_password' ], + [ 'bp_user' => $this->userId ], + __METHOD__ + ); + foreach ( $res as $row ) { + try { + $password = $passwordFactory->newFromCiphertext( $row->bp_password ); + $passwordInvalid = $password instanceof InvalidPassword; + unset( $password ); + } catch ( PasswordError $ex ) { + $passwordInvalid = true; + } + + $text = $linkRenderer->makeKnownLink( + $this->getPageTitle( $row->bp_app_id ), + $row->bp_app_id + ); + if ( $passwordInvalid ) { + $text .= $this->msg( 'word-separator' )->escaped() + . $this->msg( 'botpasswords-label-needsreset' )->parse(); + } + + $fields[] = [ + 'section' => 'existing', + 'type' => 'info', + 'raw' => true, + 'default' => $text, + ]; + } + + $fields['appId'] = [ + 'section' => 'createnew', + 'type' => 'textwithbutton', + 'label-message' => 'botpasswords-label-appid', + 'buttondefault' => $this->msg( 'botpasswords-label-create' )->text(), + 'buttonflags' => [ 'progressive', 'primary' ], + 'required' => true, + 'size' => BotPassword::APPID_MAXLENGTH, + 'maxlength' => BotPassword::APPID_MAXLENGTH, + 'validation-callback' => function ( $v ) { + $v = trim( $v ); + return $v !== '' && strlen( $v ) <= BotPassword::APPID_MAXLENGTH; + }, + ]; + + $fields[] = [ + 'type' => 'hidden', + 'default' => 'new', + 'name' => 'op', + ]; + } + + return $fields; + } + + protected function alterForm( HTMLForm $form ) { + $form->setId( 'mw-botpasswords-form' ); + $form->setTableId( 'mw-botpasswords-table' ); + $form->addPreText( $this->msg( 'botpasswords-summary' )->parseAsBlock() ); + $form->suppressDefaultSubmit(); + + if ( $this->par !== null ) { + if ( $this->botPassword->isSaved() ) { + $form->setWrapperLegendMsg( 'botpasswords-editexisting' ); + $form->addButton( [ + 'name' => 'op', + 'value' => 'update', + 'label-message' => 'botpasswords-label-update', + 'flags' => [ 'primary', 'progressive' ], + ] ); + $form->addButton( [ + 'name' => 'op', + 'value' => 'delete', + 'label-message' => 'botpasswords-label-delete', + 'flags' => [ 'destructive' ], + ] ); + } else { + $form->setWrapperLegendMsg( 'botpasswords-createnew' ); + $form->addButton( [ + 'name' => 'op', + 'value' => 'create', + 'label-message' => 'botpasswords-label-create', + 'flags' => [ 'primary', 'progressive' ], + ] ); + } + + $form->addButton( [ + 'name' => 'op', + 'value' => 'cancel', + 'label-message' => 'botpasswords-label-cancel' + ] ); + } + } + + public function onSubmit( array $data ) { + $op = $this->getRequest()->getVal( 'op', '' ); + + switch ( $op ) { + case 'new': + $this->getOutput()->redirect( $this->getPageTitle( $data['appId'] )->getFullURL() ); + return false; + + case 'create': + $this->operation = 'insert'; + return $this->save( $data ); + + case 'update': + $this->operation = 'update'; + return $this->save( $data ); + + case 'delete': + $this->operation = 'delete'; + $bp = BotPassword::newFromCentralId( $this->userId, $this->par ); + if ( $bp ) { + $bp->delete(); + } + return Status::newGood(); + + case 'cancel': + $this->getOutput()->redirect( $this->getPageTitle()->getFullURL() ); + return false; + } + + return false; + } + + private function save( array $data ) { + $bp = BotPassword::newUnsaved( [ + 'centralId' => $this->userId, + 'appId' => $this->par, + 'restrictions' => $data['restrictions'], + 'grants' => array_merge( + MWGrants::getHiddenGrants(), + preg_replace( '/^grant-/', '', $data['grants'] ) + ) + ] ); + + if ( $this->operation === 'insert' || !empty( $data['resetPassword'] ) ) { + $this->password = BotPassword::generatePassword( $this->getConfig() ); + $passwordFactory = new PasswordFactory(); + $passwordFactory->init( RequestContext::getMain()->getConfig() ); + $password = $passwordFactory->newFromPlaintext( $this->password ); + } else { + $password = null; + } + + if ( $bp->save( $this->operation, $password ) ) { + return Status::newGood(); + } else { + // Messages: botpasswords-insert-failed, botpasswords-update-failed + return Status::newFatal( "botpasswords-{$this->operation}-failed", $this->par ); + } + } + + public function onSuccess() { + $out = $this->getOutput(); + + $username = $this->getUser()->getName(); + switch ( $this->operation ) { + case 'insert': + $out->setPageTitle( $this->msg( 'botpasswords-created-title' )->text() ); + $out->addWikiMsg( 'botpasswords-created-body', $this->par, $username ); + break; + + case 'update': + $out->setPageTitle( $this->msg( 'botpasswords-updated-title' )->text() ); + $out->addWikiMsg( 'botpasswords-updated-body', $this->par, $username ); + break; + + case 'delete': + $out->setPageTitle( $this->msg( 'botpasswords-deleted-title' )->text() ); + $out->addWikiMsg( 'botpasswords-deleted-body', $this->par, $username ); + $this->password = null; + break; + } + + if ( $this->password !== null ) { + $sep = BotPassword::getSeparator(); + $out->addWikiMsg( + 'botpasswords-newpassword', + htmlspecialchars( $username . $sep . $this->par ), + htmlspecialchars( $this->password ), + htmlspecialchars( $username ), + htmlspecialchars( $this->par . $sep . $this->password ) + ); + $this->password = null; + } + + $out->addReturnTo( $this->getPageTitle() ); + } + + protected function getGroupName() { + return 'users'; + } + + protected function getDisplayFormat() { + return 'ooui'; + } +}