Setting permissions for a curator role
The following example shows how to create and set permissions for a curator
role that is allowed to insert, update, or delete any entity apart from a set of
RecordTypes and properties that define a “core data model” which can only be
altered with administration permissions.
In the following, you’ll learn how to
create the
curator
role.configure the
global_entity_permissions.xml
s.th. thecurator
role is allowed to insert, update, or delete any entity by default.use a Python script to override the above configuration for the entities in the externally defined core data model.
Prerequisites
This example needs some preparations regarding your LinkAhead setup that have to (or, for the sake of simplicity, should) be done outside the actual Python example script.
The curator role
First, a curator
role is created with a meaningful description. We’ll use
linkahead_admin.py
for this which leads to the following command:
$ linkahead_admin.py create_role "curator" "A user who is permitted to create new Records, Properties, and RecordTypes but who is not allowed to change the core data model."
To actually see how this role’s permissions change, we also need a user with
this role. Assume you already have created and activated (see
Administration) a test_curator
user, then
linkahead_admin.py
is used again to assign it the correct role:
$ linkahead_admin.py add_user_roles test_curator curator
Note
The test_curator
user shouldn’t have administration privileges, otherwise
the below changes won’t have any effect.
The core data model and linkahead-advanced-user-tools
In principle, the following script works with any data model defined in a json or yaml file (just adapt lines 39-42 accordingly). In this example, we’ll use the metadata schema that was developed by J. Schmidt at the Leibniz Centre for Tropical Marine Research.
Clone the schemata into the same directory containing the below script via
$ git clone https://github.com/leibniz-zmt/zmt-metadata-schema.git
Furthermore, we’ll need the LinkAhead Advanced User Tools for loading the metadata schemata from the json files, so install them via
$ pip install caosadvancedtools
The global entity permissions file
Users with the curator
role should be able to have any permission for all
entities by default. The exceptions for the core data model entities will be set
with the script below. These default settings are best done via the
global_entities_permissions.xml
config file (see the server documentation). Simply
add the following line to the file
<Grant priority="true" role="curator"><Permission name="*"/></Grant>
This means that, by default, all users with the curator
role are granted
all entity permissions (including insert, update, and delete as specified in the
beginning) with priority. This ensures, that no normal user is allowed to
overrule these permissions (since it is granted with priority), but it can still
be denied for the core data model entities by a deny rule with priority. See
the server documentation on permission
calculation
for more information on which permission rules can or can’t be overruled.
Your complete global_entities_permissions.xml
might then look like
<globalPermissions>
<Grant priority="false" role="?OWNER?"><Permission name="*"/></Grant>
<Grant priority="false" role="?OTHER?"><Permission name="RETRIEVE:*"/></Grant>
<Grant priority="false" role="?OTHER?"><Permission name="USE:*"/></Grant>
<Grant priority="false" role="anonymous"><Permission name="RETRIEVE:*"/></Grant>
<Grant priority="true" role="curator"><Permission name="*"/></Grant>
<Deny priority="false" role="?OTHER?"><Permission name="UPDATE:*"/></Deny>
<Deny priority="false" role="?OTHER?"><Permission name="DELETE"/></Deny>
<Deny priority="true" role="?OTHER?"><Permission name="EDIT:ACL"/></Deny>
</globalPermissions>
Note
Note that you have to restart your LinkAhead server after modifying the
global_entities_permissions.xml
.
The code
After having applied all of the above prerequisites and restarting your LinkAhead server, execute the following code.
#!/usr/bin/env python3
# encoding: utf-8
#
# This file is a part of the LinkAhead Project.
#
# Copyright (C) 2022 Indiscale GmbH <info@indiscale.com>
# Copyright (C) 2022 Florian Spreckelsen <f.spreckelsen@indiscale.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
import os
import sys
import linkahead as db
from caosadvancedtools.models.parser import parse_model_from_json_schema
from linkahead import administration as admin
CURATOR = "curator"
def main():
"""Set curator role permissions: Is allowed to edit all Records; is allowed
to create new RTs and Properties and change them, but is not allowed to
change anything defined in the core data model, i.e., in the schemas.
"""
dataspace_definitions = parse_model_from_json_schema(
"zmt-metadata-schema/schemas/dataspace.schema.json")
dataset_definitions = parse_model_from_json_schema(
"zmt-metadata-schema/schemas/dataset.schema.json")
# Set general permissions. The curator users should be allowed to perform
# any transaction.
perms = admin._get_permissions(CURATOR)
general_grant_perms = [
"TRANSACTION:*"
]
for p in general_grant_perms:
g = admin.PermissionRule(action="Grant", permission=p, priority=True)
d = admin.PermissionRule(action="Deny", permission=p, priority=True)
if g in perms:
perms.remove(g)
if d in perms:
perms.remove(d)
perms.add(g)
admin._set_permissions(CURATOR, permission_rules=perms)
# Deny all permissions that could change the data model ...
core_model_deny_permissions = [
"DELETE",
"UPDATE:*",
"EDIT:ACL"
]
# ... but allow read-access and of course using the entities as parents,
# properties, ...
core_model_grant_permissions = [
"RETRIEVE:*",
"USE:*",
]
# Iterate over all entities defined in the schemas and update their access control list (ACL) accordingly.
updates = db.Container()
for model in [dataspace_definitions, dataset_definitions]:
for ent in model.values():
if ent.name in [u.name for u in updates]:
# Skip entities that have been updated already
continue
# The entity needs to be retrieved with the ACL flag to update the
# ACL down the road
ent.retrieve(flags={"ACL": None})
for d in core_model_deny_permissions:
ent.deny(role=CURATOR, priority=True, permission=d)
ent.update_acl()
ent.retrieve(flags={"ACL": None})
for g in core_model_grant_permissions:
ent.grant(role=CURATOR, priority=True, permission=g)
updates.append(ent)
ent.update_acl()
if __name__ == "__main__":
sys.exit(main())