Skip to content

Commit 9c145b6

Browse files
ruvenzxCopilot
andauthored
tenant session settings functions (#684)
* tenant session settings functions * tests fix and readme * fix readme * coverage fix * pr fix * readme fix * readme fix * co-fix for co-pilot * Update descope/management/tenant.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 52b2a35 commit 9c145b6

File tree

4 files changed

+204
-2
lines changed

4 files changed

+204
-2
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,14 @@ descope_client.mgmt.tenant.update(
556556
custom_attributes={"attribute-name": "value"},
557557
)
558558

559+
# Managing the tenant's settings
560+
# Getting the settings
561+
descope_client.mgmt.tenant.load_settings(id="my-custom-id")
562+
563+
# updating the settings
564+
descope_client.mgmt.tenant.update_settings(id="my-custom-id", self_provisioning_domains=["domain.com"], session_settings_enabled=True, refresh_token_expiration=1, refresh_token_expiration_unit="hours")
565+
566+
559567
# Tenant deletion cannot be undone. Use carefully.
560568
# Pass true to cascade value, in case you want to delete all users/keys associated only with this tenant
561569
descope_client.mgmt.tenant.delete(id="my-custom-id", cascade=False)

descope/management/common.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
from enum import Enum
22
from typing import List, Optional
33

4+
class SessionExpirationUnit(Enum):
5+
MINUTES = "minutes"
6+
HOURS = "hours"
7+
DAYS = "days"
8+
WEEKS = "weeks"
9+
10+
class TenantAuthType(Enum):
11+
NONE = "none"
12+
SAML = "saml"
13+
OIDC = "oidc"
414

515
class AccessType(Enum):
616
OFFLINE = "offline"
@@ -35,6 +45,7 @@ class MgmtV1:
3545
tenant_update_path = "/v1/mgmt/tenant/update"
3646
tenant_delete_path = "/v1/mgmt/tenant/delete"
3747
tenant_load_path = "/v1/mgmt/tenant"
48+
tenant_settings_path = "/v1/mgmt/tenant/settings"
3849
tenant_load_all_path = "/v1/mgmt/tenant/all"
3950
tenant_search_all_path = "/v1/mgmt/tenant/search"
4051

@@ -292,7 +303,6 @@ def associated_tenants_to_dict(associated_tenants: List[AssociatedTenant]) -> li
292303
)
293304
return associated_tenant_list
294305

