branch permissions: added logic to define in UI branch permissions.

- db models
- new permissions pages placeholders
This commit is contained in:
Marcin Kuzminski 2018-08-28 13:41:30 +02:00
parent 8676434a71
commit e41725c078
35 changed files with 5433 additions and 89 deletions

View file

@ -51,7 +51,7 @@ PYRAMID_SETTINGS = {}
EXTENSIONS = {}
__version__ = ('.'.join((str(each) for each in VERSION[:3])))
__dbversion__ = 87 # defines current db version for migrations
__dbversion__ = 90 # defines current db version for migrations
__platform__ = platform.system()
__license__ = 'AGPLv3, and Commercial License'
__author__ = 'RhodeCode GmbH'

View file

@ -210,6 +210,11 @@ def admin_routes(config):
name='admin_permissions_object_update',
pattern='/permissions/object/update')
# Branch perms EE feature
config.add_route(
name='admin_permissions_branch',
pattern='/permissions/branch')
config.add_route(
name='admin_permissions_ips',
pattern='/permissions/ips')

View file

@ -182,7 +182,8 @@ class AdminPermissionsView(BaseAppView, DataGridAppView):
self.request.translate,
[x[0] for x in c.repo_perms_choices],
[x[0] for x in c.group_perms_choices],
[x[0] for x in c.user_group_perms_choices])()
[x[0] for x in c.user_group_perms_choices],
)()
try:
form_result = _form.to_python(dict(self.request.POST))
@ -215,6 +216,30 @@ class AdminPermissionsView(BaseAppView, DataGridAppView):
raise HTTPFound(h.route_path('admin_permissions_object'))
@LoginRequired()
@HasPermissionAllDecorator('hg.admin')
@view_config(
route_name='admin_permissions_branch', request_method='GET',
renderer='rhodecode:templates/admin/permissions/permissions.mako')
def permissions_branch(self):
c = self.load_default_context()
c.active = 'branch'
c.user = User.get_default_user(refresh=True)
defaults = {}
defaults.update(c.user.get_default_perms())
data = render(
'rhodecode:templates/admin/permissions/permissions.mako',
self._get_template_context(c), self.request)
html = formencode.htmlfill.render(
data,
defaults=defaults,
encoding="UTF-8",
force_defaults=False
)
return Response(html)
@LoginRequired()
@HasPermissionAllDecorator('hg.admin')
@view_config(

View file

@ -345,6 +345,15 @@ def includeme(config):
name='edit_repo_perms',
pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
# Permissions Branch (EE feature)
config.add_route(
name='edit_repo_perms_branch',
pattern='/{repo_name:.*?[^/]}/settings/branch_permissions', repo_route=True)
config.add_route(
name='edit_repo_perms_branch_delete',
pattern='/{repo_name:.*?[^/]}/settings/branch_permissions/{rule_id}/delete',
repo_route=True)
# Maintenance
config.add_route(
name='edit_repo_maintenance',

View file

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2011-2018 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
import logging
from pyramid.view import view_config
from rhodecode.apps._base import RepoAppView
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
log = logging.getLogger(__name__)
class RepoSettingsBranchPermissionsView(RepoAppView):
def load_default_context(self):
c = self._get_local_tmpl_context()
return c
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.admin')
@view_config(
route_name='edit_repo_perms_branch', request_method='GET',
renderer='rhodecode:templates/admin/repos/repo_edit.mako')
def branch_permissions(self):
c = self.load_default_context()
c.active = 'permissions_branch'
return self._get_template_context(c)

View file

@ -367,10 +367,38 @@ class PermOriginDict(dict):
self.perm_origin_stack = collections.OrderedDict()
def __setitem__(self, key, (perm, origin)):
self.perm_origin_stack.setdefault(key, []).append((perm, origin))
self.perm_origin_stack.setdefault(key, []).append(
(perm, origin))
dict.__setitem__(self, key, perm)
class BranchPermOriginDict(PermOriginDict):
"""
Dedicated branch permissions dict, with tracking of patterns and origins.
>>> perms = BranchPermOriginDict()
>>> perms['resource'] = '*pattern', 'read', 'default'
>>> perms['resource']
{'*pattern': 'read'}
>>> perms['resource'] = '*pattern', 'write', 'admin'
>>> perms['resource']
{'*pattern': 'write'}
>>> perms.perm_origin_stack
{'resource': {'*pattern': [('read', 'default'), ('write', 'admin')]}}
"""
def __setitem__(self, key, (pattern, perm, origin)):
self.perm_origin_stack.setdefault(key, {}) \
.setdefault(pattern, []).append((perm, origin))
if key in self:
self[key].__setitem__(pattern, perm)
else:
patterns = collections.OrderedDict()
patterns[pattern] = perm
dict.__setitem__(self, key, patterns)
class PermissionCalculator(object):
def __init__(
@ -395,6 +423,7 @@ class PermissionCalculator(object):
self.permissions_repositories = PermOriginDict()
self.permissions_repository_groups = PermOriginDict()
self.permissions_user_groups = PermOriginDict()
self.permissions_repository_branches = BranchPermOriginDict()
self.permissions_global = set()
self.default_repo_perms = Permission.get_default_repo_perms(
@ -405,6 +434,11 @@ class PermissionCalculator(object):
Permission.get_default_user_group_perms(
self.default_user_id, self.scope_user_group_id)
# default branch perms
self.default_branch_repo_perms = \
Permission.get_default_repo_branch_perms(
self.default_user_id, self.scope_repo_id)
def calculate(self):
if self.user_is_admin and not self.calculate_super_admin:
return self._admin_permissions()
@ -413,6 +447,7 @@ class PermissionCalculator(object):
self._calculate_global_permissions()
self._calculate_default_permissions()
self._calculate_repository_permissions()
self._calculate_repository_branch_permissions()
self._calculate_repository_group_permissions()
self._calculate_user_group_permissions()
return self._permission_structure()
@ -443,6 +478,15 @@ class PermissionCalculator(object):
p = 'usergroup.admin'
self.permissions_user_groups[u_k] = p, PermOrigin.SUPER_ADMIN
# branch permissions
# TODO(marcink): validate this, especially
# how this should work using multiple patterns specified ??
# looks ok, but still needs double check !!
for perm in self.default_branch_repo_perms:
r_k = perm.UserRepoToPerm.repository.repo_name
p = 'branch.push_force'
self.permissions_repository_branches[r_k] = '*', p, PermOrigin.SUPER_ADMIN
return self._permission_structure()
def _calculate_global_default_permissions(self):
@ -472,18 +516,14 @@ class PermissionCalculator(object):
# now we read the defined permissions and overwrite what we have set
# before those can be configured from groups or users explicitly.
# TODO: johbo: This seems to be out of sync, find out the reason
# for the comment below and update it.
# In case we want to extend this list we should be always in sync with
# User.DEFAULT_USER_PERMISSIONS definitions
# In case we want to extend this list we should make sure
# this is in sync with User.DEFAULT_USER_PERMISSIONS definitions
_configurable = frozenset([
'hg.fork.none', 'hg.fork.repository',
'hg.create.none', 'hg.create.repository',
'hg.usergroup.create.false', 'hg.usergroup.create.true',
'hg.repogroup.create.false', 'hg.repogroup.create.true',
'hg.create.write_on_repogroup.false',
'hg.create.write_on_repogroup.true',
'hg.create.write_on_repogroup.false', 'hg.create.write_on_repogroup.true',
'hg.inherit_default_perms.false', 'hg.inherit_default_perms.true'
])
@ -506,7 +546,7 @@ class PermissionCalculator(object):
for gr, perms in _explicit_grouped_perms:
# since user can be in multiple groups iterate over them and
# select the lowest permissions first (more explicit)
# TODO: marcink: do this^^
# TODO(marcink): do this^^
# group doesn't inherit default permissions so we actually set them
if not gr.inherit_default_permissions:
@ -533,8 +573,8 @@ class PermissionCalculator(object):
def _calculate_default_permissions(self):
"""
Set default user permissions for repositories, repository groups
taken from the default user.
Set default user permissions for repositories, repository branches,
repository groups, user groups taken from the default user.
Calculate inheritance of object permissions based on what we have now
in GLOBAL permissions. We check if .false is in GLOBAL since this is
@ -551,8 +591,7 @@ class PermissionCalculator(object):
user_inherit_object_permissions = not ('hg.inherit_default_perms.false'
in self.permissions_global)
# defaults for repositories, taken from `default` user permissions
# on given repo
# default permissions for repositories, taken from `default` user permissions
for perm in self.default_repo_perms:
r_k = perm.UserRepoToPerm.repository.repo_name
p = perm.Permission.permission_name
@ -585,8 +624,24 @@ class PermissionCalculator(object):
o = PermOrigin.SUPER_ADMIN
self.permissions_repositories[r_k] = p, o
# defaults for repository groups taken from `default` user permission
# on given group
# default permissions branch for repositories, taken from `default` user permissions
for perm in self.default_branch_repo_perms:
r_k = perm.UserRepoToPerm.repository.repo_name
p = perm.Permission.permission_name
pattern = perm.UserToRepoBranchPermission.branch_pattern
o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
if not self.explicit:
# TODO(marcink): fix this for multiple entries
cur_perm = self.permissions_repository_branches.get(r_k) or 'branch.none'
p = self._choose_permission(p, cur_perm)
# NOTE(marcink): register all pattern/perm instances in this
# special dict that aggregates entries
self.permissions_repository_branches[r_k] = pattern, p, o
# default permissions for repository groups taken from `default` user permission
for perm in self.default_repo_groups_perms:
rg_k = perm.UserRepoGroupToPerm.group.group_name
p = perm.Permission.permission_name
@ -611,8 +666,7 @@ class PermissionCalculator(object):
o = PermOrigin.SUPER_ADMIN
self.permissions_repository_groups[rg_k] = p, o
# defaults for user groups taken from `default` user permission
# on given user group
# default permissions for user groups taken from `default` user permission
for perm in self.default_user_group_perms:
u_k = perm.UserUserGroupToPerm.user_group.users_group_name
p = perm.Permission.permission_name
@ -703,6 +757,49 @@ class PermissionCalculator(object):
o = PermOrigin.SUPER_ADMIN
self.permissions_repositories[r_k] = p, o
def _calculate_repository_branch_permissions(self):
# user group for repositories permissions
user_repo_branch_perms_from_user_group = Permission\
.get_default_repo_branch_perms_from_user_group(
self.user_id, self.scope_repo_id)
multiple_counter = collections.defaultdict(int)
for perm in user_repo_branch_perms_from_user_group:
r_k = perm.UserGroupRepoToPerm.repository.repo_name
p = perm.Permission.permission_name
pattern = perm.UserGroupToRepoBranchPermission.branch_pattern
o = PermOrigin.REPO_USERGROUP % perm.UserGroupRepoToPerm\
.users_group.users_group_name
multiple_counter[r_k] += 1
if multiple_counter[r_k] > 1:
# TODO(marcink): fix this for multi branch support, and multiple entries
cur_perm = self.permissions_repository_branches[r_k]
p = self._choose_permission(p, cur_perm)
self.permissions_repository_branches[r_k] = pattern, p, o
# user explicit branch permissions for repositories, overrides
# any specified by the group permission
user_repo_branch_perms = Permission.get_default_repo_branch_perms(
self.user_id, self.scope_repo_id)
for perm in user_repo_branch_perms:
r_k = perm.UserRepoToPerm.repository.repo_name
p = perm.Permission.permission_name
pattern = perm.UserToRepoBranchPermission.branch_pattern
o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
if not self.explicit:
# TODO(marcink): fix this for multiple entries
cur_perm = self.permissions_repository_branches.get(r_k) or 'branch.none'
p = self._choose_permission(p, cur_perm)
# NOTE(marcink): register all pattern/perm instances in this
# special dict that aggregates entries
self.permissions_repository_branches[r_k] = pattern, p, o
def _calculate_repository_group_permissions(self):
"""
Repository group permissions for the current user.
@ -845,6 +942,7 @@ class PermissionCalculator(object):
return {
'global': self.permissions_global,
'repositories': self.permissions_repositories,
'repository_branches': self.permissions_repository_branches,
'repositories_groups': self.permissions_repository_groups,
'user_groups': self.permissions_user_groups,
}
@ -956,6 +1054,9 @@ class AuthUser(object):
perms['user_groups'] = {
k: v for k, v in perms['user_groups'].items()
if v != 'usergroup.none'}
perms['repository_branches'] = {
k: v for k, v in perms['repository_branches'].iteritems()
if v != 'branch.none'}
return perms
@LazyProperty
@ -1800,7 +1901,6 @@ class PermsFunction(object):
def __call__(self, check_location='', user=None):
if not user:
log.debug('Using user attribute from global request')
# TODO: remove this someday,put as user as attribute here
request = self._get_request()
user = request.user

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,46 @@
import logging
from sqlalchemy import *
from sqlalchemy.engine import reflection
from sqlalchemy.dialects.mysql import LONGTEXT
from alembic.migration import MigrationContext
from alembic.operations import Operations
from rhodecode.lib.dbmigrate.utils import create_default_permissions, \
create_default_object_permission
from rhodecode.model import meta
from rhodecode.lib.dbmigrate.versions import _reset_base, notify
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)
from rhodecode.lib.dbmigrate.schema import db_4_13_0_0 as db
# issue fixups
fixups(db, meta.Session)
def downgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
def fixups(models, _SESSION):
# create default permissions
create_default_permissions(_SESSION, models)
log.info('created default global permissions definitions')
_SESSION().commit()
# # fix default object permissions
# create_default_object_permission(_SESSION, models)
log.info('created default permission')
_SESSION().commit()

View file

@ -0,0 +1,39 @@
import logging
from sqlalchemy import *
from sqlalchemy.engine import reflection
from sqlalchemy.dialects.mysql import LONGTEXT
from alembic.migration import MigrationContext
from alembic.operations import Operations
from rhodecode.model import meta
from rhodecode.lib.dbmigrate.versions import _reset_base, notify
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)
from rhodecode.lib.dbmigrate.schema import db_4_13_0_0 as db
db.UserToRepoBranchPermission.__table__.create()
db.UserGroupToRepoBranchPermission.__table__.create()
# issue fixups
fixups(db, meta.Session)
def downgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
def fixups(models, _SESSION):
pass

View file

@ -0,0 +1,43 @@
import logging
from sqlalchemy import *
from rhodecode.lib.dbmigrate.utils import (
create_default_object_permission, create_default_permissions)
from rhodecode.model import meta
from rhodecode.lib.dbmigrate.versions import _reset_base, notify
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)
from rhodecode.lib.dbmigrate.schema import db_4_13_0_0 as db
# issue fixups
fixups(db, meta.Session)
def downgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
def fixups(models, _SESSION):
# create default permissions
create_default_permissions(_SESSION, models)
log.info('created default global permissions definitions')
_SESSION().commit()
# fix default object permissions
create_default_object_permission(_SESSION, models)
log.info('created default permission')
_SESSION().commit()

View file

@ -751,6 +751,13 @@ class AttributeDict(AttributeDictBase):
class OrderedDefaultDict(collections.OrderedDict, collections.defaultdict):
def __init__(self, default_factory=None, *args, **kwargs):
# in python3 you can omit the args to super
super(OrderedDefaultDict, self).__init__(*args, **kwargs)
self.default_factory = default_factory
def fix_PATH(os_=None):
"""
Get current active python path, and append it to PATH variable to fix

View file

@ -1913,6 +1913,7 @@ class Repository(Base, BaseModel):
for _usr in q.all():
usr = AttributeDict(_usr.user.get_dict())
usr.permission = _usr.permission.permission_name
usr.permission_id = _usr.repo_to_perm_id
perm_rows.append(usr)
# filter the perm rows by 'default' first and then sort them by
@ -1926,6 +1927,7 @@ class Repository(Base, BaseModel):
usr = AttributeDict(self.user.get_dict())
usr.owner_row = True
usr.permission = _admin_perm
usr.permission_id = None
owner_row.append(usr)
super_admin_rows = []
@ -1938,6 +1940,7 @@ class Repository(Base, BaseModel):
usr = AttributeDict(usr.get_dict())
usr.admin_row = True
usr.permission = _admin_perm
usr.permission_id = None
super_admin_rows.append(usr)
return super_admin_rows + owner_row + perm_rows
@ -2694,6 +2697,11 @@ class Permission(Base, BaseModel):
('usergroup.write', _('User group write access')),
('usergroup.admin', _('User group admin access')),
('branch.none', _('Branch no permissions')),
('branch.merge', _('Branch access by web merge')),
('branch.push', _('Branch access by push')),
('branch.push_force', _('Branch access by push with force')),
('hg.repogroup.create.false', _('Repository Group creation disabled')),
('hg.repogroup.create.true', _('Repository Group creation enabled')),
@ -2723,11 +2731,16 @@ class Permission(Base, BaseModel):
('hg.inherit_default_perms.true', _('Inherit object permissions from default user enabled')),
]
# definition of system default permissions for DEFAULT user
# definition of system default permissions for DEFAULT user, created on
# system setup
DEFAULT_USER_PERMISSIONS = [
# object perms
'repository.read',
'group.read',
'usergroup.read',
# branch, for backward compat we need same value as before so forced pushed
'branch.push_force',
# global
'hg.create.repository',
'hg.repogroup.create.false',
'hg.usergroup.create.false',
@ -2758,6 +2771,11 @@ class Permission(Base, BaseModel):
'usergroup.write': 3,
'usergroup.admin': 4,
'branch.none': 0,
'branch.merge': 1,
'branch.push': 3,
'branch.push_force': 4,
'hg.repogroup.create.false': 0,
'hg.repogroup.create.true': 1,
@ -2793,6 +2811,21 @@ class Permission(Base, BaseModel):
q = q.filter(UserRepoToPerm.repository_id == repo_id)
return q.all()
@classmethod
def get_default_repo_branch_perms(cls, user_id, repo_id=None):
q = Session().query(UserToRepoBranchPermission, UserRepoToPerm, Permission) \
.join(
Permission,
UserToRepoBranchPermission.permission_id == Permission.permission_id) \
.join(
UserRepoToPerm,
UserToRepoBranchPermission.rule_to_perm_id == UserRepoToPerm.repo_to_perm_id) \
.filter(UserRepoToPerm.user_id == user_id)
if repo_id:
q = q.filter(UserToRepoBranchPermission.repository_id == repo_id)
return q.order_by(UserToRepoBranchPermission.rule_order).all()
@classmethod
def get_default_repo_perms_from_user_group(cls, user_id, repo_id=None):
q = Session().query(UserGroupRepoToPerm, Repository, Permission)\
@ -2817,11 +2850,38 @@ class Permission(Base, BaseModel):
q = q.filter(UserGroupRepoToPerm.repository_id == repo_id)
return q.all()
@classmethod
def get_default_repo_branch_perms_from_user_group(cls, user_id, repo_id=None):
q = Session().query(UserGroupToRepoBranchPermission, UserGroupRepoToPerm, Permission) \
.join(
Permission,
UserGroupToRepoBranchPermission.permission_id == Permission.permission_id) \
.join(
UserGroupRepoToPerm,
UserGroupToRepoBranchPermission.rule_to_perm_id == UserGroupRepoToPerm.users_group_to_perm_id) \
.join(
UserGroup,
UserGroupRepoToPerm.users_group_id == UserGroup.users_group_id) \
.join(
UserGroupMember,
UserGroupRepoToPerm.users_group_id == UserGroupMember.users_group_id) \
.filter(
UserGroupMember.user_id == user_id,
UserGroup.users_group_active == true())
if repo_id:
q = q.filter(UserGroupToRepoBranchPermission.repository_id == repo_id)
return q.order_by(UserGroupToRepoBranchPermission.rule_order).all()
@classmethod
def get_default_group_perms(cls, user_id, repo_group_id=None):
q = Session().query(UserRepoGroupToPerm, RepoGroup, Permission)\
.join((Permission, UserRepoGroupToPerm.permission_id == Permission.permission_id))\
.join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
.join(
Permission,
UserRepoGroupToPerm.permission_id == Permission.permission_id)\
.join(
RepoGroup,
UserRepoGroupToPerm.group_id == RepoGroup.group_id)\
.filter(UserRepoGroupToPerm.user_id == user_id)
if repo_group_id:
q = q.filter(UserRepoGroupToPerm.group_id == repo_group_id)
@ -2910,6 +2970,8 @@ class UserRepoToPerm(Base, BaseModel):
repository = relationship('Repository')
permission = relationship('Permission')
branch_perm_entry = relationship('UserToRepoBranchPermission', cascade="all, delete, delete-orphan", lazy='joined')
@classmethod
def create(cls, user, repository, permission):
n = cls()
@ -4470,6 +4532,100 @@ def set_task_uid(mapper, connection, target):
target.task_uid = ScheduleEntry.get_uid(target)
class _BaseBranchPerms(BaseModel):
@classmethod
def compute_hash(cls, value):
return sha1_safe(value)
@hybrid_property
def branch_pattern(self):
return self._branch_pattern or '*'
@hybrid_property
def branch_hash(self):
return self._branch_hash
def _validate_glob(self, value):
re.compile('^' + glob2re(value) + '$')
@branch_pattern.setter
def branch_pattern(self, value):
self._validate_glob(value)
self._branch_pattern = value or '*'
# set the Hash when setting the branch pattern
self._branch_hash = self.compute_hash(self._branch_pattern)
def matches(self, branch):
"""
Check if this the branch matches entry
:param branch: branch name for the commit
"""
branch = branch or ''
branch_matches = True
if branch:
branch_regex = re.compile('^' + glob2re(self.branch_pattern) + '$')
branch_matches = bool(branch_regex.search(branch))
return branch_matches
class UserToRepoBranchPermission(Base, _BaseBranchPerms):
__tablename__ = 'user_to_repo_branch_permissions'
__table_args__ = (
{'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8', 'sqlite_autoincrement': True,}
)
branch_rule_id = Column('branch_rule_id', Integer(), primary_key=True)
repository_id = Column('repository_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
repo = relationship('Repository', backref='user_branch_perms')
permission_id = Column('permission_id', Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
permission = relationship('Permission')
rule_to_perm_id = Column('rule_to_perm_id', Integer(), ForeignKey('repo_to_perm.repo_to_perm_id'), nullable=False, unique=None, default=None)
user_repo_to_perm = relationship('UserRepoToPerm')
rule_order = Column('rule_order', Integer(), nullable=False)
_branch_pattern = Column('branch_pattern', UnicodeText().with_variant(UnicodeText(2048), 'mysql'), default=u'*') # glob
_branch_hash = Column('branch_hash', UnicodeText().with_variant(UnicodeText(2048), 'mysql'))
def __unicode__(self):
return u'<UserBranchPermission(%s => %r)>' % (
self.user_repo_to_perm, self.branch_pattern)
class UserGroupToRepoBranchPermission(Base, _BaseBranchPerms):
__tablename__ = 'user_group_to_repo_branch_permissions'
__table_args__ = (
{'extend_existing': True, 'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8', 'sqlite_autoincrement': True,}
)
branch_rule_id = Column('branch_rule_id', Integer(), primary_key=True)
repository_id = Column('repository_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
repo = relationship('Repository', backref='user_group_branch_perms')
permission_id = Column('permission_id', Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
permission = relationship('Permission')
rule_to_perm_id = Column('rule_to_perm_id', Integer(), ForeignKey('users_group_repo_to_perm.users_group_to_perm_id'), nullable=False, unique=None, default=None)
user_group_repo_to_perm = relationship('UserGroupRepoToPerm')
rule_order = Column('rule_order', Integer(), nullable=False)
_branch_pattern = Column('branch_pattern', UnicodeText().with_variant(UnicodeText(2048), 'mysql'), default=u'*') # glob
_branch_hash = Column('branch_hash', UnicodeText().with_variant(UnicodeText(2048), 'mysql'))
def __unicode__(self):
return u'<UserBranchPermission(%s => %r)>' % (
self.user_group_repo_to_perm, self.branch_pattern)
class DbMigrateVersion(Base, BaseModel):
__tablename__ = 'db_migrate_version'
__table_args__ = (

View file

@ -504,12 +504,26 @@ def ObjectPermissionsForm(localizer, repo_perms_choices, group_perms_choices,
overwrite_default_repo = v.StringBoolean(if_missing=False)
overwrite_default_group = v.StringBoolean(if_missing=False)
overwrite_default_user_group = v.StringBoolean(if_missing=False)
default_repo_perm = v.OneOf(repo_perms_choices)
default_group_perm = v.OneOf(group_perms_choices)
default_user_group_perm = v.OneOf(user_group_perms_choices)
return _ObjectPermissionsForm
def BranchPermissionsForm(localizer, branch_perms_choices):
_ = localizer
class _BranchPermissionsForm(formencode.Schema):
allow_extra_fields = True
filter_extra_fields = True
overwrite_default_branch = v.StringBoolean(if_missing=False)
default_branch_perm = v.OneOf(branch_perms_choices)
return _BranchPermissionsForm
def UserPermissionsForm(localizer, create_choices, create_on_write_choices,
repo_group_create_choices, user_group_create_choices,
fork_choices, inherit_default_permissions_choices):

View file

@ -31,7 +31,7 @@ from sqlalchemy.exc import DatabaseError
from rhodecode.model import BaseModel
from rhodecode.model.db import (
User, Permission, UserToPerm, UserRepoToPerm, UserRepoGroupToPerm,
UserUserGroupToPerm, UserGroup, UserGroupToPerm)
UserUserGroupToPerm, UserGroup, UserGroupToPerm, UserToRepoBranchPermission)
from rhodecode.lib.utils2 import str2bool, safe_int
log = logging.getLogger(__name__)
@ -59,6 +59,9 @@ class PermissionModel(BaseModel):
'default_repo_perm': None,
'default_group_perm': None,
'default_user_group_perm': None,
# branch
'default_branch_perm': None,
}
def set_global_permission_choices(self, c_obj, gettext_translator):
@ -82,6 +85,12 @@ class PermissionModel(BaseModel):
('usergroup.write', _('Write'),),
('usergroup.admin', _('Admin'),)]
c_obj.branch_perms_choices = [
('branch.none', _('Protected/No Access'),),
('branch.merge', _('Web merge'),),
('branch.push', _('Push'),),
('branch.push_force', _('Force Push'),)]
c_obj.register_choices = [
('hg.register.none', _('Disabled')),
('hg.register.manual_activate', _('Allowed with manual account activation')),
@ -133,6 +142,10 @@ class PermissionModel(BaseModel):
if perm.permission.permission_name.startswith('usergroup.'):
defaults['default_user_group_perm' + suffix] = perm.permission.permission_name
# branch
if perm.permission.permission_name.startswith('branch.'):
defaults['default_branch_perm' + suffix] = perm.permission.permission_name
# creation of objects
if perm.permission.permission_name.startswith('hg.create.write_on_repogroup'):
defaults['default_repo_create_on_write' + suffix] = perm.permission.permission_name
@ -199,6 +212,9 @@ class PermissionModel(BaseModel):
'default_repo_perm': 'repository.',
'default_group_perm': 'group.',
'default_user_group_perm': 'usergroup.',
# branch
'default_branch_perm': 'branch.',
}[field_name]
for field in keep_fields:
pat = get_pat(field)
@ -236,8 +252,12 @@ class PermissionModel(BaseModel):
_global_perms = self.global_perms.copy()
if obj_type not in ['user', 'user_group']:
raise ValueError("obj_type must be on of 'user' or 'user_group'")
if len(_global_perms) != len(Permission.DEFAULT_USER_PERMISSIONS):
raise Exception('Inconsistent permissions definition')
global_perms = len(_global_perms)
default_user_perms = len(Permission.DEFAULT_USER_PERMISSIONS)
if global_perms != default_user_perms:
raise Exception(
'Inconsistent permissions definition. Got {} vs {}'.format(
global_perms, default_user_perms))
if obj_type == 'user':
self._clear_user_perms(object.user_id, preserve)
@ -337,8 +357,8 @@ class PermissionModel(BaseModel):
def create_default_user_group_permissions(self, user_group, force=False):
"""
Creates only missing default permissions for user group, if force is set it
resets the default permissions for that user group
Creates only missing default permissions for user group, if force is
set it resets the default permissions for that user group
:param user_group:
:param force:
@ -366,6 +386,7 @@ class PermissionModel(BaseModel):
'default_repo_perm',
'default_group_perm',
'default_user_group_perm',
'default_branch_perm',
'default_repo_group_create',
'default_user_group_create',
@ -392,6 +413,7 @@ class PermissionModel(BaseModel):
'default_repo_perm',
'default_group_perm',
'default_user_group_perm',
'default_branch_perm',
'default_register',
'default_password_reset',
@ -414,6 +436,7 @@ class PermissionModel(BaseModel):
'default_repo_perm',
'default_group_perm',
'default_user_group_perm',
'default_branch_perm',
'default_register',
'default_password_reset',
@ -440,6 +463,7 @@ class PermissionModel(BaseModel):
'default_repo_create',
'default_fork_create',
'default_inherit_default_permissions',
'default_branch_perm',
'default_register',
'default_password_reset',
@ -477,8 +501,57 @@ class PermissionModel(BaseModel):
.all():
g2p.permission = _def
self.sa.add(g2p)
# COMMIT
self.sa.commit()
except (DatabaseError,):
log.exception('Failed to set default object permissions')
self.sa.rollback()
raise
def update_branch_permissions(self, form_result):
if 'perm_user_id' in form_result:
perm_user = User.get(safe_int(form_result['perm_user_id']))
else:
# used mostly to do lookup for default user
perm_user = User.get_by_username(form_result['perm_user_name'])
try:
# stage 2 reset defaults and set them from form data
self._set_new_user_perms(perm_user, form_result, preserve=[
'default_repo_perm',
'default_group_perm',
'default_user_group_perm',
'default_repo_group_create',
'default_user_group_create',
'default_repo_create_on_write',
'default_repo_create',
'default_fork_create',
'default_inherit_default_permissions',
'default_register',
'default_password_reset',
'default_extern_activate'])
# overwrite default branch permissions
if form_result['overwrite_default_branch']:
_def_name = \
form_result['default_branch_perm'].split('branch.')[-1]
_def = Permission.get_by_key('branch.' + _def_name)
# TODO(marcink): those are bind to repo, perms, we need to unfold user somehow from this
for g2p in self.sa.query(UserToRepoBranchPermission) \
.filter(UserToRepoBranchPermission.user == perm_user) \
.all():
g2p.permission = _def
self.sa.add(g2p)
# COMMIT
self.sa.commit()
except (DatabaseError,):
log.exception('Failed to set default branch permissions')
self.sa.rollback()
raise

View file

@ -116,6 +116,11 @@
.label;
padding-top: 5px;
}
.label-branch-perm {
.label;
width: 20px;
}
// Used to position content on the right side of a .label
.content,
.side-by-side-selector {
@ -135,6 +140,15 @@
}
}
.input-branch-perm {
.input;
margin-left: 90px;
}
.input-branch-perm-order {
width: 40px;
}
.checkboxes,
.input,
.select {
@ -169,6 +183,9 @@
}
.input {
.branch-perm {
width: 80px;
}
.medium {
width: @fields-input-m;
}

View file

@ -462,7 +462,7 @@ ul.auth_plugins {
.radios {
position: relative;
width: 405px;
width: 505px;
}
}

View file

@ -159,3 +159,9 @@
&:extend(.icon-svn-transparent:before);
}
}
.icon-user-group:before {
&:extend(.icon-group:before);
margin: 0;
font-size: 16px;
}

