Index: trunk/CentralAuth/TestPlugin.php |
— | — | @@ -59,10 +59,61 @@ |
60 | 60 | unique key (gu_name) |
61 | 61 | ) CHARSET=latin1; |
62 | 62 | |
| 63 | + |
| 64 | +-- Migration state table |
| 65 | +CREATE TABLE localuser ( |
| 66 | + -- Database name of the wiki |
| 67 | + lu_dbname varchar(32) binary, |
| 68 | + |
| 69 | + -- user_id on the local wiki |
| 70 | + lu_id int, |
| 71 | + |
| 72 | + -- Username |
| 73 | + lu_name varchar(255) binary, |
| 74 | + |
| 75 | + -- User'd old password hash; salt is lu_id |
| 76 | + lu_password varchar(255) binary, |
| 77 | + |
| 78 | + -- The user_email and user_email_authenticated state from local wiki |
| 79 | + lu_email varchar(255) binary, |
| 80 | + lu_email_authenticated char(14) binary, |
| 81 | + |
| 82 | + -- A count of revisions and/or other actions made during migration |
| 83 | + -- May be null if it hasn't yet been checked |
| 84 | + lu_editcount int, |
| 85 | + |
| 86 | + -- Set to 1 if already migrated successfully, |
| 87 | + -- 0 if the account is still awaiting migration and attachment. |
| 88 | + lu_attached tinyint, |
| 89 | + |
| 90 | + primary key (lu_dbname,lu_id), |
| 91 | + unique key (lu_dbname,lu_name), |
| 92 | + key (lu_name,lu_dbname) |
| 93 | +) CHARSET=latin1; |
| 94 | + |
| 95 | + |
| 96 | + |
63 | 97 | */ |
64 | 98 | |
65 | 99 | $wgCentralAuthDatabase = 'authtest'; |
66 | 100 | |
| 101 | +/** |
| 102 | + * Migration states: [not yet implemented fully] |
| 103 | + * 'premigrate': Local 'user' tables are still used for authentication, |
| 104 | + * but with certain operations disabled to prevent conflicts |
| 105 | + * while data is migrated to the central auth server. |
| 106 | + * |
| 107 | + * 'migration': Authentication is done against 'globaluser', with automatic |
| 108 | + * transparent migration on login. |
| 109 | + * |
| 110 | + * 'production': Any remaining non-migrated accounts are locked out. |
| 111 | + * |
| 112 | + * 'testing': As 'premigrate', but no locking is done. Use to run tests |
| 113 | + * of the pass-0 data generation. |
| 114 | + */ |
| 115 | +$wgCentralAuthState = 'disabled'; |
| 116 | + |
| 117 | + |
67 | 118 | class CentralAuthUser { |
68 | 119 | function __construct( $username ) { |
69 | 120 | $this->mName = $username; |
— | — | @@ -123,6 +174,87 @@ |
124 | 175 | } |
125 | 176 | |
126 | 177 | /** |
| 178 | + * For use in migration pass zero. |
| 179 | + * Store local user data into the auth server's migration table. |
| 180 | + */ |
| 181 | + static function storeLocalData( $dbname, $row, $editCount ) { |
| 182 | + $dbw = wfGetDB( DB_MASTER, 'centralauth' ); |
| 183 | + $ok = $dbw->insert( |
| 184 | + 'localuser', |
| 185 | + array( |
| 186 | + 'lu_dbname' => $dbname, |
| 187 | + 'lu_id' => $row->user_id, |
| 188 | + 'lu_name' => $row->user_name, |
| 189 | + 'lu_password' => $row->user_password, |
| 190 | + 'lu_email' => $row->user_email, |
| 191 | + 'lu_email_authenticated' => $row->user_email_authenticated, |
| 192 | + 'lu_editcount' => $editCount, |
| 193 | + 'lu_attached' => 0, // Not yet migrated! |
| 194 | + ), |
| 195 | + __METHOD__ ); |
| 196 | + wfDebugLog( 'CentralAuth', |
| 197 | + "stored migration data for '$row->user_name' on $dbname" ); |
| 198 | + } |
| 199 | + |
| 200 | + /** |
| 201 | + * Try to auto-migrate this account, possibly using a given |
| 202 | + * password plaintext for additional oomph. |
| 203 | + * @fixme add some locking or something |
| 204 | + */ |
| 205 | + function attemptAutoMigration( $password='' ) { |
| 206 | + $dbw = wfGetDB( DB_MASTER, 'centralauth' ); |
| 207 | + $dbw->begin(); |
| 208 | + |
| 209 | + $attached = array(); |
| 210 | + $unattached = array(); |
| 211 | + |
| 212 | + $result = $dbw->select( 'localuser', |
| 213 | + array( |
| 214 | + 'lu_dbname', |
| 215 | + 'lu_id', |
| 216 | + 'lu_name', |
| 217 | + 'lu_password', |
| 218 | + 'lu_email', |
| 219 | + 'lu_email_authenticated', |
| 220 | + 'lu_editcount', |
| 221 | + 'lu_attached', |
| 222 | + ), |
| 223 | + array( 'lu_name' => $this->mName ), |
| 224 | + __METHOD__ ); |
| 225 | + while( $row = $dbw->fetchObject( $result ) ) { |
| 226 | + if( $row->lu_attched ) { |
| 227 | + $attached[] = $row; |
| 228 | + } else { |
| 229 | + $unattached[] = $row; |
| 230 | + } |
| 231 | + } |
| 232 | + $dbw->freeResult( $result ); |
| 233 | + |
| 234 | + if( count( $unattached ) == 0 ) { |
| 235 | + wfDebugLog( 'CentralAuth', |
| 236 | + "All accounts already migrated for '$this->mName'" ); |
| 237 | + $dbw->rollback(); |
| 238 | + return false; |
| 239 | + // Or... should this return true ? |
| 240 | + } |
| 241 | + |
| 242 | + if( count( $attached ) == 0 ) { |
| 243 | + // We have to pick a winner... |
| 244 | + } |
| 245 | + |
| 246 | + // Look for accounts we can match by password |
| 247 | + foreach( $unattached as $key => $row ) { |
| 248 | + if( $this->matchHash( $password, $row->lu_id, $row->lu_password ) ) { |
| 249 | + wfDebugLog( 'CentralAuth', |
| 250 | + "Attaching '$this->mName' on $row->lu_dbname by password" ); |
| 251 | + $this->attach( $row->lu_dbname ); |
| 252 | + } |
| 253 | + } |
| 254 | + |
| 255 | + $dbw->commit(); |
| 256 | + } |
| 257 | + |
| 258 | + /** |
127 | 259 | * Check if the current username is defined and attached on this wiki yet |
128 | 260 | * @param $dbname Local database key to look up |
129 | 261 | * @return ("attached", "unattached", "no local user") |
— | — | @@ -187,7 +319,8 @@ |
188 | 320 | if( $rows > 0 ) { |
189 | 321 | return true; |
190 | 322 | } else { |
191 | | - wfDebug( __METHOD__ . " failed to attach \"{$this->mName}@$dbname\", not in localuser\n" ); |
| 323 | + wfDebugLog( 'CentralAuth', |
| 324 | + "failed to attach \"{$this->mName}@$dbname\", not in localuser\n" ); |
192 | 325 | return false; |
193 | 326 | } |
194 | 327 | } |
— | — | @@ -305,6 +438,21 @@ |
306 | 439 | * Quickie test implementation using local test database |
307 | 440 | */ |
308 | 441 | class CentralAuth extends AuthPlugin { |
| 442 | + static function factory() { |
| 443 | + global $wgCentralAuthState; |
| 444 | + switch( $wgCentralAuthState ) { |
| 445 | + case 'premigrate': |
| 446 | + case 'testing': |
| 447 | + // FIXME |
| 448 | + return new AuthPlugin(); |
| 449 | + case 'migration': |
| 450 | + case 'production': |
| 451 | + return new CentralAuth(); |
| 452 | + default: |
| 453 | + die('wtf'); |
| 454 | + } |
| 455 | + } |
| 456 | + |
309 | 457 | /** |
310 | 458 | * Check whether there exists a user account with the given name. |
311 | 459 | * The name will be normalized to MediaWiki's requirements, so |
Index: trunk/CentralAuth/migratePass0.php |
— | — | @@ -0,0 +1,63 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +// --> disable account creations, password changes |
| 5 | +// pass 0: |
| 6 | +// * generate 'localuser' entries for each user on each wiki |
| 7 | +// * generate 'globaluser' entries for each username |
| 8 | +// --> enable |
| 9 | + |
| 10 | +require_once 'commandLine.inc'; |
| 11 | + |
| 12 | + |
| 13 | +/** |
| 14 | + * Copy user data for this wiki into the localuser table |
| 15 | + */ |
| 16 | +function migratePassZero() { |
| 17 | + global $wgDBname; |
| 18 | + $dbBackground = wfGetDB( DB_SLAVE ); // fixme for large dbs |
| 19 | + $result = $dbBackground->select( |
| 20 | + 'user', |
| 21 | + array( |
| 22 | + 'user_id', |
| 23 | + 'user_name', |
| 24 | + 'user_password', |
| 25 | + 'user_newpassword', |
| 26 | + 'user_email', |
| 27 | + 'user_email_authenticated', |
| 28 | + ), |
| 29 | + '', |
| 30 | + __METHOD__ ); |
| 31 | + while( $row = $dbBackground->fetchObject( $result ) ) { |
| 32 | + $count = getEditCount( $row->user_id ); |
| 33 | + CentralAuthUser::storeLocalData( $wgDBname, $row, $count ); |
| 34 | + } |
| 35 | + $dbBackground->freeResult( $result ); |
| 36 | +} |
| 37 | + |
| 38 | +function getEditCount( $userId ) { |
| 39 | + return countEdits( $userId, 'revision', 'rev_user' ); |
| 40 | +} |
| 41 | + |
| 42 | +function countEdits( $userId, $table, $field ) { |
| 43 | + $dbr = wfGetDB( DB_SLAVE ); |
| 44 | + $count = $dbr->selectField( $table, 'COUNT(*)', |
| 45 | + array(), |
| 46 | + __METHOD__, |
| 47 | + array( 'GROUP BY' => $field ) ); |
| 48 | + return intval( $count ); |
| 49 | +} |
| 50 | + |
| 51 | +if( $wgCentralAuthState != 'premigrate' ) { |
| 52 | + if( $wgCentralAuthState == 'testing' ) { |
| 53 | + echo "WARNING: \$wgCentralAuthState is set to 'testing', generated data may be corrupt.\n"; |
| 54 | + } else { |
| 55 | + wfDie( "\$wgCentralAuthState is '$wgCentralAuthState', please set to 'premigrate' to prevent conflicts.\n" ); |
| 56 | + } |
| 57 | +} |
| 58 | + |
| 59 | +echo "CentralAuth migration pass 0:\n"; |
| 60 | +echo "$wgDBname preparing migration data...\n"; |
| 61 | +migratePassZero(); |
| 62 | +echo "done.\n"; |
| 63 | + |
| 64 | +?> |
\ No newline at end of file |
Property changes on: trunk/CentralAuth/migratePass0.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 65 | + native |
Index: trunk/CentralAuth/migratePass1.php |
— | — | @@ -0,0 +1,41 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +die( 'not done' ); |
| 5 | + |
| 6 | +// pass 1: go through all usernames in 'localuser' and create 'globaluser' rows |
| 7 | +// for those that can be automatically migrated, go ahead and do it. |
| 8 | + |
| 9 | +function migratePassZero() { |
| 10 | + $dbBackground = wfGetDB( DB_SLAVE ); // fixme for large dbs |
| 11 | + $result = $dbBackground->select( |
| 12 | + 'localuser', |
| 13 | + array( 'lu_name' ), |
| 14 | + '', |
| 15 | + __METHOD__, |
| 16 | + array( 'GROUP BY' => 'lu_name' ) ); |
| 17 | + while( $row = $dbBackground->fetchObject( $result ) ) { |
| 18 | + $name = $row->lu_name; |
| 19 | + $central = new CentralAuthUser( $name ); |
| 20 | + if( $central->attemptAutoMigration() ) { |
| 21 | + echo "Migrated '$name'\n"; |
| 22 | + } |
| 23 | + $count = getEditCount( $row->user_id ); |
| 24 | + CentralAuthUser::storeLocalData( $wgDBname, $row, $count ); |
| 25 | + } |
| 26 | + $dbBackground->freeResult( $result ); |
| 27 | +} |
| 28 | + |
| 29 | +if( $wgCentralAuthState != 'premigrate' ) { |
| 30 | + if( $wgCentralAuthState == 'testing' ) { |
| 31 | + echo "WARNING: \$wgCentralAuthState is set to 'testing', generated data may be corrupt.\n"; |
| 32 | + } else { |
| 33 | + wfDie( "\$wgCentralAuthState is '$wgCentralAuthState', please set to 'migrating'.\n" ); |
| 34 | + } |
| 35 | +} |
| 36 | + |
| 37 | +echo "CentralAuth migration pass 1:\n"; |
| 38 | +echo "Finding accounts which can be migrated without interaction...\n"; |
| 39 | +migratePassOne(); |
| 40 | +echo "done.\n"; |
| 41 | + |
| 42 | +?> |
\ No newline at end of file |
Property changes on: trunk/CentralAuth/migratePass1.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 43 | + native |