Design and implement ABAC policy to add authorization checks to the keytray and SSE
Summary
The current system allows all authenticated requests made to the cpabe_server/keytray and SSE storage, and does not enforce any access control policies.
User's access to resources on these services should be restricted according to the permissions on the corresponding resource on XNAT.
XNAT permissions
XNAT uses a role and membership based permission system where a user is a member of one or more project, and for each project they have one of the following roles and corresponding permissions:
Role/Activity | Project Owners | Project Members | Project Collaborators | Project Subjects |
---|---|---|---|---|
Create Data | ✓ | ✓ | ✘ | ✓ |
Read Data | ✓ | ✓ | ✓ | ✘ |
Update Data | ✓ | ✓ | ✘ | ✘ |
Delete Data | ✓ | ✘ | ✘ | ✘ |
Relating SSE keys to XNAT objects
When a scan is uploaded to the system, it is first encrypted with a pair of SSE keys, and that pair of keys has a unique keyid. There is one pair of SSE keys created for each subject in xnat.
the keyid is also stored on the scan object for easy access
Relating SSE resources to XNAT resources
Since the content of all requests to and responses from the SSE are encrypted, we can not use content of the metadata to determine which corresponding XNAT object the resource relates to.
However every request to the SSE should contain the keyid of the pair of keys used to encrypt the resource. So we can use this keyid to determine which subject, and therefore which XNAT project, the resource is related to.
ABAC enforcement
The SSE and cpabe_server/keytray both have an ABAC enforcement layer. With some integration we can use this to enforce a policy driven by XNAT's roles and memberships.
In order to achieve this we will need to create a custom context handler that allows us to use XNAT as a attribute provider.
Early Prototyping for context handler:
When retrieving data from the cpabe_server/keytray or SSE.
- All requests should contain the keycloak access token, from which it is possible to derive the users keycloak user id.
- All requests should contain a keyid either in the URL or in the request body.
Given a keycloak user id, it is possible to retrieve the XNAT projects that the user is a member of like this:
$ curl -su admin:admin http://192.168.178.107:8080/xnat/xapi/access/keycloak_7437293d-b4ca-4e41-90fa-242eeb5a2772/projects?format=json | jq .
[
{
"role": "Owners",
"group_id": "dev_owner",
"secondary_ID": "dev",
"name": "dev",
"description": "",
"ID": "dev",
"URI": "/data/projects/dev"
}
]
It is possible to retrieve the XNAT subject who's data is encrypted with a given keyid, along with the project they are a part of like this:
$ curl -X POST -u admin:admin -s http://192.168.178.107:8080/xnat/data/search/?format=json -F "file=@query.xml" | jq .
{
"ResultSet": {
"Columns": [
{
"key": "xnat_subjectdata_field_map_keyid",
"type": "string",
"xPATH": "xnat:subjectData.XNAT_SUBJECTDATA_FIELD_MAP",
"element_name": "xnat:subjectData",
"header": "keyid",
"id": "XNAT_SUBJECTDATA_FIELD_MAP=keyid"
},
{
"key": "project",
"type": "string",
"xPATH": "xnat:subjectData.PROJECT",
"element_name": "xnat:subjectData",
"header": "Project",
"id": "PROJECT"
},
{
"key": "quarantine_status"
}
],
"Result": [
{
"quarantine_status": "active",
"xnat_subjectdata_field_map_keyid": "c590f041-c5ed-4de6-8eef-d9448ec53a21",
"project": "dev"
}
],
"rootElementName": "xnat:subjectData",
"totalRecords": "1"
}
}
and here is the content of query.xml:
<?xml version="1.0" encoding="utf-8"?>
<xdat:search ID="" allow-diff-columns="0" secure="false" brief-description="Get project for keyid" xmlns:cnda="http://nrg.wustl.edu/cnda" xmlns:arc="http://nrg.wustl.edu/arc" xmlns:pipe="http://nrg.wustl.edu/pipe" xmlns:fs="http://nrg.wustl.edu/fs" xmlns:wrk="http://nrg.wustl.edu/workflow" xmlns:xdat="http://nrg.wustl.edu/security" xmlns:cat="http://nrg.wustl.edu/catalog" xmlns:prov="http://www.nbirn.net/prov" xmlns:xnat="http://nrg.wustl.edu/xnat" xmlns:xnat_a="http://nrg.wustl.edu/xnat_assessments" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xdat:root_element_name>xnat:subjectData</xdat:root_element_name>
<xdat:search_field>
<xdat:element_name>xnat:subjectData</xdat:element_name>
<xdat:field_ID>XNAT_SUBJECTDATA_FIELD_MAP=keyid</xdat:field_ID>
<xdat:sequence>1</xdat:sequence>
<xdat:type>string</xdat:type>
<xdat:header>keyid</xdat:header>
</xdat:search_field>
<xdat:search_field>
<xdat:element_name>xnat:subjectData</xdat:element_name>
<xdat:field_ID>PROJECT</xdat:field_ID>
<xdat:sequence>2</xdat:sequence>
<xdat:type>string</xdat:type>
<xdat:header>Project</xdat:header>
</xdat:search_field>
<xdat:search_where method="AND">
<xdat:child_set method="OR">
<xdat:criteria>
<xdat:schema_field>xnat:subjectData.XNAT_SUBJECTDATA_FIELD_MAP=keyid</xdat:schema_field>
<xdat:comparison_type>=</xdat:comparison_type>
<xdat:value>c590f041-c5ed-4de6-8eef-d9448ec53a21</xdat:value>
</xdat:criteria>
</xdat:child_set>
</xdat:search_where>
</xdat:search>
Links:
- Understanding XNAT User Roles and Permissions: https://wiki.xnat.org/documentation/how-to-use-xnat/user-registration-and-accounts-in-xnat/understanding-user-roles-and-permissions
- Example custom context handler: https://gitlab.com/asclepios-project/abac-docker/-/tree/asclepios/R3/AMBULANCE_EXAMPLE_with_context_handlers/context-handlers