Extension:Permission
Appearance
From mediawiki.org
MediaWiki was not designed to support per-page or partial-page access restrictions. If you require this level of control, you are strongly advised to use a content management system that supports it natively.
Patches or third-party extensions claiming to provide access control, when in use with MediaWiki, may not work in all cases, potentially exposing confidential data. Use them at your own risk. Neither the MediaWiki developers nor the Wikimedia Foundation are responsible for any data leaks that may result. This message is added to all extensions of this nature and may not reflect the actual security status of this extension.
For more information, see Security issues with authorization extensions .This extension stores its source code on a editable wiki page rather than in a code repository. As a result, this code may be maliciously altered.
It may contain security vulnerabilities, and will not receive localisation updates from translatewiki.net . Developers are strongly encouraged to host their code in a code repository rather than a wiki page so that the extension can be properly maintained, reviewed, and kept secure.
It may contain security vulnerabilities, and will not receive localisation updates from translatewiki.net . Developers are strongly encouraged to host their code in a code repository rather than a wiki page so that the extension can be properly maintained, reviewed, and kept secure.
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 | Extends native MediaWiki protection to allow article's viewability to be restricted |
| Author(s) | Alexandre Porto da Silva (Alexandre Porto talk ) |
| Latest version | 11.06.30 alpha (2011年06月30日) |
| MediaWiki | 1.17+ |
| Licence | GNU General Public License 2.0 or later |
| Download | copy/paste |
Install
Copy & paste:
Include on LocalSettings.php:
require_once( "$IP/extensions/Permission/Permission.php" );
Permission.php
<?php # Credits: http://www.mediawiki.org/wiki/Manual:Tag_extensions#How_do_I_get_my_extension_to_show_up_on_Special:Version.3F /** * Permission - permissions for reading and editing based on categories. * * To activate this extension, add the following into your LocalSettings.php file: * require_once( "$IP/extensions/Permission/Permission.php" ); * * @ingroup Extensions * @author Alexandre Porto da Silva * @link http://www.mediawiki.org/wiki/Extension:Permission * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later */ /** * Protect against register_globals vulnerabilities. * This line must be present before any global variable is referenced. */ if( !defined( 'MEDIAWIKI' ) ) { echo( "This is an extension to the MediaWiki package and cannot be run standalone.\n" ); die( -1 ); } define( 'PERMISSION_VERSION', '11.06.26 alpha' ); define( 'PERMISSION_TIME', 10 ); // time in seconds for cache: "Permission.session.php" $wgPermissionHidden = true; # Extension credits that will show up on Special:Version $wgExtensionCredits['permission'][] = array( 'path' => __FILE__, 'name' => 'Permission', 'version' => PERMISSION_VERSION, 'author' => 'Alexandre Porto da Silva', 'url' => 'http://www.mediawiki.org/wiki/Extension:Permission', 'descriptionmsg' => 'permission-desc', 'description' => 'permission' ); # Manual: http://www.mediawiki.org/wiki/Manual:Special_pages#The_Body_File $dir = dirname(__FILE__); $wgExtensionMessagesFiles['Permission'] = "$dir/Permission.i18n.php"; require_once( "$dir/Permission.session.php" ); require_once( "$dir/Permission.hooks.php" ); class Permission { private static function getRequiredCategories( $parents ) { $stack = array(); foreach ( $parents as $parent => $current ) { $token = substr( $parent, 0, 1 ); if ( preg_match( "/^[$@][^:]+:/i", $parent ) ) { $stack[$parent] = $token; } else { if ( $current && $token == '@' ) { // @acl $stack = array_merge( $stack, self::getRequiredCategories( $current ) ); } } } return $stack; } //start/adapted from "$IP/includes/Title.php" private static function getParentCategories( $title ) { #adapted $titlekey = $title->getArticleID(); #adapted $dbr = wfGetDB( DB_REPLICA ); $categorylinks = $dbr->tableName( 'categorylinks' ); # NEW SQL $sql = "SELECT * FROM $categorylinks" . " WHERE cl_from=$titlekey" . " AND cl_to REGEXP '^([$][^:]+:|@)'" #added . " ORDER BY cl_sortkey"; $res = $dbr->query( $sql ); $data = array(); if ( $dbr->numRows( $res ) > 0 ) { foreach ( $res as $row ) { $data[$row->cl_to] = 1; #changed } } return $data; } private static function getParentCategoryTree( $title, $children = array() ) { #adapted $stack = array(); $parents = self::getParentCategories( $title ); #adapted if ( $parents ) { foreach ( $parents as $parent => $current ) { if ( array_key_exists( $parent, $children ) ) { # Circular reference $stack[$parent] = array(); } else { $nt = Title::makeTitle( NS_CATEGORY, $parent ); #changed if ( $nt ) { $stack[$parent] = self::getParentCategoryTree( $nt, $children + array( $parent => 1 ) ); #adapted } } } } return $stack; } //end/adapted from "$IP/includes/Title.php" private static function getRequired( &$title, &$categories ) { $categories = $stack = array(); while ( true ) { $parents = self::getParentCategoryTree( $title ); $categories = self::getRequiredCategories( $parents ); if ( $categories || !$title->isRedirect() ) { break; } # Circular reference $titleDBkey = $title->getDBkey(); if ( isset( $stack[$titleDBkey] ) ) { break; } else { $stack[$titleDBkey] = 1; } $article = new Article( $title ); $title = $article->getRedirectTarget(); if ( !$title instanceOf Title || !$title->isValidRedirectTarget() ) { break; } } if ( !$categories ) { return false; } $required = array(); if ( in_array( '$', $categories ) ) { $required['$'] = '$'; } if ( in_array( '@', $categories ) ) { $required['@'] = '@'; } return $required; } private static function userCan( $categories, $action ) { $userCan = PermissionSession::userCan(); if ( $userCan ) { $pattern = "/^[$action]($userCan)$/i"; foreach ( $categories as $category => $current ) { if ( preg_match( $pattern, $category ) ) { return true; } } } return false; } private static function getPermission( &$title, $action ) { $required = self::getRequired( $title, $categories ); if ( !$required ) { return true; } if ( $action == '$' && !in_array( '$', $required ) ) { return 1; } $action = in_array( '@', $required ) ? ( $action == '@' ? '@' : '$@' ) : '$'; return self::userCan( $categories, $action ) ? 1 : 0; } public static function check( $title, $action, $session = false ) { if ( $action == 'purge' || $action == 'unwatch' || !$title instanceof Title || $title->isSpecialPage() ) { return true; } # http://www.mediawiki.org/wiki/Action#Actions $actions = array( '$' => '$', // $Secret, private (read) 'history' => '$', 'markpatrolled' => '$', 'print' => '$', 'raw' => '$', 'raw' => '$', 'read' => '$', 'view' => '$', 'watch' => '$', '@' => '@', // @author, alter (edit) 'delete' => '@', 'deletetrackback' => '@', 'edit' => '@', 'editredlink' => '@', 'protect' => '@', 'unprotect' => '@', 'revert' => '@', 'rollback' => '@', 'submit' => '@', ); $action = isset( $actions[$action] ) ? $actions[$action] : '@'; $permission = true; $titlekey = $title->getArticleID(); if ( $titlekey ) { if ( $session ) { $permission = PermissionSession::getPermission( $titlekey, $action ); if ( $permission !== null ) { return $permission; } } $permission = self::getPermission( $title, $action ); } switch ( $permission ) { case 0: $permission = false; break; case 1: $permission = true; break; default: $titleDBkey = $title->getDBkey(); if ( $i = strrpos( $titleDBkey, '/' ) ) { $titleDBkey = substr( $titleDBkey, 0, $i ); $ns = $title->getNamespace(); $nt = Title::makeTitle( $ns, $titleDBkey ); $permission = self::check( $nt, $action, $session ); } } if ( $titlekey && $session ) { PermissionSession::setPermission( $titlekey, $action, $permission ); } return $permission; } private static function checkRedirect( $text ) { # private static function checkRedirect( $parserOutput ) { # $out = new OutputPage; # $out->addParserOutput( $parserOutput ); # $redirect = $out->getRedirect(); # private static function checkRedirect( $title ) { # $redirect = $title->isRedirect(); # $article = new Article( $title ); # $title = $article->getRedirectTarget(); # FIXME: redirect to page (target) without permission # TODO: not allow redirect to page without permission return true; } public static function articleSave( $title, $user, &$text ) { if( !self::check( $title, 'edit', false ) ) { return false; } $parser = new Parser; $options = ParserOptions::newFromUser( $user ); $out = $parser->parse( $text, $title, $options, true, true ); $parents = $out->getCategories(); if ( !$parents ) { return self::checkRedirect( $text ); } $stack = array(); foreach ( $parents as $parent => $current ) { if ( preg_match( "/^[$@][^:]+:/i", $parent ) ) { $stack[$parent] = array(); } else { if ( substr( $parent, 0, 1 ) == '@' ) { // @acl $nt = Title::makeTitle( NS_CATEGORY, $parent ); if ( $nt ) { $stack[$parent] = self::getParentCategoryTree( $nt ); } } } } $categories = self::getRequiredCategories( $stack ); if ( $categories ) { if ( !PermissionSession::userCan() ) { return false; } $required = in_array( '@', $categories ) ? '@' : '$'; if ( !self::userCan( $categories, $required ) ) { global $wgContLang; $categoryText = $wgContLang->getNSText( NS_CATEGORY ); $usr = wfMsg( 'permission-user' ); $text .= "\n[[$categoryText:$required$usr:{$user->getName()}]]"; } } return true; } public static function articleSaveComplete( $title ) { $categories = self::getParentCategories( $title ); if ( $categories ) { global $wgPermissionHidden; $text = wfMsg( 'permission-text' ); $summary = wfMsg( 'permission-summary' ); foreach ( $categories as $category => $current ) { $nt = Title::makeTitle( NS_CATEGORY, $category ); if ( !$nt->exists() ) { $hidden = $wgPermissionHidden && preg_match( "/^[$@][^:]+:.+$/i", $category ) ? "__HIDDENCAT__\n" : ''; $article = new Article( $nt ); $article->doEdit( $hidden . $text, $summary, EDIT_NEW ); } } } return true; } }
Permission.hooks.php
<?php # Hooks: http://www.mediawiki.org/wiki/Hooks class PermissionHooks { # userCan: public static function userCan( &$title, &$user, $action, &$result ) { return $result = Permission::check( $title, $action, true ); } # article: public static function beforeParserFetchTemplateAndtitle( $parser, &$title, &$skip, &$id ) { if ( !Permission::check( $title, 'read', false ) ) { $skip = true; return false; } return true; } public static function articleSave( &$article, &$user, &$text, &$summary, $minor, $watch, $sectionanchor, &$flags ) { return Permission::articleSave( $article->getTitle(), $user, $text ); } public static function articleSaveComplete( &$article, &$user, $text, $summary, $minoredit, $watchthis, $sectionanchor, &$flags, $revision, &$status, $baseRevId, &$redirect ) { return Permission::articleSaveComplete( $article->getTitle() ); } public static function watchArticle( &$user, &$article ) { return Permission::check( $article->getTitle(), 'read', false ); } # file: public static function imgAuthBeforeStream( $title, $path, $name, $result ) { if( !Permission::check( $title, 'read', false ) ) { $result = array( 'img-auth-accessdenied', 'img-auth-noread', $name ); return false; } return true; } # session: public static function userLoginComplete( &$user, &$inject_html ) { $usr = wfMsg( 'permission-user' ); $userName = $user->getName(); $permissions = "$usr:$userName"; $groups = $user->getGroups(); if ( in_array( 'sysop', $groups ) ) { $permissions .= '|sys:op'; } if ( in_array( 'bureaucrat', $groups ) ) { $permissions .= '|sys:bureaucrat'; } $vars = array(); wfRunHooks( 'PermissionLoginComplete', array( &$user, &$permissions, &$vars ) ); PermissionSession::userLogin( $userName, $permissions, $vars ); return true; } public static function userLogoutComplete( &$user, &$inject_html, $old_name ) { PermissionSession::userLogout(); return true; } } # userCan: $wgHooks['userCan'][] = 'PermissionHooks::userCan'; # article: $wgHooks['BeforeParserFetchTemplateAndtitle'][] = 'PermissionHooks::beforeParserFetchTemplateAndtitle'; $wgHooks['ArticleSave'][] = 'PermissionHooks::articleSave'; $wgHooks['ArticleSaveComplete'][] = 'PermissionHooks::articleSaveComplete'; $wgHooks['WatchArticle'][] = 'PermissionHooks::watchArticle'; # file: $wgHooks['ImgAuthBeforeStream'][] = 'PermissionHooks::imgAuthBeforeStream'; # session: $wgHooks['UserLoginComplete'][] = 'PermissionHooks::userLoginComplete'; $wgHooks['UserLogoutComplete'][] = 'PermissionHooks::userLogoutComplete';
Permission.session.php
<?php /* * Permission Extension for MediaWiki * Adapted from Collection: http://www.mediawiki.org/wiki/Extension:Collection * Copyright (C) PediaPress GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * Adapted by: Alexandre Porto da Silva * Kept under: GNU General Public License */ class PermissionSession { private static function hasSession() { if ( !session_id() ) { return false; } return isset( $_SESSION['wsPermission'] ); } private static function clearPermission() { $_SESSION['wsPermission'] = array(); } private static function startSession() { if ( session_id() == '' ) { wfSetupSession(); } self::clearPermission(); } public static function userLogin( $username, $permissions = '', $vars = array() ) { if ( !self::hasSession() ) { self::startSession(); } $_SESSION['wsPermission']['userName'] = $username; $_SESSION['wsPermission']['permissions'] = $permissions; $_SESSION['wsPermission']['var'] = $vars; } public static function userLogout() { if ( !self::hasSession() ) { return; } self::clearPermission(); } public static function userCan() { return self::hasSession() ? $_SESSION['wsPermission']['permissions'] : ''; } # permission public static function setPermission( $titlekey, $action, $data ) { if ( self::hasSession() ) { $_SESSION['wsPermission']['page'][$titlekey]['permission'][$action]['time'] = time(); $_SESSION['wsPermission']['page'][$titlekey]['permission'][$action]['data'] = $data; } return; } public static function getPermission( $titlekey, $action ) { if ( self::hasSession() && isset( $_SESSION['wsPermission']['page'][$titlekey]['permission'][$action] ) && ( time() - $_SESSION['wsPermission']['page'][$titlekey]['permission'][$action]['time'] ) < PERMISSION_TIME ) { return $_SESSION['wsPermission']['page'][$titlekey]['permission'][$action]['data']; } return null; } # var public static function setVar( $var, $data = '' ) { if ( $var && self::hasSession() ) { $_SESSION['wsPermission']['var'][$var] = $data; } return; } public static function getVar( $var ) { if ( $var && self::hasSession() && isset( $_SESSION['wsPermission']['var'][$var] ) ) { return $_SESSION['wsPermission']['var'][$var]; } return ''; } }
Permission.i18n.php
<?php # Manual: http://www.mediawiki.org/wiki/Manual:Special_pages#The_Messages.2FInternationalization_File $messages = array(); /* *** English *** */ $messages['en'] = array( 'permission' => 'Permission', 'permission-desc' => "Permissions for reading and editing based on categories.", 'permission-user' => 'user', # messages to create the category page: 'permission-text' => 'This [[Special:Categories|category]] contains articles of restricted access.', 'permission-summary' => 'Category page automatically created by extension "Permission".' ); /* *** Português (Brasil) *** */ $messages['pt-br'] = array( 'permission' => 'Permissão', 'permission-desc' => 'Permissões para leitura e edição baseado em categorias.', 'permission-user' => 'user', # mensagens para criar a página da categoria: 'permission-text' => 'Esta [[Especial:Categorias|categoria]] reúne artigos de acesso restrito.', 'permission-summary' => 'Página da categoria criada automaticamente pela extensão "Permission".' );