diff --git a/.gitignore b/.gitignore index 4714cf42..40ae7098 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,7 @@ nosetests.xml coverage.xml *,cover .hypothesis/ +.pytest_cache # Translations *.mo diff --git a/auth0/v3/management/__init__.py b/auth0/v3/management/__init__.py index 750b728b..3c8c0325 100644 --- a/auth0/v3/management/__init__.py +++ b/auth0/v3/management/__init__.py @@ -3,14 +3,17 @@ from .clients import Clients from .client_grants import ClientGrants from .connections import Connections +from .custom_domains import CustomDomains from .device_credentials import DeviceCredentials from .emails import Emails from .email_templates import EmailTemplates +from .grants import Grants from .guardian import Guardian from .jobs import Jobs from .logs import Logs from .resource_servers import ResourceServers from .rules import Rules +from .rules_configs import RulesConfigs from .stats import Stats from .tenants import Tenants from .tickets import Tickets diff --git a/auth0/v3/management/auth0.py b/auth0/v3/management/auth0.py index 10d534d4..b014d398 100644 --- a/auth0/v3/management/auth0.py +++ b/auth0/v3/management/auth0.py @@ -1,3 +1,5 @@ +from .grants import Grants +from .custom_domains import CustomDomains from .blacklists import Blacklists from .clients import Clients from .client_grants import ClientGrants @@ -10,6 +12,7 @@ from .logs import Logs from .resource_servers import ResourceServers from .rules import Rules +from .rules_configs import RulesConfigs from .stats import Stats from .tenants import Tenants from .tickets import Tickets @@ -31,15 +34,18 @@ def __init__(self, domain, token): self.blacklists = Blacklists(domain, token) self.clients = Clients(domain, token) self.client_grants = ClientGrants(domain, token) + self.custom_domains = CustomDomains(domain, token) self.connections = Connections(domain, token) self.device_credentials = DeviceCredentials(domain, token) self.emails = Emails(domain, token) self.email_templates = EmailTemplates(domain, token) + self.grants = Grants(domain.token) self.guardian = Guardian(domain, token) self.jobs = Jobs(domain, token) self.logs = Logs(domain, token) self.resource_servers = ResourceServers(domain, token) self.rules = Rules(domain, token) + self.rules_configs = RulesConfigs(domain, token) self.stats = Stats(domain, token) self.tenants = Tenants(domain, token) self.tickets = Tickets(domain, token) diff --git a/auth0/v3/management/clients.py b/auth0/v3/management/clients.py index 60db7a9f..a69dfa68 100644 --- a/auth0/v3/management/clients.py +++ b/auth0/v3/management/clients.py @@ -111,7 +111,7 @@ def update(self, id, body): attributes can only be updated with the update:client_keys scope. Args: - id (str): Client id of the application. + id (str): Client ID of the application. body (dict): Attributes to modify. See: https://auth0.com/docs/api/management/v2#!/Clients/patch_clients_by_id @@ -119,3 +119,17 @@ def update(self, id, body): return self.client.patch(self._url(id), data=body) + def rotate_secret(self, id): + """Rotate a client secret. The generated secret is NOT base64 encoded. + + Args: + id (str): Client ID of the application. + + body (dict): Attributes to modify. + See: https://auth0.com/docs/api/management/v2#!/Clients/post_rotate_secret + """ + + params = {'id': id } + + url = self._url('%s/rotate-secret' % id) + return self.client.get(url, params=params) \ No newline at end of file diff --git a/auth0/v3/management/custom_domains.py b/auth0/v3/management/custom_domains.py new file mode 100644 index 00000000..98e3d45e --- /dev/null +++ b/auth0/v3/management/custom_domains.py @@ -0,0 +1,75 @@ +from .rest import RestClient + + +class CustomDomains(object): + + """Auth0 custom domains 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) + """ + + def __init__(self, domain, token, telemetry=True): + self.domain = domain + self.client = RestClient(jwt=token, telemetry=telemetry) + + def _url(self, id=None): + url = 'https://%s/api/v2/custom-domains' % self.domain + if id is not None: + return url + '/' + id + return url + + def all(self): + """Retrieves all custom domains. + + See: https://auth0.com/docs/api/management/v2#!/Custom_Domains/get_custom_domains + """ + return self.client.get(self._url()) + + def get(self, id): + """Retrieves custom domain. + + See: https://auth0.com/docs/api/management/v2#!/Custom_Domains/get_custom_domains_by_id + """ + url = self._url('%s' % (id)) + return self.client.get(url) + + def delete(self, id): + """Deletes a grant. + + Args: + id (str): The id of the custom domain to delete + + + See: https://auth0.com/docs/api/management/v2#!/Custom_Domains/delete_custom_domains_by_id + """ + url = self._url('%s' % (id)) + return self.client.delete(url) + + def create_new(self, body): + """Configure a new custom domain + + Args: + body (str): The domain, tye and verification method in json + + + See: https://auth0.com/docs/api/management/v2#!/Custom_Domains/post_custom_domains + """ + return self.client.post(self._url(), data=body) + + def verify(self, id): + """Verify a custom domain + + Args: + id (str): The id of the custom domain to delete + + + See: https://auth0.com/docs/api/management/v2#!/Custom_Domains/post_verify + """ + url = self._url('%s/verify' % (id)) + return self.client.post(url) \ No newline at end of file diff --git a/auth0/v3/management/grants.py b/auth0/v3/management/grants.py new file mode 100644 index 00000000..da3b332a --- /dev/null +++ b/auth0/v3/management/grants.py @@ -0,0 +1,63 @@ +from .rest import RestClient + + +class Grants(object): + + """Auth0 grants 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) + """ + + def __init__(self, domain, token, telemetry=True): + self.domain = domain + self.client = RestClient(jwt=token, telemetry=telemetry) + + def _url(self, id=None): + url = 'https://%s/api/v2/grants' % self.domain + if id is not None: + return url + '/' + id + return url + + def all(self, page=None, per_page=None, include_totals=False, extra_params=None): + """Retrieves all grants. + + Args: + page (int, optional): The result's page number (zero based). + + per_page (int, optional): The amount of entries per page. + + include_totals (bool, optional): True if the query summary is + to be included in the result, False otherwise. + + extra_params (dictionary, optional): The extra parameters to add to + the request. The page, per_page, and include_totals values + specified as parameters take precedence over the ones defined here. + + See: https://auth0.com/docs/api/management/v2#!/Grants/get_grants + """ + params = extra_params or {} + params.update({ + 'page': page, + 'per_page': per_page, + 'include_totals': str(include_totals).lower() + }) + + return self.client.get(self._url(), params=params) + + def delete(self, id): + """Deletes a grant. + + Args: + id (str): The id of the grant to delete + + + See: https://auth0.com/docs/api/management/v2#!/Grants/delete_grants_by_id + """ + url = self._url('%s' % (id)) + return self.client.delete(url) \ No newline at end of file diff --git a/auth0/v3/management/jobs.py b/auth0/v3/management/jobs.py index c06f3db4..9d8864b3 100644 --- a/auth0/v3/management/jobs.py +++ b/auth0/v3/management/jobs.py @@ -45,6 +45,17 @@ def get_failed_job(self, id): url = self._url('{}/errors'.format(id)) return self.client.get(url) + def get_results(self, job_id): + """Get results of a job + + Args: + job_id (str): The ID of the job. + + See: https://auth0.com/docs/api/management/v2#!/Jobs/get_results + """ + url = self._url('%s/results' % job_id) + return self.client.get(url) + def export_users(self, body): """Export all users to a file using a long running job. diff --git a/auth0/v3/management/rules_configs.py b/auth0/v3/management/rules_configs.py new file mode 100644 index 00000000..cfde7b02 --- /dev/null +++ b/auth0/v3/management/rules_configs.py @@ -0,0 +1,60 @@ +from .rest import RestClient + + +class RulesConfigs(object): + + """RulesConfig endpoint implementation. + + 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) + """ + + def __init__(self, domain, token, telemetry=True): + self.domain = domain + self.client = RestClient(jwt=token, telemetry=telemetry) + + def _url(self, id=None): + url = 'https://%s/api/v2/rules-configs' % self.domain + if id is not None: + return url + '/' + id + return url + + def all(self): + """Lists the config variable keys for rules. + + See: https://auth0.com/docs/api/management/v2#!/Rules_Configs/get_rules_configs + """ + return self.client.get(self._url()) + + def unset(self, key): + """Removes the rules config for a given key. + + Args: + key (str): rules config key to remove + + See: https://auth0.com/docs/api/management/v2#!/Rules_Configs/delete_rules_configs_by_key + """ + params = { + 'key': key + } + return self.client.delete(self._url(), params=params) + + def set(self, key, value): + """Sets the rules config for a given key. + + Args: + key (str): rules config key to set + + value (str): value to set for the rules config key + + See: https://auth0.com/docs/api/management/v2#!/Rules_Configs/put_rules_configs_by_key + """ + url = self._url('{}'.format(key)) + body = {'value': value} + return self.client.put(url, data=body) + diff --git a/auth0/v3/test/management/test_clients.py b/auth0/v3/test/management/test_clients.py index 1c952b0d..ae9d5e55 100644 --- a/auth0/v3/test/management/test_clients.py +++ b/auth0/v3/test/management/test_clients.py @@ -111,3 +111,17 @@ def test_update(self, mock_rc): self.assertEqual('https://domain/api/v2/clients/this-id', args[0]) self.assertEqual(kwargs['data'], {'a': 'b', 'c': 'd'}) + + @mock.patch('auth0.v3.management.clients.RestClient') + def test_rotate_secret(self, mock_rc): + mock_instance = mock_rc.return_value + + c = Clients(domain='domain', token='jwttoken') + c.rotate_secret('this-id') + + mock_instance.get.assert_called_with( + 'https://domain/api/v2/clients/this-id/rotate-secret', params={'id': 'this-id'} + ) + + + diff --git a/auth0/v3/test/management/test_custom_domains.py b/auth0/v3/test/management/test_custom_domains.py new file mode 100644 index 00000000..d80dc825 --- /dev/null +++ b/auth0/v3/test/management/test_custom_domains.py @@ -0,0 +1,52 @@ +import unittest +import mock +from ...management.custom_domains import CustomDomains + + +class TestCustomDomains(unittest.TestCase): + + @mock.patch('auth0.v3.management.custom_domains.RestClient') + def test_get_all(self, mock_rc): + mock_instance = mock_rc.return_value + + g = CustomDomains(domain='domain', token='jwttoken') + g.all() + + mock_instance.get.assert_called_with( + 'https://domain/api/v2/custom-domains' + ) + + @mock.patch('auth0.v3.management.custom_domains.RestClient') + def test_create_new(self, mock_rc): + mock_instance = mock_rc.return_value + + g = CustomDomains(domain='domain', token='jwttoken') + g.create_new(body={'a': 'b', 'c': 'd','e': 'f'}) + + args, kwargs = mock_instance.post.call_args + + self.assertEqual('https://domain/api/v2/custom-domains',args[0]) + self.assertEqual(kwargs['data'], {'a': 'b', 'c': 'd','e': 'f'}) + + @mock.patch('auth0.v3.management.custom_domains.RestClient') + def test_get_domain_by_id(self, mock_rc): + mock_instance = mock_rc.return_value + + g = CustomDomains(domain='domain', token='jwttoken') + g.get('an-id') + + mock_instance.get.assert_called_with('https://domain/api/v2/custom-domains/an-id') + + + @mock.patch('auth0.v3.management.custom_domains.RestClient') + def test_verify(self, mock_rc): + mock_instance = mock_rc.return_value + + g = CustomDomains(domain='domain', token='jwttoken') + g.verify('an-id') + + args, kwargs = mock_instance.post.call_args + + self.assertEqual('https://domain/api/v2/custom-domains/an-id/verify', args[0]) + + diff --git a/auth0/v3/test/management/test_grants.py b/auth0/v3/test/management/test_grants.py new file mode 100644 index 00000000..35d06369 --- /dev/null +++ b/auth0/v3/test/management/test_grants.py @@ -0,0 +1,31 @@ +import unittest +import mock +from ...management.grants import Grants + + +class TestGrants(unittest.TestCase): + + @mock.patch('auth0.v3.management.grants.RestClient') + def test_get_all(self, mock_rc): + mock_instance = mock_rc.return_value + + g = Grants(domain='domain', token='jwttoken') + g.all(extra_params={'user_id':'an-id', 'client_id': 'an-id', 'audience':'test'}) + + args, kwargs = mock_instance.get.call_args + + mock_instance.get.assert_called_with( + 'https://domain/api/v2/grants', params={'user_id': 'an-id', 'client_id': 'an-id', 'audience': 'test', 'page': None, 'per_page': None, 'include_totals': 'false'} + ) + + + @mock.patch('auth0.v3.management.grants.RestClient') + def test_delete(self, mock_rc): + mock_instance = mock_rc.return_value + + c = Grants(domain='domain', token='jwttoken') + c.delete('an-id') + + mock_instance.delete.assert_called_with( + 'https://domain/api/v2/grants/an-id' + ) diff --git a/auth0/v3/test/management/test_jobs.py b/auth0/v3/test/management/test_jobs.py index 72870118..e2685c4e 100644 --- a/auth0/v3/test/management/test_jobs.py +++ b/auth0/v3/test/management/test_jobs.py @@ -27,6 +27,17 @@ def get_failed_job(self, mock_rc): 'https://domain/api/v2/jobs/an-id/errors', ) + @mock.patch('auth0.v3.management.jobs.RestClient') + def get_job_results(self, mock_rc): + mock_instance = mock_rc.return_value + + j = Jobs(domain='domain', token='jwttoken') + j.get('an-id') + + mock_instance.get.assert_called_with( + 'https://domain/api/v2/jobs/an-id/results', + ) + @mock.patch('auth0.v3.management.jobs.RestClient') def test_export_users(self, mock_rc): mock_instance = mock_rc.return_value diff --git a/auth0/v3/test/management/test_rules_configs.py b/auth0/v3/test/management/test_rules_configs.py new file mode 100644 index 00000000..68ee0181 --- /dev/null +++ b/auth0/v3/test/management/test_rules_configs.py @@ -0,0 +1,43 @@ +import unittest +import mock +from ...management.rules_configs import RulesConfigs + + +class TestRules(unittest.TestCase): + + @mock.patch('auth0.v3.management.rules_configs.RestClient') + def test_all(self, mock_rc): + mock_instance = mock_rc.return_value + + c = RulesConfigs(domain='domain', token='jwttoken') + + c.all() + + args, kwargs = mock_instance.get.call_args + + self.assertEqual('https://domain/api/v2/rules-configs', args[0]) + + @mock.patch('auth0.v3.management.rules_configs.RestClient') + def test_unset(self, mock_rc): + mock_instance = mock_rc.return_value + + c = RulesConfigs(domain='domain', token='jwttoken') + c.unset('an-id') + + mock_instance.delete.assert_called_with( + 'https://domain/api/v2/rules-configs', params={'key': 'an-id'} + ) + + @mock.patch('auth0.v3.management.rules_configs.RestClient') + def test_set(self, mock_rc): + mock_instance = mock_rc.return_value + + g = RulesConfigs(domain='domain', token='jwttoken') + g.set('key', 'MY_RULES_CONFIG_VALUES') + + args, kwargs = mock_instance.put.call_args + self.assertEqual('https://domain/api/v2/rules-configs/key', args[0]) + + + +