Tutorial.Permissions guide

Title: Permissions System Overview for MOD Authors Written by: poyntesm Written On: 10 Oct 2007 Description: A overview of the permissions system that is geared towards MOD authors Source: Part I, Part II

Permission System Overview For MOD Authors

This guide is intended as a overview to the whole permissions system and where & what data gets stored and how its used. It is not meant to be a userguide to setting up permissions although it does cover it to some degree. I have tried to be 100% sure of all this information by checking everything I believed against the code ... but this is not an official document so there could be mistakes. If you spot any let me know. I wrote this because I think its a brilliant system and some people might like to know a little more without having to study the code themselves.

What is the permissions system?
It's the system that allows board administrator decide what users are allowed do on the board.

How does it work?
An administrator with the right permission (Yes ... An administrator needs the permission to set permissions ) has access to the administration control panel(ACP) which contains the administrative side of the permissions system. The administrator then setups the permissions to allow users have the rights to do certain thing tasks or see certain features. At the end of the day each permission is what is called a permission option and users obtain the rights to these options either directly or from a group or from a role that is assigned to them or a group they are in.

What are permission options?
These are the individual options that allow or deny you access to features. Examples are 'f_post', 'm_delete', 'a_ban' & 'u_sendpm'. What are those f_, m_, a_ & u_ bits in the options? These are what are called permission types.

What are permission types?
Permission types are groupings of permissions of the same type. f_ is the forum type. m_ is the moderator type. a_ is the administrator type & u_ is the user type.

Where are my own users permissions stored?
Your permissions are stored in the phpbb_users table

What fields are in the phpbb_users table that relate to permissions?
user_permissions - Holds the users permission. We will talk later how this gets filled and when it gets updated. user_perm_from - If you have the a_switchperm permission option then you can assume the permissions of another user. If you do that this field contains the user_id you have assumed the permissions of. Normally its 0

Where are permissions options stored?
They are stored in the table phpbb_acl_options.

What fields are there and what do they mean?
auth_option_id - Unique ID of option auth_option - permission option name is_global - Set to 1 if a global option else set to 0 is_local - Set to 1 if a local option else set to 0 founder_only - Set to 1 if a found only option else set to 0

What is a local permission option?
A local permission option is a option that can be granted to a user on a forum by forum basis. This allows you grant users options to perform a task in one forum but not another. They are also called forum based permissions.

What is a global permission option?
A global permission option is a option that is valid board wide.

Can a permission option be both global & local?
Yes. A option can be both. An example is 'm_edit' this is the moderator permission to edit a topic. You might grant it for a user to one forum. Another user might be given it board wide. To allow this the option is set to both local & global.

What is a founder only option?
A permission that only a founder can have.

Are any default phpBB permissions founder only?
No.

What is a founder?
A found is a special type of user. It should only be granted to the most trusted of administrators. A founder can access the permission system to correct his permissions even if another administrator has removed his permissions. Only a founder can remove the founder status of another founder.

What are roles?
Roles are a predefined setup of permission options that can be applied to users or groups. If you change a roles permission options the users or groups assigned the role get updated automatically.

Where are roles stored?
They are stored in the table phpbb_acl_roles.

What fields are there and what do they mean?
role_id - Unique ID of option role_name - role name normally as a lang key role_description - role description normally as a lang key role_type - a_, u_, m_ or f_ depending on what role is for role_order - number indicating display order in the ACP

Where are the permission options for a role stored?
They are stored in the table phpbb_acl_roles_data.

What fields are there and what do they mean?
role_id - role id from phpbb_acl_roles auth_option_id - option id from phpbb_acl_options auth_setting - Stores either ACL_YES (1), ACL_NO (-1) or ACL_NEVER (0)

What are these ACL_YES, ACL_NO & ACL_NEVER?
Your effective permission for any option is built up from a combination of details such as which groups you are a member of, which role you have assigned and whether you have been assigned directly that permission. As such you might have opposing permissions. The YES, NO & NEVER system works to allow phpBB combine your this different answers for a option and give you the effective permission. If anywhere you get a NEVER that will be your permission for that option ... a YES can not override a NEVER. However a YES does override a NO.

