def rows(self): rows = [] event_ids = navigation_event_ids_by_user(self.selected_user, self.datespan.startdate, self.datespan.enddate) for event_doc in iter_docs(NavigationEventAudit.get_db(), event_ids): event = NavigationEventAudit.wrap(event_doc) if not self.selected_domain or self.selected_domain == event.domain: rows.append([ event.event_date, event.user, event.domain or '', event.ip_address, event.request_path ]) return rows
def test_copy(self): def _assert(): self.assertEqual(NavigationEventAudit.objects.count(), 3) self.assertEqual([ e.path for e in NavigationEventAudit.objects.order_by( "-event_date").all() ], [ "just/a/checkpoint", '/a/random/phone/restore/', '/a/test-space/phone/restore/' ]) self.assertEqual([ e.params for e in NavigationEventAudit.objects.order_by( "-event_date").all() ], ["", "version=2.0&since=...", "version=2.0&since=..."]) self.assertEqual(AccessAudit.objects.count(), 1) self.assertEqual(AccessAudit.objects.first().path, "/a/delmar/login/") NavigationEventAudit(event_date=datetime(2021, 2, 1, 4), path="just/a/checkpoint").save() copy_events_to_sql(start_time=datetime(2021, 2, 1, 5), end_time=datetime(2021, 2, 1, 1, 59), batch_size=10) _assert() # Re-copying should have no effect copy_events_to_sql(start_time=datetime(2021, 2, 1, 5), end_time=datetime(2021, 2, 1, 2), batch_size=10) _assert()
def test_audit_trace_id_header(self): trace_id = "Root=1-67891233-abcdef012345678912345678" with patch_trace_id_header(): view = make_view() request = make_request(**{to_django_header(TRACE_HEADER): trace_id}) event = NavigationEventAudit.audit_view(request, "*****@*****.**", view, {}) self.assertEqual(event.headers[to_django_header(TRACE_HEADER)], trace_id) event.save()
def test_audit_view_should_truncate_params(self): path = "/path" view = make_view(path) request = make_request(path, params={f"a{x}": "b" for x in range(1000)}) event = NavigationEventAudit.audit_view(request, "*****@*****.**", view, {}) event.save() event.refresh_from_db() self.assertEqual(len(event.params), 4096)
def test_audit_view_should_set_properties(self): path = "/a/block/path" view = make_view(path) request = make_request(path) event = NavigationEventAudit.audit_view(request, "*****@*****.**", view, {}) self.assertEqual(event.path, path) self.assertEqual(event.domain, "block") self.assertEqual(event.request_path, f"{path}?key=value") self.assertEqual(event.description, "*****@*****.**") self.assertNotIn(to_django_header(TRACE_HEADER), event.headers) event.save()
def create_session_events(domain): login_event = dict( user=self.username, domain=domain, session_key=uuid.uuid4().hex, event_date=datetime.utcnow(), ) for event_domain, minutes in [(None, -1), (domain, 0), (domain, 1)]: fields = login_event.copy() # save one event with domain/date changed by loop params: fields["domain"] = event_domain fields['event_date'] += timedelta(minutes=minutes) NavigationEventAudit(**fields).save() # update the fields and save another: # - same session # - different domain # - 5 minutes ealier fields["domain"] = "not-queried" fields["event_date"] += timedelta(minutes=-5) NavigationEventAudit(**fields).save() return login_event
def test_audit_view_should_not_save(self): view = make_view() event = NavigationEventAudit.audit_view(self.request, "username", view, {}) self.assertIsNone(event._id)
def test_audit_view_should_not_save(self): view = make_view() event = NavigationEventAudit.audit_view(make_request(), "*****@*****.**", view, {}) self.assertIsNone(event.id)
def get_events_from_couch(batch_start_key, start_key, end_key, batch_size, start_doc_id=None): navigation_objects = [] access_objects = [] records_returned = 0 next_start_key = None nav_couch_ids = [] access_couch_ids = [] other_doc_type_count = 0 processed_doc_id = start_doc_id couch_docs = _get_couch_docs(start_key, end_key, batch_size, start_doc_id) for result in couch_docs: next_start_key = result['key'] records_returned += 1 doc = result["doc"] kwargs = _pick(doc, ["user", "domain", "ip_address", "session_key", "status_code", "user_agent"]) kwargs.update({ "event_date": force_to_datetime(doc.get("event_date")), "couch_id": doc["_id"], }) processed_doc_id = doc["_id"] if doc["doc_type"] == "NavigationEventAudit": nav_couch_ids.append(doc['_id']) kwargs.update(_pick(doc, ["headers", "status_code", "view", "view_kwargs"])) # Postgres does not play well with control characters in strings # Some crafted URLs can contain these charachters, so replacing them with '' in request_path # https://stackoverflow.com/a/14946355/3537212 request_path = re.sub( r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\xff]', lambda match: repr(match.group(0)), doc.get("request_path", "") ) path, _, params = request_path.partition("?") kwargs.update({ "path": path, "params": params, }) navigation_objects.append(NavigationEventAudit(**kwargs)) elif doc["doc_type"] == "AccessAudit": access_couch_ids.append(doc['_id']) kwargs.update(_pick(doc, ["http_accept", "trace_id"])) access_type = doc.get('access_type') kwargs.update({ "access_type": ACCESS_LOOKUP.get(doc.get("access_type")), "path": doc.get("path_info"), }) if access_type == "logout": kwargs.update({"path": "accounts/logout"}) access_objects.append(AccessAudit(**kwargs)) else: assert doc["doc_type"] in IGNORED_DOC_TYPES, doc other_doc_type_count += 1 res_obj = get_unsaved_events( navigation_objects, access_objects, nav_couch_ids, access_couch_ids, batch_start_key, end_key ) res_obj.update({ "break_query": records_returned < batch_size or not next_start_key, "next_start_key": next_start_key, "last_doc_id": processed_doc_id, "other_doc_type_count": other_doc_type_count }) return res_obj