Extension:Read Restrict

This is my en devour to lockdown my MediaWiki site. It has some assumptions on the setup so millage will vary.

One of the more notable depedency is that it will need two databases. Each one with the same prefix (or scheme). '''I tried an install that used table prefixes and it didn't work. Not sure if they were the same or not. My tests without prefixes did work'''

For this I used MediaWiki/1.9.3 and MySQL/5.0.27, on Apache/1.3.33 with PHP/5.2.1. This was tested on a "Mac OS X"/10.4.9 (darwin) system.

This extension or the concepts here have not been tested against any of the issues addressed in Security issues with authorization extensions. Use at your own risk.
 * -- Suki 21:00, 26 March 2007 (EDT)

Concepts
In this setup I needed part of the Wiki to be publicly accessible and another part locked down to just those in a special group. I refer to this group as the family version because it fit the needs for what I was accomplishing.

I had a home site that anyone can contribute to but only family members could work on the hidden pages. This was asked by those in my family to avoid personal information from being archive in search engines and prowlers.

So I decided to make two separate Wiki's one for the world and one for the family. I thought that I could lock the family one down using some authentication perhaps provided by the apache server. However I found that MediaWiki (at least since version 1.9.3) had some facilities to accomplish this via a well crafted extension and the "userCan" hook.

I perceive that most Security issues with authorization extensions are blocked because the user is unable to retrieve the pages required to interface with the feature. For example search would work because the user cannot "read" the search results. Although none of that has been tested so I give no suggestion that this stuff actually works.

This was usefull because the two Wiki's could still use the same user names and avoid having to login twice when switching between them. It also gave the advantage of interlinking them with the Help:Interwiki linking feature.

Below I will try my best to describe the prccess I used to achieve this set up.

Setting up the first Wiki
I downloaded the latest MediaWiki (1.9.3 at the time) and untar'ed it to my DocRoot. I named the directory. prompt$ tar zxvf mediawiki-1.9.3.tar.gz prompt$ mv mediawiki-1.9.3 main_wiki

I then set my permissions for the writeable directories: prompt$ cd main_wiki prompt$ chmod 777 config prompt$ chmod 777 images #Optional for file uploads

I ran the setup and created a database specific for the Main Wiki. I also took note of the database Name, Username, Password, and Table Prefix.

Finish the setup and move the LocalSettings.php to the root and close up the permissions on the config directory. prompt$ mv config/LocalSettings.php. prompt$ chmod 755 config

Setting up the second Wiki
This step was a bit more complicated. I didn't like the idea of having two versions of the source. I wanted one place to store the files and one place to upgrade from. However I didn't want the data to mix since I was planning to lock down the second wiki. So the  used for uploading media would need to be seperate. And since the setting would be different I would need a seperate  directory to run the setup.

I decided to use symbolic links found on most unix based systems. I'm very confident that these ideas will work if one has two seperate copies of the code in different directories.

Linking the source
Make a new drectory and link the entire source tree: prompt$ cd .. #Should now be in the DocRoot prompt$ mkdir family_wiki prompt$ cd family_wiki prompt$ ln -s ../main_wiki/*.

As described above some things should not be in the cloned tree. I removed,  , and. Then I recreated custom ones. prompt$ rm LocalSettings.php config images prompt$ mkdir images prompt$ chmod 777 images The config directory has the setup page inside it so we have to sym-link that after we recreate the directory. prompt$ mkdir config prompt$ chmod 777 config prompt$ cd config prompt$ ln -s ../../main_wiki/config/index.php. prompt$ cd .. #Should now be in the family_wiki root Please note that the extensions directory doesn't need to be seperated because you choose which extensions load in the  which is seperated so all extensions can be stored in one place.

Running the setup of the second wiki
Now we do the exact same procedure as above and run the setup of the second wiki. Open the second wiki in the browser. It is very important that the database table prefixes are the same or this will not work. I am under the impression that the Username and the Password should be the same however at the very least the choice of user must have access to both databases. And of course, a second database must be created.

Also note that the use that is created as the Admin for this second wiki is just a place holder and is not used once the two are linked. (See bellow)

After running the setup we close up the second wiki by moving the  to the root and locking down the permissions to the config directory. prompt$ mv config/LocalSettings.php. prompt$ chmod 755 config

There should be two seperate wiki's installed. Next is how I linked the two together.

Inter-linking the two together
I linked the two wiki's together first by using the same user table for both and also using the Help:Interwiki linking feature to link the two so they could become a Wiki family.

Using the same user table
In order for the two wiki's to use the same users I used the  option in the latest MediaWiki. This basically tells the current wiki that it should look up the user info from another wiki database. This does not as far as I can tell link the group information. I belive everything is separated except the info in the user table (username, password, etc)

