diff --git a/README.md b/README.md index 13200c29..1efbcfa8 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,7 @@ For more code samples on how to integrate the auth0-python SDK in your Python ap - Roles() ( `Auth0().roles` ) - RulesConfigs() ( `Auth0().rules_configs` ) - Rules() ( `Auth0().rules` ) +- SelfServiceProfiles() ( `Auth0().self_service_profiles` ) - Stats() ( `Auth0().stats` ) - Tenants() ( `Auth0().tenants` ) - Tickets() ( `Auth0().tickets` ) diff --git a/auth0/management/__init__.py b/auth0/management/__init__.py index d6fee4bc..62b1e8a9 100644 --- a/auth0/management/__init__.py +++ b/auth0/management/__init__.py @@ -22,6 +22,7 @@ from .roles import Roles from .rules import Rules from .rules_configs import RulesConfigs +from .self_service_profiles import SelfServiceProfiles from .stats import Stats from .tenants import Tenants from .tickets import Tickets @@ -59,6 +60,7 @@ "Roles", "RulesConfigs", "Rules", + "SelfServiceProfiles", "Stats", "Tenants", "Tickets", diff --git a/auth0/management/auth0.py b/auth0/management/auth0.py index 2879a9e7..5615f86c 100644 --- a/auth0/management/auth0.py +++ b/auth0/management/auth0.py @@ -26,6 +26,7 @@ from .roles import Roles from .rules import Rules from .rules_configs import RulesConfigs +from .self_service_profiles import SelfServiceProfiles from .stats import Stats from .tenants import Tenants from .tickets import Tickets @@ -86,6 +87,9 @@ def __init__( self.roles = Roles(domain, token, rest_options=rest_options) self.rules_configs = RulesConfigs(domain, token, rest_options=rest_options) self.rules = Rules(domain, token, rest_options=rest_options) + self.self_service_profiles = SelfServiceProfiles( + domain, token, rest_options=rest_options + ) self.stats = Stats(domain, token, rest_options=rest_options) self.tenants = Tenants(domain, token, rest_options=rest_options) self.tickets = Tickets(domain, token, rest_options=rest_options) diff --git a/auth0/management/self_service_profiles.py b/auth0/management/self_service_profiles.py new file mode 100644 index 00000000..a748de90 --- /dev/null +++ b/auth0/management/self_service_profiles.py @@ -0,0 +1,180 @@ +from __future__ import annotations + +from typing import Any, List # List is being used as list is already a method. + +from ..rest import RestClient, RestClientOptions +from ..types import TimeoutType + + +class SelfServiceProfiles: + """Auth0 Self Service Profiles endpoints + + Args: + domain (str): Your Auth0 domain, e.g: 'username.auth0.com' + + token (str): Management API v2 Token + + telemetry (bool, optional): Enable or disable Telemetry + (defaults to True) + + timeout (float or tuple, optional): Change the requests + connect and read timeout. Pass a tuple to specify + both values separately or a float to set both to it. + (defaults to 5.0 for both) + + protocol (str, optional): Protocol to use when making requests. + (defaults to "https") + + rest_options (RestClientOptions): Pass an instance of + RestClientOptions to configure additional RestClient + options, such as rate-limit retries. + (defaults to None) + """ + + def __init__( + self, + domain: str, + token: str, + telemetry: bool = True, + timeout: TimeoutType = 5.0, + protocol: str = "https", + rest_options: RestClientOptions | None = None, + ) -> None: + self.domain = domain + self.protocol = protocol + self.client = RestClient( + jwt=token, telemetry=telemetry, timeout=timeout, options=rest_options + ) + + def _url(self, profile_id: str | None = None) -> str: + url = f"{self.protocol}://{self.domain}/api/v2/self-service-profiles" + if profile_id is not None: + return f"{url}/{profile_id}" + return url + + def all( + self, + page: int = 0, + per_page: int = 25, + include_totals: bool = True, + ) -> List[dict[str, Any]]: + """List self-service profiles. + + Args: + page (int, optional): The result's page number (zero based). By default, + retrieves the first page of results. + + per_page (int, optional): The amount of entries per page. By default, + retrieves 25 results per page. + + include_totals (bool, optional): True if the query summary is + to be included in the result, False otherwise. Defaults to True. + + See: https://auth0.com/docs/api/management/v2/self-service-profiles/get-self-service-profiles + """ + + params = { + "page": page, + "per_page": per_page, + "include_totals": str(include_totals).lower(), + } + + return self.client.get(self._url(), params=params) + + def create(self, body: dict[str, Any]) -> dict[str, Any]: + """Create a new self-service profile. + + Args: + body (dict): Attributes for the new self-service profile. + + See: https://auth0.com/docs/api/management/v2/self-service-profiles/post-self-service-profiles + """ + + return self.client.post(self._url(), data=body) + + def get(self, profile_id: str) -> dict[str, Any]: + """Get a self-service profile. + + Args: + id (str): The id of the self-service profile to retrieve. + + See: https://auth0.com/docs/api/management/v2/self-service-profiles/get-self-service-profiles-by-id + """ + + return self.client.get(self._url(profile_id)) + + def delete(self, profile_id: str) -> None: + """Delete a self-service profile. + + Args: + id (str): The id of the self-service profile to delete. + + See: https://auth0.com/docs/api/management/v2/self-service-profiles/delete-self-service-profiles-by-id + """ + + self.client.delete(self._url(profile_id)) + + def update(self, profile_id: str, body: dict[str, Any]) -> dict[str, Any]: + """Update a self-service profile. + + Args: + id (str): The id of the self-service profile to update. + + body (dict): Attributes of the self-service profile to modify. + + See: https://auth0.com/docs/api/management/v2/self-service-profiles/patch-self-service-profiles-by-id + """ + + return self.client.patch(self._url(profile_id), data=body) + + def get_custom_text( + self, profile_id: str, language: str, page: str + ) -> dict[str, Any]: + """Get the custom text for a self-service profile. + + See: https://auth0.com/docs/api/management/v2/self-service-profiles/get-self-service-profile-custom-text + """ + + url = self._url(f"{profile_id}/custom-text/{language}/{page}") + return self.client.get(url) + + def update_custom_text( + self, profile_id: str, language: str, page: str, body: dict[str, Any] + ) -> dict[str, Any]: + """Update the custom text for a self-service profile. + + See: https://auth0.com/docs/api/management/v2/self-service-profiles/put-self-service-profile-custom-text + """ + + url = self._url(f"{profile_id}/custom-text/{language}/{page}") + return self.client.put(url, data=body) + + def create_sso_ticket( + self, profile_id: str, body: dict[str, Any] + ) -> dict[str, Any]: + """Create a single sign-on ticket for a self-service profile. + + Args: + id (str): The id of the self-service profile to create the ticket for. + + body (dict): Attributes for the single sign-on ticket. + + See: https://auth0.com/docs/api/management/v2/self-service-profiles/post-sso-ticket + """ + + url = self._url(f"{profile_id}/sso-ticket") + return self.client.post(url, data=body) + + def revoke_sso_ticket(self, profile_id: str, ticket_id: str) -> None: + """Revoke a single sign-on ticket for a self-service profile. + + Args: + id (str): The id of the self-service profile to revoke the ticket from. + + ticket (str): The ticket to revoke. + + See: https://auth0.com/docs/api/management/v2/self-service-profiles/post-revoke + """ + + url = self._url(f"{profile_id}/sso-ticket/{ticket_id}/revoke") + self.client.post(url) diff --git a/auth0/test/management/test_self_service_profiles.py b/auth0/test/management/test_self_service_profiles.py new file mode 100644 index 00000000..25465047 --- /dev/null +++ b/auth0/test/management/test_self_service_profiles.py @@ -0,0 +1,124 @@ +import unittest +from unittest import mock + +from ...management.self_service_profiles import SelfServiceProfiles + + +class TestSelfServiceProfiles(unittest.TestCase): + def test_init_with_optionals(self): + t = SelfServiceProfiles( + domain="domain", token="jwttoken", telemetry=False, timeout=(10, 2) + ) + self.assertEqual(t.client.options.timeout, (10, 2)) + telemetry_header = t.client.base_headers.get("Auth0-Client", None) + self.assertEqual(telemetry_header, None) + + @mock.patch("auth0.management.self_service_profiles.RestClient") + def test_all(self, mock_rc): + mock_instance = mock_rc.return_value + + s = SelfServiceProfiles(domain="domain", token="jwttoken") + s.all() + + mock_instance.get.assert_called_with( + "https://domain/api/v2/self-service-profiles", + params={"page": 0, "per_page": 25, "include_totals": "true"}, + ) + + s.all(page=1, per_page=50, include_totals=False) + + mock_instance.get.assert_called_with( + "https://domain/api/v2/self-service-profiles", + params={"page": 1, "per_page": 50, "include_totals": "false"}, + ) + + @mock.patch("auth0.management.self_service_profiles.RestClient") + def test_create(self, mock_rc): + mock_instance = mock_rc.return_value + + s = SelfServiceProfiles(domain="domain", token="jwttoken") + s.create({"name": "test"}) + + mock_instance.post.assert_called_with( + "https://domain/api/v2/self-service-profiles", data={"name": "test"} + ) + + @mock.patch("auth0.management.self_service_profiles.RestClient") + def test_get(self, mock_rc): + mock_instance = mock_rc.return_value + + s = SelfServiceProfiles(domain="domain", token="jwttoken") + s.get("an-id") + + mock_instance.get.assert_called_with( + "https://domain/api/v2/self-service-profiles/an-id" + ) + + @mock.patch("auth0.management.self_service_profiles.RestClient") + def test_delete(self, mock_rc): + mock_instance = mock_rc.return_value + + s = SelfServiceProfiles(domain="domain", token="jwttoken") + s.delete("an-id") + + mock_instance.delete.assert_called_with( + "https://domain/api/v2/self-service-profiles/an-id" + ) + + @mock.patch("auth0.management.self_service_profiles.RestClient") + def test_update(self, mock_rc): + mock_instance = mock_rc.return_value + + s = SelfServiceProfiles(domain="domain", token="jwttoken") + s.update("an-id", {"a": "b", "c": "d"}) + + mock_instance.patch.assert_called_with( + "https://domain/api/v2/self-service-profiles/an-id", + data={"a": "b", "c": "d"}, + ) + + @mock.patch("auth0.management.self_service_profiles.RestClient") + def test_get_custom_text(self, mock_rc): + mock_instance = mock_rc.return_value + + s = SelfServiceProfiles(domain="domain", token="jwttoken") + s.get_custom_text("an-id", "en", "page") + + mock_instance.get.assert_called_with( + "https://domain/api/v2/self-service-profiles/an-id/custom-text/en/page" + ) + + @mock.patch("auth0.management.self_service_profiles.RestClient") + def test_update_custom_text(self, mock_rc): + mock_instance = mock_rc.return_value + + s = SelfServiceProfiles(domain="domain", token="jwttoken") + s.update_custom_text("an-id", "en", "page", {"a": "b", "c": "d"}) + + mock_instance.put.assert_called_with( + "https://domain/api/v2/self-service-profiles/an-id/custom-text/en/page", + data={"a": "b", "c": "d"}, + ) + + @mock.patch("auth0.management.self_service_profiles.RestClient") + def test_create_sso_ticket(self, mock_rc): + mock_instance = mock_rc.return_value + + s = SelfServiceProfiles(domain="domain", token="jwttoken") + s.create_sso_ticket("an-id", {"a": "b", "c": "d"}) + + mock_instance.post.assert_called_with( + "https://domain/api/v2/self-service-profiles/an-id/sso-ticket", + data={"a": "b", "c": "d"}, + ) + + @mock.patch("auth0.management.self_service_profiles.RestClient") + def test_revoke_sso_ticket(self, mock_rc): + mock_instance = mock_rc.return_value + + s = SelfServiceProfiles(domain="domain", token="jwttoken") + s.revoke_sso_ticket("an-id", "ticket-id") + + mock_instance.post.assert_called_with( + "https://domain/api/v2/self-service-profiles/an-id/sso-ticket/ticket-id/revoke" + ) diff --git a/docs/source/management.rst b/docs/source/management.rst index e928f008..a8d72444 100644 --- a/docs/source/management.rst +++ b/docs/source/management.rst @@ -177,6 +177,14 @@ management.rules module :undoc-members: :show-inheritance: +management.self_service_profiles module +----------------------------------------- + +.. automodule:: auth0.management.self_service_profiles + :members: + :undoc-members: + :show-inheritance: + management.stats module --------------------------