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 | 404 | def __init__( |
|
405 | 405 | self, user_id, scope, user_is_admin, |
|
406 | 406 | user_inherit_default_permissions, explicit, algo, |
|
407 | calculate_super_admin=False): | |
|
407 | calculate_super_admin_as_user=False): | |
|
408 | 408 | |
|
409 | 409 | self.user_id = user_id |
|
410 | 410 | self.user_is_admin = user_is_admin |
|
411 | 411 | self.inherit_default_permissions = user_inherit_default_permissions |
|
412 | 412 | self.explicit = explicit |
|
413 | 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 | 416 | scope = scope or {} |
|
417 | 417 | self.scope_repo_id = scope.get('repo_id') |
@@ -440,8 +440,8 b' class PermissionCalculator(object):' | |||
|
440 | 440 | self.default_user_id, self.scope_repo_id) |
|
441 | 441 | |
|
442 | 442 | def calculate(self): |
|
443 | if self.user_is_admin and not self.calculate_super_admin: | |
|
444 | return self._admin_permissions() | |
|
443 | if self.user_is_admin and not self.calculate_super_admin_as_user: | |
|
444 | return self._calculate_admin_permissions() | |
|
445 | 445 | |
|
446 | 446 | self._calculate_global_default_permissions() |
|
447 | 447 | self._calculate_global_permissions() |
@@ -452,7 +452,7 b' class PermissionCalculator(object):' | |||
|
452 | 452 | self._calculate_user_group_permissions() |
|
453 | 453 | return self._permission_structure() |
|
454 | 454 | |
|
455 | def _admin_permissions(self): | |
|
455 | def _calculate_admin_permissions(self): | |
|
456 | 456 | """ |
|
457 | 457 | admin user have all default rights for repositories |
|
458 | 458 | and groups set to admin |
@@ -479,13 +479,11 b' class PermissionCalculator(object):' | |||
|
479 | 479 | self.permissions_user_groups[u_k] = p, PermOrigin.SUPER_ADMIN |
|
480 | 480 | |
|
481 | 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 | |
|
482 | # since super-admin also can have custom rule permissions | |
|
483 | # we *always* need to calculate those inherited from default, and also explicit | |
|
484 | self._calculate_default_permissions_repository_branches( | |
|
485 | user_inherit_object_permissions=False) | |
|
486 | self._calculate_repository_branch_permissions() | |
|
489 | 487 | |
|
490 | 488 | return self._permission_structure() |
|
491 | 489 | |
@@ -571,27 +569,7 b' class PermissionCalculator(object):' | |||
|
571 | 569 | for perm in user_perms: |
|
572 | 570 | self.permissions_global.add(perm.permission.permission_name) |
|
573 | 571 | |
|
574 | def _calculate_default_permissions(self): | |
|
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 | |
|
572 | def _calculate_default_permissions_repositories(self, user_inherit_object_permissions): | |
|
595 | 573 | for perm in self.default_repo_perms: |
|
596 | 574 | r_k = perm.UserRepoToPerm.repository.repo_name |
|
597 | 575 | p = perm.Permission.permission_name |
@@ -624,7 +602,7 b' class PermissionCalculator(object):' | |||
|
624 | 602 | o = PermOrigin.SUPER_ADMIN |
|
625 | 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 | 606 | for perm in self.default_branch_repo_perms: |
|
629 | 607 | |
|
630 | 608 | r_k = perm.UserRepoToPerm.repository.repo_name |
@@ -641,7 +619,7 b' class PermissionCalculator(object):' | |||
|
641 | 619 | # special dict that aggregates entries |
|
642 | 620 | self.permissions_repository_branches[r_k] = pattern, p, o |
|
643 | 621 | |
|
644 |
|
|
|
622 | def _calculate_default_permissions_repository_groups(self, user_inherit_object_permissions): | |
|
645 | 623 | for perm in self.default_repo_groups_perms: |
|
646 | 624 | rg_k = perm.UserRepoGroupToPerm.group.group_name |
|
647 | 625 | p = perm.Permission.permission_name |
@@ -666,7 +644,7 b' class PermissionCalculator(object):' | |||
|
666 | 644 | o = PermOrigin.SUPER_ADMIN |
|
667 | 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 | 648 | for perm in self.default_user_group_perms: |
|
671 | 649 | u_k = perm.UserUserGroupToPerm.user_group.users_group_name |
|
672 | 650 | p = perm.Permission.permission_name |
@@ -691,6 +669,39 b' class PermissionCalculator(object):' | |||
|
691 | 669 | o = PermOrigin.SUPER_ADMIN |
|
692 | 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 | 705 | def _calculate_repository_permissions(self): |
|
695 | 706 | """ |
|
696 | 707 | Repository permissions for the current user. |
@@ -783,6 +794,7 b' class PermissionCalculator(object):' | |||
|
783 | 794 | # any specified by the group permission |
|
784 | 795 | user_repo_branch_perms = Permission.get_default_repo_branch_perms( |
|
785 | 796 | self.user_id, self.scope_repo_id) |
|
797 | ||
|
786 | 798 | for perm in user_repo_branch_perms: |
|
787 | 799 | |
|
788 | 800 | r_k = perm.UserRepoToPerm.repository.repo_name |
@@ -799,7 +811,6 b' class PermissionCalculator(object):' | |||
|
799 | 811 | # special dict that aggregates entries |
|
800 | 812 | self.permissions_repository_branches[r_k] = pattern, p, o |
|
801 | 813 | |
|
802 | ||
|
803 | 814 | def _calculate_repository_group_permissions(self): |
|
804 | 815 | """ |
|
805 | 816 | Repository group permissions for the current user. |
@@ -1036,14 +1047,14 b' class AuthUser(object):' | |||
|
1036 | 1047 | |
|
1037 | 1048 | @LazyProperty |
|
1038 | 1049 | def permissions(self): |
|
1039 |
return self.get_perms(user=self, cache= |
|
|
1050 | return self.get_perms(user=self, cache=None) | |
|
1040 | 1051 | |
|
1041 | 1052 | @LazyProperty |
|
1042 | 1053 | def permissions_safe(self): |
|
1043 | 1054 | """ |
|
1044 | 1055 | Filtered permissions excluding not allowed repositories |
|
1045 | 1056 | """ |
|
1046 |
perms = self.get_perms(user=self, cache= |
|
|
1057 | perms = self.get_perms(user=self, cache=None) | |
|
1047 | 1058 | |
|
1048 | 1059 | perms['repositories'] = { |
|
1049 | 1060 | k: v for k, v in perms['repositories'].items() |
@@ -1062,7 +1073,7 b' class AuthUser(object):' | |||
|
1062 | 1073 | @LazyProperty |
|
1063 | 1074 | def permissions_full_details(self): |
|
1064 | 1075 | return self.get_perms( |
|
1065 |
user=self, cache= |
|
|
1076 | user=self, cache=None, calculate_super_admin=True) | |
|
1066 | 1077 | |
|
1067 | 1078 | def permissions_with_scope(self, scope): |
|
1068 | 1079 | """ |
@@ -1088,7 +1099,7 b' class AuthUser(object):' | |||
|
1088 | 1099 | # store in cache to mimic how the @LazyProperty works, |
|
1089 | 1100 | # the difference here is that we use the unique key calculated |
|
1090 | 1101 | # from params and values |
|
1091 |
return self.get_perms(user=self, cache= |
|
|
1102 | return self.get_perms(user=self, cache=None, scope=_scope) | |
|
1092 | 1103 | |
|
1093 | 1104 | def get_instance(self): |
|
1094 | 1105 | return User.get(self.user_id) |
@@ -1143,7 +1154,7 b' class AuthUser(object):' | |||
|
1143 | 1154 | log.debug('AuthUser: propagated user is now %s', self) |
|
1144 | 1155 | |
|
1145 | 1156 | def get_perms(self, user, scope=None, explicit=True, algo='higherwin', |
|
1146 |
calculate_super_admin=False, cache= |
|
|
1157 | calculate_super_admin=False, cache=None): | |
|
1147 | 1158 | """ |
|
1148 | 1159 | Fills user permission attribute with permissions taken from database |
|
1149 | 1160 | works for permissions given for repositories, and for permissions that |
@@ -1158,6 +1169,9 b' class AuthUser(object):' | |||
|
1158 | 1169 | it's multiple defined, eg user in two different groups. It also |
|
1159 | 1170 | decides if explicit flag is turned off how to specify the permission |
|
1160 | 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 | 1176 | user_id = user.user_id |
|
1163 | 1177 | user_is_admin = user.is_admin |
@@ -1168,7 +1182,12 b' class AuthUser(object):' | |||
|
1168 | 1182 | cache_seconds = safe_int( |
|
1169 | 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 | 1191 | log.debug( |
|
1173 | 1192 | 'Computing PERMISSION tree for user %s scope `%s` ' |
|
1174 | 1193 | 'with caching: %s[TTL: %ss]' % (user, scope, cache_on, cache_seconds or 0)) |
@@ -1186,9 +1205,10 b' class AuthUser(object):' | |||
|
1186 | 1205 | explicit, algo, calculate_super_admin) |
|
1187 | 1206 | |
|
1188 | 1207 | start = time.time() |
|
1189 |
result = compute_perm_tree( |
|
|
1190 | user_inherit_default_permissions, explicit, algo, | |
|
1191 | calculate_super_admin) | |
|
1208 | result = compute_perm_tree( | |
|
1209 | 'permissions', user_id, scope, user_is_admin, | |
|
1210 | user_inherit_default_permissions, explicit, algo, | |
|
1211 | calculate_super_admin) | |
|
1192 | 1212 | |
|
1193 | 1213 | result_repr = [] |
|
1194 | 1214 | for k in result: |
@@ -1340,6 +1360,35 b' class AuthUser(object):' | |||
|
1340 | 1360 | 'not in %s' % (ip_addr, user_id, allowed_ips)) |
|
1341 | 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 | 1392 | def __repr__(self): |
|
1344 | 1393 | return "<AuthUser('id:%s[%s] ip:%s auth:%s')>"\ |
|
1345 | 1394 | % (self.user_id, self.username, self.ip_addr, self.is_authenticated) |
@@ -2084,23 +2133,22 b' class HasPermissionAnyMiddleware(object)' | |||
|
2084 | 2133 | def __init__(self, *perms): |
|
2085 | 2134 | self.required_perms = set(perms) |
|
2086 | 2135 | |
|
2087 | def __call__(self, user, repo_name): | |
|
2136 | def __call__(self, auth_user, repo_name): | |
|
2088 | 2137 | # repo_name MUST be unicode, since we handle keys in permission |
|
2089 | 2138 | # dict by unicode |
|
2090 | 2139 | repo_name = safe_unicode(repo_name) |
|
2091 | user = AuthUser(user.user_id) | |
|
2092 | 2140 | log.debug( |
|
2093 | 2141 | 'Checking VCS protocol permissions %s for user:%s repo:`%s`', |
|
2094 | self.required_perms, user, repo_name) | |
|
2095 | ||
|
2096 | if self.check_permissions(user, repo_name): | |
|
2142 | self.required_perms, auth_user, repo_name) | |
|
2143 | ||
|
2144 | if self.check_permissions(auth_user, repo_name): | |
|
2097 | 2145 | log.debug('Permission to repo:`%s` GRANTED for user:%s @ %s', |
|
2098 | repo_name, user, 'PermissionMiddleware') | |
|
2146 | repo_name, auth_user, 'PermissionMiddleware') | |
|
2099 | 2147 | return True |
|
2100 | 2148 | |
|
2101 | 2149 | else: |
|
2102 | 2150 | log.debug('Permission to repo:`%s` DENIED for user:%s @ %s', |
|
2103 | repo_name, user, 'PermissionMiddleware') | |
|
2151 | repo_name, auth_user, 'PermissionMiddleware') | |
|
2104 | 2152 | return False |
|
2105 | 2153 | |
|
2106 | 2154 | def check_permissions(self, user, repo_name): |
@@ -153,7 +153,7 b' def get_user_agent(environ):' | |||
|
153 | 153 | |
|
154 | 154 | def vcs_operation_context( |
|
155 | 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 | 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 | 193 | 'user_agent': get_user_agent(environ), |
|
194 | 194 | 'hooks': get_enabled_hook_classes(ui_settings), |
|
195 | 195 | 'is_shadow_repo': is_shadow_repo, |
|
196 | 'detect_force_push': detect_force_push, | |
|
197 | 'check_branch_perms': check_branch_perms, | |
|
196 | 198 | } |
|
197 | 199 | return extras |
|
198 | 200 |
@@ -107,6 +107,21 b' class HTTPLockedRC(HTTPClientError):' | |||
|
107 | 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 | 125 | class IMCCommitError(Exception): |
|
111 | 126 | pass |
|
112 | 127 |
@@ -32,7 +32,8 b' from rhodecode import events' | |||
|
32 | 32 | from rhodecode.lib import helpers as h |
|
33 | 33 | from rhodecode.lib import audit_logger |
|
34 | 34 | from rhodecode.lib.utils2 import safe_str |
|
35 |
from rhodecode.lib.exceptions import |
|
|
35 | from rhodecode.lib.exceptions import ( | |
|
36 | HTTPLockedRC, HTTPBranchProtected, UserCreationError) | |
|
36 | 37 | from rhodecode.model.db import Repository, User |
|
37 | 38 | |
|
38 | 39 | log = logging.getLogger(__name__) |
@@ -94,9 +95,9 b' def pre_push(extras):' | |||
|
94 | 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 | 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 | 101 | locked_by = User.get(extras.locked_by[0]).username |
|
101 | 102 | reason = extras.locked_by[2] |
|
102 | 103 | # this exception is interpreted in git/hg middlewares and based |
@@ -109,9 +110,48 b' def pre_push(extras):' | |||
|
109 | 110 | else: |
|
110 | 111 | raise _http_ret |
|
111 | 112 | |
|
112 | # Propagate to external components. This is done after checking the | |
|
113 | # lock, for consistent behavior. | |
|
114 | 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 | ||
|
153 | # Propagate to external components. This is done after checking the | |
|
154 | # lock, for consistent behavior. | |
|
115 | 155 | pre_push_extension(repo_store_path=Repository.base_path(), **extras) |
|
116 | 156 | events.trigger(events.RepoPrePushEvent( |
|
117 | 157 | repo_name=extras.repository, extras=extras)) |
@@ -29,6 +29,7 b' from BaseHTTPServer import BaseHTTPReque' | |||
|
29 | 29 | from SocketServer import TCPServer |
|
30 | 30 | |
|
31 | 31 | import rhodecode |
|
32 | from rhodecode.lib.exceptions import HTTPLockedRC, HTTPBranchProtected | |
|
32 | 33 | from rhodecode.model import meta |
|
33 | 34 | from rhodecode.lib.base import bootstrap_request, bootstrap_config |
|
34 | 35 | from rhodecode.lib import hooks_base |
@@ -285,9 +286,20 b' class Hooks(object):' | |||
|
285 | 286 | |
|
286 | 287 | try: |
|
287 | 288 | result = hook(extras) |
|
288 |
except |
|
|
289 | exc_tb = traceback.format_exc() | |
|
290 | log.exception('Exception when handling hook %s', hook) | |
|
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): | |
|
301 | exc_tb = traceback.format_exc() | |
|
302 | log.exception('Exception when handling hook %s', hook) | |
|
291 | 303 | error_args = error.args |
|
292 | 304 | return { |
|
293 | 305 | 'status': 128, |
@@ -297,7 +297,7 b' class SimpleVCS(object):' | |||
|
297 | 297 | def is_shadow_repo_dir(self): |
|
298 | 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 | 301 | plugin_id='', plugin_cache_active=False, cache_ttl=0): |
|
302 | 302 | """ |
|
303 | 303 | Checks permissions using action (push/pull) user and repository |
@@ -335,14 +335,14 b' class SimpleVCS(object):' | |||
|
335 | 335 | |
|
336 | 336 | if action == 'push': |
|
337 | 337 | perms = ('repository.write', 'repository.admin') |
|
338 | if not HasPermissionAnyMiddleware(*perms)(user, repo_name): | |
|
338 | if not HasPermissionAnyMiddleware(*perms)(auth_user, repo_name): | |
|
339 | 339 | return False |
|
340 | 340 | |
|
341 | 341 | else: |
|
342 | 342 | # any other action need at least read permission |
|
343 | 343 | perms = ( |
|
344 | 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 | 346 | return False |
|
347 | 347 | |
|
348 | 348 | return True |
@@ -441,14 +441,17 b' class SimpleVCS(object):' | |||
|
441 | 441 | # ====================================================================== |
|
442 | 442 | # CHECK ANONYMOUS PERMISSION |
|
443 | 443 | # ====================================================================== |
|
444 | detect_force_push = False | |
|
445 | check_branch_perms = False | |
|
444 | 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 | 449 | username = anonymous_user.username |
|
447 | 450 | if anonymous_user.active: |
|
448 | 451 | plugin_cache_active, cache_ttl = self._get_default_cache_ttl() |
|
449 | 452 | # ONLY check permissions if the user is activated |
|
450 | 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 | 455 | plugin_id='anonymous_access', |
|
453 | 456 | plugin_cache_active=plugin_cache_active, |
|
454 | 457 | cache_ttl=cache_ttl, |
@@ -525,6 +528,7 b' class SimpleVCS(object):' | |||
|
525 | 528 | |
|
526 | 529 | # check user attributes for password change flag |
|
527 | 530 | user_obj = user |
|
531 | auth_user = user_obj.AuthUser() | |
|
528 | 532 | if user_obj and user_obj.username != User.DEFAULT_USER and \ |
|
529 | 533 | user_obj.user_data.get('force_password_change'): |
|
530 | 534 | reason = 'password change required' |
@@ -533,19 +537,27 b' class SimpleVCS(object):' | |||
|
533 | 537 | |
|
534 | 538 | # check permissions for this repository |
|
535 | 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 | 541 | plugin, plugin_cache_active, cache_ttl) |
|
538 | 542 | if not perm: |
|
539 | 543 | return HTTPForbidden()(environ, start_response) |
|
540 | 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 | 552 | # extras are injected into UI object and later available |
|
543 | 553 | # in hooks executed by RhodeCode |
|
544 | 554 | check_locking = _should_check_locking(environ.get('QUERY_STRING')) |
|
555 | ||
|
545 | 556 | extras = vcs_operation_context( |
|
546 | 557 | environ, repo_name=self.acl_repo_name, username=username, |
|
547 | 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 | 420 | calculator = auth.PermissionCalculator( |
|
421 | 421 | user.user_id, {}, False, False, True, 'higherwin') |
|
422 | permissions = calculator._admin_permissions() | |
|
422 | permissions = calculator._calculate_admin_permissions() | |
|
423 | 423 | |
|
424 | 424 | assert permissions['repositories_groups'][repo_group.group_name] == \ |
|
425 | 425 | 'group.admin' |
@@ -77,22 +77,18 b' class Command(object):' | |||
|
77 | 77 | assert self.process.returncode == 0 |
|
78 | 78 | |
|
79 | 79 | |
|
80 |
def _add_files |
|
|
81 | """ | |
|
82 | Generate some files, add it to DEST repo and push back | |
|
83 | vcs is git or hg and defines what VCS we want to make those files for | |
|
84 | """ | |
|
85 | # commit some stuff into this repo | |
|
80 | def _add_files(vcs, dest, clone_url=None, tags=None, target_branch=None, | |
|
81 | new_branch=False, **kwargs): | |
|
82 | git_ident = "git config user.name {} && git config user.email {}".format( | |
|
83 | 'Marcin KuΕΊminski', 'me@email.com') | |
|
84 | cwd = path = jn(dest) | |
|
85 | ||
|
86 | 86 | tags = tags or [] |
|
87 | cwd = path = jn(dest) | |
|
88 | 87 | added_file = jn(path, '%ssetup.py' % tempfile._RandomNameSequence().next()) |
|
89 | 88 | Command(cwd).execute('touch %s' % added_file) |
|
90 | 89 | Command(cwd).execute('%s add %s' % (vcs, added_file)) |
|
91 | 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 | 92 | for i in range(kwargs.get('files_no', 3)): |
|
97 | 93 | cmd = """echo 'added_line%s' >> %s""" % (i, added_file) |
|
98 | 94 | Command(cwd).execute(cmd) |
@@ -107,30 +103,55 b' def _add_files_and_push(vcs, dest, clone' | |||
|
107 | 103 | |
|
108 | 104 | for tag in tags: |
|
109 | 105 | if vcs == 'hg': |
|
110 |
|
|
|
106 | Command(cwd).execute( | |
|
111 | 107 | 'hg tag', tag['name']) |
|
112 | 108 | elif vcs == 'git': |
|
113 | 109 | if tag['commit']: |
|
114 | 110 | # annotated tag |
|
115 | stdout, stderr = Command(cwd).execute( | |
|
111 | _stdout, _stderr = Command(cwd).execute( | |
|
116 | 112 | """%s && git tag -a %s -m "%s" """ % ( |
|
117 | 113 | git_ident, tag['name'], tag['commit'])) |
|
118 | 114 | else: |
|
119 | 115 | # lightweight tag |
|
120 | stdout, stderr = Command(cwd).execute( | |
|
116 | _stdout, _stderr = Command(cwd).execute( | |
|
121 | 117 | """%s && git tag %s""" % ( |
|
122 | 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 | 141 | # PUSH it back |
|
125 | 142 | stdout = stderr = None |
|
126 | 143 | if vcs == 'hg': |
|
144 | maybe_new_branch = '' | |
|
145 | if new_branch: | |
|
146 | maybe_new_branch = '--new-branch' | |
|
127 | 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 | 150 | elif vcs == 'git': |
|
130 | 151 | stdout, stderr = Command(cwd).execute( |
|
131 |
""" |
|
|
132 |
git push --verbose --tags |
|
|
133 | git_ident, clone_url)) | |
|
152 | """{} && | |
|
153 | git push --verbose --tags {} {}""".format(git_ident, clone_url, target_branch) | |
|
154 | ) | |
|
134 | 155 | |
|
135 | 156 | return stdout, stderr |
|
136 | 157 |
@@ -33,7 +33,8 b' import textwrap' | |||
|
33 | 33 | import pytest |
|
34 | 34 | |
|
35 | 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 | 38 | from rhodecode.model.integration import IntegrationModel |
|
38 | 39 | from rhodecode.model.db import Repository |
|
39 | 40 | from rhodecode.model.meta import Session |
@@ -267,3 +268,74 b' def enable_webhook_push_integration(requ' | |||
|
267 | 268 | Session().delete(integration) |
|
268 | 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