Open up the second wiki's  and add the first wiki's databse to it. ... $wgSharedDB = "first_wiki_db"; ... Since the second wiki will be manipulating the first wiki's user data the password salt needs to match. So if we open the first wiki's  and find the   variable and copy it to the Second wiki's ... $wgProxyKey = "e2b3d33282cf934253cfc36ef1a36a3c9f7caa3632b9ff497f0160b4a6683177"; ...
 * 1) Second wiki's original $wgProxyKey commented out:
 * 2) $wgProxyKey = "b4ba0f901a90baf79a878202e43bf7750d87bb7c683e03f4fb10f56b4fb45fde";
 * 3) First wiki's $wgProxyKey setting copied:

Now the two wiki's use the same user table. Logging in one will log into the other. creation of a user in one will create one in the other. The only thing separate user wise is the groups.

InterWiki Linking
Adding non standard Interwiki links are not as easy. You have to manually manipulate the database tables for both wiki's. If you have phpMyAdmin you can add the links with that. I will place the SQL below for good measure.

Add an InterWiki link from the first wiki to the second. (I chose family as the second name and main as the first) And yes the path can be relative. INSERT INTO `sukiwiki1`.`interwiki` ( `iw_prefix`,  `iw_url` ,  `iw_local` ,  `iw_trans` ) VALUES ( 'family', '../family_wiki/index.php/$1', '1', '0' ); See Help:Interwiki_linking for what each field does. Above is what I tested on my site. If you use the  you need to set.

Now add the same for the second wiki. INSERT INTO `sukiwiki2`.`interwiki` ( `iw_prefix`,  `iw_url` ,  `iw_local` ,  `iw_trans` ) VALUES ( 'main', '../main_wiki/index.php/$1', '1', '0' );

That should complete the Interwiki linking. Also note that this should not affect the security described below when using this setup. Because the Interwiki logic uses URL's to link not internal databases the use has to go through the authentication process for the linked wiki. (Feature? Hell yeah!)

Locking down the second Wiki
I used the userCan hook to test who was looking to read the pages and determine if that user is in the group with access. So the first thing we have to do is create such a group. I chose to use the name "family" but you can do any one you wish.

To add a new group you have to use it programatically. By installing the below extension it will add the group to the wiki. Copy and paste ReadRestrict.php to the extensions directory of the second wiki.

Edit the first line to choose what group you want to use. (I chose family)

Add this at the end of your second wiki  file. require_once ("extensions/ReadRestrict.php");

While logged into the second wiki as a SysOp. Open   and add the group to each user you want to have access.

And that should complete the instalation.

Additional Resources and Tips
I would suggest changing the style for each wiki that way the users will have a better idea which wiki they are working on. I would hope it would lower the confusion and help prevent accidental content showing up in the unrestricted wiki which was meant for the restricted users.

You should also lockdown edits from anonymous at least on the restricted wiki. $wgGroupPermissions['*']['edit'] = false;

I recomend the following extensions:
 * Extension:ConfirmEdit Captcha Extension (Works for user creation too. and tested on MediaWiki/1.9.3)
 * My Blog extension (For adding blogs to the wiki's)

ReadRestrict.php
<?php $wgReadAllowableGroups = array (   'sysop',    'family' );
 * 1) Which groups are allowed to access the pages.

$wgWhitelistRead = array(   "Special:Userlogin",    "Special:Userlogout",    "Special:Captcha",    "Special:Captcha/help",    "Special:Captcha/image",    "-",    "MediaWiki:Monobook.css" );
 * 1) Which pages out trump any restrictions (Used to allow user login etc.)
 * 2) If you use Extension:ConfirmEdit you must add the following:
 * 3)    "Special:Captcha",
 * 4)    "Special:Captcha/help",
 * 5)    "Special:Captcha/image",

function familyAuthUserCan ($title, $user, $action, $result) {   global $wgReadAllowableGroups, $wgWhitelistRead; // if null is returned then the other things in the hook chain are checked, the result is still undetermined // if true is returned then can the user do want he want to do, the result is true // if false is returned then the evaluation is aborted and the result is false
 * 1) Stop all access to those not allowed.

if ($action == 'read') {	if (in_array ($title->getPrefixedText, $wgWhitelistRead)) {	   // Allow everyone to access the pages in trumpTitles regardless $result = true; }	elseif (0 < count( array_intersect ($wgReadAllowableGroups, $user->getGroups))) {	   // The $user is in the $wgReadAllowableGroups so allow access. $result = true; }	else {	   // Deny access $result = false; }   }    // Else don't touch $result to leave the address to null }

$wgHooks['userCan'][] = 'familyAuthUserCan';
 * 1) Add the hook to userCan

foreach ($wgReadAllowableGroups as $group) {   $wgGroupPermissions[$group]['read'] = true; }
 * 1) Open up permission read for the groups in $wgReadAllowableGroups.
 * 2) FIXME: Is this needed when using the userCan hook?

$wgGroupPermissions['*']['read'] = false; $wgGroupPermissions['user']['read'] = false;
 * 1) Lock down all read access.

$wgWhitelistRead['*'] = $wgWhitelistRead; $wgWhitelistRead['user'] = $wgWhitelistRead; ?>
 * 1) Add login pages to be accessed by anyone.