middleware: Log user.id/realm.string_id instead of _email.

This commit is contained in:
Mateusz Mandera
2020-03-09 11:39:20 +01:00
committed by Tim Abbott
parent 700123a30b
commit 0255ca9b6a
10 changed files with 30 additions and 26 deletions

View File

@@ -197,7 +197,7 @@ def validate_api_key(request: HttpRequest, role: Optional[str],
if get_subdomain(request) != Realm.SUBDOMAIN_FOR_ROOT_DOMAIN:
raise JsonableError(_("Invalid subdomain for push notifications bouncer"))
request.user = remote_server
request._email = "zulip-server:" + role
request._requestor_for_logs = "zulip-server:" + role
remote_server.rate_limits = ""
# Skip updating UserActivity, since remote_server isn't actually a UserProfile object.
process_client(request, remote_server, skip_update_user_activity=True)
@@ -208,7 +208,7 @@ def validate_api_key(request: HttpRequest, role: Optional[str],
raise JsonableError(_("This API is not available to incoming webhook bots."))
request.user = user_profile
request._email = user_profile.delivery_email
request._requestor_for_logs = user_profile.format_requestor_for_logs()
process_client(request, user_profile, client_name=client_name)
return user_profile
@@ -409,7 +409,7 @@ def do_login(request: HttpRequest, user_profile: UserProfile) -> None:
and also adds helpful data needed by our server logs.
"""
django_login(request, user_profile)
request._email = user_profile.delivery_email
request._requestor_for_logs = user_profile.format_requestor_for_logs()
process_client(request, user_profile, is_browser_view=True)
if settings.TWO_FACTOR_AUTHENTICATION_ENABLED:
# Login with two factor authentication as well.
@@ -425,7 +425,7 @@ def log_view_func(view_func: ViewFuncT) -> ViewFuncT:
def add_logging_data(view_func: ViewFuncT) -> ViewFuncT:
@wraps(view_func)
def _wrapped_view_func(request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
request._email = request.user.delivery_email
request._requestor_for_logs = request.user.format_requestor_for_logs()
process_client(request, request.user, is_browser_view=True,
query=view_func.__name__)
return rate_limit()(view_func)(request, *args, **kwargs)
@@ -649,7 +649,7 @@ def authenticate_log_and_execute_json(request: HttpRequest,
process_client(request, user_profile, is_browser_view=True,
query=view_func.__name__)
request._email = user_profile.delivery_email
request._requestor_for_logs = user_profile.format_requestor_for_logs()
return limited_view_func(request, user_profile, *args, **kwargs)
# Checks if the request is a POST request and that the user is logged
@@ -712,7 +712,7 @@ def internal_notify_view(is_tornado_view: bool) -> Callable[[ViewFuncT], ViewFun
raise RuntimeError('Tornado notify view called with no Tornado handler')
if not is_tornado_view and is_tornado_request:
raise RuntimeError('Django notify view called with Tornado handler')
request._email = "internal"
request._requestor_for_logs = "internal"
return view_func(request, *args, **kwargs)
return _wrapped_func_arguments
return _wrapped_view_func

View File

@@ -280,7 +280,7 @@ class HostRequestMock:
self.method = ''
self.body = ''
self.content_type = ''
self._email = ''
self._requestor_for_logs = ''
def get_host(self) -> str:
return self.host

View File

@@ -105,8 +105,9 @@ statsd_blacklisted_requests = [
'upload_file', 'realm_activity', 'user_activity'
]
def write_log_line(log_data: MutableMapping[str, Any], path: str, method: str, remote_ip: str, email: str,
client_name: str, status_code: int=200, error_content: Optional[AnyStr]=None,
def write_log_line(log_data: MutableMapping[str, Any], path: str, method: str, remote_ip: str,
requestor_for_logs: str, client_name: str, status_code: int=200,
error_content: Optional[AnyStr]=None,
error_content_iter: Optional[Iterable[AnyStr]]=None) -> None:
assert error_content is None or error_content_iter is None
if error_content is not None:
@@ -199,7 +200,7 @@ def write_log_line(log_data: MutableMapping[str, Any], path: str, method: str, r
extra_request_data = " %s" % (log_data['extra'],)
else:
extra_request_data = ""
logger_client = "(%s via %s)" % (email, client_name)
logger_client = "(%s via %s)" % (requestor_for_logs, client_name)
logger_timing = ('%5s%s%s%s%s%s %s' %
(format_timedelta(time_delta), optional_orig_delta,
remote_cache_output, bugdown_output,
@@ -214,7 +215,7 @@ def write_log_line(log_data: MutableMapping[str, Any], path: str, method: str, r
if (is_slow_query(time_delta, path)):
queue_json_publish("slow_queries", dict(
query="%s (%s)" % (logger_line, email)))
query="%s (%s)" % (logger_line, requestor_for_logs)))
if settings.PROFILE_ALL_REQUESTS:
log_data["prof"].disable()
@@ -233,7 +234,7 @@ def write_log_line(log_data: MutableMapping[str, Any], path: str, method: str, r
error_data = repr(b''.join(error_content_list))
if len(error_data) > 200:
error_data = u"[content more than 200 characters]"
logger.info('status=%3d, data=%s, uid=%s' % (status_code, error_data, email))
logger.info('status=%3d, data=%s, uid=%s' % (status_code, error_data, requestor_for_logs))
class LogRequests(MiddlewareMixin):
# We primarily are doing logging using the process_view hook, but
@@ -284,11 +285,11 @@ class LogRequests(MiddlewareMixin):
if remote_ip is None:
remote_ip = request.META['REMOTE_ADDR']
# Get the requestor's email address and client, if available.
# Get the requestor's identifier and client, if available.
try:
email = request._email
requestor_for_logs = request._requestor_for_logs
except Exception:
email = "unauth"
requestor_for_logs = "unauth"
try:
client = request.client.name
except Exception:
@@ -302,7 +303,7 @@ class LogRequests(MiddlewareMixin):
content_iter = None
write_log_line(request._log_data, request.path, request.method,
remote_ip, email, client, status_code=response.status_code,
remote_ip, requestor_for_logs, client, status_code=response.status_code,
error_content=content, error_content_iter=content_iter)
return response

View File

@@ -1176,6 +1176,9 @@ class UserProfile(AbstractBaseUser, PermissionsMixin):
else:
return -1
def format_requestor_for_logs(self) -> str:
return "{}/{}".format(self.realm.string_id, self.id)
def set_password(self, password: Optional[str]) -> None:
if password is None:
self.set_unusable_password()

View File

@@ -379,7 +379,7 @@ body:
self.assertTrue(rate_limit_mock.called)
# Verify decorator set the magic _email field used by some of our back end logging.
self.assertEqual(request._email, webhook_bot_email)
self.assertEqual(request._requestor_for_logs, webhook_bot.format_requestor_for_logs())
# Verify the main purpose of the decorator, which is that it passed in the
# user_profile to my_webhook, allowing it return the correct
@@ -1365,7 +1365,7 @@ class TestInternalNotifyView(TestCase):
with self.settings(SHARED_SECRET=secret):
self.assertTrue(authenticate_notify(req))
self.assertEqual(self.internal_notify(False, req), self.BORING_RESULT)
self.assertEqual(req._email, 'internal')
self.assertEqual(req._requestor_for_logs, 'internal')
with self.assertRaises(RuntimeError):
self.internal_notify(True, req)
@@ -1374,7 +1374,7 @@ class TestInternalNotifyView(TestCase):
with self.settings(SHARED_SECRET=secret):
self.assertTrue(authenticate_notify(req))
self.assertEqual(self.internal_notify(True, req), self.BORING_RESULT)
self.assertEqual(req._email, 'internal')
self.assertEqual(req._requestor_for_logs, 'internal')
with self.assertRaises(RuntimeError):
self.internal_notify(False, req)

View File

@@ -40,7 +40,7 @@ class SlowQueryTest(ZulipTestCase):
self.log_data['time_started'] = time.time() - self.SLOW_QUERY_TIME
write_log_line(self.log_data, path='/socket/open', method='SOCKET',
remote_ip='123.456.789.012', email='unknown', client_name='?')
remote_ip='123.456.789.012', requestor_for_logs='unknown', client_name='?')
last_message = self.get_last_message()
self.assertEqual(last_message.sender.email, "error-bot@zulip.com")
self.assertIn("logs", str(last_message.recipient))
@@ -56,7 +56,7 @@ class SlowQueryTest(ZulipTestCase):
mock_logging_info: Mock) -> None:
self.log_data['time_started'] = time.time() - self.SLOW_QUERY_TIME
write_log_line(self.log_data, path='/socket/open', method='SOCKET',
remote_ip='123.456.789.012', email='unknown', client_name='?')
remote_ip='123.456.789.012', requestor_for_logs='unknown', client_name='?')
mock_internal_send_stream_message.assert_not_called()
class OpenGraphTest(ZulipTestCase):

View File

@@ -96,7 +96,7 @@ class WorkerTest(ZulipTestCase):
worker.setup()
# `write_log_line` is where we publish slow queries to the queue.
with patch('zerver.middleware.is_slow_query', return_value=True):
write_log_line(log_data=dict(test='data'), email='test@zulip.com',
write_log_line(log_data=dict(test='data'), requestor_for_logs='test@zulip.com',
remote_ip='127.0.0.1', client_name='website', path='/test/',
method='GET')
worker.start()

View File

@@ -218,7 +218,7 @@ class AsyncDjangoHandler(tornado.web.RequestHandler, base.BaseHandler):
request._log_data = old_request._log_data
if hasattr(request, "_rate_limit"):
request._rate_limit = old_request._rate_limit
request._email = old_request._email
request._requestor_for_logs = old_request._requestor_for_logs
request.user = old_request.user
request.client = old_request.client

View File

@@ -37,7 +37,7 @@ def cleanup_event_queue(request: HttpRequest, user_profile: UserProfile,
def get_events_internal(request: HttpRequest,
user_profile_id: int = REQ(validator=check_int)) -> HttpResponse:
user_profile = get_user_profile_by_id(user_profile_id)
request._email = user_profile.delivery_email
request._requestor_for_logs = user_profile.format_requestor_for_logs()
process_client(request, user_profile, client_name="internal")
return get_events_backend(request, user_profile)

View File

@@ -309,7 +309,7 @@ def finish_mobile_flow(request: HttpRequest, user_profile: UserProfile, otp: str
# Mark this request as having a logged-in user for our server logs.
process_client(request, user_profile)
request._email = user_profile.delivery_email
request._requestor_for_logs = user_profile.format_requestor_for_logs()
return response
@@ -920,7 +920,7 @@ def api_fetch_api_key(request: HttpRequest, username: str=REQ(), password: str=R
# Mark this request as having a logged-in user for our server logs.
process_client(request, user_profile)
request._email = user_profile.delivery_email
request._requestor_for_logs = user_profile.format_requestor_for_logs()
api_key = get_api_key(user_profile)
return json_success({"api_key": api_key, "email": user_profile.delivery_email})