diff --git a/rhodecode/controllers/admin/users.py b/rhodecode/controllers/admin/users.py
--- a/rhodecode/controllers/admin/users.py
+++ b/rhodecode/controllers/admin/users.py
@@ -31,15 +31,17 @@ from pylons.controllers.util import redi
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 R
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:
- #
- # 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)
+ 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')
- Session().commit()
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:
- #
- # 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')
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
@@ -27,7 +27,7 @@ from rhodecode.model.db import User, Use
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::
diff --git a/rhodecode/model/user.py b/rhodecode/model/user.py
--- a/rhodecode/model/user.py
+++ b/rhodecode/model/user.py
@@ -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):