From eba3591111c1b9f955a938a9148080f080c83a6d Mon Sep 17 00:00:00 2001 From: Bartosz Date: Tue, 9 Jun 2026 10:40:20 +0200 Subject: [PATCH 1/2] 7.1.1: slim down default request context Reduce the default request context built by ContextGetDefault to headers, ip and library. Remove the internal ClientIdExtract service and the unused cookies plumbing in ContextGetDefault / ContextPrepare. The client id is carried by headers (the X-Castle-Client-Id header / __cid cookie) and resolved server-side. --- CHANGELOG.rst | 8 ++++++ castle/client_id/__init__.py | 0 castle/client_id/extract.py | 7 ----- castle/context/get_default.py | 30 ++------------------- castle/context/prepare.py | 2 +- castle/test/__init__.py | 1 - castle/test/client_id/extract_test.py | 36 ------------------------- castle/test/client_test.py | 3 --- castle/test/context/get_default_test.py | 18 ++++--------- castle/test/context/prepare_test.py | 9 ------- castle/test/payload/prepare_test.py | 3 --- castle/version.py | 2 +- 12 files changed, 17 insertions(+), 102 deletions(-) delete mode 100644 castle/client_id/__init__.py delete mode 100644 castle/client_id/extract.py delete mode 100644 castle/test/client_id/extract_test.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f524e6f..8f6deb0 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,14 @@ master ------ +7.1.1 (2026-06-09) +------------------ + +Housekeeping: +~~~~~~~~~~~~~ +- slim down the default request context to ``headers``, ``ip``, and ``library``; the client id is carried by ``headers`` (the ``X-Castle-Client-Id`` header / ``__cid`` cookie) and resolved server-side +- remove the internal ``ClientIdExtract`` service and the unused ``cookies`` plumbing in ``ContextGetDefault`` / ``ContextPrepare`` + 7.1.0 (2026-05-29) ------------------ diff --git a/castle/client_id/__init__.py b/castle/client_id/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/castle/client_id/extract.py b/castle/client_id/extract.py deleted file mode 100644 index d555552..0000000 --- a/castle/client_id/extract.py +++ /dev/null @@ -1,7 +0,0 @@ -class ClientIdExtract(object): - def __init__(self, headers, cookies=None): - self.headers = headers - self.cookies = cookies or dict() - - def call(self): - return self.headers.get('X-Castle-Client-Id', self.cookies.get('__cid', '')) diff --git a/castle/context/get_default.py b/castle/context/get_default.py index 2096b99..428d018 100644 --- a/castle/context/get_default.py +++ b/castle/context/get_default.py @@ -1,6 +1,5 @@ from castle.version import VERSION from castle.headers.filter import HeadersFilter -from castle.client_id.extract import ClientIdExtract from castle.headers.extract import HeadersExtract from castle.ips.extract import IPsExtract @@ -8,45 +7,20 @@ class ContextGetDefault(object): - def __init__(self, request, cookies): - self.cookies = self._fetch_cookies(request, cookies) + def __init__(self, request): self.pre_headers = HeadersFilter(request).call() def call(self): - context = dict( + return dict( { - 'client_id': self._client_id(), - 'active': True, 'headers': self._headers(), 'ip': self._ip(), 'library': {'name': 'castle-python', 'version': __version__}, } ) - context.update(self._optional_defaults()) - - return context def _ip(self): return IPsExtract(self.pre_headers).call() - def _client_id(self): - return ClientIdExtract(self.pre_headers, self.cookies).call() - def _headers(self): return HeadersExtract(self.pre_headers).call() - - def _optional_defaults(self): - context = dict() - if 'Accept-Language' in self.pre_headers: - context['locale'] = self.pre_headers.get('Accept-Language') - if 'User-Agent' in self.pre_headers: - context['user_agent'] = self.pre_headers.get('User-Agent') - return context - - @staticmethod - def _fetch_cookies(request, cookies): - if cookies: - return cookies - if hasattr(request, 'COOKIES') and request.COOKIES: - return request.COOKIES - return None diff --git a/castle/context/prepare.py b/castle/context/prepare.py index ddad5af..36d3a76 100644 --- a/castle/context/prepare.py +++ b/castle/context/prepare.py @@ -7,5 +7,5 @@ class ContextPrepare(object): def call(request, options=None): if options is None: options = {} - default_context = ContextGetDefault(request, options.get('cookies')).call() + default_context = ContextGetDefault(request).call() return ContextMerge.call(default_context, options.get('context', {})) diff --git a/castle/test/__init__.py b/castle/test/__init__.py index c053fcd..9842a8e 100644 --- a/castle/test/__init__.py +++ b/castle/test/__init__.py @@ -6,7 +6,6 @@ TEST_MODULES = [ 'castle.test.api_request_test', - 'castle.test.client_id.extract_test', 'castle.test.client_test', 'castle.test.command_test', 'castle.test.commands.filter_test', diff --git a/castle/test/client_id/extract_test.py b/castle/test/client_id/extract_test.py deleted file mode 100644 index b46719d..0000000 --- a/castle/test/client_id/extract_test.py +++ /dev/null @@ -1,36 +0,0 @@ -from castle.test import unittest - -from castle.client_id.extract import ClientIdExtract - - -def client_id(): - return 'cookies' - - -def client_id_environ(): - return 'environ' - - -def cookies(): - return {'__cid': client_id()} - - -def environ(): - return {'X-Castle-Client-Id': client_id_environ()} - - -class ClientIdExtractTestCase(unittest.TestCase): - def test_extract_client_id_from_cookiesand_environ(self): - self.assertEqual(ClientIdExtract(environ(), cookies()).call(), client_id_environ()) - - def test_extract_client_id_from_cookies(self): - self.assertEqual(ClientIdExtract({}, cookies()).call(), client_id()) - - def test_extract_client_id_from_environ(self): - self.assertEqual(ClientIdExtract(environ(), {}).call(), client_id_environ()) - - def test_extract_client_id_unavailable(self): - self.assertEqual(ClientIdExtract({}, {}).call(), '') - - def test_extract_client_id_no_cookies(self): - self.assertEqual(ClientIdExtract({}).call(), '') diff --git a/castle/test/client_test.py b/castle/test/client_test.py index 449cb08..d5ea506 100644 --- a/castle/test/client_test.py +++ b/castle/test/client_test.py @@ -30,8 +30,6 @@ def tearDown(self): def test_init(self): context = { - 'active': True, - 'client_id': '1234', 'headers': { 'User-Agent': 'test', 'X-Forwarded-For': '217.144.192.112', @@ -39,7 +37,6 @@ def test_init(self): }, 'ip': '217.144.192.112', 'library': {'name': 'castle-python', 'version': VERSION}, - 'user_agent': 'test', } client = Client.from_request(request(), {}) self.assertEqual(client.do_not_track, False) diff --git a/castle/test/context/get_default_test.py b/castle/test/context/get_default_test.py index 3faf063..5c494f8 100644 --- a/castle/test/context/get_default_test.py +++ b/castle/test/context/get_default_test.py @@ -4,14 +4,6 @@ from castle.context.get_default import ContextGetDefault -def client_id(): - return 'abcd' - - -def cookies(): - return {'__cid': client_id()} - - def request_ip(): return '5.5.5.5' @@ -19,7 +11,7 @@ def request_ip(): def environ(): return { 'HTTP_X_FORWARDED_FOR': request_ip(), - 'HTTP_COOKIE': "__cid={client_id()};other=efgh", + 'HTTP_COOKIE': "__cid=abcd;other=efgh", 'HTTP-Accept-Language': 'en', 'HTTP-User-Agent': 'test', } @@ -34,9 +26,7 @@ def request(env): class ContextGetDefaultTestCase(unittest.TestCase): def test_default_context(self): - context = ContextGetDefault(request(environ()), cookies()).call() - self.assertEqual(context['client_id'], client_id()) - self.assertEqual(context['active'], True) + context = ContextGetDefault(request(environ())).call() self.assertEqual( context['headers'], { @@ -48,4 +38,6 @@ def test_default_context(self): ) self.assertEqual(context['ip'], request_ip()) self.assertDictEqual(context['library'], {'name': 'castle-python', 'version': __version__}) - self.assertEqual(context['user_agent'], 'test') + self.assertNotIn('client_id', context) + self.assertNotIn('active', context) + self.assertNotIn('user_agent', context) diff --git a/castle/test/context/prepare_test.py b/castle/test/context/prepare_test.py index 0c0fac5..48b80d1 100644 --- a/castle/test/context/prepare_test.py +++ b/castle/test/context/prepare_test.py @@ -19,8 +19,6 @@ def request(): class ContextPrepareTestCase(unittest.TestCase): def test_call(self): context = { - 'active': True, - 'client_id': '1234', 'headers': { 'User-Agent': 'test', 'X-Forwarded-For': '217.144.192.112', @@ -28,13 +26,6 @@ def test_call(self): }, 'ip': '217.144.192.112', 'library': {'name': 'castle-python', 'version': VERSION}, - 'user_agent': 'test', } result_context = ContextPrepare.call(request(), {}) self.assertEqual(result_context, context) - - def test_setup_client_id_from_cookies(self): - cookies = {'__cid': '1234'} - options = {'cookies': cookies} - result_context = ContextPrepare.call(request(), options) - self.assertEqual(result_context['client_id'], '1234') diff --git a/castle/test/payload/prepare_test.py b/castle/test/payload/prepare_test.py index 8f833dc..a10cc0f 100644 --- a/castle/test/payload/prepare_test.py +++ b/castle/test/payload/prepare_test.py @@ -18,8 +18,6 @@ def request(): def ctx(): return { - 'active': True, - 'client_id': '1234', 'headers': { 'User-Agent': 'test', 'X-Castle-Client-Id': '1234', @@ -27,7 +25,6 @@ def ctx(): }, 'ip': '217.144.192.112', 'library': {'name': 'castle-python', 'version': VERSION}, - 'user_agent': 'test', } diff --git a/castle/version.py b/castle/version.py index e5ec502..8877077 100644 --- a/castle/version.py +++ b/castle/version.py @@ -1 +1 @@ -VERSION = '7.1.0' +VERSION = '7.1.1' From c6a43965512b6c170be75adcab4320488ed8dbc7 Mon Sep 17 00:00:00 2001 From: Bartosz Date: Tue, 9 Jun 2026 14:38:00 +0200 Subject: [PATCH 2/2] Release as 7.2.0 (minor) instead of 7.1.1 --- CHANGELOG.rst | 2 +- castle/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8f6deb0..7abafa7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,7 +1,7 @@ master ------ -7.1.1 (2026-06-09) +7.2.0 (2026-06-09) ------------------ Housekeeping: diff --git a/castle/version.py b/castle/version.py index 8877077..52dac8d 100644 --- a/castle/version.py +++ b/castle/version.py @@ -1 +1 @@ -VERSION = '7.1.1' +VERSION = '7.2.0'