Extension:DatabaseFetchHook

This hook was created for the Simple Security extension so that it could overcome all the flaws listed in security issues with authorization extensions in a generic way. To address these flaws, a hook is needed which intercepts the requests for each row of a query result, but no such hook exists as of MediaWiki version 1.9. However, it is possible for such a hook to be added by directly manipulating the classes at runtime thereby not requiring any patching of include files.

Usage
The hook is used like normal, in this case by adding a function to $wgHooks[DatabaseFetchObject]. The function has two parameters, one is the database object, and the other an object containing the columns of the requested row in its properties. The object is passed by reference so that the properties can be adjusted to remove secure information for example.

How it works
All database interactions are done on an object of a DatabaseXXX class (the actual name of the class depends on the kind of database, which the hook code can determine at runtime) and a reference to that object is returned by the wfGetDB global function which in turn asks the global LoadBalancer for a connection. The LoadBalancer is a global variable called $wgLoadBalancer which never changes after it's created, which means that we can exchange it for a new one based on an extended LoadBlancer class which has its getConnection method extended, so that the returned connection has a new hook added to one of its methods.

The DatabaseXXX class also has a new subclass which has its fetchObject method extended the same way the new LoadBalancer class extended its getConnection method. The extended fetchObject method has a hook added to allow checking if any text content with security directives is being requested. It would just use a preg_match_callback rather than initiating a parser since this code is executing at too lower level to risk calling parser methods.

Installation
The code can be added to the index.php file just after includes/Setup.php is included. The code is included below, but you may find a more recent version at DatabaseFetchObject hook.php. $LoadBalancer = get_class($wgLoadBalancer); $secureLoadBalancer = "secure$LoadBalancer"; eval('class '.$secureLoadBalancer.' extends '.$LoadBalancer.' {
 * 1) Create a new secureLoadBalancer class by extending the existing one

# Override the getConnection method to return a secureDatabase connection function &getConnection($i, $fail = true, $groups = array) { $db =& parent::getConnection($i,$fail,$groups);

# Create a secure subclass of the database class with extended fetchObject method $dbClass = get_class($db); $dbSecure = "secure$dbClass"; if (!class_exists($dbSecure)) { eval(\'class \'.$dbSecure.\' extends \'.$dbClass.\' {				function fetchObject($res) {					$row = parent::fetchObject($res);					wfRunHooks("DatabaseFetchObject", array(&$this,&$row));					return $row;					}				}\'); }

# Replace the DatabaseXXX connection with an identical secureDatabaseXXX connection $sdb = new $dbSecure; foreach(array_keys(get_class_vars($dbClass)) as $k) $sdb->$k = $db->$k; unset($db);

return $sdb; }

}');

$oldLoadBalancer = $wgLoadBalancer; $wgLoadBalancer = new $secureLoadBalancer; foreach(array_keys(get_class_vars($LoadBalancer)) as $k) $wgLoadBalancer->$k = $oldLoadBalancer->$k;
 * 1) Replace the global LoadBalancer object with an identical secureLoadBalancer