diff --git a/castle/api/approve_device.py b/castle/api/approve_device.py new file mode 100644 index 0000000..7593a34 --- /dev/null +++ b/castle/api/approve_device.py @@ -0,0 +1,8 @@ +from castle.api_request import APIRequest +from castle.commands.approve_device import CommandsApproveDevice + + +class APIApproveDevice(object): + @staticmethod + def retrieve(device_token): + return APIRequest().call(CommandsApproveDevice.build(device_token)) diff --git a/castle/api/report_device.py b/castle/api/report_device.py new file mode 100644 index 0000000..26d9bd6 --- /dev/null +++ b/castle/api/report_device.py @@ -0,0 +1,8 @@ +from castle.api_request import APIRequest +from castle.commands.report_device import CommandsReportDevice + + +class APIReportDevice(object): + @staticmethod + def retrieve(device_token): + return APIRequest().call(CommandsReportDevice.build(device_token)) diff --git a/castle/commands/approve_device.py b/castle/commands/approve_device.py new file mode 100644 index 0000000..ea77533 --- /dev/null +++ b/castle/commands/approve_device.py @@ -0,0 +1,15 @@ +from castle.command import Command +from castle.validators.present import ValidatorsPresent + + +class CommandsApproveDevice(object): + + @staticmethod + def build(device_token): + ValidatorsPresent.call({'device_token': device_token}, 'device_token') + + return Command( + method='put', + path="devices/{device_token}/approve".format(device_token=device_token), + data=None + ) diff --git a/castle/commands/report_device.py b/castle/commands/report_device.py new file mode 100644 index 0000000..208a25a --- /dev/null +++ b/castle/commands/report_device.py @@ -0,0 +1,15 @@ +from castle.command import Command +from castle.validators.present import ValidatorsPresent + + +class CommandsReportDevice(object): + + @staticmethod + def build(device_token): + ValidatorsPresent.call({'device_token': device_token}, 'device_token') + + return Command( + method='put', + path="devices/{device_token}/report".format(device_token=device_token), + data=None + ) diff --git a/castle/test/__init__.py b/castle/test/__init__.py index d11aa83..9aa67a4 100644 --- a/castle/test/__init__.py +++ b/castle/test/__init__.py @@ -5,18 +5,22 @@ TEST_MODULES = [ + 'castle.test.api.approve_device_test', 'castle.test.api.get_device_test', 'castle.test.api.get_devices_test', + 'castle.test.api.report_device_test', 'castle.test.api.review_test', 'castle.test.api_request_test', 'castle.test.client_id.extract_test', 'castle.test.client_test', 'castle.test.command_test', + 'castle.test.commands.approve_device_test', 'castle.test.commands.authenticate_test', - 'castle.test.commands.identify_test', - 'castle.test.commands.impersonate_test', 'castle.test.commands.get_device_test', 'castle.test.commands.get_devices_test', + 'castle.test.commands.identify_test', + 'castle.test.commands.impersonate_test', + 'castle.test.commands.report_device_test', 'castle.test.commands.review_test', 'castle.test.commands.track_test', 'castle.test.configuration_test', diff --git a/castle/test/api/approve_device_test.py b/castle/test/api/approve_device_test.py new file mode 100644 index 0000000..837974b --- /dev/null +++ b/castle/test/api/approve_device_test.py @@ -0,0 +1,27 @@ +import json +import responses + +from castle.test import unittest +from castle.api.approve_device import APIApproveDevice +from castle.configuration import configuration + + +class APIApproveDeviceTestCase(unittest.TestCase): + def setUp(self): + configuration.api_secret = 'test' + + def tearDown(self): + configuration.api_secret = None + + @responses.activate + def test_retrieve(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( + responses.PUT, + 'https://api.castle.io/v1/devices/1234/approve', + body=response_text, + status=200 + ) + device_token = '1234' + self.assertEqual(APIApproveDevice.retrieve(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 f5b94e4..d56b970 100644 --- a/castle/test/api/get_device_test.py +++ b/castle/test/api/get_device_test.py @@ -16,7 +16,7 @@ def tearDown(self): @responses.activate def test_retrieve(self): # pylint: disable=line-too-long - response_text = "{\"id\":\"d_id\",\"manufacturer\":\"d_manufacturer\",\"model\":\"d_model\",\"name\":\"d_name\",\"type\":\"d_type\"}" + 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( responses.GET, 'https://api.castle.io/v1/devices/1234', diff --git a/castle/test/api/get_devices_test.py b/castle/test/api/get_devices_test.py index 8854787..174c096 100644 --- a/castle/test/api/get_devices_test.py +++ b/castle/test/api/get_devices_test.py @@ -16,7 +16,7 @@ def tearDown(self): @responses.activate def test_retrieve(self): # pylint: disable=line-too-long - response_text = "{\"total_count\":1,\"data\":[{\"token\":\"abcdefg12345\",\"risk\":0.0,\"created_at\":\"2018-06-15T16:36:22.916Z\",\"last_seen_at\":\"2018-07-19T23:09:29.681Z\",\"context\":{\"ip\":\"1.1.1.1\",\"user_agent\":{\"raw\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36 OPR/54.0.2952.51\",\"browser\":\"Opera\",\"version\":\"54.0.2952\",\"os\":\"Mac OS X 10.13.6\",\"mobile\":false,\"platform\":\"Mac OS X\",\"device\":\"Unknown\",\"family\":\"Opera\"},\"type\":\"desktop\"},\"is_current_device\":true}]}" + 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( responses.GET, 'https://api.castle.io/v1/users/1234/devices', diff --git a/castle/test/api/report_device_test.py b/castle/test/api/report_device_test.py new file mode 100644 index 0000000..610d2a3 --- /dev/null +++ b/castle/test/api/report_device_test.py @@ -0,0 +1,27 @@ +import json +import responses + +from castle.test import unittest +from castle.api.report_device import APIReportDevice +from castle.configuration import configuration + + +class APIReportDeviceTestCase(unittest.TestCase): + def setUp(self): + configuration.api_secret = 'test' + + def tearDown(self): + configuration.api_secret = None + + @responses.activate + def test_retrieve(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( + responses.PUT, + 'https://api.castle.io/v1/devices/1234/report', + body=response_text, + status=200 + ) + device_token = '1234' + self.assertEqual(APIReportDevice.retrieve(device_token), json.loads(response_text)) diff --git a/castle/test/commands/approve_device_test.py b/castle/test/commands/approve_device_test.py new file mode 100644 index 0000000..2fddef3 --- /dev/null +++ b/castle/test/commands/approve_device_test.py @@ -0,0 +1,21 @@ +from castle.test import unittest +from castle.command import Command +from castle.commands.approve_device import CommandsApproveDevice +from castle.errors import InvalidParametersError + + +def device_token(): + return '1234' + + +class CommandsApproveDeviceTestCase(unittest.TestCase): + def test_build_no_device_token(self): + with self.assertRaises(InvalidParametersError): + CommandsApproveDevice.build('') + + def test_build(self): + command = CommandsApproveDevice.build(device_token()) + self.assertIsInstance(command, Command) + self.assertEqual(command.method, 'put') + self.assertEqual(command.path, 'devices/1234/approve') + self.assertEqual(command.data, None) diff --git a/castle/test/commands/report_device_test.py b/castle/test/commands/report_device_test.py new file mode 100644 index 0000000..a11f298 --- /dev/null +++ b/castle/test/commands/report_device_test.py @@ -0,0 +1,21 @@ +from castle.test import unittest +from castle.command import Command +from castle.commands.report_device import CommandsReportDevice +from castle.errors import InvalidParametersError + + +def device_token(): + return '1234' + + +class CommandsReportDeviceTestCase(unittest.TestCase): + def test_build_no_device_token(self): + with self.assertRaises(InvalidParametersError): + CommandsReportDevice.build('') + + def test_build(self): + command = CommandsReportDevice.build(device_token()) + self.assertIsInstance(command, Command) + self.assertEqual(command.method, 'put') + self.assertEqual(command.path, 'devices/1234/report') + self.assertEqual(command.data, None)