<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://ohcnwiki.tellmey.fyi/index.php?action=history&amp;feed=atom&amp;title=References%2FPermission_Association</id>
	<title>References/Permission Association - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://ohcnwiki.tellmey.fyi/index.php?action=history&amp;feed=atom&amp;title=References%2FPermission_Association"/>
	<link rel="alternate" type="text/html" href="https://ohcnwiki.tellmey.fyi/index.php?title=References/Permission_Association&amp;action=history"/>
	<updated>2026-07-06T05:22:10Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.45.4</generator>
	<entry>
		<id>https://ohcnwiki.tellmey.fyi/index.php?title=References/Permission_Association&amp;diff=431&amp;oldid=prev</id>
		<title>Admin: Automated edit (via update-page on MediaWiki MCP Server)</title>
		<link rel="alternate" type="text/html" href="https://ohcnwiki.tellmey.fyi/index.php?title=References/Permission_Association&amp;diff=431&amp;oldid=prev"/>
		<updated>2026-07-06T04:09:11Z</updated>

		<summary type="html">&lt;p&gt;Automated edit (via update-page on MediaWiki MCP Server)&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 09:39, 6 July 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;
  &lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 287:&lt;/td&gt;
  &lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 287:&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;expiry&amp;lt;/code&amp;gt; is advisory at the model layer; do not assume the row is automatically deactivated once it passes.&lt;/div&gt;&lt;/td&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* &amp;lt;code&amp;gt;expiry&amp;lt;/code&amp;gt; is advisory at the model layer; do not assume the row is automatically deactivated once it passes.&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* The granted &amp;lt;code&amp;gt;role&amp;lt;/code&amp;gt; is written and read through the role specs (&amp;lt;code&amp;gt;RoleCreateSpec&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;RoleReadSpec&amp;lt;/code&amp;gt;), and each role&#039;s &amp;lt;code&amp;gt;contexts&amp;lt;/code&amp;gt; is bound to the &amp;lt;code&amp;gt;RoleContext&amp;lt;/code&amp;gt; enum rather than an open string at the API layer.&lt;/div&gt;&lt;/td&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* The granted &amp;lt;code&amp;gt;role&amp;lt;/code&amp;gt; is written and read through the role specs (&amp;lt;code&amp;gt;RoleCreateSpec&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;RoleReadSpec&amp;lt;/code&amp;gt;), and each role&#039;s &amp;lt;code&amp;gt;contexts&amp;lt;/code&amp;gt; is bound to the &amp;lt;code&amp;gt;RoleContext&amp;lt;/code&amp;gt; enum rather than an open string at the API layer.&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td colspan=&quot;2&quot; class=&quot;diff-empty diff-side-deleted&quot;&gt;&lt;/td&gt;
  &lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td colspan=&quot;2&quot; class=&quot;diff-empty diff-side-deleted&quot;&gt;&lt;/td&gt;
  &lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Navbox access governance}}&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/td&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Related}}&lt;/div&gt;&lt;/td&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Related}}&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td colspan=&quot;2&quot; class=&quot;diff-empty diff-side-deleted&quot;&gt;&lt;/td&gt;
  &lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Wiki: ohcnwiki.tellmey.fyi&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://ohcnwiki.tellmey.fyi/index.php?title=References/Permission_Association&amp;diff=129&amp;oldid=prev</id>
		<title>Admin: OHC identity seed</title>
		<link rel="alternate" type="text/html" href="https://ohcnwiki.tellmey.fyi/index.php?title=References/Permission_Association&amp;diff=129&amp;oldid=prev"/>
		<updated>2026-07-04T22:43:52Z</updated>

		<summary type="html">&lt;p&gt;OHC identity seed&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Doc header&lt;br /&gt;