View file

@ -109,13 +109,21 @@
&.read {
&:extend(.tag1);
}
&.write {
&:extend(.tag4);
}
&.admin {
&:extend(.tag5);
}
&.merge {
&:extend(.tag1);
}
&.push {
&:extend(.tag4);
}
&.push_force {
&:extend(.tag5);
}
}
.phase-draft {

View file

@ -87,6 +87,7 @@ function registerRCRoutes() {
pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
pyroutes.register('admin_permissions_branch', '/_admin/permissions/branch', []);
pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
@ -233,6 +234,8 @@ function registerRCRoutes() {
pyroutes.register('edit_repo_advanced_hooks', '/%(repo_name)s/settings/advanced/hooks', ['repo_name']);
pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
pyroutes.register('edit_repo_perms_branch', '/%(repo_name)s/settings/branch_permissions', ['repo_name']);
pyroutes.register('edit_repo_perms_branch_delete', '/%(repo_name)s/settings/branch_permissions/%(rule_id)s/delete', ['repo_name', 'rule_id']);
pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);

View file

@ -38,7 +38,7 @@ $(document).ready(function() {
{ data: {"_": "group_name",
"sort": "group_name"}, title: "${_('Name')}", className: "td-componentname," ,
render: function (data,type,full,meta)
{return '<div><i class="icon-group" title="User group">'+data+'</i></div>'}},
{return '<div><i class="icon-user-group" title="User group">'+data+'</i></div>'}},
{ data: {"_": "group_description",
"sort": "group_description"}, title: "${_('Description')}", className: "td-description" },

View file

@ -38,6 +38,9 @@
<li class="${'active' if c.active=='objects' else ''}">
<a href="${h.route_path('admin_permissions_object')}">${_('Object')}</a>
</li>
<li class="${'active' if c.active=='branch' else ''}">
<a href="${h.route_path('admin_permissions_branch')}">${_('Branch')}</a>
</li>
<li class="${'active' if c.active=='ips' else ''}">
<a href="${h.route_path('admin_permissions_ips')}">${_('IP Whitelist')}</a>
</li>

View file

@ -0,0 +1,9 @@
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">${_('Default Permissions for Branches.')}</h3>
</div>
<div class="panel-body">
<h4>${_('This feature is available in RhodeCode EE edition only. Contact {sales_email} to obtain a trial license.').format(sales_email='<a href="mailto:sales@rhodecode.com">sales@rhodecode.com</a>')|n}</h4>
<img style="width: 100%; height: 100%" src="${h.asset('images/ee_features/admin_branch_permissions.png')}"/>
</div>
</div>

View file

@ -3,7 +3,10 @@
<h3 class="panel-title">${_('Default Permissions for Repositories, User Groups and Repository Groups.')}</h3>
</div>
<div class="panel-body">
<p>${_('Default system permissions. Each permissions management entity will be created with the following default settings. Check the overwrite checkbox to force any permission changes on already existing settings.')}
<p>
${_('Default access permissions. This defines permissions for the `default` user from which other users inherit permissions.')}
<br/>
${_('Check the overwrite checkbox to force change all previously defined permissions for `default` user to the new selected value.')}
</p>
${h.secure_form(h.route_path('admin_permissions_object_update'), request=request)}
<div class="form">
@ -45,7 +48,7 @@
${h.select('default_user_group_perm','',c.user_group_perms_choices)}
${h.checkbox('overwrite_default_user_group','true')}
<label for="overwrite_default_user_group">
<span class="tooltip" title="${h.tooltip(_('All default permissions on each user group will be reset to chosen permission, note that all custom default permission on repository groups will be lost'))}">
<span class="tooltip" title="${h.tooltip(_('All default permissions on each user group will be reset to chosen permission, note that all custom default permission on user groups will be lost'))}">
${_('Overwrite Existing Settings')}
</span>
</label>

View file

@ -57,7 +57,7 @@
%if _user.username != h.DEFAULT_USER:
<span class="btn btn-link btn-danger revoke_perm"
member="${_user.user_id}" member_type="user">
<i class="icon-remove"></i> ${_('Revoke')}
${_('Revoke')}
</span>
%endif
</td>
@ -92,7 +92,7 @@
<td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'group.write', checked=_user_group.permission=='group.write')}</td>
<td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'group.admin', checked=_user_group.permission=='group.admin')}</td>
<td class="td-componentname">
<i class="icon-group" ></i>
<i class="icon-user-group"></i>
%if h.HasPermissionAny('hg.admin')():
<a href="${h.route_path('edit_user_group',user_group_id=_user_group.users_group_id)}">
${_user_group.users_group_name}
@ -104,17 +104,27 @@
<td class="td-action">
<span class="btn btn-link btn-danger revoke_perm"
member="${_user_group.users_group_id}" member_type="user_group">
<i class="icon-remove"></i> ${_('Revoke')}
${_('Revoke')}
</span>
</td>
</tr>
%endfor
<tr class="new_members" id="add_perm_input"></tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>
<span id="add_perm" class="link">
${_('Add user/user group')}
</span>
</td>
</tr>
</table>
<div id="add_perm" class="link">
${_('Add new')}
</div>
<div class="fields">
<div class="field">
<div class="label label-radio">

