Difference between revisions of "Extension:SimpleSecurity"

From Organic Design wiki
(base on current version to start with)
(Start restructuring)
Line 12: Line 12:
 
# Global security settings
 
# Global security settings
 
$wgSecurityMagic            = "security";    # the parser-function name for security directives
 
$wgSecurityMagic            = "security";    # the parser-function name for security directives
$wgSecurityMagicNoi          = "!security";    # the name for non-inheriting security directives
+
$wgSecurityMagicInherit      = "!security";    # the name for security directives which apply to member articles
$wgSecurityMagicIf          = "ifusercan";    # the name for doing a permission-based conditional
+
$wgSecurityMagicIf          = "ifallowed";    # the name for doing a permission-based conditional
 
$wgSecurityMagicGroup        = "ifgroup";      # the name for doing a group-based conditional
 
$wgSecurityMagicGroup        = "ifgroup";      # the name for doing a group-based conditional
$wgSecurityEnableInheritance = false;          # specifies whether or not security directives in categories inherit to member articles
 
$wgSecurityEnableForImages  = false;          # specifies whether security directives in image/file articles also apply to the associated binary
 
 
$wgSecuritySysops            = array('sysop'); # the list of groups whose members bypass all security (groups a all lowercase, user are ucfirst)
 
$wgSecuritySysops            = array('sysop'); # the list of groups whose members bypass all security (groups a all lowercase, user are ucfirst)
 
$wgSecurityDenyTemplate      = 'Template:Action not permitted';
 
$wgSecurityDenyTemplate      = 'Template:Action not permitted';
 
$wgSecurityInfoTemplate      = 'Template:Security info';
 
$wgSecurityInfoTemplate      = 'Template:Security info';
 
$wgSecurityRuleTemplate      = '';            # set to a template for the  
 
$wgSecurityRuleTemplate      = '';            # set to a template for the  
$wgSecurityDenyImage        = "$IP/skins/common/images/mediawiki.png"; # the image returned in place of requested image if read access denied
+
$wgSecurityDenyImage        = dirname(__FILE__).'/deny.png'; # the image returned in place of requested image if read access denied
$wgSecurityParseInfo        = isset($wgSecurityParseInfo) ? $wgSecurityParseInfo : false;
+
$wgNamespacePermissions      = array();
$wgSecurityGroupsArticle    = '';             # Name of an article which contains a bullet list of available groups for Special:Userrights
 
 
$wgSecurityLogActions        = array('download');  # Actions that should be logged
 
$wgSecurityLogActions        = array('download');  # Actions that should be logged
  
