Difference between revisions of "Extension:SimpleSecurity2.1.php"
From Organic Design wiki
m |
m |
||
(13 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
− | <? | + | {{legacy}} |
− | # | + | <source lang="php"><?php |
+ | # THIS SCRIPT IS OBSOLETE! | ||
+ | # for the newer Version 3.1 (2007-03-14) see: | ||
+ | # http://www.organicdesign.co.nz/SimpleSecurity.php | ||
+ | # | ||
+ | # and also the whole help and needed reference at: | ||
+ | # http://www.mediawiki.org/wiki/Extension:Simple_Security | ||
+ | # | ||
+ | #------------------------------------------------------------------------------------# | ||
+ | # | ||
# - Version 2.1 (2007-03-09) | # - Version 2.1 (2007-03-09) | ||
− | # - See http://www.organicdesign | + | # - See http://www.organicdesign/MediaWiki_Security for installation and usage details |
# - Licenced under LGPL (http://www.gnu.org/copyleft/lesser.html) | # - Licenced under LGPL (http://www.gnu.org/copyleft/lesser.html) | ||
# - Needs apache's mod-rewrite for security on images, see code comments below | # - Needs apache's mod-rewrite for security on images, see code comments below | ||
Line 8: | Line 17: | ||
# {{Security:*|sysop}} | # {{Security:*|sysop}} | ||
# {{Security:view|*}} | # {{Security:view|*}} | ||
− | |||
# These globals can be set in LocalSettings to adjust the Simple Security attributes | # These globals can be set in LocalSettings to adjust the Simple Security attributes | ||
− | if (!isset($ | + | if (!isset($wgSecurityName)) $wgSecurityName = "Security"; |
− | if (!isset($ | + | if (!isset($wgSecurityDontInheritName)) $wgSecurityDontInheritName = "*$wgSecurityName"; |
if (!isset($wgSecurityInheritable)) $wgSecurityInheritable = true; | if (!isset($wgSecurityInheritable)) $wgSecurityInheritable = true; | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
# Handle moves (since they don't use $action) | # Handle moves (since they don't use $action) | ||
Line 57: | Line 28: | ||
$securityAction = 'move'; | $securityAction = 'move'; | ||
$securityTitle = $wgRequest->getText('wpOldTitle',$wgRequest->getVal('target')); | $securityTitle = $wgRequest->getText('wpOldTitle',$wgRequest->getVal('target')); | ||
− | } else $securityTitle = $title; | + | } else $securityTitle = str_replace(' ','_',$title); |
# If title exists, activate the security | # If title exists, activate the security | ||
Line 130: | Line 101: | ||
$securityAfterFetchActive = true; | $securityAfterFetchActive = true; | ||
function securityAfterFetchContent(&$article,&$text) { | function securityAfterFetchContent(&$article,&$text) { | ||
− | + | global $wgUser,$wgTitle,$securityItems,$securityCache,$securityAfterFetchActive, | |
− | global $ | + | $wgSecurityName,$wgSecurityDontInheritName,$wgSecurityInheritable; |
− | $ | ||
if (!$securityAfterFetchActive) return true; | if (!$securityAfterFetchActive) return true; | ||
− | if (isset($securityCache[$ | + | # Get the security info straight from cache if already exists |
+ | $key = $article->mTitle->getPrefixedURL(); | ||
+ | |||
+ | if (isset($securityCache[$key])) $items = $securityCache[$key]; else { | ||
# Set up a new local parser object to process security items independently of main rendering process | # Set up a new local parser object to process security items independently of main rendering process | ||
Line 141: | Line 114: | ||
$parser = new Parser; | $parser = new Parser; | ||
$options = ParserOptions::newFromUser($wgUser); | $options = ParserOptions::newFromUser($wgUser); | ||
− | $parser->setFunctionHook($ | + | $parser->setFunctionHook($wgSecurityName,'securityProcessItem'); |
− | $parser->setFunctionHook($ | + | $parser->setFunctionHook($wgSecurityDontInheritName,'securityProcessItem'); |
$securityItems = array(); | $securityItems = array(); | ||
$output = $parser->parse($text,$wgTitle,$options,false,false); | $output = $parser->parse($text,$wgTitle,$options,false,false); | ||
Line 150: | Line 123: | ||
# - stop checking *Security because they shouldn't inherit | # - stop checking *Security because they shouldn't inherit | ||
# - disable this AfterDatabaseFetch hook while reading category contents | # - disable this AfterDatabaseFetch hook while reading category contents | ||
− | unset($parser->mFunctionHooks[$ | + | unset($parser->mFunctionHooks[$wgSecurityDontInheritName]); |
$securityAfterFetchActive = false; | $securityAfterFetchActive = false; | ||
Line 163: | Line 136: | ||
# Re-enable AfterFetch hook and cache the security info | # Re-enable AfterFetch hook and cache the security info | ||
$securityAfterFetchActive = true; | $securityAfterFetchActive = true; | ||
− | $securityCache[$ | + | $securityCache[$key] = $items; |
} | } | ||
Line 174: | Line 147: | ||
$wgHooks['ParserBeforeStrip'][] = 'securityBeforeParsing'; | $wgHooks['ParserBeforeStrip'][] = 'securityBeforeParsing'; | ||
function securityBeforeParsing(&$parser,&$text,&$strip_state) { | function securityBeforeParsing(&$parser,&$text,&$strip_state) { | ||
− | + | global $wgParser,$wgUser,$securityAllow,$securityCache,$securityAction,$wgSecurityName,$wgSecurityDontInheritName; | |
− | global $ | ||
− | |||
# Only process this hook if it's the main article | # Only process this hook if it's the main article | ||
if ($parser != $wgParser) return true; | if ($parser != $wgParser) return true; | ||
+ | $key = $parser->mTitle->getPrefixedURL(); | ||
# Replace the content with the "Action not permitted" template if action not allowed | # Replace the content with the "Action not permitted" template if action not allowed | ||
Line 185: | Line 157: | ||
# Append the security info to the article content | # Append the security info to the article content | ||
− | if (count($securityCache[$ | + | if (count($securityCache[$key]) && $securityAction == 'view') { |
$text .= "\n{{Security info|1=\n"; | $text .= "\n{{Security info|1=\n"; | ||
− | foreach ($securityCache[$ | + | foreach ($securityCache[$key] as $i) { |
$a = $i[0] == '*' ? 'Every action' : ucfirst($i[0]); | $a = $i[0] == '*' ? 'Every action' : ucfirst($i[0]); | ||
$b = $i[1] == '*' ? 'anybody' : $i[1]; | $b = $i[1] == '*' ? 'anybody' : $i[1]; | ||
Line 197: | Line 169: | ||
# Ensure the parser doesn't render the security items | # Ensure the parser doesn't render the security items | ||
− | $parser->setFunctionHook($ | + | $parser->setFunctionHook($wgSecurityName,'securityProcessItem'); |
− | $parser->setFunctionHook($ | + | $parser->setFunctionHook($wgSecurityDontInheritName,'securityProcessItem'); |
return true; | return true; | ||
+ | } | ||
+ | |||
+ | # Accumulates security information in the global $securityItems whenever a security item is parsed | ||
+ | function securityProcessItem(&$parser,$a,$b) { | ||
+ | global $securityItems; | ||
+ | $securityItems[] = array($a,$b); | ||
+ | return ''; | ||
} | } | ||
Line 230: | Line 209: | ||
# Read the article content to update security info cache | # Read the article content to update security info cache | ||
− | + | $tmp = new Article(Title::newFromText($securityTitle)); | |
− | + | $tmp->fetchContent(0,false,false); | |
$items = $securityCache[$securityTitle]; | $items = $securityCache[$securityTitle]; | ||
# Validate current action and set to view if not allowed | # Validate current action and set to view if not allowed | ||
− | + | if (!$securityAllow = securityValidate($wgUser,$items,$securityAction)) $action = 'view'; | |
# If its a file, send it back to the client and exit, or block by redirecting to the file's associated article | # If its a file, send it back to the client and exit, or block by redirecting to the file's associated article | ||
Line 266: | Line 245: | ||
} | } | ||
} | } | ||
− | + | ?></source> | |
− | ?> | + | [[Category:Legacy Extensions|SimpleSecurity2.1]] |
Latest revision as of 15:17, 27 February 2020
<?php
# THIS SCRIPT IS OBSOLETE!
# for the newer Version 3.1 (2007-03-14) see:
# http://www.organicdesign.co.nz/SimpleSecurity.php
#
# and also the whole help and needed reference at:
# http://www.mediawiki.org/wiki/Extension:Simple_Security
#
#------------------------------------------------------------------------------------#
#
# - Version 2.1 (2007-03-09)
# - See http://www.organicdesign/MediaWiki_Security for installation and usage details
# - Licenced under LGPL (http://www.gnu.org/copyleft/lesser.html)
# - Needs apache's mod-rewrite for security on images, see code comments below
# - The following directives allows everyone view-only access to this article
# {{Security:*|sysop}}
# {{Security:view|*}}
# These globals can be set in LocalSettings to adjust the Simple Security attributes
if (!isset($wgSecurityName)) $wgSecurityName = "Security";
if (!isset($wgSecurityDontInheritName)) $wgSecurityDontInheritName = "*$wgSecurityName";
if (!isset($wgSecurityInheritable)) $wgSecurityInheritable = true;
# Handle moves (since they don't use $action)
$securityAction = $action == 'submit' ? 'edit' : strtolower($action);
if ($title == 'Special:Movepage' && $action == 'submit') {
$securityAction = 'move';
$securityTitle = $wgRequest->getText('wpOldTitle',$wgRequest->getVal('target'));
} else $securityTitle = str_replace(' ','_',$title);
# If title exists, activate the security
if ($securityTitle) {
# Globals holding security info
$securityDenyRevisionID = 2575; # the current-revision-id of the Template:Action not permitted
$securityCache = array();
$securityItems = array();
# Handle security on files (needs apache mod-rewrite)
# - mod-rewrite redirects /wiki/images/... requests to article title Download/image-path...
# - Use the following rewrite condition and rule (adjust to your wiki path if necessary)
# RewriteCond %{REQUEST_URI} ^/wiki/images/.+
# RewriteRule ^/wiki/images/(.+) /wiki/index.php/Download/$1 [L]
if (ereg('^Download/(.+)/([^/]+)$',$securityTitle,$m)) {
$path = $m[1];
$file = $image = $m[2];
if (ereg('^thumb/.+/([^/]+)$',$path,$m)) $image = $m[1];
$securityTitle = "Image:$image";
} else $file = '';
if (0) {
# Create a new secureLoadBalancer class by extending the existing one
$LoadBalancer = get_class($wgLoadBalancer);
$secureLoadBalancer = "secure$LoadBalancer";
eval('class '.$secureLoadBalancer.' extends '.$LoadBalancer.' {
# 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;
}
}');
# Replace the global LoadBalancer object with an identical secureLoadBalancer
$oldLoadBalancer = $wgLoadBalancer;
$wgLoadBalancer = new $secureLoadBalancer;
foreach(array_keys(get_class_vars($LoadBalancer)) as $k) $wgLoadBalancer->$k = $oldLoadBalancer->$k;
# Add our security function to the new hook
# - also fills the global $securityCache cache with info to append to the rendered article
#$wgHooks['DatabaseFetchObject'][] = 'securityAfterFetchObject';
function securityAfterFetchObject(&$db,&$row) {
if (is_object($row) && $row->rev_text_id) {
if ($row->rev_text_id == 2638) { $row->rev_text_id = $securityDenyRevisionID; msg($db->lastQuery()); }
}
}
}
# Hook2: 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
$wgHooks['ArticleAfterFetchContent'][] = 'securityAfterFetchContent';
$securityAfterFetchActive = true;
function securityAfterFetchContent(&$article,&$text) {
global $wgUser,$wgTitle,$securityItems,$securityCache,$securityAfterFetchActive,
$wgSecurityName,$wgSecurityDontInheritName,$wgSecurityInheritable;
if (!$securityAfterFetchActive) return true;
# Get the security info straight from cache if already exists
$key = $article->mTitle->getPrefixedURL();
if (isset($securityCache[$key])) $items = $securityCache[$key]; else {
# Set up a new local parser object to process security items independently of main rendering process
# - both Security and *Security items are processed and remembered
$parser = new Parser;
$options = ParserOptions::newFromUser($wgUser);
$parser->setFunctionHook($wgSecurityName,'securityProcessItem');
$parser->setFunctionHook($wgSecurityDontInheritName,'securityProcessItem');
$securityItems = array();
$output = $parser->parse($text,$wgTitle,$options,false,false);
$items = $securityItems;
# Before inheriting security from categories
# - stop checking *Security because they shouldn't inherit
# - disable this AfterDatabaseFetch hook while reading category contents
unset($parser->mFunctionHooks[$wgSecurityDontInheritName]);
$securityAfterFetchActive = false;
# Get the security items from the cats by running the parser over the content of each
if ($wgSecurityInheritable) foreach ($output->getCategoryLinks() as $cat) {
$ca = new Article($ct = Title::newFromText($cat = "Category:$cat"));
$securityItems = array();
$parser->parse($ca->fetchContent(0,false,false),$ct,$options,false,false);
foreach ($securityItems as $i) $items[] = array($i[0],$i[1],"this rule is inherited from [[:$cat]]");
}
# Re-enable AfterFetch hook and cache the security info
$securityAfterFetchActive = true;
$securityCache[$key] = $items;
}
# Don't allow any content to be returned if not viewable
if (!securityValidate($wgUser,$items,'view')) $text = "{{Action not permitted|view}}";
return true;
}
# Hook3: Block content if action not allowed, and append any security info before parsing
$wgHooks['ParserBeforeStrip'][] = 'securityBeforeParsing';
function securityBeforeParsing(&$parser,&$text,&$strip_state) {
global $wgParser,$wgUser,$securityAllow,$securityCache,$securityAction,$wgSecurityName,$wgSecurityDontInheritName;
# Only process this hook if it's the main article
if ($parser != $wgParser) return true;
$key = $parser->mTitle->getPrefixedURL();
# Replace the content with the "Action not permitted" template if action not allowed
if (!$securityAllow) $text = "{{Action not permitted|$securityAction}}";
# Append the security info to the article content
if (count($securityCache[$key]) && $securityAction == 'view') {
$text .= "\n{{Security info|1=\n";
foreach ($securityCache[$key] as $i) {
$a = $i[0] == '*' ? 'Every action' : ucfirst($i[0]);
$b = $i[1] == '*' ? 'anybody' : $i[1];
$c = $i[2] ? " ''($i[2])''" : '';
$text .= "*'''$a''' requires the user to be '''$b'''$c\n";
}
$text .= "\n}}";
}
# Ensure the parser doesn't render the security items
$parser->setFunctionHook($wgSecurityName,'securityProcessItem');
$parser->setFunctionHook($wgSecurityDontInheritName,'securityProcessItem');
return true;
}
# Accumulates security information in the global $securityItems whenever a security item is parsed
function securityProcessItem(&$parser,$a,$b) {
global $securityItems;
$securityItems[] = array($a,$b);
return '';
}
# Return whether or not a user is allowed to perform an action according to an array of security items
function securityValidate(&$user,&$items,$action) {
if (!is_array($items)) return true;
# Resolve permission for this action from the extracted security links
$security = '';
foreach ($items as $i) {
if ($i[1] == '') $i[1] = 'sysop';
$actions = preg_split("/\\s*,\\s*/",strtolower($i[0]));
if ((in_array($action,$actions)) || (in_array('*',$actions) && ($security == ''))) $security = $i[1];
}
# Validate extracted security against this user/groups
$groups = $user->getGroups();
foreach($groups as $k => $v) $groups[$k] = strtolower($v);
$deny = false;
if ($security && !in_array('sysop',$groups) && !in_array('director',$groups)) {
$security = preg_split("/\\s*,\\s*/",$security);
if (!in_array('*',$security)) {
$groups[] = ucfirst($user->mName);
if (count(array_intersect($groups,$security))==0) $deny = true;
}
}
return !$deny;
}
# Read the article content to update security info cache
$tmp = new Article(Title::newFromText($securityTitle));
$tmp->fetchContent(0,false,false);
$items = $securityCache[$securityTitle];
# Validate current action and set to view if not allowed
if (!$securityAllow = securityValidate($wgUser,$items,$securityAction)) $action = 'view';
# If its a file, send it back to the client and exit, or block by redirecting to the file's associated article
if ($file) {
# Log the download event if any security on file
if (count($items)) {
$user = $wgUser->mName;
$entry = $securityAllow ? "User:$user accessed \"$file\"" : "User:$user was denied access to \"$file\"";
$ts = $wgLang->timeanddate(wfTimestampNow(),true);
$la = new Article(Title::newFromText('Download log'));
$log = "{{Log entry".
"|ts=$ts".
"|entry=$entry".
"|ip=".$_SERVER['REMOTE_ADDR'].
"|ua=".$_SERVER['HTTP_USER_AGENT'].
"|cookie=".$_SERVER['HTTP_COOKIE'].
"|qs=".$_SERVER['QUERY_STRING'].
"}}\n";
$la->quickEdit($log.$la->fetchContent(0,false,false));
}
# Return the file to the client if security validates
if ($securityAllow) {
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"$file\"");
@readfile("$wgUploadDirectory/$path/$file");
die;
} else $title = $securityTitle;
}
}
?>