Extension talk:CategoryPermissions

Please add comments or suggestions

Special pages (well.. namespaces)
Hey there. A category-bases permission system is just what I need, so I am trying to get this one working on my Mediawiki installation (now if only I could find out its version, but think it's 1.6'ish). I use the proposed default rule to deny for all categories in CustomPermission.php. Unfortunately, it doesn't allow anonymous users to access the "meta-namespace" (sorry for lacking the appropriate term here) even though $wgGroupDefaultAllow == true. The start page for example doesn't have a category (at least none that I can see) and neither has the login page, amongst others. So, I cannot even log on to alter my access level. Does this work as expected? --Carp 08:39, 20 April 2007 (UTC)
 * Got a running version - I changed quite a bit, so I'm posting a diff instead of editing the code on the main site. It had nothing to do with namespace, instead I found that a few variables and properties where never checked. I'm running it on a production site now. Seems to work smoothly. Thanks for your work! --Carp 13:03, 20 April 2007 (UTC)


 * Deleted buggy diff -> didn't work with more than category, see below --Carp 11:28, 22 April 2007 (UTC)

Thanks for posting your version. I have made several changes to the code that I am currently running and have not had time to update the page. I will look through your version and update the page sometime this week ... and I suppose i should create an account so I can watch the talk page.

