Index: trunk/extensions/SimpleSecurity/SimpleSecurity_body.php |
— | — | @@ -14,6 +14,13 @@ |
15 | 15 | |
16 | 16 | |
17 | 17 | function __construct() { |
| 18 | + global $wgExtensionFunctions; |
| 19 | + |
| 20 | + # Put SimpleSecurity's setup function before all others |
| 21 | + array_unshift( $wgExtensionFunctions, array( $this, 'setup' ) ); |
| 22 | + } |
| 23 | + |
| 24 | + function setup() { |
18 | 25 | global $wgParser, $wgHooks, $wgLogTypes, $wgLogNames, $wgLogHeaders, $wgLogActions, |
19 | 26 | $wgSecurityMagicIf, $wgSecurityMagicGroup, $wgSecurityExtraActions, $wgSecurityExtraGroups, |
20 | 27 | $wgRestrictionTypes, $wgRestrictionLevels, $wgGroupPermissions, |
— | — | @@ -37,9 +44,7 @@ |
38 | 45 | $wgLogHeaders['security'] = 'securitylogpagetext'; |
39 | 46 | $wgLogActions['security/deny'] = 'securitylogentry'; |
40 | 47 | |
41 | | - # Load messages |
42 | | - |
43 | | - |
| 48 | + # Each extra action is also a restriction type |
44 | 49 | foreach ( $wgSecurityExtraActions as $k => $v ) { |
45 | 50 | $wgRestrictionTypes[] = $k; |
46 | 51 | } |
— | — | @@ -68,6 +73,9 @@ |
69 | 74 | $wgGroupPermissions[$k][$k] = true; # members of $k must be allowed to perform $k |
70 | 75 | $wgGroupPermissions['sysop'][$k] = true; # sysops must be allowed to perform $k as well |
71 | 76 | } |
| 77 | + |
| 78 | + $db = &wfGetDB( DB_SLAVE ); |
| 79 | + |
72 | 80 | } |
73 | 81 | |
74 | 82 | |
— | — | @@ -372,34 +380,103 @@ |
373 | 381 | } |
374 | 382 | } |
375 | 383 | |
376 | | - |
377 | 384 | /** |
378 | | - * Updates passed LoadBalancer's DB servers to secure class |
| 385 | + * Add hooks into the database classes query() and fetchObject() methods |
379 | 386 | */ |
380 | | - static function updateLB( &$lb ) { |
381 | | - $lb->closeAll(); |
382 | | - $serverCount = $lb->getServerCount(); |
383 | | - for ( $i = 0; $i < $serverCount; $i++ ) { |
384 | | - $server = $lb->getServerInfo( $i ); |
385 | | - $sever['type'] = 'SimpleSecurity'; |
386 | | - $lb->setServerInfo ( $i, $server ); |
| 387 | + static function applyDatabaseHook() { |
| 388 | + global $wgDBtype, $wgLBFactoryConf; |
| 389 | + |
| 390 | + # Create a new "Database_SimpleSecurity" database class with hooks into its query() and fetchObject() methods |
| 391 | + # - hooks are added in a sub-class of the database type specified in $wgDBtype |
| 392 | + # - query method is overriden to ensure that old_id field is returned for all queries which read old_text field |
| 393 | + # - only SELECT statements are ever patched |
| 394 | + # - fetchObject method is overridden to validate row content based on old_id |
| 395 | + $oldClass = ucfirst( $wgDBtype ); |
| 396 | + eval( "class Database_SimpleSecurity extends Database{$oldClass}" . ' { |
| 397 | + public function query( $sql, $fname = "", $tempIgnore = false ) { |
| 398 | + $patched = preg_replace_callback( "/(?<=SELECT ).+?(?= FROM)/", array("SimpleSecurity", "patchSQL"), $sql, 1 ); |
| 399 | + return parent::query( $patched, $fname, $tempIgnore ); |
| 400 | + } |
| 401 | + function fetchObject( $res ) { |
| 402 | + global $wgSimpleSecurity; |
| 403 | + $row = parent::fetchObject( $res ); |
| 404 | + if( isset( $row->old_text ) ) $wgSimpleSecurity->validateRow( $row ); |
| 405 | + return $row; |
| 406 | + } |
| 407 | + }' ); |
| 408 | + |
| 409 | + # Make sure our new LBFactory is used which in turn uses our LoadBalancer and Database classes |
| 410 | + $wgLBFactoryConf = array( 'class' => 'LBFactory_SimpleSecurity' ); |
| 411 | + |
| 412 | + } |
| 413 | + |
| 414 | +} |
| 415 | + |
| 416 | +/** |
| 417 | + * Create a new class based on LoadBalancer which opens connections to our new hooked database class |
| 418 | + */ |
| 419 | +class LoadBalancer_SimpleSecurity extends LoadBalancer { |
| 420 | + |
| 421 | + function reallyOpenConnection( $server, $dbNameOverride = false ) { |
| 422 | + if( !is_array( $server ) ) { |
| 423 | + throw new MWException( 'You must update your load-balancing configuration. See DefaultSettings.php entry for $wgDBservers.' ); |
387 | 424 | } |
| 425 | + $host = $server['host']; |
| 426 | + $dbname = $server['dbname']; |
| 427 | + if ( $dbNameOverride !== false ) { |
| 428 | + $server['dbname'] = $dbname = $dbNameOverride; |
| 429 | + } |
| 430 | + wfDebug( "Connecting to $host $dbname...\n" ); |
| 431 | + $db = new Database_SimpleSecurity( |
| 432 | + isset( $server['host'] ) ? $server['host'] : false, |
| 433 | + isset( $server['user'] ) ? $server['user'] : false, |
| 434 | + isset( $server['password'] ) ? $server['password'] : false, |
| 435 | + isset( $server['dbname'] ) ? $server['dbname'] : false, |
| 436 | + isset( $server['flags'] ) ? $server['flags'] : 0, |
| 437 | + isset( $server['tableprefix'] ) ? $server['tableprefix'] : 'get from global' |
| 438 | + ); |
| 439 | + if ( $db->isOpen() ) { |
| 440 | + wfDebug( "Connected to $host $dbname.\n" ); |
| 441 | + } else { |
| 442 | + wfDebug( "Connection failed to $host $dbname.\n" ); |
| 443 | + } |
| 444 | + $db->setLBInfo( $server ); |
| 445 | + if ( isset( $server['fakeSlaveLag'] ) ) { |
| 446 | + $db->setFakeSlaveLag( $server['fakeSlaveLag'] ); |
| 447 | + } |
| 448 | + if ( isset( $server['fakeMaster'] ) ) { |
| 449 | + $db->setFakeMaster( true ); |
| 450 | + } |
| 451 | + return $db; |
388 | 452 | } |
389 | 453 | |
| 454 | +} |
390 | 455 | |
391 | | - /** |
392 | | - * Hack to ensure proper search class is used |
393 | | - * - $wgDBtype determines search class unless already defined in $wgSearchType |
394 | | - * - just copied method from SearchEngine::create() |
395 | | - */ |
396 | | - static function fixSearchType() { |
397 | | - global $wgDBtype, $wgSearchType; |
398 | | - if ( $wgSearchType ) return; |
399 | | - elseif ( $wgDBtype == 'mysql' ) $wgSearchType = 'SearchMySQL'; |
400 | | - elseif ( $wgDBtype == 'postgres' ) $wgSearchType = 'SearchPostgres'; |
401 | | - elseif ( $wgDBtype == 'sqlite' ) $wgSearchType = 'SearchSqlite'; |
402 | | - elseif ( $wgDBtype == 'oracle' ) $wgSearchType = 'SearchOracle'; |
403 | | - elseif ( $wgDBtype == 'ibm_db2' ) $wgSearchType = 'SearchIBM_DB2'; |
404 | | - else $wgSearchType = 'SearchEngineDummy'; |
| 456 | +/** |
| 457 | + * Create a new LBFactory class based on LBFactory_Simple which uses our new LoadBalancer class |
| 458 | + */ |
| 459 | +class LBFactory_SimpleSecurity extends LBFactory_Simple { |
| 460 | + |
| 461 | + function newMainLB( $wiki = false ) { |
| 462 | + global $wgDBservers, $wgMasterWaitTimeout; |
| 463 | + if ( $wgDBservers ) { |
| 464 | + $servers = $wgDBservers; |
| 465 | + } else { |
| 466 | + global $wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, $wgDBtype, $wgDebugDumpSql; |
| 467 | + $servers = array(array( |
| 468 | + 'host' => $wgDBserver, |
| 469 | + 'user' => $wgDBuser, |
| 470 | + 'password' => $wgDBpassword, |
| 471 | + 'dbname' => $wgDBname, |
| 472 | + 'type' => $wgDBtype, |
| 473 | + 'load' => 1, |
| 474 | + 'flags' => ($wgDebugDumpSql ? DBO_DEBUG : 0) | DBO_DEFAULT |
| 475 | + )); |
| 476 | + } |
| 477 | + return new LoadBalancer_SimpleSecurity( array( |
| 478 | + 'servers' => $servers, |
| 479 | + 'masterWaitTimeout' => $wgMasterWaitTimeout |
| 480 | + )); |
405 | 481 | } |
| 482 | + |
406 | 483 | } |
Index: trunk/extensions/SimpleSecurity/SimpleSecurity.php |
— | — | @@ -1,29 +1,29 @@ |
2 | 2 | <?php |
| 3 | +if( !defined( 'MEDIAWIKI' ) ) die( 'Not an entry point.' ); |
| 4 | +if( version_compare( $wgVersion, '1.17.0' ) < 0 ) die( 'This version of SimpleSecurity is for MediaWiki 1.17 or greater, please install SimpleSecurity 4.x which can be found at http://svn.organicdesign.co.nz/listing.php?repname=extensions' ); |
3 | 5 | /** |
4 | 6 | * SimpleSecurity extension |
5 | 7 | * - Extends the MediaWiki article protection to allow restricting viewing of article content |
6 | 8 | * - Also adds #ifusercan and #ifgroup parser functions for rendering restriction-based content |
7 | 9 | * |
8 | 10 | * See http://www.mediawiki.org/Extension:SimpleSecurity for installation and usage details |
9 | | - * See http://www.organicdesign.co.nz/Extension_talk:SimpleSecurity.php for development notes and disucssion |
| 11 | + * See http://www.organicdesign.co.nz/Extension:SimpleSecurity.php for development notes and disucssion |
10 | 12 | * |
11 | | - * Version 4.0 - Oct 2007 - new version for modern MediaWiki's using DatabaseFetchHook |
| 13 | + * Version 4.0 - Oct 2007 - new version for MediaWiki 1.12+ using DatabaseFetchHook |
12 | 14 | * Version 4.1 - Jun 2008 - development funded for a slimmed down functional version |
13 | 15 | * Version 4.2 - Aug 2008 - fattened up a bit again - $wgPageRestrictions and security info added in again |
14 | 16 | * Version 4.3 - Mar 2009 - bug fixes and split out to separate class and i18n files |
15 | 17 | * Version 4.5 - Sep 2010 - File security started again - by Josh Adams |
| 18 | + * Version 5.0 - Jun 2011 - major changes to the DB hooking method to handle changes in MediaWiki 1.17 |
16 | 19 | * |
17 | 20 | * @file |
18 | 21 | * @ingroup Extensions |
19 | 22 | * @author Aran Dunkley [http://www.organicdesign.co.nz/nad User:Nad] |
20 | | - * @copyright © 2007 Aran Dunkley |
| 23 | + * @copyright © 2007-2011 Aran Dunkley |
21 | 24 | * @license GNU General Public Licence 2.0 or later |
22 | 25 | */ |
| 26 | +define( 'SIMPLESECURITY_VERSION', '5.0.0, 2011-06-24' ); |
23 | 27 | |
24 | | -if ( !defined( 'MEDIAWIKI' ) ) die( 'Not an entry point.' ); |
25 | | - |
26 | | -define( 'SIMPLESECURITY_VERSION', '4.5.1, 2010-10-16' ); |
27 | | - |
28 | 28 | # Load the SimpleSecurity class and messages |
29 | 29 | $dir = dirname( __FILE__ ) . '/'; |
30 | 30 | $wgExtensionMessagesFiles['SimpleSecurity'] = $dir . 'SimpleSecurity.i18n.php'; |
— | — | @@ -55,10 +55,9 @@ |
56 | 56 | # protection to apply to all instances of that record type |
57 | 57 | $wgSecurityProtectRecords = true; |
58 | 58 | |
| 59 | +# Don;t use the DB hook by default since it's voodoo |
| 60 | +if( !isset( $wgSecurityUseDBHook ) ) $wgSecurityUseDBHook = false; |
59 | 61 | |
60 | | -# Put SimpleSecurity's setup function before all others |
61 | | -array_unshift( $wgExtensionFunctions, 'wfSetupSimpleSecurity' ); |
62 | | - |
63 | 62 | $wgHooks['LanguageGetMagic'][] = 'wfSimpleSecurityLanguageGetMagic'; |
64 | 63 | $wgExtensionCredits['parserhook'][] = array( |
65 | 64 | 'path' => __FILE__, |
— | — | @@ -67,54 +66,16 @@ |
68 | 67 | 'url' => "http://www.mediawiki.org/wiki/Extension:SimpleSecurity", |
69 | 68 | 'version' => SIMPLESECURITY_VERSION, |
70 | 69 | 'descriptionmsg' => 'security-desc', |
71 | | - |
72 | 70 | ); |
73 | | - |
74 | 71 | $wgHooks['MessagesPreLoad'][] = 'wfSimpleSecurityMessagesPreLoad'; |
75 | 72 | |
76 | | -# SearchEngine is based on $wgDBtype so must be set before it gets changed to DatabaseSimpleSecurity |
77 | | -# - this may be paranoid now since $wgDBtype is changed back after LoadBalancer has initialised |
78 | | -SimpleSecurity::fixSearchType(); |
| 73 | +# Instantiate the SimpleSecurity singleton now that the environment is prepared |
| 74 | +$wgSimpleSecurity = new SimpleSecurity(); |
79 | 75 | |
80 | | -# If the database class already exists, add the DB hook now, otherwise wait until extension setup |
81 | | -if ( !isset( $wgSecurityUseDBHook ) ) $wgSecurityUseDBHook = false; |
82 | | -if ( $wgSecurityUseDBHook && class_exists( 'Database' ) ) wfSimpleSecurityDBHook(); |
| 76 | +# If using the DBHook, apply it now (must be done from the root scope since it creates classes) |
| 77 | +if( $wgSecurityUseDBHook ) SimpleSecurity::applyDatabaseHook(); |
83 | 78 | |
84 | 79 | /** |
85 | | - * Hook into Database::query and Database::fetchObject of database instances |
86 | | - * - this can't be executed from within a method because PHP doesn't like nested class definitions |
87 | | - * - it needs an eval because the class statement isn't allowed to contain strings |
88 | | - * - the hooks aren't called if $wgSimpleSecurity doesn't exist yet |
89 | | - * - hooks are added in a sub-class of the database type specified in $wgDBtype called DatabaseSimpleSecurity |
90 | | - * - $wgDBtype is changed so that new DB instances are based on the sub-class |
91 | | - * - query method is overriden to ensure that old_id field is returned for all queries which read old_text field |
92 | | - * - only SELECT statements are ever patched |
93 | | - * - fetchObject method is overridden to validate row content based on old_id |
94 | | - */ |
95 | | -function wfSimpleSecurityDBHook() { |
96 | | - global $wgDBtype, $wgSecurityUseDBHook, $wgOldDBtype; |
97 | | - $wgOldDBtype = $wgDBtype; |
98 | | - $oldClass = ucfirst( $wgDBtype ); |
99 | | - $wgDBtype = 'SimpleSecurity'; |
100 | | - eval( "class Database{$wgDBtype} extends Database{$oldClass}" . ' { |
101 | | - public function query($sql, $fname = "", $tempIgnore = false) { |
102 | | - global $wgSimpleSecurity; |
103 | | - $count = false; |
104 | | - if (is_object($wgSimpleSecurity)) |
105 | | - $patched = preg_replace_callback("/(?<=SELECT ).+?(?= FROM)/", array("SimpleSecurity", "patchSQL"), $sql, 1, $count); |
106 | | - return parent::query($count ? $patched : $sql, $fname, $tempIgnore); |
107 | | - } |
108 | | - function fetchObject($res) { |
109 | | - global $wgSimpleSecurity; |
110 | | - $row = parent::fetchObject($res); |
111 | | - if (is_object($wgSimpleSecurity) && isset($row->old_text)) $wgSimpleSecurity->validateRow($row); |
112 | | - return $row; |
113 | | - } |
114 | | - }' ); |
115 | | - $wgSecurityUseDBHook = false; |
116 | | -} |
117 | | - |
118 | | -/** |
119 | 80 | * Register magic words |
120 | 81 | */ |
121 | 82 | function wfSimpleSecurityLanguageGetMagic( &$magicWords, $langCode = 0 ) { |
— | — | @@ -127,34 +88,34 @@ |
128 | 89 | function wfSimpleSecurityMessagesPreLoad( $title, &$text ) { |
129 | 90 | global $wgSecurityExtraActions, $wgSecurityExtraGroups; |
130 | 91 | |
131 | | - if ( substr( $title, 0, 12 ) == 'Restriction-' ) { |
| 92 | + if( substr( $title, 0, 12 ) == 'Restriction-' ) { |
132 | 93 | $key = substr( $title, 12 ); |
133 | | - if ( isset( $wgSecurityExtraActions[$key] ) ) { |
| 94 | + if( isset( $wgSecurityExtraActions[$key] ) ) { |
134 | 95 | $text = empty( $wgSecurityExtraActions[$key] ) ? ucfirst( $key ) : $wgSecurityExtraActions[$key]; |
135 | 96 | } |
136 | 97 | return true; |
137 | | - } elseif ( substr( $title, 0, 14 ) == 'Protect-level-' ) { |
| 98 | + } elseif( substr( $title, 0, 14 ) == 'Protect-level-' ) { |
138 | 99 | $key = substr( $title, 14 ); |
139 | 100 | $type = 'level'; |
140 | | - } elseif ( substr( $title, 0, 6 ) == 'Right-' ) { |
| 101 | + } elseif( substr( $title, 0, 6 ) == 'Right-' ) { |
141 | 102 | $key = substr( $title, 6 ); |
142 | 103 | $type = 'right'; |
143 | 104 | } else { |
144 | 105 | return true; |
145 | 106 | } |
146 | 107 | |
147 | | - if ( isset( $wgSecurityExtraGroups[$key] ) ) { |
| 108 | + if( isset( $wgSecurityExtraGroups[$key] ) ) { |
148 | 109 | $name = empty( $wgSecurityExtraGroups[$key] ) ? ucfirst( $key ) : $wgSecurityExtraGroups[$key]; |
149 | 110 | } else { |
150 | 111 | $lower = array_map( 'strtolower', $wgSecurityExtraGroups ); |
151 | 112 | $nkey = array_search( strtolower( $key ), $lower, true ); |
152 | | - if ( !is_numeric( $nkey ) ) { |
| 113 | + if( !is_numeric( $nkey ) ) { |
153 | 114 | return true; |
154 | 115 | } |
155 | 116 | $name = ucfirst( $wgSecurityExtraGroups[$nkey] ); |
156 | 117 | } |
157 | 118 | |
158 | | - if ( $type == 'level' ) { |
| 119 | + if( $type == 'level' ) { |
159 | 120 | $text = $name; |
160 | 121 | } else { |
161 | 122 | $text = wfMsg( 'security-restricttogroup', $name ); |
— | — | @@ -163,28 +124,4 @@ |
164 | 125 | return true; |
165 | 126 | } |
166 | 127 | |
167 | | -/** |
168 | | - * Called from $wgExtensionFunctions array when initialising extensions |
169 | | - */ |
170 | | -function wfSetupSimpleSecurity() { |
171 | | - global $wgSimpleSecurity, $wgSecurityUseDBHook, $wgLoadBalancer, $wgDBtype, $wgOldDBtype; |
172 | 128 | |
173 | | - # Instantiate the SimpleSecurity singleton now that the environment is prepared |
174 | | - $wgSimpleSecurity = new SimpleSecurity(); |
175 | | - |
176 | | - # If the DB hook couldn't be set up early, do it now |
177 | | - # - but now the LoadBalancer exists and must have its DB types changed |
178 | | - if ( $wgSecurityUseDBHook ) { |
179 | | - wfSimpleSecurityDBHook(); |
180 | | - if ( function_exists( 'wfGetLBFactory' ) ) wfGetLBFactory()->forEachLB( array( 'SimpleSecurity', 'updateLB' ) ); |
181 | | - elseif ( is_object( $wgLoadBalancer ) ) SimpleSecurity::updateLB( $wgLoadBalancer ); |
182 | | - else die( "Can't hook in to Database class!" ); |
183 | | - } |
184 | | - |
185 | | - # Request a DB connection to ensure the LoadBalancer is initialised, |
186 | | - # then change back to old DBtype since it won't be used for making connections again but can affect other operations |
187 | | - # such as $wgContLang->stripForSearch which is called by SearchMySQL::parseQuery |
188 | | - wfGetDB( DB_MASTER ); |
189 | | - $wgDBtype = $wgOldDBtype; |
190 | | -} |
191 | | - |