Extension:PermissionACL

From MediaWiki.org

Jump to: navigation, search
If you need per-page or partial page access restrictions, you are advised to install an appropriate content management package. MediaWiki was not written to provide per-page access restrictions, and almost all hacks or patches promising to add them will likely have flaws somewhere, which could lead to exposure of confidential data. We are not responsible for anything being leaked, leading to loss of funds or one's job.
For further details, see Security issues with authorization extensions


Manual on MediaWiki Extensions
List of MediaWiki Extensions
PermissionACL

Release status: beta

Implementation User rights
Description implements per-{namespace, page, category, user, group} access permissions
Author(s) Jan Vavříček (VavricekTalk)
License GPL
Download Code

Contents

[edit] What can this extension do?

The PermissionACL extension implements a way to restrict access to specific {namespaces, pages, categories} based on user group or user name. This provides a more fine grained security model than the one provided by the default $wgGroupPermissions.

PermissionACL extension configuration is based on ACL (Access Control List) - list of rules which are processing from first to last. First applicable rule is used! On the end of list is implicit rule DENY TO ALL!

[edit] Usage

If $wgPermissionACL is set, then ACL model is used - if not, extension do nothing.

Rules are array elements and their order in array is used by ACL mechanism.

Syntax of rules (every rule has 4 parts):

  1. which page : select of pages (namespaces, categories)
  2. which user : select of users (groups)
  3. which action : select of actions ( userCan actions - read, edit, create, move)
  4. operation : permit or deny access

First, second and third rule part can be:

  • one value
  • array of values
  • ALL (represented by asterisk)

[edit] Example 1

Story about:

  • namespaces: Private, Ccna, Ccnp, Ns, Fwl
  • user groups: private, ccna, ccnp, ns, fwl;
  • group ccna has RW access only to namespace Ccna, group fwl to Fwl, ...
  • group private has RW access to every namespace
  • unlogged users can only read NS_MAIN namespace
  • administrator (user wikisysop, vav166) can do everything
require_once('$IP/extensions/PermissionACL/PermissionACL.php');
$wgExtraNamespaces = array( 100 => "Private",
                            101 => "Private_Talk",
                            102 => "Ccna",
                            103 => "Ccna_Talk",
                            104 => "Ccnp",
                            105 => "Ccnp_Talk",
                            106 => "Ns",
                            107 => "Ns_Talk",
                            108 => "Fwl",
                            109 => "Fwl_Talk" );
 
$wgGroupPermissions['ccna']['read'] = true;
$wgGroupPermissions['ccnp']['read'] = true;
$wgGroupPermissions['ns']['read']   = true;
$wgGroupPermissions['fwl']['read']  = true;
$wgGroupPermissions['private']['read'] = true;
 
//whitelist is used, but same thing can be done by ACL:
/*
$wgPermissionACL[] = array('group'     => '*',
                           'page'      =>  array('Special:UserLogin', 'Special:UserLogout', 'Special:Resetpass', 'Special:Confirmemail'),
                           'action'    => 'read',
                           'operation' => 'permit');
*/
$wgWhitelistRead = array('Special:Userlogin', 'Special:Userlogout', 'Special:Resetpass', 'Special:Confirmemail');
 
//Superuser is only simplification - same result will have ACL:
/*
$wgPermissionACL[] = array('user'      =>  array('wikisysop', 'vav166'),
                           'page'      => '*'
                           'action'    => '*',
                           'operation' => 'permit');
*/
$wgPermissionACL_Superuser = array('wikisysop', 'vav166');
 
$wgPermissionACL[] = array('group'     => '*',
                           'namespace' =>  NS_MAIN,
                           'action'    => 'read',
                           'operation' => 'permit');
 
$wgPermissionACL[] = array('group'     => 'user',
                           'namespace' =>  array(NS_MAIN, NS_SPECIAL, NS_USER, NS_CATEGORY),
                           'action'    => 'read',
                           'operation' => 'permit');
 
$wgPermissionACL[] = array('group'     => 'private',
                           'namespace' =>  array(100, 101, 102, 103, 104, 105, 106, 107, 108, 109),
                           'action'    => '*',
                           'operation' => 'permit');
 
