Skip to content

Commit 5e728a4

Browse files
MaciejWasglatosinski
authored andcommitted
[#69784] server: document permission system
1 parent 424aa3a commit 5e728a4

File tree

3 files changed

+179
-6
lines changed

3 files changed

+179
-6
lines changed

documentation/source/api.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,11 @@ Device Authorization API
9696
:modules: api.v1.auth
9797
:undoc-static:
9898
:order: path
99+
100+
Permissions API
101+
~~~~~~~~~~~~~~~
102+
103+
.. autoflask:: rdfm_mgmt_server:create_docs_app()
104+
:modules: api.v1.permissions
105+
:undoc-static:
106+
:order: path

documentation/source/rdfm_mgmt_server.md

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,16 @@ The authorization server is configured using the following environment variables
154154

155155
For accessing the management API, the RDFM server does not issue any tokens itself.
156156
This task is delegated to the authorization server that is used in conjunction with RDFM.
157+
158+
### Users' and applications' permissions
159+
160+
The authorization server needs to implement certain applications' scopes and users' permissions for users and applications to access RDFM API.
161+
162+
#### Scopes
163+
157164
The following scopes are used for controlling access to different methods of the RDFM API:
158-
- `rdfm_admin_ro` - read-only access to the API (fetching devices, groups, packages)
159-
- `rdfm_admin_rw` - complete administrative access to the API with modification rights
165+
- `rdfm_admin_ro` - read-only access to the API (fetching devices, groups, packages).
166+
- `rdfm_admin_rw` - complete administrative access to the API with modification rights.
160167

161168
Additional rules are defined for package uploading route from [Packages API](api.rst#Packages_API).
162169
- `rdfm_upload_single_file` - allows uploading an artifact of type `single-file`.
@@ -165,6 +172,34 @@ Each package type requires its corresponding scope, or the complete admin access
165172

166173
Refer to the [RDFM Server API Reference chapter](api.rst) for a breakdown of the scopes required for accessing each API method.
167174

175+
#### Permissions
176+
177+
In addition to the above, you can assign specific permissions to users for specific resources (devices, groups, packages).
178+
There are three types of permissions:
179+
180+
* **`read`** permission – Allows listing devices, groups, and packages, as well as downloading packages.
181+
* **`update`** permission – Allows changing, adding, and updating groups and packages.
182+
* **`delete`** permission – Allows deleting groups and packages.
183+
184+
Those permissions are not mutually exclusive and have no hierarchy (none of the above permissions imply or contain the other).
185+
186+
Permissions to a group also apply to the devices and packages within that group.
187+
For example, if you assign a user an `update` permission to a group, they will also be able to update any resources within that group.
188+
These propagated permissions are implicit and are not stored in the RDFM Management Server.
189+
190+
You can inspect the current permissions of each user using the [Permissions API](api.rst#Permissions_API).
191+
192+
##### Assigning a Permission
193+
194+
To assign a permission to a user, you must have the `rdfm_admin_rw` scope and you will need the following information:
195+
* The **ID of the user** you want to assign the permission to, which you can obtain from your OAuth2 provider’s administration panel.
196+
* The **ID of the resource**, which can be retrieved via:
197+
* The [RDFM Manager](rdfm_manager.md) – Use the `rdfm-mgmt {devices,packages,groups} list` command to get a list of resources and their IDs.
198+
* The [RDFM Server API](api.rst) – The `/api/{v2,v1}/{devices,packages,groups}` endpoints will return a list of resources and their IDs.
199+
200+
Permissions can be assigned via a POST request to `/api/v1/permissions`.
201+
For an example of such request, see this [endpoint's documentation](api.rst#post--api-v1-permissions).
202+
168203
### API authentication using Keycloak
169204

170205
#### Running the services

server/src/api/v1/permissions.py

Lines changed: 134 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,47 @@ def model_to_schema(
3737
@management_read_only_api
3838
@wrap_api_exception("permissions fetching failed")
3939
def fetch_all():
40-
"""Fetch all permissions
41-
"""
40+
"""Fetch all permissions
41+
42+
:status 200: no error
43+
:status 401: user did not provide authorization data,
44+
or the authorization has expired
45+
:status 403: user was authorized, but did not have permission
46+
to read permissions
47+
48+
:>jsonarr string created: UTC creation date (RFC822)
49+
:>jsonarr integer id: permission identifier
50+
:>jsonarr str permission: permission type (read/update/delete)
51+
:>jsonarr str resource: type of resource (group/device/package)
52+
:>jsonarr str user_id: id of the user to whom this permission applies
53+
:>jsonarr integer resource_id: id of the resource to which this permission applies
54+
55+
**Example Request**
56+
57+
.. sourcecode:: http
58+
59+
GET /api/v1/permissions HTTP/1.1
60+
Accept: application/json
61+
62+
**Example Response**
63+
64+
.. sourcecode:: http
65+
66+
HTTP/1.1 200 OK
67+
Content-Type: application/json
68+
69+
[
70+
{
71+
"created": "Thu, 16 Jan 2025 13:31:53 -0000",
72+
"id": 1,
73+
"permission": "read",
74+
"resource": "group",
75+
"resource_id": 2,
76+
"user_id": "095e4160-9017-4868-82a5-fe0a0c44d34c"
77+
}
78+
]
79+
""" # noqa: E501
80+
4281
permissions: List[
4382
models.permission.
4483
Permission] = server.instance._permissions_db.fetch_all(
@@ -54,7 +93,46 @@ def fetch_all():
5493
@deserialize_schema(schema_dataclass=Permission, key="perm")
5594
def create(perm: Permission):
5695
"""Create a new permission
57-
"""
96+
97+
:status 200: no error
98+
:status 401: user did not provide authorization data,
99+
or the authorization has expired
100+
:status 403: user was authorized, but did not have permission
101+
to create permissions
102+
103+
**Example Request**
104+
105+
.. sourcecode:: http
106+
107+
POST /api/v1/permissions HTTP/1.1
108+
Content-Type: application/json
109+
Accept: application/json
110+
111+
{
112+
"resource": "group",
113+
"resource_id": 5,
114+
"user_id": "095e4160-9017-4868-82a5-fe0a0c44d34c",
115+
"permission": "read"
116+
}
117+
118+
**Example Response**
119+
120+
.. sourcecode:: http
121+
122+
HTTP/1.1 200 OK
123+
Content-Type: application/json
124+
125+
{
126+
"created": "Thu, 23 Jan 2025 13:03:44 -0000",
127+
"id": 26,
128+
"permission": "read",
129+
"resource": "group",
130+
"resource_id": 5,
131+
"user_id": "095e4160-9017-4868-82a5-fe0a0c44d34c"
132+
}
133+
134+
""" # noqa: E501
135+
58136
permission = models.permission.Permission()
59137
permission.created = datetime.datetime.utcnow()
60138
permission.resource = perm.resource
@@ -76,7 +154,37 @@ def create(perm: Permission):
76154
@wrap_api_exception("permission fetching failed")
77155
def fetch_one(identifier: int):
78156
"""Fetch permission
79-
"""
157+
158+
:status 200: no error
159+
:status 401: user did not provide authorization data,
160+
or the authorization has expired
161+
:status 403: user was authorized, but did not have permission
162+
to read permissions
163+
164+
**Example Request**
165+
166+
.. sourcecode:: http
167+
168+
GET /api/v1/permissions/26 HTTP/1.1
169+
Accept: application/json
170+
171+
**Example Response**
172+
173+
.. sourcecode:: http
174+
175+
HTTP/1.1 200 OK
176+
Content-Type: application/json
177+
178+
{
179+
"created": "Thu, 23 Jan 2025 13:03:44 -0000",
180+
"id": 26,
181+
"permission": "read",
182+
"resource": "group",
183+
"resource_id": 5,
184+
"user_id": "095e4160-9017-4868-82a5-fe0a0c44d34c"
185+
}
186+
""" # noqa: E501
187+
80188
permission: Optional[
81189
models.permission.Permission
82190
] = server.instance._permissions_db.fetch_one(identifier)
@@ -92,7 +200,29 @@ def fetch_one(identifier: int):
92200
@wrap_api_exception("permission deletion failed")
93201
def delete_one(identifier: int):
94202
"""Delete permission
203+
204+
:status 200: no error
205+
:status 401: user did not provide authorization data,
206+
or the authorization has expired
207+
:status 403: user was authorized, but did not have permission
208+
to delete permissions
209+
210+
**Example Request**
211+
212+
.. sourcecode:: http
213+
214+
DELETE /api/v1/permissions/26 HTTP/1.1
215+
216+
**Example Response**
217+
218+
.. sourcecode:: http
219+
220+
HTTP/1.1 200 OK
221+
Content-Type: application/json
222+
223+
{}
95224
"""
225+
96226
permission: Optional[
97227
models.permission.Permission
98228
] = server.instance._permissions_db.fetch_one(identifier)

0 commit comments

Comments
 (0)