Working version
Hey again. Forget the diff I submitted the other day. I found out that it didn't work on a page that was listed in more than one category, hence I rewrote the entire block that checks for parentCategories. Another LocalSettings.php variable can be set to set default allow / deny to categories that haven't been explicitly denied in the LocalSettings.php file. --- CustomPermissions.php.old	Sat Apr 21 17:39:56 2007 +++ CustomPermissions.php	Sat Apr 21 17:39:27 2007 @@ -11,73 +11,87 @@ * Usage: * * require_once('extensions/CategoryPermissions.php'); - * =''; //set a group name to ALWAYS allow access to this group - * =true; //set to true to allow everyone access to pages without a category + * $wgGroupAlwaysAllow=''; //set a group name to ALWAYS allow access to this group + * $wgGroupDefaultAllow=true; //set to true to allow everyone access to pages without a category + * $wgGroupDefaultAllowCategory=true; //set to true to allow everyone access to pages with a category that is not explicitly restricted * *  * //add groups to category permissions by: - * ['group_name']['Category:categoryname_read']=true; - * ['group_name']['Category:categoryname_edit']=true; - * ['group_name']['Category:categoryname_move']=true; - * ['group_name']['Category:categoryname_create']=true; + * $wgGroupPermissions['group_name']['Category:categoryname_read']=true; + * $wgGroupPermissions['group_name']['Category:categoryname_edit']=true; + * $wgGroupPermissions['group_name']['Category:categoryname_move']=true; + * $wgGroupPermissions['group_name']['Category:categoryname_create']=true; */ //set up hook -[] = "wfCategoryPermissions"; +$wgExtensionFunctions[] = "wfCategoryPermissions"; function wfCategoryPermissions { - global ; + global $wgHooks; // use the userCan hook to check permissions - [ 'userCan' ][] = 'checkCategoryPermissions'; + $wgHooks[ 'userCan' ][] = 'checkCategoryPermissions'; } -function checkCategoryPermissions { - global, , ; - +function checkCategoryPermissions( $title, $user, $action, $result ) { + global $wgGroupAlwaysAllow, $wgGroupDefaultAllow, $wgGroupPermissions, $wgGroupDefaultAllowCategory; //get categories for this page - =->getParentCategories; + $parentCategories=$title->getParentCategories; //always allow wgGroupAlwaysAllow group - if(in_array(,->mGroups)) + if(in_array($wgGroupAlwaysAllow,$user->mGroups)) { -   wfDebug("checkCategoryPermissions:{} allowed on {->mPrefixedText} to {->mName} : AlwaysAllow\n"); -    = true; +   wfDebug("checkCategoryPermissions:{$action} allowed on {$title->mPrefixedText} to {$user->mName} : AlwaysAllow\n"); +   $result = true; return null; }  //scan list of categories, if any - if{ -   foreach(  as =>){ - -      = ->isAllowed("{}_{}"); -     if{ -       wfDebug("checkCategoryPermissions:{}_{} allowed on {->mPrefixedText} to {->mName}\n"); -       if(->isAnon){ -         =true; -         return true; -       } -        else{ -         return null; -       } +  if(is_array($parentCategories)) { +   if ($user->isAnon or sizeof($user->mGroups) == 0) { +     $groups = array( '*' ); +   } +    else { +     $groups = $user->mGroups; +   } +    // Defaults to deny acces unless category is $wgGroupDefaultAllowCategory is true +   $allowed_in_category = isset($wgGroupDefaultAllowCategory) ? $wgGroupDefaultAllowCategory : false; +   // Search permissions for groups (or '*') and store result for each +   // group the user is in +    foreach ($groups as $group) { +     // Iterate through all of the article's categories +     foreach( $parentCategories as $category=>$dd){ +	if (isset($wgGroupPermissions["$group"]["{$category}_{$action}"])) { +         // if there's one false in the chain, access is not granted ... +	  // which is exactly what we want. +         $allowed_in_category = $allowed_in_category && $wgGroupPermissions["$group"]["{$category}_{$action}"]; +	}      }     } +    if ($allowed_in_category) { +     wfDebug("checkCategoryPermissions:{$category}_{$action} allowed on {$title->mPrefixedText} to {$user->mName}\n"); +   } +    else { +     wfDebug("checkCategoryPermissions:{$category}_{$action} DENIED on {$title->mPrefixedText} to {$user->mName}\n"); +   } +    $result = $allowed_in_category; +   return $result; } + // No categories found -> grant access based on $wgGroupDefaultAllow else { -   wfDebug("checkCategoryPermissions:{} DENIED on {->mPrefixedText} to {->mName} : No Categories\n"); -   =false; -   return false; +   wfDebug("checkCategoryPermissions:{$action} allowed on {$title->mPrefixedText} to {$user->mName} : No Categories\n"); +   $result= $wgGroupDefaultAllow; +   return $result; }  //default action=deny - wfDebug("checkCategoryPermissions:{} DENIED on {->mPrefixedText} to {->mName}\n"); - =false; - return false; + wfDebug("checkCategoryPermissions:{$action} DENIED on {$title->mPrefixedText} to {$user->mName}\n"); + $result=false; + return $result; } -

And here's is the entire new file: <?php /* * Custom Permissions Scheme using Categories * based on Extension:NamespacePermissions by Petr Andreev * * Provides separate permissions for each action (read,edit,create,move) based * on category tags on pages. * * Author: Matthew Vernon * * Usage: * * require_once('extensions/CategoryPermissions.php'); * $wgGroupAlwaysAllow=''; //set a group name to ALWAYS allow access to this group * $wgGroupDefaultAllow=true; //set to true to allow everyone access to pages without a category * $wgGroupDefaultAllowCategory=true; //set to true to allow everyone access to pages with a category that is not explicitly restricted * * * //add groups to category permissions by: * $wgGroupPermissions['group_name']['Category:categoryname_read']=true; * $wgGroupPermissions['group_name']['Category:categoryname_edit']=true; * $wgGroupPermissions['group_name']['Category:categoryname_move']=true; * $wgGroupPermissions['group_name']['Category:categoryname_create']=true; */

//set up hook $wgExtensionFunctions[] = "wfCategoryPermissions";

function wfCategoryPermissions { global $wgHooks;

// use the userCan hook to check permissions $wgHooks[ 'userCan' ][] = 'checkCategoryPermissions';

}

