audit-logs: allow showing individual entries for audit log.

This commit is contained in:
Marcin Kuzminski 2017-09-21 16:47:10 +02:00
parent cb071ab951
commit b95e835ebb
8 changed files with 159 additions and 5 deletions

View file

@ -33,6 +33,10 @@ def admin_routes(config):
name='admin_audit_logs',
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+}')

View file

@ -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)

View file

@ -133,9 +133,6 @@ def _store_log(action_name, action_data, user_id, username, user_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, user_data=None, ip_addr=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')

View file

@ -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])

View file

@ -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?

View file

@ -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']);

View file

@ -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:
&middot; ${h.branding(c.rhodecode_name)}
%endif
</%def>
<%def name="breadcrumbs_links()">
${_('Audit long entry')} ${c.audit_log_entry.entry_id}
</%def>
<%def name="menu_bar_nav()">
${self.menu_items(active='admin')}
</%def>
<%def name="main()">
<div class="box">
<!-- box / title -->
<div class="title">
${self.breadcrumbs()}
</div>
<!-- end box / title -->
<div class="table">
<div id="user_log">
<table class="rctable audit-log">
<tr>
<td>
${_('User')}:
</td>
<td>
%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
</td>
</tr>
<tr>
<td>
${_('Date')}:
</td>
<td>
${h.format_date(c.audit_log_entry.action_date)}
</td>
</tr>
<tr>
<td>
${_('IP')}:
</td>
<td>
${c.audit_log_entry.user_ip}
</td>
</tr>
<tr>
<td>
${_('Action')}:
</td>
<td>
% 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
<div class="journal_action_params">
% if c.audit_log_entry.version == c.audit_log_entry.VERSION_1:
${h.literal(h.action_parser(l)[1]())}
% endif
</div>
</td>
</tr>
<tr>
<td>
${_('Action Data')}:
</td>
<td class="td-journalaction">
% if c.audit_log_entry.version == c.audit_log_entry.VERSION_2:
<div>
<pre>${h.json.dumps(c.audit_log_entry.action_data, indent=4, sort_keys=True)}</pre>
</div>
% else:
<pre title="${_('data not available for v1 entries type')}">-</pre>
% endif
</td>
</tr>
<tr>
<td>
${_('Repository')}:
</td>
<td class="td-journalaction">
%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
</td>
</tr>
</table>
</div>
</div>
</div>
<script>
$('#j_filter').autoGrowInput();
</script>
</%def>

View file

@ -3,6 +3,7 @@
%if c.audit_logs:
<table class="rctable admin_log">
<tr>
<th>${_('Uid')}</th>
<th>${_('Username')}</th>
<th>${_('Action')}</th>
<th>${_('Action Data')}</th>
@ -13,6 +14,9 @@
%for cnt,l in enumerate(c.audit_logs):
<tr class="parity${cnt%2}">
<td class="td-col">
<a href="${h.route_path('admin_audit_log_entry', audit_log_id=l.entry_id)}">${l.entry_id}</a>
</td>
<td class="td-user">
%if l.user is not None:
${base.gravatar_with_user(l.user.email)}