What happens if I have a global option set to NO but also the same option is local and set to YES?
This depends partly on how the check is done. If acl_get is called without a forum id then only the global will be checked. If a forum id is specified then the local option will be combined with the global one. If the local one is YES then so will the global one & vice versa. One thing to note is that when checking if a user has a permission the answer is only every YES or NO. The YES|NO|NEVER system is only used to build the YES|NO final setting. The example above is a good reminder that local & global permissions are different. There is also a check called acl_getf_global this is effective the same as acl_get.

If I assigned a role to a user where is that stored?
That is stored in the table phpbb_acl_users.

What fields are there and what do they mean?
user_id - user id from phpbb_users forum_id - forum id from phpbb_forums if a local option else 0 auth_option_id - option id from phpbb_acl_options auth_role_id - role id from phpbb_acl_roles if obtained from a role else 0 auth_setting - Stores either ACL_YES (1), ACL_NO (-1) or ACL_NEVER (0)

If I assigned a role to a group where is that stored?
That is stored in the table phpbb_acl_groups.

What fields are there and what do they mean?
group_id - group id from phpbb_groups forum_id - forum id from phpbb_forums if a local option else 0 auth_option_id - option id from phpbb_acl_options auth_role_id - role id from phpbb_acl_roles if obtained from a role else 0 auth_setting - Stores either ACL_YES (1), ACL_NO (-1) or ACL_NEVER (0)

How do I setup permissions now I know the different parts that are involved?
First you need to decide if you what you are trying to achieve. If you need to set one user to have maybe just one extra permission option then doing it to the user direction might be OK. Otherwise its best to set permission options on a group and place the user in the group. Or if its a lot of permission options you are setting up then a role might be best. You also need to understand if the options are global or local. Setting global permissions From the "PERMISSIONS" tab select from "Users' permissions", "Groups' permissions", "Administrators" or "Global moderators". If you select either of the last two you will first need to select the forum for which you are trying to set. Setting local (forum) permissions Setting forum based (local) permissions From the "PERMISSIONS" tab select from "Forum permissions", "Forum Moderators", "Users' forum permissions" or "Group' forum permissions". If you select either of the last two you will first need to select the user/group and then the forum for which you are trying to set. Setting role permissions From the "PERMISSIONS" tab select from "Admin roles", "User roles", "Moderator roles" or "Forum roles".

When setting advanced permissions or defining roles what are those tabs?
Each of the tabs represents a permission category. Categories are used to further group permission options together.

So where in the ACP can permissions be setup?
The ACP can be configured to place this modules in multiple places. So while some of the ways are listed above there are others and the administrators can move them.

How can I view what permissions a user/group will get?
You view what permissions a user/group has by viewing its permission mask.

What are permission masks?
Masks are the effective permissions your user has for the different type of permission as we described already. You have user permissions, moderator permissions both local & global, forum permissions & administrator permissions. So for each of these you have a permission mask which can be viewed in the ACP. For forum permissions & moderator local permissions you need to select the forum you want to check first.

How do I workout permission masks?
From the "PERMISSIONS" tab select from "View administrative permissions", "View user-based permissions", "View global moderation permissions", "View forum moderation permissions" & "View forum-based permissions". If you select either of the last two you will first need to select the forum for which you are trying to view for. When viewing masks you can view for multiple/single user(s) or group(s). Permission for a group are set. However since permissions for a user can come from multiple groups you can trace the permission.

What is tracing a permission?
When viewing the mask of permissions for a user a icon( Image) appears beside each option will show how the option got its value. It will start with the default value and then work through each group the user is a member of and then also the user direct permission at the end. For each group or finally the user permission it looks at the setting and updates the total. Remember a NEVER will wipe out any YES and a YES can not overwrite a NEVER. This is a very handy tool to determine why a user is getting or not getting a permission. The final total is the effective permission for any option.

How do permissions get setup for a page when a user views it?
When a users (remember bots & guests are still users) views a page a session is started with $user->session_begin; Next you call the following $auth->acl($user->data); this will setup the users permission. This $auth->acl function checks to work out if the user_permission field we mentioned early needs updated. When permissions are updated via the ACP this field is cleared. If you only set a user permission then only that user has it clear otherwise it is cleared for all users. So on next login the acl function has to repopulate it with new permission options for user. It first checks the cache for _acl_options and updates if needed and then rebuilds the users permissions. Else if the cache is OK but the user permissions is empty it rebuilds just the users permissions. If the cache is OK and the user permissions is OK the field is left alone. The field is then turned from a bitstring into an array and it is this array we check against when checking a users permissions.

