Extension:SimpleSecurity

The general problem with implementing a proper security solution in MediaWiki (giving rise to the scary looking warning box shown above) is that although most of the actions one can perform on articles can be restricted easily, the ability to read content cannot be easily restricted on a per-title basis. The reason reading is difficult to restrict is because it's an operation which is not just performed via one action, but rather that many different actions, special-pages and extensions access article content and display it in diverse ways. To make matters worse, many of these diverse means of article access are done by querying the database directly rather than going via the Article class.

See the technical details section below for more details on this problem and the solution that SimpleSecurity version 4 uses to overcome it. The solution is disabled by default (see configuration settings below), it is still functional without it but there are then simple means available to view restricted content such as by exporting it or transcluding it in another page.

If you're using a version of MediaWiki older than 1.11.0 then you will need to use an older version of this extension, but please note that versions prior to 4 are unstable, unreliable and are not used in the same way as version 4 and above. The last 3.x version is available [ here]

Installation
Create a SimpleSecurity folder in your extensions directory. Download the latest snapshot and extract it to your extensions directory. Then include it in your LocalSettings.php file as in the following example.

Very important note
The order of the configuration directives is important or the extension will not protect pages as expected. It is usual that all configuration directives applying to a specific extension be added to the LocalSettings.php file after the line which requires or includes the extension script. But the $wgSecurityUseDBHook directive is a special case which is required before the extension is included. The reason for this is that the DB hook overrides the database classes and so if it is going to be used it must implemented its changes immediately before any database objects are instantiated.

Usage
SimpleSecurity4 extends the way the inherent MediaWiki permissions and restrictions system works. Following is a list of the specific functionalities which are added or extended and how to use them.

"Read" restriction added to protect tab
It seems that the main code-base development won't have a solution to the read problem for some time, but the current method of article protection already allows for restricting edit, create and move actions by group, and allows for the possibility of other actions to be handled, so the approach that Simple Security 4 has taken is to add a new restriction to the existing protection page called read which you can see in the images below which show the protection page with and without Simple Security installed.

The image on the right shows the additional read restriction has become available. Also the tick-box which is used to allow adjustment of the restrictions individually is changed to be more general so it will be appropriate for any number of additional restrictions.

Extra groups added to protection tab
To have extra groups available in the select box for each action (like "Security test group" in the picture above), add them to the $wgSecurityExtraGroups array in your LocalSettings.php file, for example: The index should be the internal name of a group (as seen in the user rights special page etc), and the value is a more friendly name shown in the protection form. There should not be any actions specified in that array it's just for adding groups into the protection form.

In Mediwaiki 1.13.x you can only assign extra groups that you belong to. Extra groups that you do not belong to will show up as blanks in the group selection boxes. (disc)

Restriction by category & namespace
Category-based permissions are now be handled from LocalSettings.php for efficiency reasons and will not inherit more than a single level. Namespace permissions are also supported, both are defined in the $wgPageRestrictions array which uses a format as follows: This example restricts article in Category:Foo such that only members of groups group1 and group2 can perform "action1". And "action2" can only be performed by group3 for all articles in the "Bar" namespace.

Restrictions set in $wgPageRestrictions can be overridden by those in the article's protect tab.

Security-based conditions
SimpleSecurity adds two parser-functions for rendering content conditionally based on security permissions or group membership. Here's a couple of examples,

This example renders a link to a todo list if the user has permission to edit the main page, otherwise a link to the news is rendered instead.

Here's the same example again, but this time the conditions is whether or not the user belongs to the sysop group. A comma separated list of groups can be used for the group parameter in which case the condition will evaluate to true if the current user belongs to any of the groups in the list.


 * Notes
 * The last parameter is optional and defaults to an empty string if not supplied
 * Extension:TreeAndMenu can be used with these options since it doesn't render nodes with no content

Security information
Articles which exhibit restrictions either from the protect tab or from $wgPageRestrictions can optionally have the information displayed by setting the $wgSecurityRenderInfo global variable to true. A link will be rendered entitled Security information which when clicked will reveal (or hide) the list of restrictions exhibited by the article. The show/hiding link is of the CSS id security-info-toggle, and the list of restrictions is contained in a div of id security-info, so a custom style can easily be applied by adding some CSS rules to your MediaWiki:Common.css article. For example the following rule gives a border and background to the listed rules when they are visible.

Unreadable links
Still has some bugs to iron out

If the $wgSecurityAllowUnreadableLinks global is set to false (default), then links to local articles which the current user does not have permission to read are rendered as plain text rather than a hyperlink. The image above shows a small fragment of a recent changes list. Notice that the Sandbox article title and its diff and hist show up grey and are not links, their style can be set in CSS by addressing the unreadable class attribute.

Global security settings
Here are the global variables which affect the operation of the extension. These should be set in your LocalSettings file after the include of the SimpleSecurity.php script.

DB Hook: Technical details
To allow restrictions on the reading of article content requires a hook at a very low-level in the programming which is common to all the kinds of operations involved in the retrieving of article content. The MediaWiki code is written to support a number of different database servers each with their own implementation of the SQL language, and it has also been designed to allow a wiki's database to be served from many servers concurrently. To achieve this, a Database class has been created to abstract the database access functions from the SQL langauge specifics. All database interaction is handled via the methods of the Database class, and specifically, the only way that any legitimate MediaWiki code would obtain any article content is through the fetchRow and fetchObject methods.

So to really solve the per-title read-restriction issue, some of the methods of the Database class would need to be intercepted. There are no official hooks in the methods we need to intercept, so instead hooks are added in a sub-class of the database type specified in $wgDBtype called DatabaseSimpleSecurity. To ensure that the new class is the one that new DB instances are based on, $wgDBtype is changed to the name of the new sub-class.

All text content is held in the old_text field of the text table, so the row-reading needs to be intercepted. But the SELECT queries also have to be adjusted to ensure that the old_id field is available along with old_text otherwise it cannot be established whether or not the text is allowed to be viewed (since the id is needed to relate the text fragment with a current article title). The query method of the database class has been overridden to patch the SQL, and fetchObject has been overridden to replace the content of the requested old_text field if it should not be accessible.

Patching SQL statements directly is not an ideal solution as it's inefficient and lacks portability, but we have a number of clients running mediawiki on their intranets who have been in dire need of a reasonable means of restricting their content on a per-title basis, but are also dead-set on sticking with MediaWiki. Unfortunately this is the only solution we've been able to come up with for them, but testing so far is showing positive results and should be no problem for all but the very high traffic sites.


 * See also the development notes at OrganicDesign:Extension talk:SimpleSecurity.php.
 * $wgDBtype is changed back after the LoadBalancer has initialised because some other operations such as $wgContLang->stripForSearch depend on it and caused multibyte searches to fail.