audit-logs: added audit-logs on user actions.

- adding/deleting ips
- adding/deleting emails
- edit/create
This commit is contained in:
Marcin Kuzminski 2017-06-14 14:38:59 +02:00
parent 7618001212
commit 4bd5c08e87
3 changed files with 82 additions and 56 deletions

View file

@ -31,15 +31,17 @@ from pylons.controllers.util import redirect
from pylons.i18n.translation import _
from rhodecode.authentication.plugins import auth_rhodecode
from rhodecode.lib import helpers as h
from rhodecode.lib import auth
from rhodecode.lib import audit_logger
from rhodecode.lib.auth import (
LoginRequired, HasPermissionAllDecorator, AuthUser)
from rhodecode.lib.base import BaseController, render
from rhodecode.lib.exceptions import (
DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
UserOwnsUserGroupsException, UserCreationError)
from rhodecode.lib import helpers as h
from rhodecode.lib import auth
from rhodecode.lib.auth import (
LoginRequired, HasPermissionAllDecorator, AuthUser, generate_auth_token)
from rhodecode.lib.base import BaseController, render
from rhodecode.model.auth_token import AuthTokenModel
from rhodecode.lib.utils2 import safe_int, AttributeDict
from rhodecode.model.db import (
PullRequestReviewers, User, UserEmailMap, UserIpMap, RepoGroup)
@ -49,8 +51,6 @@ from rhodecode.model.repo_group import RepoGroupModel
from rhodecode.model.user import UserModel
from rhodecode.model.meta import Session
from rhodecode.model.permission import PermissionModel
from rhodecode.lib.utils import action_logger
from rhodecode.lib.utils2 import datetime_to_time, safe_int, AttributeDict
log = logging.getLogger(__name__)
@ -88,7 +88,6 @@ class UsersController(BaseController):
@HasPermissionAllDecorator('hg.admin')
@auth.CSRFRequired()
def create(self):
"""POST /users: Create a new item"""
c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
user_model = UserModel()
user_form = UserForm()()
@ -96,9 +95,12 @@ class UsersController(BaseController):
form_result = user_form.to_python(dict(request.POST))
user = user_model.create(form_result)
Session().flush()
creation_data = user.get_api_data()
username = form_result['username']
action_logger(c.rhodecode_user, 'admin_created_user:%s' % username,
None, self.ip_addr, self.sa)
audit_logger.store_web(
'user.create', action_data={'data': creation_data},
user=c.rhodecode_user)
user_link = h.link_to(h.escape(username),
url('edit_user',
@ -125,8 +127,6 @@ class UsersController(BaseController):
@HasPermissionAllDecorator('hg.admin')
def new(self):
"""GET /users/new: Form to create a new item"""
# url('new_user')
c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
self._get_personal_repo_group_template_vars()
return render('admin/users/user_add.mako')
@ -134,13 +134,7 @@ class UsersController(BaseController):
@HasPermissionAllDecorator('hg.admin')
@auth.CSRFRequired()
def update(self, user_id):
"""PUT /users/user_id: Update an existing item"""
# Forms posted to this method should contain a hidden field:
# <input type="hidden" name="_method" value="PUT" />
# Or using helpers:
# h.form(url('update_user', user_id=ID),
# method='put')
# url('user', user_id=ID)
user_id = safe_int(user_id)
c.user = User.get_or_404(user_id)
c.active = 'profile'
@ -152,6 +146,7 @@ class UsersController(BaseController):
old_data={'user_id': user_id,
'email': c.user.email})()
form_result = {}
old_values = c.user.get_api_data()
try:
form_result = _form.to_python(dict(request.POST))
skip_attrs = ['extern_type', 'extern_name']
@ -160,12 +155,15 @@ class UsersController(BaseController):
# forbid updating username for external accounts
skip_attrs.append('username')
UserModel().update_user(user_id, skip_attrs=skip_attrs, **form_result)
usr = form_result['username']
action_logger(c.rhodecode_user, 'admin_updated_user:%s' % usr,
None, self.ip_addr, self.sa)
h.flash(_('User updated successfully'), category='success')
UserModel().update_user(
user_id, skip_attrs=skip_attrs, **form_result)
audit_logger.store_web(
'user.edit', action_data={'old_data': old_values},
user=c.rhodecode_user)
Session().commit()
h.flash(_('User updated successfully'), category='success')
except formencode.Invalid as errors:
defaults = errors.value
e = errors.error_dict or {}
@ -188,13 +186,6 @@ class UsersController(BaseController):
@HasPermissionAllDecorator('hg.admin')
@auth.CSRFRequired()
def delete(self, user_id):
"""DELETE /users/user_id: Delete an existing item"""
# Forms posted to this method should contain a hidden field:
# <input type="hidden" name="_method" value="DELETE" />
# Or using helpers:
# h.form(url('delete_user', user_id=ID),
# method='delete')
# url('user', user_id=ID)
user_id = safe_int(user_id)
c.user = User.get_or_404(user_id)
@ -249,10 +240,16 @@ class UsersController(BaseController):
_('Deleted %s user groups') % len(_user_groups),
category='success')
old_values = c.user.get_api_data()
try:
UserModel().delete(c.user, handle_repos=handle_repos,
handle_repo_groups=handle_repo_groups,
handle_user_groups=handle_user_groups)
audit_logger.store_web(
'user.delete', action_data={'old_data': old_values},
user=c.rhodecode_user)
Session().commit()
set_handle_flash_repos()
set_handle_flash_repo_groups()
@ -272,19 +269,25 @@ class UsersController(BaseController):
def reset_password(self, user_id):
"""
toggle reset password flag for this user
:param user_id:
"""
user_id = safe_int(user_id)
c.user = User.get_or_404(user_id)
try:
old_value = c.user.user_data.get('force_password_change')
c.user.update_userdata(force_password_change=not old_value)
Session().commit()
if old_value:
msg = _('Force password change disabled for user')
audit_logger.store_web(
'user.edit.password_reset.disabled',
user=c.rhodecode_user)
else:
msg = _('Force password change enabled for user')
audit_logger.store_web(
'user.edit.password_reset.enabled',
user=c.rhodecode_user)
Session().commit()
h.flash(msg, category='success')
except Exception:
log.exception("Exception during password reset for user")
@ -298,8 +301,6 @@ class UsersController(BaseController):
def create_personal_repo_group(self, user_id):
"""
Create personal repository group for this user
:param user_id:
"""
from rhodecode.model.repo_group import RepoGroupModel
@ -428,8 +429,6 @@ class UsersController(BaseController):
@HasPermissionAllDecorator('hg.admin')
@auth.CSRFRequired()
def update_global_perms(self, user_id):
"""PUT /users_perm/user_id: Update an existing item"""
# url('user_perm', user_id=ID, method='put')
user_id = safe_int(user_id)
user = User.get_or_404(user_id)
c.active = 'global_perms'
@ -456,11 +455,13 @@ class UsersController(BaseController):
PermissionModel().update_user_permissions(form_result)
# TODO(marcink): implement global permissions
# audit_log.store_web('user.edit.permissions')
Session().commit()
h.flash(_('User global permissions updated successfully'),
category='success')
Session().commit()
except formencode.Invalid as errors:
defaults = errors.value
c.user = user
@ -512,16 +513,18 @@ class UsersController(BaseController):
@HasPermissionAllDecorator('hg.admin')
@auth.CSRFRequired()
def add_email(self, user_id):
"""POST /user_emails:Add an existing item"""
# url('user_emails', user_id=ID, method='put')
user_id = safe_int(user_id)
c.user = User.get_or_404(user_id)
email = request.POST.get('new_email')
user_model = UserModel()
user_data = c.user.get_api_data()
try:
user_model.add_extra_email(user_id, email)
audit_logger.store_web(
'user.edit.email.add',
action_data={'email': email, 'user': user_data},
user=c.rhodecode_user)
Session().commit()
h.flash(_("Added new email address `%s` for user account") % email,
category='success')
@ -537,13 +540,18 @@ class UsersController(BaseController):
@HasPermissionAllDecorator('hg.admin')
@auth.CSRFRequired()
def delete_email(self, user_id):
"""DELETE /user_emails_delete/user_id: Delete an existing item"""
# url('user_emails_delete', user_id=ID, method='delete')
user_id = safe_int(user_id)
c.user = User.get_or_404(user_id)
email_id = request.POST.get('del_email_id')
user_model = UserModel()
email = UserEmailMap.query().get(email_id).email
user_data = c.user.get_api_data()
user_model.delete_extra_email(user_id, email_id)
audit_logger.store_web(
'user.edit.email.delete',
action_data={'email': email, 'user': user_data},
user=c.rhodecode_user)
Session().commit()
h.flash(_("Removed email address from user account"), category='success')
return redirect(url('edit_user_emails', user_id=user_id))
@ -574,9 +582,6 @@ class UsersController(BaseController):
@HasPermissionAllDecorator('hg.admin')
@auth.CSRFRequired()
def add_ip(self, user_id):
"""POST /user_ips:Add an existing item"""
# url('user_ips', user_id=ID, method='put')
user_id = safe_int(user_id)
c.user = User.get_or_404(user_id)
user_model = UserModel()
@ -590,9 +595,14 @@ class UsersController(BaseController):
desc = request.POST.get('description')
added = []
user_data = c.user.get_api_data()
for ip in ip_list:
try:
user_model.add_extra_ip(user_id, ip, desc)
audit_logger.store_web(
'user.edit.ip.add',
action_data={'ip': ip, 'user': user_data},
user=c.rhodecode_user)
Session().commit()
added.append(ip)
except formencode.Invalid as error:
@ -613,14 +623,18 @@ class UsersController(BaseController):
@HasPermissionAllDecorator('hg.admin')
@auth.CSRFRequired()
def delete_ip(self, user_id):
"""DELETE /user_ips_delete/user_id: Delete an existing item"""
# url('user_ips_delete', user_id=ID, method='delete')
user_id = safe_int(user_id)
c.user = User.get_or_404(user_id)
ip_id = request.POST.get('del_ip_id')
user_model = UserModel()
ip = UserIpMap.query().get(ip_id).ip_addr
user_data = c.user.get_api_data()
user_model.delete_extra_ip(user_id, ip_id)
audit_logger.store_web(
'user.edit.ip.delete',
action_data={'ip': ip, 'user': user_data},
user=c.rhodecode_user)
Session().commit()
h.flash(_("Removed ip address from user whitelist"), category='success')

