Regional-training LocalSettings.php: Difference between revisions

From regional-training
No edit summary
No edit summary
 
Line 7: Line 7:


<pre>
<pre>
# RBH - block out most of it for anon users
<?php
wfLoadExtension( 'HideSidebar' );
$wgEnableSidebarCache = false;


#RBH
// History:
wfLoadExtension( 'RSS' );
// 2023-10-14 Ralph Holland - corrected the checks for access to pages with non-private categories.
// 2023-06-08 Ralph Holland - include Special:Search  an Special:RecentChanges in white-listing.
// 2022-09-20 Ralph Holland - include Special:Categories white-listing.
// 2022-09-20 Ralph Holland - now permit quite a few more Special: page titles.
// 2022-09-20 Ralph Holland - fixed a comparison typo after r
// 2022-09-19 Ralph Holland - made category:edit: markings inclusive while excluding users that have no corresponding marking for the edit action.
// 2022-09-18 Ralph Holland - Now User:category is not completely exclusive, other User:category can be put on page to share with another users
//                            public is not public when a private has been placed on the page or an exclusive user:category is on the page.
//       white-listed pages cannot override an exclusive category denying access
// 2022-08-15 Ralph Holland - made pages without a category not public, made private exclusive, implemented category:User:<name> as exclusive page access
//


# RBH Page Access authorisation
if ( !defined( 'MEDIAWIKI' ) ) {
require_once "$IP/extensions/rabcg/RestrictAccessByCategoryAndGroup.php";
        die( 'Not a valid entry point.' );
}
 
$wgExtensionCredits['parserhook'][] = array(
        'name' => 'Restrict access by category and group',
        'author' => 'Andrés Orencio Ramirez Perez & Ralph Holland',
        'url' => 'https://www.mediawiki.org/wiki/Extension:Restrict_access_by_category_and_group',
        'description' => 'Allows to restrict access to pages by users groups and page categories: See [[Security]]',
        'version' => '3.1.0-adapted-by-ralph-holland'
);
 
$wgHooks['userCan'][] = 'restrictAccessByCategoryAndGroup';


# SECURITY prevent public privleges  (anonymous user)
function debug( $a ) {
$wgReadPermissions['*']['read'] = false;
for ($i =0; $i < 35; $i++) {
$wgGroupPermissions['*']['edit'] = false;
print "&nbsp;";
$wgGroupPermisisons['*']['createpage'] = false;
}
$wgGroupPermissions['*']['createaccount'] = false;
print "$a";
}


