Skip to content

Commit 593e8d0

Browse files
committed
context: allow setting tenant_id
1 parent d1843c3 commit 593e8d0

3 files changed

Lines changed: 114 additions & 0 deletions

File tree

aikido_zen/context/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ def __init__(self, context_obj=None, body=None, req=None, source=None):
5555
self.cookies = {}
5656
self.query = {}
5757
self.protection_forced_off = None
58+
self.tenant_id = None
5859

5960
# Parse WSGI/ASGI/... request :
6061
self.method = self.remote_address = self.url = None
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""
2+
Exports set_tenant_id for setting the tenant ID on the current context.
3+
"""
4+
5+
from aikido_zen.helpers.logging import logger
6+
from . import get_current_context
7+
8+
9+
def set_tenant_id(tenant_id):
10+
"""
11+
Sets the tenant ID on the current request context.
12+
Used for IDOR protection to verify SQL queries filter on the correct tenant.
13+
"""
14+
if not isinstance(tenant_id, (str, int)):
15+
logger.info(
16+
"set_tenant_id(...) expects a string or integer, found %s instead.",
17+
type(tenant_id),
18+
)
19+
return
20+
21+
str_id = str(tenant_id)
22+
if len(str_id) == 0:
23+
logger.info("set_tenant_id(...) expects a non-empty value.")
24+
return
25+
26+
context = get_current_context()
27+
if not context:
28+
logger.debug("No context set, cannot set tenant_id")
29+
return
30+
31+
context.tenant_id = str_id
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import pytest
2+
from . import current_context, Context
3+
from .set_tenant_id import set_tenant_id
4+
5+
6+
@pytest.fixture(autouse=True)
7+
def run_around_tests():
8+
yield
9+
current_context.set(None)
10+
11+
12+
def _create_context():
13+
wsgi_request = {
14+
"REQUEST_METHOD": "GET",
15+
"HTTP_HEADER_1": "header 1 value",
16+
"wsgi.url_scheme": "http",
17+
"HTTP_HOST": "localhost:8080",
18+
"PATH_INFO": "/hello",
19+
"QUERY_STRING": "",
20+
"CONTENT_TYPE": "application/json",
21+
"REMOTE_ADDR": "198.51.100.23",
22+
}
23+
context = Context(req=wsgi_request, body=None, source="flask")
24+
context.set_as_current_context()
25+
return context
26+
27+
28+
def test_set_tenant_id_string():
29+
ctx = _create_context()
30+
set_tenant_id("tenant_123")
31+
assert ctx.tenant_id == "tenant_123"
32+
33+
34+
def test_set_tenant_id_integer():
35+
ctx = _create_context()
36+
set_tenant_id(42)
37+
assert ctx.tenant_id == "42"
38+
39+
40+
def test_set_tenant_id_empty_string(caplog):
41+
ctx = _create_context()
42+
set_tenant_id("")
43+
assert ctx.tenant_id is None
44+
assert "non-empty" in caplog.text
45+
46+
47+
def test_set_tenant_id_invalid_type(caplog):
48+
ctx = _create_context()
49+
set_tenant_id(12.34)
50+
assert ctx.tenant_id is None
51+
assert "expects a string or integer" in caplog.text
52+
53+
54+
def test_set_tenant_id_none(caplog):
55+
ctx = _create_context()
56+
set_tenant_id(None)
57+
assert ctx.tenant_id is None
58+
assert "expects a string or integer" in caplog.text
59+
60+
61+
def test_set_tenant_id_dict(caplog):
62+
ctx = _create_context()
63+
set_tenant_id({"id": 1})
64+
assert ctx.tenant_id is None
65+
assert "expects a string or integer" in caplog.text
66+
67+
68+
def test_set_tenant_id_no_context(caplog):
69+
import logging
70+
71+
# No context set — should not raise, tenant_id is not applied anywhere
72+
with caplog.at_level(logging.DEBUG, logger="Zen"):
73+
set_tenant_id("tenant_123")
74+
assert "No context set" in caplog.text
75+
76+
77+
def test_set_tenant_id_overwrites():
78+
ctx = _create_context()
79+
set_tenant_id("first")
80+
assert ctx.tenant_id == "first"
81+
set_tenant_id("second")
82+
assert ctx.tenant_id == "second"

0 commit comments

Comments
 (0)