Extension:PermissionACL
![]() | 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.
For further details, see Security issues with authorization extensions |
![]() | This extension stores its source code on a wiki page. Please be aware that this code may be unreviewed or maliciously altered. They may contain security holes, outdated interfaces that are no longer compatible etc. Note: No localisation updates are provided for this extension by translatewiki.net . |
![]() | This extension is currently not actively maintained! Although it may still work, any bug reports or feature requests will more than likely be ignored. |
![]() Release status: unmaintained |
|
---|---|
Implementation | User rights |
Description | Implements per namespace, page, category, user or user group access permissions |
Author(s) | Jan Vavříček (Vavricektalk) |
MediaWiki | |
Database changes | No |
License | GPL |
Download | See the Code section |
|
|
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!
How secure is this extension?[edit]
Security issues with authorization extensions | |||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Inclusion/transclusion | unsafe | ||||||||||||||||||
Preloading | ? | ||||||||||||||||||
XML export (Special:Export) | safe | ||||||||||||||||||
Atom/RSS feeds | ? | ||||||||||||||||||
Listings & search | unsafe | ||||||||||||||||||
Diff & revision links | ? | ||||||||||||||||||
API | ? | ||||||||||||||||||
Action links | safe | ||||||||||||||||||
Related rights | ? | ||||||||||||||||||
Author backdoor | safe | ||||||||||||||||||
Caching | ? | ||||||||||||||||||
Files & Images | ? | ||||||||||||||||||
Redirects | ? | ||||||||||||||||||
Edit Section | safe | ||||||||||||||||||
Watching Pages | unsafe | ||||||||||||||||||
Other extensions | ? | ||||||||||||||||||
Raw/render | ? | ||||||||||||||||||
Recentchanges | ? | ||||||||||||||||||
Known bugs | Configuration is case-sensitive (e.g. Special:Userlogin is different to Special:UserLogin) |
Usage[edit]
If $wgPermissionACL is set, then ACL model is used - if not, the extension will do nothing.
Rules are array elements and their order in array is used by ACL mechanism.
Syntax of rules (every rule has 4 parts):
- which page : select of pages (namespaces, categories)
- which user : select of users (groups)
- which action : select of actions ( userCan actions - read, edit, create, move)
- operation : permit or deny access
First, second and third rule part can be:
- one value
- array of values
- ALL (represented by asterisk)
Summary of syntax[edit]
The following may be repeated multiple times to add rules to the ACL:
$wgPermissionACL[] = array(
{'group' | 'user'}, => {<username> | <groupname> | '*'} [, {<username> | <groupname> | '*'}...] ,
{'namespace' | 'page' | 'category'} => <namespace> [, <namespace>...] ,
'action' => {'read', 'edit', 'create', 'move', '*'} [, {'read', 'edit', 'create', 'move', '*'}...] ,
'operation' => {'permit', 'deny'}
);
Example[edit]
This example configures for the scenario:
- 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
- Administrators (users "wikisysop" and "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');
Download instructions[edit]
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 .
Installation[edit]
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
Code[edit]
<?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' => 'https://mediawiki.org/wiki/Extension:PermissionACL',
'description' => 'Provides a 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->isUserConfigPage()) 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
// If the user's trying to "read"/"edit" a page which doesn't exist;
// check for "create" and "createpage" actions
if (
(!($title->isKnown())) &&
(
($action == 'read') ||
($action == 'edit')
)
) {
if (
PermissionACL_userCan($title, $user, 'create', $result) ||
PermissionACL_userCan($title, $user, 'createpage', $result)
) {
return $result;
}
}
//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'])) {
if (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']);
return ($tmp == $action) ||
(is_string($tmp) && $tmp=='*') ||
(is_array($tmp) && in_array($action, $tmp));
}//function PermissionACL_isRuleApplicable
function PermissionACL_Permit($rule) {
return $rule['operation'] == 'permit';
}//function PermissionACL_Permit
function ArrayToLower($ar) {
$tmp = array();
foreach($ar as $index => $value)
$tmp[$index] = strtolower($value);
return $tmp;
}//function ArrayToLower
![]() | This extension is included in the following packages and/or wiki farms: This is not an authoritative list. Some wiki farms/hosts may contain this extension even if they are not listed here. Always check with your wiki farms/hosts or bundle to confirm. |