# install Page Access authorisation extension
function restrictAccessByCategoryAndGroup( $title, $user, $action, $result ) {
require_once "$IP/extensions/rabcg/RestrictAccessByCategoryAndGroup.php";


# sysop can create accounts
# debug("Ralph is debugging access controls ATM title=".$title." user=".$user." action=".$action."<br/>");
$wgGroupPermissions['sysop']['createaccount'] = true;


# exclusive private category - where only user's with private group can access, or the sysop
global $wgGroupPermissions;
$wgGroupPermissions['private']['*'] = false;
global $wgWhitelistRead;
$wgGroupPermissions['private']['private'] = true;
global $wgLang;
global $wgHooks;
global $wgContLang;
global $wgVersion;


# private category that users with group:lesson may access, or sysop
// strip the prefix of Talk etc off
$wgGroupPermissions['lesson']['*'] = false;
$posn = strpos( $title,":");
$wgGroupPermissions['lesson']['private'] = true;
$prefix = $posn > -1 ? substr( $title, 0, $posn ) : "";
$page = $posn > -1 ? substr( $title, $posn+1 ) : $title;


# private category that users with group:student may access, or sysop
# debug( "RBH is debugging page=".$page." prefix=".$prefix." action=".$action." user=".$user."</br>" );
$wgGroupPermissions['student']['*'] = false;
$wgGroupPermissions['student']['private'] = true;


# private category that users with group:projects may access, or sysop
//These are special pages to be white-listed
$wgGroupPermissions['projects']['*'] = false;
if (strpos($title,'Special:')===0) {
$wgGroupPermissions['projects']['private'] = true;


# private category that users with group:trainer may access, or sysop
# debug('...special...<br/>');
$wgGroupPermissions['trainer']['*'] = false;
$wgGroupPermissions['trainer']['private'] = true;


# the above private categories will show up as group privileges that may be assigned to users.
        switch($title) {
# you may define nearly any group you may required (except for predefined names such as user)
case 'Special:Login':
$wgGroupPermissions['sysop']['deletelogentry'] = true;
case 'Special:Logout':
$wgGroupPermissions['sysop']['deleterevision'] = true;
        case 'Special:UserLogin':
        case 'Special:UserLogout':
        case 'Special:Badtitle':
        case 'Special:Random':
        // case 'Special:RecentChanges':
case 'Special:Version':
case 'Special:Categories':
case 'Special:AllPages':
case 'Special:Search': // added 2023-06-08
case 'Special:RecentChanges';  // added 2023-06-08
                # debug( "*** $title permitted<br/>" );
                return true;


$wgShowExceptionDetails = true;
default:
$wgRightsPage = 'wiki rights';
if ( strpos($title,"Special:WhatLinksHere")===0 ) { // || strpos($title,"Special:RecentChangesLinked")===0 ) {
# debug("*** $title must be permitted<br/>");
return true;
}
break;
}
        }
if ( in_array('sysop',$user->getGroups()) ) {
# debug( "*** sysop can see all pages<br/>' );
return true;
}


# prevent anonymous edit and view source
//Build up System categories from the title's categories
function preventAnonymousEditHistory( $output,$article, $title, $user, $request, $wiki ) {
$systemCategory = array();
        if ( !user || $user->isAnon() ) {
 
                $action = $request->getVal('action');
// get the users groups
                switch($action) {
$userGroups = $user->getGroups();
                case 'edit':
$hasUserCategory = false;
                        if($title == 'Talk:Main') {
$userCategoryMatched = false;
                            return true;
 
                        }
// now check the page categories against the system groups
                        return false;
$publicPage = false;
$privatePage = false;
$editUserMatched = false;
$editGroupMatched = false;
 
// now process page categories
foreach ( array_change_key_case( $title->getParentCategories(), CASE_LOWER ) as $key => $value ) {
$formattedKey = substr( $key, ( strpos( $key, ":" ) + 1 ) );
 
# debug( "checking category:".$formattedKey."=>[$formattedKey]"."<br/>" );


                case 'history':
// check for the exclusive category public
                         return false;
if ( $formattedKey=='public' ) {
$publicPage = true;
}
// check for the exclusive category private
elseif ( $formattedKey==='private' ) {
// check that the user holds private group
if (! in_array($formattedKey,$userGroups) ) {
$privatePage = true;
}
}
// check for the exclusive category user:*
elseif ( strpos( $formattedKey, 'user:' ) === 0  ) {
// these are special user categories e.g. [[User:Ralph]]
$hasUserCategory = true;
$name = 'user:'.strtolower($user->getName());
# debug( $formattedKey.' '.$name.'<-name<br/>' );
if ( $name && $formattedKey===$name ) {
debug( '**** user permitted by user category'.$formattedKey.' '.'allowed<br/>' );
$userCategoryMatched = true;
}
}
              elseif ( $formattedKey === 'edit:*' ){
                        # debug('... matched '.$formattedKey.'<br/>');
                        $hasEdit = true;
                         $editGroupMatched = true;
                 }
                 }
elseif ( strpos( $formattedKey, 'edit:user:' ) === 0 ) {
// check if user is not permitted to edit
if ($action === 'edit') {
$hasEdit = true;
$name = 'edit:user:'.strtolower($user->getName());
if ($name === $formattedKey) {
# debug('*** edit user matched'.$name,'<br/>');
$editUserMatched = true;
}
}
}
elseif ( strpos( $formattedKey, 'edit:' ) === 0 ) {
//  check is user does not have groups to permit edit
if ($action === 'edit') {
$hasEdit = true;
$group = substr( $formattedKey, ( strpos( $formattedKey, ":" ) + 1 ) );
if ( in_array($group,$userGroups) ) {
# debug('*** edit group matched '.$group.'<br/>');
$editGroupMatched = true;
}
}
}
else {
// build up non-exclusive categories
$systemCategory[ $formattedKey ] = $value;
}
}
// check if category:edit: marking found
if ($hasEdit) {
#debug('*** found category:edit:...<br/>');
if ($editGroupMatched) {
#debug('*** matched a category:edit:group marking<br/>');
return true;
}
else if ($editUserMatched) {
#debug('*** matched a category:edit:user: marking<br/>');
return true;
}
else {
#debug('--- denied user by category:edit marking<br/>');
return false;
}
}
// check if page marked with the user:category
if ($userCategoryMatched) {
# debug('*** user allowed by user:category');
return true;
}
// check if user is excluded by page marked private
if ($privatePage) {
# debug('--- denied because the page was marked private');
return false;
}
// check if user is excluded by another user:category
if ($hasUserCategory) {
# debug('--- denied by user:category');
return false;
}
// check if page was marked as public
if ($publicPage) {
# debug('*** permitted by public page');
return true;
}


        // honour the existing white-list mechanism
        if ( $wgWhitelistRead && count( $wgWhitelistRead ) != 0 ) {
                if ( in_array( $title, $wgWhitelistRead ) ) {
                        # debug("*** white listed" );
                        return true;
                }
         }
         }
        return true;
 
// check if page has any remaining categories
if ( count( $systemCategory ) === 0 ) {
if ( count($userGroups) > 0 ) {
# debug('*** logged in users can access pages without categories');
return true;
}
# debug('-- anonymous user cannot access pages without categories');
return false;
} else {
// check remaining page categories for inclusive private permissions
// users must be logged in to process remaining category markings
if ( count($userGroups) != 0) {
 
# check is user has any group that permits private category access
foreach ( $wgGroupPermissions as $key => $value ) {
            # debug( 'group->'.$key.' '.$action.' '.$title.'<br/>' );
 
              //  ... if the group permission is marked as 'private' then check ...
              if ( isset( $wgGroupPermissions[$key]['private'] ) ) {
                    // ... if page is marked with a category that corresponds to the private group
                    if ( array_key_exists( strtolower( str_replace( " ", "_", $key ) ), $systemCategory ) ) {
                        // ... permit access if user is assigned the group
                        if ( in_array( $key, $userGroups) ) {
                            # debug( '*** permitted user holds the group '.$key );
                            return true;
                      }
                    }
              }
            }
// check if any category on page is private
                        foreach ( $systemCategory as $key => $value ) {
                                #debug('****check '.$key.' val '.$value);
                                if ( isset( $wgGroupPermission[$key]['private'] ) ) {
// not permitted because checks above did not grant
                                        return false;
                                }
                        }
// otherwise allow access to page that does not contain any private categories
                        return true;
 
}
}
# debug("--- user does not hold an appropriate private category and page is not marked for their access");
return false;
}
}
$wgHooks['MediaWikiPerformAction'][] = 'preventAnonymousEditHistory';
</pre>
</pre>



