In MongooseIM access control is performed via Access Control Lists (ACL). Initially, this functionality was supposed to answer the following question: "Is a given user allowed to access a particular resource?". The answer was either allow or deny, but this functionality was extended to return any value. For instance, we can ask how many concurrent session a given user can open, where the answer may vary depending on user affiliation - are they an admin or a normal user.

The functionality is defined via two top-level options:

  • {acl, Name, Pattern}: contrary to its name, it defines a user or the whole group. For instance we can define admin users or banned users.
  • {access, Name, List}: an ACL that has a name and list of rules and values returned by them in the case of a successfull match.

ACL tuple

{acl, Name, Pattern}: the name could be any atom. This name will be used in the access definitions. Note that there might be many ACL entries with the same name, in this case the result will be the union of all patterns.

Patterns

This sections describes all possible ACL patterns. Some of them use the re syntax for regular expressions and some accept the glob syntax.

Patterns can be defined in one of the following formats:

all

All users/JIDs match.

{user, username()}

All users with a given user name:
{user, "admin"}: includes admin@localhost, admin@xmpp.org, etc.

{user, username(), server()}

In this case the username and the domain have to match:
{user, "admin", "localhost"}: admin@localhost matches, butadmin@xmpp.org doesn't.

{server, server()}

All users from a given domain:
{server, "localhost"}: admin@localhost and pawel@localhost match, but pawel@xmpp.org doesn't.

{resource, resource()}

All users with a matching resource:
{resource, "res1"}: admin@localhost/res1 matches, but admin@localhost/res2 doesn't.

{user_regexp, username_regexp()}

Similar to user, but the match is done against a regular expression:
{user_regexp, "^ad.*"}: admin@localhost and ads@localhost2 match, but pad@localhost doesn't.

{user_regexp, username_regexp(), server()}

Similar to user, the username is matched against regex, the server part has to be exactly the same:
{user_regexp, "^ad.*", "localhost"}: admin@localhost matches, but admin@xmpp.org doesn't.

{server_regexp, server_regexp()}

Analogous to user_regexp, but regex matches on the server part of the JID:
{server_regexp, "^local.*"}: admin@localhost matches, but admin@relocal doesn't.

{resource_regexp, resource_regexp()}

The same story as above, but for the resource part:
{resource_regexp, "res.*"}: admin@localhost/resource matches, but admin@localhost/ios doesn't.

{node_regexp, username_regexp(), server_regexp()}

Regexp matching on both the username and the domain:
{node_regexp, "ad.*", "loc.*"}: admin@localhost matches, but pawel@xmpp.org doesn't.

{user_glob, username_glob()}

Match on the username part using glob style patterns:
{user_glob, "paw*"}: pawel@localhost matches, but admin@localhost doesn't.

{user_glob, username_glob(), server()}

Match on the username part using glob patterns and on the server using exact match:
{user_glob, "paw*", "localhost"}: pawel@localhost matches, but pawel@xmpp.org doesn't.

{server_glob, server_glob()}

Match on the server part using glob patterns:
{server_glob, "local*"}: pawel@localhost matches, but pawel@xmpp.org doesn't.

{resource_glob, resource_glob()}

Match on the resource part using glob patterns:
{resource_glob, "r*"}: pawel@localhost/res matches, but pawel@xmpp.org doesn't.

{node_glob, username_glob(), server_glob()}

Match on the username and the server part using glob patterns:
{node_glob, "paw*", "loc*"}: pawel@localhost/res matches, but pawel@xmpp.org doesn't.

Access tuple

Once we have our group of interest gathered in an ACL tuple, we can use them in an access control list. To do so, we need to specify a tuple like the following:

{access, name_of_access_rule, [{value(), acl_name()}]}

As an example we can discuss 2 rules which are present in the default config file:

  • {access, register, [{allow, all}]}.
    This rule is used while registering a new user. The example above has no restrictions, but we might want to block certain JIDs, admin JID for instance. To do so we need to set:
    {acl, admin, {user, "admin"}}., then {access, register, [{deny, admin}, {allow, all}]}.
  • {access, max_user_offline_messages, [{5000, admin}, {100, all}]}.
    This rule is used in mod_offline, it determines mod_offline's storage limit. For users defined in the admin ACL (for example {acl, admin, {user, "pawel", "localhost"}}) the size is 5000 messages, while the size for a normal user is 10.

Priority: global vs host access lists

By default, both ACL and access elements are "global", so they apply to all domains available on the server. However, using the host_config option, we are able to override the rules for a particular domain.

%%  Define specific Access Rules in a virtual host.
{host_config, "localhost",
 [
  {access, c2s, [{allow, admin}, {deny, all}]},
  {access, register, [{deny, all}]}
 ]
}.

The global rule has the highest priority, however if the global rule ends with {allow, all} the host specific rule is taken into account.

For developers

To access the ACL functionality, one has to use the acl:match_rule/3 function.

Given the following ACL:

{access, register, [{deny, all}]}

One can call:

acl:match_rule(<<"localhost">>, register, jid:make(<<"p">>, <<"localhost">>, <<>>)).

Which in our case will return deny. If the rule is not host specific, one can use global instead of <<"localhost">>.