$wgPermissionACL[] = array('group'     => 'ccna',
                           'namespace' =>  array(102, 103),
                           'action'    => '*',
                           'operation' => 'permit');
 
$wgPermissionACL[] = array('group'     => 'ccnp',
                           'namespace' =>  array(104, 105),
                           'action'    => '*',
                           'operation' => 'permit');
 
$wgPermissionACL[] = array('group'     => 'ns',
                           'namespace' => array(106, 107),
                           'action'    => '*',
                           'operation' => 'permit');
 
$wgPermissionACL[] = array('group'     => 'fwl',
                           'namespace' => array(108, 109),
                           'action'    => '*',
                           'operation' => 'permit');

[edit] Download instructions

Please cut and paste the code found below and place it in $IP/extensions/PermissionACL/PermissionACL.php.

Note: $IP stands for the root directory of your MediaWiki installation, the same directory that holds LocalSettings.php.

[edit] Installation

To install this extension, add the following to LocalSettings.php:

require_once("$IP/extensions/PermissionACL/PermissionACL.php");
 
#add configuration parameters here
#setup user rights here

[edit] Code

<?php
 
if( !defined( 'MEDIAWIKI' ) ) {
    echo("This file is an extension to the MediaWiki software and cannot be used standalone.\n");
    die(1);
}
 
$wgExtensionCredits['other'][] = array(
        'name' => 'PermissionACL',
        'author' => 'Jan Vavříček',
        'url' => 'http://mediawiki.org/wiki/Extension:PermissionACL',
        'description' => 'permissions access control list',
);
 
$wgHooks['userCan'][] = 'PermissionACL_userCan';
 
function PermissionACL_userCan($title, $user, $action, &$result) {
    global $wgPermissionACL, $wgPermissionACL_Superuser, $wgWhitelistRead;
 
    $result = NULL;
 
    if(!isset($wgPermissionACL)) return TRUE; //if not set - grant access
 
    if($title->isCssJsSubpage()) return TRUE;
 
    if($action == 'read' && is_array($wgWhitelistRead)) { //don't continue if - read action on whitelisted pages
        if(in_array($title->getPrefixedText(), @$wgWhitelistRead)) {
            $result = TRUE;
            return TRUE;
        }//if-in_array
    }//if-whitelist
 
    if(isset($wgPermissionACL_Superuser)) { //Superuser can do everything - no need to read ACLs
        if(is_array($wgPermissionACL_Superuser)) {
            if(in_array(strtolower($user->getName()), ArrayToLower($wgPermissionACL_Superuser))) return TRUE;
        }//if-superusers array
        else if(strtolower($user->getName()) == strtolower($wgPermissionACL_Superuser)) return TRUE;
    }//if-superuser
 
    foreach($wgPermissionACL as $rule) { //process ACLs
        if(!PermissionACL_isRuleValid($rule)) continue; //syntax checking
 
        if(PermissionACL_isRuleApplicable($rule, $title, $user, $action)) {
            if(PermissionACL_Permit($rule)) {
                $result = TRUE;
                return TRUE;
            }
            else {
                $result = FALSE;
                return FALSE;
            }
        }//if-applicable
    }//foreach
 
    //implicit end rule - DENY ALL
    $result = FALSE;
    return FALSE;
}//PermissionACL_userCan
 
function PermissionACL_isRuleValid($rule) {
    /* rule parts:
        'group' || 'user'
        'namespace' || 'page' || 'category'
        'action' = (read, edit, create, move, *)
        'operation' = (permit, deny)
    */
    $tmp_actions    = array('read', 'edit', 'create', 'move', '*');
    $tmp_operations = array('permit', 'deny');
 
    if( ( isset($rule['group']) && !isset($rule['user'])) ||
        (!isset($rule['group']) &&  isset($rule['user'])) ) {
 
        if( ( isset($rule['namespace']) && !isset($rule['page']) && !isset($rule['category'])) ||
            (!isset($rule['namespace']) &&  isset($rule['page']) && !isset($rule['category'])) ||
            (!isset($rule['namespace']) && !isset($rule['page']) &&  isset($rule['category'])) ) {
 
            if( isset($rule['action'])  && ((is_string($rule['action']) && in_array($rule['action'], $tmp_actions)) ||
                is_array($rule['action']) )) {
 
                if( isset($rule['operation']) && in_array($rule['operation'], $tmp_operations)) {
                    return TRUE;
                }//if-operation test
            }//if-action test
        }//if-namespace, page, category test
    }//if-user, group test
 
    return FALSE;
}//function PermissionACL_isRuleValid
 