295-
296306
class SAMLIDPAttributeMappingInfo:
297307
"""
298308
Represents a SAML IDP attribute mapping object. use this class for mapping Descope attribute

descope/management/tenant.py

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from typing import Any, List, Optional
22

33
from descope._http_base import HTTPBase
4-
from descope.management.common import MgmtV1
4+
from descope.management.common import MgmtV1, TenantAuthType, SessionExpirationUnit
55

66

77
class Tenant(HTTPBase):
@@ -92,6 +92,73 @@ def update(
9292
),
9393
)
9494

95+
def update_settings(
96+
self,
97+
id: str,
98+
self_provisioning_domains: List[str],
99+
domains: Optional[List[str]] = None,
100+
auth_type: Optional[TenantAuthType] = None,
101+
session_settings_enabled: Optional[bool] = None,
102+
refresh_token_expiration: Optional[int] = None,
103+
refresh_token_expiration_unit: Optional[SessionExpirationUnit] = None,
104+
session_token_expiration: Optional[int] = None,
105+
session_token_expiration_unit: Optional[SessionExpirationUnit] = None,
106+
stepup_token_expiration: Optional[int] = None,
107+
stepup_token_expiration_unit: Optional[SessionExpirationUnit] = None,
108+
enable_inactivity: Optional[bool] = None,
109+
inactivity_time: Optional[int] = None,
110+
inactivity_time_unit: Optional[SessionExpirationUnit] = None,
111+
JITDisabled: Optional[bool] = None
112+
):
113+
"""
114+
Update an existing tenant's session settings.
115+
116+
Args:
117+
id (str): The ID of the tenant to update.
118+
self_provisioning_domains (List[str]): Domains for self-provisioning.
119+
domains (Optional[List[str]]): List of domains associated with the tenant.
120+
auth_type (Optional[TenantAuthType]): Authentication type for the tenant.
121+
session_settings_enabled (Optional[bool]): Whether session settings are enabled.
122+
refresh_token_expiration (Optional[int]): Expiration time for refresh tokens.
123+
refresh_token_expiration_unit (Optional[SessionExiprationUnit]): Unit for refresh token expiration.
124+
session_token_expiration (Optional[int]): Expiration time for session tokens.
125+
session_token_expiration_unit (Optional[SessionExiprationUnit]): Unit for session token expiration.
126+
stepup_token_expiration (Optional[int]): Expiration time for step-up tokens.
127+
stepup_token_expiration_unit (Optional[SessionExiprationUnit]): Unit for step-up token expiration.
128+
enable_inactivity (Optional[bool]): Whether inactivity timeout is enabled.
129+
inactivity_time (Optional[int]): Inactivity timeout duration.
130+
inactivity_time_unit (Optional[SessionExiprationUnit]): Unit for inactivity timeout.
131+
JITDisabled (Optional[bool]): Whether JIT is disabled.
132+
133+
Raise:
134+
AuthException: raised if update operation fails
135+
"""
136+
body: dict[str, Any] = {
137+
"tenantId": id,
138+
"selfProvisioningDomains": self_provisioning_domains,
139+
"domains": domains,
140+
"authType": auth_type,
141+
"enabled": session_settings_enabled,
142+
"refreshTokenExpiration": refresh_token_expiration,
143+
"refreshTokenExpirationUnit": refresh_token_expiration_unit,
144+
"sessionTokenExpiration": session_token_expiration,
145+
"sessionTokenExpirationUnit": session_token_expiration_unit,
146+
"stepupTokenExpiration": stepup_token_expiration,
147+
"stepupTokenExpirationUnit": stepup_token_expiration_unit,
148+
"enableInactivity": enable_inactivity,
149+
"inactivityTime": inactivity_time,
150+
"inactivityTimeUnit": inactivity_time_unit,
151+
"JITDisabled": JITDisabled,
152+
}
153+
154+
body = {k: v for k, v in body.items() if v is not None}
155+
156+
self._http.post(
157+
MgmtV1.tenant_settings_path,
158+
body=body,
159+
params=None
160+
)
161+
95162
def delete(
96163
self,
97164
id: str,
@@ -134,6 +201,35 @@ def load(
134201
params={"id": id},
135202
)
136203
return response.json()
204+
205+
def load_settings(
206+
self,
207+
id: str,
208+
) -> dict:
209+
"""
210+
Load tenant session settings by id.
211+
212+
Args:
213+
id (str): The ID of the tenant to load session settings for.
214+
215+
Return value (dict):
216+
Return dict in the format
217+
{ "domains":<list[str]>, "selfProvisioningDomains":<list[str]>, "authType":<str>,
218+
"enabled":<bool>, "refreshTokenExpiration":<int>, "refreshTokenExpirationUnit":<str>,
219+
"sessionTokenExpiration":<int>, "sessionTokenExpirationUnit":<str>,
220+
"stepupTokenExpiration":<int>, "stepupTokenExpirationUnit":<str>,
221+
"enableInactivity":<bool>, "inactivityTime":<int>, "inactivityTimeUnit":<str>,
222+
"JITDisabled":<bool> }
223+
Containing the loaded tenant session settings.
224+
225+
Raise:
226+
AuthException: raised if load operation fails
227+
"""
228+
response = self._http.get(
229+
MgmtV1.tenant_settings_path,
230+
params={"id": id},
231+
)
232+
return response.json()
137233

138234
def load_all(
139235
self,

tests/management/test_tenant.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,3 +366,91 @@ def test_search_all(self):
366366
params=None,
367367
timeout=DEFAULT_TIMEOUT_SECONDS,
368368
)
369+
370+
def test_update_settings(self):
371+
client = DescopeClient(
372+
self.dummy_project_id,
373+
self.public_key_dict,
374+
False,
375+
self.dummy_management_key,
376+
)
377+
378+
# Test failed flows
379+
with patch("requests.post") as mock_post:
380+
mock_post.return_value.ok = False
381+
self.assertRaises(
382+
AuthException,
383+
client.mgmt.tenant.update_settings,
384+
"valid-id",
385+
{},
386+
)
387+
388+
# Test success flow
389+
with patch("requests.post") as mock_post:
390+
mock_post.return_value.ok = True
391+
self.assertIsNone(
392+
client.mgmt.tenant.update_settings("t1", self_provisioning_domains=["domain1.com"], domains=["domain1.com", "domain2.com"], auth_type="oidc", session_settings_enabled=True)
393+
)
394+
mock_post.assert_called_with(
395+
f"{common.DEFAULT_BASE_URL}{MgmtV1.tenant_settings_path}",
396+
headers={
397+
**common.default_headers,
398+
"Authorization": f"Bearer {self.dummy_project_id}:{self.dummy_management_key}",
399+
"x-descope-project-id": self.dummy_project_id,
400+
},
401+
json={
402+
"tenantId": "t1",
403+
"selfProvisioningDomains": ["domain1.com"],
404+
"domains": ["domain1.com", "domain2.com"],
405+
"authType": "oidc",
406+
"enabled": True
407+
},
408+
allow_redirects=False,
409+
params=None,
410+
verify=True,
411+
timeout=DEFAULT_TIMEOUT_SECONDS,
412+
)
413+
414+
def test_load_settings(self):
415+
client = DescopeClient(
416+
self.dummy_project_id,
417+
self.public_key_dict,
418+
False,
419+
self.dummy_management_key,
420+
)
421+
422+
# Test failed flows
423+
with patch("requests.get") as mock_get:
424+
mock_get.return_value.ok = False
425+
self.assertRaises(
426+
AuthException,
427+
client.mgmt.tenant.load_settings,
428+
"valid-id",
429+
)
430+
431+
# Test success flow
432+
with patch("requests.get") as mock_get:
433+
network_resp = mock.Mock()
434+
network_resp.ok = True
435+
network_resp.json.return_value = json.loads(
436+
"""
437+
{"domains": ["domain1.com", "domain2.com"], "authType": "oidc", "sessionSettingsEnabled": true}
438+
"""
439+
)
440+
mock_get.return_value = network_resp
441+
resp = client.mgmt.tenant.load_settings("t1")
442+
self.assertEqual(resp["domains"], ["domain1.com", "domain2.com"])
443+
self.assertEqual(resp["authType"], "oidc")
444+
self.assertEqual(resp["sessionSettingsEnabled"], True)
445+
mock_get.assert_called_with(
446+
f"{common.DEFAULT_BASE_URL}{MgmtV1.tenant_settings_path}",
447+
headers={
448+
**common.default_headers,
449+
"Authorization": f"Bearer {self.dummy_project_id}:{self.dummy_management_key}",
450+
"x-descope-project-id": self.dummy_project_id,
451+
},
452+
params={"id": "t1"},
453+
allow_redirects=True,
454+
verify=True,
455+
timeout=DEFAULT_TIMEOUT_SECONDS,
456+
)

0 commit comments

Comments
 (0)