From OrganicDesign Wiki
| This talk page pertains specifically to the development of this extension. For more general discussion about bugs and usage etc, please refer to the mediawiki.org talk page at MW:Extension talk:Simple Security
|
|
The SimpleSecurity extension has some major problems which need to be addressed (as do all of the MediaWiki security extensions). The general problem is that although 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, many different actions, special-pages and extensions access article content and display it in diverse ways.
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 server's 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 add a unified database interface to the MediaWiki runtime environment. 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, a hook would need to be placed into some of the methods of the Database class. The DatabaseFetchHook extension was created to test if such hooks were a feasible option, and to add these hooks dynamically without modifying any of the MediaWiki code-base files. The results were positive and so now need to be incorporated into a usable security extension.
1 Functionality Overview
The new version is designed to be in line with MediaWiki's own development plans for security. It seems that they 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. Also, they have a database column prepared for allowing restriction based on individual user as well.
Due to this we'll be dropping the idea on in-line annotations and work instead on extending the current official protection scheme which uses a database table to relate security information to articles. The following points cover the initial development effort to be completed over the next month.
1.1 Read permission
| Protection form without Simple Security
|
| Protection form with Simple Security
|
|
|
|
1.2 Category & Namespace Restrictions
Category-based permissions are now be handled from LocalSettings.php for efficiency reasons and will not inherit more than a single level. Namespace permissions will now also be supported, both will be defined in the $wgPageRestrictions array which uses a format as follows:
|
| $wgPageRestrictions['Category:Foo']['action1'] = array('group1', 'group2');
$wgPageRestrictions['Namespace:Bar']['action2'] = 'group3';
|
|
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.
- NOTE: the tests for this feature must cover all actions, particularly read
1.3 Security information
If the $wgSecurityRenderInfo global is set to true (default), then pages exhibiting restrictions will include a link which can be clicked to show/hide and information box. The information shows three things:
- what restrictions affect the article (if any) from $wgPageRestrictions and the article's category and namespace.
- what restrictions are associated with the title itself from the protect tab.
- what restrictions are currently in effect for the current user and page request.
1.4 Unreadable links
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.
2 TODO
- Add and handle read and source actions (in userCan, not fetchHook yet)
- Add #ifusercan and #ifgroup parser functions
- Implement the individual user functionality (making it switchable since it will be handled in the code-base soon)
- Allow with or without database hook
3 Tests
To test the functionality of the extension, we can use SimpleSecurity test article which is an article with its read permission set to group "foo". Each of the following test are performed four times, for each anon, non foo user, foo, sysop.
- Standard actions: Test each action read, edit, history, protect, delete, move
- Special: Special page actions should not be supported for unreadable articles
- Transclusion: Test whether the article can be read by previewing transclusion into sandbox
- Search: search for "unreadable" and see if content from the test article shows up
4 Bugs
- Edit comments can contain information which shouldn't be viewable but is currently not filtered
5 Specifics
5.1 User rights
Integration with MediaWiki's native permissions system is achieved through the UserGetRights hook. This hook allows the available rights for the user to be filtered based on the directives specified in $wgTitle and $wgGroupPermissions. The UserGetRights hook (introduced in 1.11) is sufficient for all the actions except for read. UserGetRights hook is called from User::getRights and returns an array (list of strings) of the rights the current user has. User::getRights passes User::getEffectiveGroups to User::getGroupPermissions to filter the rights in $wgGroupPermissions.
5.2 Title restrictions
The Title::loadRestrictionsFromRow method loads all the restrictions information for a title from the page_restrictions table into the Title::mRestrictions array which has actions for keys (from pr_type column), and arrays of groups (from pr_level column) as values. Restrictions are obtained publicly via Title::getRestrictions method by passing an action key and getting the groups that are allowed to perform that action in response.
5.3 Implementing the read restriction
Before implementing anything sophisticated with the DB hook, the standard functionality of blocking actual page views on pages with restricted read action. The obvious way to do this would be to check whether the title had such a restriction early on and then update the rights as if they had been set like that with $wgGroupPermissions['*']['read'] = false.
5.3.1 $wgGroupPermissions defaults problem
The first problem encountered with this method is that it only seems to block viewing of articles by anonymous users, it can't be made to block viewing by logged in users of any group. I.e. when a user is logged in, they can always view an article regardless of any read restrictions in $wgGroupPermissions. Article::View() checks whether the article is allowed to be read with Title::userCanRead() and calls $wgOut->loginToUse() if not. Title::userCanRead() simply calls User::isAllowed('read') which checks if read is in the list of actions returned by getRights.
This problem is due to a performance-based short cut that has been added in Title::userCanRead() which returns true without further processing if $wgGroupPermissions['*']['read'] is set to true (which it is by default). To avoid it, $wgGroupPermissions['*']['read'] is remembered and set to false in the UserGetRights hook, if there are no read restrictions in the title, and the site is public, then the read right is put back again.
5.3.2 System messages
Some of the system messages need to be adjusted to match the more generic context the extension allows for article protection:
- protect-unchain Unlock move permissions (changed to "Modify actions individually")
- badaccess* e.g. The action you have requested is limited to users in one of the groups $1 (wrong groups in $1)
5.3.3 Move problem
The read action when restricted prevents the user from performing most actions on the page such as edit or history, but move must be handled specifically because it's implemented as a special page not a normal page action.
5.4 Implementing the source restriction
5.5 DatabaseFetchHook
All text content is held in the old_text field of the text table, so the row reading needs to be intercepted. But the SQL queries also have to be adjusted to ensure that the old_id field is available along with old_text because 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 title and its security directives). The DatabaseFetchHook has been implemented and tested along with the SQL query patch.
5.6 Page-Restrictions Table
The current protection form could be extended or replaced to allow all available actions. Here's an example of the permissions tables content:
|
|
mysql> select * from page_restrictions;
+---------+---------+----------+------------+---------+-----------+-------+
| pr_page | pr_type | pr_level | pr_cascade | pr_user | pr_expiry | pr_id |
+---------+---------+----------+------------+---------+-----------+-------+
| 9 | edit | sysop | 1 | NULL | infinity | 1 |
| 9 | move | sysop | 1 | NULL | infinity | 2 |
| 18 | edit | sysop | 1 | NULL | infinity | 3 |
| 18 | move | sysop | 1 | NULL | infinity | 4 |
| 20 | edit | sysop | 1 | NULL | infinity | 5 |
| 20 | move | sysop | 1 | NULL | infinity | 6 |
| 21 | edit | sysop | 1 | NULL | infinity | 7 |
| 21 | move | sysop | 1 | NULL | infinity | 8 |
+---------+---------+----------+------------+---------+-----------+-------+
8 rows in set (0.03 sec)
|
|
6 Road Map
Here are some options and ideas planned for development after the initial version is up and running.
6.1 History & Other Actions
It would be useful to be able to restrict access to an article's history. Further it may be useful to add the ability to generically restrict any possible actions.
6.2 ReImplement File Restrictions
There are other existing ways of doing this, but if they're no good, basing it on the original SimpleSecurity method would be fine.
6.3 Split Into 3-File Code Structure
Split into the 3 files of main, body and i18n and commit to SVN
6.4 Create test suite
The Selenium browser test environment would be a perfect solution for testing the security extension over many versions. Tests can be set up for each of the flaws listed in MW:Security issues with authorization extensions. These tests can then be performed for many different extensions over many different MediaWiki versions. By having a filesync'd localsettings, it would also be possible to create a list of tests which include different combinations of extensions and localsettings parameters.
For testing which may involve complex procedures such as transcluding specific content and then testing readability etc, it may be handy to use MW:Extension:Packages.
Once the tests are setup, a master test-suite can be maintained in-wiki (as are all the individual tests), and the tests run by a bot in the crontab which can email the admins with relevant information whenever any tests fail.
7 See also