Extension:Permission

From MediaWiki.org
Jump to: navigation, search
MediaWiki extensions manual - list
Crystal Clear action run.png
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".'
);
Personal tools
Namespaces

Variants
Actions
Navigation
Support
Download
Development
Communication
Print/export
Toolbox