|type=reference&lt;br /&gt;
|domain=access-governance&lt;br /&gt;
|title=Permission Association&lt;br /&gt;
|order=5&lt;br /&gt;
|introduced=3.0&lt;br /&gt;
|model=PermissionAssociation&lt;br /&gt;
|reference=References/Base models &amp;amp;amp; conventions&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;RoleAssociation&amp;lt;/code&amp;gt; grants a [[References/Role|&amp;lt;code&amp;gt;RoleModel&amp;lt;/code&amp;gt;]] to a [[References/User &amp;amp;amp; Skills|&amp;lt;code&amp;gt;User&amp;lt;/code&amp;gt;]] inside a specific context — an organization or facility. A role is a named collection of [[References/Permission|permissions]]; the association scopes that collection to one user in one context. A user can hold many roles across different contexts, one association per grant.&lt;br /&gt;
&lt;br /&gt;
The Django model (&amp;lt;code&amp;gt;care/security/models/permission_association.py&amp;lt;/code&amp;gt;) is the storage layer: the &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;role&amp;lt;/code&amp;gt; foreign keys, the generic &amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;context_id&amp;lt;/code&amp;gt; pair, and &amp;lt;code&amp;gt;expiry&amp;lt;/code&amp;gt;. It has no resource spec of its own — it is a platform-internal authorization join, not a client-writable EMR resource. Two sets of Pydantic resource specs surround it: those that govern the [[References/Role|&amp;lt;code&amp;gt;role&amp;lt;/code&amp;gt;]] it points at (&amp;lt;code&amp;gt;care/emr/resources/role/spec.py&amp;lt;/code&amp;gt;), and the permission-resolution mixins (&amp;lt;code&amp;gt;care/emr/resources/permissions.py&amp;lt;/code&amp;gt;) that read these rows to compute a user&amp;#039;s effective permissions in a context.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Source:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Model: [&amp;lt;nowiki/&amp;gt;https://github.com/ohcnetwork/care/blob/develop/care/security/models/permission_association.py &amp;lt;code&amp;gt;care/security/models/permission_association.py&amp;lt;/code&amp;gt;]&lt;br /&gt;
* Role spec (the granted role): [&amp;lt;nowiki/&amp;gt;https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/role/spec.py &amp;lt;code&amp;gt;care/emr/resources/role/spec.py&amp;lt;/code&amp;gt;]&lt;br /&gt;
* Permission-resolution mixins: [&amp;lt;nowiki/&amp;gt;https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/permissions.py &amp;lt;code&amp;gt;care/emr/resources/permissions.py&amp;lt;/code&amp;gt;]&lt;br /&gt;
* Role definitions / &amp;lt;code&amp;gt;RoleContext&amp;lt;/code&amp;gt;: [&amp;lt;nowiki/&amp;gt;https://github.com/ohcnetwork/care/blob/develop/care/security/roles/role.py &amp;lt;code&amp;gt;care/security/roles/role.py&amp;lt;/code&amp;gt;]&lt;br /&gt;
* Permission contexts: [&amp;lt;nowiki/&amp;gt;https://github.com/ohcnetwork/care/blob/develop/care/security/permissions/constants.py &amp;lt;code&amp;gt;care/security/permissions/constants.py&amp;lt;/code&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
== Models ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Model&lt;br /&gt;
! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RoleAssociation&amp;lt;/code&amp;gt;&lt;br /&gt;
| Grants a &amp;lt;code&amp;gt;RoleModel&amp;lt;/code&amp;gt; to a &amp;lt;code&amp;gt;User&amp;lt;/code&amp;gt; within a named context, with optional expiry&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;RoleAssociation&amp;lt;/code&amp;gt; extends &amp;lt;code&amp;gt;BaseModel&amp;lt;/code&amp;gt; — the lightweight Care base providing &amp;lt;code&amp;gt;external_id&amp;lt;/code&amp;gt; (UUID), &amp;lt;code&amp;gt;created_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;modified_date&amp;lt;/code&amp;gt;, and soft-delete via &amp;lt;code&amp;gt;deleted&amp;lt;/code&amp;gt; (the overridden &amp;lt;code&amp;gt;delete()&amp;lt;/code&amp;gt; sets &amp;lt;code&amp;gt;deleted=True&amp;lt;/code&amp;gt; instead of removing the row). See [[References/Base models &amp;amp;amp; conventions|Base model]].&lt;br /&gt;
&lt;br /&gt;
It does not extend &amp;lt;code&amp;gt;EMRBaseModel&amp;lt;/code&amp;gt;, so there are no &amp;lt;code&amp;gt;created_by&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;updated_by&amp;lt;/code&amp;gt; audit fields, no &amp;lt;code&amp;gt;history&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;meta&amp;lt;/code&amp;gt; JSON, and no &amp;lt;code&amp;gt;external_id&amp;lt;/code&amp;gt;-routed EMR API. There is no &amp;lt;code&amp;gt;RoleAssociationCreateSpec&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;...ReadSpec&amp;lt;/code&amp;gt;; rows are created and removed through Care&amp;#039;s access-control flows, not through a Pydantic serializer.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;code&amp;gt;RoleAssociation&amp;lt;/code&amp;gt; fields ==&lt;br /&gt;
&lt;br /&gt;
=== Subject and role ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Required&lt;br /&gt;
! Default&lt;br /&gt;
! Notes&lt;br /&gt;
{{Field|model=PermissionAssociation|section=Subject and role|name=user|type=FK → User|notes=yes}}&lt;br /&gt;
{{Field|model=PermissionAssociation|section=Subject and role|name=role|type=FK → RoleModel|notes=yes}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Context ===&lt;br /&gt;
&lt;br /&gt;
The context identifies where the role applies. Storing it as a type/id pair rather than a typed foreign key lets one table scope roles to any kind of context — organization, facility, and so on. The pair has no DB-level referential integrity, so nothing stops &amp;lt;code&amp;gt;context_id&amp;lt;/code&amp;gt; from pointing at a row that no longer exists.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Required&lt;br /&gt;
! Default&lt;br /&gt;
! Notes&lt;br /&gt;
{{Field|model=PermissionAssociation|section=Context|name=context|type=CharField(1024)|notes=yes}}&lt;br /&gt;
{{Field|model=PermissionAssociation|section=Context|name=context_id|type=BigIntegerField|notes=yes}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Lifecycle ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Required&lt;br /&gt;
! Default&lt;br /&gt;
! Notes&lt;br /&gt;
{{Field|model=PermissionAssociation|section=Lifecycle|name=expiry|type=DateTimeField|notes=no}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Related models ==&lt;br /&gt;
&lt;br /&gt;
The granted role resolves to permissions through the models below. None belong to &amp;lt;code&amp;gt;RoleAssociation&amp;lt;/code&amp;gt;, but every effective-permission lookup walks through them.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;RoleModel&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
The granted role ([[References/Role]]). A named, flat set of permissions; &amp;lt;code&amp;gt;is_system&amp;lt;/code&amp;gt; roles are platform-seeded and cannot be edited through the API.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;name        → CharField(1024)        # unique among non-deleted rows&lt;br /&gt;
description → TextField (default &amp;quot;&amp;quot;)&lt;br /&gt;
is_system   → BooleanField (default False)&lt;br /&gt;
is_archived → BooleanField (default False)&lt;br /&gt;
temp_deleted→ BooleanField (default False)&lt;br /&gt;
contexts    → ArrayField[CharField(24)] (default [])   # bound to RoleContext in specs&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== &amp;lt;code&amp;gt;RolePermission&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Join table linking one &amp;lt;code&amp;gt;RoleModel&amp;lt;/code&amp;gt; to one &amp;lt;code&amp;gt;PermissionModel&amp;lt;/code&amp;gt;. A role accumulates permissions across many &amp;lt;code&amp;gt;RolePermission&amp;lt;/code&amp;gt; rows; lookups exclude &amp;lt;code&amp;gt;temp_deleted=True&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;role         → FK RoleModel (CASCADE, required)&lt;br /&gt;
permission   → FK PermissionModel (CASCADE, required)&lt;br /&gt;
temp_deleted → BooleanField (default False)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== &amp;lt;code&amp;gt;PermissionModel&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
The atomic permission a role grants ([[References/Permission]]).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;slug         → CharField(1024) unique, indexed&lt;br /&gt;
name         → CharField(1024)&lt;br /&gt;
description  → TextField (default &amp;quot;&amp;quot;)&lt;br /&gt;
context      → CharField(1024)   # one of the PermissionContext values&lt;br /&gt;
temp_deleted → BooleanField (default False)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== Enum / value tables ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;RoleAssociation&amp;lt;/code&amp;gt; stores plain strings and integers, but these enums constrain the role it points at and the permissions that role resolves to.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;RoleContext&amp;lt;/code&amp;gt; values ===&lt;br /&gt;
&lt;br /&gt;
Each element of the granted role&amp;#039;s &amp;lt;code&amp;gt;contexts&amp;lt;/code&amp;gt; array is validated against this enum (&amp;lt;code&amp;gt;care/security/roles/role.py&amp;lt;/code&amp;gt;). These are the organizational boundary types a role can apply to — distinct from &amp;lt;code&amp;gt;PermissionContext&amp;lt;/code&amp;gt;. The free-form &amp;lt;code&amp;gt;RoleAssociation.context&amp;lt;/code&amp;gt; column names an entity of one of these boundary types.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Value&lt;br /&gt;
! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FACILITY&amp;lt;/code&amp;gt;&lt;br /&gt;
| Role applies within a facility&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;GOVT_ORG&amp;lt;/code&amp;gt;&lt;br /&gt;
| Role applies within a government organization&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ROLE_ORG&amp;lt;/code&amp;gt;&lt;br /&gt;
| Role applies within a role (user-group) organization&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;PermissionContext&amp;lt;/code&amp;gt; values ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;PermissionModel.context&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;Permission.context&amp;lt;/code&amp;gt; takes one of these values (&amp;lt;code&amp;gt;care/security/permissions/constants.py&amp;lt;/code&amp;gt;). When computing effective permissions from &amp;lt;code&amp;gt;RoleAssociation&amp;lt;/code&amp;gt; rows, the permission-resolution mixins (below) filter a user&amp;#039;s grants by these contexts.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Value&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;GENERIC&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FACILITY&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PATIENT&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;QUESTIONNAIRE&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ORGANIZATION&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FACILITY_ORGANIZATION&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ENCOUNTER&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;PermissionEnum&amp;lt;/code&amp;gt; (role write field) ===&lt;br /&gt;
&lt;br /&gt;
When a role is written through &amp;lt;code&amp;gt;RoleCreateSpec&amp;lt;/code&amp;gt;, its &amp;lt;code&amp;gt;permissions&amp;lt;/code&amp;gt; field is typed against &amp;lt;code&amp;gt;PermissionController.get_enum()&amp;lt;/code&amp;gt; — a &amp;lt;code&amp;gt;str&amp;lt;/code&amp;gt; enum built at runtime from every registered permission &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; across all permission handlers (for example &amp;lt;code&amp;gt;can_create_patient&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;can_write_patient&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;can_list_patients&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;can_view_clinical_data&amp;lt;/code&amp;gt;). The set is deployment-dependent: internal handlers plus any plugin-registered ones, not a fixed list.&lt;br /&gt;
&lt;br /&gt;
=== Built-in (&amp;lt;code&amp;gt;is_system&amp;lt;/code&amp;gt;) roles ===&lt;br /&gt;
&lt;br /&gt;
Seeded by &amp;lt;code&amp;gt;RoleController&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;care/security/roles/role.py&amp;lt;/code&amp;gt;). Associations often bind a user to one of these. They cannot be created, updated, or deleted through the API.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Role&lt;br /&gt;
! Contexts&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Doctor&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;FACILITY&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;GOVT_ORG&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Nurse&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;FACILITY&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;GOVT_ORG&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Staff&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;FACILITY&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;GOVT_ORG&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Volunteer&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;FACILITY&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;GOVT_ORG&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Pharmacist&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;FACILITY&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Administrator&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;FACILITY&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;GOVT_ORG&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Facility Admin&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;FACILITY&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Admin&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;FACILITY&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;GOVT_ORG&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Admin&amp;lt;/code&amp;gt; (role org)&lt;br /&gt;
| &amp;lt;code&amp;gt;ROLE_ORG&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Manager&amp;lt;/code&amp;gt; (role org)&lt;br /&gt;
| &amp;lt;code&amp;gt;ROLE_ORG&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Member&amp;lt;/code&amp;gt; (role org)&lt;br /&gt;
| &amp;lt;code&amp;gt;ROLE_ORG&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Resource specs (API schema) ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;RoleAssociation&amp;lt;/code&amp;gt; has no dedicated &amp;lt;code&amp;gt;...CreateSpec&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;...UpdateSpec&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;...ListSpec&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;...RetrieveSpec&amp;lt;/code&amp;gt; — it is not exposed as an EMR resource. Its Pydantic surface is twofold: the specs that define the role an association grants, and the mixins that consume associations to expose a user&amp;#039;s effective permissions on another resource. All extend &amp;lt;code&amp;gt;EMRResource&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;care/emr/resources/base.py&amp;lt;/code&amp;gt;), which provides &amp;lt;code&amp;gt;serialize&amp;lt;/code&amp;gt; (DB → Pydantic via &amp;lt;code&amp;gt;perform_extra_serialization&amp;lt;/code&amp;gt;) and &amp;lt;code&amp;gt;de_serialize&amp;lt;/code&amp;gt; (Pydantic → DB via &amp;lt;code&amp;gt;perform_extra_deserialization&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
=== Specs for the granted role ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spec class&lt;br /&gt;
! Role&lt;br /&gt;
! Fields&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RoleBaseSpec&amp;lt;/code&amp;gt;&lt;br /&gt;
| shared base (&amp;lt;code&amp;gt;__exclude__ = [&amp;amp;quot;permissions&amp;amp;quot;]&amp;lt;/code&amp;gt;)&lt;br /&gt;
| &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_system&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_archived&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;contexts&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RoleCreateSpec&amp;lt;/code&amp;gt;&lt;br /&gt;
| write · create &amp;amp;amp; update&lt;br /&gt;
| base fields + &amp;lt;code&amp;gt;permissions: list[PermissionEnum]&amp;lt;/code&amp;gt; (≥ 1, de-duplicated)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RoleReadSpec&amp;lt;/code&amp;gt;&lt;br /&gt;
| read · detail/list&lt;br /&gt;
| base fields + &amp;lt;code&amp;gt;permissions: list[PermissionSpec]&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RoleReadMinimalSpec&amp;lt;/code&amp;gt;&lt;br /&gt;
| read · minimal/embedded&lt;br /&gt;
| base fields only&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PermissionSpec&amp;lt;/code&amp;gt;&lt;br /&gt;
| nested (read)&lt;br /&gt;
| &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;slug&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;SlugType&amp;lt;/code&amp;gt;), &amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;RoleBaseSpec.contexts&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;list[RoleContext]&amp;lt;/code&amp;gt; — bound to the &amp;lt;code&amp;gt;RoleContext&amp;lt;/code&amp;gt; enum at the API layer even though the model column is an open string array. &amp;lt;code&amp;gt;PermissionSpec.slug&amp;lt;/code&amp;gt; is a &amp;lt;code&amp;gt;SlugType&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;str&amp;lt;/code&amp;gt;, length 5–50, pattern &amp;lt;code&amp;gt;^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$&amp;lt;/code&amp;gt;. Core role validation (&amp;lt;code&amp;gt;RoleCreateSpec.validate_role&amp;lt;/code&amp;gt;, mode=&amp;amp;quot;after&amp;amp;quot;) requires a non-blank unique name (&amp;lt;code&amp;gt;name__iexact&amp;lt;/code&amp;gt;), rejects &amp;lt;code&amp;gt;is_system=True&amp;lt;/code&amp;gt;, rejects updating a system role, and requires ≥ 1 permission. See [[References/Role]] for the full spec breakdown.&lt;br /&gt;
&lt;br /&gt;
=== Permission-resolution mixins (&amp;lt;code&amp;gt;care/emr/resources/permissions.py&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
Other resource specs (patient, encounter, facility) inherit these mixins. When &amp;lt;code&amp;gt;serialize&amp;lt;/code&amp;gt; runs with an authenticated &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;perform_extra_user_serialization&amp;lt;/code&amp;gt; walks the user&amp;#039;s &amp;lt;code&amp;gt;RoleAssociation&amp;lt;/code&amp;gt; rows for that object, resolves the granted roles to permissions, and writes them into &amp;lt;code&amp;gt;mapping[&amp;amp;quot;permissions&amp;amp;quot;]&amp;lt;/code&amp;gt;. This is the read path that turns stored associations into a permission list on the wire.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Mixin&lt;br /&gt;
! Resolves (from the user&amp;#039;s associations)&lt;br /&gt;
! Context filter&lt;br /&gt;
! Extra fields&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PermissionsMixin&amp;lt;/code&amp;gt;&lt;br /&gt;
| base — adds &amp;lt;code&amp;gt;permissions: list[str]&amp;lt;/code&amp;gt;&lt;br /&gt;
| —&lt;br /&gt;
| —&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PatientPermissionsMixin&amp;lt;/code&amp;gt;&lt;br /&gt;
| roles the user holds on a patient&lt;br /&gt;
| &amp;lt;code&amp;gt;permission__context in (&amp;amp;quot;PATIENT&amp;amp;quot;, &amp;amp;quot;FACILITY&amp;amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
| —&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;EncounterPermissionsMixin&amp;lt;/code&amp;gt;&lt;br /&gt;
| roles the user holds on an encounter&lt;br /&gt;
| &amp;lt;code&amp;gt;permission__context in (&amp;amp;quot;ENCOUNTER&amp;amp;quot;, &amp;amp;quot;PATIENT&amp;amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
| —&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FacilityPermissionsMixin&amp;lt;/code&amp;gt;&lt;br /&gt;
| roles on facility root + sub-orgs&lt;br /&gt;
| none (root) / child set excludes &amp;lt;code&amp;gt;can_update_facility&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;root_org_permissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;child_org_permissions&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Permission slugs are resolved via &amp;lt;code&amp;gt;RolePermission.objects.filter(role_id__in=roles, …).values_list(&amp;amp;quot;permission__slug&amp;amp;quot;, flat=True)&amp;lt;/code&amp;gt;, so only active (non-&amp;lt;code&amp;gt;temp_deleted&amp;lt;/code&amp;gt;) grants on the resolved roles count.&lt;br /&gt;
&lt;br /&gt;
== Methods &amp;amp;amp; save behaviour ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;RoleAssociation&amp;lt;/code&amp;gt; defines no model methods, validators, or &amp;lt;code&amp;gt;save()&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;delete()&amp;lt;/code&amp;gt; overrides of its own. It inherits soft-delete from &amp;lt;code&amp;gt;BaseModel&amp;lt;/code&amp;gt;: calling &amp;lt;code&amp;gt;delete()&amp;lt;/code&amp;gt; sets &amp;lt;code&amp;gt;deleted=True&amp;lt;/code&amp;gt; and persists with &amp;lt;code&amp;gt;save(update_fields=[&amp;amp;quot;deleted&amp;amp;quot;])&amp;lt;/code&amp;gt;, and the default manager filters out soft-deleted rows.&lt;br /&gt;
&lt;br /&gt;
A source &amp;lt;code&amp;gt;TODO&amp;lt;/code&amp;gt; flags a planned composite index on &amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;context_id&amp;lt;/code&amp;gt; that does not yet exist; lookups by those fields are not index-backed today.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;expiry&amp;lt;/code&amp;gt; is a stored timestamp only — the model does not enforce it. Expiry-based revocation falls to the authorization layer that reads these associations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;RoleAssociation&amp;lt;/code&amp;gt; has no &amp;lt;code&amp;gt;perform_extra_serialization&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;perform_extra_deserialization&amp;lt;/code&amp;gt;, since it has no spec. The serialization hooks that matter live on the role specs and the permission mixins above.&lt;br /&gt;
&lt;br /&gt;
== API integration notes ==&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;RoleAssociation&amp;lt;/code&amp;gt; is a platform-maintained authorization record, not a client-writable EMR resource. It is created and removed through Care&amp;#039;s access-control flows, not through FHIR, a Pydantic spec, or direct REST writes.&lt;br /&gt;
* The &amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;context_id&amp;lt;/code&amp;gt; pair is a generic (non-FK) reference. &amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt; is a free-form string naming an entity in one of the &amp;lt;code&amp;gt;RoleContext&amp;lt;/code&amp;gt; boundary types, and &amp;lt;code&amp;gt;context_id&amp;lt;/code&amp;gt; is that entity&amp;#039;s integer PK, not its &amp;lt;code&amp;gt;external_id&amp;lt;/code&amp;gt; UUID. Pair the right type string with the matching PK; nothing at the database level enforces it.&lt;br /&gt;
* A user may have many &amp;lt;code&amp;gt;RoleAssociation&amp;lt;/code&amp;gt; rows, one per (role, context) grant. The &amp;lt;code&amp;gt;permissions&amp;lt;/code&amp;gt; mixins compute effective permissions in a given context by unioning the active permission slugs of all resolved roles, filtered by &amp;lt;code&amp;gt;PermissionContext&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;expiry&amp;lt;/code&amp;gt; is advisory at the model layer; do not assume the row is automatically deactivated once it passes.&lt;br /&gt;
* The granted &amp;lt;code&amp;gt;role&amp;lt;/code&amp;gt; is written and read through the role specs (&amp;lt;code&amp;gt;RoleCreateSpec&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;RoleReadSpec&amp;lt;/code&amp;gt;), and each role&amp;#039;s &amp;lt;code&amp;gt;contexts&amp;lt;/code&amp;gt; is bound to the &amp;lt;code&amp;gt;RoleContext&amp;lt;/code&amp;gt; enum rather than an open string at the API layer.&lt;br /&gt;
&lt;br /&gt;
{{Related}}&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
</feed>