# HG changeset patch # User Marcin Kuzminski # Date 1365454055 -7200 # Node ID 7e3d89d9d3a21b9e4ac4beac0ef15b6ed7c9bb76 # Parent e45f8cefd7d9219f450237eb32b41dd238a9a578 - Manage User’s Groups: create, delete, rename, add/remove users inside. by user group admin. In this case, a user's group can be owned by several people thru an owner user's group. Some refactoring of naming, permission handling logic. - remove some code duplicity as well as inconsistent naming diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 docs/api/api.rst --- a/docs/api/api.rst Mon Apr 08 20:38:37 2013 +0200 +++ b/docs/api/api.rst Mon Apr 08 22:47:35 2013 +0200 @@ -518,8 +518,9 @@ api_key : "" method : "create_users_group" args: { - "group_name": "", - "active":" = Optional(True)" + "group_name": "", + "owner" : "", + "active": " = Optional(True)" } OUTPUT:: diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/__init__.py --- a/rhodecode/__init__.py Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/__init__.py Mon Apr 08 22:47:35 2013 +0200 @@ -38,7 +38,7 @@ __version__ = ('.'.join((str(each) for each in VERSION[:3])) + '.'.join(VERSION[3:])) -__dbversion__ = 11 # defines current db version for migrations +__dbversion__ = 12 # defines current db version for migrations __platform__ = platform.system() __license__ = 'GPLv3' __py_version__ = sys.version_info diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/config/routing.py --- a/rhodecode/config/routing.py Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/config/routing.py Mon Apr 08 22:47:35 2013 +0200 @@ -200,6 +200,13 @@ m.connect("formatted_repos_group", "/repos_groups/{group_name:.*?}.{format}", action="show", conditions=dict(method=["GET"], function=check_group)) + + #add repo perm member + m.connect('set_repo_group_perm_member', + "/set_repo_group_perm_member/{group_name:.*?}", + action="set_repo_group_perm_member", + conditions=dict(method=["POST"], function=check_group)) + # ajax delete repository group perm user m.connect('delete_repos_group_user_perm', "/delete_repos_group_user_perm/{group_name:.*?}", @@ -279,9 +286,20 @@ action="show", conditions=dict(method=["GET"])) #EXTRAS USER ROUTES - m.connect("users_group_perm", "/users_groups_perm/{id}", + # update + m.connect("users_group_perm", "/users_groups/{id}/update_global_perm", action="update_perm", conditions=dict(method=["PUT"])) + #add user group perm member + m.connect('set_user_group_perm_member', "/users_groups/{id}/grant_perm", + action="set_user_group_perm_member", + conditions=dict(method=["POST"])) + + #ajax delete user group perm + m.connect('delete_user_group_perm_member', "/users_groups/{id}/revoke_perm", + action="delete_user_group_perm_member", + conditions=dict(method=["DELETE"])) + #ADMIN GROUP REST ROUTES rmap.resource('group', 'groups', controller='admin/groups', path_prefix=ADMIN_PREFIX) diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/controllers/admin/repos.py --- a/rhodecode/controllers/admin/repos.py Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/controllers/admin/repos.py Mon Apr 08 22:47:35 2013 +0200 @@ -46,7 +46,7 @@ from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup,\ RhodeCodeSetting, RepositoryField from rhodecode.model.forms import RepoForm, RepoFieldForm, RepoPermsForm -from rhodecode.model.scm import ScmModel, GroupList +from rhodecode.model.scm import ScmModel, RepoGroupList from rhodecode.model.repo import RepoModel from rhodecode.lib.compat import json from sqlalchemy.sql.expression import func @@ -67,7 +67,7 @@ super(ReposController, self).__before__() def __load_defaults(self): - acl_groups = GroupList(RepoGroup.query().all(), + acl_groups = RepoGroupList(RepoGroup.query().all(), perm_set=['group.write', 'group.admin']) c.repo_groups = RepoGroup.groups_choices(groups=acl_groups) c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups) @@ -214,7 +214,7 @@ if not HasReposGroupPermissionAny('group.admin', 'group.write')(group_name=gr_name): raise HTTPForbidden - acl_groups = GroupList(RepoGroup.query().all(), + acl_groups = RepoGroupList(RepoGroup.query().all(), perm_set=['group.write', 'group.admin']) c.repo_groups = RepoGroup.groups_choices(groups=acl_groups) c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups) @@ -330,32 +330,8 @@ @HasRepoPermissionAllDecorator('repository.admin') def set_repo_perm_member(self, repo_name): form = RepoPermsForm()().to_python(request.POST) - - perms_new = form['perms_new'] - perms_updates = form['perms_updates'] - cur_repo = repo_name - - # update permissions - for member, perm, member_type in perms_updates: - if member_type == 'user': - # this updates existing one - RepoModel().grant_user_permission( - repo=cur_repo, user=member, perm=perm - ) - else: - RepoModel().grant_users_group_permission( - repo=cur_repo, group_name=member, perm=perm - ) - # set new permissions - for member, perm, member_type in perms_new: - if member_type == 'user': - RepoModel().grant_user_permission( - repo=cur_repo, user=member, perm=perm - ) - else: - RepoModel().grant_users_group_permission( - repo=cur_repo, group_name=member, perm=perm - ) + RepoModel()._update_permissions(repo_name, form['perms_new'], + form['perms_updates']) #TODO: implement this #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions', # repo_name, self.ip_addr, self.sa) diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/controllers/admin/repos_groups.py --- a/rhodecode/controllers/admin/repos_groups.py Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/controllers/admin/repos_groups.py Mon Apr 08 22:47:35 2013 +0200 @@ -43,14 +43,15 @@ HasPermissionAll from rhodecode.lib.base import BaseController, render from rhodecode.model.db import RepoGroup, Repository +from rhodecode.model.scm import RepoGroupList from rhodecode.model.repos_group import ReposGroupModel -from rhodecode.model.forms import ReposGroupForm +from rhodecode.model.forms import ReposGroupForm, RepoGroupPermsForm from rhodecode.model.meta import Session from rhodecode.model.repo import RepoModel from webob.exc import HTTPInternalServerError, HTTPNotFound from rhodecode.lib.utils2 import str2bool, safe_int from sqlalchemy.sql.expression import func -from rhodecode.model.scm import GroupList + log = logging.getLogger(__name__) @@ -72,7 +73,7 @@ #override the choices for this form, we need to filter choices #and display only those we have ADMIN right - groups_with_admin_rights = GroupList(RepoGroup.query().all(), + groups_with_admin_rights = RepoGroupList(RepoGroup.query().all(), perm_set=['group.admin']) c.repo_groups = RepoGroup.groups_choices(groups=groups_with_admin_rights, show_empty_group=allow_empty_group) @@ -94,12 +95,12 @@ data = repo_group.get_dict() data['group_name'] = repo_group.name - # fill repository users + # fill repository group users for p in repo_group.repo_group_to_perm: data.update({'u_perm_%s' % p.user.username: p.permission.permission_name}) - # fill repository groups + # fill repository group groups for p in repo_group.users_group_to_perm: data.update({'g_perm_%s' % p.users_group.users_group_name: p.permission.permission_name}) @@ -118,7 +119,8 @@ def index(self, format='html'): """GET /repos_groups: All items in the collection""" # url('repos_groups') - group_iter = GroupList(RepoGroup.query().all(), perm_set=['group.admin']) + group_iter = RepoGroupList(RepoGroup.query().all(), + perm_set=['group.admin']) sk = lambda g: g.parents[0].group_name if g.parents else g.group_name c.groups = sorted(group_iter, key=sk) return render('admin/repos_groups/repos_groups_show.html') @@ -190,7 +192,7 @@ # method='put') # url('repos_group', group_name=GROUP_NAME) - c.repos_group = ReposGroupModel()._get_repos_group(group_name) + c.repos_group = ReposGroupModel()._get_repo_group(group_name) if HasPermissionAll('hg.admin')('group edit'): #we're global admin, we're ok and we can create TOP level groups allow_empty_group = True @@ -247,7 +249,7 @@ # method='delete') # url('repos_group', group_name=GROUP_NAME) - gr = c.repos_group = ReposGroupModel()._get_repos_group(group_name) + gr = c.repos_group = ReposGroupModel()._get_repo_group(group_name) repos = gr.repositories.all() if repos: h.flash(_('This group contains %s repositores and cannot be ' @@ -274,6 +276,24 @@ return redirect(url('repos_groups')) @HasReposGroupPermissionAnyDecorator('group.admin') + def set_repo_group_perm_member(self, group_name): + c.repos_group = ReposGroupModel()._get_repo_group(group_name) + form = RepoGroupPermsForm()().to_python(request.POST) + + recursive = form['recursive'] + # iterate over all members(if in recursive mode) of this groups and + # set the permissions ! + # this can be potentially heavy operation + ReposGroupModel()._update_permissions(c.repos_group, form['perms_new'], + form['perms_updates'], recursive) + #TODO: implement this + #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions', + # repo_name, self.ip_addr, self.sa) + Session().commit() + h.flash(_('Repository Group permissions updated'), category='success') + return redirect(url('edit_repos_group', group_name=group_name)) + + @HasReposGroupPermissionAnyDecorator('group.admin') def delete_repos_group_user_perm(self, group_name): """ DELETE an existing repository group permission user @@ -337,7 +357,7 @@ """GET /repos_groups/group_name: Show a specific item""" # url('repos_group', group_name=GROUP_NAME) - c.group = c.repos_group = ReposGroupModel()._get_repos_group(group_name) + c.group = c.repos_group = ReposGroupModel()._get_repo_group(group_name) c.group_repos = c.group.repositories.all() #overwrite our cached list with current filter @@ -369,7 +389,7 @@ """GET /repos_groups/group_name/edit: Form to edit an existing item""" # url('edit_repos_group', group_name=GROUP_NAME) - c.repos_group = ReposGroupModel()._get_repos_group(group_name) + c.repos_group = ReposGroupModel()._get_repo_group(group_name) #we can only allow moving empty group if it's already a top-level #group, ie has no parents, or we're admin if HasPermissionAll('hg.admin')('group edit'): diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/controllers/admin/settings.py --- a/rhodecode/controllers/admin/settings.py Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/controllers/admin/settings.py Mon Apr 08 22:47:35 2013 +0200 @@ -47,7 +47,7 @@ RhodeCodeSetting, PullRequest, PullRequestReviewers from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \ ApplicationUiSettingsForm, ApplicationVisualisationForm -from rhodecode.model.scm import ScmModel, GroupList +from rhodecode.model.scm import ScmModel, RepoGroupList from rhodecode.model.user import UserModel from rhodecode.model.repo import RepoModel from rhodecode.model.db import User diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/controllers/admin/users_groups.py --- a/rhodecode/controllers/admin/users_groups.py Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/controllers/admin/users_groups.py Mon Apr 08 22:47:35 2013 +0200 @@ -34,18 +34,20 @@ from rhodecode.lib import helpers as h from rhodecode.lib.exceptions import UserGroupsAssignedException -from rhodecode.lib.utils2 import safe_unicode, str2bool -from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator +from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int +from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator,\ + HasUserGroupPermissionAnyDecorator from rhodecode.lib.base import BaseController, render - +from rhodecode.model.scm import UserGroupList from rhodecode.model.users_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 +from rhodecode.model.forms import UserGroupForm, UserGroupPermsForm from rhodecode.model.meta import Session from rhodecode.lib.utils import action_logger from sqlalchemy.orm import joinedload +from webob.exc import HTTPInternalServerError log = logging.getLogger(__name__) @@ -57,17 +59,82 @@ # map.resource('users_group', 'users_groups') @LoginRequired() - @HasPermissionAllDecorator('hg.admin') def __before__(self): super(UsersGroupsController, self).__before__() c.available_permissions = config['available_permissions'] + def __load_data(self, user_group_id): + ugroup_repo_perms = UserGroupRepoToPerm.query()\ + .options(joinedload(UserGroupRepoToPerm.permission))\ + .options(joinedload(UserGroupRepoToPerm.repository))\ + .filter(UserGroupRepoToPerm.users_group_id == user_group_id)\ + .all() + + for gr in ugroup_repo_perms: + c.users_group.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 == user_group_id)\ + .all() + + for gr in ugroup_group_perms: + c.users_group.permissions['repositories_groups'][gr.group.group_name] \ + = gr.permission.permission_name + + c.group_members_obj = sorted((x.user for x in c.users_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()) + repo_model = RepoModel() + c.users_array = repo_model.get_users_js() + + # commented out due to not now supporting assignment for user group + # on user group + c.users_groups_array = "[]" # repo_model.get_users_groups_js() + c.available_permissions = config['available_permissions'] + + 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() + + ug_model = UserGroupModel() + + data.update({ + 'create_repo_perm': ug_model.has_perm(user_group, + 'hg.create.repository'), + 'fork_repo_perm': ug_model.has_perm(user_group, + 'hg.fork.repository'), + }) + + # fill user group users + for p in user_group.user_user_group_to_perm: + data.update({'u_perm_%s' % p.user.username: + p.permission.permission_name}) + + return data + def index(self, format='html'): """GET /users_groups: All items in the collection""" # url('users_groups') - c.users_groups_list = UserGroup().query().all() + + group_iter = UserGroupList(UserGroup().query().all(), + perm_set=['usergroup.admin']) + sk = lambda g: g.users_group_name + c.users_groups_list = sorted(group_iter, key=sk) return render('admin/users_groups/users_groups.html') + @HasPermissionAllDecorator('hg.admin') def create(self): """POST /users_groups: Create a new item""" # url('users_groups') @@ -76,7 +143,9 @@ try: form_result = users_group_form.to_python(dict(request.POST)) UserGroupModel().create(name=form_result['users_group_name'], - active=form_result['users_group_active']) + 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, @@ -97,45 +166,13 @@ return redirect(url('users_groups')) + @HasPermissionAllDecorator('hg.admin') def new(self, format='html'): """GET /users_groups/new: Form to create a new item""" # url('new_users_group') return render('admin/users_groups/users_group_add.html') - def _load_data(self, id): - c.users_group.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: - c.users_group.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: - c.users_group.permissions['repositories_groups'][gr.group.group_name] \ - = gr.permission.permission_name - - c.group_members_obj = sorted((x.user for x in c.users_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()) - + @HasUserGroupPermissionAnyDecorator('usergroup.admin') def update(self, id): """PUT /users_groups/id: Update an existing item""" # Forms posted to this method should contain a hidden field: @@ -146,7 +183,7 @@ # url('users_group', id=ID) c.users_group = UserGroup.get_or_404(id) - self._load_data(id) + self.__load_data(id) available_members = [safe_unicode(x[0]) for x in c.available_members] @@ -188,6 +225,7 @@ return redirect(url('edit_users_group', id=id)) + @HasUserGroupPermissionAnyDecorator('usergroup.admin') def delete(self, id): """DELETE /users_groups/id: Delete an existing item""" # Forms posted to this method should contain a hidden field: @@ -209,25 +247,71 @@ category='error') return redirect(url('users_groups')) + @HasUserGroupPermissionAnyDecorator('usergroup.admin') + def set_user_group_perm_member(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 ! + UserGroupModel()._update_permissions(user_group, form['perms_new'], + form['perms_updates']) + #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_users_group', id=id)) + + @HasUserGroupPermissionAnyDecorator('usergroup.admin') + def delete_user_group_perm_member(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': + pass + Session().commit() + except Exception: + log.error(traceback.format_exc()) + h.flash(_('An error occurred during deletion of group user'), + category='error') + raise HTTPInternalServerError() + def show(self, id, format='html'): """GET /users_groups/id: Show a specific item""" # url('users_group', id=ID) + @HasUserGroupPermissionAnyDecorator('usergroup.admin') def edit(self, id, format='html'): """GET /users_groups/id/edit: Form to edit an existing item""" # url('edit_users_group', id=ID) c.users_group = UserGroup.get_or_404(id) - self._load_data(id) + self.__load_data(id) - ug_model = UserGroupModel() - defaults = c.users_group.get_dict() - defaults.update({ - 'create_repo_perm': ug_model.has_perm(c.users_group, - 'hg.create.repository'), - 'fork_repo_perm': ug_model.has_perm(c.users_group, - 'hg.fork.repository'), - }) + defaults = self.__load_defaults(id) return htmlfill.render( render('admin/users_groups/users_group_edit.html'), @@ -236,6 +320,7 @@ force_defaults=False ) + @HasUserGroupPermissionAnyDecorator('usergroup.admin') def update_perm(self, id): """PUT /users_perm/id: Update an existing item""" # url('users_group_perm', id=ID, method='put') diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/controllers/api/api.py --- a/rhodecode/controllers/api/api.py Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/controllers/api/api.py Mon Apr 08 22:47:35 2013 +0200 @@ -537,12 +537,15 @@ return result @HasPermissionAllDecorator('hg.admin') - def create_users_group(self, apiuser, group_name, active=Optional(True)): + def create_users_group(self, apiuser, group_name, + owner=Optional(OAttr('apiuser')), + active=Optional(True)): """ Creates an new usergroup :param apiuser: :param group_name: + :param owner: :param active: """ @@ -550,8 +553,14 @@ raise JSONRPCError("user group `%s` already exist" % group_name) try: + if isinstance(owner, Optional): + owner = apiuser.user_id + + owner = get_user_or_error(owner) active = Optional.extract(active) - ug = UserGroupModel().create(name=group_name, active=active) + ug = UserGroupModel().create(name=group_name, + owner=owner, + active=active) Session().commit() return dict( msg='created new user group `%s`' % group_name, diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/controllers/forks.py --- a/rhodecode/controllers/forks.py Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/controllers/forks.py Mon Apr 08 22:47:35 2013 +0200 @@ -42,7 +42,7 @@ RhodeCodeUi from rhodecode.model.repo import RepoModel from rhodecode.model.forms import RepoForkForm -from rhodecode.model.scm import ScmModel, GroupList +from rhodecode.model.scm import ScmModel, RepoGroupList from rhodecode.lib.utils2 import safe_int log = logging.getLogger(__name__) @@ -55,7 +55,7 @@ super(ForksController, self).__before__() def __load_defaults(self): - acl_groups = GroupList(RepoGroup.query().all(), + acl_groups = RepoGroupList(RepoGroup.query().all(), perm_set=['group.write', 'group.admin']) c.repo_groups = RepoGroup.groups_choices(groups=acl_groups) c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups) diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/lib/auth.py --- a/rhodecode/lib/auth.py Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/lib/auth.py Mon Apr 08 22:47:35 2013 +0200 @@ -42,7 +42,8 @@ from rhodecode.lib.utils2 import str2bool, safe_unicode from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError,\ LdapImportError -from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug +from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug,\ + get_user_group_slug from rhodecode.lib.auth_ldap import AuthLdap from rhodecode.model import meta @@ -410,7 +411,7 @@ if x[1] == 'repository.admin'] @property - def groups_admin(self): + def repository_groups_admin(self): """ Returns list of repository groups you're an admin of """ @@ -418,6 +419,14 @@ if x[1] == 'group.admin'] @property + def user_groups_admin(self): + """ + Returns list of user groups you're an admin of + """ + return [x[0] for x in self.permissions['user_groups'].iteritems() + if x[1] == 'usergroup.admin'] + + @property def ip_allowed(self): """ Checks if ip_addr used in constructor is allowed from defined list of @@ -693,7 +702,7 @@ class HasReposGroupPermissionAllDecorator(PermsDecorator): """ Checks for access permission for all given predicates for specific - repository. All of them have to be meet in order to fulfill the request + repository group. All of them have to be meet in order to fulfill the request """ def check_permissions(self): @@ -711,7 +720,7 @@ class HasReposGroupPermissionAnyDecorator(PermsDecorator): """ Checks for access permission for any of given predicates for specific - repository. In order to fulfill the request any of predicates must be meet + repository group. In order to fulfill the request any of predicates must be meet """ def check_permissions(self): @@ -726,6 +735,42 @@ return False +class HasUserGroupPermissionAllDecorator(PermsDecorator): + """ + Checks for access permission for all given predicates for specific + user group. All of them have to be meet in order to fulfill the request + """ + + def check_permissions(self): + group_name = get_user_group_slug(request) + try: + user_perms = set([self.user_perms['user_groups'][group_name]]) + except KeyError: + return False + + if self.required_perms.issubset(user_perms): + return True + return False + + +class HasUserGroupPermissionAnyDecorator(PermsDecorator): + """ + Checks for access permission for any of given predicates for specific + user group. In order to fulfill the request any of predicates must be meet + """ + + def check_permissions(self): + group_name = get_user_group_slug(request) + try: + user_perms = set([self.user_perms['user_groups'][group_name]]) + except KeyError: + return False + + if self.required_perms.intersection(user_perms): + return True + return False + + #============================================================================== # CHECK FUNCTIONS #============================================================================== @@ -865,6 +910,39 @@ return False +class HasUserGroupPermissionAny(PermsFunction): + def __call__(self, user_group_name=None, check_location=''): + self.user_group_name = user_group_name + return super(HasUserGroupPermissionAny, self).__call__(check_location) + + def check_permissions(self): + try: + self._user_perms = set( + [self.user_perms['user_groups'][self.user_group_name]] + ) + except KeyError: + return False + if self.required_perms.intersection(self._user_perms): + return True + return False + + +class HasUserGroupPermissionAll(PermsFunction): + def __call__(self, user_group_name=None, check_location=''): + self.user_group_name = user_group_name + return super(HasUserGroupPermissionAll, self).__call__(check_location) + + def check_permissions(self): + try: + self._user_perms = set( + [self.user_perms['user_groups'][self.user_group_name]] + ) + except KeyError: + return False + if self.required_perms.issubset(self._user_perms): + return True + return False + #============================================================================== # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH #============================================================================== diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/lib/db_manage.py --- a/rhodecode/lib/db_manage.py Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/lib/db_manage.py Mon Apr 08 22:47:35 2013 +0200 @@ -89,7 +89,7 @@ else: destroy = ask_ok('Are you sure to destroy old database ? [y/n]') if not destroy: - sys.exit('Nothing done') + sys.exit('Nothing tables created') if destroy: Base.metadata.drop_all() @@ -127,7 +127,7 @@ 'sure You backed up your database before. ' 'Continue ? [y/n]') if not upgrade: - sys.exit('Nothing done') + sys.exit('No upgrade performed') repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))), 'rhodecode/lib/dbmigrate') @@ -292,6 +292,10 @@ def step_11(self): self.klass.update_repo_info() + def step_12(self): + self.klass.create_permissions() + self.klass.populate_default_permissions() + upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1) # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE @@ -528,7 +532,8 @@ if default is None: log.debug('missing default permission for group %s adding' % g) - ReposGroupModel()._create_default_perms(g) + perm_obj = ReposGroupModel()._create_default_perms(g) + self.sa.add(perm_obj) def reset_permissions(self, username): """ diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/lib/dbmigrate/versions/012_version_1_7_0.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rhodecode/lib/dbmigrate/versions/012_version_1_7_0.py Mon Apr 08 22:47:35 2013 +0200 @@ -0,0 +1,62 @@ +import logging +import datetime + +from sqlalchemy import * +from sqlalchemy.exc import DatabaseError +from sqlalchemy.orm import relation, backref, class_mapper, joinedload +from sqlalchemy.orm.session import Session +from sqlalchemy.ext.declarative import declarative_base + +from rhodecode.lib.dbmigrate.migrate import * +from rhodecode.lib.dbmigrate.migrate.changeset import * + +from rhodecode.model.meta import Base +from rhodecode.model import meta +from rhodecode.lib.dbmigrate.versions import _reset_base + +log = logging.getLogger(__name__) + + +def upgrade(migrate_engine): + """ + Upgrade operations go here. + Don't create your own engine; bind migrate_engine to your metadata + """ + _reset_base(migrate_engine) + + #========================================================================== + # UserUserGroupToPerm + #========================================================================== + from rhodecode.lib.dbmigrate.schema.db_1_7_0 import UserUserGroupToPerm + tbl = UserUserGroupToPerm.__table__ + tbl.create() + + #========================================================================== + # UserGroupUserGroupToPerm + #========================================================================== + from rhodecode.lib.dbmigrate.schema.db_1_7_0 import UserGroupUserGroupToPerm + tbl = UserGroupUserGroupToPerm.__table__ + tbl.create() + + #========================================================================== + # UserGroup + #========================================================================== + from rhodecode.lib.dbmigrate.schema.db_1_7_0 import UserGroup + tbl = UserGroup.__table__ + user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=False, default=None) + # create username column + user_id.create(table=tbl) + + #========================================================================== + # UserGroup + #========================================================================== + from rhodecode.lib.dbmigrate.schema.db_1_7_0 import RepoGroup + tbl = RepoGroup.__table__ + user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=False, default=None) + # create username column + user_id.create(table=tbl) + + +def downgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/lib/utils.py --- a/rhodecode/lib/utils.py Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/lib/utils.py Mon Apr 08 22:47:35 2013 +0200 @@ -53,7 +53,7 @@ from rhodecode.model import meta from rhodecode.model.db import Repository, User, RhodeCodeUi, \ - UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation + UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation, UserGroup from rhodecode.model.meta import Session from rhodecode.model.repos_group import ReposGroupModel from rhodecode.lib.utils2 import safe_str, safe_unicode @@ -114,6 +114,14 @@ return _group +def get_user_group_slug(request): + _group = request.environ['pylons.routes_dict'].get('id') + _group = UserGroup.get(_group) + if _group: + _group = _group.users_group_name + return _group + + def action_logger(user, action, repo, ipaddr='', sa=None, commit=False): """ Action logger for various actions made by users @@ -372,6 +380,7 @@ # last element is repo in nested groups structure groups = groups[:-1] rgm = ReposGroupModel(sa) + owner = User.get_first_admin() for lvl, group_name in enumerate(groups): group_name = '/'.join(groups[:lvl] + [group_name]) group = RepoGroup.get_by_group_name(group_name) @@ -382,13 +391,16 @@ break if group is None: - log.debug('creating group level: %s group_name: %s' % (lvl, - group_name)) + log.debug('creating group level: %s group_name: %s' + % (lvl, group_name)) group = RepoGroup(group_name, parent) group.group_description = desc + group.user = owner sa.add(group) - rgm._create_default_perms(group) + perm_obj = rgm._create_default_perms(group) + sa.add(perm_obj) sa.flush() + parent = group return group @@ -409,9 +421,7 @@ from rhodecode.model.scm import ScmModel sa = meta.Session() rm = RepoModel() - user = sa.query(User).filter(User.admin == True).first() - if user is None: - raise Exception('Missing administrative account!') + user = User.get_first_admin() added = [] ##creation defaults diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/model/db.py --- a/rhodecode/model/db.py Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/model/db.py Mon Apr 08 22:47:35 2013 +0200 @@ -131,6 +131,11 @@ @classmethod def getAll(cls): + # deprecated and left for backward compatibility + return cls.get_all() + + @classmethod + def get_all(cls): return cls.query().all() @classmethod @@ -490,6 +495,13 @@ Session().add(self) log.debug('updated user %s lastlogin' % self.username) + @classmethod + def get_first_admin(cls): + user = User.query().filter(User.admin == True).first() + if user is None: + raise Exception('Missing administrative account!') + return user + def get_api_data(self): """ Common function for generating user related data for API @@ -616,13 +628,18 @@ users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None) users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None) inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True) + user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None) members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined") users_group_to_perm = relationship('UserGroupToPerm', cascade='all') users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all') + user_user_group_to_perm = relationship('UserUserGroupToPerm ', cascade='all') + user = relationship('User') def __unicode__(self): - return u'' % (self.users_group_name) + return u"<%s('id:%s:%s')>" % (self.__class__.__name__, + self.users_group_id, + self.users_group_name) @classmethod def get_by_group_name(cls, group_name, cache=False, @@ -1229,19 +1246,20 @@ group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None) group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False) + user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None) repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id') users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all') - parent_group = relationship('RepoGroup', remote_side=group_id) + user = relationship('User') def __init__(self, group_name='', parent_group=None): self.group_name = group_name self.parent_group = parent_group def __unicode__(self): - return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id, - self.group_name) + return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id, + self.group_name) @classmethod def groups_choices(cls, groups=None, show_empty_group=True): @@ -1397,6 +1415,11 @@ ('group.write', _('Repository group write access')), ('group.admin', _('Repository group admin access')), + ('usergroup.none', _('User group no access')), + ('usergroup.read', _('User group read access')), + ('usergroup.write', _('User group write access')), + ('usergroup.admin', _('User group admin access')), + ('hg.admin', _('RhodeCode Administrator')), ('hg.create.none', _('Repository creation disabled')), ('hg.create.repository', _('Repository creation enabled')), @@ -1422,10 +1445,15 @@ 'group.write': 3, 'group.admin': 4, + 'usergroup.none': 0, + 'usergroup.read': 1, + 'usergroup.write': 3, + 'usergroup.admin': 4, + 'hg.fork.none': 0, 'hg.fork.repository': 1, 'hg.create.none': 0, - 'hg.create.repository':1 + 'hg.create.repository': 1 } permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) @@ -1459,6 +1487,15 @@ return q.all() + @classmethod + def get_default_user_group_perms(cls, default_user_id): + q = Session().query(UserUserGroupToPerm, UserGroup, cls)\ + .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\ + .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\ + .filter(UserUserGroupToPerm.user_id == default_user_id) + + return q.all() + class UserRepoToPerm(Base, BaseModel): __tablename__ = 'repo_to_perm' @@ -1486,7 +1523,36 @@ return n def __unicode__(self): - return u' %s >' % (self.user, self.repository) + return u'<%s => %s >' % (self.user, self.repository) + + +class UserUserGroupToPerm(Base, BaseModel): + __tablename__ = 'user_user_group_to_perm' + __table_args__ = ( + UniqueConstraint('user_id', 'user_group_id', 'permission_id'), + {'extend_existing': True, 'mysql_engine': 'InnoDB', + 'mysql_charset': 'utf8'} + ) + user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None) + permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) + user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) + + user = relationship('User') + user_group = relationship('UserGroup') + permission = relationship('Permission') + + @classmethod + def create(cls, user, user_group, permission): + n = cls() + n.user = user + n.user_group = user_group + n.permission = permission + Session().add(n) + return n + + def __unicode__(self): + return u'<%s => %s >' % (self.user, self.user_group) class UserToPerm(Base, BaseModel): @@ -1533,6 +1599,36 @@ return u' %s >' % (self.users_group, self.repository) +#TODO; not sure if this will be ever used +class UserGroupUserGroupToPerm(Base, BaseModel): + __tablename__ = 'user_group_user_group_to_perm' + __table_args__ = ( + UniqueConstraint('user_group_id', 'user_group_id', 'permission_id'), + {'extend_existing': True, 'mysql_engine': 'InnoDB', + 'mysql_charset': 'utf8'} + ) + user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True) + target_user_group_id = Column("target_users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) + permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None) + user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None) + + target_user_group = relationship('UserGroup', remote_side=target_user_group_id, primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id') + user_group = relationship('UserGroup', remote_side=user_group_id, primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id') + permission = relationship('Permission') + + @classmethod + def create(cls, target_user_group, user_group, permission): + n = cls() + n.target_user_group = target_user_group + n.user_group = user_group + n.permission = permission + Session().add(n) + return n + + def __unicode__(self): + return u' %s >' % (self.target_user_group, self.user_group) + + class UserGroupToPerm(Base, BaseModel): __tablename__ = 'users_group_to_perm' __table_args__ = ( diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/model/forms.py --- a/rhodecode/model/forms.py Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/model/forms.py Mon Apr 08 22:47:35 2013 +0200 @@ -134,9 +134,7 @@ testValueList=True, if_missing=None, not_empty=True)) enable_locking = v.StringBoolean(if_missing=False) - recursive = v.StringBoolean(if_missing=False) - chained_validators = [v.ValidReposGroup(edit, old_data), - v.ValidPerms('group')] + chained_validators = [v.ValidReposGroup(edit, old_data)] return _ReposGroupForm @@ -207,10 +205,27 @@ class _RepoPermsForm(formencode.Schema): allow_extra_fields = True filter_extra_fields = False - chained_validators = [v.ValidPerms()] + chained_validators = [v.ValidPerms(type_='repo')] return _RepoPermsForm +def RepoGroupPermsForm(): + class _RepoGroupPermsForm(formencode.Schema): + allow_extra_fields = True + filter_extra_fields = False + recursive = v.StringBoolean(if_missing=False) + chained_validators = [v.ValidPerms(type_='repo_group')] + return _RepoGroupPermsForm + + +def UserGroupPermsForm(): + class _UserPermsForm(formencode.Schema): + allow_extra_fields = True + filter_extra_fields = False + chained_validators = [v.ValidPerms(type_='user_group')] + return _UserPermsForm + + def RepoFieldForm(): class _RepoFieldForm(formencode.Schema): filter_extra_fields = True diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/model/repo.py --- a/rhodecode/model/repo.py Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/model/repo.py Mon Apr 08 22:47:35 2013 +0200 @@ -52,14 +52,33 @@ cls = Repository URL_SEPARATOR = Repository.url_sep() - def __get_users_group(self, users_group): + def _get_user_group(self, users_group): return self._get_instance(UserGroup, users_group, callback=UserGroup.get_by_group_name) - def _get_repos_group(self, repos_group): + def _get_repo_group(self, repos_group): return self._get_instance(RepoGroup, repos_group, callback=RepoGroup.get_by_group_name) + def _create_default_perms(self, repository, private): + # create default permission + default = 'repository.read' + def_user = User.get_by_username('default') + for p in def_user.user_perms: + if p.permission.permission_name.startswith('repository.'): + default = p.permission.permission_name + break + + default_perm = 'repository.none' if private else default + + repo_to_perm = UserRepoToPerm() + repo_to_perm.permission = Permission.get_by_key(default_perm) + + repo_to_perm.repository = repository + repo_to_perm.user_id = def_user.user_id + + return repo_to_perm + @LazyProperty def repos_path(self): """ @@ -336,7 +355,7 @@ owner = self._get_user(owner) fork_of = self._get_repo(fork_of) - repos_group = self._get_repos_group(repos_group) + repos_group = self._get_repo_group(repos_group) try: # repo name is just a name of repository @@ -369,26 +388,6 @@ self.sa.add(new_repo) - def _create_default_perms(): - # create default permission - repo_to_perm = UserRepoToPerm() - default = 'repository.read' - for p in User.get_by_username('default').user_perms: - if p.permission.permission_name.startswith('repository.'): - default = p.permission.permission_name - break - - default_perm = 'repository.none' if private else default - - repo_to_perm.permission_id = self.sa.query(Permission)\ - .filter(Permission.permission_name == default_perm)\ - .one().permission_id - - repo_to_perm.repository = new_repo - repo_to_perm.user_id = User.get_by_username('default').user_id - - self.sa.add(repo_to_perm) - if fork_of: if copy_fork_permissions: repo = fork_of @@ -405,9 +404,11 @@ UserGroupRepoToPerm.create(perm.users_group, new_repo, perm.permission) else: - _create_default_perms() + perm_obj = self._create_default_perms(new_repo, private) + self.sa.add(perm_obj) else: - _create_default_perms() + perm_obj = self._create_default_perms(new_repo, private) + self.sa.add(perm_obj) if not just_db: self.__create_repo(repo_name, repo_type, @@ -456,6 +457,35 @@ enable_statistics, enable_locking, enable_downloads ) + def _update_permissions(self, repo, perms_new=None, + perms_updates=None): + if not perms_new: + perms_new = [] + if not perms_updates: + perms_updates = [] + + # update permissions + for member, perm, member_type in perms_updates: + if member_type == 'user': + # this updates existing one + self.grant_user_permission( + repo=repo, user=member, perm=perm + ) + else: + self.grant_users_group_permission( + repo=repo, group_name=member, perm=perm + ) + # set new permissions + for member, perm, member_type in perms_new: + if member_type == 'user': + self.grant_user_permission( + repo=repo, user=member, perm=perm + ) + else: + self.grant_users_group_permission( + repo=repo, group_name=member, perm=perm + ) + def create_fork(self, form_data, cur_user): """ Simple wrapper into executing celery task for fork creation @@ -559,7 +589,7 @@ :param perm: Instance of Permission, or permission_name """ repo = self._get_repo(repo) - group_name = self.__get_users_group(group_name) + group_name = self._get_user_group(group_name) permission = self._get_perm(perm) # check if we have that permission already @@ -587,7 +617,7 @@ or user group name """ repo = self._get_repo(repo) - group_name = self.__get_users_group(group_name) + group_name = self._get_user_group(group_name) obj = self.sa.query(UserGroupRepoToPerm)\ .filter(UserGroupRepoToPerm.repository == repo)\ diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/model/repos_group.py --- a/rhodecode/model/repos_group.py Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/model/repos_group.py Mon Apr 08 22:47:35 2013 +0200 @@ -42,11 +42,11 @@ cls = RepoGroup - def __get_users_group(self, users_group): + def _get_user_group(self, users_group): return self._get_instance(UserGroup, users_group, callback=UserGroup.get_by_group_name) - def _get_repos_group(self, repos_group): + def _get_repo_group(self, repos_group): return self._get_instance(RepoGroup, repos_group, callback=RepoGroup.get_by_group_name) @@ -61,21 +61,19 @@ def _create_default_perms(self, new_group): # create default permission - repo_group_to_perm = UserRepoGroupToPerm() default_perm = 'group.read' - for p in User.get_by_username('default').user_perms: + def_user = User.get_by_username('default') + for p in def_user.user_perms: if p.permission.permission_name.startswith('group.'): default_perm = p.permission.permission_name break - repo_group_to_perm.permission_id = self.sa.query(Permission)\ - .filter(Permission.permission_name == default_perm)\ - .one().permission_id + repo_group_to_perm = UserRepoGroupToPerm() + repo_group_to_perm.permission = Permission.get_by_key(default_perm) repo_group_to_perm.group = new_group - repo_group_to_perm.user_id = User.get_by_username('default').user_id - - self.sa.add(repo_group_to_perm) + repo_group_to_perm.user_id = def_user.user_id + return repo_group_to_perm def __create_group(self, group_name): """ @@ -143,12 +141,14 @@ def create(self, group_name, group_description, owner, parent=None, just_db=False): try: new_repos_group = RepoGroup() + new_repos_group.user = self._get_user(owner) new_repos_group.group_description = group_description or group_name - new_repos_group.parent_group = self._get_repos_group(parent) + new_repos_group.parent_group = self._get_repo_group(parent) new_repos_group.group_name = new_repos_group.get_new_name(group_name) self.sa.add(new_repos_group) - self._create_default_perms(new_repos_group) + perm_obj = self._create_default_perms(new_repos_group) + self.sa.add(perm_obj) #create an ADMIN permission for owner, later owner should go into #the owner field of groups @@ -176,7 +176,7 @@ def _set_perm_user(obj, user, perm): if isinstance(obj, RepoGroup): - ReposGroupModel().grant_user_permission( + self.grant_user_permission( repos_group=obj, user=user, perm=perm ) elif isinstance(obj, Repository): @@ -193,7 +193,7 @@ def _set_perm_group(obj, users_group, perm): if isinstance(obj, RepoGroup): - ReposGroupModel().grant_users_group_permission( + self.grant_users_group_permission( repos_group=obj, group_name=users_group, perm=perm ) elif isinstance(obj, Repository): @@ -237,14 +237,7 @@ def update(self, repos_group, form_data): try: - repos_group = self._get_repos_group(repos_group) - recursive = form_data['recursive'] - # iterate over all members(if in recursive mode) of this groups and - # set the permissions ! - # this can be potentially heavy operation - self._update_permissions(repos_group, form_data['perms_new'], - form_data['perms_updates'], recursive) - + repos_group = self._get_repo_group(repos_group) old_path = repos_group.full_path # change properties @@ -288,7 +281,7 @@ raise def delete(self, repos_group, force_delete=False): - repos_group = self._get_repos_group(repos_group) + repos_group = self._get_repo_group(repos_group) try: self.sa.delete(repos_group) self.__delete_group(repos_group, force_delete) @@ -307,7 +300,7 @@ :param recursive: recurse to all children of group """ from rhodecode.model.repo import RepoModel - repos_group = self._get_repos_group(repos_group) + repos_group = self._get_repo_group(repos_group) for el in repos_group.recursive_groups_and_repos(): if not recursive: @@ -346,7 +339,7 @@ :param perm: Instance of Permission, or permission_name """ - repos_group = self._get_repos_group(repos_group) + repos_group = self._get_repo_group(repos_group) user = self._get_user(user) permission = self._get_perm(perm) @@ -373,7 +366,7 @@ :param user: Instance of User, user_id or username """ - repos_group = self._get_repos_group(repos_group) + repos_group = self._get_repo_group(repos_group) user = self._get_user(user) obj = self.sa.query(UserRepoGroupToPerm)\ @@ -395,8 +388,8 @@ or user group name :param perm: Instance of Permission, or permission_name """ - repos_group = self._get_repos_group(repos_group) - group_name = self.__get_users_group(group_name) + repos_group = self._get_repo_group(repos_group) + group_name = self._get_user_group(group_name) permission = self._get_perm(perm) # check if we have that permission already @@ -424,8 +417,8 @@ :param group_name: Instance of UserGroup, users_group_id, or user group name """ - repos_group = self._get_repos_group(repos_group) - group_name = self.__get_users_group(group_name) + repos_group = self._get_repo_group(repos_group) + group_name = self._get_user_group(group_name) obj = self.sa.query(UserGroupRepoGroupToPerm)\ .filter(UserGroupRepoGroupToPerm.group == repos_group)\ diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/model/scm.py --- a/rhodecode/model/scm.py Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/model/scm.py Mon Apr 08 22:47:35 2013 +0200 @@ -46,7 +46,8 @@ from rhodecode.lib import helpers as h from rhodecode.lib.utils2 import safe_str, safe_unicode, get_server_url,\ _set_extras -from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny +from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny,\ + HasUserGroupPermissionAnyDecorator, HasUserGroupPermissionAny from rhodecode.lib.utils import get_filesystem_repos, make_ui, \ action_logger, REMOVED_REPO_PAT from rhodecode.model import BaseModel @@ -165,36 +166,58 @@ yield tmp_d -class GroupList(object): - - def __init__(self, db_repo_group_list, perm_set=None): +class _PermCheckIterator(object): + def __init__(self, obj_list, obj_attr, perm_set, perm_checker): """ - Creates iterator from given list of group objects, additionally + Creates iterator from given list of objects, additionally checking permission for them from perm_set var - :param db_repo_group_list: - :param perm_set: list of permissons to check + :param obj_list: list of db objects + :param obj_attr: attribute of object to pass into perm_checker + :param perm_set: list of permissions to check + :param perm_checker: callable to check permissions against """ - self.db_repo_group_list = db_repo_group_list - if not perm_set: - perm_set = ['group.read', 'group.write', 'group.admin'] + self.obj_list = obj_list + self.obj_attr = obj_attr self.perm_set = perm_set + self.perm_checker = perm_checker def __len__(self): - return len(self.db_repo_group_list) + return len(self.obj_list) def __repr__(self): return '<%s (%s)>' % (self.__class__.__name__, self.__len__()) def __iter__(self): - for dbgr in self.db_repo_group_list: + for db_obj in self.obj_list: # check permission at this level - if not HasReposGroupPermissionAny( - *self.perm_set - )(dbgr.group_name, 'get group repo check'): + name = getattr(db_obj, self.obj_attr, None) + if not self.perm_checker(*self.perm_set)(name, self.__class__.__name__): continue - yield dbgr + yield db_obj + + +class RepoGroupList(_PermCheckIterator): + + def __init__(self, db_repo_group_list, perm_set=None): + if not perm_set: + perm_set = ['group.read', 'group.write', 'group.admin'] + + super(RepoGroupList, self).__init__(obj_list=db_repo_group_list, + obj_attr='group_name', perm_set=perm_set, + perm_checker=HasReposGroupPermissionAny) + + +class UserGroupList(_PermCheckIterator): + + def __init__(self, db_user_group_list, perm_set=None): + if not perm_set: + perm_set = ['usergroup.read', 'usergroup.write', 'usergroup.admin'] + + super(UserGroupList, self).__init__(obj_list=db_user_group_list, + obj_attr='users_group_name', perm_set=perm_set, + perm_checker=HasUserGroupPermissionAny) class ScmModel(BaseModel): @@ -293,7 +316,7 @@ if all_groups is None: all_groups = RepoGroup.query()\ .filter(RepoGroup.group_parent_id == None).all() - return [x for x in GroupList(all_groups)] + return [x for x in RepoGroupList(all_groups)] def mark_for_invalidation(self, repo_name): """ diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/model/user.py --- a/rhodecode/model/user.py Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/model/user.py Mon Apr 08 22:47:35 2013 +0200 @@ -425,9 +425,11 @@ """ RK = 'repositories' GK = 'repositories_groups' + UK = 'user_groups' GLOBAL = 'global' user.permissions[RK] = {} user.permissions[GK] = {} + user.permissions[UK] = {} user.permissions[GLOBAL] = set() def _choose_perm(new_perm, cur_perm): @@ -450,6 +452,7 @@ default_repo_perms = Permission.get_default_perms(default_user_id) default_repo_groups_perms = Permission.get_default_group_perms(default_user_id) + default_user_group_perms = Permission.get_default_user_group_perms(default_user_id) if user.is_admin: #================================================================== @@ -469,6 +472,12 @@ rg_k = perm.UserRepoGroupToPerm.group.group_name p = 'group.admin' user.permissions[GK][rg_k] = p + + # user groups + for perm in default_user_group_perms: + u_k = perm.UserUserGroupToPerm.user_group.users_group_name + p = 'usergroup.admin' + user.permissions[UK][u_k] = p return user #================================================================== @@ -504,6 +513,13 @@ p = perm.Permission.permission_name user.permissions[GK][rg_k] = p + # defaults for user groups taken from default user permission + # on given user group + for perm in default_user_group_perms: + u_k = perm.UserUserGroupToPerm.user_group.users_group_name + p = perm.Permission.permission_name + user.permissions[UK][u_k] = p + #====================================================================== # !! OVERRIDE GLOBALS !! with user permissions if any found #====================================================================== @@ -588,15 +604,7 @@ # user explicit permissions for repositories, overrides any specified # by the group permission - user_repo_perms = \ - self.sa.query(UserRepoToPerm, Permission, Repository)\ - .join((Repository, UserRepoToPerm.repository_id == - Repository.repo_id))\ - .join((Permission, UserRepoToPerm.permission_id == - Permission.permission_id))\ - .filter(UserRepoToPerm.user_id == uid)\ - .all() - + user_repo_perms = Permission.get_default_perms(uid) for perm in user_repo_perms: r_k = perm.UserRepoToPerm.repository.repo_name cur_perm = user.permissions[RK][r_k] @@ -639,14 +647,7 @@ user.permissions[GK][g_k] = p # user explicit permissions for repository groups - user_repo_groups_perms = \ - self.sa.query(UserRepoGroupToPerm, Permission, RepoGroup)\ - .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\ - .join((Permission, UserRepoGroupToPerm.permission_id - == Permission.permission_id))\ - .filter(UserRepoGroupToPerm.user_id == uid)\ - .all() - + user_repo_groups_perms = Permission.get_default_group_perms(uid) for perm in user_repo_groups_perms: rg_k = perm.UserRepoGroupToPerm.group.group_name p = perm.Permission.permission_name @@ -655,6 +656,19 @@ p = _choose_perm(p, cur_perm) user.permissions[GK][rg_k] = p + #====================================================================== + # !! PERMISSIONS FOR USER GROUPS !! + #====================================================================== + #user explicit permission for user groups + user_user_groups_perms = Permission.get_default_user_group_perms(uid) + for perm in user_user_groups_perms: + u_k = perm.UserUserGroupToPerm.user_group.users_group_name + p = perm.Permission.permission_name + cur_perm = user.permissions[UK][u_k] + if not explicit: + p = _choose_perm(p, cur_perm) + user.permissions[UK][u_k] = p + return user def has_perm(self, user, perm): diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/model/users_group.py --- a/rhodecode/model/users_group.py Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/model/users_group.py Mon Apr 08 22:47:35 2013 +0200 @@ -29,7 +29,7 @@ from rhodecode.model import BaseModel from rhodecode.model.db import UserGroupMember, UserGroup,\ - UserGroupRepoToPerm, Permission, UserGroupToPerm, User + UserGroupRepoToPerm, Permission, UserGroupToPerm, User, UserUserGroupToPerm from rhodecode.lib.exceptions import UserGroupsAssignedException log = logging.getLogger(__name__) @@ -39,26 +39,74 @@ cls = UserGroup - def __get_users_group(self, users_group): + def _get_user_group(self, users_group): return self._get_instance(UserGroup, users_group, callback=UserGroup.get_by_group_name) + def _create_default_perms(self, user_group): + # create default permission + default_perm = 'usergroup.read' + def_user = User.get_by_username('default') + for p in def_user.user_perms: + if p.permission.permission_name.startswith('usergroup.'): + default_perm = p.permission.permission_name + break + + user_group_to_perm = UserUserGroupToPerm() + user_group_to_perm.permission = Permission.get_by_key(default_perm) + + user_group_to_perm.user_group = user_group + user_group_to_perm.user_id = def_user.user_id + return user_group_to_perm + + def _update_permissions(self, user_group, perms_new=None, + perms_updates=None): + if not perms_new: + perms_new = [] + if not perms_updates: + perms_updates = [] + + # update permissions + for member, perm, member_type in perms_updates: + if member_type == 'user': + # this updates existing one + self.grant_user_permission( + user_group=user_group, user=member, perm=perm + ) + else: + self.grant_users_group_permission( + user_group=user_group, group_name=member, perm=perm + ) + # set new permissions + for member, perm, member_type in perms_new: + if member_type == 'user': + self.grant_user_permission( + user_group=user_group, user=member, perm=perm + ) + else: + self.grant_users_group_permission( + user_group=user_group, group_name=member, perm=perm + ) + def get(self, users_group_id, cache=False): return UserGroup.get(users_group_id) def get_group(self, users_group): - return self.__get_users_group(users_group) + return self._get_user_group(users_group) def get_by_name(self, name, cache=False, case_insensitive=False): return UserGroup.get_by_group_name(name, cache, case_insensitive) - def create(self, name, active=True): + def create(self, name, owner, active=True): try: - new = UserGroup() - new.users_group_name = name - new.users_group_active = active - self.sa.add(new) - return new + new_user_group = UserGroup() + new_user_group.user = self._get_user(owner) + new_user_group.users_group_name = name + new_user_group.users_group_active = active + self.sa.add(new_user_group) + perm_obj = self._create_default_perms(new_user_group) + self.sa.add(perm_obj) + return new_user_group except Exception: log.error(traceback.format_exc()) raise @@ -66,7 +114,7 @@ def update(self, users_group, form_data): try: - users_group = self.__get_users_group(users_group) + users_group = self._get_user_group(users_group) for k, v in form_data.items(): if k == 'users_group_members': @@ -96,7 +144,7 @@ :param force: """ try: - users_group = self.__get_users_group(users_group) + users_group = self._get_user_group(users_group) # check if this group is not assigned to repo assigned_groups = UserGroupRepoToPerm.query()\ @@ -112,7 +160,7 @@ raise def add_user_to_group(self, users_group, user): - users_group = self.__get_users_group(users_group) + users_group = self._get_user_group(users_group) user = self._get_user(user) for m in users_group.members: @@ -135,7 +183,7 @@ raise def remove_user_from_group(self, users_group, user): - users_group = self.__get_users_group(users_group) + users_group = self._get_user_group(users_group) user = self._get_user(user) users_group_member = None @@ -157,7 +205,7 @@ return False def has_perm(self, users_group, perm): - users_group = self.__get_users_group(users_group) + users_group = self._get_user_group(users_group) perm = self._get_perm(perm) return UserGroupToPerm.query()\ @@ -165,7 +213,7 @@ .filter(UserGroupToPerm.permission == perm).scalar() is not None def grant_perm(self, users_group, perm): - users_group = self.__get_users_group(users_group) + users_group = self._get_user_group(users_group) perm = self._get_perm(perm) # if this permission is already granted skip it @@ -182,7 +230,7 @@ self.sa.add(new) def revoke_perm(self, users_group, perm): - users_group = self.__get_users_group(users_group) + users_group = self._get_user_group(users_group) perm = self._get_perm(perm) obj = UserGroupToPerm.query()\ @@ -190,3 +238,58 @@ .filter(UserGroupToPerm.permission == perm).scalar() if obj: self.sa.delete(obj) + + def grant_user_permission(self, user_group, user, perm): + """ + Grant permission for user on given user group, or update + existing one if found + + :param user_group: Instance of UserGroup, users_group_id, + or users_group_name + :param user: Instance of User, user_id or username + :param perm: Instance of Permission, or permission_name + """ + + user_group = self._get_user_group(user_group) + user = self._get_user(user) + permission = self._get_perm(perm) + + # check if we have that permission already + obj = self.sa.query(UserUserGroupToPerm)\ + .filter(UserUserGroupToPerm.user == user)\ + .filter(UserUserGroupToPerm.user_group == user_group)\ + .scalar() + if obj is None: + # create new ! + obj = UserUserGroupToPerm() + obj.user_group = user_group + obj.user = user + obj.permission = permission + self.sa.add(obj) + log.debug('Granted perm %s to %s on %s' % (perm, user, user_group)) + + def revoke_user_permission(self, user_group, user): + """ + Revoke permission for user on given repository group + + :param user_group: Instance of ReposGroup, repositories_group_id, + or repositories_group name + :param user: Instance of User, user_id or username + """ + + user_group = self._get_user_group(user_group) + user = self._get_user(user) + + obj = self.sa.query(UserUserGroupToPerm)\ + .filter(UserUserGroupToPerm.user == user)\ + .filter(UserUserGroupToPerm.user_group == user_group)\ + .scalar() + if obj: + self.sa.delete(obj) + log.debug('Revoked perm on %s on %s' % (user_group, user)) + + def grant_users_group_permission(self, user_group, group_name, perm): + raise NotImplementedError() + + def revoke_users_group_permission(self, user_group, group_name): + raise NotImplementedError() diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/model/validators.py --- a/rhodecode/model/validators.py Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/model/validators.py Mon Apr 08 22:47:35 2013 +0200 @@ -546,10 +546,12 @@ def ValidPerms(type_='repo'): - if type_ == 'group': + if type_ == 'repo_group': EMPTY_PERM = 'group.none' elif type_ == 'repo': EMPTY_PERM = 'repository.none' + elif type_ == 'user_group': + EMPTY_PERM = 'usergroup.none' class _validator(formencode.validators.FancyValidator): messages = { diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/templates/admin/repos/repo_edit_perms.html --- a/rhodecode/templates/admin/repos/repo_edit_perms.html Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/templates/admin/repos/repo_edit_perms.html Mon Apr 08 22:47:35 2013 +0200 @@ -29,7 +29,7 @@ %if r2p.user.username !='default': - + ${_('revoke')} %endif @@ -54,7 +54,7 @@ %endif - + ${_('revoke')} @@ -86,35 +86,29 @@ diff -r e45f8cefd7d9 -r 7e3d89d9d3a2 rhodecode/templates/admin/users_groups/users_group_edit.html --- a/rhodecode/templates/admin/users_groups/users_group_edit.html Mon Apr 08 20:38:37 2013 +0200 +++ b/rhodecode/templates/admin/users_groups/users_group_edit.html Mon Apr 08 22:47:35 2013 +0200 @@ -18,7 +18,7 @@ <%def name="main()"> -
+
${self.breadcrumbs()} @@ -92,12 +92,54 @@
${h.end_form()} +
+ % if c.group_members_obj: +
    + %for user in c.group_members_obj: +
  • +
    +
    gravatar
    +
    ${h.link_to(user.username, h.url('edit_user',id=user.user_id))}
    +
    ${user.full_name}
    +
    +
  • + %endfor +
+ %else: + ${_('No members yet')} + %endif +
+
+ + +
+
+
${_('Permissions')}
+
+ ${h.form(url('set_user_group_perm_member', id=c.users_group.users_group_id),method='post')} +
+
+
+
+ +
+
+ <%include file="user_group_edit_perms.html"/> +
+
+
+ ${h.submit('save',_('Save'),class_="ui-btn large")} + ${h.reset('reset',_('Reset'),class_="ui-btn large")} +
+
+
+ ${h.end_form()}
-
${_('Permissions')}
+
${_('Permissions summary')}
${h.form(url('users_group_perm', id=c.users_group.users_group_id), method='put')}
@@ -138,35 +180,6 @@
${h.end_form()} - - ## permissions overview - <%namespace name="p" file="/base/perms_summary.html"/> - ${p.perms_summary(c.users_group.permissions)} - - -
- -
-
${_('Group members')}
-
- -
- % if c.group_members_obj: -
    - %for user in c.group_members_obj: -
  • -
    -
    gravatar
    -
    ${h.link_to(user.username, h.url('edit_user',id=user.user_id))}
    -
    ${user.full_name}
    -
    -
  • - %endfor -
- %else: - ${_('No members yet')} - %endif -