##// END OF EJS Templates
branch-permissions: handle vcs operations and branch permissions....
marcink -
r2979:095dcb4b default
parent child Browse files
Show More
@@ -0,0 +1,94 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-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 os
22 import pytest
23
24 from rhodecode.tests import (
25 TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
26 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
27 from rhodecode.tests.vcs_operations import (
28 Command, _check_proper_hg_push, _check_proper_git_push, _add_files_and_push)
29
30
31 @pytest.mark.usefixtures("disable_anonymous_user")
32 class TestVCSOperations(object):
33
34 @pytest.mark.parametrize('username, password', [
35 (TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS),
36 (TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS),
37 ])
38 @pytest.mark.parametrize('branch_perm', [
39 'branch.none',
40 'branch.merge',
41 'branch.push',
42 'branch.push_force',
43 ])
44 def test_push_to_protected_branch_fails_with_message_hg(
45 self, rc_web_server, tmpdir, branch_perm, user_util,
46 branch_permission_setter, username, password):
47 repo = user_util.create_repo(repo_type='hg')
48 repo_name = repo.repo_name
49 branch_permission_setter(repo_name, username, permission=branch_perm)
50
51 clone_url = rc_web_server.repo_clone_url(
52 repo.repo_name, user=username, passwd=password)
53 Command(os.path.dirname(tmpdir.strpath)).execute(
54 'hg clone', clone_url, tmpdir.strpath)
55
56 stdout, stderr = _add_files_and_push(
57 'hg', tmpdir.strpath, clone_url=clone_url)
58 if branch_perm in ['branch.push', 'branch.push_force']:
59 _check_proper_hg_push(stdout, stderr)
60 else:
61 msg = "Branch `default` changes rejected by rule `*`=>{}".format(branch_perm)
62 assert msg in stdout
63 assert "transaction abort" in stdout
64
65 @pytest.mark.parametrize('username, password', [
66 (TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS),
67 (TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS),
68 ])
69 @pytest.mark.parametrize('branch_perm', [
70 'branch.none',
71 'branch.merge',
72 'branch.push',
73 'branch.push_force',
74 ])
75 def test_push_to_protected_branch_fails_with_message_git(
76 self, rc_web_server, tmpdir, branch_perm, user_util,
77 branch_permission_setter, username, password):
78 repo = user_util.create_repo(repo_type='git')
79 repo_name = repo.repo_name
80 branch_permission_setter(repo_name, username, permission=branch_perm)
81
82 clone_url = rc_web_server.repo_clone_url(
83 repo.repo_name, user=username, passwd=password)
84 Command(os.path.dirname(tmpdir.strpath)).execute(
85 'git clone', clone_url, tmpdir.strpath)
86
87 stdout, stderr = _add_files_and_push(
88 'git', tmpdir.strpath, clone_url=clone_url)
89 if branch_perm in ['branch.push', 'branch.push_force']:
90 _check_proper_git_push(stdout, stderr)
91 else:
92 msg = "Branch `master` changes rejected by rule `*`=>{}".format(branch_perm)
93 assert msg in stderr
94 assert "(pre-receive hook declined)" in stderr
@@ -0,0 +1,122 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-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
22 import os
23 import pytest
24
25 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
26 from rhodecode.tests.vcs_operations import (
27 Command, _check_proper_hg_push, _check_proper_git_push,
28 _add_files, _add_files_and_push)
29
30
31 @pytest.mark.usefixtures("disable_anonymous_user")
32 class TestVCSOperations(object):
33
34 def test_push_force_hg(self, rc_web_server, tmpdir, user_util):
35 repo = user_util.create_repo(repo_type='hg')
36 clone_url = rc_web_server.repo_clone_url(repo.repo_name)
37 Command(os.path.dirname(tmpdir.strpath)).execute(
38 'hg clone', clone_url, tmpdir.strpath)
39
40 stdout, stderr = _add_files_and_push(
41 'hg', tmpdir.strpath, clone_url=clone_url)
42 _check_proper_hg_push(stdout, stderr)
43
44 # rewrite history, and push with force
45 Command(tmpdir.strpath).execute(
46 'hg checkout -r 1 && hg commit -m "starting new head"')
47 _add_files('hg', tmpdir.strpath, clone_url=clone_url)
48
49 stdout, stderr = Command(tmpdir.strpath).execute(
50 'hg push --verbose -f {}'.format(clone_url))
51
52 _check_proper_hg_push(stdout, stderr)
53
54 def test_push_force_git(self, rc_web_server, tmpdir, user_util):
55 repo = user_util.create_repo(repo_type='git')
56 clone_url = rc_web_server.repo_clone_url(repo.repo_name)
57 Command(os.path.dirname(tmpdir.strpath)).execute(
58 'git clone', clone_url, tmpdir.strpath)
59
60 stdout, stderr = _add_files_and_push(
61 'git', tmpdir.strpath, clone_url=clone_url)
62 _check_proper_git_push(stdout, stderr)
63
64 # rewrite history, and push with force
65 Command(tmpdir.strpath).execute(
66 'git reset --hard HEAD~2')
67 stdout, stderr = Command(tmpdir.strpath).execute(
68 'git push -f {} master'.format(clone_url))
69
70 assert '(forced update)' in stderr
71
72 def test_push_force_hg_blocked_by_branch_permissions(
73 self, rc_web_server, tmpdir, user_util, branch_permission_setter):
74 repo = user_util.create_repo(repo_type='hg')
75 repo_name = repo.repo_name
76 username = TEST_USER_ADMIN_LOGIN
77 branch_permission_setter(repo_name, username, permission='branch.push')
78
79 clone_url = rc_web_server.repo_clone_url(repo.repo_name)
80 Command(os.path.dirname(tmpdir.strpath)).execute(
81 'hg clone', clone_url, tmpdir.strpath)
82
83 stdout, stderr = _add_files_and_push(
84 'hg', tmpdir.strpath, clone_url=clone_url)
85 _check_proper_hg_push(stdout, stderr)
86
87 # rewrite history, and push with force
88 Command(tmpdir.strpath).execute(
89 'hg checkout -r 1 && hg commit -m "starting new head"')
90 _add_files('hg', tmpdir.strpath, clone_url=clone_url)
91
92 stdout, stderr = Command(tmpdir.strpath).execute(
93 'hg push --verbose -f {}'.format(clone_url))
94
95 assert "Branch `default` changes rejected by rule `*`=>branch.push" in stdout
96 assert "FORCE PUSH FORBIDDEN" in stdout
97 assert "transaction abort" in stdout
98
99 def test_push_force_git_blocked_by_branch_permissions(
100 self, rc_web_server, tmpdir, user_util, branch_permission_setter):
101 repo = user_util.create_repo(repo_type='git')
102 repo_name = repo.repo_name
103 username = TEST_USER_ADMIN_LOGIN
104 branch_permission_setter(repo_name, username, permission='branch.push')
105
106 clone_url = rc_web_server.repo_clone_url(repo.repo_name)
107 Command(os.path.dirname(tmpdir.strpath)).execute(
108 'git clone', clone_url, tmpdir.strpath)
109
110 stdout, stderr = _add_files_and_push(
111 'git', tmpdir.strpath, clone_url=clone_url)
112 _check_proper_git_push(stdout, stderr)
113
114 # rewrite history, and push with force
115 Command(tmpdir.strpath).execute(
116 'git reset --hard HEAD~2')
117 stdout, stderr = Command(tmpdir.strpath).execute(
118 'git push -f {} master'.format(clone_url))
119
120 assert "Branch `master` changes rejected by rule `*`=>branch.push" in stderr
121 assert "FORCE PUSH FORBIDDEN" in stderr
122 assert "(pre-receive hook declined)" in stderr
@@ -0,0 +1,115 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-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
22 import os
23 import pytest
24
25 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
26 from rhodecode.tests.vcs_operations import (
27 Command, _check_proper_hg_push, _check_proper_git_push, _add_files_and_push)
28
29
30 @pytest.mark.usefixtures("disable_anonymous_user")
31 class TestVCSOperations(object):
32
33 def test_push_new_branch_hg(self, rc_web_server, tmpdir, user_util):
34 repo = user_util.create_repo(repo_type='hg')
35 clone_url = rc_web_server.repo_clone_url(repo.repo_name)
36 Command(os.path.dirname(tmpdir.strpath)).execute(
37 'hg clone', clone_url, tmpdir.strpath)
38
39 stdout, stderr = _add_files_and_push(
40 'hg', tmpdir.strpath, clone_url=clone_url)
41 _check_proper_hg_push(stdout, stderr)
42
43 # start new branch, and push file into it
44 Command(tmpdir.strpath).execute(
45 'hg branch dev && hg commit -m "starting dev branch"')
46 stdout, stderr = _add_files_and_push(
47 'hg', tmpdir.strpath, clone_url=clone_url, target_branch='dev',
48 new_branch=True)
49
50 _check_proper_hg_push(stdout, stderr)
51
52 def test_push_new_branch_git(self, rc_web_server, tmpdir, user_util):
53 repo = user_util.create_repo(repo_type='git')
54 clone_url = rc_web_server.repo_clone_url(repo.repo_name)
55 Command(os.path.dirname(tmpdir.strpath)).execute(
56 'git clone', clone_url, tmpdir.strpath)
57
58 stdout, stderr = _add_files_and_push(
59 'git', tmpdir.strpath, clone_url=clone_url)
60 _check_proper_git_push(stdout, stderr)
61
62 # start new branch, and push file into it
63 Command(tmpdir.strpath).execute('git checkout -b dev')
64 stdout, stderr = _add_files_and_push(
65 'git', tmpdir.strpath, clone_url=clone_url, target_branch='dev',
66 new_branch=True)
67
68 _check_proper_git_push(stdout, stderr, branch='dev')
69
70 def test_push_new_branch_hg_with_branch_permissions_no_force_push(
71 self, rc_web_server, tmpdir, user_util, branch_permission_setter):
72 repo = user_util.create_repo(repo_type='hg')
73 repo_name = repo.repo_name
74 username = TEST_USER_ADMIN_LOGIN
75 branch_permission_setter(repo_name, username, permission='branch.push')
76
77 clone_url = rc_web_server.repo_clone_url(repo.repo_name)
78 Command(os.path.dirname(tmpdir.strpath)).execute(
79 'hg clone', clone_url, tmpdir.strpath)
80
81 stdout, stderr = _add_files_and_push(
82 'hg', tmpdir.strpath, clone_url=clone_url)
83 _check_proper_hg_push(stdout, stderr)
84
85 # start new branch, and push file into it
86 Command(tmpdir.strpath).execute(
87 'hg branch dev && hg commit -m "starting dev branch"')
88 stdout, stderr = _add_files_and_push(
89 'hg', tmpdir.strpath, clone_url=clone_url, target_branch='dev',
90 new_branch=True)
91
92 _check_proper_hg_push(stdout, stderr)
93
94 def test_push_new_branch_git_with_branch_permissions_no_force_push(
95 self, rc_web_server, tmpdir, user_util, branch_permission_setter):
96 repo = user_util.create_repo(repo_type='git')
97 repo_name = repo.repo_name
98 username = TEST_USER_ADMIN_LOGIN
99 branch_permission_setter(repo_name, username, permission='branch.push')
100
101 clone_url = rc_web_server.repo_clone_url(repo.repo_name)
102 Command(os.path.dirname(tmpdir.strpath)).execute(
103 'git clone', clone_url, tmpdir.strpath)
104
105 stdout, stderr = _add_files_and_push(
106 'git', tmpdir.strpath, clone_url=clone_url)
107 _check_proper_git_push(stdout, stderr)
108
109 # start new branch, and push file into it
110 Command(tmpdir.strpath).execute('git checkout -b dev')
111 stdout, stderr = _add_files_and_push(
112 'git', tmpdir.strpath, clone_url=clone_url, target_branch='dev',
113 new_branch=True)
114
115 _check_proper_git_push(stdout, stderr, branch='dev')
@@ -404,14 +404,14 b' class PermissionCalculator(object):'
404 def __init__(
404 def __init__(
405 self, user_id, scope, user_is_admin,
405 self, user_id, scope, user_is_admin,
406 user_inherit_default_permissions, explicit, algo,
406 user_inherit_default_permissions, explicit, algo,
407 calculate_super_admin=False):
407 calculate_super_admin_as_user=False):
408
408
409 self.user_id = user_id
409 self.user_id = user_id
410 self.user_is_admin = user_is_admin
410 self.user_is_admin = user_is_admin
411 self.inherit_default_permissions = user_inherit_default_permissions
411 self.inherit_default_permissions = user_inherit_default_permissions
412 self.explicit = explicit
412 self.explicit = explicit
413 self.algo = algo
413 self.algo = algo
414 self.calculate_super_admin = calculate_super_admin
414 self.calculate_super_admin_as_user = calculate_super_admin_as_user
415
415
416 scope = scope or {}
416 scope = scope or {}
417 self.scope_repo_id = scope.get('repo_id')
417 self.scope_repo_id = scope.get('repo_id')
@@ -440,8 +440,8 b' class PermissionCalculator(object):'
440 self.default_user_id, self.scope_repo_id)
440 self.default_user_id, self.scope_repo_id)
441
441
442 def calculate(self):
442 def calculate(self):
443 if self.user_is_admin and not self.calculate_super_admin:
443 if self.user_is_admin and not self.calculate_super_admin_as_user:
444 return self._admin_permissions()
444 return self._calculate_admin_permissions()
445
445
446 self._calculate_global_default_permissions()
446 self._calculate_global_default_permissions()
447 self._calculate_global_permissions()
447 self._calculate_global_permissions()
@@ -452,7 +452,7 b' class PermissionCalculator(object):'
452 self._calculate_user_group_permissions()
452 self._calculate_user_group_permissions()
453 return self._permission_structure()
453 return self._permission_structure()
454
454
455 def _admin_permissions(self):
455 def _calculate_admin_permissions(self):
456 """
456 """
457 admin user have all default rights for repositories
457 admin user have all default rights for repositories
458 and groups set to admin
458 and groups set to admin
@@ -479,13 +479,11 b' class PermissionCalculator(object):'
479 self.permissions_user_groups[u_k] = p, PermOrigin.SUPER_ADMIN
479 self.permissions_user_groups[u_k] = p, PermOrigin.SUPER_ADMIN
480
480
481 # branch permissions
481 # branch permissions
482 # TODO(marcink): validate this, especially
482 # since super-admin also can have custom rule permissions
483 # how this should work using multiple patterns specified ??
483 # we *always* need to calculate those inherited from default, and also explicit
484 # looks ok, but still needs double check !!
484 self._calculate_default_permissions_repository_branches(
485 for perm in self.default_branch_repo_perms:
485 user_inherit_object_permissions=False)
486 r_k = perm.UserRepoToPerm.repository.repo_name
486 self._calculate_repository_branch_permissions()
487 p = 'branch.push_force'
488 self.permissions_repository_branches[r_k] = '*', p, PermOrigin.SUPER_ADMIN
489
487
490 return self._permission_structure()
488 return self._permission_structure()
491
489
@@ -571,27 +569,7 b' class PermissionCalculator(object):'
571 for perm in user_perms:
569 for perm in user_perms:
572 self.permissions_global.add(perm.permission.permission_name)
570 self.permissions_global.add(perm.permission.permission_name)
573
571
574 def _calculate_default_permissions(self):
572 def _calculate_default_permissions_repositories(self, user_inherit_object_permissions):
575 """
576 Set default user permissions for repositories, repository branches,
577 repository groups, user groups taken from the default user.
578
579 Calculate inheritance of object permissions based on what we have now
580 in GLOBAL permissions. We check if .false is in GLOBAL since this is
581 explicitly set. Inherit is the opposite of .false being there.
582
583 .. note::
584
585 the syntax is little bit odd but what we need to check here is
586 the opposite of .false permission being in the list so even for
587 inconsistent state when both .true/.false is there
588 .false is more important
589
590 """
591 user_inherit_object_permissions = not ('hg.inherit_default_perms.false'
592 in self.permissions_global)
593
594 # default permissions for repositories, taken from `default` user permissions
595 for perm in self.default_repo_perms:
573 for perm in self.default_repo_perms:
596 r_k = perm.UserRepoToPerm.repository.repo_name
574 r_k = perm.UserRepoToPerm.repository.repo_name
597 p = perm.Permission.permission_name
575 p = perm.Permission.permission_name
@@ -624,7 +602,7 b' class PermissionCalculator(object):'
624 o = PermOrigin.SUPER_ADMIN
602 o = PermOrigin.SUPER_ADMIN
625 self.permissions_repositories[r_k] = p, o
603 self.permissions_repositories[r_k] = p, o
626
604
627 # default permissions branch for repositories, taken from `default` user permissions
605 def _calculate_default_permissions_repository_branches(self, user_inherit_object_permissions):
628 for perm in self.default_branch_repo_perms:
606 for perm in self.default_branch_repo_perms:
629
607
630 r_k = perm.UserRepoToPerm.repository.repo_name
608 r_k = perm.UserRepoToPerm.repository.repo_name
@@ -641,7 +619,7 b' class PermissionCalculator(object):'
641 # special dict that aggregates entries
619 # special dict that aggregates entries
642 self.permissions_repository_branches[r_k] = pattern, p, o
620 self.permissions_repository_branches[r_k] = pattern, p, o
643
621
644 # default permissions for repository groups taken from `default` user permission
622 def _calculate_default_permissions_repository_groups(self, user_inherit_object_permissions):
645 for perm in self.default_repo_groups_perms:
623 for perm in self.default_repo_groups_perms:
646 rg_k = perm.UserRepoGroupToPerm.group.group_name
624 rg_k = perm.UserRepoGroupToPerm.group.group_name
647 p = perm.Permission.permission_name
625 p = perm.Permission.permission_name
@@ -666,7 +644,7 b' class PermissionCalculator(object):'
666 o = PermOrigin.SUPER_ADMIN
644 o = PermOrigin.SUPER_ADMIN
667 self.permissions_repository_groups[rg_k] = p, o
645 self.permissions_repository_groups[rg_k] = p, o
668
646
669 # default permissions for user groups taken from `default` user permission
647 def _calculate_default_permissions_user_groups(self, user_inherit_object_permissions):
670 for perm in self.default_user_group_perms:
648 for perm in self.default_user_group_perms:
671 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
649 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
672 p = perm.Permission.permission_name
650 p = perm.Permission.permission_name
@@ -691,6 +669,39 b' class PermissionCalculator(object):'
691 o = PermOrigin.SUPER_ADMIN
669 o = PermOrigin.SUPER_ADMIN
692 self.permissions_user_groups[u_k] = p, o
670 self.permissions_user_groups[u_k] = p, o
693
671
672 def _calculate_default_permissions(self):
673 """
674 Set default user permissions for repositories, repository branches,
675 repository groups, user groups taken from the default user.
676
677 Calculate inheritance of object permissions based on what we have now
678 in GLOBAL permissions. We check if .false is in GLOBAL since this is
679 explicitly set. Inherit is the opposite of .false being there.
680
681 .. note::
682
683 the syntax is little bit odd but what we need to check here is
684 the opposite of .false permission being in the list so even for
685 inconsistent state when both .true/.false is there
686 .false is more important
687
688 """
689 user_inherit_object_permissions = not ('hg.inherit_default_perms.false'
690 in self.permissions_global)
691
692 # default permissions inherited from `default` user permissions
693 self._calculate_default_permissions_repositories(
694 user_inherit_object_permissions)
695
696 self._calculate_default_permissions_repository_branches(
697 user_inherit_object_permissions)
698
699 self._calculate_default_permissions_repository_groups(
700 user_inherit_object_permissions)
701
702 self._calculate_default_permissions_user_groups(
703 user_inherit_object_permissions)
704
694 def _calculate_repository_permissions(self):
705 def _calculate_repository_permissions(self):
695 """
706 """
696 Repository permissions for the current user.
707 Repository permissions for the current user.
@@ -783,6 +794,7 b' class PermissionCalculator(object):'
783 # any specified by the group permission
794 # any specified by the group permission
784 user_repo_branch_perms = Permission.get_default_repo_branch_perms(
795 user_repo_branch_perms = Permission.get_default_repo_branch_perms(
785 self.user_id, self.scope_repo_id)
796 self.user_id, self.scope_repo_id)
797
786 for perm in user_repo_branch_perms:
798 for perm in user_repo_branch_perms:
787
799
788 r_k = perm.UserRepoToPerm.repository.repo_name
800 r_k = perm.UserRepoToPerm.repository.repo_name
@@ -799,7 +811,6 b' class PermissionCalculator(object):'
799 # special dict that aggregates entries
811 # special dict that aggregates entries
800 self.permissions_repository_branches[r_k] = pattern, p, o
812 self.permissions_repository_branches[r_k] = pattern, p, o
801
813
802
803 def _calculate_repository_group_permissions(self):
814 def _calculate_repository_group_permissions(self):
804 """
815 """
805 Repository group permissions for the current user.
816 Repository group permissions for the current user.
@@ -1036,14 +1047,14 b' class AuthUser(object):'
1036
1047
1037 @LazyProperty
1048 @LazyProperty
1038 def permissions(self):
1049 def permissions(self):
1039 return self.get_perms(user=self, cache=False)
1050 return self.get_perms(user=self, cache=None)
1040
1051
1041 @LazyProperty
1052 @LazyProperty
1042 def permissions_safe(self):
1053 def permissions_safe(self):
1043 """
1054 """
1044 Filtered permissions excluding not allowed repositories
1055 Filtered permissions excluding not allowed repositories
1045 """
1056 """
1046 perms = self.get_perms(user=self, cache=False)
1057 perms = self.get_perms(user=self, cache=None)
1047
1058
1048 perms['repositories'] = {
1059 perms['repositories'] = {
1049 k: v for k, v in perms['repositories'].items()
1060 k: v for k, v in perms['repositories'].items()
@@ -1062,7 +1073,7 b' class AuthUser(object):'
1062 @LazyProperty
1073 @LazyProperty
1063 def permissions_full_details(self):
1074 def permissions_full_details(self):
1064 return self.get_perms(
1075 return self.get_perms(
1065 user=self, cache=False, calculate_super_admin=True)
1076 user=self, cache=None, calculate_super_admin=True)
1066
1077
1067 def permissions_with_scope(self, scope):
1078 def permissions_with_scope(self, scope):
1068 """
1079 """
@@ -1088,7 +1099,7 b' class AuthUser(object):'
1088 # store in cache to mimic how the @LazyProperty works,
1099 # store in cache to mimic how the @LazyProperty works,
1089 # the difference here is that we use the unique key calculated
1100 # the difference here is that we use the unique key calculated
1090 # from params and values
1101 # from params and values
1091 return self.get_perms(user=self, cache=False, scope=_scope)
1102 return self.get_perms(user=self, cache=None, scope=_scope)
1092
1103
1093 def get_instance(self):
1104 def get_instance(self):
1094 return User.get(self.user_id)
1105 return User.get(self.user_id)
@@ -1143,7 +1154,7 b' class AuthUser(object):'
1143 log.debug('AuthUser: propagated user is now %s', self)
1154 log.debug('AuthUser: propagated user is now %s', self)
1144
1155
1145 def get_perms(self, user, scope=None, explicit=True, algo='higherwin',
1156 def get_perms(self, user, scope=None, explicit=True, algo='higherwin',
1146 calculate_super_admin=False, cache=False):
1157 calculate_super_admin=False, cache=None):
1147 """
1158 """
1148 Fills user permission attribute with permissions taken from database
1159 Fills user permission attribute with permissions taken from database
1149 works for permissions given for repositories, and for permissions that
1160 works for permissions given for repositories, and for permissions that
@@ -1158,6 +1169,9 b' class AuthUser(object):'
1158 it's multiple defined, eg user in two different groups. It also
1169 it's multiple defined, eg user in two different groups. It also
1159 decides if explicit flag is turned off how to specify the permission
1170 decides if explicit flag is turned off how to specify the permission
1160 for case when user is in a group + have defined separate permission
1171 for case when user is in a group + have defined separate permission
1172 :param calculate_super_admin: calculate permissions for super-admin in the
1173 same way as for regular user without speedups
1174 :param cache: Use caching for calculation, None = let the cache backend decide
1161 """
1175 """
1162 user_id = user.user_id
1176 user_id = user.user_id
1163 user_is_admin = user.is_admin
1177 user_is_admin = user.is_admin
@@ -1168,7 +1182,12 b' class AuthUser(object):'
1168 cache_seconds = safe_int(
1182 cache_seconds = safe_int(
1169 rhodecode.CONFIG.get('rc_cache.cache_perms.expiration_time'))
1183 rhodecode.CONFIG.get('rc_cache.cache_perms.expiration_time'))
1170
1184
1171 cache_on = cache or cache_seconds > 0
1185 if cache is None:
1186 # let the backend cache decide
1187 cache_on = cache_seconds > 0
1188 else:
1189 cache_on = cache
1190
1172 log.debug(
1191 log.debug(
1173 'Computing PERMISSION tree for user %s scope `%s` '
1192 'Computing PERMISSION tree for user %s scope `%s` '
1174 'with caching: %s[TTL: %ss]' % (user, scope, cache_on, cache_seconds or 0))
1193 'with caching: %s[TTL: %ss]' % (user, scope, cache_on, cache_seconds or 0))
@@ -1186,7 +1205,8 b' class AuthUser(object):'
1186 explicit, algo, calculate_super_admin)
1205 explicit, algo, calculate_super_admin)
1187
1206
1188 start = time.time()
1207 start = time.time()
1189 result = compute_perm_tree('permissions', user_id, scope, user_is_admin,
1208 result = compute_perm_tree(
1209 'permissions', user_id, scope, user_is_admin,
1190 user_inherit_default_permissions, explicit, algo,
1210 user_inherit_default_permissions, explicit, algo,
1191 calculate_super_admin)
1211 calculate_super_admin)
1192
1212
@@ -1340,6 +1360,35 b' class AuthUser(object):'
1340 'not in %s' % (ip_addr, user_id, allowed_ips))
1360 'not in %s' % (ip_addr, user_id, allowed_ips))
1341 return False
1361 return False
1342
1362
1363 def get_branch_permissions(self, repo_name, perms=None):
1364 perms = perms or self.permissions_with_scope({'repo_name': repo_name})
1365 branch_perms = perms.get('repository_branches')
1366 return branch_perms
1367
1368 def get_rule_and_branch_permission(self, repo_name, branch_name):
1369 """
1370 Check if this AuthUser has defined any permissions for branches. If any of
1371 the rules match in order, we return the matching permissions
1372 """
1373
1374 rule = default_perm = ''
1375
1376 branch_perms = self.get_branch_permissions(repo_name=repo_name)
1377 if not branch_perms:
1378 return rule, default_perm
1379
1380 repo_branch_perms = branch_perms.get(repo_name)
1381 if not repo_branch_perms:
1382 return rule, default_perm
1383
1384 # now calculate the permissions
1385 for pattern, branch_perm in repo_branch_perms.items():
1386 if fnmatch.fnmatch(branch_name, pattern):
1387 rule = '`{}`=>{}'.format(pattern, branch_perm)
1388 return rule, branch_perm
1389
1390 return rule, default_perm
1391
1343 def __repr__(self):
1392 def __repr__(self):
1344 return "<AuthUser('id:%s[%s] ip:%s auth:%s')>"\
1393 return "<AuthUser('id:%s[%s] ip:%s auth:%s')>"\
1345 % (self.user_id, self.username, self.ip_addr, self.is_authenticated)
1394 % (self.user_id, self.username, self.ip_addr, self.is_authenticated)
@@ -2084,23 +2133,22 b' class HasPermissionAnyMiddleware(object)'
2084 def __init__(self, *perms):
2133 def __init__(self, *perms):
2085 self.required_perms = set(perms)
2134 self.required_perms = set(perms)
2086
2135
2087 def __call__(self, user, repo_name):
2136 def __call__(self, auth_user, repo_name):
2088 # repo_name MUST be unicode, since we handle keys in permission
2137 # repo_name MUST be unicode, since we handle keys in permission
2089 # dict by unicode
2138 # dict by unicode
2090 repo_name = safe_unicode(repo_name)
2139 repo_name = safe_unicode(repo_name)
2091 user = AuthUser(user.user_id)
2092 log.debug(
2140 log.debug(
2093 'Checking VCS protocol permissions %s for user:%s repo:`%s`',
2141 'Checking VCS protocol permissions %s for user:%s repo:`%s`',
2094 self.required_perms, user, repo_name)
2142 self.required_perms, auth_user, repo_name)
2095
2143
2096 if self.check_permissions(user, repo_name):
2144 if self.check_permissions(auth_user, repo_name):
2097 log.debug('Permission to repo:`%s` GRANTED for user:%s @ %s',
2145 log.debug('Permission to repo:`%s` GRANTED for user:%s @ %s',
2098 repo_name, user, 'PermissionMiddleware')
2146 repo_name, auth_user, 'PermissionMiddleware')
2099 return True
2147 return True
2100
2148
2101 else:
2149 else:
2102 log.debug('Permission to repo:`%s` DENIED for user:%s @ %s',
2150 log.debug('Permission to repo:`%s` DENIED for user:%s @ %s',
2103 repo_name, user, 'PermissionMiddleware')
2151 repo_name, auth_user, 'PermissionMiddleware')
2104 return False
2152 return False
2105
2153
2106 def check_permissions(self, user, repo_name):
2154 def check_permissions(self, user, repo_name):
@@ -153,7 +153,7 b' def get_user_agent(environ):'
153
153
154 def vcs_operation_context(
154 def vcs_operation_context(
155 environ, repo_name, username, action, scm, check_locking=True,
155 environ, repo_name, username, action, scm, check_locking=True,
156 is_shadow_repo=False):
156 is_shadow_repo=False, check_branch_perms=False, detect_force_push=False):
157 """
157 """
158 Generate the context for a vcs operation, e.g. push or pull.
158 Generate the context for a vcs operation, e.g. push or pull.
159
159
@@ -193,6 +193,8 b' def vcs_operation_context('
193 'user_agent': get_user_agent(environ),
193 'user_agent': get_user_agent(environ),
194 'hooks': get_enabled_hook_classes(ui_settings),
194 'hooks': get_enabled_hook_classes(ui_settings),
195 'is_shadow_repo': is_shadow_repo,
195 'is_shadow_repo': is_shadow_repo,
196 'detect_force_push': detect_force_push,
197 'check_branch_perms': check_branch_perms,
196 }
198 }
197 return extras
199 return extras
198
200
@@ -107,6 +107,21 b' class HTTPLockedRC(HTTPClientError):'
107 self.args = (message, )
107 self.args = (message, )
108
108
109
109
110 class HTTPBranchProtected(HTTPClientError):
111 """
112 Special Exception For Indicating that branch is protected in RhodeCode, the
113 return code can be overwritten by _code keyword argument passed into constructors
114 """
115 code = 403
116 title = explanation = 'Branch Protected'
117 reason = None
118
119 def __init__(self, message, *args, **kwargs):
120 self.title = self.explanation = message
121 super(HTTPBranchProtected, self).__init__(*args, **kwargs)
122 self.args = (message, )
123
124
110 class IMCCommitError(Exception):
125 class IMCCommitError(Exception):
111 pass
126 pass
112
127
@@ -32,7 +32,8 b' from rhodecode import events'
32 from rhodecode.lib import helpers as h
32 from rhodecode.lib import helpers as h
33 from rhodecode.lib import audit_logger
33 from rhodecode.lib import audit_logger
34 from rhodecode.lib.utils2 import safe_str
34 from rhodecode.lib.utils2 import safe_str
35 from rhodecode.lib.exceptions import HTTPLockedRC, UserCreationError
35 from rhodecode.lib.exceptions import (
36 HTTPLockedRC, HTTPBranchProtected, UserCreationError)
36 from rhodecode.model.db import Repository, User
37 from rhodecode.model.db import Repository, User
37
38
38 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
@@ -94,9 +95,9 b' def pre_push(extras):'
94 It bans pushing when the repository is locked.
95 It bans pushing when the repository is locked.
95 """
96 """
96
97
97 usr = User.get_by_username(extras.username)
98 user = User.get_by_username(extras.username)
98 output = ''
99 output = ''
99 if extras.locked_by[0] and usr.user_id != int(extras.locked_by[0]):
100 if extras.locked_by[0] and user.user_id != int(extras.locked_by[0]):
100 locked_by = User.get(extras.locked_by[0]).username
101 locked_by = User.get(extras.locked_by[0]).username
101 reason = extras.locked_by[2]
102 reason = extras.locked_by[2]
102 # this exception is interpreted in git/hg middlewares and based
103 # this exception is interpreted in git/hg middlewares and based
@@ -109,9 +110,48 b' def pre_push(extras):'
109 else:
110 else:
110 raise _http_ret
111 raise _http_ret
111
112
113 if not is_shadow_repo(extras):
114 if extras.commit_ids and extras.check_branch_perms:
115
116 auth_user = user.AuthUser()
117 repo = Repository.get_by_repo_name(extras.repository)
118 affected_branches = []
119 if repo.repo_type == 'hg':
120 for entry in extras.commit_ids:
121 if entry['type'] == 'branch':
122 is_forced = bool(entry['multiple_heads'])
123 affected_branches.append([entry['name'], is_forced])
124 elif repo.repo_type == 'git':
125 for entry in extras.commit_ids:
126 if entry['type'] == 'heads':
127 is_forced = bool(entry['pruned_sha'])
128 affected_branches.append([entry['name'], is_forced])
129
130 for branch_name, is_forced in affected_branches:
131
132 rule, branch_perm = auth_user.get_rule_and_branch_permission(
133 extras.repository, branch_name)
134 if not branch_perm:
135 # no branch permission found for this branch, just keep checking
136 continue
137
138 if branch_perm == 'branch.push_force':
139 continue
140 elif branch_perm == 'branch.push' and is_forced is False:
141 continue
142 elif branch_perm == 'branch.push' and is_forced is True:
143 halt_message = 'Branch `{}` changes rejected by rule {}. ' \
144 'FORCE PUSH FORBIDDEN.'.format(branch_name, rule)
145 else:
146 halt_message = 'Branch `{}` changes rejected by rule {}.'.format(
147 branch_name, rule)
148
149 if halt_message:
150 _http_ret = HTTPBranchProtected(halt_message)
151 raise _http_ret
152
112 # Propagate to external components. This is done after checking the
153 # Propagate to external components. This is done after checking the
113 # lock, for consistent behavior.
154 # lock, for consistent behavior.
114 if not is_shadow_repo(extras):
115 pre_push_extension(repo_store_path=Repository.base_path(), **extras)
155 pre_push_extension(repo_store_path=Repository.base_path(), **extras)
116 events.trigger(events.RepoPrePushEvent(
156 events.trigger(events.RepoPrePushEvent(
117 repo_name=extras.repository, extras=extras))
157 repo_name=extras.repository, extras=extras))
@@ -29,6 +29,7 b' from BaseHTTPServer import BaseHTTPReque'
29 from SocketServer import TCPServer
29 from SocketServer import TCPServer
30
30
31 import rhodecode
31 import rhodecode
32 from rhodecode.lib.exceptions import HTTPLockedRC, HTTPBranchProtected
32 from rhodecode.model import meta
33 from rhodecode.model import meta
33 from rhodecode.lib.base import bootstrap_request, bootstrap_config
34 from rhodecode.lib.base import bootstrap_request, bootstrap_config
34 from rhodecode.lib import hooks_base
35 from rhodecode.lib import hooks_base
@@ -285,7 +286,18 b' class Hooks(object):'
285
286
286 try:
287 try:
287 result = hook(extras)
288 result = hook(extras)
288 except Exception as error:
289 except HTTPBranchProtected as handled_error:
290 # Those special cases doesn't need error reporting. It's a case of
291 # locked repo or protected branch
292 result = AttributeDict({
293 'status': handled_error.code,
294 'output': handled_error.explanation
295 })
296 except (HTTPLockedRC, Exception) as error:
297 # locked needs different handling since we need to also
298 # handle PULL operations
299 exc_tb = ''
300 if not isinstance(error, HTTPLockedRC):
289 exc_tb = traceback.format_exc()
301 exc_tb = traceback.format_exc()
290 log.exception('Exception when handling hook %s', hook)
302 log.exception('Exception when handling hook %s', hook)
291 error_args = error.args
303 error_args = error.args
@@ -297,7 +297,7 b' class SimpleVCS(object):'
297 def is_shadow_repo_dir(self):
297 def is_shadow_repo_dir(self):
298 return os.path.isdir(self.vcs_repo_name)
298 return os.path.isdir(self.vcs_repo_name)
299
299
300 def _check_permission(self, action, user, repo_name, ip_addr=None,
300 def _check_permission(self, action, user, auth_user, repo_name, ip_addr=None,
301 plugin_id='', plugin_cache_active=False, cache_ttl=0):
301 plugin_id='', plugin_cache_active=False, cache_ttl=0):
302 """
302 """
303 Checks permissions using action (push/pull) user and repository
303 Checks permissions using action (push/pull) user and repository
@@ -335,14 +335,14 b' class SimpleVCS(object):'
335
335
336 if action == 'push':
336 if action == 'push':
337 perms = ('repository.write', 'repository.admin')
337 perms = ('repository.write', 'repository.admin')
338 if not HasPermissionAnyMiddleware(*perms)(user, repo_name):
338 if not HasPermissionAnyMiddleware(*perms)(auth_user, repo_name):
339 return False
339 return False
340
340
341 else:
341 else:
342 # any other action need at least read permission
342 # any other action need at least read permission
343 perms = (
343 perms = (
344 'repository.read', 'repository.write', 'repository.admin')
344 'repository.read', 'repository.write', 'repository.admin')
345 if not HasPermissionAnyMiddleware(*perms)(user, repo_name):
345 if not HasPermissionAnyMiddleware(*perms)(auth_user, repo_name):
346 return False
346 return False
347
347
348 return True
348 return True
@@ -441,14 +441,17 b' class SimpleVCS(object):'
441 # ======================================================================
441 # ======================================================================
442 # CHECK ANONYMOUS PERMISSION
442 # CHECK ANONYMOUS PERMISSION
443 # ======================================================================
443 # ======================================================================
444 detect_force_push = False
445 check_branch_perms = False
444 if action in ['pull', 'push']:
446 if action in ['pull', 'push']:
445 anonymous_user = User.get_default_user()
447 user_obj = anonymous_user = User.get_default_user()
448 auth_user = user_obj.AuthUser()
446 username = anonymous_user.username
449 username = anonymous_user.username
447 if anonymous_user.active:
450 if anonymous_user.active:
448 plugin_cache_active, cache_ttl = self._get_default_cache_ttl()
451 plugin_cache_active, cache_ttl = self._get_default_cache_ttl()
449 # ONLY check permissions if the user is activated
452 # ONLY check permissions if the user is activated
450 anonymous_perm = self._check_permission(
453 anonymous_perm = self._check_permission(
451 action, anonymous_user, self.acl_repo_name, ip_addr,
454 action, anonymous_user, auth_user, self.acl_repo_name, ip_addr,
452 plugin_id='anonymous_access',
455 plugin_id='anonymous_access',
453 plugin_cache_active=plugin_cache_active,
456 plugin_cache_active=plugin_cache_active,
454 cache_ttl=cache_ttl,
457 cache_ttl=cache_ttl,
@@ -525,6 +528,7 b' class SimpleVCS(object):'
525
528
526 # check user attributes for password change flag
529 # check user attributes for password change flag
527 user_obj = user
530 user_obj = user
531 auth_user = user_obj.AuthUser()
528 if user_obj and user_obj.username != User.DEFAULT_USER and \
532 if user_obj and user_obj.username != User.DEFAULT_USER and \
529 user_obj.user_data.get('force_password_change'):
533 user_obj.user_data.get('force_password_change'):
530 reason = 'password change required'
534 reason = 'password change required'
@@ -533,19 +537,27 b' class SimpleVCS(object):'
533
537
534 # check permissions for this repository
538 # check permissions for this repository
535 perm = self._check_permission(
539 perm = self._check_permission(
536 action, user, self.acl_repo_name, ip_addr,
540 action, user, auth_user, self.acl_repo_name, ip_addr,
537 plugin, plugin_cache_active, cache_ttl)
541 plugin, plugin_cache_active, cache_ttl)
538 if not perm:
542 if not perm:
539 return HTTPForbidden()(environ, start_response)
543 return HTTPForbidden()(environ, start_response)
540 environ['rc_auth_user_id'] = user_id
544 environ['rc_auth_user_id'] = user_id
541
545
546 if action == 'push':
547 perms = auth_user.get_branch_permissions(self.acl_repo_name)
548 if perms:
549 check_branch_perms = True
550 detect_force_push = True
551
542 # extras are injected into UI object and later available
552 # extras are injected into UI object and later available
543 # in hooks executed by RhodeCode
553 # in hooks executed by RhodeCode
544 check_locking = _should_check_locking(environ.get('QUERY_STRING'))
554 check_locking = _should_check_locking(environ.get('QUERY_STRING'))
555
545 extras = vcs_operation_context(
556 extras = vcs_operation_context(
546 environ, repo_name=self.acl_repo_name, username=username,
557 environ, repo_name=self.acl_repo_name, username=username,
547 action=action, scm=self.SCM, check_locking=check_locking,
558 action=action, scm=self.SCM, check_locking=check_locking,
548 is_shadow_repo=self.is_shadow_repo
559 is_shadow_repo=self.is_shadow_repo, check_branch_perms=check_branch_perms,
560 detect_force_push=detect_force_push
549 )
561 )
550
562
551 # ======================================================================
563 # ======================================================================
@@ -419,7 +419,7 b' def test_permission_calculator_admin_per'
419
419
420 calculator = auth.PermissionCalculator(
420 calculator = auth.PermissionCalculator(
421 user.user_id, {}, False, False, True, 'higherwin')
421 user.user_id, {}, False, False, True, 'higherwin')
422 permissions = calculator._admin_permissions()
422 permissions = calculator._calculate_admin_permissions()
423
423
424 assert permissions['repositories_groups'][repo_group.group_name] == \
424 assert permissions['repositories_groups'][repo_group.group_name] == \
425 'group.admin'
425 'group.admin'
@@ -77,22 +77,18 b' class Command(object):'
77 assert self.process.returncode == 0
77 assert self.process.returncode == 0
78
78
79
79
80 def _add_files_and_push(vcs, dest, clone_url=None, tags=None, **kwargs):
80 def _add_files(vcs, dest, clone_url=None, tags=None, target_branch=None,
81 """
81 new_branch=False, **kwargs):
82 Generate some files, add it to DEST repo and push back
82 git_ident = "git config user.name {} && git config user.email {}".format(
83 vcs is git or hg and defines what VCS we want to make those files for
83 'Marcin KuΕΊminski', 'me@email.com')
84 """
84 cwd = path = jn(dest)
85 # commit some stuff into this repo
85
86 tags = tags or []
86 tags = tags or []
87 cwd = path = jn(dest)
88 added_file = jn(path, '%ssetup.py' % tempfile._RandomNameSequence().next())
87 added_file = jn(path, '%ssetup.py' % tempfile._RandomNameSequence().next())
89 Command(cwd).execute('touch %s' % added_file)
88 Command(cwd).execute('touch %s' % added_file)
90 Command(cwd).execute('%s add %s' % (vcs, added_file))
89 Command(cwd).execute('%s add %s' % (vcs, added_file))
91 author_str = 'Marcin KuΕΊminski <me@email.com>'
90 author_str = 'Marcin KuΕΊminski <me@email.com>'
92
91
93 git_ident = "git config user.name {} && git config user.email {}".format(
94 'Marcin KuΕΊminski', 'me@email.com')
95
96 for i in range(kwargs.get('files_no', 3)):
92 for i in range(kwargs.get('files_no', 3)):
97 cmd = """echo 'added_line%s' >> %s""" % (i, added_file)
93 cmd = """echo 'added_line%s' >> %s""" % (i, added_file)
98 Command(cwd).execute(cmd)
94 Command(cwd).execute(cmd)
@@ -107,30 +103,55 b' def _add_files_and_push(vcs, dest, clone'
107
103
108 for tag in tags:
104 for tag in tags:
109 if vcs == 'hg':
105 if vcs == 'hg':
110 stdout, stderr = Command(cwd).execute(
106 Command(cwd).execute(
111 'hg tag', tag['name'])
107 'hg tag', tag['name'])
112 elif vcs == 'git':
108 elif vcs == 'git':
113 if tag['commit']:
109 if tag['commit']:
114 # annotated tag
110 # annotated tag
115 stdout, stderr = Command(cwd).execute(
111 _stdout, _stderr = Command(cwd).execute(
116 """%s && git tag -a %s -m "%s" """ % (
112 """%s && git tag -a %s -m "%s" """ % (
117 git_ident, tag['name'], tag['commit']))
113 git_ident, tag['name'], tag['commit']))
118 else:
114 else:
119 # lightweight tag
115 # lightweight tag
120 stdout, stderr = Command(cwd).execute(
116 _stdout, _stderr = Command(cwd).execute(
121 """%s && git tag %s""" % (
117 """%s && git tag %s""" % (
122 git_ident, tag['name']))
118 git_ident, tag['name']))
123
119
120
121 def _add_files_and_push(vcs, dest, clone_url=None, tags=None, target_branch=None,
122 new_branch=False, **kwargs):
123 """
124 Generate some files, add it to DEST repo and push back
125 vcs is git or hg and defines what VCS we want to make those files for
126 """
127 git_ident = "git config user.name {} && git config user.email {}".format(
128 'Marcin KuΕΊminski', 'me@email.com')
129 cwd = path = jn(dest)
130
131 # commit some stuff into this repo
132 _add_files(vcs, dest, clone_url, tags, target_branch, new_branch, **kwargs)
133
134 default_target_branch = {
135 'git': 'master',
136 'hg': 'default'
137 }.get(vcs)
138
139 target_branch = target_branch or default_target_branch
140
124 # PUSH it back
141 # PUSH it back
125 stdout = stderr = None
142 stdout = stderr = None
126 if vcs == 'hg':
143 if vcs == 'hg':
144 maybe_new_branch = ''
145 if new_branch:
146 maybe_new_branch = '--new-branch'
127 stdout, stderr = Command(cwd).execute(
147 stdout, stderr = Command(cwd).execute(
128 'hg push --verbose', clone_url)
148 'hg push --verbose {} -r {} {}'.format(maybe_new_branch, target_branch, clone_url)
149 )
129 elif vcs == 'git':
150 elif vcs == 'git':
130 stdout, stderr = Command(cwd).execute(
151 stdout, stderr = Command(cwd).execute(
131 """%s &&
152 """{} &&
132 git push --verbose --tags %s master""" % (
153 git push --verbose --tags {} {}""".format(git_ident, clone_url, target_branch)
133 git_ident, clone_url))
154 )
134
155
135 return stdout, stderr
156 return stdout, stderr
136
157
@@ -33,7 +33,8 b' import textwrap'
33 import pytest
33 import pytest
34
34
35 from rhodecode import events
35 from rhodecode import events
36 from rhodecode.model.db import Integration
36 from rhodecode.model.db import Integration, UserRepoToPerm, Permission, \
37 UserToRepoBranchPermission, User
37 from rhodecode.model.integration import IntegrationModel
38 from rhodecode.model.integration import IntegrationModel
38 from rhodecode.model.db import Repository
39 from rhodecode.model.db import Repository
39 from rhodecode.model.meta import Session
40 from rhodecode.model.meta import Session
@@ -267,3 +268,74 b' def enable_webhook_push_integration(requ'
267 Session().delete(integration)
268 Session().delete(integration)
268 Session().commit()
269 Session().commit()
269
270
271
272 @pytest.fixture
273 def branch_permission_setter(request):
274 """
275
276 def my_test(branch_permission_setter)
277 branch_permission_setter(repo_name, username, pattern='*', permission='branch.push')
278
279 """
280
281 rule_id = None
282 write_perm_id = None
283
284 def _branch_permissions_setter(
285 repo_name, username, pattern='*', permission='branch.push_force'):
286 global rule_id, write_perm_id
287
288 repo = Repository.get_by_repo_name(repo_name)
289 repo_id = repo.repo_id
290
291 user = User.get_by_username(username)
292 user_id = user.user_id
293
294 rule_perm_obj = Permission.get_by_key(permission)
295
296 write_perm = None
297
298 # add new entry, based on existing perm entry
299 perm = UserRepoToPerm.query() \
300 .filter(UserRepoToPerm.repository_id == repo_id) \
301 .filter(UserRepoToPerm.user_id == user_id) \
302 .first()
303
304 if not perm:
305 # such user isn't defined in Permissions for repository
306 # we now on-the-fly add new permission
307
308 write_perm = UserRepoToPerm()
309 write_perm.permission = Permission.get_by_key('repository.write')
310 write_perm.repository_id = repo_id
311 write_perm.user_id = user_id
312 Session().add(write_perm)
313 Session().flush()
314
315 perm = write_perm
316
317 rule = UserToRepoBranchPermission()
318 rule.rule_to_perm_id = perm.repo_to_perm_id
319 rule.branch_pattern = pattern
320 rule.rule_order = 10
321 rule.permission = rule_perm_obj
322 rule.repository_id = repo_id
323 Session().add(rule)
324 Session().commit()
325
326 global rule, write_perm
327
328 return rule
329
330 @request.addfinalizer
331 def cleanup():
332 if rule:
333 Session().delete(rule)
334 Session().commit()
335 if write_perm:
336 Session().delete(write_perm)
337 Session().commit()
338
339 return _branch_permissions_setter
340
341
General Comments 0
You need to be logged in to leave comments. Login now