View file

@ -48,6 +48,9 @@
<li class="${'active' if c.active=='permissions' else ''}">
<a href="${h.route_path('edit_repo_perms', repo_name=c.repo_name)}">${_('Permissions')}</a>
</li>
<li class="${'active' if c.active=='permissions_branch' else ''}">
<a href="${h.route_path('edit_repo_perms_branch', repo_name=c.repo_name)}">${_('Branch Permissions')}</a>
</li>
<li class="${'active' if c.active=='advanced' else ''}">
<a href="${h.route_path('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a>
</li>

View file

@ -67,7 +67,7 @@
%if _user.username != h.DEFAULT_USER:
<span class="btn btn-link btn-danger revoke_perm"
member="${_user.user_id}" member_type="user">
<i class="icon-remove"></i> ${_('Revoke')}
${_('Revoke')}
</span>
%endif
</td>
@ -83,7 +83,7 @@
<td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.write', checked=_user_group.permission=='repository.write')}</td>
<td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.admin', checked=_user_group.permission=='repository.admin')}</td>
<td class="td-componentname">
<i class="icon-group" ></i>
<i class="icon-user-group"></i>
%if h.HasPermissionAny('hg.admin')():
<a href="${h.route_path('edit_user_group',user_group_id=_user_group.users_group_id)}">
${_user_group.users_group_name}
@ -95,16 +95,28 @@
<td class="td-action">
<span class="btn btn-link btn-danger revoke_perm"
member="${_user_group.users_group_id}" member_type="user_group">
<i class="icon-remove"></i> ${_('Revoke')}
${_('Revoke')}
</span>
</td>
</tr>
%endfor
<tr class="new_members" id="add_perm_input"></tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>
<span id="add_perm" class="link">
${_('Add user/user group')}
</span>
</td>
</tr>
</table>
<div id="add_perm" class="link">
${_('Add new')}
</div>
<div class="buttons">
${h.submit('save',_('Save'),class_="btn btn-primary")}
${h.reset('reset',_('Reset'),class_="btn btn-danger")}

