<?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</id>
	<title>References/Permission - 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"/>
	<link rel="alternate" type="text/html" href="https://ohcnwiki.tellmey.fyi/index.php?title=References/Permission&amp;action=history"/>
	<updated>2026-07-06T05:22:07Z</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&amp;diff=430&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&amp;diff=430&amp;oldid=prev"/>
		<updated>2026-07-06T04:08:41Z</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:38, 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 215:&lt;/td&gt;
  &lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 215:&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;context&amp;lt;/code&amp;gt; has no DB &amp;lt;code&amp;gt;choices&amp;lt;/code&amp;gt; but is always a &amp;lt;code&amp;gt;PermissionContext&amp;lt;/code&amp;gt; enum string. The mixins filter resource permissions by context, so the value matters.&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;context&amp;lt;/code&amp;gt; has no DB &amp;lt;code&amp;gt;choices&amp;lt;/code&amp;gt; but is always a &amp;lt;code&amp;gt;PermissionContext&amp;lt;/code&amp;gt; enum string. The mixins filter resource permissions by context, so the value matters.&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;* When you write a role, &amp;lt;code&amp;gt;PermissionController.get_enum()&amp;lt;/code&amp;gt; constrains the &amp;lt;code&amp;gt;permissions&amp;lt;/code&amp;gt; field to known slugs; an unknown slug fails validation. See [[References/Role]].&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;* When you write a role, &amp;lt;code&amp;gt;PermissionController.get_enum()&amp;lt;/code&amp;gt; constrains the &amp;lt;code&amp;gt;permissions&amp;lt;/code&amp;gt; field to known slugs; an unknown slug fails validation. See [[References/Role]].&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&amp;diff=130&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&amp;diff=130&amp;oldid=prev"/>
		<updated>2026-07-04T22:43:55Z</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&lt;br /&gt;
