Skip to content

Commit d9ceaf7

Browse files
Add service account and RBAC support (#15)
* Add the service account and rbac support Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 114e02b commit d9ceaf7

16 files changed

+3288
-103
lines changed

Diff for: README.md

+34-4
Original file line numberDiff line numberDiff line change
@@ -248,16 +248,46 @@ In general my focus inside this project is to implement and deliver old and new
248248
- Reload LDAP provisioning configuration
249249
- Rotate data encryption keys
250250

251+
### Service Account
252+
- Search service accounts
253+
- Create a service account
254+
- Get service account by id
255+
- Update a service account
256+
- Get service account token by id
257+
- Create service account token by id
258+
- Delete service account by id
259+
260+
### RBAC
261+
- Get status
262+
- Get all roles
263+
- Get role
264+
- Create role
265+
- Update role
266+
- Delete role
267+
- Get user assigned roles
268+
- Get user assigned permissions
269+
- Add user role assignment
270+
- Update user role assignment
271+
- Remove user role assignment
272+
- Get service account assigned roles
273+
- Get service account assigned permissions
274+
- Add service account role assignment
275+
- Update service account role assignment
276+
- Remove service account role assignment
277+
- Get team assigned roles
278+
- Add team role assignment
279+
- Update team role assignment
280+
- Remove team role assignment
281+
- Reset basic roles to their default
282+
251283
## Feature timeline
252284

253285
The following table describes the plan to implement the rest of the Grafana API functionality. Please, open an issue and vote them up, if you prefer a faster implementation of an API functionality.
254286

255287
| API endpoint group | Implementation week | Maintainer | PR | State |
256288
|:------------------:|:-------------------:|:----------:|:--:|:-----:|
257-
| [Fine-grained access control HTTP API](https://grafana.com/docs/grafana/latest/http_api/access_control/) | 28 | | | |
258-
| [Library Element HTTP API](https://grafana.com/docs/grafana/latest/http_api/library_element/) | 30 | | | |
259-
| [Alerting Provisioning HTTP API](https://grafana.com/docs/grafana/latest/developers/http_api/alerting_provisioning/) | 31 | | | |
260-
| [Service Account HTTP API](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/) | 28 | | | |
289+
| [Library Element HTTP API](https://grafana.com/docs/grafana/latest/http_api/library_element/) | 34 | | | |
290+
| [Alerting Provisioning HTTP API](https://grafana.com/docs/grafana/latest/developers/http_api/alerting_provisioning/) | 36 | | | |
261291

262292
## Installation
263293

Diff for: docs/content/grafana_api/model.md

+39
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
* [AnnotationObject](#grafana_api.model.AnnotationObject)
2222
* [AnnotationGraphiteObject](#grafana_api.model.AnnotationGraphiteObject)
2323
* [GlobalUser](#grafana_api.model.GlobalUser)
24+
* [CustomRole](#grafana_api.model.CustomRole)
25+
* [RolePermission](#grafana_api.model.RolePermission)
2426

2527
<a id="grafana_api.model"></a>
2628

@@ -373,3 +375,40 @@ The class includes all necessary variables to generate a global user object
373375
- `password` _str_ - Specify the password of the user
374376
- `org_id` _int_ - Specify the optional org id of the user (default None)
375377

378+
<a id="grafana_api.model.CustomRole"></a>
379+
380+
## CustomRole Objects
381+
382+
```python
383+
class CustomRole(NamedTuple)
384+
```
385+
386+
The class includes all necessary variables to generate a custom role object
387+
388+
**Arguments**:
389+
390+
- `name` _str_ - Specify the name of the role
391+
- `uid` _str_ - Specify the optional uid of the role (default None)
392+
- `global_role` _bool_ - Specify the if the role is global or not. If set to False, the default org id of the authenticated user will be used from the request (default False)
393+
- `version` _int_ - Specify the optional version of the role (default None)
394+
- `description` _str_ - Specify the optional description of the role (default None)
395+
- `display_name` _str_ - Specify the optional display_name of the role (default None)
396+
- `group` _str_ - Specify the optional org group of the role (default None)
397+
- `hidden` _bool_ - Specify whether the role is hidden or not. If set to True, then the role does not show in the role picker. It will not be listed by API endpoints unless explicitly specified (default False)
398+
- `permissions` _list_ - Specify the optional permissions of the role as a list of the RolePermission objects (default None)
399+
400+
<a id="grafana_api.model.RolePermission"></a>
401+
402+
## RolePermission Objects
403+
404+
```python
405+
class RolePermission(NamedTuple)
406+
```
407+
408+
The class includes all necessary variables to generate a role permission object
409+
410+
**Arguments**:
411+
412+
- `action` _str_ - Specify the custom role action definition
413+
- `scope` _str_ - Specify the scope definition. If not present, no scope will be mapped to the permission (default None)
414+

Diff for: docs/content/index.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@ The following list describes the currently supported features of the Grafana API
1717
- [x] [Folder Permissions HTTP API](https://grafana.com/docs/grafana/v7.5/http_api/folder_permissions/)
1818
- [x] [Search HTTP API](https://grafana.com/docs/grafana/v7.5/http_api/folder_dashboard_search/)
1919
- [x] [External Group Sync HTTP API](https://grafana.com/docs/grafana/latest/http_api/external_group_sync/)
20-
- [ ] [Access control HTTP API](https://grafana.com/docs/grafana/latest/developers/http_api/access_control/)
20+
- [x] [Access control HTTP API](https://grafana.com/docs/grafana/latest/developers/http_api/access_control/)
2121
- [x] [HTTP Preferences API](https://grafana.com/docs/grafana/latest/http_api/preferences/)
2222
- [x] [HTTP Snapshot API](https://grafana.com/docs/grafana/latest/http_api/snapshot/)
23-
- [ ] [Library Element HTTP API](https://grafana.com/docs/grafana/latest/http_api/library_element/)
2423
- [x] [Query History API](https://grafana.com/docs/grafana/latest/http_api/query_history/)
2524
- [x] [Licensing HTTP API](https://grafana.com/docs/grafana/latest/http_api/licensing/)
2625
- [x] [Organization HTTP API](https://grafana.com/docs/grafana/latest/http_api/org/)
@@ -30,7 +29,9 @@ The following list describes the currently supported features of the Grafana API
3029
- [x] [Short URL HTTP API](https://grafana.com/docs/grafana/latest/http_api/short_url/)
3130
- [x] [Team HTTP API](https://grafana.com/docs/grafana/latest/http_api/team/)
3231
- [x] [User HTTP API](https://grafana.com/docs/grafana/latest/http_api/user/)
33-
- [ ] [Service Account HTTP API](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/)
32+
- [x] [Service Account HTTP API](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/)
33+
- [x] [RBAC HTTP API](https://grafana.com/docs/grafana/latest/http_api/access_control/)
34+
- [ ] [Library Element HTTP API](https://grafana.com/docs/grafana/latest/http_api/library_element/)
3435
- [ ] [Alerting Provisioning HTTP API](https://grafana.com/docs/grafana/latest/developers/http_api/alerting_provisioning/)
3536

3637
## Installation

Diff for: docs/grafana_api_sdk.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@ The following list describes the currently supported features of the Grafana API
1717
- [x] [Folder Permissions HTTP API](https://grafana.com/docs/grafana/v7.5/http_api/folder_permissions/)
1818
- [x] [Search HTTP API](https://grafana.com/docs/grafana/v7.5/http_api/folder_dashboard_search/)
1919
- [x] [External Group Sync HTTP API](https://grafana.com/docs/grafana/latest/http_api/external_group_sync/)
20-
- [ ] [Access control HTTP API](https://grafana.com/docs/grafana/latest/developers/http_api/access_control/)
20+
- [x] [Access control HTTP API](https://grafana.com/docs/grafana/latest/developers/http_api/access_control/)
2121
- [x] [HTTP Preferences API](https://grafana.com/docs/grafana/latest/http_api/preferences/)
2222
- [x] [HTTP Snapshot API](https://grafana.com/docs/grafana/latest/http_api/snapshot/)
23-
- [ ] [Library Element HTTP API](https://grafana.com/docs/grafana/latest/http_api/library_element/)
2423
- [x] [Query History API](https://grafana.com/docs/grafana/latest/http_api/query_history/)
2524
- [x] [Licensing HTTP API](https://grafana.com/docs/grafana/latest/http_api/licensing/)
2625
- [x] [Organization HTTP API](https://grafana.com/docs/grafana/latest/http_api/org/)
@@ -30,7 +29,9 @@ The following list describes the currently supported features of the Grafana API
3029
- [x] [Short URL HTTP API](https://grafana.com/docs/grafana/latest/http_api/short_url/)
3130
- [x] [Team HTTP API](https://grafana.com/docs/grafana/latest/http_api/team/)
3231
- [x] [User HTTP API](https://grafana.com/docs/grafana/latest/http_api/user/)
33-
- [ ] [Service Account HTTP API](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/)
32+
- [x] [Service Account HTTP API](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/)
33+
- [x] [RBAC HTTP API](https://grafana.com/docs/grafana/latest/http_api/access_control/)
34+
- [ ] [Library Element HTTP API](https://grafana.com/docs/grafana/latest/http_api/library_element/)
3435
- [ ] [Alerting Provisioning HTTP API](https://grafana.com/docs/grafana/latest/developers/http_api/alerting_provisioning/)
3536

3637
## Installation

Diff for: setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
setuptools.setup(
1010
name="grafana-api-sdk",
11-
version="0.0.7",
11+
version="0.0.8",
1212
author="Pascal Zimmermann",
1313
author_email="[email protected]",
1414
description="A Grafana API SDK",

Diff for: src/grafana_api/admin.py

+38-24
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def update_settings(self, updates: dict = None, removals: dict = None):
7777
.call_the_api(
7878
f"{APIEndpoints.ADMIN.value}/settings",
7979
RequestsMethods.PUT,
80-
json.dumps(settings_update)
80+
json.dumps(settings_update),
8181
)
8282
.json()
8383
)
@@ -153,8 +153,21 @@ def create_global_user(self, user: GlobalUser) -> int:
153153
api_call (int): Returns the corresponding user id
154154
"""
155155

156-
if user is not None and len(user.name) != 0 and len(user.email) != 0 and len(user.login) != 0 and len(user.password) != 0:
157-
user_object: dict = dict({"name": user.name, "email": user.email, "login": user.login, "password": user.password})
156+
if (
157+
user is not None
158+
and len(user.name) != 0
159+
and len(user.email) != 0
160+
and len(user.login) != 0
161+
and len(user.password) != 0
162+
):
163+
user_object: dict = dict(
164+
{
165+
"name": user.name,
166+
"email": user.email,
167+
"login": user.login,
168+
"password": user.password,
169+
}
170+
)
158171

159172
if user.org_id is not None:
160173
user_object.update(dict({"OrgId": user.org_id}))
@@ -199,7 +212,7 @@ def update_user_password(self, id: int, password: str):
199212
.call_the_api(
200213
f"{APIEndpoints.ADMIN.value}/users/{id}/password",
201214
RequestsMethods.PUT,
202-
json.dumps(dict({"password": password}))
215+
json.dumps(dict({"password": password})),
203216
)
204217
.json()
205218
)
@@ -208,7 +221,9 @@ def update_user_password(self, id: int, password: str):
208221
logging.error(f"Please, check the error: {api_call}.")
209222
raise Exception
210223
else:
211-
logging.info("You successfully updated the corresponding user password.")
224+
logging.info(
225+
"You successfully updated the corresponding user password."
226+
)
212227
else:
213228
logging.error("There is no id or password defined.")
214229
raise ValueError
@@ -234,7 +249,7 @@ def update_user_permissions(self, id: int, is_grafana_admin: bool = None):
234249
.call_the_api(
235250
f"{APIEndpoints.ADMIN.value}/users/{id}/permissions",
236251
RequestsMethods.PUT,
237-
json.dumps(dict({"isGrafanaAdmin": is_grafana_admin}))
252+
json.dumps(dict({"isGrafanaAdmin": is_grafana_admin})),
238253
)
239254
.json()
240255
)
@@ -243,7 +258,9 @@ def update_user_permissions(self, id: int, is_grafana_admin: bool = None):
243258
logging.error(f"Please, check the error: {api_call}.")
244259
raise Exception
245260
else:
246-
logging.info("You successfully updated the corresponding user permissions.")
261+
logging.info(
262+
"You successfully updated the corresponding user permissions."
263+
)
247264
else:
248265
logging.error("There is no id or is_grafana_admin defined.")
249266
raise ValueError
@@ -297,7 +314,7 @@ def pause_all_alerts(self):
297314
.call_the_api(
298315
f"{APIEndpoints.ADMIN.value}/pause-all-alerts",
299316
RequestsMethods.POST,
300-
json.dumps(dict({"paused": True}))
317+
json.dumps(dict({"paused": True})),
301318
)
302319
.json()
303320
)
@@ -324,7 +341,7 @@ def unpause_all_alerts(self):
324341
.call_the_api(
325342
f"{APIEndpoints.ADMIN.value}/pause-all-alerts",
326343
RequestsMethods.POST,
327-
json.dumps(dict({"paused": False}))
344+
json.dumps(dict({"paused": False})),
328345
)
329346
.json()
330347
)
@@ -388,7 +405,7 @@ def revoke_user_auth_token(self, id: int, auth_token_id: int):
388405
.call_the_api(
389406
f"{APIEndpoints.ADMIN.value}/users/{id}/revoke-auth-token",
390407
RequestsMethods.POST,
391-
json.dumps(dict({"authTokenId": auth_token_id}))
408+
json.dumps(dict({"authTokenId": auth_token_id})),
392409
)
393410
.json()
394411
)
@@ -422,7 +439,7 @@ def logout_user(self, id: int):
422439
.call_the_api(
423440
f"{APIEndpoints.ADMIN.value}/users/{id}/logout",
424441
RequestsMethods.POST,
425-
json.dumps(dict())
442+
json.dumps(dict()),
426443
)
427444
.json()
428445
)
@@ -451,7 +468,7 @@ def reload_dashboards_provisioning_configuration(self):
451468
.call_the_api(
452469
f"{APIEndpoints.ADMIN.value}/provisioning/dashboards/reload",
453470
RequestsMethods.POST,
454-
json.dumps(dict())
471+
json.dumps(dict()),
455472
)
456473
.json()
457474
)
@@ -477,7 +494,7 @@ def reload_datasources_provisioning_configuration(self):
477494
.call_the_api(
478495
f"{APIEndpoints.ADMIN.value}/provisioning/datasources/reload",
479496
RequestsMethods.POST,
480-
json.dumps(dict())
497+
json.dumps(dict()),
481498
)
482499
.json()
483500
)
@@ -503,7 +520,7 @@ def reload_plugins_provisioning_configuration(self):
503520
.call_the_api(
504521
f"{APIEndpoints.ADMIN.value}/provisioning/plugins/reload",
505522
RequestsMethods.POST,
506-
json.dumps(dict())
523+
json.dumps(dict()),
507524
)
508525
.json()
509526
)
@@ -529,7 +546,7 @@ def reload_notifications_provisioning_configuration(self):
529546
.call_the_api(
530547
f"{APIEndpoints.ADMIN.value}/provisioning/notifications/reload",
531548
RequestsMethods.POST,
532-
json.dumps(dict())
549+
json.dumps(dict()),
533550
)
534551
.json()
535552
)
@@ -555,7 +572,7 @@ def reload_access_controls_provisioning_configuration(self):
555572
.call_the_api(
556573
f"{APIEndpoints.ADMIN.value}/provisioning/access-control/reload",
557574
RequestsMethods.POST,
558-
json.dumps(dict())
575+
json.dumps(dict()),
559576
)
560577
.json()
561578
)
@@ -581,7 +598,7 @@ def reload_ldap_configuration(self):
581598
.call_the_api(
582599
f"{APIEndpoints.ADMIN.value}/ldap/reload",
583600
RequestsMethods.POST,
584-
json.dumps(dict())
601+
json.dumps(dict()),
585602
)
586603
.json()
587604
)
@@ -602,13 +619,10 @@ def rotate_data_encryption_keys(self):
602619
None
603620
"""
604621

605-
api_call: any = (
606-
Api(self.grafana_api_model)
607-
.call_the_api(
608-
f"{APIEndpoints.ADMIN.value}/encryption/rotate-data-keys",
609-
RequestsMethods.POST,
610-
json.dumps(dict())
611-
)
622+
api_call: any = Api(self.grafana_api_model).call_the_api(
623+
f"{APIEndpoints.ADMIN.value}/encryption/rotate-data-keys",
624+
RequestsMethods.POST,
625+
json.dumps(dict()),
612626
)
613627

614628
if api_call.status_code != 204:

Diff for: src/grafana_api/model.py

+40
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ class APIEndpoints(Enum):
4242
ORG_PREFERENCES = f"{api_prefix}/org/preferences"
4343
ANNOTATIONS = f"{api_prefix}/annotations"
4444
ADMIN = f"{api_prefix}/admin"
45+
SERVICE_ACCOUNTS = f"{api_prefix}/serviceaccounts"
46+
RBAC = f"{api_prefix}/access-control"
4547

4648

4749
class RequestsMethods(Enum):
@@ -383,3 +385,41 @@ class GlobalUser(NamedTuple):
383385
login: str
384386
password: str
385387
org_id: int = None
388+
389+
390+
class CustomRole(NamedTuple):
391+
"""The class includes all necessary variables to generate a custom role object
392+
393+
Args:
394+
name (str): Specify the name of the role
395+
uid (str): Specify the optional uid of the role (default None)
396+
global_role (bool): Specify the if the role is global or not. If set to False, the default org id of the authenticated user will be used from the request (default False)
397+
version (int): Specify the optional version of the role (default None)
398+
description (str): Specify the optional description of the role (default None)
399+
display_name (str): Specify the optional display_name of the role (default None)
400+
group (str): Specify the optional org group of the role (default None)
401+
hidden (bool): Specify whether the role is hidden or not. If set to True, then the role does not show in the role picker. It will not be listed by API endpoints unless explicitly specified (default False)
402+
permissions (list): Specify the optional permissions of the role as a list of the RolePermission objects (default None)
403+
"""
404+
405+
name: str
406+
uid: str = None
407+
global_role: bool = False
408+
version: int = None
409+
description: str = None
410+
display_name: str = None
411+
group: str = None
412+
hidden: bool = False
413+
permissions: list = None
414+
415+
416+
class RolePermission(NamedTuple):
417+
"""The class includes all necessary variables to generate a role permission object
418+
419+
Args:
420+
action (str): Specify the custom role action definition
421+
scope (str): Specify the scope definition. If not present, no scope will be mapped to the permission (default None)
422+
"""
423+
424+
action: str
425+
scope: str = None

0 commit comments

Comments
 (0)