audit-logs: implemented full audit logs across application.

- Fixes #5321
- Api+web actions
- To be extended while we develop new features.
This commit is contained in:
Marcin Kuzminski 2017-06-21 12:03:14 +02:00
parent a9ea42785b
commit 25436a7365
16 changed files with 141 additions and 79 deletions

View file

@ -917,12 +917,13 @@ def update_repo(
ref_choices, _labels = ScmModel().get_repo_landing_revs(repo=repo)
old_values = repo.get_api_data()
schema = repo_schema.RepoSchema().bind(
repo_type_options=rhodecode.BACKENDS.keys(),
repo_ref_options=ref_choices,
# user caller
user=apiuser,
old_values=repo.get_api_data())
old_values=old_values)
try:
schema_data = schema.deserialize(dict(
# we save old value, users cannot change type
@ -967,6 +968,9 @@ def update_repo(
try:
RepoModel().update(repo, **validated_updates)
audit_logger.store_api(
'repo.edit', action_data={'old_data': old_values},
user=apiuser, repo=repo)
Session().commit()
return {
'msg': 'updated repo ID:%s %s' % (repo.repo_id, repo.repo_name),
@ -1173,15 +1177,14 @@ def delete_repo(request, apiuser, repoid, forks=Optional('')):
'Cannot delete `%s` it still contains attached forks' %
(repo.repo_name,)
)
repo_data = repo.get_api_data()
old_data = repo.get_api_data()
RepoModel().delete(repo, forks=forks)
repo = audit_logger.RepoWrap(repo_id=None,
repo_name=repo.repo_name)
audit_logger.store_api(
action='repo.delete',
action_data={'data': repo_data},
'repo.delete', action_data={'old_data': old_data},
user=apiuser, repo=repo)
ScmModel().mark_for_invalidation(repo_name, delete=True)
@ -1472,7 +1475,7 @@ def comment_commit(
rc_config = SettingsModel().get_all_settings()
renderer = rc_config.get('rhodecode_markup_renderer', 'rst')
status_change_label = ChangesetStatus.get_status_lbl(status)
comm = CommentsModel().create(
comment = CommentsModel().create(
message, repo, user, commit_id=commit_id,
status_change=status_change_label,
status_change_type=status,
@ -1484,7 +1487,7 @@ def comment_commit(
# also do a status change
try:
ChangesetStatusModel().set_status(
repo, status, user, comm, revision=commit_id,
repo, status, user, comment, revision=commit_id,
dont_allow_on_closed_pull_request=True
)
except StatusChangeOnClosedPullRequestError:
@ -1498,7 +1501,7 @@ def comment_commit(
return {
'msg': (
'Commented on commit `%s` for repository `%s`' % (
comm.revision, repo.repo_name)),
comment.revision, repo.repo_name)),
'status_change': status,
'success': True,
}
@ -1879,6 +1882,11 @@ def strip(request, apiuser, repoid, revision, branch):
try:
ScmModel().strip(repo, revision, branch)
audit_logger.store_api(
'repo.commit.strip', action_data={'commit_id': revision},
repo=repo,
user=apiuser, commit=True)
return {
'msg': 'Stripped commit %s from repo `%s`' % (
revision, repo.repo_name),

View file

@ -27,6 +27,7 @@ from rhodecode.api.utils import (
has_superadmin_permission, Optional, OAttr, get_user_or_error,
get_repo_group_or_error, get_perm_or_error, get_user_group_or_error,
get_origin, validate_repo_group_permissions, validate_set_owner_permissions)
from rhodecode.lib import audit_logger
from rhodecode.lib.auth import (
HasRepoGroupPermissionAnyApi, HasUserGroupPermissionAnyApi)
from rhodecode.model.db import Session
@ -222,6 +223,13 @@ def create_repo_group(
group_name=validated_group_name,
group_description=schema_data['repo_group_name'],
copy_permissions=schema_data['repo_group_copy_permissions'])
Session().flush()
repo_group_data = repo_group.get_api_data()
audit_logger.store_api(
'repo_group.create', action_data={'data': repo_group_data},
user=apiuser)
Session().commit()
return {
'msg': 'Created new repo group `%s`' % validated_group_name,
@ -310,8 +318,13 @@ def update_repo_group(
enable_locking=schema_data['repo_group_enable_locking'],
)
old_data = repo_group.get_api_data()
try:
RepoGroupModel().update(repo_group, validated_updates)
audit_logger.store_api(
'repo_group.edit', action_data={'old_data': old_data},
user=apiuser)
Session().commit()
return {
'msg': 'updated repository group ID:%s %s' % (
@ -365,8 +378,12 @@ def delete_repo_group(request, apiuser, repogroupid):
validate_repo_group_permissions(
apiuser, repogroupid, repo_group, ('group.admin',))
old_data = repo_group.get_api_data()
try:
RepoGroupModel().delete(repo_group)
audit_logger.store_api(
'repo_group.delete', action_data={'old_data': old_data},
user=apiuser)
Session().commit()
return {
'msg': 'deleted repo group ID:%s %s' %

View file

@ -23,6 +23,7 @@ import logging
from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden
from rhodecode.api.utils import (
Optional, OAttr, has_superadmin_permission, get_user_or_error, store_update)
from rhodecode.lib import audit_logger
from rhodecode.lib.auth import AuthUser, PasswordGenerator
from rhodecode.lib.exceptions import DefaultUserException
from rhodecode.lib.utils2 import safe_int, str2bool
@ -251,6 +252,12 @@ def create_user(request, apiuser, username, email, password=Optional(''),
force_password_change=Optional.extract(force_password_change),
create_repo_group=create_repo_group
)
Session().flush()
creation_data = user.get_api_data()
audit_logger.store_api(
'user.create', action_data={'data': creation_data},
user=apiuser)
Session().commit()
return {
'msg': 'created new user `%s`' % username,
@ -326,7 +333,7 @@ def update_user(request, apiuser, userid, username=Optional(None),
raise JSONRPCForbidden()
user = get_user_or_error(userid)
old_data = user.get_api_data()
# only non optional arguments will be stored in updates
updates = {}
@ -343,6 +350,9 @@ def update_user(request, apiuser, userid, username=Optional(None),
store_update(updates, extern_type, 'extern_type')
user = UserModel().update_user(user, **updates)
audit_logger.store_api(
'user.edit', action_data={'old_data': old_data},
user=apiuser)
Session().commit()
return {
'msg': 'updated user ID:%s %s' % (user.user_id, user.username),
@ -405,9 +415,13 @@ def delete_user(request, apiuser, userid):
raise JSONRPCForbidden()
user = get_user_or_error(userid)
old_data = user.get_api_data()
try:
UserModel().delete(userid)
audit_logger.store_api(
'user.delete', action_data={'old_data': old_data},
user=apiuser)
Session().commit()
return {
'msg': 'deleted user ID:%s %s' % (user.user_id, user.username),

View file

@ -24,6 +24,7 @@ from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden
from rhodecode.api.utils import (
Optional, OAttr, store_update, has_superadmin_permission, get_origin,
get_user_or_error, get_user_group_or_error, get_perm_or_error)
from rhodecode.lib import audit_logger
from rhodecode.lib.auth import HasUserGroupPermissionAnyApi, HasPermissionAnyApi
from rhodecode.lib.exceptions import UserGroupAssignedException
from rhodecode.model.db import Session
@ -217,13 +218,18 @@ def create_user_group(
owner = get_user_or_error(owner)
active = Optional.extract(active)
description = Optional.extract(description)
ug = UserGroupModel().create(
user_group = UserGroupModel().create(
name=group_name, description=description, owner=owner,
active=active)
Session().flush()
creation_data = user_group.get_api_data()
audit_logger.store_api(
'user_group.create', action_data={'data': creation_data},
user=apiuser)
Session().commit()
return {
'msg': 'created new user group `%s`' % group_name,
'user_group': ug.get_api_data()
'user_group': creation_data
}
except Exception:
log.exception("Error occurred during creation of user group")
@ -291,6 +297,7 @@ def update_user_group(request, apiuser, usergroupid, group_name=Optional(''),
if not isinstance(owner, Optional):
owner = get_user_or_error(owner)
old_data = user_group.get_api_data()
updates = {}
store_update(updates, group_name, 'users_group_name')
store_update(updates, description, 'user_group_description')
@ -298,6 +305,9 @@ def update_user_group(request, apiuser, usergroupid, group_name=Optional(''),
store_update(updates, active, 'users_group_active')
try:
UserGroupModel().update(user_group, updates)
audit_logger.store_api(
'user_group.edit', action_data={'old_data': old_data},
user=apiuser)
Session().commit()
return {
'msg': 'updated user group ID:%s %s' % (
@ -359,8 +369,12 @@ def delete_user_group(request, apiuser, usergroupid):
raise JSONRPCError(
'user group `%s` does not exist' % (usergroupid,))
old_data = user_group.get_api_data()
try:
UserGroupModel().delete(user_group)
audit_logger.store_api(
'user_group.delete', action_data={'old_data': old_data},
user=apiuser)
Session().commit()
return {
'msg': 'deleted user group ID:%s %s' % (
@ -438,6 +452,12 @@ def add_user_to_user_group(request, apiuser, usergroupid, userid):
user.username, user_group.users_group_name
)
msg = msg if success else 'User is already in that group'
if success:
user_data = user.get_api_data()
audit_logger.store_api(
'user_group.edit.member.add', action_data={'user': user_data},
user=apiuser)
Session().commit()
return {
@ -501,6 +521,12 @@ def remove_user_from_user_group(request, apiuser, usergroupid, userid):
user.username, user_group.users_group_name
)
msg = msg if success else "User wasn't in group"
if success:
user_data = user.get_api_data()
audit_logger.store_api(
'user_group.edit.member.delete', action_data={'user': user_data},
user=apiuser)
Session().commit()
return {'success': success, 'msg': msg}
except Exception:

View file

@ -209,8 +209,8 @@ class AdminUsersView(BaseAppView, DataGridAppView):
self.maybe_attach_token_scope(token)
audit_logger.store_web(
action='user.edit.token.add',
action_data={'data': {'token': token_data, 'user': user_data}},
'user.edit.token.add', action_data={
'data': {'token': token_data, 'user': user_data}},
user=self._rhodecode_user, )
Session().commit()
@ -239,8 +239,8 @@ class AdminUsersView(BaseAppView, DataGridAppView):
AuthTokenModel().delete(del_auth_token, c.user.user_id)
audit_logger.store_web(
action='user.edit.token.delete',
action_data={'data': {'token': token_data, 'user': user_data}},
'user.edit.token.delete', action_data={
'data': {'token': token_data, 'user': user_data}},
user=self._rhodecode_user,)
Session().commit()
h.flash(_("Auth token successfully deleted"), category='success')
@ -284,8 +284,7 @@ class AdminUsersView(BaseAppView, DataGridAppView):
try:
UserModel().add_extra_email(c.user.user_id, email)
audit_logger.store_web(
'user.edit.email.add',
action_data={'email': email, 'user': user_data},
'user.edit.email.add', action_data={'email': email, 'user': user_data},
user=self._rhodecode_user)
Session().commit()
h.flash(_("Added new email address `%s` for user account") % email,
@ -318,8 +317,7 @@ class AdminUsersView(BaseAppView, DataGridAppView):
user_data = c.user.get_api_data()
user_model.delete_extra_email(c.user.user_id, email_id)
audit_logger.store_web(
'user.edit.email.delete',
action_data={'email': email, 'user': user_data},
'user.edit.email.delete', action_data={'email': email, 'user': user_data},
user=self._rhodecode_user)
Session().commit()
h.flash(_("Removed email address from user account"),
@ -379,8 +377,7 @@ class AdminUsersView(BaseAppView, DataGridAppView):
try:
user_model.add_extra_ip(c.user.user_id, ip, desc)
audit_logger.store_web(
'user.edit.ip.add',
action_data={'ip': ip, 'user': user_data},
'user.edit.ip.add', action_data={'ip': ip, 'user': user_data},
user=self._rhodecode_user)
Session().commit()
added.append(ip)
@ -420,8 +417,7 @@ class AdminUsersView(BaseAppView, DataGridAppView):
ip = UserIpMap.query().get(ip_id).ip_addr
user_model.delete_extra_ip(c.user.user_id, ip_id)
audit_logger.store_web(
'user.edit.ip.delete',
action_data={'ip': ip, 'user': user_data},
'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
user=self._rhodecode_user)
Session().commit()
h.flash(_("Removed ip address from user whitelist"), category='success')

View file

@ -173,7 +173,7 @@ class LoginView(BaseAppView):
ip_addr=self.request.remote_addr)
action_data = {'user_agent': self.request.user_agent}
audit_logger.store_web(
action='user.login.success', action_data=action_data,
'user.login.success', action_data=action_data,
user=audit_user, commit=True)
raise HTTPFound(c.came_from, headers=headers)
@ -192,7 +192,7 @@ class LoginView(BaseAppView):
ip_addr=self.request.remote_addr)
action_data = {'user_agent': self.request.user_agent}
audit_logger.store_web(
action='user.login.failure', action_data=action_data,
'user.login.failure', action_data=action_data,
user=audit_user, commit=True)
return render_ctx
@ -212,7 +212,7 @@ class LoginView(BaseAppView):
action_data = {'user_agent': self.request.user_agent}
audit_logger.store_web(
action='user.logout', action_data=action_data,
'user.logout', action_data=action_data,
user=auth_user, commit=True)
self.session.delete()
return HTTPFound(h.route_path('home'))
@ -365,8 +365,7 @@ class LoginView(BaseAppView):
action_data = {'email': user_email,
'user_agent': self.request.user_agent}
audit_logger.store_web(
action='user.password.reset_request',
action_data=action_data,
'user.password.reset_request', action_data=action_data,
user=self._rhodecode_user, commit=True)
return HTTPFound(self.request.route_path('reset_password'))

View file

@ -183,8 +183,8 @@ class MyAccountView(BaseAppView):
self.maybe_attach_token_scope(token)
audit_logger.store_web(
action='user.edit.token.add',
action_data={'data': {'token': token_data, 'user': 'self'}},
'user.edit.token.add', action_data={
'data': {'token': token_data, 'user': 'self'}},
user=self._rhodecode_user, )
Session().commit()
@ -208,8 +208,8 @@ class MyAccountView(BaseAppView):
AuthTokenModel().delete(del_auth_token, c.user.user_id)
audit_logger.store_web(
action='user.edit.token.delete',
action_data={'data': {'token': token_data, 'user': 'self'}},
'user.edit.token.delete', action_data={
'data': {'token': token_data, 'user': 'self'}},
user=self._rhodecode_user,)
Session().commit()
h.flash(_("Auth token successfully deleted"), category='success')
@ -245,8 +245,8 @@ class MyAccountView(BaseAppView):
try:
UserModel().add_extra_email(c.user.user_id, email)
audit_logger.store_web(
action='user.edit.email.add',
action_data={'data': {'email': email, 'user': 'self'}},
'user.edit.email.add', action_data={
'data': {'email': email, 'user': 'self'}},
user=self._rhodecode_user,)
Session().commit()
@ -274,8 +274,8 @@ class MyAccountView(BaseAppView):
email = UserEmailMap.get_or_404(del_email_id, pyramid_exc=True).email
UserModel().delete_extra_email(c.user.user_id, del_email_id)
audit_logger.store_web(
action='user.edit.email.delete',
action_data={'data': {'email': email, 'user': 'self'}},
'user.edit.email.delete', action_data={
'data': {'email': email, 'user': 'self'}},
user=self._rhodecode_user,)
Session().commit()
h.flash(_("Email successfully deleted"),

View file

@ -95,14 +95,13 @@ class RepoSettingsView(RepoAppView):
handle_forks = 'delete'
h.flash(_('Deleted %s forks') % _forks, category='success')
repo_data = self.db_repo.get_api_data()
old_data = self.db_repo.get_api_data()
RepoModel().delete(self.db_repo, forks=handle_forks)
repo = audit_logger.RepoWrap(repo_id=None,
repo_name=self.db_repo.repo_name)
audit_logger.store_web(
action='repo.delete',
action_data={'data': repo_data},
'repo.delete', action_data={'old_data': old_data},
user=self._rhodecode_user, repo=repo)
ScmModel().mark_for_invalidation(self.db_repo_name, delete=True)

View file

@ -106,10 +106,8 @@ class StripView(RepoAppView):
data[commit['rev']] = True
audit_logger.store_web(
action='repo.commit.strip',
action_data={'commit_id': commit['rev']},
repo=self.db_repo,
user=self._rhodecode_user, commit=True)
'repo.commit.strip', action_data={'commit_id': commit['rev']},
repo=self.db_repo, user=self._rhodecode_user, commit=True)
except Exception as e:
data[commit['rev']] = False

View file

@ -185,14 +185,16 @@ class RepoGroupsController(BaseController):
owner=owner.user_id,
copy_permissions=form_result['group_copy_permissions']
)
Session().commit()
repo_group_data = repo_group.get_api_data()
_new_group_name = form_result['group_name_full']
Session().flush()
repo_group_data = repo_group.get_api_data()
audit_logger.store_web(
action='repo_group.create',
action_data={'data': repo_group_data},
user=c.rhodecode_user, commit=True)
'repo_group.create', action_data={'data': repo_group_data},
user=c.rhodecode_user)
Session().commit()
_new_group_name = form_result['group_name_full']
repo_group_url = h.link_to(
_new_group_name,
@ -304,8 +306,7 @@ class RepoGroupsController(BaseController):
RepoGroupModel().delete(group_name)
audit_logger.store_web(
'repo_group.delete',
action_data={'old_data': old_values},
'repo_group.delete', action_data={'old_data': old_values},
user=c.rhodecode_user)
Session().commit()

View file

@ -205,13 +205,16 @@ class UserGroupsController(BaseController):
pstruct = peppercorn.parse(request.POST.items())
form_result['users_group_members'] = pstruct['user_group_members']
UserGroupModel().update(c.user_group, form_result)
user_group, added_members, removed_members = \
UserGroupModel().update(c.user_group, form_result)
updated_user_group = form_result['users_group_name']
audit_logger.store_web(
'user_group.edit', action_data={'old_data': old_values},
user=c.rhodecode_user)
# TODO(marcink): use added/removed to set user_group.edit.member.add
h.flash(_('Updated user group %s') % updated_user_group,
category='success')
Session().commit()

View file

@ -815,11 +815,11 @@ class FilesController(BaseRepoController):
# store download action
audit_logger.store_web(
action='repo.archive.download',
action_data={'user_agent': request.user_agent,
'archive_name': archive_name,
'archive_spec': fname,
'archive_cached': use_cached_archive},
'repo.archive.download', action_data={
'user_agent': request.user_agent,
'archive_name': archive_name,
'archive_spec': fname,
'archive_cached': use_cached_archive},
user=c.rhodecode_user,
repo=dbrepo,
commit=True

View file

@ -53,8 +53,8 @@ ACTIONS_V1 = {
'user_group.delete': {'old_data': {}},
'user_group.edit': {'old_data': {}},
'user_group.edit.permissions': {},
'user_group.edit.member.add': {},
'user_group.edit.member.delete': {},
'user_group.edit.member.add': {'user': {}},
'user_group.edit.member.delete': {'user': {}},
'repo.create': {'data': {}},
'repo.fork': {'data': {}},
@ -76,8 +76,8 @@ ACTIONS_V1 = {
'repo.pull_request.reviewer.add': '',
'repo.pull_request.reviewer.delete': '',
'repo.commit.comment.create': '',
'repo.commit.comment.delete': '',
'repo.commit.comment.create': {'data': {}},
'repo.commit.comment.delete': {'data': {}},
'repo.commit.vote': '',
'repo_group.create': {'data': {}},
@ -164,34 +164,32 @@ def store(action, user, action_data=None, user_data=None, ip_addr=None,
from rhodecode.lib import audit_logger
audit_logger.store(
action='repo.edit', user=self._rhodecode_user)
'repo.edit', user=self._rhodecode_user)
audit_logger.store(
action='repo.delete', action_data={'data': repo_data},
'repo.delete', action_data={'data': repo_data},
user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'))
# repo action
audit_logger.store(
action='repo.delete',
'repo.delete',
user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'),
repo=audit_logger.RepoWrap(repo_name='some-repo'))
# repo action, when we know and have the repository object already
audit_logger.store(
action='repo.delete',
action_data={'source': audit_logger.SOURCE_WEB, },
'repo.delete', action_data={'source': audit_logger.SOURCE_WEB, },
user=self._rhodecode_user,
repo=repo_object)
# alternative wrapper to the above
audit_logger.store_web(
action='repo.delete',
action_data={},
'repo.delete', action_data={},
user=self._rhodecode_user,
repo=repo_object)
# without an user ?
audit_logger.store(
action='user.login.failure',
'user.login.failure',
user=audit_logger.UserWrap(
username=self.request.params.get('username'),
ip_addr=self.request.remote_addr))

View file

@ -179,9 +179,8 @@ def create_repo(form_data, cur_user):
repo_id = repo.repo_id
repo_data = repo.get_api_data()
audit_logger.store_web(
action='repo.create',
action_data={'data': repo_data},
audit_logger.store(
'repo.create', action_data={'data': repo_data},
user=cur_user,
repo=audit_logger.RepoWrap(repo_name=repo_name, repo_id=repo_id))
@ -274,9 +273,8 @@ def create_repo_fork(form_data, cur_user):
repo_id = repo.repo_id
repo_data = repo.get_api_data()
audit_logger.store_web(
action='repo.fork',
action_data={'data': repo_data},
audit_logger.store(
'repo.fork', action_data={'data': repo_data},
user=cur_user,
repo=audit_logger.RepoWrap(repo_name=repo_name, repo_id=repo_id))

View file

@ -158,7 +158,7 @@ def post_pull(extras):
ip_addr=extras.ip)
repo = audit_logger.RepoWrap(repo_name=extras.repository)
audit_logger.store(
action='user.pull', action_data={
'user.pull', action_data={
'user_agent': extras.user_agent},
user=audit_user, repo=repo, commit=True)
@ -199,7 +199,7 @@ def post_push(extras):
username=extras.username, ip_addr=extras.ip)
repo = audit_logger.RepoWrap(repo_name=extras.repository)
audit_logger.store(
action='user.push', action_data={
'user.push', action_data={
'user_agent': extras.user_agent,
'commit_ids': commit_ids[:10000]},
user=audit_user, repo=repo, commit=True)

View file

@ -175,7 +175,7 @@ class UserGroupModel(BaseModel):
user_id for user_id in current_members_ids
if user_id not in user_id_list]
return (added_members, deleted_members)
return added_members, deleted_members
def _set_users_as_members(self, user_group, user_ids):
user_group.members = []
@ -191,6 +191,7 @@ class UserGroupModel(BaseModel):
self._set_users_as_members(user_group, user_ids)
self._log_user_changes('added to', user_group, added)
self._log_user_changes('removed from', user_group, removed)
return added, removed
def _clean_members_data(self, members_data):
if not members_data:
@ -225,12 +226,16 @@ class UserGroupModel(BaseModel):
user_group.user = owner
added_user_ids = []
removed_user_ids = []
if 'users_group_members' in form_data:
members_id_list = self._clean_members_data(
form_data['users_group_members'])
self._update_members_from_user_ids(user_group, members_id_list)
added_user_ids, removed_user_ids = \
self._update_members_from_user_ids(user_group, members_id_list)
self.sa.add(user_group)
return user_group, added_user_ids, removed_user_ids
def delete(self, user_group, force=False):
"""