function checkCategoryPermissions( $title, $user, $action, $result ) { global $wgGroupAlwaysAllow, $wgGroupDefaultAllow, $wgGroupPermissions, $wgGroupDefaultAllowCategory;

//get categories for this page $parentCategories=$title->getParentCategories;

//always allow wgGroupAlwaysAllow group if(in_array($wgGroupAlwaysAllow,$user->mGroups)) {   wfDebug("checkCategoryPermissions:{$action} allowed on {$title->mPrefixedText} to {$user->mName} : AlwaysAllow\n"); $result = true; return null; }

//scan list of categories, if any if(is_array($parentCategories)) { if ($user->isAnon or sizeof($user->mGroups) == 0) { $groups = array( '*' ); }   else { $groups = $user->mGroups; }   // Defaults to deny acces unless category is $wgGroupDefaultAllowCategory is true $allowed_in_category = isset($wgGroupDefaultAllowCategory) ? $wgGroupDefaultAllowCategory : false; // Search permissions for groups (or '*') and store result for each // group the user is in   foreach ($groups as $group) { // Iterate through all of the article's categories foreach( $parentCategories as $category=>$dd){ if (isset($wgGroupPermissions["$group"]["{$category}_{$action}"])) { // if there's one false in the chain, access is not granted ... // which is exactly what we want. $allowed_in_category = $allowed_in_category && $wgGroupPermissions["$group"]["{$category}_{$action}"]; }     }    }    if ($allowed_in_category) { wfDebug("checkCategoryPermissions:{$category}_{$action} allowed on {$title->mPrefixedText} to {$user->mName}\n"); }   else { wfDebug("checkCategoryPermissions:{$category}_{$action} DENIED on {$title->mPrefixedText} to {$user->mName}\n"); }   $result = $allowed_in_category; return $result; } // No categories found -> grant access based on $wgGroupDefaultAllow else { wfDebug("checkCategoryPermissions:{$action} allowed on {$title->mPrefixedText} to {$user->mName} : No Categories\n"); $result= $wgGroupDefaultAllow; return $result; }

//default action=deny wfDebug("checkCategoryPermissions:{$action} DENIED on {$title->mPrefixedText} to {$user->mName}\n"); $result=false; return $result; }

Sorry for the long page now, but I can't upload arbitrary files here (which is probably a Good Thing).--Carp 11:43, 22 April 2007 (UTC)

Licensing
Maybe I only didn't see it ... but I think you didn't mention a licence for your original file. I hope I was allowed to modify and / or use it at all ;) . If your CustomPermissions isn't GPL'ed, I put my modified file under a BSD Licence - in which case I'd be happy if you could list me, Carsten Zimmermann somewhere should you decide to incorporate the code. But I am really not that concerned ;) Cheers, --Carp 11:47, 22 April 2007 (UTC)

This is my first foray into posting code, so I don't know all the semantics of licensing or documentation requirements yet. Everything I post is GPL ... I suppose I should add it to the file. As far as adding names, my philosophy is that if you contribute your name can be included. --Sunergos 18:48, 22 April 2007 (UTC)

Updated Code
I have updated the code to the current version that I am running. The main difference between my code and what is above is that I set categories that should deny access by default while the code above sets categories that should accept access by default. I have also added permissions of type *_read etc to allow a user that permission on all categories. This is evaluated after the categories are scanned. Thank you Carp for your contribution, as my previous code would only deny access to an exclusive category if it ocurred before another category granted access.

--Sunergos 17:18, 23 April 2007 (UTC)

Need help on CategoryPermissions
Hi,

I'm trying to use the extension but somehow can't get it to work as I expected it. I have pages that have different categories, and one page can be in multiple categories. I also have groups that have the same name as the category. I wanted to set up permissions such that a group has read access to pages only in his category and no one else's.

The extension works for pages that have only 1 category, but for pages with multiple categories, it doesn't work. None of the groups can access the page.

