branch-permissions: handle vcs operations and branch permissions....
marcink -
r2979:095dcb4b default
Not Reviewed
Show More
Add another comment
TODOs: 0 unresolved 0 Resolved
COMMENTS: 0 General 0 Inline
@@ -0,0 +1,94
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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,9 +1205,10
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(
1190 user_inherit_default_permissions, explicit, algo,
1209 'permissions', user_id, scope, user_is_admin,
1191 calculate_super_admin)
1210 user_inherit_default_permissions, explicit, algo,
1211 calculate_super_admin)
1192
1212
1193 result_repr = []
1213 result_repr = []
1194 for k in result:
1214 for k in result:
@@ -1340,6 +1360,35
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
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
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):