View file

@ -0,0 +1,9 @@
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">${_('Repository Branch Permissions.')}</h3>
</div>
<div class="panel-body">
<h4>${_('This feature is available in RhodeCode EE edition only. Contact {sales_email} to obtain a trial license.').format(sales_email='<a href="mailto:sales@rhodecode.com">sales@rhodecode.com</a>')|n}</h4>
<img style="width: 100%; height: 100%" src="${h.asset('images/ee_features/repo_branch_permissions.png')}"/>
</div>
</div>

View file

@ -59,7 +59,7 @@
%if _user.username != h.DEFAULT_USER:
<span class="btn btn-link btn-danger revoke_perm"
member="${_user.user_id}" member_type="user">
<i class="icon-remove"></i> ${_('revoke')}
${_('Revoke')}
</span>
%endif
</td>
@ -94,7 +94,7 @@
<td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'usergroup.write')}</td>
<td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'usergroup.admin')}</td>
<td class="td-user">
<i class="icon-group" ></i>
<i class="icon-user-group"></i>
%if h.HasPermissionAny('hg.admin')():
<a href="${h.route_path('edit_user_group',user_group_id=_user_group.users_group_id)}">
${_user_group.users_group_name}
@ -106,16 +106,26 @@
<td class="td-action">
<span class="btn btn-link btn-danger revoke_perm"
member="${_user_group.users_group_id}" member_type="user_group">
<i class="icon-remove"></i> ${_('revoke')}
${_('Revoke')}
</span>
</td>
</tr>
%endfor
<tr class="new_members" id="add_perm_input"></tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>
<span id="add_perm" class="link">
${_('Add user/user group')}
</span>
</td>
</tr>
</table>
<div id="add_perm" class="link">
${_('Add new')}
</div>
<div class="buttons">
${h.submit('save',_('Save'),class_="btn btn-primary")}
${h.reset('reset',_('Reset'),class_="btn btn-danger")}

