|
| 1 | +--- |
| 2 | +sidebar_position: 6 |
| 3 | +--- |
| 4 | + |
| 5 | +# Responsibility |
| 6 | + |
| 7 | +Technical reference for the **responsibility** — the `role` organization type of the `Organization` model in Care EMR. See the [Responsibility](../../concepts/access-governance/responsibility.mdx) concept for the plain-language layer, and the [Organization](../access-governance/organization.mdx) reference for the full model. |
| 8 | + |
| 9 | +**Source:** |
| 10 | + |
| 11 | +- [`care/emr/models/organization.py`](https://github.com/ohcnetwork/care/blob/develop/care/emr/models/organization.py) |
| 12 | +- [`care/emr/resources/organization/spec.py`](https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/organization/spec.py) |
| 13 | +- [`care/emr/resources/organization/organization_user_spec.py`](https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/organization/organization_user_spec.py) |
| 14 | +- [`care/emr/api/viewsets/organization.py`](https://github.com/ohcnetwork/care/blob/develop/care/emr/api/viewsets/organization.py) |
| 15 | +- [`care/security/authorization/organization.py`](https://github.com/ohcnetwork/care/blob/develop/care/security/authorization/organization.py) |
| 16 | +- [`care/security/permissions/organization.py`](https://github.com/ohcnetwork/care/blob/develop/care/security/permissions/organization.py) |
| 17 | +- [`care/security/roles/role.py`](https://github.com/ohcnetwork/care/blob/develop/care/security/roles/role.py) |
| 18 | + |
| 19 | +A responsibility has no model of its own. It is an `Organization` row whose `org_type` is `role`, one of the four values of `OrganizationTypeChoices` (`team`, `govt`, `role`, `product_supplier`). The behaviour that makes it a responsibility comes from the viewset, the authorization handler, and the role contexts. |
| 20 | + |
| 21 | +## Distinguishing fields |
| 22 | + |
| 23 | +| Field | Type | Behaviour for `org_type = "role"` | |
| 24 | +| --- | --- | --- | |
| 25 | +| `org_type` | `CharField(255)` | Fixed to `role`. Writes bind to `OrganizationTypeChoices` | |
| 26 | +| `parent` | `FK(self), nullable` | Always null. `authorize_create` rejects any organization created under a `role` organization | |
| 27 | +| `has_children` | `BooleanField` | Always `False`. Responsibilities are flat | |
| 28 | +| `managing_organizations` | `ArrayField[int]` | Internal ids of the responsibilities that govern this one. Only `role` organizations may appear here | |
| 29 | +| `name` | `CharField(255)` | Unique among siblings, enforced by `Organization.validate_uniqueness` | |
| 30 | +| `description` | `TextField, nullable` | Free text. Defaults to `""` in the spec | |
| 31 | + |
| 32 | +`OrganizationRetrieveSpec` expands `managing_organizations` to nested `OrganizationReadSpec` JSON, and adds the caller's `permissions`. |
| 33 | + |
| 34 | +## Membership |
| 35 | + |
| 36 | +Membership is an `OrganizationUser` row: `organization` + `user` + `role`. For a responsibility, that `role` is the member's designation. |
| 37 | + |
| 38 | +```text |
| 39 | +OrganizationUser |
| 40 | + organization -> Organization (org_type = "role") |
| 41 | + user -> users.User |
| 42 | + role -> security.RoleModel (contexts contains ROLE_ORG) |
| 43 | +``` |
| 44 | + |
| 45 | +`OrganizationUser.save()` clears `User.cached_role_orgs` whenever the linked organization is of type `role`, so the user's cached responsibility list rebuilds on the next read. `User.get_cached_role_orgs()` repopulates it from `OrganizationUser.get_cached_role_orgs(user_id)`, which serializes each membership with `OrganizationUserExtendedReadSpec`. `UserSpec` and `UserRetrieveSpec` expose the result as `role_orgs`. |
| 46 | + |
| 47 | +`UserCreateSpec` accepts `role_orgs: list[UserRoleOrgCreateSpec]` (each `{ organization, role }`). `UserViewSet.perform_create` creates the memberships inside the same transaction, rejects any organization whose `org_type` is not `role`, and authorizes each pair through `can_manage_organization_users_obj`. |
| 48 | + |
| 49 | +## Designation roles |
| 50 | + |
| 51 | +`RoleModel.contexts` is an `ArrayField` of `RoleContext` values: `FACILITY`, `GOVT_ORG`, `ROLE_ORG`. Only roles carrying `ROLE_ORG` are selectable as designations. `RoleController.internal_roles` defines three: |
| 52 | + |
| 53 | +| Role constant | Name | Description | |
| 54 | +| --- | --- | --- | |
| 55 | +| `ROLE_ORGANIZATION_ADMIN_ROLE` | Admin | Administrator of a role organization | |
| 56 | +| `ROLE_ORGANIZATION_MANAGER_ROLE` | Manager | Manager of a role organization | |
| 57 | +| `ROLE_ORGANIZATION_MEMBER_ROLE` | Member | Member of a role organization | |
| 58 | + |
| 59 | +## Authorization |
| 60 | + |
| 61 | +The `OrganizationViewSet` treats responsibilities differently from other organization types: |
| 62 | + |
| 63 | +| Action | Behaviour | |
| 64 | +| --- | --- | |
| 65 | +| create | `authorize_create` raises `PermissionDenied` for `org_type` in `govt`, `role` unless the caller is a superuser | |
| 66 | +| update | `authorize_update` raises `PermissionDenied` for `org_type` in `govt`, `role` unless the caller is a superuser | |
| 67 | +| destroy | `authorize_destroy` raises `PermissionDenied` for `org_type` in `govt`, `role` unless the caller is a superuser, and for any organization that still has children | |
| 68 | +| retrieve | `get_queryset` returns the unfiltered queryset when the requested organization is of type `role` | |
| 69 | +| `managing_organization` | Both the target and the requested organization must be of type `role`. The caller needs `can_manage_organization_obj` on both | |
| 70 | +| `accessible_role_organizations` | See below | |
| 71 | + |
| 72 | +`OrganizationAccess.can_list_organization_users_obj` and `can_manage_organization_users_obj` widen the search set for a `role` organization from `[*parent_cache, id]` to `[id, *managing_organizations]`. `can_manage_organization_users_obj` therefore passes when the caller holds `can_manage_organization_users` on the responsibility itself, **or** `can_manage_connected_role_organizations` on one of its managing responsibilities. `check_role_subset` still applies: the requested designation must be a subset of the caller's own permissions. |
| 73 | + |
| 74 | +### Permission slugs |
| 75 | + |
| 76 | +| Permission | Roles | |
| 77 | +| --- | --- | |
| 78 | +| `can_view_organization` | Facility Admin, Admin, Staff, Doctor, Administrator, Nurse, Volunteer, Pharmacist, Admin (responsibility), Manager (responsibility), Member (responsibility) | |
| 79 | +| `can_manage_organization` | Admin, Admin (responsibility) | |
| 80 | +| `can_list_organization_users` | Facility Admin, Admin, Staff, Doctor, Administrator, Nurse, Volunteer, Pharmacist, Admin (responsibility), Manager (responsibility) | |
| 81 | +| `can_manage_organization_users` | Admin, Administrator, Facility Admin, Admin (responsibility) | |
| 82 | +| `can_manage_connected_role_organizations` | Admin (responsibility), Manager (responsibility) | |
| 83 | + |
| 84 | +### `accessible_role_organizations` |
| 85 | + |
| 86 | +`GET /api/v1/organization/accessible_role_organizations/` returns `{ count, results }`, where each result is `{ role, organization }`. The set is: |
| 87 | + |
| 88 | +- every `role` organization the caller is a member of, with `role` set to the caller's designation, and |
| 89 | +- every `role` organization governed by a responsibility where the caller holds `can_manage_connected_role_organizations`, with `role` set to `null` when the caller is not a member. |
| 90 | + |
| 91 | +Superusers receive every `role` organization. |
| 92 | + |
| 93 | +## API integration notes |
| 94 | + |
| 95 | +- Responsibilities use the standard organization endpoints under `/api/v1/organization/`, filtered with `org_type=role`. |
| 96 | +- Governance links are written through `POST /api/v1/organization/{id}/managing_organization/` with `{ organization, action }`, where `action` is `add` or `remove`. The body's `organization` is the managing responsibility, and `{id}` is the managed one. |
| 97 | +- Members are read and written through `/api/v1/organization/{id}/users/`. |
| 98 | +- `role_orgs` on the user read specs is a cached, denormalized list. Treat it as read-only. |
| 99 | + |
| 100 | +## Related |
| 101 | + |
| 102 | +- Concept: [Responsibility](../../concepts/access-governance/responsibility.mdx) |
| 103 | +- Reference: [Organization](../access-governance/organization.mdx) · [Role](../access-governance/role.mdx) · [User](../access-governance/user.mdx) · [Permission](../access-governance/permission.mdx) |
0 commit comments