Skip to content

Commit 4569dce

Browse files
authored
Issue 84: Add SSO Settings support (#86)
* feat: Add the SSO settings feature
1 parent afeb960 commit 4569dce

File tree

8 files changed

+551
-3
lines changed

8 files changed

+551
-3
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,12 @@ In general, my focus on this project is to implement and deliver old and new fea
347347
- Update correlations
348348
- Delete correlations
349349

350+
### SSO Settings
351+
- Get SSO settings
352+
- Get SSO settings by provider
353+
- Update SSO settings
354+
- Delete SSO settings
355+
350356
## Installation
351357

352358
Please be aware to not install the `grafana-api` and `grafana-api-sdk` packages in parallel and the same environment. This result in name clashes, and it's not possible to use the Grafana API SDK.

docs/content/grafana_api/model.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
* [CustomRole](#model.CustomRole)
3838
* [DatasourceCache](#model.DatasourceCache)
3939
* [PublicDashboard](#model.PublicDashboard)
40+
* [SSOSetting](#model.SSOSetting)
4041

4142
<a id="model"></a>
4243

@@ -726,3 +727,22 @@ The class includes all necessary variables to generate a public dashboard object
726727
- `annotations_enabled` _bool_ - Specify the optional enablement of the annotations inside the public dashboard (default False)
727728
- `share` _str_ - Specify the optional share mode of the public dashboard (default public)
728729

730+
<a id="model.SSOSetting"></a>
731+
732+
## SSOSetting Objects
733+
734+
```python
735+
@dataclass
736+
class SSOSetting()
737+
```
738+
739+
The class includes all necessary variables to generate an SSO setting object
740+
741+
**Arguments**:
742+
743+
- `api_url` _str_ - Specify the SSO api url
744+
- `client_id` _str_ - Specify the SSO client id
745+
- `client_secret` _str_ - Specify the SSO client secret
746+
- `enabled` _bool_ - Specify if the SSO provider is enabled or not
747+
- `scopes` _str_ - Specify the SSO scopes
748+

docs/content/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ The following list describes the currently supported features of the Grafana API
4040
- [x] [Playlist HTTP API](https://grafana.com/docs/grafana/latest/http_api/playlist/)
4141
- [x] [Reporting API](https://grafana.com/docs/grafana/latest/http_api/reporting/)
4242
- [x] [Short URL HTTP API](https://grafana.com/docs/grafana/latest/http_api/short_url/)
43+
- [x] [SSO Settings](https://grafana.com/docs/grafana/latest/developers/http_api/sso-settings/)
4344
- [x] [Team HTTP API](https://grafana.com/docs/grafana/latest/http_api/team/)
4445
- [x] [Team Sync](https://grafana.com/docs/grafana/latest/developers/http_api/team_sync/)
4546
- [x] [User HTTP API](https://grafana.com/docs/grafana/latest/http_api/user/)

docs/grafana_api_sdk.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ The following list describes the currently supported features of the Grafana API
4040
- [x] [Playlist HTTP API](https://grafana.com/docs/grafana/latest/http_api/playlist/)
4141
- [x] [Reporting API](https://grafana.com/docs/grafana/latest/http_api/reporting/)
4242
- [x] [Short URL HTTP API](https://grafana.com/docs/grafana/latest/http_api/short_url/)
43+
- [x] [SSO Settings](https://grafana.com/docs/grafana/latest/developers/http_api/sso-settings/)
4344
- [x] [Team HTTP API](https://grafana.com/docs/grafana/latest/http_api/team/)
4445
- [x] [Team Sync](https://grafana.com/docs/grafana/latest/developers/http_api/team_sync/)
4546
- [x] [User HTTP API](https://grafana.com/docs/grafana/latest/http_api/user/)

grafana_api/model.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ class APIEndpoints(Enum):
2424
ALERTS_ALERTMANAGER: str = f"{api_prefix}/alertmanager"
2525
ALERTS_PROMETHEUS: str = f"{api_prefix}/prometheus"
2626
ALERTS_RULER: str = f"{api_prefix}/ruler"
27-
ALERTS_TESTING: str = f"{api_prefix}/v1"
28-
ALERTS_NGALERT: str = f"{api_prefix}/v1/ngalert"
27+
ALERTS_TESTING: str = f"{api_prefix}/{version_1}"
28+
ALERTS_NGALERT: str = f"{api_prefix}/{version_1}/ngalert"
2929
DATASOURCES: str = f"{api_prefix}/datasources"
3030
DATASOURCE_QUERY: str = f"{api_prefix}/tsdb/query"
3131
SHORT_URLS: str = f"{api_prefix}/short-urls"
@@ -52,6 +52,7 @@ class APIEndpoints(Enum):
5252
RBAC: str = f"{api_prefix}/access-control"
5353
LIBRARY: str = f"{api_prefix}/library-elements"
5454
ALERTING_PROVISIONING: str = f"{api_prefix}/{version_1}/provisioning"
55+
SSO_SETTINGS: str = f"{api_prefix}/{version_1}/sso-settings"
5556

5657

5758
class RequestsMethods(Enum):
@@ -757,3 +758,22 @@ class PublicDashboard:
757758
is_enabled: bool = False
758759
annotations_enabled: bool = False
759760
share: str = "public"
761+
762+
763+
@dataclass
764+
class SSOSetting:
765+
"""The class includes all necessary variables to generate an SSO setting object
766+
767+
Args:
768+
api_url (str): Specify the SSO api url
769+
client_id (str): Specify the SSO client id
770+
client_secret (str): Specify the SSO client secret
771+
enabled (bool): Specify if the SSO provider is enabled or not
772+
scopes (str): Specify the SSO scopes
773+
"""
774+
775+
api_url: str
776+
client_id: str
777+
client_secret: str
778+
enabled: bool
779+
scopes: str

grafana_api/other_http.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def renew_login_session_based_on_remember_cookie(self):
6060
logging.error(f"Check the error: {api_call}.")
6161
raise Exception
6262
else:
63-
logging.info("You successfully destroyed the dashboard snapshot.")
63+
logging.info("You successfully renewed the login session based on the cookie.")
6464

6565
def get_health_status(self) -> dict:
6666
"""The method includes a functionality to get the health information

grafana_api/sso_settings.py

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
import json
2+
import logging
3+
from typing import Union
4+
5+
from .model import (
6+
APIModel,
7+
APIEndpoints,
8+
RequestsMethods,
9+
SSOSetting,
10+
)
11+
from .api import Api
12+
13+
14+
class SSOSettings:
15+
"""The class includes all necessary methods to access the Grafana sso settings API endpoints
16+
17+
Args:
18+
grafana_api_model (APIModel): Inject a Grafana API model object that includes all necessary values and information
19+
20+
Attributes:
21+
grafana_api_model (APIModel): This is where we store the grafana_api_model
22+
"""
23+
24+
def __init__(self, grafana_api_model: APIModel):
25+
self.grafana_api_model = grafana_api_model
26+
27+
def get_sso_settings(self) -> list:
28+
"""The method includes a functionality to get the SSO settings for all providers
29+
30+
Required Permissions:
31+
Action: settings:read
32+
Scope: settings:auth.{provider}:*
33+
34+
Raises:
35+
Exception: Unspecified error by executing the API call
36+
37+
Returns:
38+
api_call (list): Returns the all SSO settings
39+
"""
40+
41+
api_call: Union[list, dict] = Api(self.grafana_api_model).call_the_api(
42+
f"{APIEndpoints.SSO_SETTINGS.value}",
43+
response_status_code=True,
44+
)
45+
46+
status_code: int = api_call[0].get("status") if isinstance(api_call, list) else api_call.get("status")
47+
48+
sso_settings_status_dict: dict = dict(
49+
{
50+
400: "Bad request.",
51+
401: "Unauthorized.",
52+
403: "Access Denied.",
53+
}
54+
)
55+
56+
if status_code == 200:
57+
return api_call
58+
elif 400 <= status_code <= 403:
59+
logging.error(sso_settings_status_dict.get(status_code))
60+
raise Exception
61+
else:
62+
logging.error(f"Check the error: {api_call}.")
63+
raise Exception
64+
65+
def get_sso_settings_by_provider(self, provider: str) -> dict:
66+
"""The method includes a functionality to get the SSO settings for the specified provider
67+
68+
Args:
69+
provider (str): Specify the provider
70+
71+
Required Permissions:
72+
Action: settings:read
73+
Scope: settings:auth.{provider}:*
74+
75+
Raises:
76+
ValueError: Missed specifying a necessary value
77+
Exception: Unspecified error by executing the API call
78+
79+
Returns:
80+
api_call (dict): Returns the corresponding provider SSO settings
81+
"""
82+
83+
if len(provider) != 0:
84+
api_call: dict = Api(self.grafana_api_model).call_the_api(
85+
f"{APIEndpoints.SSO_SETTINGS.value}/{provider}",
86+
response_status_code=True
87+
)
88+
89+
status_code: int = api_call.get("status")
90+
91+
sso_settings_status_dict: dict = dict(
92+
{
93+
400: "Bad request.",
94+
401: "Unauthorized.",
95+
403: "Access Denied.",
96+
404: "SSO Settings not found."
97+
}
98+
)
99+
100+
if status_code == 200:
101+
return api_call
102+
elif 400 <= status_code <= 404:
103+
logging.error(sso_settings_status_dict.get(status_code))
104+
raise Exception
105+
else:
106+
logging.error(f"Check the error: {api_call}.")
107+
raise Exception
108+
else:
109+
logging.error("There is no provider defined.")
110+
raise ValueError
111+
112+
def update_sso_settings(self, provider: str, sso_setting: SSOSetting):
113+
"""The method includes a functionality to update the SSO settings specified by the provider
114+
115+
Args:
116+
provider (str): Specify the provider
117+
sso_setting (SSOSetting): Specify the SSO setting
118+
119+
Required Permissions:
120+
Action: settings:write
121+
Scope: settings:auth.{provider}:*
122+
123+
Raises:
124+
ValueError: Missed specifying a necessary value
125+
Exception: Unspecified error by executing the API call
126+
127+
Returns:
128+
None
129+
"""
130+
131+
if len(provider) != 0 and sso_setting is not None and len(sso_setting.api_url) != 0 and len(sso_setting.client_id) != 0 and len(sso_setting.client_secret) != 0 and sso_setting.enabled is not None and len(sso_setting.scopes) != 0:
132+
api_call: dict = Api(self.grafana_api_model).call_the_api(
133+
f"{APIEndpoints.SSO_SETTINGS.value}/{provider}",
134+
RequestsMethods.PUT,
135+
json.dumps(
136+
dict(
137+
{
138+
"settings": {
139+
"apiUrl": sso_setting.api_url,
140+
"clientId": sso_setting.client_id,
141+
"clientSecret": sso_setting.client_secret,
142+
"enabled": sso_setting.enabled,
143+
"scopes": sso_setting.scopes,
144+
}
145+
}
146+
)
147+
),
148+
response_status_code=True,
149+
)
150+
151+
status_code: int = api_call.get("status")
152+
153+
sso_settings_status_dict: dict = dict(
154+
{
155+
400: "Bad request.",
156+
401: "Unauthorized.",
157+
403: "Access Denied.",
158+
}
159+
)
160+
161+
if status_code == 204:
162+
logging.info("You successfully updated the corresponding provider SSO setting.")
163+
elif 400 <= status_code <= 403:
164+
logging.error(sso_settings_status_dict.get(status_code))
165+
raise Exception
166+
else:
167+
logging.error(f"Check the error: {api_call}.")
168+
raise Exception
169+
else:
170+
logging.error("There is no provider or sso_settings object defined.")
171+
raise ValueError
172+
173+
def delete_sso_settings(self, provider: str):
174+
"""The method includes a functionality to delete the SSO settings specified by the provider
175+
176+
Args:
177+
provider (str): Specify the provider
178+
179+
Required Permissions:
180+
Action: settings:write
181+
Scope: settings:auth.{provider}:*
182+
183+
Raises:
184+
ValueError: Missed specifying a necessary value
185+
Exception: Unspecified error by executing the API call
186+
187+
Returns:
188+
None
189+
"""
190+
191+
if len(provider) != 0:
192+
api_call: dict = Api(self.grafana_api_model).call_the_api(
193+
f"{APIEndpoints.SSO_SETTINGS.value}/{provider}",
194+
RequestsMethods.DELETE,
195+
response_status_code=True,
196+
)
197+
198+
status_code: int = api_call.get("status")
199+
200+
sso_settings_status_dict: dict = dict(
201+
{
202+
400: "Bad request.",
203+
401: "Unauthorized.",
204+
403: "Access Denied.",
205+
404: "SSO Settings not found.",
206+
}
207+
)
208+
209+
if status_code == 204:
210+
logging.info("You successfully deleted the corresponding provider SSO settings.")
211+
elif 400 <= status_code <= 404:
212+
logging.error(sso_settings_status_dict.get(status_code))
213+
raise Exception
214+
else:
215+
logging.error(f"Check the error: {api_call}.")
216+
raise Exception
217+
else:
218+
logging.error("There is no provider defined.")
219+
raise ValueError

0 commit comments

Comments
 (0)