View file

@ -53,7 +53,7 @@ $(document).ready(function() {
{ data: {"_": "group_name",
"sort": "group_name"}, title: "${_('Name')}", className: "td-componentname," ,
render: function (data,type,full,meta)
{return '<div><i class="icon-group" title="User group">'+data+'</i></div>'}},
{return '<div><i class="icon-user-group" title="User group">'+data+'</i></div>'}},
{ data: {"_": "group_description",
"sort": "group_description"}, title: "${_('Description')}", className: "td-description" },

View file

@ -4,11 +4,20 @@
## ${p.perms_summary(c.perm_user.permissions)}
<%def name="perms_summary(permissions, show_all=False, actions=True, side_link=None)">
<% section_to_label = {
'global': 'Global Permissions',
'repository_branches': 'Repository Branch Rules',
'repositories': 'Repository Permissions',
'user_groups': 'User Group Permissions',
'repositories_groups': 'Repository Group Permissions',
} %>
<div id="perms" class="table fields">
%for section in sorted(permissions.keys()):
%for section in sorted(permissions.keys(), key=lambda item: {'global': 0, 'repository_branches': 1}.get(item, 1000)):
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">${section.replace("_"," ").capitalize()}</h3>
<div class="panel-heading" id="${section.replace("_","-")}-permissions">
<h3 class="panel-title">${section_to_label.get(section, section)} - ${len(permissions[section])}
<a class="permalink" href="#${section.replace("_","-")}-permissions"> ¶</a>
</h3>
% if side_link:
<div class="pull-right">
<a href="${side_link}">${_('in JSON format')}</a>
@ -18,15 +27,24 @@
<div class="panel-body">
<div class="perms_section_head field">
<div class="radios">
%if section != 'global':
% if section == 'repository_branches':
<span class="permissions_boxes">
<span class="desc">${_('show')}: </span>
${h.checkbox('perms_filter_none_%s' % section, 'none', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='none')} <label for="${'perms_filter_none_%s' % section}"><span class="perm_tag none">${_('none')}</span></label>
${h.checkbox('perms_filter_read_%s' % section, 'read', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='read')} <label for="${'perms_filter_read_%s' % section}"><span class="perm_tag read">${_('read')}</span></label>
${h.checkbox('perms_filter_write_%s' % section, 'write', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='write')} <label for="${'perms_filter_write_%s' % section}"> <span class="perm_tag write">${_('write')}</span></label>
${h.checkbox('perms_filter_admin_%s' % section, 'admin', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='admin')} <label for="${'perms_filter_admin_%s' % section}"><span class="perm_tag admin">${_('admin')}</span></label>
${h.checkbox('perms_filter_none_%s' % section, 'none', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='none')} <label for="${'perms_filter_none_{}'.format(section)}"><span class="perm_tag none">${_('none')}</span></label>
${h.checkbox('perms_filter_merge_%s' % section, 'merge', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='merge')} <label for="${'perms_filter_merge_{}'.format(section)}"><span class="perm_tag merge">${_('merge')}</span></label>
${h.checkbox('perms_filter_push_%s' % section, 'push', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='push')} <label for="${'perms_filter_push_{}'.format(section)}"> <span class="perm_tag push">${_('push')}</span></label>
${h.checkbox('perms_filter_push_force_%s' % section, 'push_force', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='push_force')} <label for="${'perms_filter_push_force_{}'.format(section)}"><span class="perm_tag push_force">${_('push force')}</span></label>
</span>
%endif
% elif section != 'global':
<span class="permissions_boxes">
<span class="desc">${_('show')}: </span>
${h.checkbox('perms_filter_none_%s' % section, 'none', '', class_='perm_filter filter_%s' % section, section=section, perm_type='none')} <label for="${'perms_filter_none_{}'.format(section)}"><span class="perm_tag none">${_('none')}</span></label>
${h.checkbox('perms_filter_read_%s' % section, 'read', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='read')} <label for="${'perms_filter_read_{}'.format(section)}"><span class="perm_tag read">${_('read')}</span></label>
${h.checkbox('perms_filter_write_%s' % section, 'write', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='write')} <label for="${'perms_filter_write_{}'.format(section)}"> <span class="perm_tag write">${_('write')}</span></label>
${h.checkbox('perms_filter_admin_%s' % section, 'admin', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='admin')} <label for="${'perms_filter_admin_{}'.format(section)}"><span class="perm_tag admin">${_('admin')}</span></label>
</span>
% endif
</div>
</div>
<div class="field">
@ -72,12 +90,10 @@
%endif
%else:
%if not val_lbl:
${
{'false': False,
${{'false': False,
'true': True,
'none': False,
'repository': True}.get(val[1][0] if 0 < len(val[1]) else 'false')
}
'repository': True}.get(val[1][0] if 0 < len(val[1]) else 'false')}
%else:
<span class="perm_tag ${val[1][0]}">${val_lbl}.${val[1][0]}</span>
%endif
@ -142,7 +158,72 @@
edit_url=h.route_path('user_edit_global_perms', user_id=c.user.user_id), edit_global_url=h.route_path('admin_permissions_object'))}
</tbody>
## Branch perms
%elif section == 'repository_branches':
<thead>
<tr>
<th>${_('Name')}</th>
<th>${_('Pattern')}</th>
<th>${_('Permission')}</th>
%if actions:
<th>${_('Edit Branch Permission')}</th>
%endif
</thead>
<tbody class="section_${section}">
<%
def name_sorter(permissions):
def custom_sorter(item):
return item[0]
return sorted(permissions, key=custom_sorter)
def branch_sorter(permissions):
def custom_sorter(item):
## none, merge, push, push_force
section = item[1].split('.')[-1]
section_importance = {'none': u'0',
'merge': u'1',
'push': u'2',
'push_force': u'3'}.get(section)
## sort by importance + name
return section_importance + item[0]
return sorted(permissions, key=custom_sorter)
%>
%for k, section_perms in name_sorter(permissions[section].items()):
% for pattern, perm in branch_sorter(section_perms.items()):
<tr class="perm_row ${'{}_{}'.format(section, perm.split('.')[-1])}">
<td class="td-name">
<a href="${h.route_path('repo_summary',repo_name=k)}">${k}</a>
</td>
<td>${pattern}</td>
<td class="td-tags">
## TODO: calculate origin somehow
## % for i, ((_pat, perm), origin) in enumerate((permissions[section].perm_origin_stack[k])):
<div>
<% i = 0 %>
<% origin = 'unknown' %>
<% _css_class = i > 0 and 'perm_overriden' or '' %>
<span class="${_css_class} perm_tag ${perm.split('.')[-1]}">
${perm}
##(${origin})
</span>
</div>
## % endfor
</td>
%if actions:
<td class="td-action">
<a href="${h.route_path('edit_repo_perms_branch',repo_name=k)}">${_('edit')}</a>
</td>
%endif
</tr>
% endfor
%endfor
</tbody>
## Repos/Repo Groups/users groups perms
%else:
## none/read/write/admin permissions on groups/repos etc
<thead>
<tr>
@ -167,8 +248,11 @@
return sorted(permissions, key=custom_sorter)
%>
%for k, section_perm in sorter(permissions[section].items()):
%if section_perm.split('.')[-1] != 'none' or show_all:
<tr class="perm_row ${'%s_%s' % (section, section_perm.split('.')[-1])}">
<% perm_value = section_perm.split('.')[-1] %>
<% _css_class = 'display:none' if perm_value in ['none'] else '' %>
%if perm_value != 'none' or show_all:
<tr class="perm_row ${'{}_{}'.format(section, section_perm.split('.')[-1])}" style="${_css_class}">
<td class="td-name">
%if section == 'repositories':
<a href="${h.route_path('repo_summary',repo_name=k)}">${k}</a>
@ -183,7 +267,7 @@
%if hasattr(permissions[section], 'perm_origin_stack'):
<div>
%for i, (perm, origin) in enumerate(reversed(permissions[section].perm_origin_stack[k])):
<% _css_class = i > 0 and 'perm_overriden' or '' %>
% if i > 0:
<div style="color: #979797">
<i class="icon-arrow_up"></i>
@ -193,7 +277,7 @@
% endif
<div>
<span class="${i > 0 and 'perm_overriden' or ''} perm_tag ${perm.split('.')[-1]}">
<span class="${_css_class} perm_tag ${perm.split('.')[-1]}">
${perm} (${origin})
</span>
</div>
@ -220,7 +304,7 @@
%endfor
<tr id="empty_${section}" class="noborder" style="display:none;">
<td colspan="6">${_('No permission defined')}</td>
<td colspan="6">${_('No matching permission defined')}</td>
</tr>
</tbody>
@ -236,15 +320,16 @@
<script>
$(document).ready(function(){
var show_empty = function(section){
var showEmpty = function(section){
var visible = $('.section_{0} tr.perm_row:visible'.format(section)).length;
if(visible == 0){
if(visible === 0){
$('#empty_{0}'.format(section)).show();
}
else{
$('#empty_{0}'.format(section)).hide();
}
};
$('.perm_filter').on('change', function(e){
var self = this;
var section = $(this).attr('section');
@ -261,7 +346,7 @@
$('.'+section+'_'+perm_type).hide();
}
});
show_empty(section);
showEmpty(section);
})
})
</script>