Latest revision as of 13:10, 9 January 2024

The LocalSettings.php customisations:


The http://regional-training.org wiki has the following LocalSettings.php customisations for category:Access Control:

<?php

// History:
// 2023-10-14 Ralph Holland - corrected the checks for access to pages with non-private categories.
// 2023-06-08 Ralph Holland - include Special:Search  an Special:RecentChanges in white-listing.
// 2022-09-20 Ralph Holland - include Special:Categories white-listing.
// 2022-09-20 Ralph Holland - now permit quite a few more Special: page titles.
// 2022-09-20 Ralph Holland - fixed a comparison typo after r
// 2022-09-19 Ralph Holland - made category:edit: markings inclusive while excluding users that have no corresponding marking for the edit action.
// 2022-09-18 Ralph Holland - Now User:category is not completely exclusive, other User:category can be put on page to share with another users
//                            public is not public when a private has been placed on the page or an exclusive user:category is on the page.
//			      white-listed pages cannot override an exclusive category denying access
// 2022-08-15 Ralph Holland - made pages without a category not public, made private exclusive, implemented category:User:<name> as exclusive page access
//

if ( !defined( 'MEDIAWIKI' ) ) {
        die( 'Not a valid entry point.' );
}

$wgExtensionCredits['parserhook'][] = array(
        'name' => 'Restrict access by category and group',
        'author' => 'Andrés Orencio Ramirez Perez & Ralph Holland',
        'url' => 'https://www.mediawiki.org/wiki/Extension:Restrict_access_by_category_and_group',
        'description' => 'Allows to restrict access to pages by users groups and page categories: See [[Security]]',
        'version' => '3.1.0-adapted-by-ralph-holland'
);

