diff --git a/README.rst b/README.rst index d451013..c5cc78e 100644 --- a/README.rst +++ b/README.rst @@ -154,6 +154,50 @@ Events List of Recognized Events can be found `here `_ or in the `docs `_. +Device management +----------------- + +This SDK allows issuing requests to `Castle's Device Management +Endpoints `__. Use these +endpoints for admin-level management of end-user devices (i.e., for an +internal dashboard). + +Fetching device data, approving a device, reporting a device requires a +valid ``device_token``. + +.. code:: python + + from castle.api.get_device import APIGetDevice + + # Get device data + APIGetDevice.call(device_token) + +.. code:: python + + from castle.api.approve_device import APIApproveDevice + + # Approve a device + APIApproveDevice.call(device_token) + +.. code:: python + + from castle.api.report_device import APIReportDevice + + # Report a device + APIReportDevice.call(device_token) + + +Fetching available devices that belong to a given user requires a valid +``user_id``. + +.. code:: python + + from castle.api.get_devices_for_user import APIGetDevicesForUser + + # Get user's devices data + APIGetDevicesForUser.call(user_id) + + Impersonation mode ------------------ diff --git a/castle/api/approve_device.py b/castle/api/approve_device.py index 7593a34..5f2b2d2 100644 --- a/castle/api/approve_device.py +++ b/castle/api/approve_device.py @@ -4,5 +4,5 @@ class APIApproveDevice(object): @staticmethod - def retrieve(device_token): + def call(device_token): return APIRequest().call(CommandsApproveDevice.build(device_token)) diff --git a/castle/api/get_device.py b/castle/api/get_device.py index d6a1035..37f118d 100644 --- a/castle/api/get_device.py +++ b/castle/api/get_device.py @@ -4,5 +4,5 @@ class APIGetDevice(object): @staticmethod - def retrieve(device_token): + def call(device_token): return APIRequest().call(CommandsGetDevice.build(device_token)) diff --git a/castle/api/get_devices.py b/castle/api/get_devices.py deleted file mode 100644 index 4c48db9..0000000 --- a/castle/api/get_devices.py +++ /dev/null @@ -1,8 +0,0 @@ -from castle.api_request import APIRequest -from castle.commands.get_devices import CommandsGetDevices - - -class APIGetDevices(object): - @staticmethod - def retrieve(user_id): - return APIRequest().call(CommandsGetDevices.build(user_id)) diff --git a/castle/api/get_devices_for_user.py b/castle/api/get_devices_for_user.py new file mode 100644 index 0000000..67b507c --- /dev/null +++ b/castle/api/get_devices_for_user.py @@ -0,0 +1,8 @@ +from castle.api_request import APIRequest +from castle.commands.get_devices_for_user import CommandsGetDevicesForUser + + +class APIGetDevicesForUser(object): + @staticmethod + def call(user_id): + return APIRequest().call(CommandsGetDevicesForUser.build(user_id)) diff --git a/castle/api/report_device.py b/castle/api/report_device.py index 26d9bd6..c6b4e21 100644 --- a/castle/api/report_device.py +++ b/castle/api/report_device.py @@ -4,5 +4,5 @@ class APIReportDevice(object): @staticmethod - def retrieve(device_token): + def call(device_token): return APIRequest().call(CommandsReportDevice.build(device_token)) diff --git a/castle/api/review.py b/castle/api/review.py index 98ca3a0..31d3caa 100644 --- a/castle/api/review.py +++ b/castle/api/review.py @@ -4,5 +4,5 @@ class APIReview(object): @staticmethod - def retrieve(review_id): + def call(review_id): return APIRequest().call(CommandsReview.build(review_id)) diff --git a/castle/commands/get_devices.py b/castle/commands/get_devices_for_user.py similarity index 89% rename from castle/commands/get_devices.py rename to castle/commands/get_devices_for_user.py index eaa7919..ed38d0c 100644 --- a/castle/commands/get_devices.py +++ b/castle/commands/get_devices_for_user.py @@ -2,7 +2,7 @@ from castle.validators.present import ValidatorsPresent -class CommandsGetDevices(object): +class CommandsGetDevicesForUser(object): @staticmethod def build(user_id): diff --git a/castle/test/__init__.py b/castle/test/__init__.py index 9aa67a4..ddb00eb 100644 --- a/castle/test/__init__.py +++ b/castle/test/__init__.py @@ -7,7 +7,7 @@ TEST_MODULES = [ 'castle.test.api.approve_device_test', 'castle.test.api.get_device_test', - 'castle.test.api.get_devices_test', + 'castle.test.api.get_devices_for_user_test', 'castle.test.api.report_device_test', 'castle.test.api.review_test', 'castle.test.api_request_test', @@ -17,7 +17,7 @@ 'castle.test.commands.approve_device_test', 'castle.test.commands.authenticate_test', 'castle.test.commands.get_device_test', - 'castle.test.commands.get_devices_test', + 'castle.test.commands.get_devices_for_user_test', 'castle.test.commands.identify_test', 'castle.test.commands.impersonate_test', 'castle.test.commands.report_device_test', diff --git a/castle/test/api/approve_device_test.py b/castle/test/api/approve_device_test.py index 837974b..6bf15b2 100644 --- a/castle/test/api/approve_device_test.py +++ b/castle/test/api/approve_device_test.py @@ -14,7 +14,7 @@ def tearDown(self): configuration.api_secret = None @responses.activate - def test_retrieve(self): + def test_call(self): # pylint: disable=line-too-long response_text = "{\"token\":\"token\",\"created_at\":\"2020-08-13T11:26:47.401Z\",\"last_seen_at\":\"2020-10-18T18:37:22.855Z\",\"user_id\":\"4\",\"approved_at\":\"2020-11-18T12:48:41.112Z\",\"escalated_at\":null,\"mitigated_at\":null,\"context\":{\"ip\":\"127.0.0.1\",\"location\":{\"country_code\":\"PL\",\"country\":\"Poland\"},\"user_agent\":{\"raw\":\"Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/86.0.4240.75 Safari\/537.36\",\"browser\":\"Chrome\",\"version\":\"86.0.4240\",\"os\":\"Mac OS X 10.15.6\",\"mobile\":false,\"platform\":\"Mac OS X\",\"device\":\"Mac\",\"family\":\"Chrome\"},\"properties\":{},\"type\":\"desktop\"},\"is_current_device\":false}" responses.add( @@ -24,4 +24,4 @@ def test_retrieve(self): status=200 ) device_token = '1234' - self.assertEqual(APIApproveDevice.retrieve(device_token), json.loads(response_text)) + self.assertEqual(APIApproveDevice.call(device_token), json.loads(response_text)) diff --git a/castle/test/api/get_device_test.py b/castle/test/api/get_device_test.py index d56b970..c6fa4ee 100644 --- a/castle/test/api/get_device_test.py +++ b/castle/test/api/get_device_test.py @@ -14,7 +14,7 @@ def tearDown(self): configuration.api_secret = None @responses.activate - def test_retrieve(self): + def test_call(self): # pylint: disable=line-too-long response_text = "{\"token\":\"token\",\"created_at\":\"2020-08-13T11:26:47.401Z\",\"last_seen_at\":\"2020-10-18T18:37:22.855Z\",\"user_id\":\"4\",\"approved_at\":null,\"escalated_at\":null,\"mitigated_at\":null,\"context\":{\"ip\":\"127.0.0.1\",\"location\":{\"country_code\":\"PL\",\"country\":\"Poland\"},\"user_agent\":{\"raw\":\"Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/86.0.4240.75 Safari\/537.36\",\"browser\":\"Chrome\",\"version\":\"86.0.4240\",\"os\":\"Mac OS X 10.15.6\",\"mobile\":false,\"platform\":\"Mac OS X\",\"device\":\"Mac\",\"family\":\"Chrome\"},\"properties\":{},\"type\":\"desktop\"},\"is_current_device\":false}" responses.add( @@ -24,4 +24,4 @@ def test_retrieve(self): status=200 ) device_token = '1234' - self.assertEqual(APIGetDevice.retrieve(device_token), json.loads(response_text)) + self.assertEqual(APIGetDevice.call(device_token), json.loads(response_text)) diff --git a/castle/test/api/get_devices_test.py b/castle/test/api/get_devices_for_user_test.py similarity index 89% rename from castle/test/api/get_devices_test.py rename to castle/test/api/get_devices_for_user_test.py index 174c096..f1dec5b 100644 --- a/castle/test/api/get_devices_test.py +++ b/castle/test/api/get_devices_for_user_test.py @@ -2,11 +2,11 @@ import responses from castle.test import unittest -from castle.api.get_devices import APIGetDevices +from castle.api.get_devices_for_user import APIGetDevicesForUser from castle.configuration import configuration -class APIGetDevicesTestCase(unittest.TestCase): +class APIGetDevicesForUserTestCase(unittest.TestCase): def setUp(self): configuration.api_secret = 'test' @@ -14,7 +14,7 @@ def tearDown(self): configuration.api_secret = None @responses.activate - def test_retrieve(self): + def test_call(self): # pylint: disable=line-too-long response_text = "{\"total_count\":2,\"data\":[{\"token\":\"token\",\"created_at\":\"2020-08-01T18:55:45.352Z\",\"last_seen_at\":\"2020-10-18T21:11:57.476Z\",\"user_id\":\"4\",\"approved_at\":\"2020-08-13T09:55:19.286Z\",\"escalated_at\":null,\"mitigated_at\":null,\"context\":{\"ip\":\"127.0.0.1\",\"location\":{\"country_code\":\"PL\",\"country\":\"Poland\"},\"user_agent\":{\"raw\":\"Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/86.0.4240.75 Safari\/537.36\",\"browser\":\"Chrome\",\"version\":\"86.0.4240\",\"os\":\"Mac OS X 10.15.6\",\"mobile\":false,\"platform\":\"Mac OS X\",\"device\":\"Mac\",\"family\":\"Chrome\"},\"properties\":{},\"type\":\"desktop\"},\"is_current_device\":false},{\"token\":\"token2\",\"created_at\":\"2020-08-13T11:26:47.401Z\",\"last_seen_at\":\"2020-10-18T18:37:22.855Z\",\"user_id\":\"4\",\"approved_at\":null,\"escalated_at\":null,\"mitigated_at\":null,\"context\":{\"ip\":\"127.0.0.1\",\"location\":{\"country_code\":\"PL\",\"country\":\"Poland\"},\"user_agent\":{\"raw\":\"Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/86.0.4240.75 Safari\/537.36\",\"browser\":\"Chrome\",\"version\":\"86.0.4240\",\"os\":\"Mac OS X 10.15.6\",\"mobile\":false,\"platform\":\"Mac OS X\",\"device\":\"Mac\",\"family\":\"Chrome\"},\"properties\":{},\"type\":\"desktop\"},\"is_current_device\":false}]}" responses.add( @@ -24,4 +24,4 @@ def test_retrieve(self): status=200 ) user_id = '1234' - self.assertEqual(APIGetDevices.retrieve(user_id), json.loads(response_text)) + self.assertEqual(APIGetDevicesForUser.call(user_id), json.loads(response_text)) diff --git a/castle/test/api/report_device_test.py b/castle/test/api/report_device_test.py index 610d2a3..2dcffa3 100644 --- a/castle/test/api/report_device_test.py +++ b/castle/test/api/report_device_test.py @@ -14,7 +14,7 @@ def tearDown(self): configuration.api_secret = None @responses.activate - def test_retrieve(self): + def test_call(self): # pylint: disable=line-too-long response_text = "{\"token\":\"token\",\"created_at\":\"2020-08-13T11:26:47.401Z\",\"last_seen_at\":\"2020-10-18T18:37:22.855Z\",\"user_id\":\"4\",\"approved_at\":\"2020-11-18T12:48:41.112Z\",\"escalated_at\":null,\"mitigated_at\":null,\"context\":{\"ip\":\"127.0.0.1\",\"location\":{\"country_code\":\"PL\",\"country\":\"Poland\"},\"user_agent\":{\"raw\":\"Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/86.0.4240.75 Safari\/537.36\",\"browser\":\"Chrome\",\"version\":\"86.0.4240\",\"os\":\"Mac OS X 10.15.6\",\"mobile\":false,\"platform\":\"Mac OS X\",\"device\":\"Mac\",\"family\":\"Chrome\"},\"properties\":{},\"type\":\"desktop\"},\"is_current_device\":false}" responses.add( @@ -24,4 +24,4 @@ def test_retrieve(self): status=200 ) device_token = '1234' - self.assertEqual(APIReportDevice.retrieve(device_token), json.loads(response_text)) + self.assertEqual(APIReportDevice.call(device_token), json.loads(response_text)) diff --git a/castle/test/api/review_test.py b/castle/test/api/review_test.py index 59f3c3c..29a27a6 100644 --- a/castle/test/api/review_test.py +++ b/castle/test/api/review_test.py @@ -14,7 +14,7 @@ def tearDown(self): configuration.api_secret = None @responses.activate - def test_retrieve(self): + def test_call(self): # pylint: disable=line-too-long response_text = "{\"id\":\"56b32fa0-880b-0135-74d6-00e650213316\",\"reviewed\":false,\"created_at\":\"2017-09-15T11:59:57.211Z\",\"user_id\":\"1\",\"context\":{\"ip\":\"8.8.8.8\",\"location\":{\"country_code\":\"US\",\"country\":\"United States\",\"region\":null,\"region_code\":null,\"city\":null,\"lat\":37.751,\"lon\":-97.822},\"user_agent\":{\"raw\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36\",\"browser\":\"Chrome\",\"version\":\"60.0.3112\",\"os\":\"Mac OS X 10.12.6\",\"mobile\":false,\"platform\":\"Mac OS X\",\"device\":\"Unknown\",\"family\":\"Chrome\"}}}" responses.add( @@ -24,4 +24,4 @@ def test_retrieve(self): status=200 ) review_id = '1234' - self.assertEqual(APIReview.retrieve(review_id), json.loads(response_text)) + self.assertEqual(APIReview.call(review_id), json.loads(response_text)) diff --git a/castle/test/commands/get_devices_test.py b/castle/test/commands/get_devices_for_user_test.py similarity index 66% rename from castle/test/commands/get_devices_test.py rename to castle/test/commands/get_devices_for_user_test.py index 5853211..f339aa8 100644 --- a/castle/test/commands/get_devices_test.py +++ b/castle/test/commands/get_devices_for_user_test.py @@ -1,6 +1,6 @@ from castle.test import unittest from castle.command import Command -from castle.commands.get_devices import CommandsGetDevices +from castle.commands.get_devices_for_user import CommandsGetDevicesForUser from castle.errors import InvalidParametersError @@ -8,13 +8,13 @@ def user_id(): return '1234' -class CommandsGetDevicesTestCase(unittest.TestCase): +class CommandsGetDevicesForUserTestCase(unittest.TestCase): def test_build_no_user_id(self): with self.assertRaises(InvalidParametersError): - CommandsGetDevices.build('') + CommandsGetDevicesForUser.build('') def test_build(self): - command = CommandsGetDevices.build(user_id()) + command = CommandsGetDevicesForUser.build(user_id()) self.assertIsInstance(command, Command) self.assertEqual(command.method, 'get') self.assertEqual(command.path, 'users/1234/devices')