Skip to content

Commit 366b4b4

Browse files
authored
Add SSO Setup Suite settings to tenant mgmt (#713)
1 parent 7647e5e commit 366b4b4

File tree

3 files changed

+170
-8
lines changed

3 files changed

+170
-8
lines changed

descope/management/common.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from enum import Enum
2-
from typing import List, Optional
2+
from typing import Dict, List, Optional, Any
33

44

55
class SessionExpirationUnit(Enum):
@@ -15,6 +15,51 @@ class TenantAuthType(Enum):
1515
OIDC = "oidc"
1616

1717

18+
class SSOSetupSuiteSettingsDisabledFeatures:
19+
def __init__(
20+
self,
21+
saml: bool = False,
22+
oidc: bool = False,
23+
scim: bool = False,
24+
sso_domains: bool = False,
25+
group_mapping: bool = False,
26+
):
27+
self.saml = saml
28+
self.oidc = oidc
29+
self.scim = scim
30+
self.sso_domains = sso_domains
31+
self.group_mapping = group_mapping
32+
33+
def to_dict(self) -> Dict[str, bool]:
34+
return {
35+
"saml": self.saml,
36+
"oidc": self.oidc,
37+
"scim": self.scim,
38+
"ssoDomains": self.sso_domains,
39+
"groupMapping": self.group_mapping,
40+
}
41+
42+
43+
class SSOSetupSuiteSettings:
44+
def __init__(
45+
self,
46+
enabled: bool,
47+
style_id: Optional[str] = None,
48+
disabled_features: Optional[SSOSetupSuiteSettingsDisabledFeatures] = None,
49+
):
50+
self.enabled = enabled
51+
self.style_id = style_id
52+
self.disabled_features = disabled_features
53+
54+
def to_dict(self) -> Dict[str, Any]:
55+
result: Dict[str, Any] = {"enabled": self.enabled}
56+
if self.style_id is not None:
57+
result["styleId"] = self.style_id
58+
if self.disabled_features is not None:
59+
result["disabledFeatures"] = self.disabled_features.to_dict()
60+
return result
61+
62+
1863
class AccessType(Enum):
1964
OFFLINE = "offline"
2065
ONLINE = "online"

descope/management/tenant.py

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

33
from descope._http_base import HTTPBase
4-
from descope.management.common import MgmtV1, SessionExpirationUnit, TenantAuthType
4+
from descope.management.common import (
5+
MgmtV1,
6+
SessionExpirationUnit,
7+
SSOSetupSuiteSettings,
8+
TenantAuthType,
9+
)
510

611

712
class Tenant(HTTPBase):
@@ -109,6 +114,7 @@ def update_settings(
109114
inactivity_time: Optional[int] = None,
110115
inactivity_time_unit: Optional[SessionExpirationUnit] = None,
111116
JITDisabled: Optional[bool] = None,
117+
sso_setup_suite_settings: Optional[SSOSetupSuiteSettings] = None,
112118
):
113119
"""
114120
Update an existing tenant's session settings.
@@ -120,15 +126,16 @@ def update_settings(
120126
auth_type (Optional[TenantAuthType]): Authentication type for the tenant.
121127
session_settings_enabled (Optional[bool]): Whether session settings are enabled.
122128
refresh_token_expiration (Optional[int]): Expiration time for refresh tokens.
123-
refresh_token_expiration_unit (Optional[SessionExiprationUnit]): Unit for refresh token expiration.
129+
refresh_token_expiration_unit (Optional[SessionExpirationUnit]): Unit for refresh token expiration.
124130
session_token_expiration (Optional[int]): Expiration time for session tokens.
125-
session_token_expiration_unit (Optional[SessionExiprationUnit]): Unit for session token expiration.
131+
session_token_expiration_unit (Optional[SessionExpirationUnit]): Unit for session token expiration.
126132
stepup_token_expiration (Optional[int]): Expiration time for step-up tokens.
127-
stepup_token_expiration_unit (Optional[SessionExiprationUnit]): Unit for step-up token expiration.
133+
stepup_token_expiration_unit (Optional[SessionExpirationUnit]): Unit for step-up token expiration.
128134
enable_inactivity (Optional[bool]): Whether inactivity timeout is enabled.
129135
inactivity_time (Optional[int]): Inactivity timeout duration.
130-
inactivity_time_unit (Optional[SessionExiprationUnit]): Unit for inactivity timeout.
136+
inactivity_time_unit (Optional[SessionExpirationUnit]): Unit for inactivity timeout.
131137
JITDisabled (Optional[bool]): Whether JIT is disabled.
138+
sso_setup_suite_settings (Optional[SSOSetupSuiteSettings]): SSO Setup Suite configuration.
132139
133140
Raise:
134141
AuthException: raised if update operation fails
@@ -149,6 +156,9 @@ def update_settings(
149156
"inactivityTime": inactivity_time,
150157
"inactivityTimeUnit": inactivity_time_unit,
151158
"JITDisabled": JITDisabled,
159+
"ssoSetupSuiteSettings": (
160+
sso_setup_suite_settings.to_dict() if sso_setup_suite_settings else None
161+
),
152162
}
153163

154164
body = {k: v for k, v in body.items() if v is not None}
@@ -215,7 +225,7 @@ def load_settings(
215225
"sessionTokenExpiration":<int>, "sessionTokenExpirationUnit":<str>,
216226
"stepupTokenExpiration":<int>, "stepupTokenExpirationUnit":<str>,
217227
"enableInactivity":<bool>, "inactivityTime":<int>, "inactivityTimeUnit":<str>,
218-
"JITDisabled":<bool> }
228+
"JITDisabled":<bool>, "ssoSetupSuiteSettings":<dict> }
219229
Containing the loaded tenant session settings.
220230
221231
Raise:

tests/management/test_tenant.py

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44

55
from descope import AuthException, DescopeClient
66
from descope.common import DEFAULT_TIMEOUT_SECONDS
7-
from descope.management.common import MgmtV1
7+
from descope.management.common import (
8+
MgmtV1,
9+
SSOSetupSuiteSettings,
10+
SSOSetupSuiteSettingsDisabledFeatures,
11+
)
812

913
from .. import common
1014

@@ -417,6 +421,58 @@ def test_update_settings(self):
417421
timeout=DEFAULT_TIMEOUT_SECONDS,
418422
)
419423

424+
# Test success flow with SSO Setup Suite settings
425+
with patch("requests.post") as mock_post:
426+
mock_post.return_value.ok = True
427+
sso_disabled_features = SSOSetupSuiteSettingsDisabledFeatures(
428+
saml=True, oidc=False, scim=True, sso_domains=False, group_mapping=True
429+
)
430+
sso_settings = SSOSetupSuiteSettings(
431+
enabled=True,
432+
style_id="style123",
433+
disabled_features=sso_disabled_features,
434+
)
435+
self.assertIsNone(
436+
client.mgmt.tenant.update_settings(
437+
"t1",
438+
self_provisioning_domains=["domain1.com"],
439+
domains=["domain1.com", "domain2.com"],
440+
auth_type="oidc",
441+
session_settings_enabled=True,
442+
sso_setup_suite_settings=sso_settings,
443+
)
444+
)
445+
mock_post.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+
json={
453+
"tenantId": "t1",
454+
"selfProvisioningDomains": ["domain1.com"],
455+
"domains": ["domain1.com", "domain2.com"],
456+
"authType": "oidc",
457+
"enabled": True,
458+
"ssoSetupSuiteSettings": {
459+
"enabled": True,
460+
"styleId": "style123",
461+
"disabledFeatures": {
462+
"saml": True,
463+
"oidc": False,
464+
"scim": True,
465+
"ssoDomains": False,
466+
"groupMapping": True,
467+
},
468+
},
469+
},
470+
allow_redirects=False,
471+
params=None,
472+
verify=True,
473+
timeout=DEFAULT_TIMEOUT_SECONDS,
474+
)
475+
420476
def test_load_settings(self):
421477
client = DescopeClient(
422478
self.dummy_project_id,
@@ -460,3 +516,54 @@ def test_load_settings(self):
460516
verify=True,
461517
timeout=DEFAULT_TIMEOUT_SECONDS,
462518
)
519+
520+
# Test success flow with SSO Setup Suite settings
521+
with patch("requests.get") as mock_get:
522+
network_resp = mock.Mock()
523+
network_resp.ok = True
524+
network_resp.json.return_value = json.loads(
525+
"""
526+
{
527+
"domains": ["domain1.com", "domain2.com"],
528+
"authType": "oidc",
529+
"sessionSettingsEnabled": true,
530+
"ssoSetupSuiteSettings": {
531+
"enabled": true,
532+
"styleId": "style123",
533+
"disabledFeatures": {
534+
"saml": true,
535+
"oidc": false,
536+
"scim": true,
537+
"ssoDomains": false,
538+
"groupMapping": true
539+
}
540+
}
541+
}
542+
"""
543+
)
544+
mock_get.return_value = network_resp
545+
resp = client.mgmt.tenant.load_settings("t1")
546+
self.assertEqual(resp["domains"], ["domain1.com", "domain2.com"])
547+
self.assertEqual(resp["authType"], "oidc")
548+
self.assertEqual(resp["sessionSettingsEnabled"], True)
549+
sso_settings = resp["ssoSetupSuiteSettings"]
550+
self.assertEqual(sso_settings["enabled"], True)
551+
self.assertEqual(sso_settings["styleId"], "style123")
552+
disabled_features = sso_settings["disabledFeatures"]
553+
self.assertEqual(disabled_features["saml"], True)
554+
self.assertEqual(disabled_features["oidc"], False)
555+
self.assertEqual(disabled_features["scim"], True)
556+
self.assertEqual(disabled_features["ssoDomains"], False)
557+
self.assertEqual(disabled_features["groupMapping"], True)
558+
mock_get.assert_called_with(
559+
f"{common.DEFAULT_BASE_URL}{MgmtV1.tenant_settings_path}",
560+
headers={
561+
**common.default_headers,
562+
"Authorization": f"Bearer {self.dummy_project_id}:{self.dummy_management_key}",
563+
"x-descope-project-id": self.dummy_project_id,
564+
},
565+
params={"id": "t1"},
566+
allow_redirects=True,
567+
verify=True,
568+
timeout=DEFAULT_TIMEOUT_SECONDS,
569+
)

0 commit comments

Comments
 (0)