How Permission Rules Are Applied
Table of Contents
Controller rules are applied for modules which are set as
restricted=True in config.py, e.g.:
("org", Storage(name_nice = T("Organizations"), restricted = True, module_type = 1 ), ),
If a controller is restricted, a controller rule must exist for a role to allow it to access to the controller.
The default/index and default/user controllers can not be restricted (otherwise users can not authenticate to gain permission in the first place).
Mixing Controller Rules and Table Rules
A controller rule limits the actions a user role can perform in that controller - irrespective of the table accessed by that controller.
A table rule limits the actions a user role can perform on that table - regardless through which controller the table is accessed.
If, in any situation, both rules exist, the most restrictive rule applies. That means:
- if an action is not permitted in that controller, then it is not permitted for any table accessed by that controller - even if table rules would allow it
- if an action is not permitted for a table, then it is not permitted in any controller - even if controller rules would allow it
This makes it possible to:
- control access levels for each table individually where a controller accesses multiple tables (e.g. component tabs, custom forms)
- grant permission for a certain action on a table, while confining it to a particular controller
When no controller rule exists for a role yet the module is restricted, access to that controller will be denied for that role.
Controller rules can be function-specific (function!=None) or module-wide (function==None). If no function-specific rule exists, the module-wide rule applies.
If for a table no table rule exists for any role, the table is treated as unrestricted and no table rule will be applied at all. Controller rules still apply in that situation, however.
If a table rule exists for at least one role, then the table is restricted. If for a restricted table no table rule exists for the role in question, then the respective controller rule will be used as the table rule. If no such controller rule exists for that role either, then access to the table will be denied.
Multiple User Roles
If a user is assigned multiple roles, and thus more than one rule exists for a situation, then the most permissive rule applies.
That means, if one role grants the user a permission, that permission can not be denied by any other of the user's roles.
Permissions granted by a role are limited to the records that belong to the realm the role has been assigned for - if the role is realm-restricted.
The "A-roles" can never be realm-restricted:
Other roles can be assigned without realm-restriction by setting pe_id=None for the auth_membership record.
The realm rule does not apply for the permission to CREATE records.
uACL vs oACL
The uACL (universal ACL) defines the permissions granted by the rule for any record.
The oACL (owner-ACL) defines additional permissions for records owned by the user, either as individual user (owned_by_user) or as member of a role (owned_by_group).
Since record ownership is only established after the record has been created, there can not be an oACL-only CREATE permission. Consequently, CREATE-permission is always universal (uACL).
Ownership vs. Realms
Users can individually own records outside of any of their realms. For such personal ownership outside of the user's realms, the oACL applies.
Users can not own by role records outside of the realm that the role has been assigned for. For records owned by role outside of the user's realms, the oACL does not apply.
Realms of person entities which are OUs (organisation units) of another person entity, become part of the realm of that other "parent" person entity (policy 7 and above).
That means that if a role is assigned for an entity, its permissions apply to the realm of that entity and equally to the realms of all its descendant OUs.
The OU-relationship is controlled by pr_role/pr_affiliation, and normally set implicitly onaccept.
The default realm is the combined realm of all entities of which the user's person record is an immediate descendant OU (=can be multiple).
If the user's person record is not an OU of any other person entity, then the default realm is only that person record itself.
A default realm entity can be any type of person entity - an organisation, a facility, a team etc. When the user's affiliation with that entity ends, the realm of that entity will no longer belong to the user's default realm. This allows for implicit control of permissions through user-managed relationships (e.g. staff records, team memberships).
Controllers are free to implement additional authorization requirements outside of ACLs, and call auth.permission.fail() individually.
Those non-ACL rules can further restrict access to data or functions (e.g. hide fields, reject certain requests), but they can not bypass ACL rules applied by S3 framework functions.
In particular, non-ACL rules can not allow access to controllers while ACL rules deny it.
ADMIN has all roles (=auth.s3_has_role is always True if the user is ADMIN, regardless of the role asked for).
ADMIN has all permissions (=auth.s3_has_permission is always True if the user is ADMIN)
auth.s3_has_role(ANONYMOUS) is always True (ANONYMOUS role is assigned to all users, logged-in or not).
Logged-in users always have at least the same permissions as anonymous users (because they could gain them anyway by simply logging).
auth.s3_has_role(ANONYMOUS) is not a suitable means to differentiate between logged-in users and unauthenticated users (because it is always True). Instead, one should use one of the following alternatives:
- auth.s3_logged_in() (which performs an implicit HTTP Basic Authentication if necessary), or
- auth.user!=None (if no implicit Basic Authentication is desired), or
auth.s3_has_role(AUTHENTICATED) is always True when the user is logged in (AUTHENTICATED role is automatically assigned to the user when logged-in)
It is possible to temporarily disable all authorization by setting auth.override=True. This setting applies until the end of the current request cycle.
This is useful in scripts and tests, but it should never be done in any permanent functions that could even possibly be exposed to the user.
Records created during a session are owned by that session, and oACLs apply like for personally owned records (i.e. without realm restriction) - even if the user is not logged in.
This ownership ends when the user logs in or out, or when the session is otherwise terminated or cleared.
Session ownership allows anonymous users to temporarily own the records they create, and thus to apply oACL rules even for unauthenticated users (=allow e.g. update or delete without the need to allow it for all records)