Difference between revisions of "Extension:SimpleSecurity2.1.php"

From Organic Design wiki
m
(version 2.0 - redoing almost from scratch, much more efficient and stronger!)
Line 1: Line 1:
 
<?
 
<?
# Simple security extension
+
# Simple security extension version 2.0
 
# - [[Security:edit,delete,move,protect|sysop]]
 
# - [[Security:edit,delete,move,protect|sysop]]
 
# - See [[[[MediaWiki Security]]]] article for installation and usage details
 
# - See [[[[MediaWiki Security]]]] article for installation and usage details
Line 6: Line 6:
 
# - 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
  
# Handle moves
+
# Asseses security after raw content fetched from database and clears if not readable
$a = $action == 'submit' ? 'edit' : strtolower($action);
+
# - also fills the global $securityItems cache with info to append to the rendered article
if ($title=='Special:Movepage' && $action=='submit') {
+
$securityAfterFetchIndex = count($wgHooks['ArticleAfterFetchContent']);
$a = 'move';
+
$wgHooks['ArticleAfterFetchContent'][] = 'securityAfterFetch';
$t = $wgRequest->getText('wpOldTitle',$wgRequest->getVal('target'));
+
function securityAfterFetch(&$this,&$text) {
} else $t = $title;
+
global $wgUser,$wgTitle,$securityItems,$securityAfterFetchIndex;
$securityAction = $a;
+
$parser = new Parser;
 +
$options = ParserOptions::newFromUser($wgUser);
 +
$parser->setFunctionHook('Security','securityProcessItem');
 +
$parser->setFunctionHook(':Security','securityProcessItem');
 +
$securityItems = array();
 +
$output = $parser->parse($text,$wgTitle,$options,false,false);
 +
$allItems = $securityItems;
  
$groups = $wgUser->getGroups();
+
# Before inheriting security from categories
foreach($groups as $k => $v) $groups[$k] = strtolower($v);
+
# - stop checking :Security because they shouldn't inherit
if ($t) {
+
# - remove this AfterDatabaseFetch hook because we need to read each cats content
 +
unset($parser->mFunctionHooks[':Security']);
 +
$hookBak = $wgHooks['ArticleAfterFetchContent'];
 +
$wgHooks['ArticleAfterFetchContent'][$securityAfterFetchIndex]
 +
= array_splice($wgHooks['ArticleAfterFetchContent'],$securityAfterFetchIndex,1);
  
# Handle security on files (needs apache mod-rewrite)
+
# Get the security items from the cats by running the parser over the content of each
# - mod-rewrite redirects /wiki/images/... requests to article title Download/image-path...
+
foreach ($output->getCategoryLinks() as $cat) {
# - Use the following rewrite condition and rule (adjust to your wiki path if necessary)
+
$article = new Article($title = Title::newFromText($cat = "Category:$cat"));
#  RewriteCond %{REQUEST_URI} ^/wiki/images/.+
+
$securityItems = array();
#  RewriteRule ^/wiki/images/(.+) /wiki/index.php/Download/$1 [L]
+
$parser->parse($article->fetchContent(0,false,false),$title,$options,false,false);
if (ereg('^Download/(.+)/([^/]+)$',$t,$m)) {
+
foreach ($securityItems as $i) $allItems[] = array($i[0],$i[1],"this rule is inherited from [[:$cat]]");
$path = $m[1];
 
$file = $image = $m[2];
 
if (ereg('^thumb/.+/([^/]+)$',$path,$m)) $image = $m[1];
 
$t = "Image:$image";
 
} else $file = '';
 
 
 
# Get security links from this article
 
$secinfo = '';
 
$text = new Article(Title::newFromText($t));
 
$text = $text->fetchContent(0,false,false);
 
if (preg_match_all('/\\[{2}\\s*:?security\\s*:\\s*([^\\]]+?)\\s*\\|\\s*([^\\]]*)\\s*\\]{2}/i',$text,$seclinks,PREG_SET_ORDER))
 
addSecurityInfo($seclinks,$secinfo); else $seclinks = array();
 
 
 
# Get security links from article's categories
 
preg_match_all('/\\[{2}category:(.+?)(\\|.+?)?\\]]/i',$text,$cats);
 
foreach ($cats[1] as $i => $cat) {
 
$cats[1][$i] = $cat = 'Category:'.ucfirst($cat);
 
if (is_object($text = new Article(Title::newFromText($cat)))) {
 
if (preg_match_all('/\\[{2}\\s*security\\s*:\\s*([^\\]]+?)\\s*\\|\\s*([^\\]]*)\\s*\\]{2}/i',$text->fetchContent(0,false,false),$match,PREG_SET_ORDER)) {
 
$seclinks = array_merge($match,$seclinks);
 
addSecurityInfo($match,$secinfo,"this rule is inherited from [[:$cat]]");
 
}
 
}
 
 
}
 
}
  
Line 54: Line 41:
 
if ($link[2]=='') $link[2] = 'sysop';
 
if ($link[2]=='') $link[2] = 'sysop';
 
$actions = preg_split("/\\s*,\\s*/",strtolower($link[1]));
 
$actions = preg_split("/\\s*,\\s*/",strtolower($link[1]));
if (in_array($a,$actions)) $security = $link[2];
+
if (in_array($securityAction,$actions)) $security = $link[2];
 
if (in_array('*',$actions) && ($security == '')) $security = $link[2];
 
if (in_array('*',$actions) && ($security == '')) $security = $link[2];
 
}
 
}
  
 
# Validate extracted security against this user/groups
 