View file

@ -284,7 +284,7 @@
<%def name="user_group_name(user_group_name)">
<div>
<i class="icon-group" title="${_('User group')}"></i>
<i class="icon-user-group" title="${_('User group')}"></i>
${h.link_to_group(user_group_name)}
</div>
</%def>

View file

@ -534,7 +534,7 @@
</tbody>
</table>
<div class="link" id="add_perm">
Add new
Add user/user group
</div>

View file

@ -8,12 +8,11 @@
<div class="panel-heading">${title}</div>
<div class="panel-body">
<div tal:condition="errormsg"
class="clearfix alert alert-danger">
<p i18n:translate="">
<div tal:condition="errormsg" class="clearfix alert alert-error">
<span i18n:translate="">
There was a problem with this section
</p>
<p>${errormsg}</p>
</span>
<div>${errormsg}</div>
</div>
<div tal:condition="description">

View file

@ -140,6 +140,10 @@ class TestPermissions(object):
assert repo_perms(user)[repo.repo_name] == 'repository.admin'
repo.user = org_owner
def test_default_owner_branch_perms(self, user_util, test_user_group):
user = user_util.create_user()
assert branch_perms(user) == {}
def test_default_owner_repo_group_perms(self, user_util, test_repo_group):
user = user_util.create_user()
org_owner = test_repo_group.user
@ -360,13 +364,15 @@ class TestPermissions(object):
user_model.revoke_perm(self.u1, 'hg.fork.repository')
user_model.grant_perm(self.u1, 'hg.fork.none')
# TODO(marcink): check branch permissions now ?
# make sure inherit flag is turned off
self.u1.inherit_default_permissions = False
Session().commit()
# this user will have non inherited permissions from he's
# explicitly set permissions
assert global_perms(self.u1) == set([
assert global_perms(self.u1) == {
'hg.create.none',
'hg.fork.none',
'hg.register.manual_activate',
@ -375,7 +381,8 @@ class TestPermissions(object):
'repository.read',
'group.read',
'usergroup.read',
])
'branch.push_force',
}
def test_non_inherited_permissions_from_default_on_user_disabled(self):
user_model = UserModel()
@ -396,9 +403,11 @@ class TestPermissions(object):
self.u1.inherit_default_permissions = False
Session().commit()
# TODO(marcink): check branch perms
# this user will have non inherited permissions from he's
# explicitly set permissions
assert global_perms(self.u1) == set([
assert global_perms(self.u1) == {
'hg.create.repository',
'hg.fork.repository',
'hg.register.manual_activate',
@ -407,7 +416,8 @@ class TestPermissions(object):
'repository.read',
'group.read',
'usergroup.read',
])
'branch.push_force',
}
@pytest.mark.parametrize('perm, expected_perm', [
('hg.inherit_default_perms.false', 'repository.none', ),
@ -425,8 +435,10 @@ class TestPermissions(object):
self.u1.inherit_default_permissions = True
Session().commit()
# TODO(marcink): check branch perms
# this user will have inherited permissions from default user
assert global_perms(self.u1) == set([
assert global_perms(self.u1) == {
'hg.create.none',
'hg.fork.none',
'hg.register.manual_activate',
@ -435,11 +447,12 @@ class TestPermissions(object):
'repository.read',
'group.read',
'usergroup.read',
'branch.push_force',
'hg.create.write_on_repogroup.true',
'hg.usergroup.create.false',
'hg.repogroup.create.false',
perm,
])
perm
}
assert set(repo_perms(self.u1).values()) == set([expected_perm])
@ -693,6 +706,11 @@ def repo_perms(user):
return auth_user.permissions['repositories']
def branch_perms(user):
auth_user = AuthUser(user_id=user.user_id)
return auth_user.permissions['repository_branches']
def group_perms(user):
auth_user = AuthUser(user_id=user.user_id)
return auth_user.permissions['repositories_groups']

View file

@ -66,5 +66,5 @@ class TestTags(BackendTestMixin):
def test_name_with_slash(self):
self.repo.tag('19/10/11', 'joe')
assert '19/10/11' in self.repo.tags
self.repo.tag('11', 'joe')
assert '11' in self.repo.tags
self.repo.tag('rel.11', 'joe')
assert 'rel.11' in self.repo.tags