function PermissionACL_isRuleApplicable($rule, $title, $user, $action) {
    //group|user rule
    if(isset($rule['group'])) { //group rule
        if(is_array($rule['group']))
            $tmp = ArrayToLower($rule['group']);
        else
            $tmp = strtolower($rule['group']);
 
        $groups = ArrayToLower($user->getEffectiveGroups());
        if(!( (is_string($tmp) && in_array($tmp, $groups)) ||
              (is_array($tmp) && count(array_intersect($tmp, $groups))>0)
            )) return FALSE;
    }
    else { // user rule
        if(is_array($rule['user']))
            $tmp = ArrayToLower($rule['user']);
        else
            $tmp = strtolower($rule['user']);
        $tmp2 = strtolower($user->getName());
 
        if(!( (is_string($tmp) && $tmp=='*') ||
              (is_string($tmp) && $tmp==$tmp2) ||
              (is_array($tmp) && in_array($tmp2, $tmp))
            )) return FALSE;
    }
 
    //namespace|page|category rule
    if(isset($rule['namespace'])) { //namespace rule
        $tmp = $rule['namespace'];
        $tmp2 = $title->getNamespace();
 
        if(!( (is_int($tmp) &&  $tmp==$tmp2) ||
              (is_string($tmp) && $tmp=='*') ||
              (is_array($tmp) && in_array($tmp2, $tmp))
            )) return FALSE;
    }
    else if(isset($rule['page'])){ //page rule
        $tmp = $rule['page'];
        $tmp2 = $title->getPrefixedText();
 
        if(!( (is_string($tmp) && $tmp==$tmp2) ||
              (is_string($tmp) && $tmp=='*') ||
              (is_array($tmp) && in_array($tmp2, $tmp))
            )) return FALSE;
    }
    else { //category rule
        $tmp = $rule['category'];
        $tmp2 = $title->getParentCategories();
        $categs = array();
 
        if(is_array($tmp2)) {
            global $wgContLang;
            $tmp_pos = strrpos($wgContLang->getNSText(NS_CATEGORY), ':');
 
            foreach($tmp2 as $cat => $page) {
                if($tmp_pos === FALSE) {
                    $categs[] = substr($cat, strpos($cat, ':')+1);
                }
                else {
                    $tmp_categ = substr($cat, $tmp_pos+1);
                    $categs[] = substr($tmp_categ, strpos($tmp_categ, ':')+1);
                }
            }
        }
 
        if(!( (is_string($tmp) && is_array($tmp2) && in_array($tmp, $categs)) ||
              (is_string($tmp) && $tmp=='*') ||
              (is_array($tmp)  && is_array($tmp2) && count(array_intersect($tmp, $categs))>0)
            )) return FALSE;
    }
 
    //action rule
    if(is_array($rule['action']))
        $tmp = ArrayToLower($rule['action']);
    else
        $tmp = strtolower($rule['action']);
 
    if(!( ($tmp == $action) ||
          (is_string($tmp) && $tmp=='*') ||
          (is_array($tmp) && in_array($action, $tmp))
        )) return FALSE;
 
    return TRUE;
}//function PermissionACL_isRuleApplicable
 
function PermissionACL_Permit($rule) {
    if($rule['operation'] == 'permit') return TRUE;
    return FALSE;
}//function PermissionACL_Permit
 
function ArrayToLower($ar) {
    $tmp = array();
    foreach($ar as $index => $value)
        $tmp[$index] = strtolower($value);
    return $tmp;
}//function ArrayToLower
?>


Personal tools