# Validate extracted security against this user/groups
 +
$groups = $wgUser->getGroups();
 +
foreach($groups as $k => $v) $groups[$k] = strtolower($v);
 
$deny = false;
 
$deny = false;
 
if ($security && !in_array('sysop',$groups) && !in_array('director',$groups)) {
 
if ($security && !in_array('sysop',$groups) && !in_array('director',$groups)) {
Line 70: Line 59:
 
}
 
}
 
}
 
}
if ($file) {
 
# Log the download event if any security on file
 
if ($secinfo) {
 
$user = $wgUser->mName;
 
if ($deny) $entry = "User:$user was denied access to \"$file\"";
 
else $entry = "User:$user accessed \"$file\"";
 
$ts = $GLOBALS['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 ($deny) $title = $t; else {
 
header("Content-Type: application/octet-stream");
 
header("Content-Disposition: attachment; filename=\"$file\"");
 
@readfile("$wgUploadDirectory/$path/$file");
 
die;
 
}
 
}
 
}
 
  
# Remove the security links and append security info before wiki-parsing
+
# Put the AfterFetch hook back how it was and exit successfully
$wgHooks['ParserBeforeStrip'][] = 'securityBeforeParsing';
+
$wgHooks['ArticleAfterFetchContent'][$securityAfterFetchIndex] = $hookBak;
function securityBeforeParsing(&$parser,&$text,&$strip_state) {
 
global $deny,$secinfo,$securityAction,$wgHooks;
 
if ($deny) {
 
$text = "{{Action not permitted|$securityAction}}";
 
$deny = false;
 
$securityAction = 'view';
 
}
 
else $text = preg_replace("/\\[{2}\\s*:?security\\s*:[^\\]]+?\\]{2}[\r\n]?/i",'',$text);
 
if ($secinfo && 0 == $GLOBALS['rsi-done']++ && $securityAction == 'view') $text .= "\n{{Security info|1=\n$secinfo\n}}";
 
 
return true;
 
return true;
 
}
 
}
  
# Add security information item
+
# Accumulates security information in a global cache whenever a security item is parsed
function addSecurityInfo(&$links,&$info,$comment='') {
+
function securityProcessItem(&$this,$a,$b) {
if ($comment) $comment = " &nbsp; ''($comment)''";
+
$GLOBALS['securityItems'][] = array($a,$b);
foreach ($links as $link) {
+
return '';
$a = $link[1] == '*' ? 'Every action' : ucfirst($link[1]);
 
$b = $link[2] == '*' ? 'anybody' : $link[2];
 
$info .= "*'''$a''' requires the user to be '''$b'''$comment\n";
 
}
 
 
}
 
}
 
?>
 
?>

Revision as of 09:30, 3 March 2007

<?

  1. Simple security extension version 2.0
  2. - sysop
  3. - See [[MediaWiki Security]] article for installation and usage details
  4. - Licenced under LGPL (http://www.gnu.org/copyleft/lesser.html)
  5. - Needs apache's mod-rewrite for security on images, see code comments below
  1. Asseses security after raw content fetched from database and clears if not readable
  2. - also fills the global $securityItems cache with info to append to the rendered article

$securityAfterFetchIndex = count($wgHooks['ArticleAfterFetchContent']); $wgHooks['ArticleAfterFetchContent'][] = 'securityAfterFetch'; function securityAfterFetch(&$this,&$text) { global $wgUser,$wgTitle,$securityItems,$securityAfterFetchIndex; $parser = new Parser; $options = ParserOptions::newFromUser($wgUser); $parser->setFunctionHook('Security','securityProcessItem'); $parser->setFunctionHook(':Security','securityProcessItem'); $securityItems = array(); $output = $parser->parse($text,$wgTitle,$options,false,false); $allItems = $securityItems;

# Before inheriting security from categories # - stop checking :Security because they shouldn't inherit # - remove this AfterDatabaseFetch hook because we need to read each cats content unset($parser->mFunctionHooks[':Security']); $hookBak = $wgHooks['ArticleAfterFetchContent']; $wgHooks['ArticleAfterFetchContent'][$securityAfterFetchIndex] = array_splice($wgHooks['ArticleAfterFetchContent'],$securityAfterFetchIndex,1);

# Get the security items from the cats by running the parser over the content of each foreach ($output->getCategoryLinks() as $cat) { $article = new Article($title = Title::newFromText($cat = "Category:$cat")); $securityItems = array(); $parser->parse($article->fetchContent(0,false,false),$title,$options,false,false); foreach ($securityItems as $i) $allItems[] = array($i[0],$i[1],"this rule is inherited from $cat"); }

# Resolve permission for this action from the extracted security links $security = ; foreach ($seclinks as $link) { if ($link[2]==) $link[2] = 'sysop'; $actions = preg_split("/\\s*,\\s*/",strtolower($link[1])); if (in_array($securityAction,$actions)) $security = $link[2]; if (in_array('*',$actions) && ($security == )) $security = $link[2]; }

# Validate extracted security against this user/groups $groups = $wgUser->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[] = ucwords($wgUser->mName); if (count(array_intersect($groups,$security))==0) { $action = 'view'; $deny = true; } } }

# Put the AfterFetch hook back how it was and exit successfully $wgHooks['ArticleAfterFetchContent'][$securityAfterFetchIndex] = $hookBak; return true; }

  1. Accumulates security information in a global cache whenever a security item is parsed

function securityProcessItem(&$this,$a,$b) { $GLOBALS['securityItems'][] = array($a,$b); return ; } ?>