So what does the acl_options cache contain?
This cache contains an array which has two elements - local & global. Each of these is an array that contains each of the acl options of that type. As with all cache it checks if the cache is within a valid time frame, if the time frame is OK it will setup the array else just return.

How do I check if a user has a single permission option?
You call the $auth->acl_get('u_garage_browse'); and the argument is the option you want to check for If it was specific to a forum (i.e local) then you do it as $auth->acl_get('u_garage_browse', 3); where 3 is the forum id

I have created a forum specific option. How do I find out which forums a user has this set for?
You call the $auth->acl_getf('f_your_permission');

Can I check for more than one option?
Yes. By using $auth->acl_gets(option1[, option2, ..., optionN, forum]);

How do I add a permission on my own for a MOD?
Firstly you need to understand there are two parts to adding a permission.
 * Database
 * Language

To add to the DB you should always use the native functions. As they handle cache etc.. session_begin; $auth->acl($user->data); $user->setup;

include($phpbb_root_path . 'includes/acp/auth.' . $phpEx); $auth_admin = new auth_admin;

$auth_admin->acl_add_option(array( 'local'    => array, 'global'   => array('u_your_mod', 'm_your_mod') )); ?> This code will setup two new global permission, one a user permission and the other a moderator permission. Now you need to setup the language side.

 array('lang' => 'Can use amazing MOD',       'cat' => 'your_mod'), 'acl_m_your_mod'   => array('lang' => 'Can moderate amazing MOD',  'cat' => 'your_mod'), )); ?>

How do I make my MOD installer setup permission automatically?
By default you can not. As we learnt above when phpBB updates permissions for users/groups/roles it works out the complete permissions and updates as a multi-insert. This approach is just too much for a MOD to try do. Please note this is not standard phpBB. A simple approach is to use functions such as the ones below. The functions check if the user/group/role already has the permission and if so does not insert it, if not it inserts the option and clears the cache. There are three functions provided below, one for user, group, role. Each works in the same way. First is whether you are granting or removing, then you pass a username, group name or role name as the second variable and an array of permissions you want to assign/remove as the third, a four variable sets the ACL_YES|ACL_NO|ACL_NEVER that we also talked about early. //examples of granting user, group & role update_user_permissions('grant', $username, array('u_your_mod', 'm_your_mod')); update_group_permissions('grant', $group_name, array('u_your_mod', 'm_your_mod')); update_role_permissions('grant', $role_name, array('u_your_mod', 'm_your_mod')); //examples of removing user, group & role update_user_permissions('remove', $username, array('u_your_mod', 'm_your_mod')); update_group_permissions('remove', $group_name, array('u_your_mod', 'm_your_mod')); update_role_permissions('remove', $role_name, array('u_your_mod', 'm_your_mod')); So now here are each of the functions, you can include them in your installation scripts and call them as above

