Skip to content

Commit 8b78771

Browse files
authored
Get my tenants (#409)
+ test related to descope/etc#7070
1 parent 5ab1199 commit 8b78771

File tree

4 files changed

+106
-0
lines changed

4 files changed

+106
-0
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ These sections show how to use the SDK to perform various authentication/authori
5252
10. [Tenant selection](#tenant-selection)
5353
11. [Logging Out](#logging-out)
5454
12. [History](#history)
55+
13. [My Tenants](#my-tenants)
5556

5657
## API Managment Function
5758

@@ -482,6 +483,19 @@ for user_history in users_history_resp:
482483
# Do something
483484
```
484485

486+
### My Tenants
487+
488+
You can get the current session user tenants.
489+
The request requires a valid refresh token.
490+
And either a boolean to receive the current selected tenant
491+
Or a list of tenant IDs that this user is part of
492+
493+
```python
494+
tenants_resp = descope_client.my_tenants(refresh_token, False, ["tenant_id"])
495+
for tenant in tenants_resp.tenants:
496+
# Do something
497+
```
498+
485499
## Management API
486500

487501
It is very common for some form of management or automation to be required. These can be performed

descope/common.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class EndpointsV1:
2626
logout_path = "/v1/auth/logout"
2727
logout_all_path = "/v1/auth/logoutall"
2828
me_path = "/v1/auth/me"
29+
my_tenants_path = "/v1/auth/me/tenants"
2930
history_path = "/v1/auth/me/history"
3031

3132
# accesskey

descope/descope_client.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,53 @@ def me(self, refresh_token: str) -> dict:
435435
)
436436
return response.json()
437437

438+
def my_tenants(
439+
self,
440+
refresh_token: str,
441+
dct: bool = False,
442+
ids: list[str] | None = None,
443+
) -> dict:
444+
"""
445+
Retrieve tenant attributes that user belongs to, one of dct/ids must be populated .
446+
447+
Args:
448+
dct (bool): Get only the selected tenant from jwt
449+
ids (List[str]): Get the list of tenants
450+
refresh_token (str): The refresh token
451+
452+
Return value (dict): returns the tenant requested from the server
453+
(id:str, name:str, customAttributes:dict)
454+
455+
Raise:
456+
AuthException: Exception is raised if session is not authorized or another error occurs
457+
"""
458+
if refresh_token is None:
459+
raise AuthException(
460+
400,
461+
ERROR_TYPE_INVALID_ARGUMENT,
462+
f"signed refresh token {refresh_token} is empty",
463+
)
464+
if dct is True and ids is not None and len(ids) > 0:
465+
raise AuthException(
466+
400,
467+
ERROR_TYPE_INVALID_ARGUMENT,
468+
"Only one of 'dct' or 'ids' should be supplied",
469+
)
470+
if dct is False and (ids is None or len(ids) == 0):
471+
raise AuthException(
472+
400,
473+
ERROR_TYPE_INVALID_ARGUMENT,
474+
"Only one of 'dct' or 'ids' should be supplied",
475+
)
476+
477+
body: dict[str, bool | list[str]] = {"dct": dct}
478+
if ids is not None:
479+
body["ids"] = ids
480+
481+
uri = EndpointsV1.my_tenants_path
482+
response = self._auth.do_post(uri, body, None, refresh_token)
483+
return response.json()
484+
438485
def history(self, refresh_token: str) -> list[dict]:
439486
"""
440487
Retrieve user authentication history for the refresh token

tests/test_descope_client.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,50 @@ def test_me(self):
135135
timeout=DEFAULT_TIMEOUT_SECONDS,
136136
)
137137

138+
def test_my_tenants(self):
139+
dummy_refresh_token = ""
140+
client = DescopeClient(self.dummy_project_id, self.public_key_dict)
141+
142+
self.assertRaises(AuthException, client.my_tenants, None)
143+
self.assertRaises(AuthException, client.my_tenants, dummy_refresh_token)
144+
self.assertRaises(
145+
AuthException, client.my_tenants, dummy_refresh_token, True, ["a"]
146+
)
147+
148+
# Test failed flow
149+
with patch("requests.post") as mock_get:
150+
mock_get.return_value.ok = False
151+
self.assertRaises(
152+
AuthException, client.my_tenants, dummy_refresh_token, True
153+
)
154+
155+
# Test success flow
156+
with patch("requests.post") as mock_post:
157+
my_mock_response = mock.Mock()
158+
my_mock_response.ok = True
159+
data = json.loads(
160+
"""{"tenants": [{"id": "tenant_id", "name": "tenant_name"}]}"""
161+
)
162+
my_mock_response.json.return_value = data
163+
mock_post.return_value = my_mock_response
164+
tenant_response = client.my_tenants(dummy_refresh_token, False, ["a"])
165+
self.assertIsNotNone(tenant_response)
166+
self.assertEqual(
167+
data["tenants"][0]["name"], tenant_response["tenants"][0]["name"]
168+
)
169+
mock_post.assert_called_with(
170+
f"{common.DEFAULT_BASE_URL}{EndpointsV1.my_tenants_path}",
171+
headers={
172+
**common.default_headers,
173+
"Authorization": f"Bearer {self.dummy_project_id}",
174+
},
175+
json={"dct": False, "ids": ["a"]},
176+
allow_redirects=False,
177+
verify=True,
178+
params=None,
179+
timeout=DEFAULT_TIMEOUT_SECONDS,
180+
)
181+
138182
def test_history(self):
139183
dummy_refresh_token = ""
140184
client = DescopeClient(self.dummy_project_id, self.public_key_dict)

0 commit comments

Comments
 (0)