Extension:Permission
From MediaWiki.org
|
|
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 |
|
Permission Release status: experimental |
|
|---|---|
| Implementation | User rights |
| Description | Extends native MediaWiki protection to allow article's viewability to be restricted |
| Author(s) | Alexandre Porto da Silva (Alexandre PortoTalk) |
| Last version | 11.06.30 alpha (2011-06-30) |
| MediaWiki | 1.17+ |
| License | GPL |
| Download | copy/paste |
|
Check usage (experimental) |
|
Contents |
[edit] Install
Copy & paste:
Include on LocalSettings.php:
require_once( "$IP/extensions/Permission/Permission.php" );
[edit] 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_SLAVE ); $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; } }
[edit] 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';
[edit] 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 ''; } }
[edit] 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".' );
