##// END OF EJS Templates
branch permissions: added logic to define in UI branch permissions....
marcink -
r2975:2d612d18 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,45 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2011-2018 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22
23 from pyramid.view import view_config
24
25 from rhodecode.apps._base import RepoAppView
26 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
27
28 log = logging.getLogger(__name__)
29
30
31 class RepoSettingsBranchPermissionsView(RepoAppView):
32
33 def load_default_context(self):
34 c = self._get_local_tmpl_context()
35 return c
36
37 @LoginRequired()
38 @HasRepoPermissionAnyDecorator('repository.admin')
39 @view_config(
40 route_name='edit_repo_perms_branch', request_method='GET',
41 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
42 def branch_permissions(self):
43 c = self.load_default_context()
44 c.active = 'permissions_branch'
45 return self._get_template_context(c)
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -0,0 +1,46 b''
1 import logging
2
3 from sqlalchemy import *
4 from sqlalchemy.engine import reflection
5 from sqlalchemy.dialects.mysql import LONGTEXT
6
7 from alembic.migration import MigrationContext
8 from alembic.operations import Operations
9
10 from rhodecode.lib.dbmigrate.utils import create_default_permissions, \
11 create_default_object_permission
12 from rhodecode.model import meta
13 from rhodecode.lib.dbmigrate.versions import _reset_base, notify
14
15 log = logging.getLogger(__name__)
16
17
18 def upgrade(migrate_engine):
19 """
20 Upgrade operations go here.
21 Don't create your own engine; bind migrate_engine to your metadata
22 """
23 _reset_base(migrate_engine)
24 from rhodecode.lib.dbmigrate.schema import db_4_13_0_0 as db
25
26 # issue fixups
27 fixups(db, meta.Session)
28
29
30 def downgrade(migrate_engine):
31 meta = MetaData()
32 meta.bind = migrate_engine
33
34
35 def fixups(models, _SESSION):
36 # create default permissions
37 create_default_permissions(_SESSION, models)
38 log.info('created default global permissions definitions')
39 _SESSION().commit()
40
41 # # fix default object permissions
42 # create_default_object_permission(_SESSION, models)
43
44 log.info('created default permission')
45 _SESSION().commit()
46
@@ -0,0 +1,39 b''
1 import logging
2
3 from sqlalchemy import *
4 from sqlalchemy.engine import reflection
5 from sqlalchemy.dialects.mysql import LONGTEXT
6
7 from alembic.migration import MigrationContext
8 from alembic.operations import Operations
9
10 from rhodecode.model import meta
11 from rhodecode.lib.dbmigrate.versions import _reset_base, notify
12
13 log = logging.getLogger(__name__)
14
15
16 def upgrade(migrate_engine):
17 """
18 Upgrade operations go here.
19 Don't create your own engine; bind migrate_engine to your metadata
20 """
21 _reset_base(migrate_engine)
22 from rhodecode.lib.dbmigrate.schema import db_4_13_0_0 as db
23
24 db.UserToRepoBranchPermission.__table__.create()
25 db.UserGroupToRepoBranchPermission.__table__.create()
26
27 # issue fixups
28 fixups(db, meta.Session)
29
30
31 def downgrade(migrate_engine):
32 meta = MetaData()
33 meta.bind = migrate_engine
34
35
36 def fixups(models, _SESSION):
37 pass
38
39
@@ -0,0 +1,43 b''
1 import logging
2
3 from sqlalchemy import *
4
5 from rhodecode.lib.dbmigrate.utils import (
6 create_default_object_permission, create_default_permissions)
7
8 from rhodecode.model import meta
9 from rhodecode.lib.dbmigrate.versions import _reset_base, notify
10
11 log = logging.getLogger(__name__)
12
13
14 def upgrade(migrate_engine):
15 """
16 Upgrade operations go here.
17 Don't create your own engine; bind migrate_engine to your metadata
18 """
19 _reset_base(migrate_engine)
20 from rhodecode.lib.dbmigrate.schema import db_4_13_0_0 as db
21
22 # issue fixups
23 fixups(db, meta.Session)
24
25
26 def downgrade(migrate_engine):
27 meta = MetaData()
28 meta.bind = migrate_engine
29
30
31 def fixups(models, _SESSION):
32 # create default permissions
33 create_default_permissions(_SESSION, models)
34 log.info('created default global permissions definitions')
35 _SESSION().commit()
36
37 # fix default object permissions
38 create_default_object_permission(_SESSION, models)
39
40 log.info('created default permission')
41 _SESSION().commit()
42
43
@@ -0,0 +1,9 b''
1 <div class="panel panel-default">
2 <div class="panel-heading">
3 <h3 class="panel-title">${_('Default Permissions for Branches.')}</h3>
4 </div>
5 <div class="panel-body">
6 <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>
7 <img style="width: 100%; height: 100%" src="${h.asset('images/ee_features/admin_branch_permissions.png')}"/>
8 </div>
9 </div>
@@ -0,0 +1,9 b''
1 <div class="panel panel-default">
2 <div class="panel-heading">
3 <h3 class="panel-title">${_('Repository Branch Permissions.')}</h3>
4 </div>
5 <div class="panel-body">
6 <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>
7 <img style="width: 100%; height: 100%" src="${h.asset('images/ee_features/repo_branch_permissions.png')}"/>
8 </div>
9 </div>
@@ -51,7 +51,7 b' PYRAMID_SETTINGS = {}'
51 51 EXTENSIONS = {}
52 52
53 53 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
54 __dbversion__ = 87 # defines current db version for migrations
54 __dbversion__ = 90 # defines current db version for migrations
55 55 __platform__ = platform.system()
56 56 __license__ = 'AGPLv3, and Commercial License'
57 57 __author__ = 'RhodeCode GmbH'
@@ -210,6 +210,11 b' def admin_routes(config):'
210 210 name='admin_permissions_object_update',
211 211 pattern='/permissions/object/update')
212 212
213 # Branch perms EE feature
214 config.add_route(
215 name='admin_permissions_branch',
216 pattern='/permissions/branch')
217
213 218 config.add_route(
214 219 name='admin_permissions_ips',
215 220 pattern='/permissions/ips')
@@ -182,7 +182,8 b' class AdminPermissionsView(BaseAppView, '
182 182 self.request.translate,
183 183 [x[0] for x in c.repo_perms_choices],
184 184 [x[0] for x in c.group_perms_choices],
185 [x[0] for x in c.user_group_perms_choices])()
185 [x[0] for x in c.user_group_perms_choices],
186 )()
186 187
187 188 try:
188 189 form_result = _form.to_python(dict(self.request.POST))
@@ -218,6 +219,30 b' class AdminPermissionsView(BaseAppView, '
218 219 @LoginRequired()
219 220 @HasPermissionAllDecorator('hg.admin')
220 221 @view_config(
222 route_name='admin_permissions_branch', request_method='GET',
223 renderer='rhodecode:templates/admin/permissions/permissions.mako')
224 def permissions_branch(self):
225 c = self.load_default_context()
226 c.active = 'branch'
227
228 c.user = User.get_default_user(refresh=True)
229 defaults = {}
230 defaults.update(c.user.get_default_perms())
231
232 data = render(
233 'rhodecode:templates/admin/permissions/permissions.mako',
234 self._get_template_context(c), self.request)
235 html = formencode.htmlfill.render(
236 data,
237 defaults=defaults,
238 encoding="UTF-8",
239 force_defaults=False
240 )
241 return Response(html)
242
243 @LoginRequired()
244 @HasPermissionAllDecorator('hg.admin')
245 @view_config(
221 246 route_name='admin_permissions_global', request_method='GET',
222 247 renderer='rhodecode:templates/admin/permissions/permissions.mako')
223 248 def permissions_global(self):
@@ -345,6 +345,15 b' def includeme(config):'
345 345 name='edit_repo_perms',
346 346 pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
347 347
348 # Permissions Branch (EE feature)
349 config.add_route(
350 name='edit_repo_perms_branch',
351 pattern='/{repo_name:.*?[^/]}/settings/branch_permissions', repo_route=True)
352 config.add_route(
353 name='edit_repo_perms_branch_delete',
354 pattern='/{repo_name:.*?[^/]}/settings/branch_permissions/{rule_id}/delete',
355 repo_route=True)
356
348 357 # Maintenance
349 358 config.add_route(
350 359 name='edit_repo_maintenance',
@@ -367,10 +367,38 b' class PermOriginDict(dict):'
367 367 self.perm_origin_stack = collections.OrderedDict()
368 368
369 369 def __setitem__(self, key, (perm, origin)):
370 self.perm_origin_stack.setdefault(key, []).append((perm, origin))
370 self.perm_origin_stack.setdefault(key, []).append(
371 (perm, origin))
371 372 dict.__setitem__(self, key, perm)
372 373
373 374
375 class BranchPermOriginDict(PermOriginDict):
376 """
377 Dedicated branch permissions dict, with tracking of patterns and origins.
378
379 >>> perms = BranchPermOriginDict()
380 >>> perms['resource'] = '*pattern', 'read', 'default'
381 >>> perms['resource']
382 {'*pattern': 'read'}
383 >>> perms['resource'] = '*pattern', 'write', 'admin'
384 >>> perms['resource']
385 {'*pattern': 'write'}
386 >>> perms.perm_origin_stack
387 {'resource': {'*pattern': [('read', 'default'), ('write', 'admin')]}}
388 """
389 def __setitem__(self, key, (pattern, perm, origin)):
390
391 self.perm_origin_stack.setdefault(key, {}) \
392 .setdefault(pattern, []).append((perm, origin))
393
394 if key in self:
395 self[key].__setitem__(pattern, perm)
396 else:
397 patterns = collections.OrderedDict()
398 patterns[pattern] = perm
399 dict.__setitem__(self, key, patterns)
400
401
374 402 class PermissionCalculator(object):
375 403
376 404 def __init__(
@@ -395,6 +423,7 b' class PermissionCalculator(object):'
395 423 self.permissions_repositories = PermOriginDict()
396 424 self.permissions_repository_groups = PermOriginDict()
397 425 self.permissions_user_groups = PermOriginDict()
426 self.permissions_repository_branches = BranchPermOriginDict()
398 427 self.permissions_global = set()
399 428
400 429 self.default_repo_perms = Permission.get_default_repo_perms(
@@ -405,6 +434,11 b' class PermissionCalculator(object):'
405 434 Permission.get_default_user_group_perms(
406 435 self.default_user_id, self.scope_user_group_id)
407 436
437 # default branch perms
438 self.default_branch_repo_perms = \
439 Permission.get_default_repo_branch_perms(
440 self.default_user_id, self.scope_repo_id)
441
408 442 def calculate(self):
409 443 if self.user_is_admin and not self.calculate_super_admin:
410 444 return self._admin_permissions()
@@ -413,6 +447,7 b' class PermissionCalculator(object):'
413 447 self._calculate_global_permissions()
414 448 self._calculate_default_permissions()
415 449 self._calculate_repository_permissions()
450 self._calculate_repository_branch_permissions()
416 451 self._calculate_repository_group_permissions()
417 452 self._calculate_user_group_permissions()
418 453 return self._permission_structure()
@@ -443,6 +478,15 b' class PermissionCalculator(object):'
443 478 p = 'usergroup.admin'
444 479 self.permissions_user_groups[u_k] = p, PermOrigin.SUPER_ADMIN
445 480
481 # branch permissions
482 # TODO(marcink): validate this, especially
483 # how this should work using multiple patterns specified ??
484 # looks ok, but still needs double check !!
485 for perm in self.default_branch_repo_perms:
486 r_k = perm.UserRepoToPerm.repository.repo_name
487 p = 'branch.push_force'
488 self.permissions_repository_branches[r_k] = '*', p, PermOrigin.SUPER_ADMIN
489
446 490 return self._permission_structure()
447 491
448 492 def _calculate_global_default_permissions(self):
@@ -472,18 +516,14 b' class PermissionCalculator(object):'
472 516 # now we read the defined permissions and overwrite what we have set
473 517 # before those can be configured from groups or users explicitly.
474 518
475 # TODO: johbo: This seems to be out of sync, find out the reason
476 # for the comment below and update it.
477
478 # In case we want to extend this list we should be always in sync with
479 # User.DEFAULT_USER_PERMISSIONS definitions
519 # In case we want to extend this list we should make sure
520 # this is in sync with User.DEFAULT_USER_PERMISSIONS definitions
480 521 _configurable = frozenset([
481 522 'hg.fork.none', 'hg.fork.repository',
482 523 'hg.create.none', 'hg.create.repository',
483 524 'hg.usergroup.create.false', 'hg.usergroup.create.true',
484 525 'hg.repogroup.create.false', 'hg.repogroup.create.true',
485 'hg.create.write_on_repogroup.false',
486 'hg.create.write_on_repogroup.true',
526 'hg.create.write_on_repogroup.false', 'hg.create.write_on_repogroup.true',
487 527 'hg.inherit_default_perms.false', 'hg.inherit_default_perms.true'
488 528 ])
489 529
@@ -506,7 +546,7 b' class PermissionCalculator(object):'
506 546 for gr, perms in _explicit_grouped_perms:
507 547 # since user can be in multiple groups iterate over them and
508 548 # select the lowest permissions first (more explicit)
509 # TODO: marcink: do this^^
549 # TODO(marcink): do this^^
510 550
511 551 # group doesn't inherit default permissions so we actually set them
512 552 if not gr.inherit_default_permissions:
@@ -533,8 +573,8 b' class PermissionCalculator(object):'
533 573
534 574 def _calculate_default_permissions(self):
535 575 """
536 Set default user permissions for repositories, repository groups
537 taken from the default user.
576 Set default user permissions for repositories, repository branches,
577 repository groups, user groups taken from the default user.
538 578
539 579 Calculate inheritance of object permissions based on what we have now
540 580 in GLOBAL permissions. We check if .false is in GLOBAL since this is
@@ -551,8 +591,7 b' class PermissionCalculator(object):'
551 591 user_inherit_object_permissions = not ('hg.inherit_default_perms.false'
552 592 in self.permissions_global)
553 593
554 # defaults for repositories, taken from `default` user permissions
555 # on given repo
594 # default permissions for repositories, taken from `default` user permissions
556 595 for perm in self.default_repo_perms:
557 596 r_k = perm.UserRepoToPerm.repository.repo_name
558 597 p = perm.Permission.permission_name
@@ -585,8 +624,24 b' class PermissionCalculator(object):'
585 624 o = PermOrigin.SUPER_ADMIN
586 625 self.permissions_repositories[r_k] = p, o
587 626
588 # defaults for repository groups taken from `default` user permission
589 # on given group
627 # default permissions branch for repositories, taken from `default` user permissions
628 for perm in self.default_branch_repo_perms:
629
630 r_k = perm.UserRepoToPerm.repository.repo_name
631 p = perm.Permission.permission_name
632 pattern = perm.UserToRepoBranchPermission.branch_pattern
633 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
634
635 if not self.explicit:
636 # TODO(marcink): fix this for multiple entries
637 cur_perm = self.permissions_repository_branches.get(r_k) or 'branch.none'
638 p = self._choose_permission(p, cur_perm)
639
640 # NOTE(marcink): register all pattern/perm instances in this
641 # special dict that aggregates entries
642 self.permissions_repository_branches[r_k] = pattern, p, o
643
644 # default permissions for repository groups taken from `default` user permission
590 645 for perm in self.default_repo_groups_perms:
591 646 rg_k = perm.UserRepoGroupToPerm.group.group_name
592 647 p = perm.Permission.permission_name
@@ -611,8 +666,7 b' class PermissionCalculator(object):'
611 666 o = PermOrigin.SUPER_ADMIN
612 667 self.permissions_repository_groups[rg_k] = p, o
613 668
614 # defaults for user groups taken from `default` user permission
615 # on given user group
669 # default permissions for user groups taken from `default` user permission
616 670 for perm in self.default_user_group_perms:
617 671 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
618 672 p = perm.Permission.permission_name
@@ -703,6 +757,49 b' class PermissionCalculator(object):'
703 757 o = PermOrigin.SUPER_ADMIN
704 758 self.permissions_repositories[r_k] = p, o
705 759
760 def _calculate_repository_branch_permissions(self):
761 # user group for repositories permissions
762 user_repo_branch_perms_from_user_group = Permission\
763 .get_default_repo_branch_perms_from_user_group(
764 self.user_id, self.scope_repo_id)
765
766 multiple_counter = collections.defaultdict(int)
767 for perm in user_repo_branch_perms_from_user_group:
768 r_k = perm.UserGroupRepoToPerm.repository.repo_name
769 p = perm.Permission.permission_name
770 pattern = perm.UserGroupToRepoBranchPermission.branch_pattern
771 o = PermOrigin.REPO_USERGROUP % perm.UserGroupRepoToPerm\
772 .users_group.users_group_name
773
774 multiple_counter[r_k] += 1
775 if multiple_counter[r_k] > 1:
776 # TODO(marcink): fix this for multi branch support, and multiple entries
777 cur_perm = self.permissions_repository_branches[r_k]
778 p = self._choose_permission(p, cur_perm)
779
780 self.permissions_repository_branches[r_k] = pattern, p, o
781
782 # user explicit branch permissions for repositories, overrides
783 # any specified by the group permission
784 user_repo_branch_perms = Permission.get_default_repo_branch_perms(
785 self.user_id, self.scope_repo_id)
786 for perm in user_repo_branch_perms:
787
788 r_k = perm.UserRepoToPerm.repository.repo_name
789 p = perm.Permission.permission_name
790 pattern = perm.UserToRepoBranchPermission.branch_pattern
791 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
792
793 if not self.explicit:
794 # TODO(marcink): fix this for multiple entries
795 cur_perm = self.permissions_repository_branches.get(r_k) or 'branch.none'
796 p = self._choose_permission(p, cur_perm)
797
798 # NOTE(marcink): register all pattern/perm instances in this
799 # special dict that aggregates entries
800 self.permissions_repository_branches[r_k] = pattern, p, o
801
802
706 803 def _calculate_repository_group_permissions(self):
707 804 """
708 805 Repository group permissions for the current user.
@@ -845,6 +942,7 b' class PermissionCalculator(object):'
845 942 return {
846 943 'global': self.permissions_global,
847 944 'repositories': self.permissions_repositories,
945 'repository_branches': self.permissions_repository_branches,
848 946 'repositories_groups': self.permissions_repository_groups,
849 947 'user_groups': self.permissions_user_groups,
850 948 }
@@ -956,6 +1054,9 b' class AuthUser(object):'
956 1054 perms['user_groups'] = {
957 1055 k: v for k, v in perms['user_groups'].items()
958 1056 if v != 'usergroup.none'}
1057 perms['repository_branches'] = {
1058 k: v for k, v in perms['repository_branches'].iteritems()
1059 if v != 'branch.none'}
959 1060 return perms
960 1061
961 1062 @LazyProperty
@@ -1800,7 +1901,6 b' class PermsFunction(object):'
1800 1901 def __call__(self, check_location='', user=None):
1801 1902 if not user:
1802 1903 log.debug('Using user attribute from global request')
1803 # TODO: remove this someday,put as user as attribute here
1804 1904 request = self._get_request()
1805 1905 user = request.user
1806 1906
@@ -751,6 +751,13 b' class AttributeDict(AttributeDictBase):'
751 751
752 752
753 753
754 class OrderedDefaultDict(collections.OrderedDict, collections.defaultdict):
755 def __init__(self, default_factory=None, *args, **kwargs):
756 # in python3 you can omit the args to super
757 super(OrderedDefaultDict, self).__init__(*args, **kwargs)
758 self.default_factory = default_factory
759
760
754 761 def fix_PATH(os_=None):
755 762 """
756 763 Get current active python path, and append it to PATH variable to fix
@@ -1913,6 +1913,7 b' class Repository(Base, BaseModel):'
1913 1913 for _usr in q.all():
1914 1914 usr = AttributeDict(_usr.user.get_dict())
1915 1915 usr.permission = _usr.permission.permission_name
1916 usr.permission_id = _usr.repo_to_perm_id
1916 1917 perm_rows.append(usr)
1917 1918
1918 1919 # filter the perm rows by 'default' first and then sort them by
@@ -1926,6 +1927,7 b' class Repository(Base, BaseModel):'
1926 1927 usr = AttributeDict(self.user.get_dict())
1927 1928 usr.owner_row = True
1928 1929 usr.permission = _admin_perm
1930 usr.permission_id = None
1929 1931 owner_row.append(usr)
1930 1932
1931 1933 super_admin_rows = []
@@ -1938,6 +1940,7 b' class Repository(Base, BaseModel):'
1938 1940 usr = AttributeDict(usr.get_dict())
1939 1941 usr.admin_row = True
1940 1942 usr.permission = _admin_perm
1943 usr.permission_id = None
1941 1944 super_admin_rows.append(usr)
1942 1945
1943 1946 return super_admin_rows + owner_row + perm_rows
@@ -2694,6 +2697,11 b' class Permission(Base, BaseModel):'
2694 2697 ('usergroup.write', _('User group write access')),
2695 2698 ('usergroup.admin', _('User group admin access')),
2696 2699
2700 ('branch.none', _('Branch no permissions')),
2701 ('branch.merge', _('Branch access by web merge')),
2702 ('branch.push', _('Branch access by push')),
2703 ('branch.push_force', _('Branch access by push with force')),
2704
2697 2705 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
2698 2706 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
2699 2707
@@ -2723,11 +2731,16 b' class Permission(Base, BaseModel):'
2723 2731 ('hg.inherit_default_perms.true', _('Inherit object permissions from default user enabled')),
2724 2732 ]
2725 2733
2726 # definition of system default permissions for DEFAULT user
2734 # definition of system default permissions for DEFAULT user, created on
2735 # system setup
2727 2736 DEFAULT_USER_PERMISSIONS = [
2737 # object perms
2728 2738 'repository.read',
2729 2739 'group.read',
2730 2740 'usergroup.read',
2741 # branch, for backward compat we need same value as before so forced pushed
2742 'branch.push_force',
2743 # global
2731 2744 'hg.create.repository',
2732 2745 'hg.repogroup.create.false',
2733 2746 'hg.usergroup.create.false',
@@ -2758,6 +2771,11 b' class Permission(Base, BaseModel):'
2758 2771 'usergroup.write': 3,
2759 2772 'usergroup.admin': 4,
2760 2773
2774 'branch.none': 0,
2775 'branch.merge': 1,
2776 'branch.push': 3,
2777 'branch.push_force': 4,
2778
2761 2779 'hg.repogroup.create.false': 0,
2762 2780 'hg.repogroup.create.true': 1,
2763 2781
@@ -2794,6 +2812,21 b' class Permission(Base, BaseModel):'
2794 2812 return q.all()
2795 2813
2796 2814 @classmethod
2815 def get_default_repo_branch_perms(cls, user_id, repo_id=None):
2816 q = Session().query(UserToRepoBranchPermission, UserRepoToPerm, Permission) \
2817 .join(
2818 Permission,
2819 UserToRepoBranchPermission.permission_id == Permission.permission_id) \
2820 .join(
2821 UserRepoToPerm,
2822 UserToRepoBranchPermission.rule_to_perm_id == UserRepoToPerm.repo_to_perm_id) \
2823 .filter(UserRepoToPerm.user_id == user_id)
2824
2825 if repo_id:
2826 q = q.filter(UserToRepoBranchPermission.repository_id == repo_id)
2827 return q.order_by(UserToRepoBranchPermission.rule_order).all()
2828
2829 @classmethod
2797 2830 def get_default_repo_perms_from_user_group(cls, user_id, repo_id=None):
2798 2831 q = Session().query(UserGroupRepoToPerm, Repository, Permission)\
2799 2832 .join(
@@ -2818,10 +2851,37 b' class Permission(Base, BaseModel):'
2818 2851 return q.all()
2819 2852
2820 2853 @classmethod
2854 def get_default_repo_branch_perms_from_user_group(cls, user_id, repo_id=None):
2855 q = Session().query(UserGroupToRepoBranchPermission, UserGroupRepoToPerm, Permission) \
2856 .join(
2857 Permission,
2858 UserGroupToRepoBranchPermission.permission_id == Permission.permission_id) \
2859 .join(
2860 UserGroupRepoToPerm,
2861 UserGroupToRepoBranchPermission.rule_to_perm_id == UserGroupRepoToPerm.users_group_to_perm_id) \
2862 .join(
2863 UserGroup,
2864 UserGroupRepoToPerm.users_group_id == UserGroup.users_group_id) \
2865 .join(
2866 UserGroupMember,
2867 UserGroupRepoToPerm.users_group_id == UserGroupMember.users_group_id) \
2868 .filter(
2869 UserGroupMember.user_id == user_id,
2870 UserGroup.users_group_active == true())
2871
2872 if repo_id:
2873 q = q.filter(UserGroupToRepoBranchPermission.repository_id == repo_id)
2874 return q.order_by(UserGroupToRepoBranchPermission.rule_order).all()
2875
2876 @classmethod
2821 2877 def get_default_group_perms(cls, user_id, repo_group_id=None):
2822 2878 q = Session().query(UserRepoGroupToPerm, RepoGroup, Permission)\
2823 .join((Permission, UserRepoGroupToPerm.permission_id == Permission.permission_id))\
2824 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
2879 .join(
2880 Permission,
2881 UserRepoGroupToPerm.permission_id == Permission.permission_id)\
2882 .join(
2883 RepoGroup,
2884 UserRepoGroupToPerm.group_id == RepoGroup.group_id)\
2825 2885 .filter(UserRepoGroupToPerm.user_id == user_id)
2826 2886 if repo_group_id:
2827 2887 q = q.filter(UserRepoGroupToPerm.group_id == repo_group_id)
@@ -2910,6 +2970,8 b' class UserRepoToPerm(Base, BaseModel):'
2910 2970 repository = relationship('Repository')
2911 2971 permission = relationship('Permission')
2912 2972
2973 branch_perm_entry = relationship('UserToRepoBranchPermission', cascade="all, delete, delete-orphan", lazy='joined')
2974
2913 2975 @classmethod
2914 2976 def create(cls, user, repository, permission):
2915 2977 n = cls()
@@ -4470,6 +4532,100 b' def set_task_uid(mapper, connection, tar'
4470 4532 target.task_uid = ScheduleEntry.get_uid(target)
4471 4533
4472 4534
4535 class _BaseBranchPerms(BaseModel):
4536 @classmethod
4537 def compute_hash(cls, value):
4538 return sha1_safe(value)
4539
4540 @hybrid_property
4541 def branch_pattern(self):
4542 return self._branch_pattern or '*'
4543
4544 @hybrid_property
4545 def branch_hash(self):
4546 return self._branch_hash
4547
4548 def _validate_glob(self, value):
4549 re.compile('^' + glob2re(value) + '$')
4550
4551 @branch_pattern.setter
4552 def branch_pattern(self, value):
4553 self._validate_glob(value)
4554 self._branch_pattern = value or '*'
4555 # set the Hash when setting the branch pattern
4556 self._branch_hash = self.compute_hash(self._branch_pattern)
4557
4558 def matches(self, branch):
4559 """
4560 Check if this the branch matches entry
4561
4562 :param branch: branch name for the commit
4563 """
4564
4565 branch = branch or ''
4566
4567 branch_matches = True
4568 if branch:
4569 branch_regex = re.compile('^' + glob2re(self.branch_pattern) + '$')
4570 branch_matches = bool(branch_regex.search(branch))
4571
4572 return branch_matches
4573
4574
4575 class UserToRepoBranchPermission(Base, _BaseBranchPerms):
4576 __tablename__ = 'user_to_repo_branch_permissions'
4577 __table_args__ = (
4578 {'extend_existing': True, 'mysql_engine': 'InnoDB',
4579 'mysql_charset': 'utf8', 'sqlite_autoincrement': True,}
4580 )
4581
4582 branch_rule_id = Column('branch_rule_id', Integer(), primary_key=True)
4583
4584 repository_id = Column('repository_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
4585 repo = relationship('Repository', backref='user_branch_perms')
4586
4587 permission_id = Column('permission_id', Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
4588 permission = relationship('Permission')
4589
4590 rule_to_perm_id = Column('rule_to_perm_id', Integer(), ForeignKey('repo_to_perm.repo_to_perm_id'), nullable=False, unique=None, default=None)
4591 user_repo_to_perm = relationship('UserRepoToPerm')
4592
4593 rule_order = Column('rule_order', Integer(), nullable=False)
4594 _branch_pattern = Column('branch_pattern', UnicodeText().with_variant(UnicodeText(2048), 'mysql'), default=u'*') # glob
4595 _branch_hash = Column('branch_hash', UnicodeText().with_variant(UnicodeText(2048), 'mysql'))
4596
4597 def __unicode__(self):
4598 return u'<UserBranchPermission(%s => %r)>' % (
4599 self.user_repo_to_perm, self.branch_pattern)
4600
4601
4602 class UserGroupToRepoBranchPermission(Base, _BaseBranchPerms):
4603 __tablename__ = 'user_group_to_repo_branch_permissions'
4604 __table_args__ = (
4605 {'extend_existing': True, 'mysql_engine': 'InnoDB',
4606 'mysql_charset': 'utf8', 'sqlite_autoincrement': True,}
4607 )
4608
4609 branch_rule_id = Column('branch_rule_id', Integer(), primary_key=True)
4610
4611 repository_id = Column('repository_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
4612 repo = relationship('Repository', backref='user_group_branch_perms')
4613
4614 permission_id = Column('permission_id', Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
4615 permission = relationship('Permission')
4616
4617 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)
4618 user_group_repo_to_perm = relationship('UserGroupRepoToPerm')
4619
4620 rule_order = Column('rule_order', Integer(), nullable=False)
4621 _branch_pattern = Column('branch_pattern', UnicodeText().with_variant(UnicodeText(2048), 'mysql'), default=u'*') # glob
4622 _branch_hash = Column('branch_hash', UnicodeText().with_variant(UnicodeText(2048), 'mysql'))
4623
4624 def __unicode__(self):
4625 return u'<UserBranchPermission(%s => %r)>' % (
4626 self.user_group_repo_to_perm, self.branch_pattern)
4627
4628
4473 4629 class DbMigrateVersion(Base, BaseModel):
4474 4630 __tablename__ = 'db_migrate_version'
4475 4631 __table_args__ = (
@@ -504,12 +504,26 b' def ObjectPermissionsForm(localizer, rep'
504 504 overwrite_default_repo = v.StringBoolean(if_missing=False)
505 505 overwrite_default_group = v.StringBoolean(if_missing=False)
506 506 overwrite_default_user_group = v.StringBoolean(if_missing=False)
507
507 508 default_repo_perm = v.OneOf(repo_perms_choices)
508 509 default_group_perm = v.OneOf(group_perms_choices)
509 510 default_user_group_perm = v.OneOf(user_group_perms_choices)
511
510 512 return _ObjectPermissionsForm
511 513
512 514
515 def BranchPermissionsForm(localizer, branch_perms_choices):
516 _ = localizer
517
518 class _BranchPermissionsForm(formencode.Schema):
519 allow_extra_fields = True
520 filter_extra_fields = True
521 overwrite_default_branch = v.StringBoolean(if_missing=False)
522 default_branch_perm = v.OneOf(branch_perms_choices)
523
524 return _BranchPermissionsForm
525
526
513 527 def UserPermissionsForm(localizer, create_choices, create_on_write_choices,
514 528 repo_group_create_choices, user_group_create_choices,
515 529 fork_choices, inherit_default_permissions_choices):
@@ -31,7 +31,7 b' from sqlalchemy.exc import DatabaseError'
31 31 from rhodecode.model import BaseModel
32 32 from rhodecode.model.db import (
33 33 User, Permission, UserToPerm, UserRepoToPerm, UserRepoGroupToPerm,
34 UserUserGroupToPerm, UserGroup, UserGroupToPerm)
34 UserUserGroupToPerm, UserGroup, UserGroupToPerm, UserToRepoBranchPermission)
35 35 from rhodecode.lib.utils2 import str2bool, safe_int
36 36
37 37 log = logging.getLogger(__name__)
@@ -59,6 +59,9 b' class PermissionModel(BaseModel):'
59 59 'default_repo_perm': None,
60 60 'default_group_perm': None,
61 61 'default_user_group_perm': None,
62
63 # branch
64 'default_branch_perm': None,
62 65 }
63 66
64 67 def set_global_permission_choices(self, c_obj, gettext_translator):
@@ -82,6 +85,12 b' class PermissionModel(BaseModel):'
82 85 ('usergroup.write', _('Write'),),
83 86 ('usergroup.admin', _('Admin'),)]
84 87
88 c_obj.branch_perms_choices = [
89 ('branch.none', _('Protected/No Access'),),
90 ('branch.merge', _('Web merge'),),
91 ('branch.push', _('Push'),),
92 ('branch.push_force', _('Force Push'),)]
93
85 94 c_obj.register_choices = [
86 95 ('hg.register.none', _('Disabled')),
87 96 ('hg.register.manual_activate', _('Allowed with manual account activation')),
@@ -133,6 +142,10 b' class PermissionModel(BaseModel):'
133 142 if perm.permission.permission_name.startswith('usergroup.'):
134 143 defaults['default_user_group_perm' + suffix] = perm.permission.permission_name
135 144
145 # branch
146 if perm.permission.permission_name.startswith('branch.'):
147 defaults['default_branch_perm' + suffix] = perm.permission.permission_name
148
136 149 # creation of objects
137 150 if perm.permission.permission_name.startswith('hg.create.write_on_repogroup'):
138 151 defaults['default_repo_create_on_write' + suffix] = perm.permission.permission_name
@@ -199,6 +212,9 b' class PermissionModel(BaseModel):'
199 212 'default_repo_perm': 'repository.',
200 213 'default_group_perm': 'group.',
201 214 'default_user_group_perm': 'usergroup.',
215 # branch
216 'default_branch_perm': 'branch.',
217
202 218 }[field_name]
203 219 for field in keep_fields:
204 220 pat = get_pat(field)
@@ -236,8 +252,12 b' class PermissionModel(BaseModel):'
236 252 _global_perms = self.global_perms.copy()
237 253 if obj_type not in ['user', 'user_group']:
238 254 raise ValueError("obj_type must be on of 'user' or 'user_group'")
239 if len(_global_perms) != len(Permission.DEFAULT_USER_PERMISSIONS):
240 raise Exception('Inconsistent permissions definition')
255 global_perms = len(_global_perms)
256 default_user_perms = len(Permission.DEFAULT_USER_PERMISSIONS)
257 if global_perms != default_user_perms:
258 raise Exception(
259 'Inconsistent permissions definition. Got {} vs {}'.format(
260 global_perms, default_user_perms))
241 261
242 262 if obj_type == 'user':
243 263 self._clear_user_perms(object.user_id, preserve)
@@ -337,8 +357,8 b' class PermissionModel(BaseModel):'
337 357
338 358 def create_default_user_group_permissions(self, user_group, force=False):
339 359 """
340 Creates only missing default permissions for user group, if force is set it
341 resets the default permissions for that user group
360 Creates only missing default permissions for user group, if force is
361 set it resets the default permissions for that user group
342 362
343 363 :param user_group:
344 364 :param force:
@@ -366,6 +386,7 b' class PermissionModel(BaseModel):'
366 386 'default_repo_perm',
367 387 'default_group_perm',
368 388 'default_user_group_perm',
389 'default_branch_perm',
369 390
370 391 'default_repo_group_create',
371 392 'default_user_group_create',
@@ -392,6 +413,7 b' class PermissionModel(BaseModel):'
392 413 'default_repo_perm',
393 414 'default_group_perm',
394 415 'default_user_group_perm',
416 'default_branch_perm',
395 417
396 418 'default_register',
397 419 'default_password_reset',
@@ -414,6 +436,7 b' class PermissionModel(BaseModel):'
414 436 'default_repo_perm',
415 437 'default_group_perm',
416 438 'default_user_group_perm',
439 'default_branch_perm',
417 440
418 441 'default_register',
419 442 'default_password_reset',
@@ -440,6 +463,7 b' class PermissionModel(BaseModel):'
440 463 'default_repo_create',
441 464 'default_fork_create',
442 465 'default_inherit_default_permissions',
466 'default_branch_perm',
443 467
444 468 'default_register',
445 469 'default_password_reset',
@@ -477,8 +501,57 b' class PermissionModel(BaseModel):'
477 501 .all():
478 502 g2p.permission = _def
479 503 self.sa.add(g2p)
504
505 # COMMIT
480 506 self.sa.commit()
481 507 except (DatabaseError,):
482 508 log.exception('Failed to set default object permissions')
483 509 self.sa.rollback()
484 510 raise
511
512 def update_branch_permissions(self, form_result):
513 if 'perm_user_id' in form_result:
514 perm_user = User.get(safe_int(form_result['perm_user_id']))
515 else:
516 # used mostly to do lookup for default user
517 perm_user = User.get_by_username(form_result['perm_user_name'])
518 try:
519
520 # stage 2 reset defaults and set them from form data
521 self._set_new_user_perms(perm_user, form_result, preserve=[
522 'default_repo_perm',
523 'default_group_perm',
524 'default_user_group_perm',
525
526 'default_repo_group_create',
527 'default_user_group_create',
528 'default_repo_create_on_write',
529 'default_repo_create',
530 'default_fork_create',
531 'default_inherit_default_permissions',
532
533 'default_register',
534 'default_password_reset',
535 'default_extern_activate'])
536
537 # overwrite default branch permissions
538 if form_result['overwrite_default_branch']:
539 _def_name = \
540 form_result['default_branch_perm'].split('branch.')[-1]
541
542 _def = Permission.get_by_key('branch.' + _def_name)
543
544 # TODO(marcink): those are bind to repo, perms, we need to unfold user somehow from this
545 for g2p in self.sa.query(UserToRepoBranchPermission) \
546 .filter(UserToRepoBranchPermission.user == perm_user) \
547 .all():
548 g2p.permission = _def
549 self.sa.add(g2p)
550
551 # COMMIT
552 self.sa.commit()
553 except (DatabaseError,):
554 log.exception('Failed to set default branch permissions')
555 self.sa.rollback()
556 raise
557
@@ -116,6 +116,11 b''
116 116 .label;
117 117 padding-top: 5px;
118 118 }
119 .label-branch-perm {
120 .label;
121 width: 20px;
122 }
123
119 124 // Used to position content on the right side of a .label
120 125 .content,
121 126 .side-by-side-selector {
@@ -135,6 +140,15 b''
135 140 }
136 141 }
137 142
143 .input-branch-perm {
144 .input;
145 margin-left: 90px;
146 }
147
148 .input-branch-perm-order {
149 width: 40px;
150 }
151
138 152 .checkboxes,
139 153 .input,
140 154 .select {
@@ -169,6 +183,9 b''
169 183 }
170 184
171 185 .input {
186 .branch-perm {
187 width: 80px;
188 }
172 189 .medium {
173 190 width: @fields-input-m;
174 191 }
@@ -462,7 +462,7 b' ul.auth_plugins {'
462 462
463 463 .radios {
464 464 position: relative;
465 width: 405px;
465 width: 505px;
466 466 }
467 467 }
468 468
@@ -159,3 +159,9 b''
159 159 &:extend(.icon-svn-transparent:before);
160 160 }
161 161 }
162
163 .icon-user-group:before {
164 &:extend(.icon-group:before);
165 margin: 0;
166 font-size: 16px;
167 }
@@ -109,13 +109,21 b''
109 109 &.read {
110 110 &:extend(.tag1);
111 111 }
112
113 112 &.write {
114 113 &:extend(.tag4);
115 114 }
116 115 &.admin {
117 116 &:extend(.tag5);
118 117 }
118 &.merge {
119 &:extend(.tag1);
120 }
121 &.push {
122 &:extend(.tag4);
123 }
124 &.push_force {
125 &:extend(.tag5);
126 }
119 127 }
120 128
121 129 .phase-draft {
@@ -87,6 +87,7 b' function registerRCRoutes() {'
87 87 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
88 88 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
89 89 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
90 pyroutes.register('admin_permissions_branch', '/_admin/permissions/branch', []);
90 91 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
91 92 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
92 93 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
@@ -233,6 +234,8 b' function registerRCRoutes() {'
233 234 pyroutes.register('edit_repo_advanced_hooks', '/%(repo_name)s/settings/advanced/hooks', ['repo_name']);
234 235 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
235 236 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
237 pyroutes.register('edit_repo_perms_branch', '/%(repo_name)s/settings/branch_permissions', ['repo_name']);
238 pyroutes.register('edit_repo_perms_branch_delete', '/%(repo_name)s/settings/branch_permissions/%(rule_id)s/delete', ['repo_name', 'rule_id']);
236 239 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
237 240 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
238 241 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
@@ -38,7 +38,7 b' var api;'
38 38 { data: {"_": "group_name",
39 39 "sort": "group_name"}, title: "${_('Name')}", className: "td-componentname," ,
40 40 render: function (data,type,full,meta)
41 {return '<div><i class="icon-group" title="User group">'+data+'</i></div>'}},
41 {return '<div><i class="icon-user-group" title="User group">'+data+'</i></div>'}},
42 42
43 43 { data: {"_": "group_description",
44 44 "sort": "group_description"}, title: "${_('Description')}", className: "td-description" },
@@ -38,6 +38,9 b''
38 38 <li class="${'active' if c.active=='objects' else ''}">
39 39 <a href="${h.route_path('admin_permissions_object')}">${_('Object')}</a>
40 40 </li>
41 <li class="${'active' if c.active=='branch' else ''}">
42 <a href="${h.route_path('admin_permissions_branch')}">${_('Branch')}</a>
43 </li>
41 44 <li class="${'active' if c.active=='ips' else ''}">
42 45 <a href="${h.route_path('admin_permissions_ips')}">${_('IP Whitelist')}</a>
43 46 </li>
@@ -3,7 +3,10 b''
3 3 <h3 class="panel-title">${_('Default Permissions for Repositories, User Groups and Repository Groups.')}</h3>
4 4 </div>
5 5 <div class="panel-body">
6 <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.')}
6 <p>
7 ${_('Default access permissions. This defines permissions for the `default` user from which other users inherit permissions.')}
8 <br/>
9 ${_('Check the overwrite checkbox to force change all previously defined permissions for `default` user to the new selected value.')}
7 10 </p>
8 11 ${h.secure_form(h.route_path('admin_permissions_object_update'), request=request)}
9 12 <div class="form">
@@ -45,7 +48,7 b''
45 48 ${h.select('default_user_group_perm','',c.user_group_perms_choices)}
46 49 ${h.checkbox('overwrite_default_user_group','true')}
47 50 <label for="overwrite_default_user_group">
48 <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'))}">
51 <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'))}">
49 52 ${_('Overwrite Existing Settings')}
50 53 </span>
51 54 </label>
@@ -57,7 +57,7 b''
57 57 %if _user.username != h.DEFAULT_USER:
58 58 <span class="btn btn-link btn-danger revoke_perm"
59 59 member="${_user.user_id}" member_type="user">
60 <i class="icon-remove"></i> ${_('Revoke')}
60 ${_('Revoke')}
61 61 </span>
62 62 %endif
63 63 </td>
@@ -92,7 +92,7 b''
92 92 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'group.write', checked=_user_group.permission=='group.write')}</td>
93 93 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'group.admin', checked=_user_group.permission=='group.admin')}</td>
94 94 <td class="td-componentname">
95 <i class="icon-group" ></i>
95 <i class="icon-user-group"></i>
96 96 %if h.HasPermissionAny('hg.admin')():
97 97 <a href="${h.route_path('edit_user_group',user_group_id=_user_group.users_group_id)}">
98 98 ${_user_group.users_group_name}
@@ -104,17 +104,27 b''
104 104 <td class="td-action">
105 105 <span class="btn btn-link btn-danger revoke_perm"
106 106 member="${_user_group.users_group_id}" member_type="user_group">
107 <i class="icon-remove"></i> ${_('Revoke')}
107 ${_('Revoke')}
108 108 </span>
109 109 </td>
110 110 </tr>
111 111 %endfor
112 112
113 113 <tr class="new_members" id="add_perm_input"></tr>
114 <tr>
115 <td></td>
116 <td></td>
117 <td></td>
118 <td></td>
119 <td></td>
120 <td>
121 <span id="add_perm" class="link">
122 ${_('Add user/user group')}
123 </span>
124 </td>
125 </tr>
114 126 </table>
115 <div id="add_perm" class="link">
116 ${_('Add new')}
117 </div>
127
118 128 <div class="fields">
119 129 <div class="field">
120 130 <div class="label label-radio">
@@ -48,6 +48,9 b''
48 48 <li class="${'active' if c.active=='permissions' else ''}">
49 49 <a href="${h.route_path('edit_repo_perms', repo_name=c.repo_name)}">${_('Permissions')}</a>
50 50 </li>
51 <li class="${'active' if c.active=='permissions_branch' else ''}">
52 <a href="${h.route_path('edit_repo_perms_branch', repo_name=c.repo_name)}">${_('Branch Permissions')}</a>
53 </li>
51 54 <li class="${'active' if c.active=='advanced' else ''}">
52 55 <a href="${h.route_path('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a>
53 56 </li>
@@ -67,7 +67,7 b''
67 67 %if _user.username != h.DEFAULT_USER:
68 68 <span class="btn btn-link btn-danger revoke_perm"
69 69 member="${_user.user_id}" member_type="user">
70 <i class="icon-remove"></i> ${_('Revoke')}
70 ${_('Revoke')}
71 71 </span>
72 72 %endif
73 73 </td>
@@ -83,7 +83,7 b''
83 83 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.write', checked=_user_group.permission=='repository.write')}</td>
84 84 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.admin', checked=_user_group.permission=='repository.admin')}</td>
85 85 <td class="td-componentname">
86 <i class="icon-group" ></i>
86 <i class="icon-user-group"></i>
87 87 %if h.HasPermissionAny('hg.admin')():
88 88 <a href="${h.route_path('edit_user_group',user_group_id=_user_group.users_group_id)}">
89 89 ${_user_group.users_group_name}
@@ -95,16 +95,28 b''
95 95 <td class="td-action">
96 96 <span class="btn btn-link btn-danger revoke_perm"
97 97 member="${_user_group.users_group_id}" member_type="user_group">
98 <i class="icon-remove"></i> ${_('Revoke')}
98 ${_('Revoke')}
99 99 </span>
100 100 </td>
101 101 </tr>
102 102 %endfor
103 103 <tr class="new_members" id="add_perm_input"></tr>
104
105 <tr>
106 <td></td>
107 <td></td>
108 <td></td>
109 <td></td>
110 <td></td>
111 <td>
112 <span id="add_perm" class="link">
113 ${_('Add user/user group')}
114 </span>
115 </td>
116 </tr>
117
104 118 </table>
105 <div id="add_perm" class="link">
106 ${_('Add new')}
107 </div>
119
108 120 <div class="buttons">
109 121 ${h.submit('save',_('Save'),class_="btn btn-primary")}
110 122 ${h.reset('reset',_('Reset'),class_="btn btn-danger")}
@@ -59,7 +59,7 b''
59 59 %if _user.username != h.DEFAULT_USER:
60 60 <span class="btn btn-link btn-danger revoke_perm"
61 61 member="${_user.user_id}" member_type="user">
62 <i class="icon-remove"></i> ${_('revoke')}
62 ${_('Revoke')}
63 63 </span>
64 64 %endif
65 65 </td>
@@ -94,7 +94,7 b''
94 94 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'usergroup.write')}</td>
95 95 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'usergroup.admin')}</td>
96 96 <td class="td-user">
97 <i class="icon-group" ></i>
97 <i class="icon-user-group"></i>
98 98 %if h.HasPermissionAny('hg.admin')():
99 99 <a href="${h.route_path('edit_user_group',user_group_id=_user_group.users_group_id)}">
100 100 ${_user_group.users_group_name}
@@ -106,16 +106,26 b''
106 106 <td class="td-action">
107 107 <span class="btn btn-link btn-danger revoke_perm"
108 108 member="${_user_group.users_group_id}" member_type="user_group">
109 <i class="icon-remove"></i> ${_('revoke')}
109 ${_('Revoke')}
110 110 </span>
111 111 </td>
112 112 </tr>
113 113 %endfor
114 114 <tr class="new_members" id="add_perm_input"></tr>
115 <tr>
116 <td></td>
117 <td></td>
118 <td></td>
119 <td></td>
120 <td></td>
121 <td>
122 <span id="add_perm" class="link">
123 ${_('Add user/user group')}
124 </span>
125 </td>
126 </tr>
115 127 </table>
116 <div id="add_perm" class="link">
117 ${_('Add new')}
118 </div>
128
119 129 <div class="buttons">
120 130 ${h.submit('save',_('Save'),class_="btn btn-primary")}
121 131 ${h.reset('reset',_('Reset'),class_="btn btn-danger")}
@@ -53,7 +53,7 b' var api;'
53 53 { data: {"_": "group_name",
54 54 "sort": "group_name"}, title: "${_('Name')}", className: "td-componentname," ,
55 55 render: function (data,type,full,meta)
56 {return '<div><i class="icon-group" title="User group">'+data+'</i></div>'}},
56 {return '<div><i class="icon-user-group" title="User group">'+data+'</i></div>'}},
57 57
58 58 { data: {"_": "group_description",
59 59 "sort": "group_description"}, title: "${_('Description')}", className: "td-description" },
@@ -4,11 +4,20 b''
4 4 ## ${p.perms_summary(c.perm_user.permissions)}
5 5
6 6 <%def name="perms_summary(permissions, show_all=False, actions=True, side_link=None)">
7 <% section_to_label = {
8 'global': 'Global Permissions',
9 'repository_branches': 'Repository Branch Rules',
10 'repositories': 'Repository Permissions',
11 'user_groups': 'User Group Permissions',
12 'repositories_groups': 'Repository Group Permissions',
13 } %>
7 14 <div id="perms" class="table fields">
8 %for section in sorted(permissions.keys()):
15 %for section in sorted(permissions.keys(), key=lambda item: {'global': 0, 'repository_branches': 1}.get(item, 1000)):
9 16 <div class="panel panel-default">
10 <div class="panel-heading">
11 <h3 class="panel-title">${section.replace("_"," ").capitalize()}</h3>
17 <div class="panel-heading" id="${section.replace("_","-")}-permissions">
18 <h3 class="panel-title">${section_to_label.get(section, section)} - ${len(permissions[section])}
19 <a class="permalink" href="#${section.replace("_","-")}-permissions"> ΒΆ</a>
20 </h3>
12 21 % if side_link:
13 22 <div class="pull-right">
14 23 <a href="${side_link}">${_('in JSON format')}</a>
@@ -18,15 +27,24 b''
18 27 <div class="panel-body">
19 28 <div class="perms_section_head field">
20 29 <div class="radios">
21 %if section != 'global':
30 % if section == 'repository_branches':
22 31 <span class="permissions_boxes">
23 32 <span class="desc">${_('show')}: </span>
24 ${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>
25 ${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>
26 ${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>
27 ${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>
33 ${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>
34 ${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>
35 ${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>
36 ${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>
28 37 </span>
29 %endif
38 % elif section != 'global':
39 <span class="permissions_boxes">
40 <span class="desc">${_('show')}: </span>
41 ${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>
42 ${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>
43 ${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>
44 ${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>
45 </span>
46 % endif
47
30 48 </div>
31 49 </div>
32 50 <div class="field">
@@ -72,12 +90,10 b''
72 90 %endif
73 91 %else:
74 92 %if not val_lbl:
75 ${
76 {'false': False,
93 ${{'false': False,
77 94 'true': True,
78 95 'none': False,
79 'repository': True}.get(val[1][0] if 0 < len(val[1]) else 'false')
80 }
96 'repository': True}.get(val[1][0] if 0 < len(val[1]) else 'false')}
81 97 %else:
82 98 <span class="perm_tag ${val[1][0]}">${val_lbl}.${val[1][0]}</span>
83 99 %endif
@@ -142,7 +158,72 b''
142 158 edit_url=h.route_path('user_edit_global_perms', user_id=c.user.user_id), edit_global_url=h.route_path('admin_permissions_object'))}
143 159
144 160 </tbody>
161 ## Branch perms
162 %elif section == 'repository_branches':
163 <thead>
164 <tr>
165 <th>${_('Name')}</th>
166 <th>${_('Pattern')}</th>
167 <th>${_('Permission')}</th>
168 %if actions:
169 <th>${_('Edit Branch Permission')}</th>
170 %endif
171 </thead>
172 <tbody class="section_${section}">
173 <%
174 def name_sorter(permissions):
175 def custom_sorter(item):
176 return item[0]
177 return sorted(permissions, key=custom_sorter)
178
179 def branch_sorter(permissions):
180 def custom_sorter(item):
181 ## none, merge, push, push_force
182 section = item[1].split('.')[-1]
183 section_importance = {'none': u'0',
184 'merge': u'1',
185 'push': u'2',
186 'push_force': u'3'}.get(section)
187 ## sort by importance + name
188 return section_importance + item[0]
189 return sorted(permissions, key=custom_sorter)
190 %>
191 %for k, section_perms in name_sorter(permissions[section].items()):
192 % for pattern, perm in branch_sorter(section_perms.items()):
193 <tr class="perm_row ${'{}_{}'.format(section, perm.split('.')[-1])}">
194 <td class="td-name">
195 <a href="${h.route_path('repo_summary',repo_name=k)}">${k}</a>
196 </td>
197 <td>${pattern}</td>
198 <td class="td-tags">
199 ## TODO: calculate origin somehow
200 ## % for i, ((_pat, perm), origin) in enumerate((permissions[section].perm_origin_stack[k])):
201
202 <div>
203 <% i = 0 %>
204 <% origin = 'unknown' %>
205 <% _css_class = i > 0 and 'perm_overriden' or '' %>
206
207 <span class="${_css_class} perm_tag ${perm.split('.')[-1]}">
208 ${perm}
209 ##(${origin})
210 </span>
211 </div>
212 ## % endfor
213 </td>
214 %if actions:
215 <td class="td-action">
216 <a href="${h.route_path('edit_repo_perms_branch',repo_name=k)}">${_('edit')}</a>
217 </td>
218 %endif
219 </tr>
220 % endfor
221 %endfor
222 </tbody>
223
224 ## Repos/Repo Groups/users groups perms
145 225 %else:
226
146 227 ## none/read/write/admin permissions on groups/repos etc
147 228 <thead>
148 229 <tr>
@@ -167,8 +248,11 b''
167 248 return sorted(permissions, key=custom_sorter)
168 249 %>
169 250 %for k, section_perm in sorter(permissions[section].items()):
170 %if section_perm.split('.')[-1] != 'none' or show_all:
171 <tr class="perm_row ${'%s_%s' % (section, section_perm.split('.')[-1])}">
251 <% perm_value = section_perm.split('.')[-1] %>
252 <% _css_class = 'display:none' if perm_value in ['none'] else '' %>
253
254 %if perm_value != 'none' or show_all:
255 <tr class="perm_row ${'{}_{}'.format(section, section_perm.split('.')[-1])}" style="${_css_class}">
172 256 <td class="td-name">
173 257 %if section == 'repositories':
174 258 <a href="${h.route_path('repo_summary',repo_name=k)}">${k}</a>
@@ -183,7 +267,7 b''
183 267 %if hasattr(permissions[section], 'perm_origin_stack'):
184 268 <div>
185 269 %for i, (perm, origin) in enumerate(reversed(permissions[section].perm_origin_stack[k])):
186
270 <% _css_class = i > 0 and 'perm_overriden' or '' %>
187 271 % if i > 0:
188 272 <div style="color: #979797">
189 273 <i class="icon-arrow_up"></i>
@@ -193,7 +277,7 b''
193 277 % endif
194 278
195 279 <div>
196 <span class="${i > 0 and 'perm_overriden' or ''} perm_tag ${perm.split('.')[-1]}">
280 <span class="${_css_class} perm_tag ${perm.split('.')[-1]}">
197 281 ${perm} (${origin})
198 282 </span>
199 283 </div>
@@ -220,7 +304,7 b''
220 304 %endfor
221 305
222 306 <tr id="empty_${section}" class="noborder" style="display:none;">
223 <td colspan="6">${_('No permission defined')}</td>
307 <td colspan="6">${_('No matching permission defined')}</td>
224 308 </tr>
225 309
226 310 </tbody>
@@ -236,15 +320,16 b''
236 320
237 321 <script>
238 322 $(document).ready(function(){
239 var show_empty = function(section){
323 var showEmpty = function(section){
240 324 var visible = $('.section_{0} tr.perm_row:visible'.format(section)).length;
241 if(visible == 0){
325 if(visible === 0){
242 326 $('#empty_{0}'.format(section)).show();
243 327 }
244 328 else{
245 329 $('#empty_{0}'.format(section)).hide();
246 330 }
247 331 };
332
248 333 $('.perm_filter').on('change', function(e){
249 334 var self = this;
250 335 var section = $(this).attr('section');
@@ -261,7 +346,7 b''
261 346 $('.'+section+'_'+perm_type).hide();
262 347 }
263 348 });
264 show_empty(section);
349 showEmpty(section);
265 350 })
266 351 })
267 352 </script>
@@ -284,7 +284,7 b''
284 284
285 285 <%def name="user_group_name(user_group_name)">
286 286 <div>
287 <i class="icon-group" title="${_('User group')}"></i>
287 <i class="icon-user-group" title="${_('User group')}"></i>
288 288 ${h.link_to_group(user_group_name)}
289 289 </div>
290 290 </%def>
@@ -534,7 +534,7 b''
534 534 </tbody>
535 535 </table>
536 536 <div class="link" id="add_perm">
537 Add new
537 Add user/user group
538 538 </div>
539 539
540 540
@@ -8,12 +8,11 b''
8 8 <div class="panel-heading">${title}</div>
9 9 <div class="panel-body">
10 10
11 <div tal:condition="errormsg"
12 class="clearfix alert alert-danger">
13 <p i18n:translate="">
11 <div tal:condition="errormsg" class="clearfix alert alert-error">
12 <span i18n:translate="">
14 13 There was a problem with this section
15 </p>
16 <p>${errormsg}</p>
14 </span>
15 <div>${errormsg}</div>
17 16 </div>
18 17
19 18 <div tal:condition="description">
@@ -140,6 +140,10 b' class TestPermissions(object):'
140 140 assert repo_perms(user)[repo.repo_name] == 'repository.admin'
141 141 repo.user = org_owner
142 142
143 def test_default_owner_branch_perms(self, user_util, test_user_group):
144 user = user_util.create_user()
145 assert branch_perms(user) == {}
146
143 147 def test_default_owner_repo_group_perms(self, user_util, test_repo_group):
144 148 user = user_util.create_user()
145 149 org_owner = test_repo_group.user
@@ -360,13 +364,15 b' class TestPermissions(object):'
360 364 user_model.revoke_perm(self.u1, 'hg.fork.repository')
361 365 user_model.grant_perm(self.u1, 'hg.fork.none')
362 366
367 # TODO(marcink): check branch permissions now ?
368
363 369 # make sure inherit flag is turned off
364 370 self.u1.inherit_default_permissions = False
365 371 Session().commit()
366 372
367 373 # this user will have non inherited permissions from he's
368 374 # explicitly set permissions
369 assert global_perms(self.u1) == set([
375 assert global_perms(self.u1) == {
370 376 'hg.create.none',
371 377 'hg.fork.none',
372 378 'hg.register.manual_activate',
@@ -375,7 +381,8 b' class TestPermissions(object):'
375 381 'repository.read',
376 382 'group.read',
377 383 'usergroup.read',
378 ])
384 'branch.push_force',
385 }
379 386
380 387 def test_non_inherited_permissions_from_default_on_user_disabled(self):
381 388 user_model = UserModel()
@@ -396,9 +403,11 b' class TestPermissions(object):'
396 403 self.u1.inherit_default_permissions = False
397 404 Session().commit()
398 405
406 # TODO(marcink): check branch perms
407
399 408 # this user will have non inherited permissions from he's
400 409 # explicitly set permissions
401 assert global_perms(self.u1) == set([
410 assert global_perms(self.u1) == {
402 411 'hg.create.repository',
403 412 'hg.fork.repository',
404 413 'hg.register.manual_activate',
@@ -407,7 +416,8 b' class TestPermissions(object):'
407 416 'repository.read',
408 417 'group.read',
409 418 'usergroup.read',
410 ])
419 'branch.push_force',
420 }
411 421
412 422 @pytest.mark.parametrize('perm, expected_perm', [
413 423 ('hg.inherit_default_perms.false', 'repository.none', ),
@@ -425,8 +435,10 b' class TestPermissions(object):'
425 435 self.u1.inherit_default_permissions = True
426 436 Session().commit()
427 437
438 # TODO(marcink): check branch perms
439
428 440 # this user will have inherited permissions from default user
429 assert global_perms(self.u1) == set([
441 assert global_perms(self.u1) == {
430 442 'hg.create.none',
431 443 'hg.fork.none',
432 444 'hg.register.manual_activate',
@@ -435,11 +447,12 b' class TestPermissions(object):'
435 447 'repository.read',
436 448 'group.read',
437 449 'usergroup.read',
450 'branch.push_force',
438 451 'hg.create.write_on_repogroup.true',
439 452 'hg.usergroup.create.false',
440 453 'hg.repogroup.create.false',
441 perm,
442 ])
454 perm
455 }
443 456
444 457 assert set(repo_perms(self.u1).values()) == set([expected_perm])
445 458
@@ -693,6 +706,11 b' def repo_perms(user):'
693 706 return auth_user.permissions['repositories']
694 707
695 708
709 def branch_perms(user):
710 auth_user = AuthUser(user_id=user.user_id)
711 return auth_user.permissions['repository_branches']
712
713
696 714 def group_perms(user):
697 715 auth_user = AuthUser(user_id=user.user_id)
698 716 return auth_user.permissions['repositories_groups']
@@ -66,5 +66,5 b' class TestTags(BackendTestMixin):'
66 66 def test_name_with_slash(self):
67 67 self.repo.tag('19/10/11', 'joe')
68 68 assert '19/10/11' in self.repo.tags
69 self.repo.tag('11', 'joe')
70 assert '11' in self.repo.tags
69 self.repo.tag('rel.11', 'joe')
70 assert 'rel.11' in self.repo.tags
General Comments 0
You need to be logged in to leave comments. Login now