Is there something wrong I'm doing or is this a limitation?

Example - (my groups are MBG, WDG and IDG. IDG has access to all categories, while MBG only has access to the MBG category, WDG only has access to the WDG category):

$wgGroupDefaultAllow=true; $wgCategoryExclusive=array("Category:MBG","Category:WDG");

$wgGroupPermissions['IDG']['Category:MBG_read']=true; $wgGroupPermissions['IDG']['Category:MBG_edit']=true; $wgGroupPermissions['IDG']['Category:MBG_move']=true; $wgGroupPermissions['IDG']['Category:MBG_create']=true; $wgGroupPermissions['IDG']['Category:WDG_read']=true; $wgGroupPermissions['IDG']['Category:WDG_edit']=true; $wgGroupPermissions['IDG']['Category:WDG_move']=true; $wgGroupPermissions['IDG']['Category:WDG_create']=true;

$wgGroupPermissions['MBG']['Category:MBG_read']=true; $wgGroupPermissions['WDG']['Category:WDG_read']=true;


 * The problem is $wgCategoryExclusive. Each category that you have in the array will block anyone who is not part of the group. In this case, the user would have to be in both groups so IDG should have access. For your use, leave $wgCategoryExclusive empty. --Sunergos 00:52, 25 July 2007 (UTC)

Patches for better debugging and some minor fixes
RichiH 16:04, 18 September 2007 (UTC)

Diff
--- CategoryPermissions_orig.php       2007-09-18 17:53:08.919059061 +0200 +++ CategoryPermissions.php    2007-09-18 17:46:06.800678311 +0200 @@ -7,7 +7,7 @@ * on category tags on pages. * * Author: Matthew Vernon - * Additional Contributions by Carsten Zimmermann + * Additional Contributions by Carsten Zimmermann and Richard Hartmann en:User:RichiH * * Licensed under GPL - use,change,enjoy * @@ -24,7 +24,7 @@ * $wgGroupPermissions['group_name']['Category:categoryname_edit']=true; * $wgGroupPermissions['group_name']['Category:categoryname_move']=true; * $wgGroupPermissions['group_name']['Category:categoryname_create']=true; * - * //allow access to all groups like this + * //allow access to all categories like this * $wgGroupPermissions['group_name']['*_read']=true; */

@@ -45,8 +45,9 @@ $wgExtensionCredits['parserhook'][] = array( 'name'=>'CategoryPermissions', 'author'=>'Matthew Vernon', 'url'=>'http://www.mediawiki.org/wiki/Extension:CategoryPermissions');

-//turn on debug messages by changing to 1 or true ... is there a better way to do this? -define("__debug_permissions",0); +//turn on debug messages by changing to 1 or true +// Remember to set $wgDebugLogFile as well. +define("__debug_permissions", 0);