View file

@ -27,7 +27,7 @@ from rhodecode.model.db import User, UserLog, Repository
log = logging.getLogger(__name__)
# action as key, and expected action_data as value
ACTIONS = {
'user.login.success': {},
'user.login.failure': {},
@ -38,6 +38,19 @@ ACTIONS = {
'repo.create': {},
'repo.edit': {},
'user.create': {'data': {}},
'user.delete': {'old_data': {}},
'user.edit': {'old_data': {}},
'user.edit.permissions': {},
'user.edit.ip.add': {},
'user.edit.ip.delete': {},
'user.edit.token.add': {},
'user.edit.token.delete': {},
'user.edit.email.add': {},
'user.edit.email.delete': {},
'user.edit.password_reset.enabled': {},
'user.edit.password_reset.disabled': {},
'repo.edit.permissions': {},
'repo.delete': {},
'repo.commit.strip': {},
@ -117,9 +130,8 @@ def store_api(*args, **kwargs):
return store(*args, **kwargs)
def store(
action, user, action_data=None, user_data=None, ip_addr=None,
repo=None, sa_session=None, commit=False):
def store(action, user, action_data=None, user_data=None, ip_addr=None,
repo=None, sa_session=None, commit=False):
"""
Audit logger for various actions made by users, typically this
results in a call such::

View file

@ -767,7 +767,7 @@ class UserModel(BaseModel):
"""
user = self._get_user(user)
obj = UserEmailMap.query().get(email_id)
if obj:
if obj and obj.user_id == user.user_id:
self.sa.delete(obj)
def parse_ip_range(self, ip_range):
@ -824,7 +824,7 @@ class UserModel(BaseModel):
"""
user = self._get_user(user)
obj = UserIpMap.query().get(ip_id)
if obj:
if obj and obj.user_id == user.user_id:
self.sa.delete(obj)
def get_accounts_in_creation_order(self, current_user=None):