Access control lists

Access Control Lists, hereafter ACLs, are a powerful way to let administrators switch certain capabilities on and off for any number of people. By registering your plugin's various actions with the ACL system, you can allow your plugin to easily scale to any deployment the administrator has in mind.

Overview of the ACL terminology and model

  • An action is something that users can perform. Each ACL-controllable action has to be registered before use. The hook where this is done is acl_rule_init.
  • A rule is associated with one user or group. It can define permissions for any number of actions.
  • A permission defines the level of access one user or group has to a given action.

The API is modeled in a very simple way:

  • At the hook acl_rule_init, plugins register their actions.
  • At any time thereafter, plugins can call the get_permissions() method on $session or on a Session_ACLPageInfo object, and get a solid true/false as to whether that action should be permitted or not.

There is currently no implementation of hierarchy, e.g. you cannot automatically have a page draw permissions from another page at the API level. You can, however, fetch permissions for the parent and child pages, and then merge the child's calculated rule on top of the parent's.

The ACL API: Getting permissions

Certain actions apply only to certain namespaces; for example, you can't edit a special page because it's logically impossible. The $session->check_acl_scope() method can be used to test an action to see if it applies to a namespace.

bool $session->check_acl_scope(string $acl_action, string $namespace)

Specify the action you're checking (such as edit_page) and the namespace (Special). If the action applies to that namespace, you'll get a return of true; otherwise, the return value will be false.

if ( $session->check_acl_scope('post_comments', 'Article') )
  echo 'The action "post_comments" applies to articles.';

object Session_ACLPageInfo $session->fetch_page_acl(string $page_id, string $namespace)

Returns an instance of Session_ACLPageInfo, which contains the fully calculated permissions for the given page. You can call the get_permissions method on it to get the final decision on whether an action is permissible or not.

bool get_permissions(string $action)

Returns true if the given action is permissible, and false otherwise.

If you call get_permissions with an action that is not defined for the current namespace, a warning will be thrown, and the function will return false.

Example of fetching permissions

$perms = $session->fetch_page_acl('Main_Page', 'Article');
if ( $perms->check_acl_scope('read', 'Article') && $perms->get_permissions('read') )
  echo "Looks like you're allowed to read the main page.";

If you've set up your own actions and/or are on your own namespace, there's not much need to call check_acl_scope, but if your plugin deals with arbitrary pages from different namespaces you should plan to use this to avoid throwing warnings.

Registering new actions

Even the Enano core uses this API to register actions. Call the $session->register_acl_type() method from the hook acl_rule_init.

void $session->register_acl_type(string $action_id, int $default_permission, string $action_name_or_langstring, array $dependencies, string $namespaces)

Creates a new action. So long as the action applies to the namespace of the page the user is managing in the ACL editor, the action will now show up in the ACL editor where the user can edit it, and get_permissions can be called with $action_id to determine what the permissions are.

Description of parameters:

  • $action_id: The name of the action. Should be all lowercase letters and underscores, or numbers if you really have to.
  • $default_permission: One of AUTH_ALLOW, AUTH_WIKIMODE, AUTH_DISALLOW, or AUTH_DENY. Allow and disallow do exactly what they say on the tin. Wikimode will only be true if wiki mode is enabled and active for the given page, and Deny is a special setting that overrides all other rules on the given action.
  • $action_name_or_langstring: A human readable description of the action. This should be very brief, and may be a language string identifier if your plugin supports localization ($lang->get() is called on it automatically at the appropriate time, because when ACL rules are registered the L10N API is not started yet).
  • $dependencies: An optional array of other actions this one depends on. For example, history_view depends on read. If history_view in itself is permitted but read is denied, get_permissions will return false because read is specified as a dependency. Choose your dependencies wisely; remember, you can always check that both permissions are true, this just makes Enano do that automatically.
  • $namespaces: Either the special string "All" or a list of namespace identifiers, separated by "|", that this action applies to. Again, calling get_permissions using this action on a namespace outside of the action's scope will result in a warning.

Example

$plugins->attachHook('acl_rule_init', 'myplugin_register_acl_actions($session);');
 
function myplugin_register_acl_actions(&$session)
{
  // A simple action that applies to all namespaces and has no dependencies
  $session->register_acl_type('mod_misc', AUTH_DISALLOW, 'perm_mod_misc', Array(), 'All');
 
  // A more complex action that applies only to namespaces that are database-driven,
  // and depends on a number of other actions.
  $session->register_acl_type('even_when_protected', AUTH_DISALLOW, 'perm_even_when_protected',
              Array('edit_page', 'rename', 'mod_comments', 'edit_cat'),
              'Article|User|Project|Template|File|Help|System|Category');
}

ACL scope and precedence

ACL rules might be overridden by other rules. For example, a rule that applies to a whole user group will defer to a rule for one specific user. Enano handles precedence intelligently; for documentation on this behavior, please see the Administrator's Handbook, section 4.2.

Categories: (Uncategorized)