Line 38: Line 35:
  
 
class SimpleSecurity {
 
class SimpleSecurity {
 
# Private internal data
 
var $initialised;
 
var $rules;
 
var $directives;
 
var $activeHooks;
 
var $title;
 
var $action;
 
var $allowed;
 
var $file;
 
var $path;
 
  
 
# Needed in some versions to prevent Special:Version from breaking
 
# Needed in some versions to prevent Special:Version from breaking
Line 54: Line 40:
  
 
# Constructor
 
# Constructor
function SimpleSecurity($inheritance = true) {
+
function SimpleSecurity() {
 
global $wgParser,$wgHooks,$wgLogTypes,$wgLogNames,$wgLogHeaders,$wgLogActions,
 
global $wgParser,$wgHooks,$wgLogTypes,$wgLogNames,$wgLogHeaders,$wgLogActions,
$wgSecurityMagic,$wgSecurityMagicNoi,$wgSecurityMagicIf,$wgSecurityMagicGroup,
+
$wgSecurityMagic,$wgSecurityMagicInherit,$wgSecurityMagicIf,$wgSecurityMagicGroup;
$wgSecurityEnableInheritance,$wgGroupPermissions,$wgSecurityGroupsArticle;
 
  
 
# Add all our required event hooks
 
# Add all our required event hooks
Line 65: Line 50:
  
 
$wgParser->setFunctionHook($wgSecurityMagic,array($this,'processDirective'));
 
$wgParser->setFunctionHook($wgSecurityMagic,array($this,'processDirective'));
if ($wgSecurityEnableInheritance) $wgParser->setFunctionHook($wgSecurityMagicNoi,array($this,'processDirectiveNoi'));
+
$wgParser->setFunctionHook($wgSecurityMagicInherit,array($this,'processInheritDirective',SFH_NO_HASH));
$wgParser->setFunctionHook($wgSecurityMagicIf,array($this,'ifUserCan'));
+
$wgParser->setFunctionHook($wgSecurityMagicIf,array($this,'ifAllowed'));
 
$wgParser->setFunctionHook($wgSecurityMagicGroup,array($this,'ifGroup'));
 
$wgParser->setFunctionHook($wgSecurityMagicGroup,array($this,'ifGroup'));
 
# Specify which of the hooks are currently active because these apply to all parser objects
 
$this->activeHooks = array(
 
'ArticleAfterFetchContent' => false,
 
'ParserBeforeStrip'        => false
 
);
 
 
# Initialise internal data
 
$this->initialised = false;
 
$this->allowed    = true;
 
$this->directives  = array();
 
$this->rules      = array();
 
$this->file        = '';
 
$this->path        = '';
 
 
# Add extra available groups if $wgSecurityGroupsArticle is set
 
if ($wgSecurityGroupsArticle) {
 
$groups = new Article(Title::newFromText($wgSecurityGroupsArticle));
 
if (preg_match_all('/^\\*\\s*(.+?)\\s*$/m',$groups->getContent(),$match))
 
foreach($match[1] as $group) $wgGroupPermissions[$group] = array();
 
}
 
  
 
# Add a new log type
 
# Add a new log type
Line 105: Line 69:
  
 
# Process a non-inheriting security directive
 
# Process a non-inheriting security directive
function processDirectiveNoi(&$parser,$actions = '',$groups = '') {
+
function processInheritDirective(&$parser,$actions = '',$groups = '') {
 
$parser->mOutput->mCacheTime = -1;
 
$parser->mOutput->mCacheTime = -1;
 
if ($actions && $groups) $this->directives[] = array($actions,$groups);
 
if ($actions && $groups) $this->directives[] = array($actions,$groups);
Line 111: Line 75:
 
}
 
}
  
# Process the ifUserCan conditional security directive
+
# Process the ifAllowed conditional security directive
function ifUserCan(&$parser,$action,$title,$if,$else = '') {
+
function ifAllowed(&$parser,$action,$title,$if,$else = '') {
 
return $this->validateTitle($action,Title::newFromText($title)) ? $if : $else;
 
return $this->validateTitle($action,Title::newFromText($title)) ? $if : $else;
 
}
 
}
Line 191: Line 155:
 
# Add IsAllowed hook to the $wgUser object
 
# Add IsAllowed hook to the $wgUser object
 
static $done = 0;   
 
static $done = 0;   
if ($done++ == 0) {
+
if ($done++ == 0) wfSimpleSecurityAddIsAllowedHook();
 
# Create a new User class ($User2) by extending the existing one with an overridden isAllowed method
 
$User  = get_class($wgUser);
 
$User2 = $User.'2';
 
eval("class $User2 extends $User".' {
 
function isAllowed($action = "") {
 
$result = NULL;
 
wfRunHooks("IsAllowed",array(&$this,$action,&$result));
 
return $result === NULL ? $result = parent::isAllowed($action) : $result;
 
}
 
}');
 
 
# Replace the $wgUser object with an identical $User2 instance
 
$oldUser = $wgUser;
 
$wgUser  = new $User2();
 
foreach(array_keys(get_class_vars($User)) as $k) $wgUser->$k = $oldUser->$k;
 
}
 
  
 
$this->title = $wgTitle->getPrefixedURL();
 
$this->title = $wgTitle->getPrefixedURL();
Line 342: Line 289:
 
function wfSetupSimpleSecurity() {
 
function wfSetupSimpleSecurity() {
 
global $wgSimpleSecurity,$wgLanguageCode,$wgMessageCache;
 
global $wgSimpleSecurity,$wgLanguageCode,$wgMessageCache;
 +
 +
wfSimpleSecurityAddDatabaseHooks();
  
 
$wgSimpleSecurity = new SimpleSecurity();
 
$wgSimpleSecurity = new SimpleSecurity();
Line 360: Line 309:
 
# Needed in MediaWiki >1.8.0 for magic word hooks to work properly
 
# Needed in MediaWiki >1.8.0 for magic word hooks to work properly
 
function wfSimpleSecurityLanguageGetMagic(&$magicWords,$langCode = 0) {
 
function wfSimpleSecurityLanguageGetMagic(&$magicWords,$langCode = 0) {
global $wgSecurityMagic,$wgSecurityMagicNoi,$wgSecurityMagicIf,$wgSecurityMagicGroup,$wgSecurityEnableInheritance;
+
global $wgSecurityMagic,$wgSecurityMagicInherit,$wgSecurityMagicIf,$wgSecurityMagicGroup;
 
$magicWords[$wgSecurityMagic] = array(0,$wgSecurityMagic);
 
$magicWords[$wgSecurityMagic] = array(0,$wgSecurityMagic);
if ($wgSecurityEnableInheritance) $magicWords[$wgSecurityMagicNoi] = array(0,$wgSecurityMagicNoi);
+
$magicWords[$wgSecurityMagicInherit] = array(0,$wgSecurityMagicInherit);
 
$magicWords[$wgSecurityMagicIf] = array(0,$wgSecurityMagicIf);
 
$magicWords[$wgSecurityMagicIf] = array(0,$wgSecurityMagicIf);
 
$magicWords[$wgSecurityMagicGroup] = array(0,$wgSecurityMagicGroup);
 
$magicWords[$wgSecurityMagicGroup] = array(0,$wgSecurityMagicGroup);
 
return true;
 
return true;
 +
}
 +
 +
# Add IsAllowed hook to the $wgUser object
 +
function wfSimpleSecurityAddIsAllowedHook() {
 +
global $wgUser;
 +
 +
# Create a new User class ($User2) by extending the existing one with an overridden isAllowed method
 +
$User  = get_class($wgUser);
 +
$User2 = $User.'2';
 +
eval("class $User2 extends $User".' {
 +
function isAllowed($action = "") {
 +
$result = NULL;
 +
wfRunHooks("IsAllowed",array(&$this,$action,&$result));
 +
return $result === NULL ? $result = parent::isAllowed($action) : $result;
 +
}
 +
}');
 +
 +
# Replace the $wgUser object with an identical $User2 instance
 +
$oldUser = $wgUser;
 +
$wgUser  = new $User2();
 +
foreach(array_keys(get_class_vars($User)) as $k) $wgUser->$k = $oldUser->$k;
 +
}
 +
 +
# Add DatabaseFetch and DatabaseQuery hooks
 +
function wfSimpleSecurityAddDatabaseHooks() {
 +
global $wgLoadBalancer,$wgDBtype;
 +
 +
wfGetDB(); # This ensures that $wgLoadBalancer is not a stub object when we subclass it
 +
 +
# Create a replica of the Database class which calls the hook in its fetch methods
 +
$type = ucfirst($wgDBtype);
 +
eval("class Database{$type}2 extends Database{$type}".' {
 +
 +
function fetchObject(&$res) {
 +
$row = parent::fetchObject($res);
 +
wfRunHooks("DatabaseFetchHook", array(&$this,&$row));
 +
return $row;
 +
}
 +
 +
}');
 +
 +
# Create a replica of the LoadBalancer class which uses the new Database class for its connection objects
 +
$LoadBalancer  = get_class($wgLoadBalancer);
 +
$LoadBalancer2 = $LoadBalancer."2";
 +
eval("class $LoadBalancer2 extends $LoadBalancer".' {
 +
function reallyOpenConnection(&$server) {
 +
$server["type"] .= "2";
 +
$db =& parent::reallyOpenConnection($server);
 +
return $db;
 +
}
 +
}');
 +
 +
# Replace the $wgLoadBalancer object with an identical instance of the new LoadBalancer2 class
 +
$wgLoadBalancer->closeAll(); # Close any open connections as they will be of the original Database class
 +
$oldLoadBalancer = $wgLoadBalancer;
 +
$wgLoadBalancer  = new $LoadBalancer2($oldLoadBalancer->mServers);
 +
foreach(array_keys(get_class_vars($LoadBalancer)) as $k) $wgLoadBalancer->$k = $oldLoadBalancer->$k;
 +
 
}
 
}
  
 
?>
 
?>

Revision as of 08:49, 12 October 2007

<?php

  1. Simple security extensionTemplate:Php
Info.svg These are the MediaWiki extensions we're using and/or developing. Please refer to the information on the mediawiki.org wiki for installation and usage details. Extensions here which have no corresponding mediawiki article are either not ready for use or have been superseded. You can also browse our extension code in our local Subversion repository or our GitHub mirror.
  1. - See http://www.mediawiki.org/Extension:Simple_Security for installation and usage details
  2. - Licenced under LGPL (http://www.gnu.org/copyleft/lesser.html)
  3. - Needs apache's mod-rewrite for security on images, see code comments below
  4. - Version 4.0.0 started 2007-10-11

if (!defined('MEDIAWIKI')) die('Not an entry point.');

define('SIMPLESECURITY_VERSION','4.0.0, 2007-10-11');

  1. Global security settings

$wgSecurityMagic = "security"; # the parser-function name for security directives $wgSecurityMagicInherit = "!security"; # the name for security directives which apply to member articles $wgSecurityMagicIf = "ifallowed"; # the name for doing a permission-based conditional $wgSecurityMagicGroup = "ifgroup"; # the name for doing a group-based conditional $wgSecuritySysops = array('sysop'); # the list of groups whose members bypass all security (groups a all lowercase, user are ucfirst) $wgSecurityDenyTemplate = 'Template:Action not permitted'; $wgSecurityInfoTemplate = 'Template:Security info'; $wgSecurityRuleTemplate = ; # set to a template for the $wgSecurityDenyImage = dirname(__FILE__).'/deny.png'; # the image returned in place of requested image if read access denied $wgNamespacePermissions = array(); $wgSecurityLogActions = array('download'); # Actions that should be logged

$wgExtensionFunctions[] = 'wfSetupSimpleSecurity'; $wgHooks['LanguageGetMagic'][] = 'wfSimpleSecurityLanguageGetMagic';

$wgExtensionCredits['parserhook'][] = array( 'name' => "Simple Security", 'author' => 'User:Nad', 'description' => 'A simple to implement security extension', 'url' => 'http://www.mediawiki.org/wiki/Extension:Simple_Security', 'version' => SIMPLESECURITY_VERSION );

class SimpleSecurity {

# Needed in some versions to prevent Special:Version from breaking function __toString() { return 'SimpleSecurity'; }

# Constructor function SimpleSecurity() { global $wgParser,$wgHooks,$wgLogTypes,$wgLogNames,$wgLogHeaders,$wgLogActions, $wgSecurityMagic,$wgSecurityMagicInherit,$wgSecurityMagicIf,$wgSecurityMagicGroup;

# Add all our required event hooks $wgHooks['userCan'][] = $this; $wgHooks['ArticleAfterFetchContent'][] = $this; $wgHooks['OutputPageBeforeHTML'][] = $this;

$wgParser->setFunctionHook($wgSecurityMagic,array($this,'processDirective')); $wgParser->setFunctionHook($wgSecurityMagicInherit,array($this,'processInheritDirective',SFH_NO_HASH)); $wgParser->setFunctionHook($wgSecurityMagicIf,array($this,'ifAllowed')); $wgParser->setFunctionHook($wgSecurityMagicGroup,array($this,'ifGroup'));

# Add a new log type $wgLogTypes[] = 'security'; $wgLogNames ['security'] = 'securitylogpage'; $wgLogHeaders['security'] = 'securitylogpagetext'; $wgLogActions['security/deny'] = 'securitylogentry'; }

# Process a normal security directive function processDirective(&$parser,$actions = ,$groups = ) { $parser->mOutput->mCacheTime = -1; if ($actions && $groups) $this->directives[] = array($actions,$groups); return ; }

# Process a non-inheriting security directive function processInheritDirective(&$parser,$actions = ,$groups = ) { $parser->mOutput->mCacheTime = -1; if ($actions && $groups) $this->directives[] = array($actions,$groups); return ; }

# Process the ifAllowed conditional security directive function ifAllowed(&$parser,$action,$title,$if,$else = ) { return $this->validateTitle($action,Title::newFromText($title)) ? $if : $else; }

# Process the ifGroup conditional security directive # - evaluates to true if current uset belongs to any of the groups in $groups (csv) function ifGroup(&$parser,$groups,$if,$else = ) { global $wgUser; $intersection = array_intersect( array_map('strtolower',split(',',$groups)), array_map('strtolower',$wgUser->getEffectiveGroups()) ); return count($intersection) > 0 ? $if : $else; }

# ArticleAfterFetchContent hook: Asseses security after raw content fetched from database and clears if not readable # - also fills the global $securityCache cache with info to append to the rendered article function onArticleAfterFetchContent(&$article,&$text) { global $wgSecurityDenyTemplate; $title = $article->mTitle->getText(); if (!$this->activeHooks['ArticleAfterFetchContent']) return true; if (!$this->validateTitle('view',$article->mTitle)) $text = '{'.'{'."$wgSecurityDenyTemplate|fetch|$title}}"; return true; }

# Render any security info function onOutputPageBeforeHTML(&$out,&$text) { global $wgUser,$wgTitle,$wgSecurityInfoTemplate,$wgSecurityDenyTemplate, $wgSiteNotice,$wgSecurityParseInfo,$wgSecurityRuleTemplate,$wgSecurityLogActions; static $done = 0; if ($done++) return true; $psr = new Parser; $psropt = ParserOptions::newFromUser($wgUser);

# Add rules information for this article if any $rules = $this->rules[$this->title]; if (count($rules)) {

# Construct wikitext for info $info = '{'.'{'."$wgSecurityInfoTemplate|1=\n"; foreach ($rules as $rule) { $a = $rule[0] == '*' ? 'Every action' : ucfirst($rule[0]); $b = $rule[1] == '*' ? 'anybody' : ($rule[1] == 'user' ? 'logged in' : $rule[1]); $c = isset($rule[2]) ? $rule[2] : ; if ($wgSecurityRuleTemplate) $info .= '{'.'{'."$wgSecurityRuleTemplate|$a|$b|$c}}\n"; else $info .= "*$a requires the user to be $b $c\n"; } $info .= "\n}}";

                       # Parse the wikitext (depending on $wgSecurityParseInfo) and add to SiteNotice
                       if ($wgSecurityParseInfo) {
                               $psrout = $psr->parse($info,$wgTitle,$psropt,true,true);
                               $info = $psrout->getText();
                               }

$wgSiteNotice .= $info; }

# Replace main body with deny-message and append log if action allowed if (!$this->allowed) { $text = ; $psrout = $psr->parse('{'.'{'."$wgSecurityDenyTemplate|{$this->action}|{$this->title}}}",$wgTitle,$psropt,true,true); $out->mBodytext = $psrout->getText(); if (in_array($this->action,$wgSecurityLogActions)) { $msg = wfMsgForContent('securitylogdeny',$this->action,$this->title); $log = new LogPage('security',false); $log->addEntry('deny',$wgUser->getUserPage(),$msg); } }

return true; }

# Main validation code for the whole request is done on the first call to userCan function onuserCan(&$title, &$user, $ucaction, &$result) { global $wgTitle,$action,$wgRequest,$wgUser,$wgUploadDirectory, $wgSecurityEnableForImages,$wgSecurityDenyImage,$wgSecurityLogActions;

# Add IsAllowed hook to the $wgUser object static $done = 0; if ($done++ == 0) wfSimpleSecurityAddIsAllowedHook();

$this->title = $wgTitle->getPrefixedURL(); $this->action = $action == 'submit' ? 'edit' : strtolower($action);

# Moves need to be handled differently if ($this->title == 'Special:Movepage' && $this->action == 'submit') { $this->action = 'move'; $this->title = $wgRequest->getText('wpOldTitle',$wgRequest->getVal('target')); }

# Handle security on files (needs apache mod-rewrite) # - /wiki/images/... rewritten to article title Download/image-path... if (ereg('^Download/(.+)/([^/]+)$',$this->title,$match)) { $this->path = $match[1]; $this->file = $image = $match[2]; if (ereg('^thumb/.+/([^/]+)$',$this->path,$match)) $image = $match[1]; $wgTitle = Title::newFromText($this->title = "Image:$image"); $action = 'raw'; }

if ($this->initialised) { # we can call $this->validate in here so mediawiki can render links according to permissions } elseif (!empty($wgUser->mDataLoaded)) {

# Activate the hooks now that enough information is present to assess security $this->initialised = true; $this->activeHooks['ArticleAfterFetchContent'] = true;

# Validate current global action and set to view if not allowed if (!$this->allowed = $this->validateTitle($this->action,$wgTitle)) { $action = 'view'; global $wgEnableParserCache,$wgOut; $wgEnableParserCache = false; $wgOut->enableClientCache(false); }

# If the requested item is a file, return it to the client if security validates otherwise return the SecurityDenyImage if ($wgSecurityEnableForImages && $this->file) { if (in_array('download',$wgSecurityLogActions)) { $msg = wfMsgForContent('securitylogdeny','download',$this->title); $log = new LogPage('security',false); $log->addEntry('deny',$wgUser->getUserPage(),$msg); } $pathname = $this->allowed ? "$wgUploadDirectory/".$this->path.'/'.$this->file : $wgSecurityDenyImage; while (@ob_end_clean()); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="'.$this->file.'"'); $content = implode(,file($pathname)); if (in_array('Content-Encoding: gzip',headers_list())) $content = gzencode($content); echo($content); die; } } return true; }

# Return whether or not the passed action is permitted on the passed title # - uses $this->rules[title] as a cache function validateTitle($action,$title) { global $extramsg,$wgUser,$wgSecurityMagic,$wgSecurityMagicNoi,$wgSecurityEnableInheritance; $key = $title->getPrefixedURL(); if (isset($this->rules[$key])) $rules = $this->rules[$key]; else { # Disable the AfterDatabaseFetch hook to avoid an infinite loop and get the article text $this->activeHooks['ArticleAfterFetchContent'] = false; $this->activeHooks['ParserBeforeStrip'] = false; $article = new Article($title); $text = $article->getContent();

# Set up a new local parser object to process security directives independently of main rendering process $psr = new Parser; $psr->setFunctionHook($wgSecurityMagic,array($this,'processDirective')); if ($wgSecurityEnableInheritance) $psr->setFunctionHook($wgSecurityMagicNoi,array($this,'processDirectiveNoi')); $opt = ParserOptions::newFromUser($wgUser); $this->directives = array(); $out = $psr->parse($text,$title,$opt,false,true); $rules = $this->directives;

# Get the security items from the cats by running the parser over the content of each # - stop checking MagicNoi directives because they shouldn't inherit if ($wgSecurityEnableInheritance) { unset($psr->mFunctionHooks[$wgSecurityMagicNoi]); foreach ($out->getCategoryLinks() as $cat) { $ca = new Article($ct = Title::newFromText($cat = "Category:$cat")); $this->directives = array(); $psr->parse($ca->getContent(),$ct,$opt,false,true); foreach ($this->directives as $i) $rules[] = array($i[0],$i[1],"this rule is inherited from $cat"); } }

# Re-enable AfterFetch hook and cache the security info $this->activeHooks['ArticleAfterFetchContent'] = true; $this->activeHooks['ParserBeforeStrip'] = true; $this->rules[$key] = $rules; }

# Return the result of validating the extracted rules return $this->validateRules($action,$rules); }

# Return whether or not a user is allowed to perform an action according to an array of security items function validateRules($action,&$rules) { global $wgUser,$wgSecuritySysops; if (!is_array($rules)) return true;

# Resolve permission for this action from the extracted security links $security = ; foreach ($rules as $i) { #if ($i[1] == ) $i[1] = join(',',$wgSecuritySysops); $actions = preg_split("/\\s*,\\s*/",strtolower($i[0])); if (in_array($action,$actions) or (in_array('*',$actions) and $security == )) $security = $i[1]; }

# Get users group lists (add own username to groups) $groups = array_map('strtolower',$wgUser->getEffectiveGroups()); $groups[] = ucfirst($wgUser->mName); $security = $security ? preg_split("/\\s*,\\s*/",$security) : array();

# Calculate whether or not the action can be performed and return the result return ( count($security) == 0 or in_array('*',$security) or count(array_intersect($groups,$wgSecuritySysops)) > 0 or count(array_intersect($groups,$security)) > 0 ); } }


  1. Called from $wgExtensionFunctions array when initialising extensions

function wfSetupSimpleSecurity() { global $wgSimpleSecurity,$wgLanguageCode,$wgMessageCache;

wfSimpleSecurityAddDatabaseHooks();

$wgSimpleSecurity = new SimpleSecurity();

# Add the messages used by the specialpage if ($wgLanguageCode == 'en') { $wgMessageCache->addMessages(array( 'security' => "Security log", 'securitylogpage' => "Security log", 'securitylogpagetext' => "This is a log of actions blocked by the SimpleSecurity extension.", 'securitylogdeny' => "Attempt to $1 $2 was denied.", 'securitylogentry' => "" )); }

}

  1. Needed in MediaWiki >1.8.0 for magic word hooks to work properly

function wfSimpleSecurityLanguageGetMagic(&$magicWords,$langCode = 0) { global $wgSecurityMagic,$wgSecurityMagicInherit,$wgSecurityMagicIf,$wgSecurityMagicGroup; $magicWords[$wgSecurityMagic] = array(0,$wgSecurityMagic); $magicWords[$wgSecurityMagicInherit] = array(0,$wgSecurityMagicInherit); $magicWords[$wgSecurityMagicIf] = array(0,$wgSecurityMagicIf); $magicWords[$wgSecurityMagicGroup] = array(0,$wgSecurityMagicGroup); return true; }

  1. Add IsAllowed hook to the $wgUser object

function wfSimpleSecurityAddIsAllowedHook() { global $wgUser;

# Create a new User class ($User2) by extending the existing one with an overridden isAllowed method $User = get_class($wgUser); $User2 = $User.'2'; eval("class $User2 extends $User".' { function isAllowed($action = "") { $result = NULL; wfRunHooks("IsAllowed",array(&$this,$action,&$result)); return $result === NULL ? $result = parent::isAllowed($action) : $result; } }');

# Replace the $wgUser object with an identical $User2 instance $oldUser = $wgUser; $wgUser = new $User2(); foreach(array_keys(get_class_vars($User)) as $k) $wgUser->$k = $oldUser->$k; }

  1. Add DatabaseFetch and DatabaseQuery hooks

function wfSimpleSecurityAddDatabaseHooks() { global $wgLoadBalancer,$wgDBtype;

wfGetDB(); # This ensures that $wgLoadBalancer is not a stub object when we subclass it

# Create a replica of the Database class which calls the hook in its fetch methods $type = ucfirst($wgDBtype); eval("class Database{$type}2 extends Database{$type}".' {

function fetchObject(&$res) { $row = parent::fetchObject($res); wfRunHooks("DatabaseFetchHook", array(&$this,&$row)); return $row; }

}');

# Create a replica of the LoadBalancer class which uses the new Database class for its connection objects $LoadBalancer = get_class($wgLoadBalancer); $LoadBalancer2 = $LoadBalancer."2"; eval("class $LoadBalancer2 extends $LoadBalancer".' { function reallyOpenConnection(&$server) { $server["type"] .= "2"; $db =& parent::reallyOpenConnection($server); return $db; } }');

# Replace the $wgLoadBalancer object with an identical instance of the new LoadBalancer2 class $wgLoadBalancer->closeAll(); # Close any open connections as they will be of the original Database class $oldLoadBalancer = $wgLoadBalancer; $wgLoadBalancer = new $LoadBalancer2($oldLoadBalancer->mServers); foreach(array_keys(get_class_vars($LoadBalancer)) as $k) $wgLoadBalancer->$k = $oldLoadBalancer->$k;

}

?>