# HG changeset patch # User Marcin Kuzminski # Date 2018-07-31 20:52:44 # Node ID a5198975653dbdd1b9cc7144ed5bf8810649d2f8 # Parent c363a5c703c49c2d979b38aa82c1f9b5a52221d9 security: update lastactivity when on audit logs. - The previous update on each call was to expensive and created lot of DB locks in high traffic env diff --git a/rhodecode/api/__init__.py b/rhodecode/api/__init__.py --- a/rhodecode/api/__init__.py +++ b/rhodecode/api/__init__.py @@ -208,6 +208,7 @@ def request_view(request): # register our auth-user request.rpc_user = auth_u + request.environ['rc_auth_user_id'] = auth_u.user_id # now check if token is valid for API auth_token = request.rpc_api_key diff --git a/rhodecode/api/tests/test_update_user.py b/rhodecode/api/tests/test_update_user.py --- a/rhodecode/api/tests/test_update_user.py +++ b/rhodecode/api/tests/test_update_user.py @@ -76,6 +76,8 @@ class TestUpdateUser(object): 'user': ret } expected = ret + expected['user']['last_activity'] = response.json['result']['user'][ + 'last_activity'] assert_ok(id_, expected, given=response.body) def test_api_update_user_by_user_id(self): @@ -91,6 +93,8 @@ class TestUpdateUser(object): 'user': ret } expected = ret + expected['user']['last_activity'] = response.json['result']['user'][ + 'last_activity'] assert_ok(id_, expected, given=response.body) def test_api_update_user_default_user(self): diff --git a/rhodecode/config/middleware.py b/rhodecode/config/middleware.py --- a/rhodecode/config/middleware.py +++ b/rhodecode/config/middleware.py @@ -25,13 +25,13 @@ import collections import tempfile from paste.gzipper import make_gzip_middleware +import pyramid.events from pyramid.wsgi import wsgiapp from pyramid.authorization import ACLAuthorizationPolicy from pyramid.config import Configurator from pyramid.settings import asbool, aslist from pyramid.httpexceptions import ( HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound) -from pyramid.events import ApplicationCreated from pyramid.renderers import render_to_response from rhodecode.model import meta @@ -39,6 +39,7 @@ from rhodecode.config import patches from rhodecode.config import utils as config_utils from rhodecode.config.environment import load_pyramid_environment +import rhodecode.events from rhodecode.lib.middleware.vcs import VCSMiddleware from rhodecode.lib.request import Request from rhodecode.lib.vcs import VCSCommunicationError @@ -268,11 +269,14 @@ def includeme(config): settings['default_locale_name'] = settings.get('lang', 'en') # Add subscribers. - config.add_subscriber(inject_app_settings, ApplicationCreated) - config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated) - config.add_subscriber(write_metadata_if_needed, ApplicationCreated) - config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated) - + config.add_subscriber(inject_app_settings, + pyramid.events.ApplicationCreated) + config.add_subscriber(scan_repositories_if_enabled, + pyramid.events.ApplicationCreated) + config.add_subscriber(write_metadata_if_needed, + pyramid.events.ApplicationCreated) + config.add_subscriber(write_js_routes_if_enabled, + pyramid.events.ApplicationCreated) # request custom methods config.add_request_method( diff --git a/rhodecode/events/__init__.py b/rhodecode/events/__init__.py --- a/rhodecode/events/__init__.py +++ b/rhodecode/events/__init__.py @@ -32,9 +32,11 @@ def trigger(event, registry=None): # For the first step we are using pyramids thread locals here. If the # event mechanism works out as a good solution we should think about # passing the registry as an argument to get rid of it. + event_name = event.__class__ + log.debug('event %s sent for execution', event_name) registry = registry or get_current_registry() registry.notify(event) - log.debug('event %s triggered using registry %s', event.__class__, registry) + log.debug('event %s triggered using registry %s', event_name, registry) # Send the events to integrations directly from rhodecode.integrations import integrations_event_handler diff --git a/rhodecode/lib/audit_logger.py b/rhodecode/lib/audit_logger.py --- a/rhodecode/lib/audit_logger.py +++ b/rhodecode/lib/audit_logger.py @@ -241,20 +241,24 @@ def store(action, user, action_data=None action_name = safe_unicode(action) ip_address = safe_unicode(ip_addr) - user_log = _store_log( - action_name=action_name, - action_data=action_data or {}, - user_id=user_id, - username=username, - user_data=user_data or {}, - ip_address=ip_address, - repository_id=repository_id, - repository_name=repository_name - ) + with sa_session.no_autoflush: + update_user_last_activity(sa_session, user_id) - sa_session.add(user_log) - if commit: - sa_session.commit() + user_log = _store_log( + action_name=action_name, + action_data=action_data or {}, + user_id=user_id, + username=username, + user_data=user_data or {}, + ip_address=ip_address, + repository_id=repository_id, + repository_name=repository_name + ) + + sa_session.add(user_log) + + if commit: + sa_session.commit() entry_id = user_log.entry_id or '' log.info('AUDIT[%s]: Logging action: `%s` by user:id:%s[%s] ip:%s', @@ -262,3 +266,14 @@ def store(action, user, action_data=None except Exception: log.exception('AUDIT: failed to store audit log') + + +def update_user_last_activity(sa_session, user_id): + _last_activity = datetime.datetime.now() + try: + sa_session.query(User).filter(User.user_id == user_id).update( + {"last_activity": _last_activity}) + log.debug( + 'updated user `%s` last activity to:%s', user_id, _last_activity) + except Exception: + log.exception("Failed last activity update") diff --git a/rhodecode/lib/middleware/simplevcs.py b/rhodecode/lib/middleware/simplevcs.py --- a/rhodecode/lib/middleware/simplevcs.py +++ b/rhodecode/lib/middleware/simplevcs.py @@ -521,6 +521,7 @@ class SimpleVCS(object): if not self.valid_and_active_user(user): return HTTPForbidden()(environ, start_response) username = user.username + user_id = user.user_id # check user attributes for password change flag user_obj = user @@ -536,6 +537,7 @@ class SimpleVCS(object): plugin, plugin_cache_active, cache_ttl) if not perm: return HTTPForbidden()(environ, start_response) + environ['rc_auth_user_id'] = user_id # extras are injected into UI object and later available # in hooks executed by RhodeCode diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py --- a/rhodecode/model/db.py +++ b/rhodecode/model/db.py @@ -931,12 +931,6 @@ class User(Base, BaseModel): Session().add(self) log.debug('updated user %s lastlogin', self.username) - def update_lastactivity(self): - """Update user lastactivity""" - self.last_activity = datetime.datetime.now() - Session().add(self) - log.debug('updated user `%s` last activity', self.username) - def update_password(self, new_password): from rhodecode.lib.auth import get_crypt_password diff --git a/rhodecode/subscribers.py b/rhodecode/subscribers.py --- a/rhodecode/subscribers.py +++ b/rhodecode/subscribers.py @@ -100,6 +100,7 @@ def add_request_user_context(event): auth_user = get_auth_user(request) request.user = auth_user request.environ['rc_auth_user'] = auth_user + request.environ['rc_auth_user_id'] = auth_user.user_id request.environ['rc_req_id'] = req_id