Permissions: Best Practices

From XMBdocs
Revision as of 14:17, 20 April 2021 by Miqrogroove (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

This article discusses changes in the Forum Permissions System.

Remembering Permission Relationships

Some unfortunate mistakes were made when the six permissions indexes were designed. You might have noticed one of them already. X_PERMS_VIEW and X_PERMS_USERLIST are defined separately. But why? Honestly, there is no good reason for it. The consequence to you, the coder, is that both of these permissions have to be checked every time a forum is accessed.

The X_PERMS_PASSWORD permission adds another mild annoyance. Even though X_PERMS_VIEW and X_PERMS_USERLIST might be set to TRUE, it is still necessary to make an explicit check of X_PERMS_PASSWORD before allowing the user to see posts inside the forum. There are no plans to change this part of the permissions system, so remember to do this!

On top of all that, none of these permissions takes into consideration that the forums.status column might be set to 'off', which also must be checked.

4 and 5 were poor choices for the values of the userlist and password permissions indexes. They are going to be changed to higher values in the next version, which makes it very important to use the named constants instead of hard-coded integers.

Coding for XMB 1.9.10

The primary mechanism for permissions in 1.9.10 is function checkForumPermissions(). A simple call to this function might look like this:

$result = $db->query('SELECT * FROM '.X_PREFIX.'forums WHERE fid=1');
$forum = $db->fetch_array($result);
$perms = checkForumPermissions($forum);
if ($perms[X_PERMS_VIEW]) { //$self is allowed to view $forum }

What's important here is that a single boolean permission setting was retrieved using the $forum array and one of the six permissions index constants from header.php, in this case X_PERMS_VIEW.

If you need to access the set of all permissions for a particular index, this can be done too.

$result = $db->query('SELECT * FROM '.X_PREFIX.'forums WHERE fid=1');
$forum = $db->fetch_array($result);
$pp = explode(',', $forum['postperm']);
$perms = $pp[X_PERMS_VIEW];
if ($perms == $status_enum['Guest']) { //$forum has guest-only view perms }

Notice that a new array was used, called $status_enum. It is highly recommended that this array should be included with any hacks or mods that will need to use this pattern of code. The $status_enum array will be included in version 1.9.11, so using it now will make the code more forward compatible.

$status_enum = array(
'Super Administrator' => 1,
'Administrator'       => 2,
'Super Moderator'     => 4,
'Moderator'           => 8,
'Member'              => 16,
'Guest'               => 32,
''                    => 32,
'Banned'              => (1 << 30)
); //$status['Banned'] == 2^30

You might have noticed that due to the complexity of the new postperm column format and the function calls needed to check these permissions, it is no longer possible to write a single SQL WHERE command that will limit results to permitted forums. There are two preferred methods for generating a set of permitted forums:

1. Make a list. For example, if you query all forums and then loop through them with a structure like while($forum = $db->fetch_array($result)) { checkForumPermissions($forum); ... } then it is pretty simple to make a comma-delimited list of FIDs. That list can be used in a second query like SELECT * FROM xmb_threads WHERE fid IN ($fids)

2. Just use a loop. As in the first method, if you already have the information you need from the first query, then just stick an if statement inside the loop to handle only the permitted forums. The $result array can be used and re-used without querying the forums more than one time.

Possibly a future version could include standard functions for generating this list for you.

Coding for XMB 1.9.11

If you followed the best practices for version 1.9.10, then coding for 1.9.11 will be even easier.

checkForumPermissions() has been expanded to allow for easy permission masquerades. This is an invaluable feature in situations such as when a user is logged in but using a feature that must be always restricted to the Guest permissions set. Now it's as simple as this:

$result = $db->query('SELECT * FROM '.X_PREFIX.'forums WHERE fid=1');
$forum = $db->fetch_array($result);
$perms = checkForumPermissions($forum, 'Guest');
if ($perms[X_PERMS_VIEW]) { //Guests are allowed to view $forum }

getOneForumPerm() is a new function that eliminates the need to parse the postperm column to obtain the permissions for a particular index. Now it's as simple as this:

$result = $db->query('SELECT * FROM '.X_PREFIX.'forums WHERE fid=1');
$forum = $db->fetch_array($result);
$perms = getOneForumPerm($forum, X_PERMS_VIEW);
if ($perms == $status_enum['Guest']) { //$forum has guest-only view perms }

Also notice the $status_enum array will always be in global scope starting with version 1.9.11. The array is declared near the top of header.php so that it can even be used in

permittedForums() is a new function that eliminates the headaches of querying and checking permissions on all the forums to create a list. Now it's as simple as this:

$forums = permittedForums(forumCache(), 'forum');
foreach($forums as $forum) { //$forum['type'] etc can now be used for each forum }

There are also advanced arguments for cool things like building CSV lists of forum IDs.

$fids = permittedForums(forumCache(), 'thread', 'csv');
$query = $db->query("SELECT t.* FROM ".X_PREFIX."threads AS t INNER JOIN ".X_PREFIX."forums AS f USING (fid) WHERE f.fid IN ($fids)");
while($thread = $db->fetch_array($query) { //$thread['subject'] etc are available for permitted threads }