# HG changeset patch # User Marcin Kuzminski # Date 2017-09-21 14:47:10 # Node ID 56ff7f716a8cb80817e694909091f7bdb0927ee6 # Parent 52576dab2c6545d3e354e3bd75fd4649c78d4540 audit-logs: allow showing individual entries for audit log. diff --git a/rhodecode/apps/admin/__init__.py b/rhodecode/apps/admin/__init__.py --- a/rhodecode/apps/admin/__init__.py +++ b/rhodecode/apps/admin/__init__.py @@ -34,6 +34,10 @@ def admin_routes(config): pattern='/audit_logs') config.add_route( + name='admin_audit_log_entry', + pattern='/audit_logs/{audit_log_id}') + + config.add_route( name='pull_requests_global_0', # backward compat pattern='/pull_requests/{pull_request_id:\d+}') config.add_route( diff --git a/rhodecode/apps/admin/views/audit_logs.py b/rhodecode/apps/admin/views/audit_logs.py --- a/rhodecode/apps/admin/views/audit_logs.py +++ b/rhodecode/apps/admin/views/audit_logs.py @@ -20,6 +20,7 @@ import logging +from pyramid.httpexceptions import HTTPNotFound from pyramid.view import view_config from rhodecode.apps._base import BaseAppView @@ -70,3 +71,21 @@ class AdminAuditLogsView(BaseAppView): c.audit_logs = Page(users_log, page=p, items_per_page=10, url=url_generator) return self._get_template_context(c) + + @LoginRequired() + @HasPermissionAllDecorator('hg.admin') + @view_config( + route_name='admin_audit_log_entry', request_method='GET', + renderer='rhodecode:templates/admin/admin_audit_log_entry.mako') + def admin_audit_log_entry(self): + c = self.load_default_context() + audit_log_id = self.request.matchdict['audit_log_id'] + + c.audit_log_entry = UserLog.query()\ + .options(joinedload(UserLog.user))\ + .options(joinedload(UserLog.repository))\ + .filter(UserLog.user_log_id == audit_log_id).scalar() + if not c.audit_log_entry: + raise HTTPNotFound() + + return self._get_template_context(c) 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 @@ -133,9 +133,6 @@ def _store_log(action_name, action_data, user_log.action_date = datetime.datetime.now() - log.info('AUDIT: Logging action: `%s` by user:id:%s[%s] ip:%s', - action_name, user_id, username, ip_address) - return user_log @@ -239,19 +236,27 @@ def store(action, user, action_data=None repository_id = getattr( Repository.get_by_repo_name(repository_name), 'repo_id', None) + action_name = safe_unicode(action) + ip_address = safe_unicode(ip_addr) + user_log = _store_log( - action_name=safe_unicode(action), + action_name=action_name, action_data=action_data or {}, user_id=user_id, username=username, user_data=user_data or {}, - ip_address=safe_unicode(ip_addr), + 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', + entry_id, action_name, user_id, username, ip_address) + except Exception: log.exception('AUDIT: failed to store audit log') diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py --- a/rhodecode/model/db.py +++ b/rhodecode/model/db.py @@ -1252,6 +1252,10 @@ class UserLog(Base, BaseModel): 'action': self.action, } + @hybrid_property + def entry_id(self): + return self.user_log_id + @property def action_as_day(self): return datetime.date(*self.action_date.timetuple()[:3]) diff --git a/rhodecode/public/css/tables.less b/rhodecode/public/css/tables.less --- a/rhodecode/public/css/tables.less +++ b/rhodecode/public/css/tables.less @@ -250,6 +250,11 @@ table.dataTable { } } } +.rctable.audit-log { + td { + vertical-align: top; + } +} // TRUNCATING // TODO: lisaq: should this possibly be moved out of tables.less? diff --git a/rhodecode/public/js/rhodecode/routes.js b/rhodecode/public/js/rhodecode/routes.js --- a/rhodecode/public/js/rhodecode/routes.js +++ b/rhodecode/public/js/rhodecode/routes.js @@ -38,6 +38,7 @@ function registerRCRoutes() { pyroutes.register('ops_error_test_legacy', '/_admin/error_test', []); pyroutes.register('admin_home', '/_admin', []); pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []); + pyroutes.register('admin_audit_log_entry', '/_admin/audit_logs/%(audit_log_id)s', ['audit_log_id']); pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']); pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']); pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']); diff --git a/rhodecode/templates/admin/admin_audit_log_entry.mako b/rhodecode/templates/admin/admin_audit_log_entry.mako new file mode 100644 --- /dev/null +++ b/rhodecode/templates/admin/admin_audit_log_entry.mako @@ -0,0 +1,112 @@ +## -*- coding: utf-8 -*- +<%inherit file="/base/base.mako"/> +<%namespace name="base" file="/base/base.mako"/> + +<%def name="title()"> + ${_('Admin audit log entry')} + %if c.rhodecode_name: + · ${h.branding(c.rhodecode_name)} + %endif + + +<%def name="breadcrumbs_links()"> + ${_('Audit long entry')} ${c.audit_log_entry.entry_id} + + +<%def name="menu_bar_nav()"> + ${self.menu_items(active='admin')} + +<%def name="main()"> +
+ +
+ ${self.breadcrumbs()} +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ${_('User')}: + + %if c.audit_log_entry.user is not None: + ${base.gravatar_with_user(c.audit_log_entry.user.email)} + %else: + ${c.audit_log_entry.username} + %endif +
+ ${_('Date')}: + + ${h.format_date(c.audit_log_entry.action_date)} +
+ ${_('IP')}: + + ${c.audit_log_entry.user_ip} +
+ ${_('Action')}: + + % if c.audit_log_entry.version == c.audit_log_entry.VERSION_1: + ${h.action_parser(l)[0]()} + % else: + ${h.literal(c.audit_log_entry.action)} + % endif + +
+ % if c.audit_log_entry.version == c.audit_log_entry.VERSION_1: + ${h.literal(h.action_parser(l)[1]())} + % endif +
+
+ ${_('Action Data')}: + + % if c.audit_log_entry.version == c.audit_log_entry.VERSION_2: +
+
${h.json.dumps(c.audit_log_entry.action_data, indent=4, sort_keys=True)}
+
+ % else: +
-
+ % endif +
+ ${_('Repository')}: + + %if c.audit_log_entry.repository is not None: + ${h.link_to(c.audit_log_entry.repository.repo_name, h.route_path('repo_summary',repo_name=c.audit_log_entry.repository.repo_name))} + %else: + ${c.audit_log_entry.repository_name or '-'} + %endif +
+ +
+
+
+ + + diff --git a/rhodecode/templates/admin/admin_log_base.mako b/rhodecode/templates/admin/admin_log_base.mako --- a/rhodecode/templates/admin/admin_log_base.mako +++ b/rhodecode/templates/admin/admin_log_base.mako @@ -3,6 +3,7 @@ %if c.audit_logs: + @@ -13,6 +14,9 @@ %for cnt,l in enumerate(c.audit_logs): +
${_('Uid')} ${_('Username')} ${_('Action')} ${_('Action Data')}
+ ${l.entry_id} + %if l.user is not None: ${base.gravatar_with_user(l.user.email)}