$wgHooks['userCan'][] = 'restrictAccessByCategoryAndGroup';

function debug( $a ) {
	for ($i =0; $i < 35; $i++) {
		print " ";
	}
	print "$a";
}

function restrictAccessByCategoryAndGroup( $title, $user, $action, $result ) {

	# debug("Ralph is debugging access controls ATM title=".$title." user=".$user." action=".$action."<br/>");

	global $wgGroupPermissions;
	global $wgWhitelistRead;
	global $wgLang;
	global $wgHooks;
	global $wgContLang;
	global $wgVersion;

	// strip the prefix of Talk etc off
	$posn = strpos( $title,":");
	$prefix = $posn > -1 ? substr( $title, 0, $posn ) : "";
	$page = $posn > -1 ? substr( $title, $posn+1 ) : $title;

	# debug( "RBH is debugging page=".$page." prefix=".$prefix." action=".$action." user=".$user."</br>" );

	//These are special pages to be white-listed 
	if (strpos($title,'Special:')===0) {

		# debug('...special...<br/>');

        	switch($title) {
		case 'Special:Login':
		case 'Special:Logout':
        	case 'Special:UserLogin':
        	case 'Special:UserLogout':
        	case 'Special:Badtitle':
        	case 'Special:Random':
        	// case 'Special:RecentChanges':
		case 'Special:Version':
		case 'Special:Categories':
		case 'Special:AllPages':
		case 'Special:Search':		// added 2023-06-08	
		case 'Special:RecentChanges';  // added 2023-06-08
                	# debug( "*** $title permitted<br/>" );
                	return true;

		default:
			if ( strpos($title,"Special:WhatLinksHere")===0 ) { // || strpos($title,"Special:RecentChangesLinked")===0 ) {
				# debug("*** $title must be permitted<br/>");
				return true;
			}
			break;
		}
        }
	
	if ( in_array('sysop',$user->getGroups()) ) {
		# debug( "*** sysop can see all pages<br/>' );
		return true;
	}

	//Build up System categories from the title's categories
	$systemCategory = array();

	// get the users groups
	$userGroups = $user->getGroups();
	$hasUserCategory = false;
	$userCategoryMatched = false;

	// now check the page categories against the system groups
	$publicPage = false;
	$privatePage = false;
	$editUserMatched = false;
	$editGroupMatched = false;

	// now process page categories
	foreach ( array_change_key_case( $title->getParentCategories(), CASE_LOWER ) as $key => $value ) {
		$formattedKey = substr( $key, ( strpos( $key, ":" ) + 1 ) );

		# debug( "checking category:".$formattedKey."=>[$formattedKey]"."<br/>" );

		// check for the exclusive category public
		if ( $formattedKey=='public' ) {
			$publicPage = true;
		}
		// check for the exclusive category private
		elseif ( $formattedKey==='private' ) {
			// check that the user holds private group
			if (! in_array($formattedKey,$userGroups) ) {
				$privatePage = true;
			}
		}
		// check for the exclusive category user:*
		elseif ( strpos( $formattedKey, 'user:' ) === 0  ) {
			// these are special user categories e.g. [[User:Ralph]]
			$hasUserCategory = true;
			$name = 'user:'.strtolower($user->getName());
			# debug( $formattedKey.' '.$name.'<-name<br/>' );
			if ( $name && $formattedKey===$name ) {
				debug( '**** user permitted by user category'.$formattedKey.' '.'allowed<br/>' );
				$userCategoryMatched = true;
			}
		}
              	elseif ( $formattedKey === 'edit:*' ){
                        # debug('... matched '.$formattedKey.'<br/>');
                        $hasEdit = true;
                        $editGroupMatched = true;
                }
		elseif ( strpos( $formattedKey, 'edit:user:' ) === 0 ) {
			// check if user is not permitted to edit
			if ($action === 'edit') {
				$hasEdit = true;
				$name = 'edit:user:'.strtolower($user->getName());
				if ($name === $formattedKey) {
					# debug('*** edit user matched'.$name,'<br/>');
					$editUserMatched = true;
				}
			}	
		}
		elseif ( strpos( $formattedKey, 'edit:' ) === 0 ) {
			//  check is user does not have groups to permit edit
			if ($action === 'edit') {
				$hasEdit = true;
				$group = substr( $formattedKey, ( strpos( $formattedKey, ":" ) + 1 ) );
				if ( in_array($group,$userGroups) ) {
					# debug('*** edit group matched '.$group.'<br/>');
					$editGroupMatched = true;
				}
			}
		}
		else {
			// build up non-exclusive categories
			$systemCategory[ $formattedKey ] = $value;
		}
	}

	// check if category:edit: marking found
	if ($hasEdit) {
		#debug('*** found category:edit:...<br/>');
		if ($editGroupMatched) {
			#debug('*** matched a category:edit:group marking<br/>');
			return true;
		}
		else if ($editUserMatched) {
			#debug('*** matched a category:edit:user: marking<br/>');
			return true;
		}
		else {
			#debug('--- denied user by category:edit marking<br/>');
			return false;
		}
	}

	// check if page marked with the user:category
	if ($userCategoryMatched) {
		# debug('*** user allowed by user:category');
		return true;
	}

	// check if user is excluded by page marked private
	if ($privatePage) {
		# debug('--- denied because the page was marked private');
		return false;
	}

	// check if user is excluded by another user:category
	if ($hasUserCategory) {
		# debug('--- denied by user:category');
		return false;
	}

	// check if page was marked as public
	if ($publicPage) {
		# debug('*** permitted by public page');
		return true;
	}

        // honour the existing white-list mechanism
        if ( $wgWhitelistRead && count( $wgWhitelistRead ) != 0 ) {
                if ( in_array( $title, $wgWhitelistRead ) ) {
                        # debug("*** white listed" );
                        return true;
                }
        }

	// check if page has any remaining categories
	if ( count( $systemCategory ) === 0 ) {
		if ( count($userGroups) > 0 ) {
			# debug('*** logged in users can access pages without categories');
			return true;
		}
		# debug('-- anonymous user cannot access pages without categories');
		return false;
	} else {
		// check remaining page categories for inclusive private permissions
		// users must be logged in to process remaining category markings
		if ( count($userGroups) != 0) {

			# check is user has any group that permits private category access
			foreach ( $wgGroupPermissions as $key => $value ) {
            			# debug( 'group->'.$key.' '.$action.' '.$title.'<br/>' );

       		         	//  ... if the group permission is marked as 'private' then check ...
               		 	if ( isset( $wgGroupPermissions[$key]['private'] ) ) {
		
                    			// ... if page is marked with a category that corresponds to the private group 
                    			if ( array_key_exists( strtolower( str_replace( " ", "_", $key ) ), $systemCategory ) ) {
                        			// ... permit access if user is assigned the group
                        			if ( in_array( $key, $userGroups) ) {
                            				# debug( '*** permitted user holds the group '.$key );
                            				return true;
                       				}
                    			}
               		 	}
            		}
			
			// check if any category on page is private
                        foreach ( $systemCategory as $key => $value ) {
                                #debug('****check '.$key.' val '.$value);
                                if ( isset( $wgGroupPermission[$key]['private'] ) ) {
					// not permitted because checks above did not grant
                                        return false;
                                }
                        }
			// otherwise allow access to page that does not contain any private categories
                        return true;

		}
	}
	# debug("--- user does not hold an appropriate private category and page is not marked for their access");
	return false;
}

summary

These privileges may be assigned to a selected user from the Special:UserRights page, and are accessible in the php hook via $user->getGroups().

.

Categories may be assigned to a page, where the category may be associated by name to group.

Matches are performed case-insensitively, and when they occur, it means that the page is subject to fine-grained category:Access Control that is implemented by the Regional-training RestrictAccessByCategoryAndGroup.php extension.