From 0c5dad5adc9d830ff84439f13211e9073f026fea Mon Sep 17 00:00:00 2001 From: bartes Date: Tue, 24 Mar 2020 01:13:19 +0100 Subject: [PATCH 1/6] moved request/response/session to apis namespace --- castle/api.py | 8 +++--- castle/apis/__init__.py | 0 castle/{ => apis}/request.py | 10 +++---- castle/{ => apis}/response.py | 2 +- castle/apis/session_sharer.py | 16 ++++++++++++ castle/session.py | 16 ------------ castle/test/__init__.py | 6 ++--- castle/test/api_test.py | 4 +-- castle/test/apis/__init__.py | 0 castle/test/{ => apis}/request_test.py | 18 ++++++------- castle/test/{ => apis}/response_test.py | 26 +++++++++---------- .../session_sharer_test.py} | 2 +- 12 files changed, 54 insertions(+), 54 deletions(-) create mode 100644 castle/apis/__init__.py rename castle/{ => apis}/request.py (82%) rename castle/{ => apis}/response.py (97%) create mode 100644 castle/apis/session_sharer.py delete mode 100644 castle/session.py create mode 100644 castle/test/apis/__init__.py rename castle/test/{ => apis}/request_test.py (82%) rename castle/test/{ => apis}/response_test.py (64%) rename castle/test/{session_test.py => apis/session_sharer_test.py} (92%) diff --git a/castle/api.py b/castle/api.py index 7523ebe..3f4257e 100644 --- a/castle/api.py +++ b/castle/api.py @@ -1,13 +1,13 @@ -from castle.request import Request -from castle.response import Response +from castle.apis.request import ApisRequest +from castle.apis.response import ApisResponse class Api(object): def __init__(self): - self.req = Request({'Content-Type': 'application/json'}) + self.req = ApisRequest({'Content-Type': 'application/json'}) def request(self, command): return self.req.build_query(command.method, command.path, command.data) def call(self, command): - return Response(self.request(command)).call() + return ApisResponse(self.request(command)).call() diff --git a/castle/apis/__init__.py b/castle/apis/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/castle/request.py b/castle/apis/request.py similarity index 82% rename from castle/request.py rename to castle/apis/request.py index 5b0ab0e..7fe1043 100644 --- a/castle/request.py +++ b/castle/apis/request.py @@ -1,13 +1,13 @@ import json from castle.configuration import configuration -from castle.session import SessionSharer +from castle.apis.session_sharer import ApisSessionSharer -class Request(object): +class ApisRequest(object): def __init__(self, headers=None): self.headers = headers or dict() - self.base_url = Request.build_base_url() - self.sharer = SessionSharer() + self.base_url = ApisRequest.build_base_url() + self.sharer = ApisSessionSharer() def build_query(self, method, path, params): return self.sharer.session.request( @@ -16,7 +16,7 @@ def build_query(self, method, path, params): auth=('', configuration.api_secret), timeout=configuration.request_timeout / 1000.0, headers=self.headers, - verify=Request.verify(), + verify=ApisRequest.verify(), data=None if params is None else json.dumps(params) ) diff --git a/castle/response.py b/castle/apis/response.py similarity index 97% rename from castle/response.py rename to castle/apis/response.py index 180d541..882bf87 100644 --- a/castle/response.py +++ b/castle/apis/response.py @@ -12,7 +12,7 @@ } -class Response(object): +class ApisResponse(object): def __init__(self, response): self.response = response diff --git a/castle/apis/session_sharer.py b/castle/apis/session_sharer.py new file mode 100644 index 0000000..6f3657a --- /dev/null +++ b/castle/apis/session_sharer.py @@ -0,0 +1,16 @@ +import requests + + +class ApisSessionSharer(object): + class __ApisSessionSharer: + def __init__(self): + self.session = requests.Session() + instance = None + + def __new__(cls): + if not ApisSessionSharer.instance: + ApisSessionSharer.instance = ApisSessionSharer.__ApisSessionSharer() + return ApisSessionSharer.instance + + def __getattr__(self, name): + return getattr(self.instance, name) diff --git a/castle/session.py b/castle/session.py deleted file mode 100644 index b1a2acc..0000000 --- a/castle/session.py +++ /dev/null @@ -1,16 +0,0 @@ -import requests - - -class SessionSharer(object): - class __SessionSharer: - def __init__(self): - self.session = requests.Session() - instance = None - - def __new__(cls): - if not SessionSharer.instance: - SessionSharer.instance = SessionSharer.__SessionSharer() - return SessionSharer.instance - - def __getattr__(self, name): - return getattr(self.instance, name) diff --git a/castle/test/__init__.py b/castle/test/__init__.py index a503094..abeba44 100644 --- a/castle/test/__init__.py +++ b/castle/test/__init__.py @@ -5,6 +5,9 @@ TEST_MODULES = [ + 'castle.test.apis.request_test', + 'castle.test.apis.response_test', + 'castle.test.apis.session_sharer_test', 'castle.test.api_test', 'castle.test.client_test', 'castle.test.configuration_test', @@ -22,11 +25,8 @@ 'castle.test.extractors.ip_test', 'castle.test.failover_response_test', 'castle.test.headers_formatter_test', - 'castle.test.request_test', - 'castle.test.response_test', 'castle.test.review_test', 'castle.test.secure_mode_test', - 'castle.test.session_test', 'castle.test.validators.not_supported_test', 'castle.test.validators.present_test', 'castle.test.utils_test' diff --git a/castle/test/api_test.py b/castle/test/api_test.py index 8953843..9ae2f6e 100644 --- a/castle/test/api_test.py +++ b/castle/test/api_test.py @@ -4,7 +4,7 @@ from castle.test import unittest from castle.api import Api from castle.command import Command -from castle.request import Request +from castle.apis.request import ApisRequest def command(): @@ -17,7 +17,7 @@ def response_text(): class ApiTestCase(unittest.TestCase): def test_init(self): - self.assertIsInstance(Api().req, Request) + self.assertIsInstance(Api().req, ApisRequest) @responses.activate def test_request(self): diff --git a/castle/test/apis/__init__.py b/castle/test/apis/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/castle/test/request_test.py b/castle/test/apis/request_test.py similarity index 82% rename from castle/test/request_test.py rename to castle/test/apis/request_test.py index 98f6e56..de78085 100644 --- a/castle/test/request_test.py +++ b/castle/test/apis/request_test.py @@ -2,7 +2,7 @@ from requests import Response import responses from castle.test import unittest -from castle.request import Request +from castle.apis.request import ApisRequest from castle.configuration import configuration try: @@ -28,13 +28,13 @@ def do_POST(self): return httpd_thread -class RequestTestCase(unittest.TestCase): +class ApisRequestTestCase(unittest.TestCase): def test_init_headers(self): headers = {'X-Castle-Client-Id': '1234'} - self.assertEqual(Request(headers).headers, headers) + self.assertEqual(ApisRequest(headers).headers, headers) def test_init_base_url(self): - self.assertEqual(Request().base_url, 'https://api.castle.io/v1') + self.assertEqual(ApisRequest().base_url, 'https://api.castle.io/v1') @responses.activate def test_build_query(self): @@ -48,7 +48,7 @@ def test_build_query(self): json=response_text, status=200 ) - res = Request().build_query('post', 'authenticate', data) + res = ApisRequest().build_query('post', 'authenticate', data) self.assertIsInstance(res, Response) self.assertEqual(res.status_code, 200) self.assertEqual(res.json(), response_text) @@ -59,7 +59,7 @@ def test_connection_pooled(self): configuration.host = 'localhost' configuration.port = 65521 run_server() - request = Request() + request = ApisRequest() data = {'event': '$login.authenticate', 'user_id': '12345'} response = request.build_query('post', 'authenticate', data) num_pools = len(response.connection.poolmanager.pools.keys()) @@ -69,14 +69,14 @@ def test_connection_pooled(self): def test_build_url(self): self.assertEqual( - Request().build_url('authenticate'), + ApisRequest().build_url('authenticate'), 'https://api.castle.io/v1/authenticate' ) def test_verify_true(self): - self.assertEqual(Request().verify(), True) + self.assertEqual(ApisRequest().verify(), True) def test_verify_false(self): configuration.port = 3001 - self.assertEqual(Request().verify(), False) + self.assertEqual(ApisRequest().verify(), False) configuration.port = 443 diff --git a/castle/test/response_test.py b/castle/test/apis/response_test.py similarity index 64% rename from castle/test/response_test.py rename to castle/test/apis/response_test.py index 860fb18..4da788b 100644 --- a/castle/test/response_test.py +++ b/castle/test/apis/response_test.py @@ -2,7 +2,7 @@ import requests from castle.test import unittest -from castle.response import Response +from castle.apis.response import ApisResponse from castle.exceptions import BadRequestError, UnauthorizedError, ForbiddenError, NotFoundError, \ UserUnauthorizedError, InvalidParametersError, InternalServerError @@ -14,16 +14,16 @@ def response(status_code=200, body=None): return resp -class ResponseTestCase(unittest.TestCase): +class ApisResponseTestCase(unittest.TestCase): def test_response_none(self): - self.assertEqual(Response(response()).call(), {}) + self.assertEqual(ApisResponse(response()).call(), {}) def test_response_empty(self): - self.assertEqual(Response(response(body=b'')).call(), {}) + self.assertEqual(ApisResponse(response(body=b'')).call(), {}) def test_response_authenticate(self): self.assertEqual( - Response( + ApisResponse( response(body=b'{"action":"allow","user_id":"12345"}')).call(), {"action": "allow", "user_id": "12345"} ) @@ -31,32 +31,32 @@ def test_response_authenticate(self): def test_verify_200_299(self): for status_code in range(200, 299): self.assertEqual( - Response(response(status_code=status_code)).verify(), None) + ApisResponse(response(status_code=status_code)).verify(), None) def test_verify_400(self): with self.assertRaises(BadRequestError): - Response(response(status_code=400)).verify() + ApisResponse(response(status_code=400)).verify() def test_verify_401(self): with self.assertRaises(UnauthorizedError): - Response(response(status_code=401)).verify() + ApisResponse(response(status_code=401)).verify() def test_verify_403(self): with self.assertRaises(ForbiddenError): - Response(response(status_code=403)).verify() + ApisResponse(response(status_code=403)).verify() def test_verify_404(self): with self.assertRaises(NotFoundError): - Response(response(status_code=404)).verify() + ApisResponse(response(status_code=404)).verify() def test_verify_419(self): with self.assertRaises(UserUnauthorizedError): - Response(response(status_code=419)).verify() + ApisResponse(response(status_code=419)).verify() def test_verify_422(self): with self.assertRaises(InvalidParametersError): - Response(response(status_code=422)).verify() + ApisResponse(response(status_code=422)).verify() def test_verify_500(self): with self.assertRaises(InternalServerError): - Response(response(status_code=500)).verify() + ApisResponse(response(status_code=500)).verify() diff --git a/castle/test/session_test.py b/castle/test/apis/session_sharer_test.py similarity index 92% rename from castle/test/session_test.py rename to castle/test/apis/session_sharer_test.py index 365b20b..3cf7e17 100644 --- a/castle/test/session_test.py +++ b/castle/test/apis/session_sharer_test.py @@ -13,7 +13,7 @@ def request(): return req -class SessionTestCase(unittest.TestCase): +class ApisSessionSharerTestCase(unittest.TestCase): def test_init(self): client = Client.from_request(request(), {}) client2 = Client.from_request(request(), {}) From fd73d9125fedda768430bcc37f1c904f09ab8686 Mon Sep 17 00:00:00 2001 From: bartes Date: Tue, 24 Mar 2020 01:34:07 +0100 Subject: [PATCH 2/6] added config validation before sending request --- castle/api.py | 4 ++++ castle/configuration.py | 3 +++ castle/test/api_test.py | 14 ++++++++++++++ castle/test/client_test.py | 4 ++++ castle/test/review_test.py | 7 +++++++ 5 files changed, 32 insertions(+) diff --git a/castle/api.py b/castle/api.py index 3f4257e..9da24f9 100644 --- a/castle/api.py +++ b/castle/api.py @@ -1,5 +1,7 @@ from castle.apis.request import ApisRequest from castle.apis.response import ApisResponse +from castle.configuration import configuration +from castle.exceptions import ConfigurationError class Api(object): @@ -7,6 +9,8 @@ def __init__(self): self.req = ApisRequest({'Content-Type': 'application/json'}) def request(self, command): + if not configuration.isValid(): + raise ConfigurationError return self.req.build_query(command.method, command.path, command.data) def call(self, command): diff --git a/castle/configuration.py b/castle/configuration.py index 0b55c3d..adb3ecd 100644 --- a/castle/configuration.py +++ b/castle/configuration.py @@ -51,6 +51,9 @@ def __init__(self): self.ip_headers = [] self.trusted_proxies = [] + def isValid(self): + return self.host and self.port and self.api_secret + @property def api_secret(self): return self.__api_secret diff --git a/castle/test/api_test.py b/castle/test/api_test.py index 9ae2f6e..5d016d7 100644 --- a/castle/test/api_test.py +++ b/castle/test/api_test.py @@ -5,6 +5,8 @@ from castle.api import Api from castle.command import Command from castle.apis.request import ApisRequest +from castle.configuration import configuration +from castle.exceptions import ConfigurationError def command(): @@ -16,6 +18,12 @@ def response_text(): class ApiTestCase(unittest.TestCase): + def setUp(self): + configuration.api_secret = 'test' + + def tearDown(self): + configuration.api_secret = None + def test_init(self): self.assertIsInstance(Api().req, ApisRequest) @@ -38,3 +46,9 @@ def test_call(self): status=200 ) self.assertEqual(Api().call(command()), response_text()) + + @responses.activate + def test_no_api_secret(self): + configuration.api_secret = '' + with self.assertRaises(ConfigurationError): + Api().call(command()) diff --git a/castle/test/client_test.py b/castle/test/client_test.py index 7a46ae4..fd82089 100644 --- a/castle/test/client_test.py +++ b/castle/test/client_test.py @@ -26,6 +26,10 @@ def setUp(self): self.mock_timestamp = timestamp_patcher.start() self.mock_timestamp.return_value = '2018-01-02T03:04:05.678' self.addCleanup(timestamp_patcher.stop) + configuration.api_secret = 'test' + + def tearDown(self): + configuration.api_secret = None def test_init(self): context = { diff --git a/castle/test/review_test.py b/castle/test/review_test.py index a8eb1f9..acb5e10 100644 --- a/castle/test/review_test.py +++ b/castle/test/review_test.py @@ -3,9 +3,16 @@ from castle.test import unittest from castle.review import Review +from castle.configuration import configuration class ReviewTestCase(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 From 44bd15c47d4a4ac7a378b00e376bbc6058b4b9f0 Mon Sep 17 00:00:00 2001 From: bartes Date: Tue, 24 Mar 2020 01:42:36 +0100 Subject: [PATCH 3/6] updated version --- .python-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.python-version b/.python-version index 4f2c1d1..d2577d9 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.6.6 +3.7.7 From d2d339008051ac6e115d73aa4b6b5258fd13c41e Mon Sep 17 00:00:00 2001 From: bartes Date: Tue, 24 Mar 2020 20:20:35 +0100 Subject: [PATCH 4/6] renamed session_sharer to session --- HISTORY.md | 3 +-- castle/apis/request.py | 6 +++--- castle/apis/session.py | 19 +++++++++++++++++++ castle/apis/session_sharer.py | 16 ---------------- castle/test/__init__.py | 2 +- ...session_sharer_test.py => session_test.py} | 4 ++-- 6 files changed, 26 insertions(+), 24 deletions(-) create mode 100644 castle/apis/session.py delete mode 100644 castle/apis/session_sharer.py rename castle/test/apis/{session_sharer_test.py => session_test.py} (82%) diff --git a/HISTORY.md b/HISTORY.md index 8dc0d15..4ee8dcd 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,8 +1,7 @@ ## master - [#61](https://github.com/castle/castle-python/pull/61) improve headers and ip extractions, improve ip_headers config, add trusted proxies config, added more events to events list - -https://github.com/castle/castle-python/pull/61 +- [#62](https://github.com/castle/castle-python/pull/62) move request,response, session to apis namespace, add config check before doing request ## 3.0.0 (2020-02-13) diff --git a/castle/apis/request.py b/castle/apis/request.py index 7fe1043..4bf56c4 100644 --- a/castle/apis/request.py +++ b/castle/apis/request.py @@ -1,16 +1,16 @@ import json from castle.configuration import configuration -from castle.apis.session_sharer import ApisSessionSharer +from castle.apis.session import ApisSession class ApisRequest(object): def __init__(self, headers=None): self.headers = headers or dict() self.base_url = ApisRequest.build_base_url() - self.sharer = ApisSessionSharer() + self.session = ApisSession() def build_query(self, method, path, params): - return self.sharer.session.request( + return self.session.get().request( method, self.build_url(path), auth=('', configuration.api_secret), diff --git a/castle/apis/session.py b/castle/apis/session.py new file mode 100644 index 0000000..965e927 --- /dev/null +++ b/castle/apis/session.py @@ -0,0 +1,19 @@ +import requests + + +class ApisSession(object): + class __ApisSession: + def __init__(self): + self.session = requests.Session() + + def get(self): + return self.session + instance = None + + def __new__(cls): + if not ApisSession.instance: + ApisSession.instance = ApisSession.__ApisSession() + return ApisSession.instance + + def get(self): + return self.instance.get() diff --git a/castle/apis/session_sharer.py b/castle/apis/session_sharer.py deleted file mode 100644 index 6f3657a..0000000 --- a/castle/apis/session_sharer.py +++ /dev/null @@ -1,16 +0,0 @@ -import requests - - -class ApisSessionSharer(object): - class __ApisSessionSharer: - def __init__(self): - self.session = requests.Session() - instance = None - - def __new__(cls): - if not ApisSessionSharer.instance: - ApisSessionSharer.instance = ApisSessionSharer.__ApisSessionSharer() - return ApisSessionSharer.instance - - def __getattr__(self, name): - return getattr(self.instance, name) diff --git a/castle/test/__init__.py b/castle/test/__init__.py index abeba44..05aea48 100644 --- a/castle/test/__init__.py +++ b/castle/test/__init__.py @@ -7,7 +7,7 @@ TEST_MODULES = [ 'castle.test.apis.request_test', 'castle.test.apis.response_test', - 'castle.test.apis.session_sharer_test', + 'castle.test.apis.session_test', 'castle.test.api_test', 'castle.test.client_test', 'castle.test.configuration_test', diff --git a/castle/test/apis/session_sharer_test.py b/castle/test/apis/session_test.py similarity index 82% rename from castle/test/apis/session_sharer_test.py rename to castle/test/apis/session_test.py index 3cf7e17..cb6eb70 100644 --- a/castle/test/apis/session_sharer_test.py +++ b/castle/test/apis/session_test.py @@ -13,9 +13,9 @@ def request(): return req -class ApisSessionSharerTestCase(unittest.TestCase): +class ApisSessionTestCase(unittest.TestCase): def test_init(self): client = Client.from_request(request(), {}) client2 = Client.from_request(request(), {}) self.assertNotEqual(client.api.request, client2.api.request) - self.assertEqual(client.api.req.sharer, client2.api.req.sharer) + self.assertEqual(client.api.req.session.get(), client2.api.req.session.get()) From ad478400fea33585232aadd8c348c6f966bee0ca Mon Sep 17 00:00:00 2001 From: bartes Date: Thu, 26 Mar 2020 12:08:39 +0100 Subject: [PATCH 5/6] updated travis.yml --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 69054dc..ed79276 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,15 @@ dist: xenial language: python +os: linux python: - "3.4" - "3.5" - "3.6" -matrix: +jobs: include: - python: "3.7" - sudo: true env: - REQUESTS="requests" # latest From 1943628afeae13f9410f743954bc44a053a8982a Mon Sep 17 00:00:00 2001 From: bartes Date: Thu, 26 Mar 2020 12:11:07 +0100 Subject: [PATCH 6/6] aded p3.8 to travis --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ed79276..6bec11a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,10 +6,11 @@ python: - "3.4" - "3.5" - "3.6" + - "3.7" jobs: include: - - python: "3.7" + - python: "3.8" env: - REQUESTS="requests" # latest