r32819 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r32818‎ | r32819 | r32820 >
Date:18:19, 5 April 2008
Author:tstarling
Status:old
Tags:
Comment:
* Removed admin merge feature, was insecure.
* Added admin global account deletion feature, which allows a merge to be completely reversed.
* Added edit tokens for Special:CentralAuth write requests.
* Improved Special:CentralAuth error reporting and tweaked layout.
* Stat InitialiseSettings.php once only.
Modified paths:
  • /trunk/extensions/CentralAuth/CentralAuth.i18n.php (modified) (history)
  • /trunk/extensions/CentralAuth/CentralAuthUser.php (modified) (history)
  • /trunk/extensions/CentralAuth/SpecialCentralAuth.php (modified) (history)
  • /trunk/extensions/CentralAuth/WikiMap.php (modified) (history)

Diff [purge]

Index: trunk/extensions/CentralAuth/WikiMap.php
@@ -7,10 +7,14 @@
88 class WikiMap {
99 static function byDatabase( $dbname ) {
1010 global $wgConf, $IP;
 11+ static $initialiseSettingsDone = false;
1112
1213 // This is a damn dirty hack
13 - if( file_exists( "$IP/InitialiseSettings.php" ) ) {
14 - require_once "$IP/InitialiseSettings.php";
 14+ if ( !$initialiseSettingsDone ) {
 15+ $initialiseSettingsDone = true;
 16+ if( file_exists( "$IP/InitialiseSettings.php" ) ) {
 17+ require_once "$IP/InitialiseSettings.php";
 18+ }
1519 }
1620
1721 list( $major, $minor ) = $wgConf->siteFromDB( $dbname );
@@ -41,12 +45,13 @@
4246 }
4347
4448 function getHostname() {
45 - if( substr( $this->mServer, 0, 7 ) === 'http://' ) {
46 - return substr( $this->mServer, 7 );
47 - } else {
48 - throw new MWException( "wtf" );
 49+ $prefixes = array( 'http://', 'https://' );
 50+ foreach ( $prefixes as $prefix ) {
 51+ if ( substr( $this->mServer, 0, strlen( $prefix ) ) ) {
 52+ return substr( $this->mServer, strlen( $prefix ) );
 53+ }
4954 }
50 - // Q: Could it happen that they're using https:// ?
 55+ throw new MWException( "Invalid hostname for wiki {$this->mMinor}.{$this->mMajor}" );
5156 }
5257
5358 /**
@@ -68,8 +73,7 @@
6974
7075 function getUrl( $page ) {
7176 return
72 - 'http://' .
73 - $this->getHostname() .
 77+ $this->mServer .
7478 $this->getLocalUrl( $page );
7579 }
7680 }
Index: trunk/extensions/CentralAuth/CentralAuth.i18n.php
@@ -137,6 +137,18 @@
138138 'centralauth-admin-merge' => 'Merge selected',
139139 'centralauth-admin-bad-input' => 'Invalid merge selection',
140140 'centralauth-admin-none-selected' => 'No accounts selected to modify.',
 141+ 'centralauth-admin-already-unmerged' => 'Skipping $1, already unmerged',
 142+ 'centralauth-admin-unmerge-success' => 'Successfully unmerged $1 {{PLURAL:$2|account|accounts}}',
 143+ 'centralauth-admin-delete-title' => 'Delete account',
 144+ 'centralauth-admin-delete-description' => 'Deleting the global account will delete any
 145+global preferences, unattach all local accounts, and leave the global name free for another user to
 146+take. All local accounts will continue to exist. The passwords for local accounts created before
 147+the merge will revert to their pre-merge values.',
 148+ 'centralauth-admin-delete-button' => 'Delete this account',
 149+ 'centralauth-admin-delete-success' => 'Successfully deleted the the global account for "<nowiki>$1</nowiki>"',
 150+ 'centralauth-admin-nonexistent' => 'There is no global account for "<nowiki>$1</nowiki>"',
 151+ 'centralauth-admin-delete-nonexistent' => 'Error: the global account "<nowiki>$1</nowiki>" does not exist.',
 152+ 'centralauth-token-mismatch' => 'Sorry, we could not process your form submission due to a loss of session data.',
141153
142154 // Pretty timespan
143155 'centralauth-seconds-ago' => '$1 {{PLURAL:$1|second|seconds}} ago',
@@ -162,6 +174,7 @@
163175 'centralauth-renameuser-exists' => "<div class=\"errorbox\">Cannot rename user $2 as this username is reserved for a global account.</div>",
164176
165177 // Other messages
 178+ 'centralauth-invalid-wiki' => 'No such wiki DB: $1',
166179 'centralauth-account-exists' => 'Cannot create account: the requested username is already taken in the unified login system.',
167180 );
168181
Index: trunk/extensions/CentralAuth/CentralAuthUser.php
@@ -574,50 +574,78 @@
575575 return $dblist;
576576 }
577577
578 - public function adminAttach( $list, &$migrated=null, &$remaining=null ) {
 578+ /**
 579+ * Unattach a list of local accounts from the global account
 580+ * @param array $list List of wiki names
 581+ * @return Status
 582+ */
 583+ public function adminUnattach( $list ) {
 584+ if ( !count( $list ) ) {
 585+ return Status::newFatal( 'centralauth-admin-none-selected' );
 586+ }
 587+ $status = new Status;
579588 $valid = $this->validateList( $list );
580 - $unattached = $this->queryUnattached();
 589+ $invalid = array_diff( $list, $valid );
 590+ foreach ( $invalid as $wikiName ) {
 591+ $status->error( 'centralauth-invalid-wiki', $wikiName );
 592+ $status->failCount++;
 593+ }
 594+
 595+ $invalidCount = count( $list ) - count( $valid );
 596+ $missingCount = 0;
 597+ $dbcw = self::getCentralDB();
581598
582 - $migrated = array();
583 - $remaining = array();
584 -
585 - foreach( $unattached as $row ) {
586 - if( in_array( $row['dbName'], $valid ) ) {
587 - $this->attach( $row['dbName'], 'admin' );
588 - $migrated[] = $row['dbName'];
589 - } else {
590 - $remaining[] = $row['dbName'];
 599+ foreach ( $valid as $wikiName ) {
 600+ # Delete the user from the central localuser table
 601+ $dbcw->delete( self::tableName( 'localuser' ),
 602+ array(
 603+ 'lu_name' => $this->mName,
 604+ 'lu_dbname' => $wikiName ),
 605+ __METHOD__ );
 606+ if ( !$dbcw->affectedRows() ) {
 607+ $wiki = WikiMap::byDatabase( $wikiName );
 608+ $status->error( 'centralauth-admin-already-unmerged', $wiki->getDisplayName() );
 609+ $status->failCount++;
 610+ continue;
591611 }
592 - }
593612
594 - return count( $migrated ) == count( $valid );
595 - }
 613+ # Touch the local user row
 614+ $lb = wfGetLB( $wikiName );
 615+ $dblw = $lb->getConnection( DB_MASTER, array(), $wikiName );
 616+ $dblw->update( 'user', array( 'user_touched' => wfTimestampNow() ),
 617+ array( 'user_name' => $this->mName ), __METHOD__ );
 618+ $lb->reuseConnection( $dblw );
596619
597 - public function adminUnattach( $list, &$migrated=null, &$remaining=null ) {
598 - $valid = $this->validateList( $list );
 620+ $status->successCount++;
 621+ }
599622
600 - $dbw = self::getCentralDB();
601 - $dbw->delete( self::tableName( 'localuser' ),
602 - array(
603 - 'lu_name' => $this->mName,
604 - 'lu_dbname' => $valid ),
605 - __METHOD__ );
606 -
607 - // FIXME: touch remote-database user accounts
608 -
609 - // FIXME: proper... stuff
610 - $migrated = array();
611 - $remaining = $list;
612 -
613623 global $wgDBname;
614624 if( in_array( $wgDBname, $valid ) ) {
615625 $this->resetState();
616626 }
617627
618 - return count( $list ) == count( $valid );
 628+ return $status;
619629 }
620630
621631 /**
 632+ * Delete a global account
 633+ */
 634+ function adminDelete() {
 635+ $dbw = self::getCentralDB();
 636+ $dbw->begin();
 637+ # Delete and lock the globaluser row
 638+ $dbw->delete( 'globaluser', array( 'gu_name' => $this->mName ), __METHOD__ );
 639+ if ( !$dbw->affectedRows() ) {
 640+ $dbw->commit();
 641+ return Status::newFatal( 'centralauth-admin-delete-nonexistent', $this->mName );
 642+ }
 643+ # Delete the localuser rows
 644+ $dbw->delete( 'localuser', array( 'lu_name' => $this->mName ), __METHOD__ );
 645+ $dbw->commit();
 646+ return Status::newGood();
 647+ }
 648+
 649+ /**
622650 * Add a local account record for the given wiki to the central database.
623651 * @param string $dbname
624652 * @param int $localid
Index: trunk/extensions/CentralAuth/SpecialCentralAuth.php
@@ -36,7 +36,7 @@
3737 str_replace( '_', ' ',
3838 $wgRequest->getText( 'target', $subpage ) ) );
3939
40 - $this->mAttemptMerge = $wgRequest->wasPosted();
 40+ $this->mPosted = $wgRequest->wasPosted();
4141 $this->mMethod = $wgRequest->getVal( 'wpMethod' );
4242 $this->mPassword = $wgRequest->getVal( 'wpPassword' );
4343 $this->mDatabases = (array)$wgRequest->getArray( 'wpWikis' );
@@ -52,41 +52,67 @@
5353 // did / did not merge some accounts
5454 // do / don't have more accounts to merge
5555
 56+ if ( $this->mUserName === '' ) {
 57+ # First time through
 58+ $this->showUsernameForm();
 59+ return;
 60+ }
 61+
5662 $globalUser = new CentralAuthUser( $this->mUserName );
57 - $merged = $remainder = array();
58 - $this->showUsernameForm();
5963
60 - if( $this->mAttemptMerge ) {
61 - if( empty( $this->mDatabases ) ) {
62 - $this->showError( wfMsg( 'centralauth-admin-none-selected' ) );
63 - } elseif( $this->mMethod == 'admin' ) {
64 - $ok = $globalUser->adminAttach(
65 - $this->mDatabases,
66 - $merged,
67 - $remainder );
 64+ if ( !$globalUser->exists() ) {
 65+ $this->showError( wfMsgNoTrans( 'centralauth-admin-nonexistent', $this->mUserName ) );
 66+ $this->showUsernameForm();
 67+ return;
 68+ }
 69+
 70+ $deleted = false;
 71+
 72+ if( $this->mPosted ) {
 73+ if ( !$wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
 74+ $this->showError( wfMsg( 'centralauth-token-mismatch' ) );
6875 } elseif( $this->mMethod == 'unmerge' ) {
69 - $ok = $globalUser->adminUnattach(
70 - $this->mDatabases,
71 - $merged,
72 - $remainder );
 76+ $status = $globalUser->adminUnattach( $this->mDatabases );
 77+ if ( !$status->isGood() ) {
 78+ $this->showError( $status->getWikiText() );
 79+ } else {
 80+ global $wgLang;
 81+ $this->showSuccess( wfMsgNoTrans( 'centralauth-admin-unmerge-success',
 82+ $wgLang->formatNum( $status->successCount ),
 83+ $status->successCount ) );
 84+ }
 85+ } elseif ( $this->mMethod == 'delete' ) {
 86+ $status = $globalUser->adminDelete();
 87+ if ( !$status->isGood() ) {
 88+ $this->showError( $status->getWikiText() );
 89+ } else {
 90+ global $wgLang;
 91+ $this->showSuccess( wfMsgNoTrans( 'centralauth-admin-delete-success', $this->mUserName ) );
 92+ $deleted = true;
 93+ }
7394 } else {
7495 $this->showError( wfMsg( 'centralauth-admin-bad-input' ) );
7596 }
7697
77 - // if (! $ok) { ....
78 - } else {
79 - $merged = $globalUser->listAttached();
80 - $remainder = $globalUser->listUnattached();
8198 }
8299
83 - $this->showInfo();
 100+ $this->showUsernameForm();
 101+ if ( !$deleted ) {
 102+ $this->showInfo();
 103+ $this->showDeleteForm();
 104+ }
84105 }
85106
86107 function showError( $message ) {
87108 global $wgOut;
88 - $wgOut->addWikiText( "<div class='error'>$message</div>" );
 109+ $wgOut->addWikiText( "<div class='error'>\n$message</div>" );
89110 }
90111
 112+ function showSuccess( $message ) {
 113+ global $wgOut;
 114+ $wgOut->addWikiText( "<div class='success'>\n$message</div>" );
 115+ }
 116+
91117 function showUsernameForm() {
92118 global $wgOut, $wgScript;
93119 $wgOut->addHtml(
@@ -168,11 +194,17 @@
169195 }
170196
171197 function listRemainder( $list ) {
172 - return $this->listForm( $list, 'listRemainingWikiItem',
173 - 'admin', wfMsg( 'centralauth-admin-merge' ) );
 198+ ksort( $list );
 199+ $s = '<ul>';
 200+ foreach ( $list as $row ) {
 201+ $s .= '<li>' . $this->foreignUserLink( $row['dbName'] ) . "</li>\n";
 202+ }
 203+ $s .= '</ul>';
 204+ return $s;
174205 }
175206
176207 function listForm( $list, $listMethod, $action, $buttonText ) {
 208+ global $wgUser;
177209 ksort( $list );
178210 return
179211 Xml::openElement( 'form',
@@ -180,6 +212,7 @@
181213 'method' => 'post',
182214 'action' => $this->getTitle( $this->mUserName )->getLocalUrl( 'action=submit' ) ) ) .
183215 Xml::hidden( 'wpMethod', $action ) .
 216+ Xml::hidden( 'wpEditToken', $wgUser->editToken() ) .
184217 '<table>' .
185218 '<thead>' .
186219 $this->tableRow( 'th',
@@ -212,16 +245,6 @@
213246 );
214247 }
215248
216 - function listRemainingWikiItem( $row ) {
217 - global $wgLang;
218 - return $this->tableRow( 'td',
219 - array(
220 - $this->adminCheck( $row['dbName'] ),
221 - $this->foreignUserLink( $row['dbName'] ),
222 - )
223 - );
224 - }
225 -
226249 function tableRow( $element, $cols ) {
227250 return "<tr><$element>" .
228251 implode( "</$element><$element>", $cols ) .
@@ -251,4 +274,20 @@
252275 return
253276 Xml::check( 'wpWikis[]', false, array( 'value' => $dbname ) );
254277 }
 278+
 279+ function showDeleteForm() {
 280+ global $wgOut, $wgUser;
 281+ $wgOut->addHtml(
 282+ Xml::element( 'h2', array(), wfMsg( 'centralauth-admin-delete-title' ) ) .
 283+ Xml::openElement( 'form', array(
 284+ 'method' => 'POST',
 285+ 'action' => $this->getTitle()->getFullUrl( 'target=' . urlencode( $this->mUserName ) ) ) ) .
 286+ Xml::hidden( 'wpMethod', 'delete' ) .
 287+ Xml::hidden( 'wpEditToken', $wgUser->editToken() ) .
 288+ wfMsgExt( 'centralauth-admin-delete-description', 'parse' ) .
 289+ '<p>' .
 290+ Xml::submitButton( wfMsg( 'centralauth-admin-delete-button' ) ) .
 291+ '</p>' .
 292+ '</form>' );
 293+ }
255294 }

Status & tagging log