User Permission Update
*code needs some cleanup for better optimisation and adhering to phpBB3 coding guidelines* <?php /** function update_user_permissions($mode = 'grant', $username, $options = array, $auth_setting = ACL_YES) {   global $db, $auth, $cache;
 * Update role-specific ACL options. Function can grant or remove options. If option already granted it will NOT be updated.
 * @param grant|remove $mode defines whether roles are granted to removed
 * @param strong $role_name role name to update
 * @param mixed $options auth_options to grant (a auth_option has to be specified)
 * @param ACL_YES|ACL_NO|ACL_NEVER $auth_setting defines the mode acl_options are getting set with
 * @param ACL_YES|ACL_NO|ACL_NEVER $auth_setting defines the mode acl_options are getting set with

// First We Get User ID   $sql = 'SELECT u.user_id FROM '. USERS_TABLE. " u       WHERE username = '". $db->sql_escape(utf8_clean_string($username)). "'";   $result = $db->sql_query($sql); $user_id = (int) $db->sql_fetchfield('user_id'); $db->sql_freeresult($result);

//Now Lets Get All Current Options For User $user_options = array; $sql = "SELECT auth_option_id       FROM ". ACL_USERS_TABLE. "       WHERE user_id = ". (int) $user_id. "       GROUP BY auth_option_id"; $result = $db->sql_query($sql); while ($row = $db->sql_fetchrow($result)) {       $user_options[] = $row; }   $db->sql_freeresult($result);

//Get Option ID Values For Options Granting Or Removing $acl_options_ids = array; $sql = "SELECT auth_option_id       FROM ". ACL_OPTIONS_TABLE. "       WHERE ". $db->sql_in_set('auth_option', $options). "       GROUP BY auth_option_id"; $result = $db->sql_query($sql); while ($row = $db->sql_fetchrow($result)) {       $acl_options_ids[] = $row; }   $db->sql_freeresult($result);

// If Granting Permissions if ($mode == 'grant') {       //Make Sure We Have Option IDs if (empty($acl_options_ids)) {           return false; }       // Build SQL Array For Query $sql_ary = array; for ($i = 0, $count = sizeof($acl_options_ids);$i < $count; $i++) {

//If Option Already Granted To User Then Skip It           if (in_array($acl_options_ids[$i]['auth_option_id'], $user_options)) {               continue; }

$sql_ary[] = array(               'user_id'           => (int) $user_id,                'auth_option_id'    => (int) $acl_options_ids[$i]['auth_option_id'],                'auth_setting'      => $auth_setting,            ); }

$db->sql_multi_insert(ACL_USERS_TABLE, $sql_ary); $cache->destroy('acl_options'); $auth->acl_clear_prefetch; }

//If Removing Permissions if ($mode == 'remove') {       //Make Sure We Have Option IDs if (empty($acl_options_ids)) {           return false; }       //Process Each Option To Remove for ($i = 0, $count = sizeof($acl_options_ids);$i < $count; $i++) {           $sql = "DELETE                FROM ". ACL_USERS_TABLE. "               WHERE auth_option_id = ". $acl_options_ids[$i]['auth_option_id'];

$db->sql_query($sql); }

$cache->destroy('acl_options'); $auth->acl_clear_prefetch; }

return; } ?>

Group Permission Update
*code needs some cleanup for better optimisation and adhering to phpBB3 coding guidelines* <?php /** function update_group_permissions($mode = 'grant', $group_name, $options = array, $auth_setting = ACL_YES) {   global $db, $auth, $cache;
 * Update group-specific ACL options. Function can grant or remove options. If option already granted it will NOT be updated.
 * @param grant|remove $mode defines whether roles are granted to removed
 * @param string $group_name group name to update
 * @param mixed $options auth_options to grant (a auth_option has to be specified)
 * @param ACL_YES|ACL_NO|ACL_NEVER $auth_setting defines the mode acl_options are getting set with
 * @param ACL_YES|ACL_NO|ACL_NEVER $auth_setting defines the mode acl_options are getting set with

//First We Get Role ID   $sql = "SELECT g.role_id        FROM ". GROUPS_TABLE. " g       WHERE group_name = '$group_name'"; $result = $db->sql_query($sql); $group_id = (int) $db->sql_fetchfield('group_id'); $db->sql_freeresult($result);

//Now Lets Get All Current Options For Role $group_options = array; $sql = "SELECT auth_option_id       FROM ". ACL_GROUPS_TABLE. "       WHERE group_id = ". (int) $group_id. "       GROUP BY auth_option_id"; $result = $db->sql_query($sql); while ($row = $db->sql_fetchrow($result)) {       $group_options[] = $row; }   $db->sql_freeresult($result);

//Get Option ID Values For Options Granting Or Removing $sql = "SELECT auth_option_id       FROM ". ACL_OPTIONS_TABLE. "       WHERE ". $db->sql_in_set('auth_option', $options). "       GROUP BY auth_option_id"; $result = $db->sql_query($sql); while ($row = $db->sql_fetchrow($result)) {       $acl_options_ids[] = $row; }   $db->sql_freeresult($result);

//If Granting Permissions if ($mode == 'grant') {       //Make Sure We Have Option IDs if (empty($acl_options_ids)) {           return false; }       //Build SQL Array For Query $sql_ary = array; for ($i = 0, $count = sizeof($acl_options_ids);$i < $count; $i++) {

//If Option Already Granted To Role Then Skip It           if (in_array($acl_options_ids[$i]['auth_option_id'], $group_options)) {               continue; }           $sql_ary[] = array(                'group_id'        => (int) $group_id,                'auth_option_id'    => (int) $acl_options_ids[$i]['auth_option_id'],                'auth_setting'        => $auth_setting,            ); }

$db->sql_multi_insert(ACL_GROUPS_TABLE, $sql_ary); $cache->destroy('acl_options'); $auth->acl_clear_prefetch; }

//If Removing Permissions if ($mode == 'remove') {       //Make Sure We Have Option IDs if (empty($acl_options_ids)) {           return false; }       //Process Each Option To Remove for ($i = 0, $count = sizeof($acl_options_ids);$i < $count; $i++) {           $sql = "DELETE                FROM ". ACL_GROUPS_TABLE. "               WHERE auth_option_id = ". $acl_options_ids[$i]['auth_option_id'];

$db->sql_query($sql); }

$cache->destroy('acl_options'); $auth->acl_clear_prefetch; }

return; } ?>

Role Permission Update
*code needs some cleanup for better optimisation and adhering to phpBB3 coding guidelines* <?php /** function update_role_permissions($mode = 'grant', $role_name, $options = array, $auth_setting = ACL_YES) {   global $db, $auth, $cache;
 * Update role-specific ACL options. Function can grant or remove options. If option already granted it will NOT be updated.
 * @param grant|remove $mode defines whether roles are granted to removed
 * @param strong $role_name role name to update
 * @param mixed $options auth_options to grant (a auth_option has to be specified)
 * @param ACL_YES|ACL_NO|ACL_NEVER $auth_setting defines the mode acl_options are getting set with
 * @param ACL_YES|ACL_NO|ACL_NEVER $auth_setting defines the mode acl_options are getting set with

//First We Get Role ID   $sql = "SELECT r.role_id        FROM ". ACL_ROLES_TABLE. " r       WHERE role_name = '$role_name'"; $result = $db->sql_query($sql); $role_id = (int) $db->sql_fetchfield('role_id'); $db->sql_freeresult($result);

//Now Lets Get All Current Options For Role $role_options = array; $sql = "SELECT auth_option_id       FROM ". ACL_ROLES_DATA_TABLE. "       WHERE role_id = ". (int) $role_id. "       GROUP BY auth_option_id"; $result = $db->sql_query($sql); while ($row = $db->sql_fetchrow($result)) {       $role_options[] = $row; }   $db->sql_freeresult($result);

//Get Option ID Values For Options Granting Or Removing $acl_options_ids = array; $sql = "SELECT auth_option_id       FROM ". ACL_OPTIONS_TABLE. "       WHERE ". $db->sql_in_set('auth_option', $options). "       GROUP BY auth_option_id"; $result = $db->sql_query($sql); while ($row = $db->sql_fetchrow($result)) {       $acl_options_ids[] = $row; }   $db->sql_freeresult($result);

//If Granting Permissions if ($mode == 'grant') {       //Make Sure We Have Option IDs if (empty($acl_options_ids)) {           return false; }       //Build SQL Array For Query $sql_ary = array; for ($i = 0, $count = sizeof($acl_options_ids);$i < $count; $i++) {

//If Option Already Granted To Role Then Skip It           if (in_array($acl_options_ids[$i]['auth_option_id'], $role_options)) {               continue; }           $sql_ary[] = array(                'role_id'        => (int) $role_id,                'auth_option_id'    => (int) $acl_options_ids[$i]['auth_option_id'],                'auth_setting'        => $auth_setting,            ); }

$db->sql_multi_insert(ACL_ROLES_DATA_TABLE, $sql_ary); $cache->destroy('acl_options'); $auth->acl_clear_prefetch; }

//If Removing Permissions if ($mode == 'remove') {       //Make Sure We Have Option IDs if (empty($acl_options_ids)) {           return false; }       //Process Each Option To Remove for ($i = 0, $count = sizeof($acl_options_ids);$i < $count; $i++) {           $sql = "DELETE                FROM ". ACL_ROLES_DATA_TABLE. "               WHERE auth_option_id = ". $acl_options_ids[$i]['auth_option_id'];

$db->sql_query($sql); }

$cache->destroy('acl_options'); $auth->acl_clear_prefetch; }

return; } ?>