Mercurial > kallithea
changeset 1982:87f0800abc7b beta
#227 Initial version of repository groups permissions system
- implemented none/read/write/admin permissions for groups
- wrote more tests for permissions, and new permissions groups
- a lot of code garden, splitted logic into proper models
- permissions on groups doesn't propagate yet to repositories
- deprecated some methods on api for managing permissions on
repositories for users, and users groups
line wrap: on
line diff
--- a/README.rst Sun Feb 05 21:45:15 2012 +0200 +++ b/README.rst Sat Jan 28 01:06:29 2012 +0200 @@ -3,9 +3,9 @@ ======================== ``RhodeCode`` is a fast and powerful management tool for Mercurial_ and GIT_ -with a built in push/pull server and full text search. +with a built in push/pull server and full text search and code-review. It works on http/https and has a built in permission/authentication system with -the ability to authenticate via LDAP or ActiveDirectory. RhodeCode also supports +the ability to authenticate via LDAP or ActiveDirectory. RhodeCode also provides simple API so it's easy integrable with existing external systems. RhodeCode is similar in some respects to github or bitbucket_,
--- a/docs/api/api.rst Sun Feb 05 21:45:15 2012 +0200 +++ b/docs/api/api.rst Sat Jan 28 01:06:29 2012 +0200 @@ -91,6 +91,7 @@ This command can be executed only using api_key belonging to user with admin rights. + INPUT:: api_key : "<api_key>" @@ -122,6 +123,7 @@ Lists all existing users. This command can be executed only using api_key belonging to user with admin rights. + INPUT:: api_key : "<api_key>" @@ -145,12 +147,14 @@ ] error: null + create_user ----------- Creates new user or updates current one if such user exists. This command can be executed only using api_key belonging to user with admin rights. + INPUT:: api_key : "<api_key>" @@ -174,12 +178,14 @@ } error: null + get_users_group --------------- Gets an existing users group. This command can be executed only using api_key belonging to user with admin rights. + INPUT:: api_key : "<api_key>" @@ -210,12 +216,14 @@ } error : null + get_users_groups ---------------- Lists all existing users groups. This command can be executed only using api_key belonging to user with admin rights. + INPUT:: api_key : "<api_key>" @@ -253,6 +261,7 @@ Creates new users group. This command can be executed only using api_key belonging to user with admin rights + INPUT:: api_key : "<api_key>" @@ -270,12 +279,14 @@ } error: null + add_user_to_users_group ----------------------- Adds a user to a users group. This command can be executed only using api_key belonging to user with admin rights + INPUT:: api_key : "<api_key>" @@ -293,12 +304,14 @@ } error: null + get_repo -------- Gets an existing repository. This command can be executed only using api_key belonging to user with admin rights + INPUT:: api_key : "<api_key>" @@ -338,12 +351,14 @@ } error: null + get_repos --------- Lists all existing repositories. This command can be executed only using api_key belonging to user with admin rights + INPUT:: api_key : "<api_key>" @@ -372,6 +387,7 @@ `dirs`. This command can be executed only using api_key belonging to user with admin rights + INPUT:: api_key : "<api_key>" @@ -395,7 +411,6 @@ error: null - create_repo ----------- @@ -405,6 +420,7 @@ For example "foo/bar/baz" will create groups "foo", "bar" (with "foo" as parent), and create "baz" repository with "bar" as group. + INPUT:: api_key : "<api_key>" @@ -420,54 +436,106 @@ OUTPUT:: result: { - "id": "<newrepoid>", - "msg": "Created new repository <reponame>", + "id": "<newrepoid>", + "msg": "Created new repository <reponame>", } error: null -add_user_to_repo ----------------- + +grant_user_permission +--------------------- -Add a user to a repository. This command can be executed only using api_key -belonging to user with admin rights. -If "perm" is None, user will be removed from the repository. +Grant permission for user on given repository, or update existing one +if found. This command can be executed only using api_key belonging to user +with admin rights. + INPUT:: api_key : "<api_key>" - method : "add_user_to_repo" + method : "grant_user_permission" args: { "repo_name" : "<reponame>", "username" : "<username>", - "perm" : "(None|repository.(read|write|admin))", + "perm" : "(repository.(none|read|write|admin))", + } + +OUTPUT:: + + result: { + "msg" : "Granted perm: <perm> for user: <username> in repo: <reponame>" + } + error: null + + +revoke_user_permission +---------------------- + +Revoke permission for user on given repository. This command can be executed +only using api_key belonging to user with admin rights. + + +INPUT:: + + api_key : "<api_key>" + method : "revoke_user_permission" + args: { + "repo_name" : "<reponame>", + "username" : "<username>", } OUTPUT:: result: { - "msg" : "Added perm: <perm> for <username> in repo: <reponame>" + "msg" : "Revoked perm for user: <suername> in repo: <reponame>" } error: null -add_users_group_to_repo ------------------------ + +grant_users_group_permission +---------------------------- -Add a users group to a repository. This command can be executed only using -api_key belonging to user with admin rights. If "perm" is None, group will -be removed from the repository. +Grant permission for users group on given repository, or update +existing one if found. This command can be executed only using +api_key belonging to user with admin rights. + INPUT:: api_key : "<api_key>" - method : "add_users_group_to_repo" + method : "grant_users_group_permission" + args: { + "repo_name" : "<reponame>", + "group_name" : "<usersgroupname>", + "perm" : "(repository.(none|read|write|admin))", + } + +OUTPUT:: + + result: { + "msg" : "Granted perm: <perm> for group: <usersgroupname> in repo: <reponame>" + } + error: null + + +revoke_users_group_permission +----------------------------- + +Revoke permission for users group on given repository.This command can be +executed only using api_key belonging to user with admin rights. + +INPUT:: + + api_key : "<api_key>" + method : "revoke_users_group_permission" args: { "repo_name" : "<reponame>", - "group_name" : "<groupname>", - "perm" : "(None|repository.(read|write|admin))", + "users_group" : "<usersgroupname>", } + OUTPUT:: - + result: { - "msg" : Added perm: <perm> for <groupname> in repo: <reponame>" + "msg" : "Revoked perm for group: <usersgroupname> in repo: <reponame>" } - + error: null \ No newline at end of file
--- a/rhodecode/config/middleware.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/config/middleware.py Sat Jan 28 01:06:29 2012 +0200 @@ -51,8 +51,8 @@ from rhodecode.lib.profiler import ProfilingMiddleware app = ProfilingMiddleware(app) + if asbool(full_stack): - if asbool(full_stack): # Handle Python exceptions app = ErrorHandler(app, global_conf, **config['pylons.errorware']) @@ -80,7 +80,6 @@ app = Cascade([static_app, app]) app = make_gzip_middleware(app, global_conf, compress_level=1) - app.config = config return app
--- a/rhodecode/config/routing.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/config/routing.py Sat Jan 28 01:06:29 2012 +0200 @@ -113,8 +113,9 @@ function=check_repo)) #ajax delete repo perm user m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}", - action="delete_perm_user", conditions=dict(method=["DELETE"], - function=check_repo)) + action="delete_perm_user", + conditions=dict(method=["DELETE"], function=check_repo)) + #ajax delete repo perm users_group m.connect('delete_repo_users_group', "/repos_delete_users_group/{repo_name:.*}", @@ -128,7 +129,7 @@ m.connect('repo_cache', "/repos_cache/{repo_name:.*}", action="repo_cache", conditions=dict(method=["DELETE"], function=check_repo)) - m.connect('repo_public_journal',"/repos_public_journal/{repo_name:.*}", + m.connect('repo_public_journal', "/repos_public_journal/{repo_name:.*}", action="repo_public_journal", conditions=dict(method=["PUT"], function=check_repo)) m.connect('repo_pull', "/repo_pull/{repo_name:.*}", @@ -169,6 +170,17 @@ m.connect("formatted_repos_group", "/repos_groups/{id}.{format}", action="show", conditions=dict(method=["GET"], function=check_int)) + # ajax delete repos group perm user + m.connect('delete_repos_group_user_perm', + "/delete_repos_group_user_perm/{group_name:.*}", + action="delete_repos_group_user_perm", + conditions=dict(method=["DELETE"], function=check_group)) + + # ajax delete repos group perm users_group + m.connect('delete_repos_group_users_group_perm', + "/delete_repos_group_users_group_perm/{group_name:.*}", + action="delete_repos_group_users_group_perm", + conditions=dict(method=["DELETE"], function=check_group)) #ADMIN USER REST ROUTES with rmap.submapper(path_prefix=ADMIN_PREFIX, @@ -310,8 +322,6 @@ m.connect("formatted_notification", "/notifications/{notification_id}.{format}", action="show", conditions=dict(method=["GET"])) - - #ADMIN MAIN PAGES with rmap.submapper(path_prefix=ADMIN_PREFIX, controller='admin/admin') as m: @@ -320,13 +330,12 @@ action='add_repo') #========================================================================== - # API V1 + # API V2 #========================================================================== with rmap.submapper(path_prefix=ADMIN_PREFIX, controller='api/api') as m: m.connect('api', '/api') - #USER JOURNAL rmap.connect('journal', '%s/journal' % ADMIN_PREFIX, controller='journal') @@ -388,11 +397,13 @@ controller='changeset', revision='tip', conditions=dict(function=check_repo)) - rmap.connect('changeset_comment', '/{repo_name:.*}/changeset/{revision}/comment', + rmap.connect('changeset_comment', + '/{repo_name:.*}/changeset/{revision}/comment', controller='changeset', revision='tip', action='comment', conditions=dict(function=check_repo)) - rmap.connect('changeset_comment_delete', '/{repo_name:.*}/changeset/comment/{comment_id}/delete', + rmap.connect('changeset_comment_delete', + '/{repo_name:.*}/changeset/comment/{comment_id}/delete', controller='changeset', action='delete_comment', conditions=dict(function=check_repo, method=["DELETE"])) @@ -493,5 +504,4 @@ controller='followers', action='followers', conditions=dict(function=check_repo)) - return rmap
--- a/rhodecode/controllers/admin/repos.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/controllers/admin/repos.py Sat Jan 28 01:06:29 2012 +0200 @@ -3,7 +3,7 @@ rhodecode.controllers.admin.repos ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Admin controller for RhodeCode + Repositories controller for RhodeCode :created_on: Apr 7, 2010 :author: marcink @@ -277,7 +277,6 @@ return redirect(url('repos')) - @HasRepoPermissionAllDecorator('repository.admin') def delete_perm_user(self, repo_name): """ @@ -287,10 +286,11 @@ """ try: - repo_model = RepoModel() - repo_model.delete_perm_user(request.POST, repo_name) + RepoModel().revoke_user_permission(repo=repo_name, + user=request.POST['user_id']) Session.commit() - except Exception, e: + except Exception: + log.error(traceback.format_exc()) h.flash(_('An error occurred during deletion of repository user'), category='error') raise HTTPInternalServerError() @@ -302,11 +302,14 @@ :param repo_name: """ + try: - repo_model = RepoModel() - repo_model.delete_perm_users_group(request.POST, repo_name) + RepoModel().revoke_users_group_permission( + repo=repo_name, group_name=request.POST['users_group_id'] + ) Session.commit() - except Exception, e: + except Exception: + log.error(traceback.format_exc()) h.flash(_('An error occurred during deletion of repository' ' users groups'), category='error') @@ -321,8 +324,7 @@ """ try: - repo_model = RepoModel() - repo_model.delete_stats(repo_name) + RepoModel().delete_stats(repo_name) Session.commit() except Exception, e: h.flash(_('An error occurred during deletion of repository stats'),
--- a/rhodecode/controllers/admin/repos_groups.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/controllers/admin/repos_groups.py Sat Jan 28 01:06:29 2012 +0200 @@ -3,7 +3,7 @@ rhodecode.controllers.admin.repos_groups ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - repos groups controller for RhodeCode + Repositories groups controller for RhodeCode :created_on: Mar 23, 2010 :author: marcink @@ -29,19 +29,22 @@ from formencode import htmlfill -from pylons import request, response, session, tmpl_context as c, url -from pylons.controllers.util import abort, redirect +from pylons import request, tmpl_context as c, url +from pylons.controllers.util import redirect from pylons.i18n.translation import _ from sqlalchemy.exc import IntegrityError from rhodecode.lib import helpers as h -from rhodecode.lib.auth import LoginRequired, HasPermissionAnyDecorator +from rhodecode.lib.auth import LoginRequired, HasPermissionAnyDecorator,\ + HasReposGroupPermissionAnyDecorator from rhodecode.lib.base import BaseController, render from rhodecode.model.db import RepoGroup from rhodecode.model.repos_group import ReposGroupModel from rhodecode.model.forms import ReposGroupForm from rhodecode.model.meta import Session +from rhodecode.model.repo import RepoModel +from webob.exc import HTTPInternalServerError log = logging.getLogger(__name__) @@ -60,6 +63,10 @@ c.repo_groups = RepoGroup.groups_choices() c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups) + repo_model = RepoModel() + c.users_array = repo_model.get_users_js() + c.users_groups_array = repo_model.get_users_groups_js() + def __load_data(self, group_id): """ Load defaults settings for edit, and update @@ -74,13 +81,22 @@ data['group_name'] = repo_group.name + # fill repository 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 + for p in repo_group.users_group_to_perm: + data.update({'g_perm_%s' % p.users_group.users_group_name: + p.permission.permission_name}) + return data @HasPermissionAnyDecorator('hg.admin') def index(self, format='html'): """GET /repos_groups: All items in the collection""" # url('repos_groups') - sk = lambda g: g.parents[0].group_name if g.parents else g.group_name c.groups = sorted(RepoGroup.query().all(), key=sk) return render('admin/repos_groups/repos_groups_show.html') @@ -94,7 +110,11 @@ c.repo_groups_choices)() try: form_result = repos_group_form.to_python(dict(request.POST)) - ReposGroupModel().create(form_result) + ReposGroupModel().create( + group_name=form_result['group_name'], + group_description=form_result['group_description'], + parent=form_result['group_parent_id'] + ) Session.commit() h.flash(_('created repos group %s') \ % form_result['group_name'], category='success') @@ -134,10 +154,11 @@ self.__load_defaults() c.repos_group = RepoGroup.get(id) - repos_group_form = ReposGroupForm(edit=True, - old_data=c.repos_group.get_dict(), - available_groups= - c.repo_groups_choices)() + repos_group_form = ReposGroupForm( + edit=True, + old_data=c.repos_group.get_dict(), + available_groups=c.repo_groups_choices + )() try: form_result = repos_group_form.to_python(dict(request.POST)) ReposGroupModel().update(id, form_result) @@ -201,10 +222,52 @@ return redirect(url('repos_groups')) + @HasReposGroupPermissionAnyDecorator('group.admin') + def delete_repos_group_user_perm(self, group_name): + """ + DELETE an existing repositories group permission user + + :param group_name: + """ + + try: + ReposGroupModel().revoke_user_permission( + repos_group=group_name, user=request.POST['user_id'] + ) + Session.commit() + except Exception: + log.error(traceback.format_exc()) + h.flash(_('An error occurred during deletion of group user'), + category='error') + raise HTTPInternalServerError() + + @HasReposGroupPermissionAnyDecorator('group.admin') + def delete_repos_group_users_group_perm(self, group_name): + """ + DELETE an existing repositories group permission users group + + :param group_name: + """ + + try: + ReposGroupModel().revoke_users_group_permission( + repos_group=group_name, + group_name=request.POST['users_group_id'] + ) + Session.commit() + except Exception: + log.error(traceback.format_exc()) + h.flash(_('An error occurred during deletion of group' + ' users groups'), + category='error') + raise HTTPInternalServerError() + def show_by_name(self, group_name): id_ = RepoGroup.get_by_group_name(group_name).group_id return self.show(id_) + @HasReposGroupPermissionAnyDecorator('group.read', 'group.write', + 'group.admin') def show(self, id, format='html'): """GET /repos_groups/id: Show a specific item""" # url('repos_group', id=ID) @@ -240,7 +303,7 @@ defaults = self.__load_data(id_) # we need to exclude this group from the group list for editing - c.repo_groups = filter(lambda x:x[0] != id_, c.repo_groups) + c.repo_groups = filter(lambda x: x[0] != id_, c.repo_groups) return htmlfill.render( render('admin/repos_groups/repos_groups_edit.html'),
--- a/rhodecode/controllers/api/api.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/controllers/api/api.py Sat Jan 28 01:06:29 2012 +0200 @@ -401,13 +401,7 @@ for g in groups: group = RepoGroup.get_by_group_name(g) if not group: - group = ReposGroupModel().create( - dict( - group_name=g, - group_description='', - group_parent_id=parent_id - ) - ) + group = ReposGroupModel().create(g, '', parent_id) parent_id = group.group_id repo = RepoModel().create( @@ -434,11 +428,11 @@ raise JSONRPCError('failed to create repository %s' % repo_name) @HasPermissionAnyDecorator('hg.admin') - def add_user_to_repo(self, apiuser, repo_name, username, perm): + def grant_user_permission(self, repo_name, username, perm): """ - Add permission for a user to a repository + Grant permission for user on given repository, or update existing one + if found - :param apiuser: :param repo_name: :param username: :param perm: @@ -449,17 +443,15 @@ if repo is None: raise JSONRPCError('unknown repository %s' % repo) - try: - user = User.get_by_username(username) - except NoResultFound: - raise JSONRPCError('unknown user %s' % user) + user = User.get_by_username(username) + if user is None: + raise JSONRPCError('unknown user %s' % username) - RepositoryPermissionModel()\ - .update_or_delete_user_permission(repo, user, perm) + RepoModel().grant_user_permission(repo=repo, user=user, perm=perm) + Session.commit() - return dict( - msg='Added perm: %s for %s in repo: %s' % ( + msg='Granted perm: %s for user: %s in repo: %s' % ( perm, username, repo_name ) ) @@ -472,11 +464,45 @@ ) @HasPermissionAnyDecorator('hg.admin') - def add_users_group_to_repo(self, apiuser, repo_name, group_name, perm): + def revoke_user_permission(self, repo_name, username): + """ + Revoke permission for user on given repository + + :param repo_name: + :param username: """ - Add permission for a users group to a repository + + try: + repo = Repository.get_by_repo_name(repo_name) + if repo is None: + raise JSONRPCError('unknown repository %s' % repo) + + user = User.get_by_username(username) + if user is None: + raise JSONRPCError('unknown user %s' % username) + + RepoModel().revoke_user_permission(repo=repo_name, user=username) - :param apiuser: + Session.commit() + return dict( + msg='Revoked perm for user: %s in repo: %s' % ( + username, repo_name + ) + ) + except Exception: + log.error(traceback.format_exc()) + raise JSONRPCError( + 'failed to edit permission %(repo)s for %(user)s' % dict( + user=username, repo=repo_name + ) + ) + + @HasPermissionAnyDecorator('hg.admin') + def grant_users_group_permission(self, repo_name, group_name, perm): + """ + Grant permission for users group on given repository, or update + existing one if found + :param repo_name: :param group_name: :param perm: @@ -487,24 +513,59 @@ if repo is None: raise JSONRPCError('unknown repository %s' % repo) - try: - user_group = UsersGroup.get_by_group_name(group_name) - except NoResultFound: + user_group = UsersGroup.get_by_group_name(group_name) + if user_group is None: raise JSONRPCError('unknown users group %s' % user_group) - RepositoryPermissionModel()\ - .update_or_delete_users_group_permission(repo, user_group, - perm) + RepoModel().grant_users_group_permission(repo=repo_name, + group_name=group_name, + perm=perm) + Session.commit() return dict( - msg='Added perm: %s for %s in repo: %s' % ( + msg='Granted perm: %s for group: %s in repo: %s' % ( perm, group_name, repo_name ) ) except Exception: log.error(traceback.format_exc()) raise JSONRPCError( - 'failed to edit permission %(repo)s for %(usergr)s' % dict( - usergr=group_name, repo=repo_name + 'failed to edit permission %(repo)s for %(usersgr)s' % dict( + usersgr=group_name, repo=repo_name ) ) + + @HasPermissionAnyDecorator('hg.admin') + def revoke_users_group_permission(self, repo_name, group_name): + """ + Revoke permission for users group on given repository + + :param repo_name: + :param group_name: + """ + + try: + repo = Repository.get_by_repo_name(repo_name) + if repo is None: + raise JSONRPCError('unknown repository %s' % repo) + + user_group = UsersGroup.get_by_group_name(group_name) + if user_group is None: + raise JSONRPCError('unknown users group %s' % user_group) + + RepoModel().revoke_users_group_permission(repo=repo_name, + group_name=group_name) + + Session.commit() + return dict( + msg='Revoked perm for group: %s in repo: %s' % ( + group_name, repo_name + ) + ) + except Exception: + log.error(traceback.format_exc()) + raise JSONRPCError( + 'failed to edit permission %(repo)s for %(usersgr)s' % dict( + usersgr=group_name, repo=repo_name + ) + )
--- a/rhodecode/controllers/home.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/controllers/home.py Sat Jan 28 01:06:29 2012 +0200 @@ -30,7 +30,7 @@ from rhodecode.lib.auth import LoginRequired from rhodecode.lib.base import BaseController, render -from rhodecode.model.db import RepoGroup, Repository +from rhodecode.model.db import Repository log = logging.getLogger(__name__) @@ -42,11 +42,8 @@ super(HomeController, self).__before__() def index(self): - c.repos_list = self.scm_model.get_repos() - - c.groups = RepoGroup.query()\ - .filter(RepoGroup.group_parent_id == None).all() + c.groups = self.scm_model.get_repos_groups() return render('/index.html')
--- a/rhodecode/lib/__init__.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/lib/__init__.py Sat Jan 28 01:06:29 2012 +0200 @@ -25,6 +25,8 @@ import os import re +from vcs.utils.lazy import LazyProperty + def __get_lem(): from pygments import lexers @@ -213,6 +215,7 @@ except (ImportError, UnicodeDecodeError, Exception): return unicode(str_, from_encoding, 'replace') + def safe_str(unicode_, to_encoding='utf8'): """ safe str function. Does few trick to turn unicode_ into string @@ -250,7 +253,6 @@ return safe_str - def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs): """ Custom engine_from_config functions that makes sure we use NullPool for @@ -393,6 +395,7 @@ return ''.join(uri) + def get_changeset_safe(repo, rev): """ Safe version of get_changeset if this changeset doesn't exists for a @@ -437,6 +440,7 @@ "was: %s" % err) return None + def extract_mentioned_users(s): """ Returns unique usernames from given string s that have @mention
--- a/rhodecode/lib/auth.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/lib/auth.py Sat Jan 28 01:06:29 2012 +0200 @@ -31,7 +31,7 @@ from tempfile import _RandomNameSequence from decorator import decorator -from pylons import config, session, url, request +from pylons import config, url, request from pylons.controllers.util import abort, redirect from pylons.i18n.translation import _ @@ -45,7 +45,7 @@ from rhodecode.lib import str2bool, safe_unicode from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError -from rhodecode.lib.utils import get_repo_slug +from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug from rhodecode.lib.auth_ldap import AuthLdap from rhodecode.model import meta @@ -80,8 +80,8 @@ def __init__(self, passwd=''): self.passwd = passwd - def gen_password(self, len, type): - self.passwd = ''.join([random.choice(type) for _ in xrange(len)]) + def gen_password(self, length, type_): + self.passwd = ''.join([random.choice(type_) for _ in xrange(length)]) return self.passwd @@ -575,6 +575,41 @@ return False +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 + """ + + def check_permissions(self): + group_name = get_repos_group_slug(request) + try: + user_perms = set([self.user_perms['repositories_groups'][group_name]]) + except KeyError: + return False + if self.required_perms.issubset(user_perms): + return True + return False + + +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 + """ + + def check_permissions(self): + group_name = get_repos_group_slug(request) + + try: + user_perms = set([self.user_perms['repositories_groups'][group_name]]) + except KeyError: + return False + if self.required_perms.intersection(user_perms): + return True + return False + + #============================================================================== # CHECK FUNCTIONS #============================================================================== @@ -641,8 +676,9 @@ self.repo_name = get_repo_slug(request) try: - self.user_perms = set([self.user_perms['reposit' - 'ories'][self.repo_name]]) + self.user_perms = set( + [self.user_perms['repositories'][self.repo_name]] + ) except KeyError: return False self.granted_for = self.repo_name @@ -662,8 +698,9 @@ self.repo_name = get_repo_slug(request) try: - self.user_perms = set([self.user_perms['reposi' - 'tories'][self.repo_name]]) + self.user_perms = set( + [self.user_perms['repositories'][self.repo_name]] + ) except KeyError: return False self.granted_for = self.repo_name @@ -672,6 +709,42 @@ return False +class HasReposGroupPermissionAny(PermsFunction): + def __call__(self, group_name=None, check_Location=''): + self.group_name = group_name + return super(HasReposGroupPermissionAny, self).__call__(check_Location) + + def check_permissions(self): + try: + self.user_perms = set( + [self.user_perms['repositories_groups'][self.group_name]] + ) + except KeyError: + return False + self.granted_for = self.repo_name + if self.required_perms.intersection(self.user_perms): + return True + return False + + +class HasReposGroupPermissionAll(PermsFunction): + def __call__(self, group_name=None, check_Location=''): + self.group_name = group_name + return super(HasReposGroupPermissionAny, self).__call__(check_Location) + + def check_permissions(self): + try: + self.user_perms = set( + [self.user_perms['repositories_groups'][self.group_name]] + ) + except KeyError: + return False + self.granted_for = self.repo_name + if self.required_perms.issubset(self.user_perms): + return True + return False + + #============================================================================== # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH #==============================================================================
--- a/rhodecode/lib/db_manage.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/lib/db_manage.py Sat Jan 28 01:06:29 2012 +0200 @@ -442,23 +442,28 @@ def create_permissions(self): # module.(access|create|change|delete)_[name] - # module.(read|write|owner) - perms = [('repository.none', 'Repository no access'), - ('repository.read', 'Repository read access'), - ('repository.write', 'Repository write access'), - ('repository.admin', 'Repository admin access'), - ('hg.admin', 'Hg Administrator'), - ('hg.create.repository', 'Repository create'), - ('hg.create.none', 'Repository creation disabled'), - ('hg.register.none', 'Register disabled'), - ('hg.register.manual_activate', 'Register new user with ' - 'RhodeCode without manual' - 'activation'), + # module.(none|read|write|admin) + perms = [ + ('repository.none', 'Repository no access'), + ('repository.read', 'Repository read access'), + ('repository.write', 'Repository write access'), + ('repository.admin', 'Repository admin access'), - ('hg.register.auto_activate', 'Register new user with ' - 'RhodeCode without auto ' - 'activation'), - ] + ('group.none', 'Repositories Group no access'), + ('group.read', 'Repositories Group read access'), + ('group.write', 'Repositories Group write access'), + ('group.admin', 'Repositories Group admin access'), + + ('hg.admin', 'Hg Administrator'), + ('hg.create.repository', 'Repository create'), + ('hg.create.none', 'Repository creation disabled'), + ('hg.register.none', 'Register disabled'), + ('hg.register.manual_activate', 'Register new user with RhodeCode ' + 'without manual activation'), + + ('hg.register.auto_activate', 'Register new user with RhodeCode ' + 'without auto activation'), + ] for p in perms: new_perm = Permission()
--- a/rhodecode/lib/hooks.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/lib/hooks.py Sat Jan 28 01:06:29 2012 +0200 @@ -130,7 +130,7 @@ Post create repository Hook. This is a dummy function for admins to re-use if needed - :param repository: dict dump of repository object + :param repository: dict dump of repository object :param created_by: username who created repository :param created_date: date of creation @@ -152,4 +152,4 @@ """ - return 0 \ No newline at end of file + return 0
--- a/rhodecode/lib/utils.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/lib/utils.py Sat Jan 28 01:06:29 2012 +0200 @@ -52,6 +52,7 @@ from rhodecode.model.db import Repository, User, RhodeCodeUi, \ UserLog, RepoGroup, RhodeCodeSetting from rhodecode.model.meta import Session +from rhodecode.model.repos_group import ReposGroupModel log = logging.getLogger(__name__) @@ -94,6 +95,10 @@ return request.environ['pylons.routes_dict'].get('repo_name') +def get_repos_group_slug(request): + return request.environ['pylons.routes_dict'].get('group_name') + + def action_logger(user, action, repo, ipaddr='', sa=None, commit=False): """ Action logger for various actions made by users @@ -197,6 +202,7 @@ except VCSError: return False + def is_valid_repos_group(repos_group_name, base_path): """ Returns True if given path is a repos group False otherwise @@ -216,6 +222,7 @@ return False + def ask_ok(prompt, retries=4, complaint='Yes or no, please!'): while True: ok = raw_input(prompt) @@ -317,7 +324,8 @@ an EmptyChangeset """ - def __init__(self, cs='0' * 40, repo=None, requested_revision=None, alias=None): + def __init__(self, cs='0' * 40, repo=None, requested_revision=None, + alias=None): self._empty_cs = cs self.revision = -1 self.message = '' @@ -368,14 +376,23 @@ # last element is repo in nested groups structure groups = groups[:-1] - + rgm = ReposGroupModel(sa) for lvl, group_name in enumerate(groups): + log.debug('creating group level: %s group_name: %s' % (lvl, group_name)) group_name = '/'.join(groups[:lvl] + [group_name]) - group = sa.query(RepoGroup).filter(RepoGroup.group_name == group_name).scalar() + group = RepoGroup.get_by_group_name(group_name) + desc = '%s group' % group_name + +# # WTF that doesn't work !? +# if group is None: +# group = rgm.create(group_name, desc, parent, just_db=True) +# sa.commit() if group is None: group = RepoGroup(group_name, parent) + group.group_description = desc sa.add(group) + rgm._create_default_perms(group) sa.commit() parent = group return group @@ -404,15 +421,14 @@ log.info('repository %s not found creating default' % name) added.append(name) form_data = { - 'repo_name': name, - 'repo_name_full': name, - 'repo_type': repo.alias, - 'description': repo.description \ - if repo.description != 'unknown' else \ - '%s repository' % name, - 'private': False, - 'group_id': getattr(group, 'group_id', None) - } + 'repo_name': name, + 'repo_name_full': name, + 'repo_type': repo.alias, + 'description': repo.description \ + if repo.description != 'unknown' else '%s repository' % name, + 'private': False, + 'group_id': getattr(group, 'group_id', None) + } rm.create(form_data, user, just_db=True) sa.commit() removed = [] @@ -426,6 +442,7 @@ return added, removed + # set cache regions for beaker so celery can utilise it def add_cache(settings): cache_settings = {'regions': None}
--- a/rhodecode/model/__init__.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/model/__init__.py Sat Jan 28 01:06:29 2012 +0200 @@ -74,12 +74,13 @@ else: self.sa = meta.Session - def _get_instance(self, cls, instance): + def _get_instance(self, cls, instance, callback=None): """ - Get's instance of given cls using some simple lookup mechanism + Get's instance of given cls using some simple lookup mechanism. :param cls: class to fetch :param instance: int or Instance + :param callback: callback to call if all lookups failed """ if isinstance(instance, cls): @@ -88,5 +89,10 @@ return cls.get(instance) else: if instance: - raise Exception('given object must be int or Instance' - ' of %s got %s' % (type(cls), type(instance))) + if callback is None: + raise Exception( + 'given object must be int or Instance of %s got %s, ' + 'no callback provided' % (cls, type(instance)) + ) + else: + return callback(instance)
--- a/rhodecode/model/db.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/model/db.py Sat Jan 28 01:06:29 2012 +0200 @@ -717,6 +717,9 @@ group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None) group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) + repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id') + users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all') + parent_group = relationship('RepoGroup', remote_side=group_id) def __init__(self, group_name='', parent_group=None): @@ -833,8 +836,9 @@ permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None) def __repr__(self): - return "<%s('%s:%s')>" % (self.__class__.__name__, - self.permission_id, self.permission_name) + return "<%s('%s:%s')>" % ( + self.__class__.__name__, self.permission_id, self.permission_name + ) @classmethod def get_by_key(cls, key): @@ -843,9 +847,18 @@ @classmethod def get_default_perms(cls, default_user_id): q = Session.query(UserRepoToPerm, Repository, cls)\ - .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\ - .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\ - .filter(UserRepoToPerm.user_id == default_user_id) + .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\ + .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\ + .filter(UserRepoToPerm.user_id == default_user_id) + + return q.all() + + @classmethod + def get_default_group_perms(cls, default_user_id): + q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\ + .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\ + .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\ + .filter(UserRepoGroupToPerm.user_id == default_user_id) return q.all()
--- a/rhodecode/model/forms.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/model/forms.py Sat Jan 28 01:06:29 2012 +0200 @@ -388,57 +388,66 @@ return _ValidForkType -class ValidPerms(formencode.validators.FancyValidator): - messages = {'perm_new_member_name': _('This username or users group name' - ' is not valid')} +def ValidPerms(type_='repo'): + if type_ == 'group': + EMPTY_PERM = 'group.none' + elif type_ == 'repo': + EMPTY_PERM = 'repository.none' - def to_python(self, value, state): - perms_update = [] - perms_new = [] - #build a list of permission to update and new permission to create - for k, v in value.items(): - #means new added member to permissions - if k.startswith('perm_new_member'): - new_perm = value.get('perm_new_member', False) - new_member = value.get('perm_new_member_name', False) - new_type = value.get('perm_new_member_type') + class _ValidPerms(formencode.validators.FancyValidator): + messages = { + 'perm_new_member_name': + _('This username or users group name is not valid') + } + + def to_python(self, value, state): + perms_update = [] + perms_new = [] + # build a list of permission to update and new permission to create + for k, v in value.items(): + # means new added member to permissions + if k.startswith('perm_new_member'): + new_perm = value.get('perm_new_member', False) + new_member = value.get('perm_new_member_name', False) + new_type = value.get('perm_new_member_type') - if new_member and new_perm: - if (new_member, new_perm, new_type) not in perms_new: - perms_new.append((new_member, new_perm, new_type)) - elif k.startswith('u_perm_') or k.startswith('g_perm_'): - member = k[7:] - t = {'u': 'user', - 'g': 'users_group' - }[k[0]] - if member == 'default': - if value['private']: - #set none for default when updating to private repo - v = 'repository.none' - perms_update.append((member, v, t)) + if new_member and new_perm: + if (new_member, new_perm, new_type) not in perms_new: + perms_new.append((new_member, new_perm, new_type)) + elif k.startswith('u_perm_') or k.startswith('g_perm_'): + member = k[7:] + t = {'u': 'user', + 'g': 'users_group' + }[k[0]] + if member == 'default': + if value.get('private'): + # set none for default when updating to private repo + v = EMPTY_PERM + perms_update.append((member, v, t)) - value['perms_updates'] = perms_update - value['perms_new'] = perms_new + value['perms_updates'] = perms_update + value['perms_new'] = perms_new - #update permissions - for k, v, t in perms_new: - try: - if t is 'user': - self.user_db = User.query()\ - .filter(User.active == True)\ - .filter(User.username == k).one() - if t is 'users_group': - self.user_db = UsersGroup.query()\ - .filter(UsersGroup.users_group_active == True)\ - .filter(UsersGroup.users_group_name == k).one() + # update permissions + for k, v, t in perms_new: + try: + if t is 'user': + self.user_db = User.query()\ + .filter(User.active == True)\ + .filter(User.username == k).one() + if t is 'users_group': + self.user_db = UsersGroup.query()\ + .filter(UsersGroup.users_group_active == True)\ + .filter(UsersGroup.users_group_name == k).one() - except Exception: - msg = self.message('perm_new_member_name', - state=State_obj) - raise formencode.Invalid( - msg, value, state, error_dict={'perm_new_member_name': msg} - ) - return value + except Exception: + msg = self.message('perm_new_member_name', + state=State_obj) + raise formencode.Invalid( + msg, value, state, error_dict={'perm_new_member_name': msg} + ) + return value + return _ValidPerms class ValidSettings(formencode.validators.FancyValidator): @@ -588,7 +597,7 @@ def ReposGroupForm(edit=False, old_data={}, available_groups=[]): class _ReposGroupForm(formencode.Schema): allow_extra_fields = True - filter_extra_fields = True + filter_extra_fields = False group_name = All(UnicodeString(strip=True, min=1, not_empty=True), SlugifyName()) @@ -598,7 +607,7 @@ testValueList=True, if_missing=None, not_empty=False) - chained_validators = [ValidReposGroup(edit, old_data)] + chained_validators = [ValidReposGroup(edit, old_data), ValidPerms('group')] return _ReposGroupForm @@ -649,7 +658,7 @@ #this is repo owner user = All(UnicodeString(not_empty=True), ValidRepoUser) - chained_validators = [ValidRepoName(edit, old_data), ValidPerms] + chained_validators = [ValidRepoName(edit, old_data), ValidPerms()] return _RepoForm @@ -683,7 +692,7 @@ repo_group = OneOf(repo_groups, hideList=True) private = StringBoolean(if_missing=False) - chained_validators = [ValidRepoName(edit, old_data), ValidPerms, + chained_validators = [ValidRepoName(edit, old_data), ValidPerms(), ValidSettings] return _RepoForm
--- a/rhodecode/model/notification.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/model/notification.py Sat Jan 28 01:06:29 2012 +0200 @@ -42,10 +42,7 @@ class NotificationModel(BaseModel): def __get_user(self, user): - if isinstance(user, basestring): - return User.get_by_username(username=user) - else: - return self._get_instance(User, user) + return self._get_instance(User, user, callback=User.get_by_username) def __get_notification(self, notification): if isinstance(notification, Notification):
--- a/rhodecode/model/repo.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/model/repo.py Sat Jan 28 01:06:29 2012 +0200 @@ -28,9 +28,9 @@ import traceback from datetime import datetime -from vcs.utils.lazy import LazyProperty from vcs.backends import get_backend +from rhodecode.lib import LazyProperty from rhodecode.lib import safe_str, safe_unicode from rhodecode.lib.caching_query import FromCache from rhodecode.lib.hooks import log_create_repository @@ -39,11 +39,31 @@ from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \ Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, RepoGroup + log = logging.getLogger(__name__) class RepoModel(BaseModel): + def __get_user(self, user): + return self._get_instance(User, user, callback=User.get_by_username) + + def __get_users_group(self, users_group): + return self._get_instance(UsersGroup, users_group, + callback=UsersGroup.get_by_group_name) + + def __get_repos_group(self, repos_group): + return self._get_instance(RepoGroup, repos_group, + callback=RepoGroup.get_by_group_name) + + def __get_repo(self, repository): + return self._get_instance(Repository, repository, + callback=Repository.get_by_repo_name) + + def __get_perm(self, permission): + return self._get_instance(Permission, permission, + callback=Permission.get_by_key) + @LazyProperty def repos_path(self): """ @@ -138,49 +158,24 @@ # update permissions for member, perm, member_type in form_data['perms_updates']: if member_type == 'user': - _member = User.get_by_username(member) - r2p = self.sa.query(UserRepoToPerm)\ - .filter(UserRepoToPerm.user == _member)\ - .filter(UserRepoToPerm.repository == cur_repo)\ - .one() - - r2p.permission = self.sa.query(Permission)\ - .filter(Permission.permission_name == - perm).scalar() - self.sa.add(r2p) + # this updates existing one + RepoModel().grant_user_permission( + repo=cur_repo, user=member, perm=perm + ) else: - g2p = self.sa.query(UsersGroupRepoToPerm)\ - .filter(UsersGroupRepoToPerm.users_group == - UsersGroup.get_by_group_name(member))\ - .filter(UsersGroupRepoToPerm.repository == - cur_repo).one() - - g2p.permission = self.sa.query(Permission)\ - .filter(Permission.permission_name == - perm).scalar() - self.sa.add(g2p) - + RepoModel().grant_users_group_permission( + repo=cur_repo, group_name=member, perm=perm + ) # set new permissions for member, perm, member_type in form_data['perms_new']: if member_type == 'user': - r2p = UserRepoToPerm() - r2p.repository = cur_repo - r2p.user = User.get_by_username(member) - - r2p.permission = self.sa.query(Permission)\ - .filter(Permission. - permission_name == perm)\ - .scalar() - self.sa.add(r2p) + RepoModel().grant_user_permission( + repo=cur_repo, user=member, perm=perm + ) else: - g2p = UsersGroupRepoToPerm() - g2p.repository = cur_repo - g2p.users_group = UsersGroup.get_by_group_name(member) - g2p.permission = self.sa.query(Permission)\ - .filter(Permission. - permission_name == perm)\ - .scalar() - self.sa.add(g2p) + RepoModel().grant_users_group_permission( + repo=cur_repo, group_name=member, perm=perm + ) # update current repo for k, v in form_data.items(): @@ -314,28 +309,93 @@ log.error(traceback.format_exc()) raise - def delete_perm_user(self, form_data, repo_name): - try: - obj = self.sa.query(UserRepoToPerm)\ - .filter(UserRepoToPerm.repository \ - == self.get_by_repo_name(repo_name))\ - .filter(UserRepoToPerm.user_id == form_data['user_id']).one() - self.sa.delete(obj) - except: - log.error(traceback.format_exc()) - raise + def grant_user_permission(self, repo, user, perm): + """ + Grant permission for user on given repository, or update existing one + if found + + :param repo: Instance of Repository, repository_id, or repository name + :param user: Instance of User, user_id or username + :param perm: Instance of Permission, or permission_name + """ + user = self.__get_user(user) + repo = self.__get_repo(repo) + permission = self.__get_perm(perm) + + # check if we have that permission already + obj = self.sa.query(UserRepoToPerm)\ + .filter(UserRepoToPerm.user == user)\ + .filter(UserRepoToPerm.repository == repo)\ + .scalar() + if obj is None: + # create new ! + obj = UserRepoToPerm() + obj.repository = repo + obj.user = user + obj.permission = permission + self.sa.add(obj) + + def revoke_user_permission(self, repo, user): + """ + Revoke permission for user on given repository + + :param repo: Instance of Repository, repository_id, or repository name + :param user: Instance of User, user_id or username + """ + user = self.__get_user(user) + repo = self.__get_repo(repo) + + obj = self.sa.query(UserRepoToPerm)\ + .filter(UserRepoToPerm.repository == repo)\ + .filter(UserRepoToPerm.user == user)\ + .one() + self.sa.delete(obj) - def delete_perm_users_group(self, form_data, repo_name): - try: - obj = self.sa.query(UsersGroupRepoToPerm)\ - .filter(UsersGroupRepoToPerm.repository \ - == self.get_by_repo_name(repo_name))\ - .filter(UsersGroupRepoToPerm.users_group_id - == form_data['users_group_id']).one() - self.sa.delete(obj) - except: - log.error(traceback.format_exc()) - raise + def grant_users_group_permission(self, repo, group_name, perm): + """ + Grant permission for users group on given repository, or update + existing one if found + + :param repo: Instance of Repository, repository_id, or repository name + :param group_name: Instance of UserGroup, users_group_id, + or users group name + :param perm: Instance of Permission, or permission_name + """ + repo = self.__get_repo(repo) + group_name = self.__get_users_group(group_name) + permission = self.__get_perm(perm) + + # check if we have that permission already + obj = self.sa.query(UsersGroupRepoToPerm)\ + .filter(UsersGroupRepoToPerm.users_group == group_name)\ + .filter(UsersGroupRepoToPerm.repository == repo)\ + .scalar() + + if obj is None: + # create new + obj = UsersGroupRepoToPerm() + + obj.repository = repo + obj.users_group = group_name + obj.permission = permission + self.sa.add(obj) + + def revoke_users_group_permission(self, repo, group_name): + """ + Revoke permission for users group on given repository + + :param repo: Instance of Repository, repository_id, or repository name + :param group_name: Instance of UserGroup, users_group_id, + or users group name + """ + repo = self.__get_repo(repo) + group_name = self.__get_users_group(group_name) + + obj = self.sa.query(UsersGroupRepoToPerm)\ + .filter(UsersGroupRepoToPerm.repository == repo)\ + .filter(UsersGroupRepoToPerm.users_group == group_name)\ + .one() + self.sa.delete(obj) def delete_stats(self, repo_name): """ @@ -345,8 +405,9 @@ """ try: obj = self.sa.query(Statistics)\ - .filter(Statistics.repository == \ - self.get_by_repo_name(repo_name)).one() + .filter(Statistics.repository == + self.get_by_repo_name(repo_name))\ + .one() self.sa.delete(obj) except: log.error(traceback.format_exc()) @@ -373,10 +434,9 @@ new_parent_path = '' # we need to make it str for mercurial - repo_path = os.path.join(*map(lambda x:safe_str(x), + repo_path = os.path.join(*map(lambda x: safe_str(x), [self.repos_path, new_parent_path, repo_name])) - # check if this path is not a repository if is_valid_repo(repo_path, self.repos_path): raise Exception('This path %s is a valid repository' % repo_path) @@ -393,7 +453,6 @@ backend(repo_path, create=True, src_url=clone_uri) - def __rename_repo(self, old, new): """ renames repository on filesystem @@ -406,8 +465,9 @@ old_path = os.path.join(self.repos_path, old) new_path = os.path.join(self.repos_path, new) if os.path.isdir(new_path): - raise Exception('Was trying to rename to already existing dir %s' \ - % new_path) + raise Exception( + 'Was trying to rename to already existing dir %s' % new_path + ) shutil.move(old_path, new_path) def __delete_repo(self, repo): @@ -426,7 +486,6 @@ shutil.move(os.path.join(rm_path, '.%s' % alias), os.path.join(rm_path, 'rm__.%s' % alias)) # disable repo - shutil.move(rm_path, os.path.join(self.repos_path, 'rm__%s__%s' \ - % (datetime.today()\ - .strftime('%Y%m%d_%H%M%S_%f'), - repo.repo_name))) + _d = 'rm__%s__%s' % (datetime.now().strftime('%Y%m%d_%H%M%S_%f'), + repo.repo_name) + shutil.move(rm_path, os.path.join(self.repos_path, _d))
--- a/rhodecode/model/repo_permission.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/model/repo_permission.py Sat Jan 28 01:06:29 2012 +0200 @@ -26,14 +26,29 @@ import logging from rhodecode.model import BaseModel -from rhodecode.model.db import UserRepoToPerm, UsersGroupRepoToPerm, Permission +from rhodecode.model.db import UserRepoToPerm, UsersGroupRepoToPerm, Permission,\ + User, Repository log = logging.getLogger(__name__) class RepositoryPermissionModel(BaseModel): + def __get_user(self, user): + return self._get_instance(User, user, callback=User.get_by_username) + + def __get_repo(self, repository): + return self._get_instance(Repository, repository, + callback=Repository.get_by_repo_name) + + def __get_perm(self, permission): + return self._get_instance(Permission, permission, + callback=Permission.get_by_key) + def get_user_permission(self, repository, user): + repository = self.__get_repo(repository) + user = self.__get_user(user) + return UserRepoToPerm.query() \ .filter(UserRepoToPerm.user == user) \ .filter(UserRepoToPerm.repository == repository) \
--- a/rhodecode/model/repos_group.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/model/repos_group.py Sat Jan 28 01:06:29 2012 +0200 @@ -28,18 +28,32 @@ import traceback import shutil -from pylons.i18n.translation import _ - -from vcs.utils.lazy import LazyProperty +from rhodecode.lib import LazyProperty from rhodecode.model import BaseModel -from rhodecode.model.db import RepoGroup, RhodeCodeUi +from rhodecode.model.db import RepoGroup, RhodeCodeUi, UserRepoGroupToPerm, \ + User, Permission, UsersGroupRepoGroupToPerm, UsersGroup log = logging.getLogger(__name__) class ReposGroupModel(BaseModel): + def __get_user(self, user): + return self._get_instance(User, user, callback=User.get_by_username) + + def __get_users_group(self, users_group): + return self._get_instance(UsersGroup, users_group, + callback=UsersGroup.get_by_group_name) + + def __get_repos_group(self, repos_group): + return self._get_instance(RepoGroup, repos_group, + callback=RepoGroup.get_by_group_name) + + def __get_perm(self, permission): + return self._get_instance(Permission, permission, + callback=Permission.get_by_key) + @LazyProperty def repos_path(self): """ @@ -49,6 +63,24 @@ q = RhodeCodeUi.get_by_key('/').one() return q.ui_value + 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: + 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.group = new_group + repo_group_to_perm.user_id = User.get_by_username('default').user_id + + self.sa.add(repo_group_to_perm) + def __create_group(self, group_name): """ makes repositories group on filesystem @@ -102,16 +134,21 @@ # delete only if that path really exists os.rmdir(rm_path) - def create(self, form_data): + def create(self, group_name, group_description, parent, just_db=False): try: new_repos_group = RepoGroup() - new_repos_group.group_description = form_data['group_description'] - new_repos_group.parent_group = RepoGroup.get(form_data['group_parent_id']) - new_repos_group.group_name = new_repos_group.get_new_name(form_data['group_name']) + new_repos_group.group_description = group_description + new_repos_group.parent_group = self.__get_repos_group(parent) + new_repos_group.group_name = new_repos_group.get_new_name(group_name) self.sa.add(new_repos_group) - self.sa.flush() - self.__create_group(new_repos_group.group_name) + self._create_default_perms(new_repos_group) + + if not just_db: + # we need to flush here, in order to check if database won't + # throw any exceptions, create filesystem dirs at the very end + self.sa.flush() + self.__create_group(new_repos_group.group_name) return new_repos_group except: @@ -122,6 +159,29 @@ try: repos_group = RepoGroup.get(repos_group_id) + + # update permissions + for member, perm, member_type in form_data['perms_updates']: + if member_type == 'user': + # this updates also current one if found + ReposGroupModel().grant_user_permission( + repos_group=repos_group, user=member, perm=perm + ) + else: + ReposGroupModel().grant_users_group_permission( + repos_group=repos_group, group_name=member, perm=perm + ) + # set new permissions + for member, perm, member_type in form_data['perms_new']: + if member_type == 'user': + ReposGroupModel().grant_user_permission( + repos_group=repos_group, user=member, perm=perm + ) + else: + ReposGroupModel().grant_users_group_permission( + repos_group=repos_group, group_name=member, perm=perm + ) + old_path = repos_group.full_path # change properties @@ -154,3 +214,97 @@ except: log.error(traceback.format_exc()) raise + + def grant_user_permission(self, repos_group, user, perm): + """ + Grant permission for user on given repositories group, or update + existing one if found + + :param repos_group: Instance of ReposGroup, repositories_group_id, + or repositories_group name + :param user: Instance of User, user_id or username + :param perm: Instance of Permission, or permission_name + """ + + repos_group = self.__get_repos_group(repos_group) + user = self.__get_user(user) + permission = self.__get_perm(perm) + + # check if we have that permission already + obj = self.sa.query(UserRepoGroupToPerm)\ + .filter(UserRepoGroupToPerm.user == user)\ + .filter(UserRepoGroupToPerm.group == repos_group)\ + .scalar() + if obj is None: + # create new ! + obj = UserRepoGroupToPerm() + obj.group = repos_group + obj.user = user + obj.permission = permission + self.sa.add(obj) + + def revoke_user_permission(self, repos_group, user): + """ + Revoke permission for user on given repositories group + + :param repos_group: Instance of ReposGroup, repositories_group_id, + or repositories_group name + :param user: Instance of User, user_id or username + """ + + repos_group = self.__get_repos_group(repos_group) + user = self.__get_user(user) + + obj = self.sa.query(UserRepoGroupToPerm)\ + .filter(UserRepoGroupToPerm.user == user)\ + .filter(UserRepoGroupToPerm.group == repos_group)\ + .one() + self.sa.delete(obj) + + def grant_users_group_permission(self, repos_group, group_name, perm): + """ + Grant permission for users group on given repositories group, or update + existing one if found + + :param repos_group: Instance of ReposGroup, repositories_group_id, + or repositories_group name + :param group_name: Instance of UserGroup, users_group_id, + or users 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) + permission = self.__get_perm(perm) + + # check if we have that permission already + obj = self.sa.query(UsersGroupRepoGroupToPerm)\ + .filter(UsersGroupRepoGroupToPerm.group == repos_group)\ + .filter(UsersGroupRepoGroupToPerm.users_group == group_name)\ + .scalar() + + if obj is None: + # create new + obj = UsersGroupRepoGroupToPerm() + + obj.group = repos_group + obj.users_group = group_name + obj.permission = permission + self.sa.add(obj) + + def revoke_users_group_permission(self, repos_group, group_name): + """ + Revoke permission for users group on given repositories group + + :param repos_group: Instance of ReposGroup, repositories_group_id, + or repositories_group name + :param group_name: Instance of UserGroup, users_group_id, + or users group name + """ + repos_group = self.__get_repos_group(repos_group) + group_name = self.__get_users_group(group_name) + + obj = self.sa.query(UsersGroupRepoGroupToPerm)\ + .filter(UsersGroupRepoGroupToPerm.group == repos_group)\ + .filter(UsersGroupRepoGroupToPerm.users_group == group_name)\ + .one() + self.sa.delete(obj)
--- a/rhodecode/model/scm.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/model/scm.py Sat Jan 28 01:06:29 2012 +0200 @@ -36,12 +36,12 @@ from rhodecode import BACKENDS from rhodecode.lib import helpers as h from rhodecode.lib import safe_str -from rhodecode.lib.auth import HasRepoPermissionAny +from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \ action_logger, EmptyChangeset from rhodecode.model import BaseModel from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \ - UserFollowing, UserLog, User + UserFollowing, UserLog, User, RepoGroup log = logging.getLogger(__name__) @@ -80,15 +80,16 @@ for dbr in self.db_repo_list: scmr = dbr.scm_instance_cached # check permission at this level - if not HasRepoPermissionAny('repository.read', 'repository.write', - 'repository.admin')(dbr.repo_name, - 'get repo check'): + if not HasRepoPermissionAny( + 'repository.read', 'repository.write', 'repository.admin' + )(dbr.repo_name, 'get repo check'): continue if scmr is None: - log.error('%s this repository is present in database but it ' - 'cannot be created as an scm instance', - dbr.repo_name) + log.error( + '%s this repository is present in database but it ' + 'cannot be created as an scm instance' % dbr.repo_name + ) continue last_change = scmr.last_change @@ -115,6 +116,28 @@ yield tmp_d +class GroupList(object): + + def __init__(self, db_repo_group_list): + self.db_repo_group_list = db_repo_group_list + + def __len__(self): + return len(self.db_repo_group_list) + + def __repr__(self): + return '<%s (%s)>' % (self.__class__.__name__, self.__len__()) + + def __iter__(self): + for dbgr in self.db_repo_group_list: + # check permission at this level + if not HasReposGroupPermissionAny( + 'group.read', 'group.write', 'group.admin' + )(dbgr.group_name, 'get group repo check'): + continue + + yield dbgr + + class ScmModel(BaseModel): """ Generic Scm Model @@ -200,6 +223,14 @@ return repo_iter + def get_repos_groups(self, all_groups=None): + if all_groups is None: + all_groups = RepoGroup.query()\ + .filter(RepoGroup.group_parent_id == None).all() + group_iter = GroupList(all_groups) + + return group_iter + def mark_for_invalidation(self, repo_name): """Puts cache invalidation task into db for further global cache invalidation
--- a/rhodecode/model/user.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/model/user.py Sat Jan 28 01:06:29 2012 +0200 @@ -35,7 +35,7 @@ from rhodecode.model import BaseModel from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \ UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \ - Notification + Notification, RepoGroup, UserRepoGroupToPerm, UsersGroup from rhodecode.lib.exceptions import DefaultUserException, \ UserOwnsReposException @@ -46,16 +46,26 @@ log = logging.getLogger(__name__) -PERM_WEIGHTS = {'repository.none': 0, - 'repository.read': 1, - 'repository.write': 3, - 'repository.admin': 3} +PERM_WEIGHTS = { + 'repository.none': 0, + 'repository.read': 1, + 'repository.write': 3, + 'repository.admin': 4, + 'group.none': 0, + 'group.read': 1, + 'group.write': 3, + 'group.admin': 4, +} class UserModel(BaseModel): def __get_user(self, user): - return self._get_instance(User, user) + return self._get_instance(User, user, callback=User.get_by_username) + + def __get_perm(self, permission): + return self._get_instance(Permission, permission, + callback=Permission.get_by_key) def get(self, user_id, cache=False): user = self.sa.query(User) @@ -348,9 +358,12 @@ :param user: user instance to fill his perms """ - - user.permissions['repositories'] = {} - user.permissions['global'] = set() + RK = 'repositories' + GK = 'repositories_groups' + GLOBAL = 'global' + user.permissions[RK] = {} + user.permissions[GK] = {} + user.permissions[GLOBAL] = set() #====================================================================== # fetch default permissions @@ -358,36 +371,45 @@ default_user = User.get_by_username('default', cache=True) default_user_id = default_user.user_id - default_perms = Permission.get_default_perms(default_user_id) + default_repo_perms = Permission.get_default_perms(default_user_id) + default_repo_groups_perms = Permission.get_default_group_perms(default_user_id) if user.is_admin: #================================================================== - # #admin have all default rights set to admin + # admin user have all default rights for repositories + # and groups set to admin #================================================================== - user.permissions['global'].add('hg.admin') + user.permissions[GLOBAL].add('hg.admin') - for perm in default_perms: + # repositories + for perm in default_repo_perms: + r_k = perm.UserRepoToPerm.repository.repo_name p = 'repository.admin' - user.permissions['repositories'][perm.UserRepoToPerm. - repository.repo_name] = p + user.permissions[RK][r_k] = p + + # repositories groups + for perm in default_repo_groups_perms: + rg_k = perm.UserRepoGroupToPerm.group.group_name + p = 'group.admin' + user.permissions[GK][rg_k] = p else: #================================================================== - # set default permissions + # set default permissions first for repositories and groups #================================================================== uid = user.user_id - # default global + # default global permissions default_global_perms = self.sa.query(UserToPerm)\ .filter(UserToPerm.user_id == default_user_id) for perm in default_global_perms: - user.permissions['global'].add(perm.permission.permission_name) + user.permissions[GLOBAL].add(perm.permission.permission_name) # default for repositories - for perm in default_perms: - if perm.Repository.private and not (perm.Repository.user_id == - uid): + for perm in default_repo_perms: + r_k = perm.UserRepoToPerm.repository.repo_name + if perm.Repository.private and not (perm.Repository.user_id == uid): # disable defaults for private repos, p = 'repository.none' elif perm.Repository.user_id == uid: @@ -396,8 +418,13 @@ else: p = perm.Permission.permission_name - user.permissions['repositories'][perm.UserRepoToPerm. - repository.repo_name] = p + user.permissions[RK][r_k] = p + + # default for repositories groups + for perm in default_repo_groups_perms: + rg_k = perm.UserRepoGroupToPerm.group.group_name + p = perm.Permission.permission_name + user.permissions[GK][rg_k] = p #================================================================== # overwrite default with user permissions if any @@ -409,25 +436,24 @@ .filter(UserToPerm.user_id == uid).all() for perm in user_perms: - user.permissions['global'].add(perm.permission.permission_name) + user.permissions[GLOBAL].add(perm.permission.permission_name) # user repositories - 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 = \ + 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() for perm in user_repo_perms: # set admin if owner + r_k = perm.UserRepoToPerm.repository.repo_name if perm.Repository.user_id == uid: p = 'repository.admin' else: p = perm.Permission.permission_name - user.permissions['repositories'][perm.UserRepoToPerm. - repository.repo_name] = p + user.permissions[RK][r_k] = p #================================================================== # check if user is part of groups for this repository and fill in @@ -442,30 +468,44 @@ .filter(UsersGroupMember.user_id == uid).all() for perm in user_perms_from_users_groups: - user.permissions['global'].add(perm.permission.permission_name) + user.permissions[GLOBAL].add(perm.permission.permission_name) # users group repositories - user_repo_perms_from_users_groups = self.sa.query( - UsersGroupRepoToPerm, - Permission, Repository,)\ - .join((Repository, UsersGroupRepoToPerm.repository_id == - Repository.repo_id))\ - .join((Permission, UsersGroupRepoToPerm.permission_id == - Permission.permission_id))\ - .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id == - UsersGroupMember.users_group_id))\ - .filter(UsersGroupMember.user_id == uid).all() + user_repo_perms_from_users_groups = \ + self.sa.query(UsersGroupRepoToPerm, Permission, Repository,)\ + .join((Repository, UsersGroupRepoToPerm.repository_id == Repository.repo_id))\ + .join((Permission, UsersGroupRepoToPerm.permission_id == Permission.permission_id))\ + .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id == UsersGroupMember.users_group_id))\ + .filter(UsersGroupMember.user_id == uid)\ + .all() for perm in user_repo_perms_from_users_groups: + r_k = perm.UsersGroupRepoToPerm.repository.repo_name p = perm.Permission.permission_name - cur_perm = user.permissions['repositories'][perm. - UsersGroupRepoToPerm. - repository.repo_name] + cur_perm = user.permissions[RK][r_k] # overwrite permission only if it's greater than permission # given from other sources if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]: - user.permissions['repositories'][perm.UsersGroupRepoToPerm. - repository.repo_name] = p + user.permissions[RK][r_k] = p + + #================================================================== + # get access for this user for repos group and override defaults + #================================================================== + + # user repositories 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(UserRepoToPerm.user_id == uid)\ + .all() + + for perm in user_repo_groups_perms: + rg_k = perm.UserRepoGroupToPerm.group.group_name + p = perm.Permission.permission_name + cur_perm = user.permissions[GK][rg_k] + if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]: + user.permissions[GK][rg_k] = p return user @@ -480,23 +520,28 @@ .filter(UserToPerm.permission == perm).scalar() is not None def grant_perm(self, user, perm): - if not isinstance(perm, Permission): - raise Exception('perm needs to be an instance of Permission class ' - 'got %s instead' % type(perm)) + """ + Grant user global permissions + :param user: + :param perm: + """ user = self.__get_user(user) - + perm = self.__get_perm(perm) new = UserToPerm() new.user = user new.permission = perm self.sa.add(new) def revoke_perm(self, user, perm): - if not isinstance(perm, Permission): - raise Exception('perm needs to be an instance of Permission class ' - 'got %s instead' % type(perm)) + """ + Revoke users global permissions + :param user: + :param perm: + """ user = self.__get_user(user) + perm = self.__get_perm(perm) obj = UserToPerm.query().filter(UserToPerm.user == user)\ .filter(UserToPerm.permission == perm).scalar()
--- a/rhodecode/model/users_group.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/model/users_group.py Sat Jan 28 01:06:29 2012 +0200 @@ -38,7 +38,12 @@ class UsersGroupModel(BaseModel): def __get_users_group(self, users_group): - return self._get_instance(UsersGroup, users_group) + return self._get_instance(UsersGroup, users_group, + callback=UsersGroup.get_by_group_name) + + def __get_perm(self, permission): + return self._get_instance(Permission, permission, + callback=Permission.get_by_key) def get(self, users_group_id, cache=False): return UsersGroup.get(users_group_id) @@ -80,7 +85,15 @@ log.error(traceback.format_exc()) raise - def delete(self, users_group): + def delete(self, users_group, force=False): + """ + Deletes repos group, unless force flag is used + raises exception if there are members in that group, else deletes + group and users + + :param users_group: + :param force: + """ try: users_group = self.__get_users_group(users_group) @@ -88,7 +101,7 @@ assigned_groups = UsersGroupRepoToPerm.query()\ .filter(UsersGroupRepoToPerm.users_group == users_group).all() - if assigned_groups: + if assigned_groups and force is False: raise UsersGroupsAssignedException('RepoGroup assigned to %s' % assigned_groups) @@ -118,10 +131,8 @@ raise def has_perm(self, users_group, perm): - if not isinstance(perm, Permission): - raise Exception('perm needs to be an instance of Permission class') - users_group = self.__get_users_group(users_group) + perm = self.__get_perm(perm) return UsersGroupToPerm.query()\ .filter(UsersGroupToPerm.users_group == users_group)\ @@ -139,10 +150,8 @@ self.sa.add(new) def revoke_perm(self, users_group, perm): - if not isinstance(perm, Permission): - raise Exception('perm needs to be an instance of Permission class') - users_group = self.__get_users_group(users_group) + perm = self.__get_perm(perm) obj = UsersGroupToPerm.query()\ .filter(UsersGroupToPerm.users_group == users_group)\
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rhodecode/templates/admin/repos_groups/repos_group_edit_perms.html Sat Jan 28 01:06:29 2012 +0200 @@ -0,0 +1,270 @@ +<table id="permissions_manage" class="noborder"> + <tr> + <td>${_('none')}</td> + <td>${_('read')}</td> + <td>${_('write')}</td> + <td>${_('admin')}</td> + <td>${_('member')}</td> + <td></td> + </tr> + ## USERS + %for r2p in c.repos_group.repo_group_to_perm: + <tr id="id${id(r2p.user.username)}"> + <td>${h.radio('u_perm_%s' % r2p.user.username,'group.none')}</td> + <td>${h.radio('u_perm_%s' % r2p.user.username,'group.read')}</td> + <td>${h.radio('u_perm_%s' % r2p.user.username,'group.write')}</td> + <td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin')}</td> + <td style="white-space: nowrap;"> + <img style="vertical-align:bottom" src="${h.url('/images/icons/user.png')}"/>${r2p.user.username} + </td> + <td> + %if r2p.user.username !='default': + <span class="delete_icon action_button" onclick="ajaxActionUser(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')"> + ${_('revoke')} + </span> + %endif + </td> + </tr> + %endfor + + ## USERS GROUPS + %for g2p in c.repos_group.users_group_to_perm: + <tr id="id${id(g2p.users_group.users_group_name)}"> + <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.none')}</td> + <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.read')}</td> + <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.write')}</td> + <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.admin')}</td> + <td style="white-space: nowrap;"> + <img style="vertical-align:bottom" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name} + </td> + <td> + <span class="delete_icon action_button" onclick="ajaxActionUsersGroup(${g2p.users_group.users_group_id},'${'id%s'%id(g2p.users_group.users_group_name)}')"> + ${_('revoke')} + </span> + </td> + </tr> + %endfor + <tr id="add_perm_input"> + <td>${h.radio('perm_new_member','group.none')}</td> + <td>${h.radio('perm_new_member','group.read')}</td> + <td>${h.radio('perm_new_member','group.write')}</td> + <td>${h.radio('perm_new_member','group.admin')}</td> + <td class='ac'> + <div class="perm_ac" id="perm_ac"> + ${h.text('perm_new_member_name',class_='yui-ac-input')} + ${h.hidden('perm_new_member_type')} + <div id="perm_container"></div> + </div> + </td> + <td></td> + </tr> + <tr> + <td colspan="6"> + <span id="add_perm" class="add_icon" style="cursor: pointer;"> + ${_('Add another member')} + </span> + </td> + </tr> +</table> +<script type="text/javascript"> +function ajaxActionUser(user_id, field_id) { + var sUrl = "${h.url('delete_repos_group_user_perm',group_name=c.repos_group.name)}"; + var callback = { + success: function (o) { + var tr = YUD.get(String(field_id)); + tr.parentNode.removeChild(tr); + }, + failure: function (o) { + alert("${_('Failed to remove user')}"); + }, + }; + var postData = '_method=delete&user_id=' + user_id; + var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData); +}; + +function ajaxActionUsersGroup(users_group_id,field_id){ + var sUrl = "${h.url('delete_repos_group_users_group_perm',group_name=c.repos_group.name)}"; + var callback = { + success:function(o){ + var tr = YUD.get(String(field_id)); + tr.parentNode.removeChild(tr); + }, + failure:function(o){ + alert("${_('Failed to remove users group')}"); + }, + }; + var postData = '_method=delete&users_group_id='+users_group_id; + var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData); +}; + +YUE.onDOMReady(function () { + if (!YUD.hasClass('perm_new_member_name', 'error')) { + YUD.setStyle('add_perm_input', 'display', 'none'); + } + YAHOO.util.Event.addListener('add_perm', 'click', function () { + YUD.setStyle('add_perm_input', 'display', ''); + YUD.setStyle('add_perm', 'opacity', '0.6'); + YUD.setStyle('add_perm', 'cursor', 'default'); + }); +}); + +YAHOO.example.FnMultipleFields = function () { + var myUsers = ${c.users_array|n}; + var myGroups = ${c.users_groups_array|n}; + + // Define a custom search function for the DataSource of users + var matchUsers = function (sQuery) { + // Case insensitive matching + var query = sQuery.toLowerCase(); + var i = 0; + var l = myUsers.length; + var matches = []; + + // Match against each name of each contact + for (; i < l; i++) { + contact = myUsers[i]; + if ((contact.fname.toLowerCase().indexOf(query) > -1) || (contact.lname.toLowerCase().indexOf(query) > -1) || (contact.nname && (contact.nname.toLowerCase().indexOf(query) > -1))) { + matches[matches.length] = contact; + } + } + return matches; + }; + + // Define a custom search function for the DataSource of usersGroups + var matchGroups = function (sQuery) { + // Case insensitive matching + var query = sQuery.toLowerCase(); + var i = 0; + var l = myGroups.length; + var matches = []; + + // Match against each name of each contact + for (; i < l; i++) { + matched_group = myGroups[i]; + if (matched_group.grname.toLowerCase().indexOf(query) > -1) { + matches[matches.length] = matched_group; + } + } + return matches; + }; + + //match all + var matchAll = function (sQuery) { + u = matchUsers(sQuery); + g = matchGroups(sQuery); + return u.concat(g); + }; + + // DataScheme for members + var memberDS = new YAHOO.util.FunctionDataSource(matchAll); + memberDS.responseSchema = { + fields: ["id", "fname", "lname", "nname", "grname", "grmembers"] + }; + + // DataScheme for owner + var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers); + ownerDS.responseSchema = { + fields: ["id", "fname", "lname", "nname"] + }; + + // Instantiate AutoComplete for perms + var membersAC = new YAHOO.widget.AutoComplete("perm_new_member_name", "perm_container", memberDS); + membersAC.useShadow = false; + membersAC.resultTypeList = false; + + // Instantiate AutoComplete for owner + var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS); + ownerAC.useShadow = false; + ownerAC.resultTypeList = false; + + + // Helper highlight function for the formatter + var highlightMatch = function (full, snippet, matchindex) { + return full.substring(0, matchindex) + "<span class='match'>" + full.substr(matchindex, snippet.length) + "</span>" + full.substring(matchindex + snippet.length); + }; + + // Custom formatter to highlight the matching letters + var custom_formatter = function (oResultData, sQuery, sResultMatch) { + var query = sQuery.toLowerCase(); + + if (oResultData.grname != undefined) { + var grname = oResultData.grname; + var grmembers = oResultData.grmembers; + var grnameMatchIndex = grname.toLowerCase().indexOf(query); + var grprefix = "${_('Group')}: "; + var grsuffix = " (" + grmembers + " ${_('members')})"; + + if (grnameMatchIndex > -1) { + return grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix; + } + + return grprefix + oResultData.grname + grsuffix; + } else if (oResultData.fname != undefined) { + + var fname = oResultData.fname, + lname = oResultData.lname, + nname = oResultData.nname || "", + // Guard against null value + fnameMatchIndex = fname.toLowerCase().indexOf(query), + lnameMatchIndex = lname.toLowerCase().indexOf(query), + nnameMatchIndex = nname.toLowerCase().indexOf(query), + displayfname, displaylname, displaynname; + + if (fnameMatchIndex > -1) { + displayfname = highlightMatch(fname, query, fnameMatchIndex); + } else { + displayfname = fname; + } + + if (lnameMatchIndex > -1) { + displaylname = highlightMatch(lname, query, lnameMatchIndex); + } else { + displaylname = lname; + } + + if (nnameMatchIndex > -1) { + displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")"; + } else { + displaynname = nname ? "(" + nname + ")" : ""; + } + + return displayfname + " " + displaylname + " " + displaynname; + } else { + return ''; + } + }; + membersAC.formatResult = custom_formatter; + ownerAC.formatResult = custom_formatter; + + var myHandler = function (sType, aArgs) { + + var myAC = aArgs[0]; // reference back to the AC instance + var elLI = aArgs[1]; // reference to the selected LI element + var oData = aArgs[2]; // object literal of selected item's result data + //fill the autocomplete with value + if (oData.nname != undefined) { + //users + myAC.getInputEl().value = oData.nname; + YUD.get('perm_new_member_type').value = 'user'; + } else { + //groups + myAC.getInputEl().value = oData.grname; + YUD.get('perm_new_member_type').value = 'users_group'; + } + + }; + + membersAC.itemSelectEvent.subscribe(myHandler); + if(ownerAC.itemSelectEvent){ + ownerAC.itemSelectEvent.subscribe(myHandler); + } + + return { + memberDS: memberDS, + ownerDS: ownerDS, + membersAC: membersAC, + ownerAC: ownerAC, + }; +}(); + +</script>
--- a/rhodecode/templates/admin/repos_groups/repos_groups_edit.html Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/templates/admin/repos_groups/repos_groups_edit.html Sat Jan 28 01:06:29 2012 +0200 @@ -53,9 +53,18 @@ ${h.select('group_parent_id','',c.repo_groups,class_="medium")} </div> </div> + <div class="field"> + <div class="label"> + <label for="input">${_('Permissions')}:</label> + </div> + <div class="input"> + <%include file="repos_group_edit_perms.html"/> + </div> + </div> <div class="buttons"> - ${h.submit('save',_('save'),class_="ui-button")} + ${h.submit('save',_('Save'),class_="ui-button")} + ${h.reset('reset',_('Reset'),class_="ui-button")} </div> </div> </div>
--- a/rhodecode/templates/index_base.html Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/templates/index_base.html Sat Jan 28 01:06:29 2012 +0200 @@ -38,7 +38,9 @@ </div> </td> <td>${gr.group_description}</td> - ##<td><b>${gr.repositories.count()}</b></td> + ## this is commented out since for multi nested repos can be HEAVY! + ## in number of executed queries during traversing uncomment at will + ##<td><b>${gr.repositories_recursive_count}</b></td> </tr> % endfor
--- a/rhodecode/tests/test_models.py Sun Feb 05 21:45:15 2012 +0200 +++ b/rhodecode/tests/test_models.py Sat Jan 28 01:06:29 2012 +0200 @@ -12,13 +12,27 @@ from rhodecode.model.meta import Session from rhodecode.model.notification import NotificationModel from rhodecode.model.users_group import UsersGroupModel +from rhodecode.lib.auth import AuthUser + + +def _make_group(path, desc='desc', parent_id=None, + skip_if_exists=False): + + gr = RepoGroup.get_by_group_name(path) + if gr and skip_if_exists: + return gr + + gr = ReposGroupModel().create(path, desc, parent_id) + Session.commit() + return gr + class TestReposGroups(unittest.TestCase): def setUp(self): - self.g1 = self.__make_group('test1', skip_if_exists=True) - self.g2 = self.__make_group('test2', skip_if_exists=True) - self.g3 = self.__make_group('test3', skip_if_exists=True) + self.g1 = _make_group('test1', skip_if_exists=True) + self.g2 = _make_group('test2', skip_if_exists=True) + self.g3 = _make_group('test3', skip_if_exists=True) def tearDown(self): print 'out' @@ -31,102 +45,81 @@ def _check_folders(self): print os.listdir(TESTS_TMP_PATH) - def __make_group(self, path, desc='desc', parent_id=None, - skip_if_exists=False): - - gr = RepoGroup.get_by_group_name(path) - if gr and skip_if_exists: - return gr - - form_data = dict(group_name=path, - group_description=desc, - group_parent_id=parent_id) - gr = ReposGroupModel().create(form_data) - Session.commit() - return gr - def __delete_group(self, id_): ReposGroupModel().delete(id_) - def __update_group(self, id_, path, desc='desc', parent_id=None): form_data = dict(group_name=path, group_description=desc, - group_parent_id=parent_id) + group_parent_id=parent_id, + perms_updates=[], + perms_new=[]) gr = ReposGroupModel().update(id_, form_data) return gr def test_create_group(self): - g = self.__make_group('newGroup') + g = _make_group('newGroup') self.assertEqual(g.full_path, 'newGroup') self.assertTrue(self.__check_path('newGroup')) - def test_create_same_name_group(self): - self.assertRaises(IntegrityError, lambda:self.__make_group('newGroup')) + self.assertRaises(IntegrityError, lambda:_make_group('newGroup')) Session.rollback() def test_same_subgroup(self): - sg1 = self.__make_group('sub1', parent_id=self.g1.group_id) + sg1 = _make_group('sub1', parent_id=self.g1.group_id) self.assertEqual(sg1.parent_group, self.g1) self.assertEqual(sg1.full_path, 'test1/sub1') self.assertTrue(self.__check_path('test1', 'sub1')) - ssg1 = self.__make_group('subsub1', parent_id=sg1.group_id) + ssg1 = _make_group('subsub1', parent_id=sg1.group_id) self.assertEqual(ssg1.parent_group, sg1) self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1') self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1')) - def test_remove_group(self): - sg1 = self.__make_group('deleteme') + sg1 = _make_group('deleteme') self.__delete_group(sg1.group_id) self.assertEqual(RepoGroup.get(sg1.group_id), None) self.assertFalse(self.__check_path('deteteme')) - sg1 = self.__make_group('deleteme', parent_id=self.g1.group_id) + sg1 = _make_group('deleteme', parent_id=self.g1.group_id) self.__delete_group(sg1.group_id) self.assertEqual(RepoGroup.get(sg1.group_id), None) self.assertFalse(self.__check_path('test1', 'deteteme')) - def test_rename_single_group(self): - sg1 = self.__make_group('initial') + sg1 = _make_group('initial') new_sg1 = self.__update_group(sg1.group_id, 'after') self.assertTrue(self.__check_path('after')) self.assertEqual(RepoGroup.get_by_group_name('initial'), None) - def test_update_group_parent(self): - sg1 = self.__make_group('initial', parent_id=self.g1.group_id) + sg1 = _make_group('initial', parent_id=self.g1.group_id) new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id) self.assertTrue(self.__check_path('test1', 'after')) self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None) - new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id) self.assertTrue(self.__check_path('test3', 'after')) self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None) - new_sg1 = self.__update_group(sg1.group_id, 'hello') self.assertTrue(self.__check_path('hello')) self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1) - - def test_subgrouping_with_repo(self): - g1 = self.__make_group('g1') - g2 = self.__make_group('g2') + g1 = _make_group('g1') + g2 = _make_group('g2') # create new repo form_data = dict(repo_name='john', @@ -150,13 +143,13 @@ RepoModel().update(r.repo_name, form_data) self.assertEqual(r.repo_name, 'g1/john') - self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id) self.assertTrue(self.__check_path('g2', 'g1')) # test repo self.assertEqual(r.repo_name, os.path.join('g2', 'g1', r.just_name)) + class TestUser(unittest.TestCase): def __init__(self, methodName='runTest'): Session.remove() @@ -245,7 +238,6 @@ self.assertEqual(len(unotification), len(usrs)) self.assertEqual([x.user.user_id for x in unotification], usrs) - def test_user_notifications(self): self.assertEqual([], Notification.query().all()) self.assertEqual([], UserNotification.query().all()) @@ -284,7 +276,6 @@ == notification).all() self.assertEqual(un, []) - def test_delete_association(self): self.assertEqual([], Notification.query().all()) @@ -361,6 +352,7 @@ self.assertEqual(NotificationModel() .get_unread_cnt_for_user(self.u3), 2) + class TestUsers(unittest.TestCase): def __init__(self, methodName='runTest'): @@ -401,4 +393,163 @@ #revoke UserModel().revoke_perm(self.u1, perm) Session.commit() - self.assertEqual(UserModel().has_perm(self.u1, perm),False) + self.assertEqual(UserModel().has_perm(self.u1, perm), False) + + +class TestPermissions(unittest.TestCase): + def __init__(self, methodName='runTest'): + super(TestPermissions, self).__init__(methodName=methodName) + + def setUp(self): + self.u1 = UserModel().create_or_update( + username=u'u1', password=u'qweqwe', + email=u'u1@rhodecode.org', name=u'u1', lastname=u'u1' + ) + self.a1 = UserModel().create_or_update( + username=u'a1', password=u'qweqwe', + email=u'a1@rhodecode.org', name=u'a1', lastname=u'a1', admin=True + ) + Session.commit() + + def tearDown(self): + UserModel().delete(self.u1) + UserModel().delete(self.a1) + if hasattr(self, 'g1'): + ReposGroupModel().delete(self.g1.group_id) + if hasattr(self, 'g2'): + ReposGroupModel().delete(self.g2.group_id) + + if hasattr(self, 'ug1'): + UsersGroupModel().delete(self.ug1, force=True) + + Session.commit() + + def test_default_perms_set(self): + u1_auth = AuthUser(user_id=self.u1.user_id) + perms = { + 'repositories_groups': {}, + 'global': set([u'hg.create.repository', u'repository.read', + u'hg.register.manual_activate']), + 'repositories': {u'vcs_test_hg': u'repository.read'} + } + self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], + perms['repositories'][HG_REPO]) + new_perm = 'repository.write' + RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm) + Session.commit() + + u1_auth = AuthUser(user_id=self.u1.user_id) + self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], new_perm) + + def test_default_admin_perms_set(self): + a1_auth = AuthUser(user_id=self.a1.user_id) + perms = { + 'repositories_groups': {}, + 'global': set([u'hg.admin']), + 'repositories': {u'vcs_test_hg': u'repository.admin'} + } + self.assertEqual(a1_auth.permissions['repositories'][HG_REPO], + perms['repositories'][HG_REPO]) + new_perm = 'repository.write' + RepoModel().grant_user_permission(repo=HG_REPO, user=self.a1, perm=new_perm) + Session.commit() + # cannot really downgrade admins permissions !? they still get's set as + # admin ! + u1_auth = AuthUser(user_id=self.a1.user_id) + self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], + perms['repositories'][HG_REPO]) + + def test_default_group_perms(self): + self.g1 = _make_group('test1', skip_if_exists=True) + self.g2 = _make_group('test2', skip_if_exists=True) + u1_auth = AuthUser(user_id=self.u1.user_id) + perms = { + 'repositories_groups': {u'test1': 'group.read', u'test2': 'group.read'}, + 'global': set([u'hg.create.repository', u'repository.read', u'hg.register.manual_activate']), + 'repositories': {u'vcs_test_hg': u'repository.read'} + } + self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], + perms['repositories'][HG_REPO]) + self.assertEqual(u1_auth.permissions['repositories_groups'], + perms['repositories_groups']) + + def test_default_admin_group_perms(self): + self.g1 = _make_group('test1', skip_if_exists=True) + self.g2 = _make_group('test2', skip_if_exists=True) + a1_auth = AuthUser(user_id=self.a1.user_id) + perms = { + 'repositories_groups': {u'test1': 'group.admin', u'test2': 'group.admin'}, + 'global': set(['hg.admin']), + 'repositories': {u'vcs_test_hg': 'repository.admin'} + } + + self.assertEqual(a1_auth.permissions['repositories'][HG_REPO], + perms['repositories'][HG_REPO]) + self.assertEqual(a1_auth.permissions['repositories_groups'], + perms['repositories_groups']) + + def test_propagated_permission_from_users_group(self): + # make group + self.ug1 = UsersGroupModel().create('G1') + # add user to group + UsersGroupModel().add_user_to_group(self.ug1, self.u1) + + # set permission to lower + new_perm = 'repository.none' + RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm) + Session.commit() + u1_auth = AuthUser(user_id=self.u1.user_id) + self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], + new_perm) + + # grant perm for group this should override permission from user + new_perm = 'repository.write' + RepoModel().grant_users_group_permission(repo=HG_REPO, + group_name=self.ug1, + perm=new_perm) + # check perms + u1_auth = AuthUser(user_id=self.u1.user_id) + perms = { + 'repositories_groups': {}, + 'global': set([u'hg.create.repository', u'repository.read', + u'hg.register.manual_activate']), + 'repositories': {u'vcs_test_hg': u'repository.read'} + } + self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], + new_perm) + self.assertEqual(u1_auth.permissions['repositories_groups'], + perms['repositories_groups']) + + def test_propagated_permission_from_users_group_lower_weight(self): + # make group + self.ug1 = UsersGroupModel().create('G1') + # add user to group + UsersGroupModel().add_user_to_group(self.ug1, self.u1) + + # set permission to lower + new_perm_h = 'repository.write' + RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, + perm=new_perm_h) + Session.commit() + u1_auth = AuthUser(user_id=self.u1.user_id) + self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], + new_perm_h) + + # grant perm for group this should NOT override permission from user + # since it's lower than granted + new_perm_l = 'repository.read' + RepoModel().grant_users_group_permission(repo=HG_REPO, + group_name=self.ug1, + perm=new_perm_l) + # check perms + u1_auth = AuthUser(user_id=self.u1.user_id) + perms = { + 'repositories_groups': {}, + 'global': set([u'hg.create.repository', u'repository.read', + u'hg.register.manual_activate']), + 'repositories': {u'vcs_test_hg': u'repository.write'} + } + self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], + new_perm_h) + self.assertEqual(u1_auth.permissions['repositories_groups'], + perms['repositories_groups'])
--- a/test.ini Sun Feb 05 21:45:15 2012 +0200 +++ b/test.ini Sat Jan 28 01:06:29 2012 +0200 @@ -89,7 +89,7 @@ beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long beaker.cache.super_short_term.type=memory -beaker.cache.super_short_term.expire=10 +beaker.cache.super_short_term.expire=1 beaker.cache.super_short_term.key_length = 256 beaker.cache.short_term.type=memory @@ -101,7 +101,7 @@ beaker.cache.long_term.key_length = 256 beaker.cache.sql_cache_short.type=memory -beaker.cache.sql_cache_short.expire=10 +beaker.cache.sql_cache_short.expire=1 beaker.cache.sql_cache_short.key_length = 256 beaker.cache.sql_cache_med.type=memory