Skip to content

Commit

Permalink
Management SDK additions (#324)
Browse files Browse the repository at this point in the history
  • Loading branch information
shilgapira authored Dec 31, 2023
1 parent 5654eb2 commit 2dda1d2
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 8 deletions.
1 change: 1 addition & 0 deletions descope/management/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class MgmtV1:
user_remove_role_path = "/v1/mgmt/user/update/role/remove"
user_set_password_path = "/v1/mgmt/user/password/set"
user_expire_password_path = "/v1/mgmt/user/password/expire"
user_remove_all_passkeys_path = "/v1/mgmt/user/passkeys/delete"
user_add_tenant_path = "/v1/mgmt/user/update/tenant/add"
user_remove_tenant_path = "/v1/mgmt/user/update/tenant/remove"
user_generate_otp_for_test_path = "/v1/mgmt/tests/generate/otp"
Expand Down
57 changes: 49 additions & 8 deletions descope/management/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def create(
Containing the created user information.
Raise:
AuthException: raised if update operation fails
AuthException: raised if create operation fails
"""
role_names = [] if role_names is None else role_names
user_tenants = [] if user_tenants is None else user_tenants
Expand Down Expand Up @@ -158,7 +158,7 @@ def create_test_user(
Containing the created test user information.
Raise:
AuthException: raised if update operation fails
AuthException: raised if create operation fails
"""
role_names = [] if role_names is None else role_names
user_tenants = [] if user_tenants is None else user_tenants
Expand Down Expand Up @@ -324,7 +324,7 @@ def update(
custom_attributes (dict): Optional, set the different custom attributes values of the keys that were previously configured in Descope console app
Raise:
AuthException: raised if creation operation fails
AuthException: raised if update operation fails
"""
role_names = [] if role_names is None else role_names
user_tenants = [] if user_tenants is None else user_tenants
Expand Down Expand Up @@ -362,22 +362,41 @@ def delete(
login_id (str): The login ID of the user to be deleted.
Raise:
AuthException: raised if creation operation fails
AuthException: raised if delete operation fails
"""
self._auth.do_post(
MgmtV1.user_delete_path,
{"loginId": login_id},
pswd=self._auth.management_key,
)

def delete_by_user_id(
self,
user_id: str,
):
"""
Delete an existing user by user ID. IMPORTANT: This action is irreversible. Use carefully.
Args:
user_id (str): The user ID from the user's JWT.
Raise:
AuthException: raised if delete operation fails
"""
self._auth.do_post(
MgmtV1.user_delete_path,
{"userId": user_id},
pswd=self._auth.management_key,
)

def delete_all_test_users(
self,
):
"""
Delete all test users in the project. IMPORTANT: This action is irreversible. Use carefully.
Raise:
AuthException: raised if creation operation fails
AuthException: raised if delete operation fails
"""
self._auth.do_delete(
MgmtV1.user_delete_all_test_users_path,
Expand Down Expand Up @@ -446,7 +465,7 @@ def logout_user(
login_id (str): The login ID of the user to be logged out.
Raise:
AuthException: raised if creation operation fails
AuthException: raised if logout operation fails
"""
self._auth.do_post(
MgmtV1.user_logout_path,
Expand All @@ -465,7 +484,7 @@ def logout_user_by_user_id(
user_id (str): The login ID of the user to be logged out.
Raise:
AuthException: raised if creation operation fails
AuthException: raised if logout operation fails
"""
self._auth.do_post(
MgmtV1.user_logout_path,
Expand Down Expand Up @@ -1069,7 +1088,7 @@ def expire_password(
Use the `password.send_reset` or `password.replace` methods to reset/replace the password.
Args:
login_id (str): The login ID of the user expire the password to.
login_id (str): The login ID of the user to expire the password to.
Raise:
AuthException: raised if the operation fails
Expand All @@ -1081,6 +1100,28 @@ def expire_password(
)
return

def remove_all_passkeys(
self,
login_id: str,
) -> None:
"""
Removes all registered passkeys (WebAuthn devices) for the user with the given login ID.
Note: The user might not be able to login anymore if they have no other authentication
methods or a verified email/phone.
Args:
login_id (str): The login ID of the user to remove passkeys for.
Raise:
AuthException: raised if the operation fails
"""
self._auth.do_post(
MgmtV1.user_remove_all_passkeys_path,
{"loginId": login_id},
pswd=self._auth.management_key,
)
return

def generate_otp_for_test_user(
self,
method: DeliveryMethod,
Expand Down
62 changes: 62 additions & 0 deletions tests/management/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,35 @@ def test_delete(self):
timeout=DEFAULT_TIMEOUT_SECONDS,
)

def test_delete_by_user_id(self):
# Test failed flows
with patch("requests.post") as mock_post:
mock_post.return_value.ok = False
self.assertRaises(
AuthException,
self.client.mgmt.user.delete_by_user_id,
"valid-id",
)

# Test success flow
with patch("requests.post") as mock_post:
mock_post.return_value.ok = True
self.assertIsNone(self.client.mgmt.user.delete_by_user_id("u1"))
mock_post.assert_called_with(
f"{common.DEFAULT_BASE_URL}{MgmtV1.user_delete_path}",
headers={
**common.default_headers,
"Authorization": f"Bearer {self.dummy_project_id}:{self.dummy_management_key}",
},
params=None,
json={
"userId": "u1",
},
allow_redirects=False,
verify=True,
timeout=DEFAULT_TIMEOUT_SECONDS,
)

def test_logout(self):
# Test failed flows
with patch("requests.post") as mock_post:
Expand Down Expand Up @@ -1426,6 +1455,39 @@ def test_user_expire_password(self):
timeout=DEFAULT_TIMEOUT_SECONDS,
)

def test_user_remove_all_passkeys(self):
# Test failed flows
with patch("requests.post") as mock_post:
mock_post.return_value.ok = False
self.assertRaises(
AuthException,
self.client.mgmt.user.remove_all_passkeys,
"login-id",
)

# Test success flow
with patch("requests.post") as mock_post:
network_resp = mock.Mock()
network_resp.ok = True
mock_post.return_value = network_resp
self.client.mgmt.user.remove_all_passkeys(
"login-id",
)
mock_post.assert_called_with(
f"{common.DEFAULT_BASE_URL}{MgmtV1.user_remove_all_passkeys_path}",
headers={
**common.default_headers,
"Authorization": f"Bearer {self.dummy_project_id}:{self.dummy_management_key}",
},
params=None,
json={
"loginId": "login-id",
},
allow_redirects=False,
verify=True,
timeout=DEFAULT_TIMEOUT_SECONDS,
)

def test_generate_magic_link_for_test_user(self):
# Test failed flows
with patch("requests.post") as mock_post:
Expand Down

0 comments on commit 2dda1d2

Please sign in to comment.