diff --git a/rhodecode/controllers/admin/user_groups.py b/rhodecode/controllers/admin/user_groups.py
new file mode 100644
--- /dev/null
+++ b/rhodecode/controllers/admin/user_groups.py
@@ -0,0 +1,461 @@
+# -*- coding: utf-8 -*-
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+"""
+rhodecode.controllers.admin.users_groups
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+User Groups crud controller for pylons
+
+:created_on: Jan 25, 2011
+:author: marcink
+:copyright: (c) 2013 RhodeCode GmbH.
+:license: GPLv3, see LICENSE for more details.
+"""
+
+import logging
+import traceback
+import formencode
+
+from formencode import htmlfill
+from pylons import request, session, tmpl_context as c, url, config
+from pylons.controllers.util import abort, redirect
+from pylons.i18n.translation import _
+
+from sqlalchemy.orm import joinedload
+from sqlalchemy.sql.expression import func
+from webob.exc import HTTPInternalServerError
+
+import rhodecode
+from rhodecode.lib import helpers as h
+from rhodecode.lib.exceptions import UserGroupsAssignedException,\
+ RepoGroupAssignmentError
+from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
+from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator,\
+ HasUserGroupPermissionAnyDecorator, HasPermissionAnyDecorator
+from rhodecode.lib.base import BaseController, render
+from rhodecode.model.scm import UserGroupList
+from rhodecode.model.user_group import UserGroupModel
+from rhodecode.model.repo import RepoModel
+from rhodecode.model.db import User, UserGroup, UserGroupToPerm,\
+ UserGroupRepoToPerm, UserGroupRepoGroupToPerm
+from rhodecode.model.forms import UserGroupForm, UserGroupPermsForm,\
+ CustomDefaultPermissionsForm
+from rhodecode.model.meta import Session
+from rhodecode.lib.utils import action_logger
+from rhodecode.lib.compat import json
+
+log = logging.getLogger(__name__)
+
+
+class UserGroupsController(BaseController):
+ """REST Controller styled on the Atom Publishing Protocol"""
+
+ @LoginRequired()
+ def __before__(self):
+ super(UserGroupsController, self).__before__()
+ c.available_permissions = config['available_permissions']
+
+ def __load_data(self, user_group_id):
+ c.group_members_obj = sorted((x.user for x in c.user_group.members),
+ key=lambda u: u.username.lower())
+
+ c.group_members = [(x.user_id, x.username) for x in c.group_members_obj]
+ c.available_members = sorted(((x.user_id, x.username) for x in
+ User.query().all()),
+ key=lambda u: u[1].lower())
+
+ def __load_defaults(self, user_group_id):
+ """
+ Load defaults settings for edit, and update
+
+ :param user_group_id:
+ """
+ user_group = UserGroup.get_or_404(user_group_id)
+ data = user_group.get_dict()
+ return data
+
+ def index(self, format='html'):
+ """GET /users_groups: All items in the collection"""
+ # url('users_groups')
+ _list = UserGroup.query()\
+ .order_by(func.lower(UserGroup.users_group_name))\
+ .all()
+ group_iter = UserGroupList(_list, perm_set=['usergroup.admin'])
+ user_groups_data = []
+ total_records = len(group_iter)
+ _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
+ template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
+
+ user_group_name = lambda user_group_id, user_group_name: (
+ template.get_def("user_group_name")
+ .render(user_group_id, user_group_name, _=_, h=h, c=c)
+ )
+ user_group_actions = lambda user_group_id, user_group_name: (
+ template.get_def("user_group_actions")
+ .render(user_group_id, user_group_name, _=_, h=h, c=c)
+ )
+ for user_gr in group_iter:
+
+ user_groups_data.append({
+ "raw_name": user_gr.users_group_name,
+ "group_name": user_group_name(user_gr.users_group_id,
+ user_gr.users_group_name),
+ "desc": user_gr.user_group_description,
+ "members": len(user_gr.members),
+ "active": h.boolicon(user_gr.users_group_active),
+ "owner": h.person(user_gr.user.username),
+ "action": user_group_actions(user_gr.users_group_id, user_gr.users_group_name)
+ })
+
+ c.data = json.dumps({
+ "totalRecords": total_records,
+ "startIndex": 0,
+ "sort": None,
+ "dir": "asc",
+ "records": user_groups_data
+ })
+
+ return render('admin/user_groups/user_groups.html')
+
+ @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
+ def create(self):
+ """POST /users_groups: Create a new item"""
+ # url('users_groups')
+
+ users_group_form = UserGroupForm()()
+ try:
+ form_result = users_group_form.to_python(dict(request.POST))
+ UserGroupModel().create(name=form_result['users_group_name'],
+ description=form_result['user_group_description'],
+ owner=self.rhodecode_user.user_id,
+ active=form_result['users_group_active'])
+
+ gr = form_result['users_group_name']
+ action_logger(self.rhodecode_user,
+ 'admin_created_users_group:%s' % gr,
+ None, self.ip_addr, self.sa)
+ h.flash(_('Created user group %s') % gr, category='success')
+ Session().commit()
+ except formencode.Invalid, errors:
+ return htmlfill.render(
+ render('admin/user_groups/user_group_add.html'),
+ defaults=errors.value,
+ errors=errors.error_dict or {},
+ prefix_error=False,
+ encoding="UTF-8")
+ except Exception:
+ log.error(traceback.format_exc())
+ h.flash(_('Error occurred during creation of user group %s') \
+ % request.POST.get('users_group_name'), category='error')
+
+ return redirect(url('users_groups'))
+
+ @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
+ def new(self, format='html'):
+ """GET /user_groups/new: Form to create a new item"""
+ # url('new_users_group')
+ return render('admin/user_groups/user_group_add.html')
+
+ @HasUserGroupPermissionAnyDecorator('usergroup.admin')
+ def update(self, id):
+ """PUT /user_groups/id: Update an existing item"""
+ # Forms posted to this method should contain a hidden field:
+ #
+ # Or using helpers:
+ # h.form(url('users_group', id=ID),
+ # method='put')
+ # url('users_group', id=ID)
+
+ c.user_group = UserGroup.get_or_404(id)
+ c.active = 'settings'
+ self.__load_data(id)
+
+ available_members = [safe_unicode(x[0]) for x in c.available_members]
+
+ users_group_form = UserGroupForm(edit=True,
+ old_data=c.user_group.get_dict(),
+ available_members=available_members)()
+
+ try:
+ form_result = users_group_form.to_python(request.POST)
+ UserGroupModel().update(c.user_group, form_result)
+ gr = form_result['users_group_name']
+ action_logger(self.rhodecode_user,
+ 'admin_updated_users_group:%s' % gr,
+ None, self.ip_addr, self.sa)
+ h.flash(_('Updated user group %s') % gr, category='success')
+ Session().commit()
+ except formencode.Invalid, errors:
+ ug_model = UserGroupModel()
+ defaults = errors.value
+ e = errors.error_dict or {}
+ defaults.update({
+ 'create_repo_perm': ug_model.has_perm(id,
+ 'hg.create.repository'),
+ 'fork_repo_perm': ug_model.has_perm(id,
+ 'hg.fork.repository'),
+ '_method': 'put'
+ })
+
+ return htmlfill.render(
+ render('admin/user_groups/user_group_edit.html'),
+ defaults=defaults,
+ errors=e,
+ prefix_error=False,
+ encoding="UTF-8")
+ except Exception:
+ log.error(traceback.format_exc())
+ h.flash(_('Error occurred during update of user group %s') \
+ % request.POST.get('users_group_name'), category='error')
+
+ return redirect(url('edit_users_group', id=id))
+
+ @HasUserGroupPermissionAnyDecorator('usergroup.admin')
+ def delete(self, id):
+ """DELETE /user_groups/id: Delete an existing item"""
+ # Forms posted to this method should contain a hidden field:
+ #
+ # Or using helpers:
+ # h.form(url('users_group', id=ID),
+ # method='delete')
+ # url('users_group', id=ID)
+ usr_gr = UserGroup.get_or_404(id)
+ try:
+ UserGroupModel().delete(usr_gr)
+ Session().commit()
+ h.flash(_('Successfully deleted user group'), category='success')
+ except UserGroupsAssignedException, e:
+ h.flash(e, category='error')
+ except Exception:
+ log.error(traceback.format_exc())
+ h.flash(_('An error occurred during deletion of user group'),
+ category='error')
+ return redirect(url('users_groups'))
+
+ def show(self, id, format='html'):
+ """GET /user_groups/id: Show a specific item"""
+ # url('users_group', id=ID)
+
+ @HasUserGroupPermissionAnyDecorator('usergroup.admin')
+ def edit(self, id, format='html'):
+ """GET /user_groups/id/edit: Form to edit an existing item"""
+ # url('edit_users_group', id=ID)
+
+ c.user_group = UserGroup.get_or_404(id)
+ c.active = 'settings'
+ self.__load_data(id)
+
+ defaults = self.__load_defaults(id)
+
+ return htmlfill.render(
+ render('admin/user_groups/user_group_edit.html'),
+ defaults=defaults,
+ encoding="UTF-8",
+ force_defaults=False
+ )
+
+ @HasUserGroupPermissionAnyDecorator('usergroup.admin')
+ def edit_perms(self, id):
+ c.user_group = UserGroup.get_or_404(id)
+ c.active = 'perms'
+
+ repo_model = RepoModel()
+ c.users_array = repo_model.get_users_js()
+ c.user_groups_array = repo_model.get_user_groups_js()
+
+ defaults = {}
+ # fill user group users
+ for p in c.user_group.user_user_group_to_perm:
+ defaults.update({'u_perm_%s' % p.user.username:
+ p.permission.permission_name})
+
+ for p in c.user_group.user_group_user_group_to_perm:
+ defaults.update({'g_perm_%s' % p.user_group.users_group_name:
+ p.permission.permission_name})
+
+ return htmlfill.render(
+ render('admin/user_groups/user_group_edit.html'),
+ defaults=defaults,
+ encoding="UTF-8",
+ force_defaults=False
+ )
+
+ @HasUserGroupPermissionAnyDecorator('usergroup.admin')
+ def update_perms(self, id):
+ """
+ grant permission for given usergroup
+
+ :param id:
+ """
+ user_group = UserGroup.get_or_404(id)
+ form = UserGroupPermsForm()().to_python(request.POST)
+
+ # set the permissions !
+ try:
+ UserGroupModel()._update_permissions(user_group, form['perms_new'],
+ form['perms_updates'])
+ except RepoGroupAssignmentError:
+ h.flash(_('Target group cannot be the same'), category='error')
+ return redirect(url('edit_user_group_perms', id=id))
+ #TODO: implement this
+ #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
+ # repo_name, self.ip_addr, self.sa)
+ Session().commit()
+ h.flash(_('User Group permissions updated'), category='success')
+ return redirect(url('edit_user_group_perms', id=id))
+
+ @HasUserGroupPermissionAnyDecorator('usergroup.admin')
+ def delete_perms(self, id):
+ """
+ DELETE an existing repository group permission user
+
+ :param group_name:
+ """
+ try:
+ obj_type = request.POST.get('obj_type')
+ obj_id = None
+ if obj_type == 'user':
+ obj_id = safe_int(request.POST.get('user_id'))
+ elif obj_type == 'user_group':
+ obj_id = safe_int(request.POST.get('user_group_id'))
+
+ if not c.rhodecode_user.is_admin:
+ if obj_type == 'user' and c.rhodecode_user.user_id == obj_id:
+ msg = _('Cannot revoke permission for yourself as admin')
+ h.flash(msg, category='warning')
+ raise Exception('revoke admin permission on self')
+ if obj_type == 'user':
+ UserGroupModel().revoke_user_permission(user_group=id,
+ user=obj_id)
+ elif obj_type == 'user_group':
+ UserGroupModel().revoke_user_group_permission(target_user_group=id,
+ user_group=obj_id)
+ Session().commit()
+ except Exception:
+ log.error(traceback.format_exc())
+ h.flash(_('An error occurred during revoking of permission'),
+ category='error')
+ raise HTTPInternalServerError()
+
+ @HasUserGroupPermissionAnyDecorator('usergroup.admin')
+ def edit_default_perms(self, id):
+ c.user_group = UserGroup.get_or_404(id)
+ c.active = 'default_perms'
+
+ permissions = {
+ 'repositories': {},
+ 'repositories_groups': {}
+ }
+ ugroup_repo_perms = UserGroupRepoToPerm.query()\
+ .options(joinedload(UserGroupRepoToPerm.permission))\
+ .options(joinedload(UserGroupRepoToPerm.repository))\
+ .filter(UserGroupRepoToPerm.users_group_id == id)\
+ .all()
+
+ for gr in ugroup_repo_perms:
+ permissions['repositories'][gr.repository.repo_name] \
+ = gr.permission.permission_name
+
+ ugroup_group_perms = UserGroupRepoGroupToPerm.query()\
+ .options(joinedload(UserGroupRepoGroupToPerm.permission))\
+ .options(joinedload(UserGroupRepoGroupToPerm.group))\
+ .filter(UserGroupRepoGroupToPerm.users_group_id == id)\
+ .all()
+
+ for gr in ugroup_group_perms:
+ permissions['repositories_groups'][gr.group.group_name] \
+ = gr.permission.permission_name
+ c.permissions = permissions
+
+ ug_model = UserGroupModel()
+
+ defaults = c.user_group.get_dict()
+ defaults.update({
+ 'create_repo_perm': ug_model.has_perm(c.user_group,
+ 'hg.create.repository'),
+ 'create_user_group_perm': ug_model.has_perm(c.user_group,
+ 'hg.usergroup.create.true'),
+ 'fork_repo_perm': ug_model.has_perm(c.user_group,
+ 'hg.fork.repository'),
+ })
+
+ return htmlfill.render(
+ render('admin/user_groups/user_group_edit.html'),
+ defaults=defaults,
+ encoding="UTF-8",
+ force_defaults=False
+ )
+
+ @HasUserGroupPermissionAnyDecorator('usergroup.admin')
+ def update_default_perms(self, id):
+ """PUT /users_perm/id: Update an existing item"""
+ # url('users_group_perm', id=ID, method='put')
+
+ user_group = UserGroup.get_or_404(id)
+
+ try:
+ form = CustomDefaultPermissionsForm()()
+ form_result = form.to_python(request.POST)
+
+ inherit_perms = form_result['inherit_default_permissions']
+ user_group.inherit_default_permissions = inherit_perms
+ Session().add(user_group)
+ usergroup_model = UserGroupModel()
+
+ defs = UserGroupToPerm.query()\
+ .filter(UserGroupToPerm.users_group == user_group)\
+ .all()
+ for ug in defs:
+ Session().delete(ug)
+
+ if form_result['create_repo_perm']:
+ usergroup_model.grant_perm(id, 'hg.create.repository')
+ else:
+ usergroup_model.grant_perm(id, 'hg.create.none')
+ if form_result['create_user_group_perm']:
+ usergroup_model.grant_perm(id, 'hg.usergroup.create.true')
+ else:
+ usergroup_model.grant_perm(id, 'hg.usergroup.create.false')
+ if form_result['fork_repo_perm']:
+ usergroup_model.grant_perm(id, 'hg.fork.repository')
+ else:
+ usergroup_model.grant_perm(id, 'hg.fork.none')
+
+ h.flash(_("Updated permissions"), category='success')
+ Session().commit()
+ except Exception:
+ log.error(traceback.format_exc())
+ h.flash(_('An error occurred during permissions saving'),
+ category='error')
+
+ return redirect(url('edit_user_group_default_perms', id=id))
+
+ @HasUserGroupPermissionAnyDecorator('usergroup.admin')
+ def edit_advanced(self, id):
+ c.user_group = UserGroup.get_or_404(id)
+ c.active = 'advanced'
+ c.group_members_obj = sorted((x.user for x in c.user_group.members),
+ key=lambda u: u.username.lower())
+ return render('admin/user_groups/user_group_edit.html')
+
+
+ @HasUserGroupPermissionAnyDecorator('usergroup.admin')
+ def edit_members(self, id):
+ c.user_group = UserGroup.get_or_404(id)
+ c.active = 'members'
+ c.group_members_obj = sorted((x.user for x in c.user_group.members),
+ key=lambda u: u.username.lower())
+
+ c.group_members = [(x.user_id, x.username) for x in c.group_members_obj]
+ return render('admin/user_groups/user_group_edit.html')