Skip to content

Commit

Permalink
Add Geo info from Cloudflare when available
Browse files Browse the repository at this point in the history
  • Loading branch information
np5 committed Nov 16, 2024
1 parent 1b67b73 commit 07e069e
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 2 deletions.
97 changes: 97 additions & 0 deletions tests/core_events/test_from_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
from unittest.mock import Mock
from django.test import RequestFactory, TestCase
from accounts.models import User
from zentral.core.events.base import EventRequest, EventRequestGeo


class EventFromRequestTestCase(TestCase):
@classmethod
def setUpTestData(cls):
cls.user = User.objects.create_user("godzilla", "[email protected]", "fomo")

def setUp(self):
self.factory = RequestFactory()

# geo

def test_geo_from_cf_headers(self):
request = self.factory.get(
'/',
HTTP_CF_IPCITY='Hamburg',
HTTP_CF_IPCONTINENT='Europe',
HTTP_CF_IPCOUNTRY='DE',
HTTP_CF_IPLATITUDE='53.551086',
HTTP_CF_IPLONGITUDE='9.993682',
HTTP_CF_REGION='Hamburg',
)
geo = EventRequestGeo.build_from_request(request)
self.assertEqual(
geo.serialize(),
{'city_name': 'Hamburg',
'continent_name': 'Europe',
'country_iso_code': 'DE',
'location': {'lat': 53.551086, 'lon': 9.993682},
'region_name': 'Hamburg'}
)

def test_geo_no_cf_headers(self):
request = self.factory.get('/')
self.assertIsNone(EventRequestGeo.build_from_request(request))

def test_geo_missing_error_cf_headers(self):
request = self.factory.get(
'/',
HTTP_CF_IPCITY='Hamburg',
# HTTP_CF_IPCONTINENT='Europe', # Missing
HTTP_CF_IPCOUNTRY='DE',
HTTP_CF_IPLATITUDE='53.551086',
HTTP_CF_IPLONGITUDE='ABCD', # Error
HTTP_CF_REGION='Hamburg',
)
geo = EventRequestGeo.build_from_request(request)
self.assertEqual(
geo.serialize(),
{'city_name': 'Hamburg',
'country_iso_code': 'DE',
'location': {'lat': 53.551086},
'region_name': 'Hamburg'}
)

# full

def test_request_from_request(self):
request = self.factory.get(
'/',
HTTP_CF_IPCITY='Hamburg',
HTTP_CF_IPCONTINENT='Europe',
HTTP_CF_IPCOUNTRY='DE',
HTTP_CF_IPLATITUDE='53.551086',
HTTP_CF_IPLONGITUDE='9.993682',
HTTP_CF_REGION='Hamburg',
)
request.user = self.user
request.session = Mock()
request.session.get_expire_at_browser_close.return_value = True
request.session.mfa_authenticated = True
event_req = EventRequest.build_from_request(request)
self.assertEqual(
event_req.serialize(),
{'geo': {'city_name': 'Hamburg',
'continent_name': 'Europe',
'country_iso_code': 'DE',
'location': {'lat': 53.551086, 'lon': 9.993682},
'region_name': 'Hamburg'},
'ip': '127.0.0.1',
'method': 'GET',
'path': '/',
'user': {'email': '[email protected]',
'id': self.user.pk,
'is_remote': False,
'is_service_account': False,
'is_superuser': False,
'session': {'expire_at_browser_close': True,
'is_remote': False,
'mfa_authenticated': True,
'token_authenticated': False},
'username': 'godzilla'}}
)
33 changes: 31 additions & 2 deletions zentral/core/events/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,33 @@ def build_from_city(cls, c):
if kwargs:
return cls(**kwargs)

@classmethod
def build_from_request(cls, request):
kwargs = {}
for header, attr, sub_attr, attr_type in (
("HTTP_CF_IPCITY", "city_name", None, None),
("HTTP_CF_IPCONTINENT", "continent_name", None, None),
("HTTP_CF_IPCOUNTRY", "country_iso_code", None, None),
("HTTP_CF_IPLATITUDE", "location", "lat", float),
("HTTP_CF_IPLONGITUDE", "location", "lon", float),
("HTTP_CF_REGION", "region_name", None, None)
):
val = request.META.get(header)
if not val:
continue
if attr_type:
try:
val = attr_type(val)
except ValueError:
continue
d = kwargs
if sub_attr:
d = d.setdefault(attr, {})
attr = sub_attr
d[attr] = val
if kwargs:
return cls(**kwargs)

def serialize(self):
d = {}
for attr in self.geo_attr_list:
Expand All @@ -191,12 +218,14 @@ def __init__(self, user_agent, ip, user=None, geo=None, method=None, path=None):
@classmethod
def build_from_request(cls, request):
user_agent, ip = user_agent_and_ip_address_from_request(request)
user = EventRequestUser.build_from_request(request)
method = request.method
path = request.get_full_path()
user = EventRequestUser.build_from_request(request)
geo = EventRequestGeo.build_from_request(request)
return EventRequest(
user_agent, ip,
user=user, method=method, path=path
method=method, path=path,
user=user, geo=geo,
)

@classmethod
Expand Down

0 comments on commit 07e069e

Please sign in to comment.