From 3ea2504e8cb90c4bc10f2b5d6559539d7f1d07e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 8 Dec 2017 14:46:01 +0100 Subject: [PATCH] Log more information in Sentry --- pillar/__init__.py | 9 +++++---- pillar/sentry_extra.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 pillar/sentry_extra.py diff --git a/pillar/__init__.py b/pillar/__init__.py index d98cc71e..0ab8ab6c 100644 --- a/pillar/__init__.py +++ b/pillar/__init__.py @@ -21,7 +21,6 @@ from flask_babel import Babel, gettext as _ from flask.templating import TemplateNotFound import pymongo.collection import pymongo.database -from raven.contrib.flask import Sentry from werkzeug.local import LocalProxy @@ -42,6 +41,7 @@ import pillar.web.jinja from . import api from . import web from . import auth +from . import sentry_extra import pillar.api.organizations empty_settings = { @@ -106,7 +106,7 @@ class PillarServer(BlinkerCompatibleEve): self._config_tempdirs() self._config_git() - self.sentry: typing.Optional[Sentry] = None + self.sentry: typing.Optional[sentry_extra.PillarSentry] = None self._config_sentry() self._config_google_cloud_storage() @@ -207,8 +207,9 @@ class PillarServer(BlinkerCompatibleEve): self.sentry = None return - self.sentry = Sentry(self, logging=True, level=logging.WARNING, - logging_exclusions=('werkzeug',)) + self.sentry = sentry_extra.PillarSentry( + self, logging=True, level=logging.WARNING, + logging_exclusions=('werkzeug',)) # bugsnag.before_notify(bugsnag_extra.add_pillar_request_to_notification) # got_request_exception.connect(self.__notify_bugsnag) diff --git a/pillar/sentry_extra.py b/pillar/sentry_extra.py new file mode 100644 index 00000000..01b033fe --- /dev/null +++ b/pillar/sentry_extra.py @@ -0,0 +1,42 @@ +from raven.contrib.flask import Sentry + +from .auth import current_user +from . import current_app + + +class PillarSentry(Sentry): + """Flask Sentry with Pillar support. + + This is mostly for obtaining user information on API calls, + and for preventing the auth tokens to be logged as user ID. + """ + + def get_user_info(self, request): + user_info = super().get_user_info(request) + + # The auth token is stored as the user ID in the flask_login + # current_user object, so don't send that to Sentry. + user_info.pop('id', None) + + if len(user_info) > 1: + # Sentry always includes the IP address, but when they find a + # logged-in user, they add more info. In that case we're done. + return user_info + + # This is pretty much a copy-paste from Sentry, except that it uses + # pillar.auth.current_user instead. + try: + if not current_user.is_authenticated: + return user_info + except AttributeError: + # HACK: catch the attribute error thrown by flask-login is not attached + # > current_user = LocalProxy(lambda: _request_ctx_stack.top.user) + # E AttributeError: 'RequestContext' object has no attribute 'user' + return user_info + + if 'SENTRY_USER_ATTRS' in current_app.config: + for attr in current_app.config['SENTRY_USER_ATTRS']: + if hasattr(current_user, attr): + user_info[attr] = getattr(current_user, attr) + + return user_info