|order=4&lt;br /&gt;
|introduced=3.0&lt;br /&gt;
|model=Permission&lt;br /&gt;
|reference=References/Base models &amp;amp;amp; conventions&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
A permission is the ability to perform one action in one context — read it as &amp;amp;quot;Action on Resource&amp;amp;quot;, like &amp;amp;quot;Can Create Patient&amp;amp;quot; in the &amp;lt;code&amp;gt;PATIENT&amp;lt;/code&amp;gt; context. Permissions are the atoms of Care&amp;#039;s access control: code declares them, the sync command writes them to the database, [[References/Role|roles]] group them, and [[References/Permission Association|permission associations]] bind them to resources. You rarely touch this table directly; you reference its rows by &amp;lt;code&amp;gt;slug&amp;lt;/code&amp;gt; when building roles.&lt;br /&gt;
&lt;br /&gt;
The Django &amp;lt;code&amp;gt;PermissionModel&amp;lt;/code&amp;gt; is only storage; two other places do the real work. The permission registry (&amp;lt;code&amp;gt;care/security/permissions/&amp;lt;/code&amp;gt;) declares every permission as a Python &amp;lt;code&amp;gt;enum&amp;lt;/code&amp;gt; member carrying a &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;context&amp;lt;/code&amp;gt;, and the default &amp;lt;code&amp;gt;roles&amp;lt;/code&amp;gt; that hold it. The Pydantic resource specs (&amp;lt;code&amp;gt;care/emr/resources/role/spec.py&amp;lt;/code&amp;gt;) define the read API schema. This doc covers all three.&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.py &amp;lt;code&amp;gt;care/security/models/permission.py&amp;lt;/code&amp;gt;]&lt;br /&gt;
* Spec: [&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;] (&amp;lt;code&amp;gt;PermissionSpec&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Computed-permission 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;
* Registry: [&amp;lt;nowiki/&amp;gt;https://github.com/ohcnetwork/care/blob/develop/care/security/permissions/base.py &amp;lt;code&amp;gt;care/security/permissions/base.py&amp;lt;/code&amp;gt;], [&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;PermissionModel&amp;lt;/code&amp;gt;&lt;br /&gt;
| A single, named permission (action) that can be granted to a user in a context&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;PermissionModel&amp;lt;/code&amp;gt; extends [[References/Base models &amp;amp;amp; conventions|&amp;lt;code&amp;gt;BaseModel&amp;lt;/code&amp;gt;]], the lowest-level Care base — not &amp;lt;code&amp;gt;EMRBaseModel&amp;lt;/code&amp;gt;. That means 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;, no &amp;lt;code&amp;gt;history&amp;lt;/code&amp;gt;, no &amp;lt;code&amp;gt;meta&amp;lt;/code&amp;gt; columns. The inherited fields:&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;
! Notes&lt;br /&gt;
{{Field|model=Permission|section=Models|name=external_id|type=UUIDField|notes=&amp;lt;code&amp;gt;default=uuid4&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;unique&amp;lt;/code&amp;gt;, indexed. Opaque public identifier — never expose the integer &amp;lt;code&amp;gt;pk&amp;lt;/code&amp;gt;}}&lt;br /&gt;
{{Field|model=Permission|section=Models|name=created_date|type=DateTimeField|notes=&amp;lt;code&amp;gt;auto_now_add&amp;lt;/code&amp;gt;; nullable, indexed}}&lt;br /&gt;
{{Field|model=Permission|section=Models|name=modified_date|type=DateTimeField|notes=&amp;lt;code&amp;gt;auto_now&amp;lt;/code&amp;gt;; nullable, indexed}}&lt;br /&gt;
{{Field|model=Permission|section=Models|name=deleted|type=BooleanField|notes=&amp;lt;code&amp;gt;default=False&amp;lt;/code&amp;gt;, indexed. Soft-delete flag; the default manager hides &amp;lt;code&amp;gt;deleted=True&amp;lt;/code&amp;gt; rows}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;code&amp;gt;PermissionModel&amp;lt;/code&amp;gt; fields ==&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=Permission|section=PermissionModel fields|name=slug|type=CharField(1024)|notes=yes}}&lt;br /&gt;
{{Field|model=Permission|section=PermissionModel fields|name=name|type=CharField(1024)|notes=yes}}&lt;br /&gt;
{{Field|model=Permission|section=PermissionModel fields|name=description|type=TextField|notes=no}}&lt;br /&gt;
{{Field|model=Permission|section=PermissionModel fields|name=context|type=CharField(1024)|notes=yes}}&lt;br /&gt;
{{Field|model=Permission|section=PermissionModel fields|name=temp_deleted|type=BooleanField|notes=no}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt; values (&amp;lt;code&amp;gt;PermissionContext&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt; is a free-form &amp;lt;code&amp;gt;CharField&amp;lt;/code&amp;gt; in the database, but the registry only writes one of these enum values ([&amp;lt;nowiki/&amp;gt;https://github.com/ohcnetwork/care/blob/develop/care/security/permissions/constants.py &amp;lt;code&amp;gt;constants.py&amp;lt;/code&amp;gt;]):&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;GENERIC&amp;lt;/code&amp;gt;&lt;br /&gt;
| Not scoped to a specific resource type&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FACILITY&amp;lt;/code&amp;gt;&lt;br /&gt;
| Scoped to a facility&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PATIENT&amp;lt;/code&amp;gt;&lt;br /&gt;
| Scoped to a patient&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;QUESTIONNAIRE&amp;lt;/code&amp;gt;&lt;br /&gt;
| Scoped to a questionnaire&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ORGANIZATION&amp;lt;/code&amp;gt;&lt;br /&gt;
| Scoped to a (govt/role) organization&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FACILITY_ORGANIZATION&amp;lt;/code&amp;gt;&lt;br /&gt;
| Scoped to a facility organization&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ENCOUNTER&amp;lt;/code&amp;gt;&lt;br /&gt;
| Scoped to an encounter&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The value is load-bearing: when resolving access for a serialized resource, the mixins below filter on &amp;lt;code&amp;gt;permission__context__in=[...]&amp;lt;/code&amp;gt;, so only permissions in the matching context count.&lt;br /&gt;
&lt;br /&gt;
== Related models ==&lt;br /&gt;
&lt;br /&gt;
=== Registry declaration — &amp;lt;code&amp;gt;Permission&amp;lt;/code&amp;gt; dataclass ===&lt;br /&gt;
&lt;br /&gt;
Permissions are authored in code, not the database. Each one is a member of a &amp;lt;code&amp;gt;*Permissions&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;enum&amp;lt;/code&amp;gt;, one per resource area (&amp;lt;code&amp;gt;PatientPermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;EncounterPermissions&amp;lt;/code&amp;gt;, and so on). The member name becomes the &amp;lt;code&amp;gt;slug&amp;lt;/code&amp;gt;; the member value is a &amp;lt;code&amp;gt;Permission&amp;lt;/code&amp;gt; dataclass ([&amp;lt;nowiki/&amp;gt;https://github.com/ohcnetwork/care/blob/develop/care/security/permissions/constants.py &amp;lt;code&amp;gt;constants.py&amp;lt;/code&amp;gt;]):&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;
! Maps to &amp;lt;code&amp;gt;PermissionModel&amp;lt;/code&amp;gt;&lt;br /&gt;
! Notes&lt;br /&gt;
{{Field|model=Permission|section=Registry declaration — Permission dataclass|name=name|type=str|notes=&amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;}}&lt;br /&gt;
{{Field|model=Permission|section=Registry declaration — Permission dataclass|name=description|type=str|notes=&amp;lt;code&amp;gt;description&amp;lt;/code&amp;gt;}}&lt;br /&gt;
{{Field|model=Permission|section=Registry declaration — Permission dataclass|name=context|type=PermissionContext|notes=&amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;.value&amp;lt;/code&amp;gt;)}}&lt;br /&gt;
{{Field|model=Permission|section=Registry declaration — Permission dataclass|name=roles|type=list[Role]|notes=— (drives &amp;lt;code&amp;gt;RolePermission&amp;lt;/code&amp;gt;)}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example (&amp;lt;code&amp;gt;care/security/permissions/patient.py&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;can_create_patient = Permission(&lt;br /&gt;
    &amp;quot;Can Create Patient&amp;quot;, &amp;quot;&amp;quot;, PermissionContext.PATIENT,&lt;br /&gt;
    [STAFF_ROLE, DOCTOR_ROLE, NURSE_ROLE, ADMINISTRATOR, ADMIN_ROLE, FACILITY_ADMIN_ROLE],&lt;br /&gt;
)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;PermissionController&amp;lt;/code&amp;gt; ([&amp;lt;nowiki/&amp;gt;https://github.com/ohcnetwork/care/blob/develop/care/security/permissions/base.py &amp;lt;code&amp;gt;base.py&amp;lt;/code&amp;gt;]) aggregates every handler enum:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;get_permissions() → dict[slug, Permission]&amp;lt;/code&amp;gt; — the full declared registry (cached).&lt;br /&gt;
* &amp;lt;code&amp;gt;get_enum() → Enum&amp;lt;/code&amp;gt; — a dynamically built &amp;lt;code&amp;gt;str&amp;lt;/code&amp;gt; enum of all permission slugs, used by [[References/Role|&amp;lt;code&amp;gt;RoleCreateSpec&amp;lt;/code&amp;gt;]] to constrain the &amp;lt;code&amp;gt;permissions&amp;lt;/code&amp;gt; write field to known slugs.&lt;br /&gt;
&lt;br /&gt;
=== Sync command (&amp;lt;code&amp;gt;sync_permissions_roles&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
The management command [&amp;lt;nowiki/&amp;gt;https://github.com/ohcnetwork/care/blob/develop/care/security/management/commands/sync_permissions_roles.py &amp;lt;code&amp;gt;sync_permissions_roles&amp;lt;/code&amp;gt;] is the only writer of this table. It is idempotent, Redis-locked, and runs in a single transaction:&lt;br /&gt;
&lt;br /&gt;
# Mark every &amp;lt;code&amp;gt;PermissionModel&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;temp_deleted=True&amp;lt;/code&amp;gt;.&lt;br /&gt;
# For each declared permission, upsert by &amp;lt;code&amp;gt;slug&amp;lt;/code&amp;gt; — setting &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;context&amp;lt;/code&amp;gt; (from &amp;lt;code&amp;gt;Permission.context.value&amp;lt;/code&amp;gt;) and clearing &amp;lt;code&amp;gt;temp_deleted&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Hard-delete any row still &amp;lt;code&amp;gt;temp_deleted=True&amp;lt;/code&amp;gt; (no longer declared).&lt;br /&gt;
# Upsert system roles, then rebuild &amp;lt;code&amp;gt;RolePermission&amp;lt;/code&amp;gt; rows from each &amp;lt;code&amp;gt;Permission.roles&amp;lt;/code&amp;gt; list, using the same mark-then-prune pattern.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;RolePermission&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Permissions reach users only through roles. &amp;lt;code&amp;gt;RolePermission&amp;lt;/code&amp;gt; is the join table (&amp;lt;code&amp;gt;role&amp;lt;/code&amp;gt; FK, &amp;lt;code&amp;gt;permission&amp;lt;/code&amp;gt; FK, &amp;lt;code&amp;gt;temp_deleted&amp;lt;/code&amp;gt;) connecting a [[References/Role|&amp;lt;code&amp;gt;RoleModel&amp;lt;/code&amp;gt;]] to a &amp;lt;code&amp;gt;PermissionModel&amp;lt;/code&amp;gt;. A user&amp;#039;s effective access is the union of permissions across their role bindings, filtered to active grants (&amp;lt;code&amp;gt;rolepermission__temp_deleted=False&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Resource specs (API schema) ==&lt;br /&gt;
&lt;br /&gt;
The Pydantic specs build on &amp;lt;code&amp;gt;EMRResource&amp;lt;/code&amp;gt; ([&amp;lt;nowiki/&amp;gt;https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/base.py &amp;lt;code&amp;gt;base.py&amp;lt;/code&amp;gt;]), whose &amp;lt;code&amp;gt;serialize&amp;lt;/code&amp;gt; (DB → spec) and &amp;lt;code&amp;gt;de_serialize&amp;lt;/code&amp;gt; (spec → DB) drive read and write, with &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; hooks for side effects.&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;
! File&lt;br /&gt;
! Notes&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PermissionSpec&amp;lt;/code&amp;gt;&lt;br /&gt;
| read · list + detail&lt;br /&gt;
| &amp;lt;code&amp;gt;role/spec.py&amp;lt;/code&amp;gt;&lt;br /&gt;
| The only permission-facing spec. Served read-only by &amp;lt;code&amp;gt;PermissionViewSet&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PermissionsMixin&amp;lt;/code&amp;gt;&lt;br /&gt;
| read augmentation&lt;br /&gt;
| &amp;lt;code&amp;gt;permissions.py&amp;lt;/code&amp;gt;&lt;br /&gt;
| Adds the requesting user&amp;#039;s computed &amp;lt;code&amp;gt;permissions&amp;lt;/code&amp;gt; list to other resources&amp;#039; serialized output&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
No &amp;lt;code&amp;gt;PermissionCreateSpec&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;PermissionUpdateSpec&amp;lt;/code&amp;gt; exists: permissions are reference data, never client-writable. Write specs exist for roles instead — see [[References/Role]].&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;PermissionSpec&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;__model__ = PermissionModel&amp;lt;/code&amp;gt;. A flat read schema with no extra serialization hooks; it inherits the base mapping that sets &amp;lt;code&amp;gt;id = external_id&amp;lt;/code&amp;gt;:&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;
! Notes&lt;br /&gt;
{{Field|model=Permission|section=PermissionSpec|name=name|type=str|notes=From &amp;lt;code&amp;gt;PermissionModel.name&amp;lt;/code&amp;gt;}}&lt;br /&gt;
{{Field|model=Permission|section=PermissionSpec|name=description|type=str|notes=From &amp;lt;code&amp;gt;PermissionModel.description&amp;lt;/code&amp;gt;}}&lt;br /&gt;
{{Field|model=Permission|section=PermissionSpec|name=slug|type=SlugType|notes=&amp;lt;code&amp;gt;str&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;min_length=5&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;max_length=50&amp;lt;/code&amp;gt;, must match &amp;lt;code&amp;gt;^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$&amp;lt;/code&amp;gt; (URL-safe; starts and ends alphanumeric). See [&amp;lt;nowiki/&amp;gt;https://github.com/ohcnetwork/care/blob/develop/care/emr/utils/slug_type.py &amp;lt;code&amp;gt;slug_type.py&amp;lt;/code&amp;gt;]}}&lt;br /&gt;
{{Field|model=Permission|section=PermissionSpec|name=context|type=str|notes=One of the &amp;lt;code&amp;gt;PermissionContext&amp;lt;/code&amp;gt; values above}}&lt;br /&gt;
{{Field|model=Permission|section=PermissionSpec|name=id|type=UUID4|notes=Added by &amp;lt;code&amp;gt;serialize&amp;lt;/code&amp;gt; as &amp;lt;code&amp;gt;external_id&amp;lt;/code&amp;gt; (inherited base behaviour)}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Served by [&amp;lt;nowiki/&amp;gt;https://github.com/ohcnetwork/care/blob/develop/care/security/api/viewsets/permissions.py &amp;lt;code&amp;gt;PermissionViewSet&amp;lt;/code&amp;gt;], an &amp;lt;code&amp;gt;EMRModelReadOnlyViewSet&amp;lt;/code&amp;gt; (list + retrieve only) with &amp;lt;code&amp;gt;lookup_field=&amp;amp;quot;slug&amp;amp;quot;&amp;lt;/code&amp;gt;, filterable by &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;icontains&amp;lt;/code&amp;gt;). No create, update, or delete endpoint exists.&lt;br /&gt;
&lt;br /&gt;
=== Computed &amp;lt;code&amp;gt;permissions&amp;lt;/code&amp;gt; on other resources (&amp;lt;code&amp;gt;PermissionsMixin&amp;lt;/code&amp;gt;) ===&lt;br /&gt;
&lt;br /&gt;
[&amp;lt;nowiki/&amp;gt;https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/permissions.py &amp;lt;code&amp;gt;permissions.py&amp;lt;/code&amp;gt;] defines mixins that other read specs inherit, so a serialized resource carries the current user&amp;#039;s effective permission slugs for that object. For authenticated users, &amp;lt;code&amp;gt;PermissionsMixin.perform_extra_user_serialization&amp;lt;/code&amp;gt; calls &amp;lt;code&amp;gt;add_permissions(mapping, user, obj)&amp;lt;/code&amp;gt; to populate a &amp;lt;code&amp;gt;permissions: list[str]&amp;lt;/code&amp;gt; field:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Mixin&lt;br /&gt;
! Output fields&lt;br /&gt;
! Resolution&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;PatientPermissionsMixin&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;permissions&amp;lt;/code&amp;gt;&lt;br /&gt;
| Roles on the patient; permission slugs where &amp;lt;code&amp;gt;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;
| &amp;lt;code&amp;gt;EncounterPermissionsMixin&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;permissions&amp;lt;/code&amp;gt;&lt;br /&gt;
| Roles on the encounter; permission slugs where &amp;lt;code&amp;gt;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;
| &amp;lt;code&amp;gt;FacilityPermissionsMixin&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;permissions&amp;lt;/code&amp;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;
| Union of root-org and sub-org role permissions; &amp;lt;code&amp;gt;child_org_permissions&amp;lt;/code&amp;gt; excludes the &amp;lt;code&amp;gt;can_update_facility&amp;lt;/code&amp;gt; slug&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The frontend reads these lists to gate UI by capability, skipping a separate authorization round-trip.&lt;br /&gt;
&lt;br /&gt;
== Methods &amp;amp;amp; save behaviour ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;PermissionModel&amp;lt;/code&amp;gt; adds no custom &amp;lt;code&amp;gt;save()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;delete()&amp;lt;/code&amp;gt; override, validators, or signals — everything comes from &amp;lt;code&amp;gt;BaseModel&amp;lt;/code&amp;gt;, including soft-delete (&amp;lt;code&amp;gt;delete()&amp;lt;/code&amp;gt; setting &amp;lt;code&amp;gt;deleted=True&amp;lt;/code&amp;gt;) and the default manager that filters out soft-deleted rows. The lifecycle runs externally through &amp;lt;code&amp;gt;sync_permissions_roles&amp;lt;/code&amp;gt; (above), which stages with &amp;lt;code&amp;gt;temp_deleted&amp;lt;/code&amp;gt; and hard-deletes undeclared rows.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;PermissionSpec&amp;lt;/code&amp;gt; runs no extra serialization or deserialization: just the base &amp;lt;code&amp;gt;serialize&amp;lt;/code&amp;gt; flow plus the inherited &amp;lt;code&amp;gt;id = external_id&amp;lt;/code&amp;gt; mapping.&lt;br /&gt;
&lt;br /&gt;
== API integration notes ==&lt;br /&gt;
&lt;br /&gt;
* Permissions are platform-maintained reference data: declared in the registry, synced into this table by &amp;lt;code&amp;gt;sync_permissions_roles&amp;lt;/code&amp;gt;, read-only over the API. &amp;lt;code&amp;gt;PermissionViewSet&amp;lt;/code&amp;gt; exposes list + retrieve only.&lt;br /&gt;
* Reference a permission by &amp;lt;code&amp;gt;slug&amp;lt;/code&amp;gt; — it is stable, &amp;lt;code&amp;gt;unique&amp;lt;/code&amp;gt;, and the API &amp;lt;code&amp;gt;lookup_field&amp;lt;/code&amp;gt;. Treat &amp;lt;code&amp;gt;external_id&amp;lt;/code&amp;gt; as the opaque public ID; never expose the integer &amp;lt;code&amp;gt;pk&amp;lt;/code&amp;gt;.&lt;br /&gt;
* You can&amp;#039;t grant a permission directly to a user. A [[References/Role|role]] collects permissions (via &amp;lt;code&amp;gt;RolePermission&amp;lt;/code&amp;gt;), and a [[References/Permission Association|permission association]] binds that role to a resource — organization, patient, encounter, and so on. Effective access is the union of active permissions across a user&amp;#039;s role bindings.&lt;br /&gt;
* &amp;lt;code&amp;gt;context&amp;lt;/code&amp;gt; has no DB &amp;lt;code&amp;gt;choices&amp;lt;/code&amp;gt; but is always a &amp;lt;code&amp;gt;PermissionContext&amp;lt;/code&amp;gt; enum string. The mixins filter resource permissions by context, so the value matters.&lt;br /&gt;
* When you write a role, &amp;lt;code&amp;gt;PermissionController.get_enum()&amp;lt;/code&amp;gt; constrains the &amp;lt;code&amp;gt;permissions&amp;lt;/code&amp;gt; field to known slugs; an unknown slug fails validation. See [[References/Role]].&lt;br /&gt;
&lt;br /&gt;
{{Related}}&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
</feed>