Version 54 (modified by 14 years ago) ( diff ) | ,
---|
Table of Contents
DeveloperGuidelinesS3Framework | S3AAA
S3 Authentication, Authorization and Accounting
Authentication is the act of establishing or confirming someone's identity.
Authorization is the concept of allowing access to resources only to those permitted to use them.
Accounting refers to the tracking of user actions - an audit trail.
Overview
AAA functions for S3 are implemented in the modules/s3/s3aaa.py
module. This module extends the web2py Auth class as AuthS3 (Authentication), and defines additional classes for role management, access control and audit.
Component Location Function AuthS3 modules/s3/s3aaa.py Authentication, Login S3Permission modules/s3/s3aaa.py Authorization of Access, ACLs S3Audit modules/s3/s3aaa.py Data access logging, audit trail S3RoleManager modules/s3/s3aaa.py RESTful method to manage roles and ACLs Admin controllers controllers/admin.py User Management, role management
Authentication
- coming soon...
Current user
- coming soon...
Interactive Login
- coming soon...
HTTP Basic Authentication
- coming soon...
Roles
Roles are defined in the auth_group
table, which is defined by the AuthS3
module (in modules/s3/s3aaa.py
).
Each role as an ID, a unique name and an optional description.
Access permissions are granted to roles, while a user gets permissions by assigning roles to him. Role assignment is stored in the auth_membership
table, which is defined by the AuthS3
class (in modules/s3/s3aaa.py
).
At the start of every request, the IDs of all roles of the currently logged-in user are stored as list in session.s3.roles
(in models/00_utils.py
).
In cases where the user logs in during the request (e.g. by HTTP Basic Auth), a refresh of this list is also triggered by the login_bare()
method of AuthS3
.
Roles can be managed in the S3RoleManager
interface (Administration => User Management => Roles).
The following roles are pre-defined in S3 and cannot be changed:
ID Name Description 1 Administrator system administrator 2 Authenticated all authenticated users 3 Anonymous unauthenticated users 4 Editor data editor
The first registered user gets the Administrator role assigned.
Users with the Administrator role always have all permissions, and may access all pages in Eden. The Administrator may also manage Users, Roles and ACLs.
Users with the Editor role may access all data with all methods, except they can not manage Users, Roles or ACLs.
Every authenticated user gets automatically the Authenticated role assigned. This role assignment cannot be revoked.
Simple Authorization
Simple authorization means, that
- anonymous users have read-only permission
- authenticated users have full access
- the Administrator may additionally access the user management
This is the fallback model wherever there are no ACLs defined.
ACLs
Access Control Lists (ACLs) are bit arrays with each bit representing a permissions to access data with a particular method:
Bit Value Permission auth.permission.CREATE 0x01 may create new records auth.permission.READ 0x02 may read or list records auth.permission.UPDATE 0x04 may update existing records auth.permission.DELETE 0x08 may delete records
ACLs are combinations of these bits (by bitwise OR), e.g. an ACL with the value 0x06 defines permissions to read and update records, while no permission to add or to delete any records.
ACLs are stored per role and request destination in the s3_permission
table, which is defined by the S3Permission
class (in modules/s3/s3aaa.py
).
ACLs can be managed by the S3RoleManager
interface (Administration => User Management => Roles).
For every destination (controller/function/table) two ACLs can be defined to apply depending on whether a user ownes the record or not:
- one ACL for users owning a record (Owner ACL =
oacl
) - one ACL for any other user not owning the record (User ACL =
uacl
).
If a user ownes a record, then the most permissive of the User ACL and the Owner ACL gets applied, otherwise only the User ACL applies.
Record Ownership
Tables can implement a record ownership by adding two meta fields:
Field name Type Description administrated_by integer (reference auth_user) ID of the user who administrates this record (defaults to record creator) owned_by integer (reference auth_group) ID of the group (role) who own the record
These meta fields are contained in both s3_authorstamp()
as well as s3_meta_fields()
.
A user is considered owner of a record if he is either administrator of the record (user ID == administrated_by
), or he is a member of the owner group (owned_by
in user roles).
In tables which do not define either of these meta-fields, ownership rules are not applied (uacl
only).
Restrictions
ACLs can be defined for controllers, and for particular functions inside controllers (Controller ACLs).
ACLs can additionally be defined for individual database tables (Table ACLs).
System-wide Policy
The system-wide permission model is configured as security.policy
in deployment settings models/000_config.py
. This defaults to simple authorization (security.policy = 1).
To configure the system-wide policy to use ACLs, set security.policy
to:
deployment_settings.security.policy = 3 # Apply Controller ACLs
or:
deployment_settings.security.policy = 4 # Apply both Controller and Function ACLs
or:
deployment_settings.security.policy = 5 # Apply Controller, Function and Table ACLs
Controller Restriction
To use controller ACLs, it must be specified for which controllers ACLs are to be used.
This can be done by setting the respective controller to restricted=True
in deployment_settings.modules
(models/000_config.py
):
dvi = Storage( name_nice = T("Disaster Victim Identification"), description = T("Disaster Victim Identification"), restricted = True, # Apply controller ACLs for the dvi module module_type = 10, resources = Storage( dvi_recreq = {"importer" : True}, ) ),
If restricted
is False
or undefined for a controller, then simple authorization is used for controller access.
The Controller ACL can be defined for all functions in a controller, and additionally for particular functions inside a controller, where the function-specific ACLs override the general controller ACL. That means, you can define a general ACL for the pr
controller, and a different one for the pr/person
function.
The Controller ACLs are applied to all resources when accessed through this controller/function. If the Controller ACL does not give any permission for the current user (ACL value==auth.permissions.NONE==0x00), then the request is rejected as "Unauthorized". Controllers do not have to implement this check - this is done at a central place (in 00_utils.py
).
Table Restriction
Once the user has passed that controller permission check (must have at least read
permission), and tries to access to a particular table, then the controller checks for table-specific ACLs. This check is to be implemented by the particular controller using s3_has_permission()
and s3_accessible_query
(except controllers using S3CRUD only, which already contains it).
If there is no ACL defined for this table at all (i.e. for none of the users), then the table is considered unrestricted and only the controller ACLs apply.
If there exist ACLs for this table, but not for the current user, access is denied for the current user.
If there are specific ACLs defined for this table and the current user, then the most restrictive of the controller and table ACLs apply (i.e. you cannot allow on the table level what you forbid at the controller level, and vice versa).
Note: For consistency reasons, creating or deleting component records in a resource requires additional permission to update the main record, even though the main record is not changed by this operation, e.g. to add an address to a person record, you must also be permitted to update the person record.
Implementation of Access Control
Permission checking is always a two-step process:
- Check permission to access the controller/function
- Check permission to access the database table
The first step is done at a central point, in 00_utils.py
before the models are loaded. If the ACLs, as defined for the current user, do not specify any permission for the target controller/function, then the request gets rejected before any models are loaded or the controller is entered.
The second step has to be implemented in the respective controller functions. This can be done in two ways:
- the controller uses s3_rest_controller() with S3CRUD, or,
- the controller uses auth.s3_has_permission() and/or auth.s3_accessible_query() to check permissions before exposing any data to the user
Checking Permissions
To check permissions to access a table (or a particular record) with a certain method, use the auth.s3_has_permission()
method:
authorised = auth.s3_has_permission("read", db.my_table) if authorised: # User may read in the db.my_table
authorised = auth.s3_has_permission("read", db.my_table, record_id=x) if authorised: # User may read record x in db.my_table
The access method can be one of these strings:
- "create": create new records in this table
- "read": read in this table (or this particular record, if specified)
- "update": update existing records in this table (or this particular record, if specified)
- "delete": delete records from this table (or this particular record, if specified)
Query for Accessible Records
You can build a query for all records in a table which are accessible for the current user with a certain method, by using auth.s3_accessible_query
:
# Define your query: query = ... # Get accessible-query (e.g., all readable record in db.my_table): accessible = auth.s3.accessible_query("read", db.my_table) # Combine both parts: query = accessible & query # Perform the query rows = db(query).select(...)
Handling Insufficient Permissions
In case the user has insufficient permissions to access a table/record with the requested method, a well-defined response action must take place depending on the request format:
- in HTML format:
- already authenticated users should be informed about the insufficient permissions, and redirected to a (unrestricted) landing page
- unauthenticated users should be requested to login, and forwarded to a login page
- in all other formats:
- authenticated clients must receive a HTTP 403 (Forbidden) error code to cancel the request properly
- unauthenticated clients must receive a HTTP 401 (Authorization Required) error in order to trigger an authentication attempt
- the client must not be redirected in either of the cases (important!)
All this is covered by the auth.permission.fail()
method:
authorised = auth.shn_has_permission("delete", db.my_table) if not authorised: auth.permission.fail()
You can alter the destinations for redirection by setting:
auth.permission.homepage
for redirection when the user is logged-in, but has insufficient privileges (defaults todefault/index
).auth.permission.loginpage
for redirection when the user is not logged-in (defaults todefault/user/login
).
Example: redirect to my/index
rather than to default/index
in case of insufficient privileges of an authenticated user:
authorised = auth.shn_has_permission("delete", db.my_table) if not authorised: auth.permission.homepage = URL(r=request, c="my", f="index") auth.permission.fail()
Data Access Tracking (Audit)
- coming soon