function checkCategoryPermissions( $title, $user, $action, $result ) { @@ -60,19 +61,19 @@  //scan list of categories, if any if(is_array($parentCategories)) { -    foreach( $parentCategories as $category=>$dd) { +     if(__debug_permissions)wfDebug("checkCategoryPermissions on line ".__LINE__.": Trying action {$action} on category {$category} for user {$user->mName}\n"); if( $user->isAllowed("{$category}_{$action}") ) { -       if(__debug_permissions)wfDebug("checkCategoryPermissions:{$category}_{$action} allowed on {$title->mPrefixedText} to {$user->mName}\n"); +       if(__debug_permissions)wfDebug("checkCategoryPermissions on line ".__LINE__.": Category-action {$category}_{$action} ALLOWED on {$title->mPrefixedText} to user {$user->mName}\n"); $user_allowed=true; }      else {        if(in_array($category,$wgCategoryExclusive)) { //if category is in $wgCategoryExclusive then deny anyone who doesn't have explicit access -         if(__debug_permissions)wfDebug("checkCategoryPermissions:{$category}_{$action} DENIED on {$title->mPrefixedText} to {$user->mName} : Exlcusive Category\n"); +         if(__debug_permissions)wfDebug("checkCategoryPermissions on line ".__LINE__.": Category-action {$category}_{$action} DENIED on {$title->mPrefixedText} to user {$user->mName}: Exclusive Category\n"); $result=false; return false; } @@ -86,32 +87,34 @@

//always allow groups with *_read etc. regardless of categories + if(__debug_permissions)wfDebug("checkCategoryPermissions on line ".__LINE__.": Trying action {$action} on * (all categories for user {$user->mName}\n");   if( $user->isAllowed("*_{$action}") )   { -    if(__debug_permissions)wfDebug("checkCategoryPermissions:{$action} allowed on {$title->mPrefixedText} to {$user->mName} : AlwaysAllow\n"); +    if(__debug_permissions)wfDebug("checkCategoryPermissions on line ".__LINE__.": Action {$action} ALLOWED on {$title->mPrefixedText} to user {$user->mName}: AlwaysAllow\n");     return null;   }

+ if(__debug_permissions)wfDebug("checkCategoryPermissions on line ".__LINE__.": Trying action {$action} on {$title->mPrefixedText} for user {$user->mName}: No categories\n"); if(!($parentCategories)) {    //handle special case of no categories //default action is based on wgGroupDefaultAllow if($wgGroupDefaultAllow) { -     if(__debug_permissions)wfDebug("checkCategoryPermissions:{$action} allowed on {$title->mPrefixedText} to {$user->mName} : No Categories\n"); +     if(__debug_permissions)wfDebug("checkCategoryPermissions on line ".__LINE__.": Action {$action} ALLOWED on {$title->mPrefixedText} to user {$user->mName}: No categories\n"); return null; }    else { -     if(__debug_permissions)wfDebug("checkCategoryPermissions:{$action} DENIED on {$title->mPrefixedText} to {$user->mName} : No Categories\n"); +     if(__debug_permissions)wfDebug("checkCategoryPermissions on line ".__LINE__.": Action {$action} DENIED on {$title->mPrefixedText} to user {$user->mName}: No categories\n"); $result=false; return false; }  }

//default action=deny - if(__debug_permissions)wfDebug("checkCategoryPermissions:{$action} DENIED on {$title->mPrefixedText} to {$user->mName}\n"); + if(__debug_permissions)wfDebug("checkCategoryPermissions on line ".__LINE__.": Action {$action} DENIED on {$title->mPrefixedText} to user {$user->mName}: Default action\n"); $result=false; return false; }

Full file
<?php /* * Custom Permissions Scheme using Categories * based on Extension:NamespacePermissions by Petr Andreev * * Provides separate permissions for each action (read,edit,create,move) based * on category tags on pages. * * Author: Matthew Vernon * Additional Contributions by Carsten Zimmermann and Richard Hartmann en:User:RichiH * * Licensed under GPL - use,change,enjoy * * Usage: * * require_once('extensions/CategoryPermissions.php'); * $wgCategoryExclusive=array("Category:cat_name","Category:cat2_name");//deny acces to these categories for anyone not in the group * $wgGroupAlwaysAllow=''; //set a group name to ALWAYS allow access to this group * $wgGroupDefaultAllow=true; //set to true to allow everyone access to pages without a category * * * //add groups to category permissions by: * $wgGroupPermissions['group_name']['Category:categoryname_read']=true; * $wgGroupPermissions['group_name']['Category:categoryname_edit']=true; * $wgGroupPermissions['group_name']['Category:categoryname_move']=true; * $wgGroupPermissions['group_name']['Category:categoryname_create']=true; * * //allow access to all categories like this * $wgGroupPermissions['group_name']['*_read']=true; */

//set up hook $wgExtensionFunctions[] = "wfCategoryPermissions";

function wfCategoryPermissions { global $wgHooks;

// use the userCan hook to check permissions $wgHooks[ 'userCan' ][] = 'checkCategoryPermissions'; }

//register extension $wgExtensionCredits['parserhook'][] = array( 'name'=>'CategoryPermissions', 'author'=>'Matthew Vernon', 'url'=>'http://www.mediawiki.org/wiki/Extension:CategoryPermissions');

//turn on debug messages by changing to 1 or true // Remember to set $wgDebugLogFile as well. define("__debug_permissions", 0);

function checkCategoryPermissions( $title, $user, $action, $result ) { global $wgGroupDefaultAllow, $wgGroupPermissions, $wgCategoryExclusive;

$user_allowed=false;

//get categories for this page $parentCategories=$title->getParentCategories;

//scan list of categories, if any if(is_array($parentCategories)) {   foreach( $parentCategories as $category=>$dd) {     if(__debug_permissions)wfDebug("checkCategoryPermissions on line ".__LINE__.": Trying action {$action} on category {$category} for user {$user->mName}\n"); if( $user->isAllowed("{$category}_{$action}") ) {       if(__debug_permissions)wfDebug("checkCategoryPermissions on line ".__LINE__.": Category-action {$category}_{$action} ALLOWED on {$title->mPrefixedText} to user {$user->mName}\n"); $user_allowed=true; }     else {       if(in_array($category,$wgCategoryExclusive)) { //if category is in $wgCategoryExclusive then deny anyone who doesn't have explicit access if(__debug_permissions)wfDebug("checkCategoryPermissions on line ".__LINE__.": Category-action {$category}_{$action} DENIED on {$title->mPrefixedText} to user {$user->mName}: Exclusive Category\n"); $result=false; return false; }     }    }//foreach( $parentCategories as $category=>$dd) }//if($parentCategories)

//if we are here, then no exclusive categories have rejected us if($user_allowed==true) {   return null;  }

//always allow groups with *_read etc. regardless of categories if(__debug_permissions)wfDebug("checkCategoryPermissions on line ".__LINE__.": Trying action {$action} on * (all categories for user {$user->mName}\n"); if( $user->isAllowed("*_{$action}") )  {    if(__debug_permissions)wfDebug("checkCategoryPermissions on line ".__LINE__.": Action {$action} ALLOWED on {$title->mPrefixedText} to user {$user->mName}: AlwaysAllow\n");    return null;  }

if(__debug_permissions)wfDebug("checkCategoryPermissions on line ".__LINE__.": Trying action {$action} on {$title->mPrefixedText} for user {$user->mName}: No categories\n"); if(!($parentCategories)) {   //handle special case of no categories //default action is based on wgGroupDefaultAllow if($wgGroupDefaultAllow) {     if(__debug_permissions)wfDebug("checkCategoryPermissions on line ".__LINE__.": Action {$action} ALLOWED on {$title->mPrefixedText} to user {$user->mName}: No categories\n"); return null; }   else {     if(__debug_permissions)wfDebug("checkCategoryPermissions on line ".__LINE__.": Action {$action} DENIED on {$title->mPrefixedText} to user {$user->mName}: No categories\n"); $result=false; return false; } }

//default action=deny if(__debug_permissions)wfDebug("checkCategoryPermissions on line ".__LINE__.": Action {$action} DENIED on {$title->mPrefixedText} to user {$user->mName}: Default action\n"); $result=false; return false; }

?>

Version question
You say GPL, but I doubt you want GPLv1. Do you mean GPLv2[.1] [or later] or GPLv3 [or later]? -- RichiH 16:03, 18 September 2007 (UTC)

Extension Bug (1.11)
In trying to set up this extension, I received this error in MediaWiki 1.11: how can I rectify this? When this extension works for me, it will be enormously helpful, thank you!

Error: "Detected bug in an extension! Hook checkCategoryPermissions failed to return a value; should return true to continue hook processing or false to abort." --jaemarr