Index: trunk/extensions/CentralAuth/sample-data.sql |
— | — | @@ -1,10 +0,0 @@ |
2 | | -insert |
3 | | -into globaluser |
4 | | - (gu_id,gu_name,gu_email,gu_email_authenticated, |
5 | | - gu_salt,gu_password,gu_locked,gu_hidden, |
6 | | - gu_registration) |
7 | | -values |
8 | | - (1, 'Duderino', 'dude@localhost', '20060719012345', |
9 | | - '34', MD5(CONCAT('34', '-', MD5('mycoolpass'))), 0, 0, |
10 | | - '20060719012345'); |
11 | | - |
Index: trunk/extensions/CentralAuth/CentralAuthUser.php |
— | — | @@ -98,7 +98,7 @@ |
99 | 99 | $localuser = self::tableName( 'localuser' ); |
100 | 100 | |
101 | 101 | $sql = |
102 | | - "SELECT gu_id, lu_attached |
| 102 | + "SELECT gu_id, lu_dbname |
103 | 103 | FROM $globaluser |
104 | 104 | LEFT OUTER JOIN $localuser |
105 | 105 | ON gu_name=lu_name |
— | — | @@ -109,8 +109,8 @@ |
110 | 110 | $dbr->freeResult( $result ); |
111 | 111 | |
112 | 112 | if( $row ) { |
113 | | - $this->mGlobalId = $row->gu_id; |
114 | | - $this->mIsAttached = (bool)$row->lu_attached; |
| 113 | + $this->mGlobalId = intval( $row->gu_id ); |
| 114 | + $this->mIsAttached = ($row->lu_dbname !== null); |
115 | 115 | } else { |
116 | 116 | $this->mGlobalId = 0; |
117 | 117 | $this->mIsAttached = false; |
— | — | @@ -596,8 +596,7 @@ |
597 | 597 | $valid = $this->validateList( $list ); |
598 | 598 | |
599 | 599 | $dbw = self::getCentralDB(); |
600 | | - $dbw->update( self::tableName( 'localuser' ), |
601 | | - array( 'lu_attached' => 0 ), |
| 600 | + $dbw->delete( self::tableName( 'localuser' ), |
602 | 601 | array( |
603 | 602 | 'lu_name' => $this->mName, |
604 | 603 | 'lu_dbname' => $valid ), |
— | — | @@ -627,39 +626,27 @@ |
628 | 627 | */ |
629 | 628 | public function attach( $dbname, $method='new' ) { |
630 | 629 | $dbw = self::getCentralDB(); |
631 | | - $dbw->update( self::tableName( 'localuser' ), |
| 630 | + $dbw->insert( self::tableName( 'localuser' ), |
632 | 631 | array( |
633 | | - 'lu_attached' => 1, |
| 632 | + 'lu_dbname' => $dbname, |
| 633 | + 'lu_name' => $this->mName , |
634 | 634 | 'lu_attached_timestamp' => $dbw->timestamp(), |
635 | 635 | 'lu_attached_method' => $method ), |
636 | | - array( |
637 | | - 'lu_dbname' => $dbname, |
638 | | - 'lu_name' => $this->mName ), |
639 | | - __METHOD__ ); |
| 636 | + __METHOD__, |
| 637 | + array( 'IGNORE' ) ); |
| 638 | + |
640 | 639 | if( $dbw->affectedRows() == 0 ) { |
641 | | - // *frowny face* |
642 | | - |
643 | | - $dbw->insert( self::tableName( 'localuser' ), |
644 | | - array( |
645 | | - 'lu_dbname' => $dbname, |
646 | | - 'lu_name' => $this->mName , |
647 | | - 'lu_attached' => 1, |
648 | | - 'lu_attached_timestamp' => $dbw->timestamp(), |
649 | | - 'lu_attached_method' => $method ), |
650 | | - __METHOD__, |
651 | | - array( 'IGNORE' ) ); |
652 | | - |
653 | | - if( $dbw->affectedRows() == 0 ) { |
654 | | - throw MWException( "Bogus attach" ); |
| 640 | + wfDebugLog( 'CentralAuth', |
| 641 | + "Race condition? Already attached $this->mName@$dbname, just tried by '$method'" ); |
| 642 | + } else { |
| 643 | + wfDebugLog( 'CentralAuth', |
| 644 | + "Attaching local user $this->mName@$dbname by '$method'" ); |
| 645 | + |
| 646 | + global $wgDBname; |
| 647 | + if( $dbname == $wgDBname ) { |
| 648 | + $this->resetState(); |
655 | 649 | } |
656 | 650 | } |
657 | | - wfDebugLog( 'CentralAuth', |
658 | | - "Attaching local user $this->mName@$dbname by '$method'" ); |
659 | | - |
660 | | - global $wgDBname; |
661 | | - if( $dbname == $wgDBname ) { |
662 | | - $this->resetState(); |
663 | | - } |
664 | 651 | } |
665 | 652 | |
666 | 653 | /** |
— | — | @@ -746,28 +733,48 @@ |
747 | 734 | * @return array of database name strings |
748 | 735 | */ |
749 | 736 | public function listUnattached() { |
750 | | - $unattached = $this->listLocalDatabases( 0 ); |
751 | | - if( !$unattached ) { |
752 | | - // Nobody? Might not have imported local lists yet... |
| 737 | + $unattached = $this->doListUnattached(); |
| 738 | + if( empty( $unattached ) ) { |
753 | 739 | if( $this->lazyImportLocalNames() ) { |
754 | | - $unattached = $this->listLocalDatabases( 0 ); |
| 740 | + $unattached = $this->doListUnattached(); |
755 | 741 | } |
756 | 742 | } |
757 | 743 | return $unattached; |
758 | 744 | } |
759 | 745 | |
| 746 | + function doListUnattached() { |
| 747 | + $dbw = self::getCentralDB(); |
| 748 | + |
| 749 | + $sql = " |
| 750 | + SELECT ln_dbname |
| 751 | + FROM localnames |
| 752 | + LEFT OUTER JOIN localuser |
| 753 | + ON ln_dbname=lu_dbname AND ln_name=lu_name |
| 754 | + WHERE ln_name=? AND lu_name IS NULL |
| 755 | + "; |
| 756 | + $result = $dbw->safeQuery( $sql, $this->mName ); |
| 757 | + |
| 758 | + $dbs = array(); |
| 759 | + while( $row = $dbw->fetchObject( $result ) ) { |
| 760 | + $dbs[] = $row->ln_dbname; |
| 761 | + } |
| 762 | + $dbw->freeResult( $result ); |
| 763 | + |
| 764 | + return $dbs; |
| 765 | + } |
| 766 | + |
760 | 767 | function lazyImportLocalNames() { |
761 | 768 | $dbw = self::getCentralDB(); |
762 | 769 | |
763 | | - $result = $dbw->select( self::tableName( 'localuser' ), |
764 | | - array( 'lu_dbname' ), |
765 | | - array( 'lu_name' => $this->mName ), |
| 770 | + $result = $dbw->select( self::tableName( 'globalnames' ), |
| 771 | + array( '1' ), |
| 772 | + array( 'gn_name' => $this->mName ), |
766 | 773 | __METHOD__, |
767 | 774 | array( 'LIMIT' => 1 ) ); |
768 | | - $any = $result->numRows(); |
| 775 | + $known = $result->numRows(); |
769 | 776 | $result->free(); |
770 | 777 | |
771 | | - if( $any ) { |
| 778 | + if( $known ) { |
772 | 779 | // No need... |
773 | 780 | return false; |
774 | 781 | } |
— | — | @@ -775,6 +782,10 @@ |
776 | 783 | return $this->importLocalNames(); |
777 | 784 | } |
778 | 785 | |
| 786 | + /** |
| 787 | + * Troll through the full set of local databases and list those |
| 788 | + * which exist into the 'localnames' table. |
| 789 | + */ |
779 | 790 | function importLocalNames() { |
780 | 791 | global $wgLocalDatabases; |
781 | 792 | |
— | — | @@ -788,15 +799,14 @@ |
789 | 800 | __METHOD__ ); |
790 | 801 | if( $id ) { |
791 | 802 | $rows[] = array( |
792 | | - 'lu_dbname' => $db, |
793 | | - 'lu_name' => $this->mName, |
794 | | - 'lu_attached' => 0 ); |
| 803 | + 'ln_dbname' => $db, |
| 804 | + 'ln_name' => $this->mName ); |
795 | 805 | } |
796 | 806 | } |
797 | 807 | |
798 | 808 | if( $rows ) { |
799 | 809 | $dbw = self::getCentralDB(); |
800 | | - $dbw->insert( self::tableName( 'localuser' ), |
| 810 | + $dbw->insert( self::tableName( 'localnames' ), |
801 | 811 | $rows, |
802 | 812 | __METHOD__, |
803 | 813 | array( 'IGNORE' ) ); |
— | — | @@ -814,9 +824,25 @@ |
815 | 825 | * @return array database name strings |
816 | 826 | */ |
817 | 827 | public function listAttached() { |
818 | | - return $this->listLocalDatabases( 1 ); |
| 828 | + $dbw = self::getCentralDB(); |
| 829 | + |
| 830 | + $result = $dbw->select( self::tableName( 'localuser' ), |
| 831 | + array( 'lu_dbname' ), |
| 832 | + array( 'lu_name' => $this->mName ), |
| 833 | + __METHOD__ ); |
| 834 | + |
| 835 | + $dbs = array(); |
| 836 | + while( $row = $result->fetchObject() ) { |
| 837 | + $dbs[] = $row->lu_dbname; |
| 838 | + } |
| 839 | + $dbw->freeResult( $result ); |
| 840 | + |
| 841 | + return $dbs; |
819 | 842 | } |
820 | 843 | |
| 844 | + /** |
| 845 | + * Flooobie! |
| 846 | + */ |
821 | 847 | private function listLocalDatabases( $attached ) { |
822 | 848 | $dbw = self::getCentralDB(); |
823 | 849 | |
— | — | @@ -852,8 +878,7 @@ |
853 | 879 | 'lu_attached_timestamp', |
854 | 880 | 'lu_attached_method' ), |
855 | 881 | array( |
856 | | - 'lu_name' => $this->mName, |
857 | | - 'lu_attached' => 1 ), |
| 882 | + 'lu_name' => $this->mName ), |
858 | 883 | __METHOD__ ); |
859 | 884 | |
860 | 885 | $dbs = array(); |
Index: trunk/extensions/CentralAuth/central-auth.sql |
— | — | @@ -4,9 +4,38 @@ |
5 | 5 | -- USE centralauth; |
6 | 6 | -- GRANT all on centralauth.* to 'wikiuser'@'localhost'; |
7 | 7 | -- source central-auth.sql |
8 | 8 | |
9 | 9 | -- |
| 10 | +-- This table simply lists all known usernames in the system. |
| 11 | +-- If no record is present here when migration processing begins, |
| 12 | +-- we know we have to sweep all the local databases and populate |
| 13 | +-- the localnames table. |
| 14 | +-- |
| 15 | +CREATE TABLE globalnames ( |
| 16 | + gn_name varchar(255) binary not null, |
| 17 | + primary key (gn_name) |
| 18 | +) /*$wgDBTableOptions*/; |
| 19 | + |
| 20 | +-- |
| 21 | +-- For each known username in globalnames, the presence of an acount |
| 22 | +-- on each local database is listed here. |
| 23 | +-- |
| 24 | +-- Email and password information used for migration checks are grabbed |
| 25 | +-- from local databases on demand when needed. |
| 26 | +-- |
| 27 | +-- This is an optimization measure, so we don't have to poke on 600+ |
| 28 | +-- separate databases to look for unmigrated accounts every time we log in; |
| 29 | +-- only existing databases not yet migrated have to be loaded. |
| 30 | +-- |
| 31 | +CREATE TABLE localnames ( |
| 32 | + ln_dbname varchar(32) binary not null, |
| 33 | + ln_name varchar(255) binary not null, |
| 34 | + |
| 35 | + primary key (ln_dbname, ln_name), |
| 36 | + key (ln_name, ln_dbname) |
| 37 | +) /*$wgDBTableOptions*/; |
| 38 | + |
| 39 | +-- |
10 | 40 | -- Global account data. |
11 | 41 | -- |
12 | 42 | CREATE TABLE globaluser ( |
— | — | @@ -58,18 +87,14 @@ |
59 | 88 | ) /*$wgDBTableOptions*/; |
60 | 89 | |
61 | 90 | -- |
| 91 | +-- Local linkage info, listing which wikis the username is attached |
| 92 | +-- to the global account. |
62 | 93 | -- |
63 | 94 | -- All local DBs will be swept on an opt-in check event. |
64 | 95 | -- |
65 | 96 | CREATE TABLE localuser ( |
66 | 97 | lu_dbname varchar(32) binary not null, |
67 | 98 | lu_name varchar(255) binary not null, |
68 | | - lu_attached bool not null default 0, |
69 | 99 | |
70 | 100 | -- Migration status/logging information, to help diagnose issues |
71 | 101 | lu_attached_timestamp varchar(14) binary, |