Module control.perm
Permission library
- Computing permissions
- Support for the authorization system
Expand source code
"""Permission library
* Computing permissions
* Support for the authorization system
"""
from config import Config as C, Names as N
from control.utils import pick as G
CT = C.tables
CP = C.perm
DEFAULT_PERM = CP.default
GROUP_RANK = CP.groupRank
TABLE_PERM = CT.perm
NOBODY = N.nobody
UNAUTH = N.public
AUTH = N.auth
EDIT = N.edit
OWN = N.own
COORD = N.coord
OFFICE = N.office
SYSTEM = N.system
ROOT = N.root
ALLOW_OUR = set(CT.userTables) | set(CT.userEntryTables)
def checkTable(
auth, table
):
"""Verify whether user's credentials match the requirements for a table.
Every table can only be listed on the interface if the user has permissions
for it.
The table permissions are configured in `tables.yaml`, under the key `perm`.
For each table the minimal role is stated that can access the table.
If a table is not permitted, then its records are also not permitted,
neither are fields in those records.
However, this holds for the generic interface. Individual tables and records
and fields may be regulated in addition by
`control.workflow.apply.WorkflowItem` conditions,
which can open up and close off material.
Parameters
----------
auth: object
The `control.auth.Auth` singleton, from which the group of the current
user can be obtained.
table: string
The table for which permission is required.
Returns
-------
boolean
"""
require = G(TABLE_PERM, table)
if require is None:
return False
if require == UNAUTH:
return True
group = auth.groupRep()
if require == NOBODY:
return False
if require == AUTH:
return group != UNAUTH
isSuper = group in {OFFICE, SYSTEM, ROOT}
if require == OFFICE:
return isSuper
if require == SYSTEM:
return group in {SYSTEM, ROOT}
if require == ROOT:
return group == ROOT
if require == COORD:
return group == COORD or isSuper
def checkPerm(require, perm):
"""Verify whether user's credentials match the requirements.
Parameters
----------
require: string
The required permission level (see the perm.yaml configuration file under
the key `roles`).
perm: dict
User attributes, in particular `group` which is the role a user can play on the
basis of his/her identity.
But it also contains attributes that links a user to ceertain records, e.g. the
records of which (s)he is creator/editor, or National Coordinator.
Returns
-------
boolean
"""
if require == UNAUTH:
return True
group = G(perm, N.group)
if require == NOBODY:
return False
if require == AUTH:
return group != UNAUTH
isSuper = group in {OFFICE, SYSTEM, ROOT}
if require == OFFICE:
return isSuper
if require == SYSTEM:
return group in {SYSTEM, ROOT}
if require == ROOT:
return group == ROOT
if require == EDIT:
return group != UNAUTH and (G(perm, N.isEdit) or isSuper)
if require == OWN:
return group != UNAUTH and (G(perm, N.isOwn) or isSuper)
if require == COORD:
return group == COORD and G(perm, N.sameCountry) or isSuper
def getPermField(table, permRecord, require, actual=None, minimum=None):
"""Determine read/edit permissions for a field in a record in a table.
!!! hint
When it comes to checking a field permission, first get the
record permission, then the field requirements.
Then apply this function to get the result.
Parameters
----------
table: string
Where the record resides
permRecord:
Permissions based on the record as a whole.
require:
Permissions required for reading/editing a particular field,
coming from the table specific .yaml files with field specifications.
actual, minimum: string, optional None
If the permission is dependent on the actual value of this field,
pass the actual value and the minimum value.
This applies to the edit permission.
The typical use case is when the field type is permissionGroup,
and an edit is only allowed if the permissionGroup of the user
has a greater or equal rank than/as the current value of the field.
We assume the ranks will be passed, not the values themselves.
Returns
-------
mayRead: boolean
mayEdit: boolean
"""
mayRead = None
if table in ALLOW_OUR:
mayRead = G(permRecord, N.isOur) or None
if mayRead is None:
readRequire = (
G(DEFAULT_PERM, N.read)
if require is None or N.read not in require
else G(require, N.read)
)
mayRead = checkPerm(readRequire, permRecord)
editRequire = (
G(DEFAULT_PERM, N.edit)
if require is None or N.edit not in require
else G(require, N.edit)
)
mayEdit = checkPerm(editRequire, permRecord)
if mayEdit and actual is not None and minimum is not None:
if G(GROUP_RANK, actual, 0) > G(GROUP_RANK, minimum, 100):
mayEdit = False
return (mayRead, mayEdit)
def permRecord(context, table, record):
"""Determine record permissions.
Various possible relationships between this user and the record will be examined.
Parameters
----------
Returns
-------
group: string
Permission group of current user
country: ObjectId
Country to which the record belongs. Follow the detail-master chain from the
record to the contrib record and read the country from the contrib.
isOwn: boolean
Is the record created by the current user?
isEdit: boolean
Is the record created by the current user or is (s)he in the list of
editors of this record?
sameCountry: boolean
Does the record belong to the same country as the currennt user?
isCoordinated: boolean
Is this record under the jurisdiction of the current user as National
Coordinator?
isOur: boolean
Is this record in the workflow of the current user?
As creator/editor/reviewer/coordinator?
contribId: ObjectId
The id of the contrib to which this record is linked.
These attributes are returned as a `dict`.
"""
auth = context.auth
user = auth.user
uid = G(user, N._id)
group = auth.groupRep()
uCountry = G(user, N.country)
cRecord = {}
aRecord = {}
if table == N.contrib:
cRecord = record
elif table == N.assessment:
aRecord = record
contribId = G(record, N.contrib)
cRecord = context.getItem(N.contrib, contribId)
elif table in {N.review, N.criteriaEntry, N.reviewEntry}:
assessmentId = G(record, N.assessment)
aRecord = context.getItem(N.assessment, assessmentId)
contribId = G(aRecord, N.contrib)
cRecord = context.getItem(N.contrib, contribId)
refCountry = G(cRecord, N.country)
reviewerE = G(aRecord, N.reviewerE)
reviewerF = G(aRecord, N.reviewerF)
reviewers = {reviewerE, reviewerF} - {None}
sameCountry = refCountry is not None and refCountry == uCountry
isAuth = group != UNAUTH and uid is not None
isCreator = uid is not None and uid == G(record, N.creator)
isEditor = uid is not None and uid in (G(record, N.editors) or set())
isCoordinated = isAuth and sameCountry and group == COORD
isACreator = uid is not None and uid == G(aRecord, N.creator)
isAEditor = uid is not None and uid in (G(aRecord, N.editors) or set())
isReviewer = uid is not None and uid in reviewers
isOur = table in ALLOW_OUR and (
isCoordinated
or isCreator
or isEditor
or isACreator
or isAEditor
or isReviewer
)
return {
N.group: group,
N.country: refCountry,
N.isOwn: isAuth and isCreator,
N.isEdit: isAuth and (isCreator or isEditor),
N.sameCountry: sameCountry,
N.isCoordinated: isCoordinated,
N.isOur: isOur,
N.isReviewer: isReviewer,
N.contribId: G(cRecord, N._id),
}
Functions
def checkPerm(require, perm)
-
Verify whether user's credentials match the requirements.
Parameters
require
:string
- The required permission level (see the perm.yaml configuration file under
the key
roles
). perm
:dict
- User attributes, in particular
group
which is the role a user can play on the basis of his/her identity. But it also contains attributes that links a user to ceertain records, e.g. the records of which (s)he is creator/editor, or National Coordinator.
Returns
boolean
Expand source code
def checkPerm(require, perm): """Verify whether user's credentials match the requirements. Parameters ---------- require: string The required permission level (see the perm.yaml configuration file under the key `roles`). perm: dict User attributes, in particular `group` which is the role a user can play on the basis of his/her identity. But it also contains attributes that links a user to ceertain records, e.g. the records of which (s)he is creator/editor, or National Coordinator. Returns ------- boolean """ if require == UNAUTH: return True group = G(perm, N.group) if require == NOBODY: return False if require == AUTH: return group != UNAUTH isSuper = group in {OFFICE, SYSTEM, ROOT} if require == OFFICE: return isSuper if require == SYSTEM: return group in {SYSTEM, ROOT} if require == ROOT: return group == ROOT if require == EDIT: return group != UNAUTH and (G(perm, N.isEdit) or isSuper) if require == OWN: return group != UNAUTH and (G(perm, N.isOwn) or isSuper) if require == COORD: return group == COORD and G(perm, N.sameCountry) or isSuper
def checkTable(auth, table)
-
Verify whether user's credentials match the requirements for a table.
Every table can only be listed on the interface if the user has permissions for it. The table permissions are configured in
tables.yaml
, under the keyperm
. For each table the minimal role is stated that can access the table.If a table is not permitted, then its records are also not permitted, neither are fields in those records.
However, this holds for the generic interface. Individual tables and records and fields may be regulated in addition by
WorkflowItem
conditions, which can open up and close off material.Parameters
auth
:object
- The
Auth
singleton, from which the group of the current user can be obtained. table
:string
- The table for which permission is required.
Returns
boolean
Expand source code
def checkTable( auth, table ): """Verify whether user's credentials match the requirements for a table. Every table can only be listed on the interface if the user has permissions for it. The table permissions are configured in `tables.yaml`, under the key `perm`. For each table the minimal role is stated that can access the table. If a table is not permitted, then its records are also not permitted, neither are fields in those records. However, this holds for the generic interface. Individual tables and records and fields may be regulated in addition by `control.workflow.apply.WorkflowItem` conditions, which can open up and close off material. Parameters ---------- auth: object The `control.auth.Auth` singleton, from which the group of the current user can be obtained. table: string The table for which permission is required. Returns ------- boolean """ require = G(TABLE_PERM, table) if require is None: return False if require == UNAUTH: return True group = auth.groupRep() if require == NOBODY: return False if require == AUTH: return group != UNAUTH isSuper = group in {OFFICE, SYSTEM, ROOT} if require == OFFICE: return isSuper if require == SYSTEM: return group in {SYSTEM, ROOT} if require == ROOT: return group == ROOT if require == COORD: return group == COORD or isSuper
def getPermField(table, permRecord, require, actual=None, minimum=None)
-
Determine read/edit permissions for a field in a record in a table.
Hint
When it comes to checking a field permission, first get the record permission, then the field requirements. Then apply this function to get the result.
Parameters
table
:string
- Where the record resides
- permRecord:
- Permissions based on the record as a whole.
- require:
- Permissions required for reading/editing a particular field,
- coming from the table specific .yaml files with field specifications.
actual
,minimum
:string
, optionalNone
- If the permission is dependent on the actual value of this field, pass the actual value and the minimum value. This applies to the edit permission. The typical use case is when the field type is permissionGroup, and an edit is only allowed if the permissionGroup of the user has a greater or equal rank than/as the current value of the field. We assume the ranks will be passed, not the values themselves.
Returns
mayRead
:boolean
mayEdit
:boolean
Expand source code
def getPermField(table, permRecord, require, actual=None, minimum=None): """Determine read/edit permissions for a field in a record in a table. !!! hint When it comes to checking a field permission, first get the record permission, then the field requirements. Then apply this function to get the result. Parameters ---------- table: string Where the record resides permRecord: Permissions based on the record as a whole. require: Permissions required for reading/editing a particular field, coming from the table specific .yaml files with field specifications. actual, minimum: string, optional None If the permission is dependent on the actual value of this field, pass the actual value and the minimum value. This applies to the edit permission. The typical use case is when the field type is permissionGroup, and an edit is only allowed if the permissionGroup of the user has a greater or equal rank than/as the current value of the field. We assume the ranks will be passed, not the values themselves. Returns ------- mayRead: boolean mayEdit: boolean """ mayRead = None if table in ALLOW_OUR: mayRead = G(permRecord, N.isOur) or None if mayRead is None: readRequire = ( G(DEFAULT_PERM, N.read) if require is None or N.read not in require else G(require, N.read) ) mayRead = checkPerm(readRequire, permRecord) editRequire = ( G(DEFAULT_PERM, N.edit) if require is None or N.edit not in require else G(require, N.edit) ) mayEdit = checkPerm(editRequire, permRecord) if mayEdit and actual is not None and minimum is not None: if G(GROUP_RANK, actual, 0) > G(GROUP_RANK, minimum, 100): mayEdit = False return (mayRead, mayEdit)
def permRecord(context, table, record)
-
Determine record permissions.
Various possible relationships between this user and the record will be examined.
Parameters
Returns
group
:string
- Permission group of current user
country
:ObjectId
- Country to which the record belongs. Follow the detail-master chain from the record to the contrib record and read the country from the contrib.
isOwn
:boolean
- Is the record created by the current user?
isEdit
:boolean
- Is the record created by the current user or is (s)he in the list of editors of this record?
sameCountry
:boolean
- Does the record belong to the same country as the currennt user?
isCoordinated
:boolean
- Is this record under the jurisdiction of the current user as National Coordinator?
isOur
:boolean
- Is this record in the workflow of the current user? As creator/editor/reviewer/coordinator?
contribId
:ObjectId
- The id of the contrib to which this record is linked.
These attributes are returned as a
dict
.Expand source code
def permRecord(context, table, record): """Determine record permissions. Various possible relationships between this user and the record will be examined. Parameters ---------- Returns ------- group: string Permission group of current user country: ObjectId Country to which the record belongs. Follow the detail-master chain from the record to the contrib record and read the country from the contrib. isOwn: boolean Is the record created by the current user? isEdit: boolean Is the record created by the current user or is (s)he in the list of editors of this record? sameCountry: boolean Does the record belong to the same country as the currennt user? isCoordinated: boolean Is this record under the jurisdiction of the current user as National Coordinator? isOur: boolean Is this record in the workflow of the current user? As creator/editor/reviewer/coordinator? contribId: ObjectId The id of the contrib to which this record is linked. These attributes are returned as a `dict`. """ auth = context.auth user = auth.user uid = G(user, N._id) group = auth.groupRep() uCountry = G(user, N.country) cRecord = {} aRecord = {} if table == N.contrib: cRecord = record elif table == N.assessment: aRecord = record contribId = G(record, N.contrib) cRecord = context.getItem(N.contrib, contribId) elif table in {N.review, N.criteriaEntry, N.reviewEntry}: assessmentId = G(record, N.assessment) aRecord = context.getItem(N.assessment, assessmentId) contribId = G(aRecord, N.contrib) cRecord = context.getItem(N.contrib, contribId) refCountry = G(cRecord, N.country) reviewerE = G(aRecord, N.reviewerE) reviewerF = G(aRecord, N.reviewerF) reviewers = {reviewerE, reviewerF} - {None} sameCountry = refCountry is not None and refCountry == uCountry isAuth = group != UNAUTH and uid is not None isCreator = uid is not None and uid == G(record, N.creator) isEditor = uid is not None and uid in (G(record, N.editors) or set()) isCoordinated = isAuth and sameCountry and group == COORD isACreator = uid is not None and uid == G(aRecord, N.creator) isAEditor = uid is not None and uid in (G(aRecord, N.editors) or set()) isReviewer = uid is not None and uid in reviewers isOur = table in ALLOW_OUR and ( isCoordinated or isCreator or isEditor or isACreator or isAEditor or isReviewer ) return { N.group: group, N.country: refCountry, N.isOwn: isAuth and isCreator, N.isEdit: isAuth and (isCreator or isEditor), N.sameCountry: sameCountry, N.isCoordinated: isCoordinated, N.isOur: isOur, N.isReviewer: isReviewer, N.contribId: G(cRecord, N._id), }