##// END OF EJS Templates
app: use simpler way to extract default_user_id, this will be now registered at server...
marcink -
r4332:b6d13602 default
parent child Browse files
Show More

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

@@ -1,299 +1,299 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import mock
21 import mock
22 import pytest
22 import pytest
23 from rhodecode.model.db import User, UserIpMap
23 from rhodecode.model.db import User, UserIpMap
24 from rhodecode.model.meta import Session
24 from rhodecode.model.meta import Session
25 from rhodecode.model.permission import PermissionModel
25 from rhodecode.model.permission import PermissionModel
26 from rhodecode.model.ssh_key import SshKeyModel
26 from rhodecode.model.ssh_key import SshKeyModel
27 from rhodecode.tests import (
27 from rhodecode.tests import (
28 TestController, clear_cache_regions, assert_session_flash)
28 TestController, clear_cache_regions, assert_session_flash)
29
29
30
30
31 def route_path(name, params=None, **kwargs):
31 def route_path(name, params=None, **kwargs):
32 import urllib
32 import urllib
33 from rhodecode.apps._base import ADMIN_PREFIX
33 from rhodecode.apps._base import ADMIN_PREFIX
34
34
35 base_url = {
35 base_url = {
36 'edit_user_ips':
36 'edit_user_ips':
37 ADMIN_PREFIX + '/users/{user_id}/edit/ips',
37 ADMIN_PREFIX + '/users/{user_id}/edit/ips',
38 'edit_user_ips_add':
38 'edit_user_ips_add':
39 ADMIN_PREFIX + '/users/{user_id}/edit/ips/new',
39 ADMIN_PREFIX + '/users/{user_id}/edit/ips/new',
40 'edit_user_ips_delete':
40 'edit_user_ips_delete':
41 ADMIN_PREFIX + '/users/{user_id}/edit/ips/delete',
41 ADMIN_PREFIX + '/users/{user_id}/edit/ips/delete',
42
42
43 'admin_permissions_application':
43 'admin_permissions_application':
44 ADMIN_PREFIX + '/permissions/application',
44 ADMIN_PREFIX + '/permissions/application',
45 'admin_permissions_application_update':
45 'admin_permissions_application_update':
46 ADMIN_PREFIX + '/permissions/application/update',
46 ADMIN_PREFIX + '/permissions/application/update',
47
47
48 'admin_permissions_global':
48 'admin_permissions_global':
49 ADMIN_PREFIX + '/permissions/global',
49 ADMIN_PREFIX + '/permissions/global',
50 'admin_permissions_global_update':
50 'admin_permissions_global_update':
51 ADMIN_PREFIX + '/permissions/global/update',
51 ADMIN_PREFIX + '/permissions/global/update',
52
52
53 'admin_permissions_object':
53 'admin_permissions_object':
54 ADMIN_PREFIX + '/permissions/object',
54 ADMIN_PREFIX + '/permissions/object',
55 'admin_permissions_object_update':
55 'admin_permissions_object_update':
56 ADMIN_PREFIX + '/permissions/object/update',
56 ADMIN_PREFIX + '/permissions/object/update',
57
57
58 'admin_permissions_ips':
58 'admin_permissions_ips':
59 ADMIN_PREFIX + '/permissions/ips',
59 ADMIN_PREFIX + '/permissions/ips',
60 'admin_permissions_overview':
60 'admin_permissions_overview':
61 ADMIN_PREFIX + '/permissions/overview',
61 ADMIN_PREFIX + '/permissions/overview',
62
62
63 'admin_permissions_ssh_keys':
63 'admin_permissions_ssh_keys':
64 ADMIN_PREFIX + '/permissions/ssh_keys',
64 ADMIN_PREFIX + '/permissions/ssh_keys',
65 'admin_permissions_ssh_keys_data':
65 'admin_permissions_ssh_keys_data':
66 ADMIN_PREFIX + '/permissions/ssh_keys/data',
66 ADMIN_PREFIX + '/permissions/ssh_keys/data',
67 'admin_permissions_ssh_keys_update':
67 'admin_permissions_ssh_keys_update':
68 ADMIN_PREFIX + '/permissions/ssh_keys/update'
68 ADMIN_PREFIX + '/permissions/ssh_keys/update'
69
69
70 }[name].format(**kwargs)
70 }[name].format(**kwargs)
71
71
72 if params:
72 if params:
73 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
73 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
74 return base_url
74 return base_url
75
75
76
76
77 class TestAdminPermissionsController(TestController):
77 class TestAdminPermissionsController(TestController):
78
78
79 @pytest.fixture(scope='class', autouse=True)
79 @pytest.fixture(scope='class', autouse=True)
80 def prepare(self, request):
80 def prepare(self, request):
81 # cleanup and reset to default permissions after
81 # cleanup and reset to default permissions after
82 @request.addfinalizer
82 @request.addfinalizer
83 def cleanup():
83 def cleanup():
84 PermissionModel().create_default_user_permissions(
84 PermissionModel().create_default_user_permissions(
85 User.get_default_user(), force=True)
85 User.get_default_user(), force=True)
86
86
87 def test_index_application(self):
87 def test_index_application(self):
88 self.log_user()
88 self.log_user()
89 self.app.get(route_path('admin_permissions_application'))
89 self.app.get(route_path('admin_permissions_application'))
90
90
91 @pytest.mark.parametrize(
91 @pytest.mark.parametrize(
92 'anonymous, default_register, default_register_message, default_password_reset,'
92 'anonymous, default_register, default_register_message, default_password_reset,'
93 'default_extern_activate, expect_error, expect_form_error', [
93 'default_extern_activate, expect_error, expect_form_error', [
94 (True, 'hg.register.none', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
94 (True, 'hg.register.none', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
95 False, False),
95 False, False),
96 (True, 'hg.register.manual_activate', '', 'hg.password_reset.enabled', 'hg.extern_activate.auto',
96 (True, 'hg.register.manual_activate', '', 'hg.password_reset.enabled', 'hg.extern_activate.auto',
97 False, False),
97 False, False),
98 (True, 'hg.register.auto_activate', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
98 (True, 'hg.register.auto_activate', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
99 False, False),
99 False, False),
100 (True, 'hg.register.auto_activate', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
100 (True, 'hg.register.auto_activate', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
101 False, False),
101 False, False),
102 (True, 'hg.register.XXX', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
102 (True, 'hg.register.XXX', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
103 False, True),
103 False, True),
104 (True, '', '', 'hg.password_reset.enabled', '', True, False),
104 (True, '', '', 'hg.password_reset.enabled', '', True, False),
105 ])
105 ])
106 def test_update_application_permissions(
106 def test_update_application_permissions(
107 self, anonymous, default_register, default_register_message, default_password_reset,
107 self, anonymous, default_register, default_register_message, default_password_reset,
108 default_extern_activate, expect_error, expect_form_error):
108 default_extern_activate, expect_error, expect_form_error):
109
109
110 self.log_user()
110 self.log_user()
111
111
112 # TODO: anonymous access set here to False, breaks some other tests
112 # TODO: anonymous access set here to False, breaks some other tests
113 params = {
113 params = {
114 'csrf_token': self.csrf_token,
114 'csrf_token': self.csrf_token,
115 'anonymous': anonymous,
115 'anonymous': anonymous,
116 'default_register': default_register,
116 'default_register': default_register,
117 'default_register_message': default_register_message,
117 'default_register_message': default_register_message,
118 'default_password_reset': default_password_reset,
118 'default_password_reset': default_password_reset,
119 'default_extern_activate': default_extern_activate,
119 'default_extern_activate': default_extern_activate,
120 }
120 }
121 response = self.app.post(route_path('admin_permissions_application_update'),
121 response = self.app.post(route_path('admin_permissions_application_update'),
122 params=params)
122 params=params)
123 if expect_form_error:
123 if expect_form_error:
124 assert response.status_int == 200
124 assert response.status_int == 200
125 response.mustcontain('Value must be one of')
125 response.mustcontain('Value must be one of')
126 else:
126 else:
127 if expect_error:
127 if expect_error:
128 msg = 'Error occurred during update of permissions'
128 msg = 'Error occurred during update of permissions'
129 else:
129 else:
130 msg = 'Application permissions updated successfully'
130 msg = 'Application permissions updated successfully'
131 assert_session_flash(response, msg)
131 assert_session_flash(response, msg)
132
132
133 def test_index_object(self):
133 def test_index_object(self):
134 self.log_user()
134 self.log_user()
135 self.app.get(route_path('admin_permissions_object'))
135 self.app.get(route_path('admin_permissions_object'))
136
136
137 @pytest.mark.parametrize(
137 @pytest.mark.parametrize(
138 'repo, repo_group, user_group, expect_error, expect_form_error', [
138 'repo, repo_group, user_group, expect_error, expect_form_error', [
139 ('repository.none', 'group.none', 'usergroup.none', False, False),
139 ('repository.none', 'group.none', 'usergroup.none', False, False),
140 ('repository.read', 'group.read', 'usergroup.read', False, False),
140 ('repository.read', 'group.read', 'usergroup.read', False, False),
141 ('repository.write', 'group.write', 'usergroup.write',
141 ('repository.write', 'group.write', 'usergroup.write',
142 False, False),
142 False, False),
143 ('repository.admin', 'group.admin', 'usergroup.admin',
143 ('repository.admin', 'group.admin', 'usergroup.admin',
144 False, False),
144 False, False),
145 ('repository.XXX', 'group.admin', 'usergroup.admin', False, True),
145 ('repository.XXX', 'group.admin', 'usergroup.admin', False, True),
146 ('', '', '', True, False),
146 ('', '', '', True, False),
147 ])
147 ])
148 def test_update_object_permissions(self, repo, repo_group, user_group,
148 def test_update_object_permissions(self, repo, repo_group, user_group,
149 expect_error, expect_form_error):
149 expect_error, expect_form_error):
150 self.log_user()
150 self.log_user()
151
151
152 params = {
152 params = {
153 'csrf_token': self.csrf_token,
153 'csrf_token': self.csrf_token,
154 'default_repo_perm': repo,
154 'default_repo_perm': repo,
155 'overwrite_default_repo': False,
155 'overwrite_default_repo': False,
156 'default_group_perm': repo_group,
156 'default_group_perm': repo_group,
157 'overwrite_default_group': False,
157 'overwrite_default_group': False,
158 'default_user_group_perm': user_group,
158 'default_user_group_perm': user_group,
159 'overwrite_default_user_group': False,
159 'overwrite_default_user_group': False,
160 }
160 }
161 response = self.app.post(route_path('admin_permissions_object_update'),
161 response = self.app.post(route_path('admin_permissions_object_update'),
162 params=params)
162 params=params)
163 if expect_form_error:
163 if expect_form_error:
164 assert response.status_int == 200
164 assert response.status_int == 200
165 response.mustcontain('Value must be one of')
165 response.mustcontain('Value must be one of')
166 else:
166 else:
167 if expect_error:
167 if expect_error:
168 msg = 'Error occurred during update of permissions'
168 msg = 'Error occurred during update of permissions'
169 else:
169 else:
170 msg = 'Object permissions updated successfully'
170 msg = 'Object permissions updated successfully'
171 assert_session_flash(response, msg)
171 assert_session_flash(response, msg)
172
172
173 def test_index_global(self):
173 def test_index_global(self):
174 self.log_user()
174 self.log_user()
175 self.app.get(route_path('admin_permissions_global'))
175 self.app.get(route_path('admin_permissions_global'))
176
176
177 @pytest.mark.parametrize(
177 @pytest.mark.parametrize(
178 'repo_create, repo_create_write, user_group_create, repo_group_create,'
178 'repo_create, repo_create_write, user_group_create, repo_group_create,'
179 'fork_create, inherit_default_permissions, expect_error,'
179 'fork_create, inherit_default_permissions, expect_error,'
180 'expect_form_error', [
180 'expect_form_error', [
181 ('hg.create.none', 'hg.create.write_on_repogroup.false',
181 ('hg.create.none', 'hg.create.write_on_repogroup.false',
182 'hg.usergroup.create.false', 'hg.repogroup.create.false',
182 'hg.usergroup.create.false', 'hg.repogroup.create.false',
183 'hg.fork.none', 'hg.inherit_default_perms.false', False, False),
183 'hg.fork.none', 'hg.inherit_default_perms.false', False, False),
184 ('hg.create.repository', 'hg.create.write_on_repogroup.true',
184 ('hg.create.repository', 'hg.create.write_on_repogroup.true',
185 'hg.usergroup.create.true', 'hg.repogroup.create.true',
185 'hg.usergroup.create.true', 'hg.repogroup.create.true',
186 'hg.fork.repository', 'hg.inherit_default_perms.false',
186 'hg.fork.repository', 'hg.inherit_default_perms.false',
187 False, False),
187 False, False),
188 ('hg.create.XXX', 'hg.create.write_on_repogroup.true',
188 ('hg.create.XXX', 'hg.create.write_on_repogroup.true',
189 'hg.usergroup.create.true', 'hg.repogroup.create.true',
189 'hg.usergroup.create.true', 'hg.repogroup.create.true',
190 'hg.fork.repository', 'hg.inherit_default_perms.false',
190 'hg.fork.repository', 'hg.inherit_default_perms.false',
191 False, True),
191 False, True),
192 ('', '', '', '', '', '', True, False),
192 ('', '', '', '', '', '', True, False),
193 ])
193 ])
194 def test_update_global_permissions(
194 def test_update_global_permissions(
195 self, repo_create, repo_create_write, user_group_create,
195 self, repo_create, repo_create_write, user_group_create,
196 repo_group_create, fork_create, inherit_default_permissions,
196 repo_group_create, fork_create, inherit_default_permissions,
197 expect_error, expect_form_error):
197 expect_error, expect_form_error):
198 self.log_user()
198 self.log_user()
199
199
200 params = {
200 params = {
201 'csrf_token': self.csrf_token,
201 'csrf_token': self.csrf_token,
202 'default_repo_create': repo_create,
202 'default_repo_create': repo_create,
203 'default_repo_create_on_write': repo_create_write,
203 'default_repo_create_on_write': repo_create_write,
204 'default_user_group_create': user_group_create,
204 'default_user_group_create': user_group_create,
205 'default_repo_group_create': repo_group_create,
205 'default_repo_group_create': repo_group_create,
206 'default_fork_create': fork_create,
206 'default_fork_create': fork_create,
207 'default_inherit_default_permissions': inherit_default_permissions
207 'default_inherit_default_permissions': inherit_default_permissions
208 }
208 }
209 response = self.app.post(route_path('admin_permissions_global_update'),
209 response = self.app.post(route_path('admin_permissions_global_update'),
210 params=params)
210 params=params)
211 if expect_form_error:
211 if expect_form_error:
212 assert response.status_int == 200
212 assert response.status_int == 200
213 response.mustcontain('Value must be one of')
213 response.mustcontain('Value must be one of')
214 else:
214 else:
215 if expect_error:
215 if expect_error:
216 msg = 'Error occurred during update of permissions'
216 msg = 'Error occurred during update of permissions'
217 else:
217 else:
218 msg = 'Global permissions updated successfully'
218 msg = 'Global permissions updated successfully'
219 assert_session_flash(response, msg)
219 assert_session_flash(response, msg)
220
220
221 def test_index_ips(self):
221 def test_index_ips(self):
222 self.log_user()
222 self.log_user()
223 response = self.app.get(route_path('admin_permissions_ips'))
223 response = self.app.get(route_path('admin_permissions_ips'))
224 response.mustcontain('All IP addresses are allowed')
224 response.mustcontain('All IP addresses are allowed')
225
225
226 def test_add_delete_ips(self):
226 def test_add_delete_ips(self):
227 clear_cache_regions(['sql_cache_short'])
227 clear_cache_regions(['sql_cache_short'])
228 self.log_user()
228 self.log_user()
229
229
230 # ADD
230 # ADD
231 default_user_id = User.get_default_user().user_id
231 default_user_id = User.get_default_user_id()
232 self.app.post(
232 self.app.post(
233 route_path('edit_user_ips_add', user_id=default_user_id),
233 route_path('edit_user_ips_add', user_id=default_user_id),
234 params={'new_ip': '0.0.0.0/24', 'csrf_token': self.csrf_token})
234 params={'new_ip': '0.0.0.0/24', 'csrf_token': self.csrf_token})
235
235
236 response = self.app.get(route_path('admin_permissions_ips'))
236 response = self.app.get(route_path('admin_permissions_ips'))
237 response.mustcontain('0.0.0.0/24')
237 response.mustcontain('0.0.0.0/24')
238 response.mustcontain('0.0.0.0 - 0.0.0.255')
238 response.mustcontain('0.0.0.0 - 0.0.0.255')
239
239
240 # DELETE
240 # DELETE
241 default_user_id = User.get_default_user().user_id
241 default_user_id = User.get_default_user_id()
242 del_ip_id = UserIpMap.query().filter(UserIpMap.user_id ==
242 del_ip_id = UserIpMap.query().filter(UserIpMap.user_id ==
243 default_user_id).first().ip_id
243 default_user_id).first().ip_id
244
244
245 response = self.app.post(
245 response = self.app.post(
246 route_path('edit_user_ips_delete', user_id=default_user_id),
246 route_path('edit_user_ips_delete', user_id=default_user_id),
247 params={'del_ip_id': del_ip_id, 'csrf_token': self.csrf_token})
247 params={'del_ip_id': del_ip_id, 'csrf_token': self.csrf_token})
248
248
249 assert_session_flash(response, 'Removed ip address from user whitelist')
249 assert_session_flash(response, 'Removed ip address from user whitelist')
250
250
251 clear_cache_regions(['sql_cache_short'])
251 clear_cache_regions(['sql_cache_short'])
252 response = self.app.get(route_path('admin_permissions_ips'))
252 response = self.app.get(route_path('admin_permissions_ips'))
253 response.mustcontain('All IP addresses are allowed')
253 response.mustcontain('All IP addresses are allowed')
254 response.mustcontain(no=['0.0.0.0/24'])
254 response.mustcontain(no=['0.0.0.0/24'])
255 response.mustcontain(no=['0.0.0.0 - 0.0.0.255'])
255 response.mustcontain(no=['0.0.0.0 - 0.0.0.255'])
256
256
257 def test_index_overview(self):
257 def test_index_overview(self):
258 self.log_user()
258 self.log_user()
259 self.app.get(route_path('admin_permissions_overview'))
259 self.app.get(route_path('admin_permissions_overview'))
260
260
261 def test_ssh_keys(self):
261 def test_ssh_keys(self):
262 self.log_user()
262 self.log_user()
263 self.app.get(route_path('admin_permissions_ssh_keys'), status=200)
263 self.app.get(route_path('admin_permissions_ssh_keys'), status=200)
264
264
265 def test_ssh_keys_data(self, user_util, xhr_header):
265 def test_ssh_keys_data(self, user_util, xhr_header):
266 self.log_user()
266 self.log_user()
267 response = self.app.get(route_path('admin_permissions_ssh_keys_data'),
267 response = self.app.get(route_path('admin_permissions_ssh_keys_data'),
268 extra_environ=xhr_header)
268 extra_environ=xhr_header)
269 assert response.json == {u'data': [], u'draw': None,
269 assert response.json == {u'data': [], u'draw': None,
270 u'recordsFiltered': 0, u'recordsTotal': 0}
270 u'recordsFiltered': 0, u'recordsTotal': 0}
271
271
272 dummy_user = user_util.create_user()
272 dummy_user = user_util.create_user()
273 SshKeyModel().create(dummy_user, 'ab:cd:ef', 'KEYKEY', 'test_key')
273 SshKeyModel().create(dummy_user, 'ab:cd:ef', 'KEYKEY', 'test_key')
274 Session().commit()
274 Session().commit()
275 response = self.app.get(route_path('admin_permissions_ssh_keys_data'),
275 response = self.app.get(route_path('admin_permissions_ssh_keys_data'),
276 extra_environ=xhr_header)
276 extra_environ=xhr_header)
277 assert response.json['data'][0]['fingerprint'] == 'ab:cd:ef'
277 assert response.json['data'][0]['fingerprint'] == 'ab:cd:ef'
278
278
279 def test_ssh_keys_update(self):
279 def test_ssh_keys_update(self):
280 self.log_user()
280 self.log_user()
281 response = self.app.post(
281 response = self.app.post(
282 route_path('admin_permissions_ssh_keys_update'),
282 route_path('admin_permissions_ssh_keys_update'),
283 dict(csrf_token=self.csrf_token), status=302)
283 dict(csrf_token=self.csrf_token), status=302)
284
284
285 assert_session_flash(
285 assert_session_flash(
286 response, 'Updated SSH keys file')
286 response, 'Updated SSH keys file')
287
287
288 def test_ssh_keys_update_disabled(self):
288 def test_ssh_keys_update_disabled(self):
289 self.log_user()
289 self.log_user()
290
290
291 from rhodecode.apps.admin.views.permissions import AdminPermissionsView
291 from rhodecode.apps.admin.views.permissions import AdminPermissionsView
292 with mock.patch.object(AdminPermissionsView, 'ssh_enabled',
292 with mock.patch.object(AdminPermissionsView, 'ssh_enabled',
293 return_value=False):
293 return_value=False):
294 response = self.app.post(
294 response = self.app.post(
295 route_path('admin_permissions_ssh_keys_update'),
295 route_path('admin_permissions_ssh_keys_update'),
296 dict(csrf_token=self.csrf_token), status=302)
296 dict(csrf_token=self.csrf_token), status=302)
297
297
298 assert_session_flash(
298 assert_session_flash(
299 response, 'SSH key support is disabled in .ini file') No newline at end of file
299 response, 'SSH key support is disabled in .ini file')
@@ -1,519 +1,519 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import re
21 import re
22 import logging
22 import logging
23 import formencode
23 import formencode
24 import formencode.htmlfill
24 import formencode.htmlfill
25 import datetime
25 import datetime
26 from pyramid.interfaces import IRoutesMapper
26 from pyramid.interfaces import IRoutesMapper
27
27
28 from pyramid.view import view_config
28 from pyramid.view import view_config
29 from pyramid.httpexceptions import HTTPFound
29 from pyramid.httpexceptions import HTTPFound
30 from pyramid.renderers import render
30 from pyramid.renderers import render
31 from pyramid.response import Response
31 from pyramid.response import Response
32
32
33 from rhodecode.apps._base import BaseAppView, DataGridAppView
33 from rhodecode.apps._base import BaseAppView, DataGridAppView
34 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
34 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
35 from rhodecode import events
35 from rhodecode import events
36
36
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib.auth import (
38 from rhodecode.lib.auth import (
39 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
39 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
40 from rhodecode.lib.utils2 import aslist, safe_unicode
40 from rhodecode.lib.utils2 import aslist, safe_unicode
41 from rhodecode.model.db import (
41 from rhodecode.model.db import (
42 or_, coalesce, User, UserIpMap, UserSshKeys)
42 or_, coalesce, User, UserIpMap, UserSshKeys)
43 from rhodecode.model.forms import (
43 from rhodecode.model.forms import (
44 ApplicationPermissionsForm, ObjectPermissionsForm, UserPermissionsForm)
44 ApplicationPermissionsForm, ObjectPermissionsForm, UserPermissionsForm)
45 from rhodecode.model.meta import Session
45 from rhodecode.model.meta import Session
46 from rhodecode.model.permission import PermissionModel
46 from rhodecode.model.permission import PermissionModel
47 from rhodecode.model.settings import SettingsModel
47 from rhodecode.model.settings import SettingsModel
48
48
49
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
51
51
52
52
53 class AdminPermissionsView(BaseAppView, DataGridAppView):
53 class AdminPermissionsView(BaseAppView, DataGridAppView):
54 def load_default_context(self):
54 def load_default_context(self):
55 c = self._get_local_tmpl_context()
55 c = self._get_local_tmpl_context()
56 PermissionModel().set_global_permission_choices(
56 PermissionModel().set_global_permission_choices(
57 c, gettext_translator=self.request.translate)
57 c, gettext_translator=self.request.translate)
58 return c
58 return c
59
59
60 @LoginRequired()
60 @LoginRequired()
61 @HasPermissionAllDecorator('hg.admin')
61 @HasPermissionAllDecorator('hg.admin')
62 @view_config(
62 @view_config(
63 route_name='admin_permissions_application', request_method='GET',
63 route_name='admin_permissions_application', request_method='GET',
64 renderer='rhodecode:templates/admin/permissions/permissions.mako')
64 renderer='rhodecode:templates/admin/permissions/permissions.mako')
65 def permissions_application(self):
65 def permissions_application(self):
66 c = self.load_default_context()
66 c = self.load_default_context()
67 c.active = 'application'
67 c.active = 'application'
68
68
69 c.user = User.get_default_user(refresh=True)
69 c.user = User.get_default_user(refresh=True)
70
70
71 app_settings = c.rc_config
71 app_settings = c.rc_config
72
72
73 defaults = {
73 defaults = {
74 'anonymous': c.user.active,
74 'anonymous': c.user.active,
75 'default_register_message': app_settings.get(
75 'default_register_message': app_settings.get(
76 'rhodecode_register_message')
76 'rhodecode_register_message')
77 }
77 }
78 defaults.update(c.user.get_default_perms())
78 defaults.update(c.user.get_default_perms())
79
79
80 data = render('rhodecode:templates/admin/permissions/permissions.mako',
80 data = render('rhodecode:templates/admin/permissions/permissions.mako',
81 self._get_template_context(c), self.request)
81 self._get_template_context(c), self.request)
82 html = formencode.htmlfill.render(
82 html = formencode.htmlfill.render(
83 data,
83 data,
84 defaults=defaults,
84 defaults=defaults,
85 encoding="UTF-8",
85 encoding="UTF-8",
86 force_defaults=False
86 force_defaults=False
87 )
87 )
88 return Response(html)
88 return Response(html)
89
89
90 @LoginRequired()
90 @LoginRequired()
91 @HasPermissionAllDecorator('hg.admin')
91 @HasPermissionAllDecorator('hg.admin')
92 @CSRFRequired()
92 @CSRFRequired()
93 @view_config(
93 @view_config(
94 route_name='admin_permissions_application_update', request_method='POST',
94 route_name='admin_permissions_application_update', request_method='POST',
95 renderer='rhodecode:templates/admin/permissions/permissions.mako')
95 renderer='rhodecode:templates/admin/permissions/permissions.mako')
96 def permissions_application_update(self):
96 def permissions_application_update(self):
97 _ = self.request.translate
97 _ = self.request.translate
98 c = self.load_default_context()
98 c = self.load_default_context()
99 c.active = 'application'
99 c.active = 'application'
100
100
101 _form = ApplicationPermissionsForm(
101 _form = ApplicationPermissionsForm(
102 self.request.translate,
102 self.request.translate,
103 [x[0] for x in c.register_choices],
103 [x[0] for x in c.register_choices],
104 [x[0] for x in c.password_reset_choices],
104 [x[0] for x in c.password_reset_choices],
105 [x[0] for x in c.extern_activate_choices])()
105 [x[0] for x in c.extern_activate_choices])()
106
106
107 try:
107 try:
108 form_result = _form.to_python(dict(self.request.POST))
108 form_result = _form.to_python(dict(self.request.POST))
109 form_result.update({'perm_user_name': User.DEFAULT_USER})
109 form_result.update({'perm_user_name': User.DEFAULT_USER})
110 PermissionModel().update_application_permissions(form_result)
110 PermissionModel().update_application_permissions(form_result)
111
111
112 settings = [
112 settings = [
113 ('register_message', 'default_register_message'),
113 ('register_message', 'default_register_message'),
114 ]
114 ]
115 for setting, form_key in settings:
115 for setting, form_key in settings:
116 sett = SettingsModel().create_or_update_setting(
116 sett = SettingsModel().create_or_update_setting(
117 setting, form_result[form_key])
117 setting, form_result[form_key])
118 Session().add(sett)
118 Session().add(sett)
119
119
120 Session().commit()
120 Session().commit()
121 h.flash(_('Application permissions updated successfully'),
121 h.flash(_('Application permissions updated successfully'),
122 category='success')
122 category='success')
123
123
124 except formencode.Invalid as errors:
124 except formencode.Invalid as errors:
125 defaults = errors.value
125 defaults = errors.value
126
126
127 data = render(
127 data = render(
128 'rhodecode:templates/admin/permissions/permissions.mako',
128 'rhodecode:templates/admin/permissions/permissions.mako',
129 self._get_template_context(c), self.request)
129 self._get_template_context(c), self.request)
130 html = formencode.htmlfill.render(
130 html = formencode.htmlfill.render(
131 data,
131 data,
132 defaults=defaults,
132 defaults=defaults,
133 errors=errors.error_dict or {},
133 errors=errors.error_dict or {},
134 prefix_error=False,
134 prefix_error=False,
135 encoding="UTF-8",
135 encoding="UTF-8",
136 force_defaults=False
136 force_defaults=False
137 )
137 )
138 return Response(html)
138 return Response(html)
139
139
140 except Exception:
140 except Exception:
141 log.exception("Exception during update of permissions")
141 log.exception("Exception during update of permissions")
142 h.flash(_('Error occurred during update of permissions'),
142 h.flash(_('Error occurred during update of permissions'),
143 category='error')
143 category='error')
144
144
145 affected_user_ids = [User.get_default_user().user_id]
145 affected_user_ids = [User.get_default_user_id()]
146 PermissionModel().trigger_permission_flush(affected_user_ids)
146 PermissionModel().trigger_permission_flush(affected_user_ids)
147
147
148 raise HTTPFound(h.route_path('admin_permissions_application'))
148 raise HTTPFound(h.route_path('admin_permissions_application'))
149
149
150 @LoginRequired()
150 @LoginRequired()
151 @HasPermissionAllDecorator('hg.admin')
151 @HasPermissionAllDecorator('hg.admin')
152 @view_config(
152 @view_config(
153 route_name='admin_permissions_object', request_method='GET',
153 route_name='admin_permissions_object', request_method='GET',
154 renderer='rhodecode:templates/admin/permissions/permissions.mako')
154 renderer='rhodecode:templates/admin/permissions/permissions.mako')
155 def permissions_objects(self):
155 def permissions_objects(self):
156 c = self.load_default_context()
156 c = self.load_default_context()
157 c.active = 'objects'
157 c.active = 'objects'
158
158
159 c.user = User.get_default_user(refresh=True)
159 c.user = User.get_default_user(refresh=True)
160 defaults = {}
160 defaults = {}
161 defaults.update(c.user.get_default_perms())
161 defaults.update(c.user.get_default_perms())
162
162
163 data = render(
163 data = render(
164 'rhodecode:templates/admin/permissions/permissions.mako',
164 'rhodecode:templates/admin/permissions/permissions.mako',
165 self._get_template_context(c), self.request)
165 self._get_template_context(c), self.request)
166 html = formencode.htmlfill.render(
166 html = formencode.htmlfill.render(
167 data,
167 data,
168 defaults=defaults,
168 defaults=defaults,
169 encoding="UTF-8",
169 encoding="UTF-8",
170 force_defaults=False
170 force_defaults=False
171 )
171 )
172 return Response(html)
172 return Response(html)
173
173
174 @LoginRequired()
174 @LoginRequired()
175 @HasPermissionAllDecorator('hg.admin')
175 @HasPermissionAllDecorator('hg.admin')
176 @CSRFRequired()
176 @CSRFRequired()
177 @view_config(
177 @view_config(
178 route_name='admin_permissions_object_update', request_method='POST',
178 route_name='admin_permissions_object_update', request_method='POST',
179 renderer='rhodecode:templates/admin/permissions/permissions.mako')
179 renderer='rhodecode:templates/admin/permissions/permissions.mako')
180 def permissions_objects_update(self):
180 def permissions_objects_update(self):
181 _ = self.request.translate
181 _ = self.request.translate
182 c = self.load_default_context()
182 c = self.load_default_context()
183 c.active = 'objects'
183 c.active = 'objects'
184
184
185 _form = ObjectPermissionsForm(
185 _form = ObjectPermissionsForm(
186 self.request.translate,
186 self.request.translate,
187 [x[0] for x in c.repo_perms_choices],
187 [x[0] for x in c.repo_perms_choices],
188 [x[0] for x in c.group_perms_choices],
188 [x[0] for x in c.group_perms_choices],
189 [x[0] for x in c.user_group_perms_choices],
189 [x[0] for x in c.user_group_perms_choices],
190 )()
190 )()
191
191
192 try:
192 try:
193 form_result = _form.to_python(dict(self.request.POST))
193 form_result = _form.to_python(dict(self.request.POST))
194 form_result.update({'perm_user_name': User.DEFAULT_USER})
194 form_result.update({'perm_user_name': User.DEFAULT_USER})
195 PermissionModel().update_object_permissions(form_result)
195 PermissionModel().update_object_permissions(form_result)
196
196
197 Session().commit()
197 Session().commit()
198 h.flash(_('Object permissions updated successfully'),
198 h.flash(_('Object permissions updated successfully'),
199 category='success')
199 category='success')
200
200
201 except formencode.Invalid as errors:
201 except formencode.Invalid as errors:
202 defaults = errors.value
202 defaults = errors.value
203
203
204 data = render(
204 data = render(
205 'rhodecode:templates/admin/permissions/permissions.mako',
205 'rhodecode:templates/admin/permissions/permissions.mako',
206 self._get_template_context(c), self.request)
206 self._get_template_context(c), self.request)
207 html = formencode.htmlfill.render(
207 html = formencode.htmlfill.render(
208 data,
208 data,
209 defaults=defaults,
209 defaults=defaults,
210 errors=errors.error_dict or {},
210 errors=errors.error_dict or {},
211 prefix_error=False,
211 prefix_error=False,
212 encoding="UTF-8",
212 encoding="UTF-8",
213 force_defaults=False
213 force_defaults=False
214 )
214 )
215 return Response(html)
215 return Response(html)
216 except Exception:
216 except Exception:
217 log.exception("Exception during update of permissions")
217 log.exception("Exception during update of permissions")
218 h.flash(_('Error occurred during update of permissions'),
218 h.flash(_('Error occurred during update of permissions'),
219 category='error')
219 category='error')
220
220
221 affected_user_ids = [User.get_default_user().user_id]
221 affected_user_ids = [User.get_default_user_id()]
222 PermissionModel().trigger_permission_flush(affected_user_ids)
222 PermissionModel().trigger_permission_flush(affected_user_ids)
223
223
224 raise HTTPFound(h.route_path('admin_permissions_object'))
224 raise HTTPFound(h.route_path('admin_permissions_object'))
225
225
226 @LoginRequired()
226 @LoginRequired()
227 @HasPermissionAllDecorator('hg.admin')
227 @HasPermissionAllDecorator('hg.admin')
228 @view_config(
228 @view_config(
229 route_name='admin_permissions_branch', request_method='GET',
229 route_name='admin_permissions_branch', request_method='GET',
230 renderer='rhodecode:templates/admin/permissions/permissions.mako')
230 renderer='rhodecode:templates/admin/permissions/permissions.mako')
231 def permissions_branch(self):
231 def permissions_branch(self):
232 c = self.load_default_context()
232 c = self.load_default_context()
233 c.active = 'branch'
233 c.active = 'branch'
234
234
235 c.user = User.get_default_user(refresh=True)
235 c.user = User.get_default_user(refresh=True)
236 defaults = {}
236 defaults = {}
237 defaults.update(c.user.get_default_perms())
237 defaults.update(c.user.get_default_perms())
238
238
239 data = render(
239 data = render(
240 'rhodecode:templates/admin/permissions/permissions.mako',
240 'rhodecode:templates/admin/permissions/permissions.mako',
241 self._get_template_context(c), self.request)
241 self._get_template_context(c), self.request)
242 html = formencode.htmlfill.render(
242 html = formencode.htmlfill.render(
243 data,
243 data,
244 defaults=defaults,
244 defaults=defaults,
245 encoding="UTF-8",
245 encoding="UTF-8",
246 force_defaults=False
246 force_defaults=False
247 )
247 )
248 return Response(html)
248 return Response(html)
249
249
250 @LoginRequired()
250 @LoginRequired()
251 @HasPermissionAllDecorator('hg.admin')
251 @HasPermissionAllDecorator('hg.admin')
252 @view_config(
252 @view_config(
253 route_name='admin_permissions_global', request_method='GET',
253 route_name='admin_permissions_global', request_method='GET',
254 renderer='rhodecode:templates/admin/permissions/permissions.mako')
254 renderer='rhodecode:templates/admin/permissions/permissions.mako')
255 def permissions_global(self):
255 def permissions_global(self):
256 c = self.load_default_context()
256 c = self.load_default_context()
257 c.active = 'global'
257 c.active = 'global'
258
258
259 c.user = User.get_default_user(refresh=True)
259 c.user = User.get_default_user(refresh=True)
260 defaults = {}
260 defaults = {}
261 defaults.update(c.user.get_default_perms())
261 defaults.update(c.user.get_default_perms())
262
262
263 data = render(
263 data = render(
264 'rhodecode:templates/admin/permissions/permissions.mako',
264 'rhodecode:templates/admin/permissions/permissions.mako',
265 self._get_template_context(c), self.request)
265 self._get_template_context(c), self.request)
266 html = formencode.htmlfill.render(
266 html = formencode.htmlfill.render(
267 data,
267 data,
268 defaults=defaults,
268 defaults=defaults,
269 encoding="UTF-8",
269 encoding="UTF-8",
270 force_defaults=False
270 force_defaults=False
271 )
271 )
272 return Response(html)
272 return Response(html)
273
273
274 @LoginRequired()
274 @LoginRequired()
275 @HasPermissionAllDecorator('hg.admin')
275 @HasPermissionAllDecorator('hg.admin')
276 @CSRFRequired()
276 @CSRFRequired()
277 @view_config(
277 @view_config(
278 route_name='admin_permissions_global_update', request_method='POST',
278 route_name='admin_permissions_global_update', request_method='POST',
279 renderer='rhodecode:templates/admin/permissions/permissions.mako')
279 renderer='rhodecode:templates/admin/permissions/permissions.mako')
280 def permissions_global_update(self):
280 def permissions_global_update(self):
281 _ = self.request.translate
281 _ = self.request.translate
282 c = self.load_default_context()
282 c = self.load_default_context()
283 c.active = 'global'
283 c.active = 'global'
284
284
285 _form = UserPermissionsForm(
285 _form = UserPermissionsForm(
286 self.request.translate,
286 self.request.translate,
287 [x[0] for x in c.repo_create_choices],
287 [x[0] for x in c.repo_create_choices],
288 [x[0] for x in c.repo_create_on_write_choices],
288 [x[0] for x in c.repo_create_on_write_choices],
289 [x[0] for x in c.repo_group_create_choices],
289 [x[0] for x in c.repo_group_create_choices],
290 [x[0] for x in c.user_group_create_choices],
290 [x[0] for x in c.user_group_create_choices],
291 [x[0] for x in c.fork_choices],
291 [x[0] for x in c.fork_choices],
292 [x[0] for x in c.inherit_default_permission_choices])()
292 [x[0] for x in c.inherit_default_permission_choices])()
293
293
294 try:
294 try:
295 form_result = _form.to_python(dict(self.request.POST))
295 form_result = _form.to_python(dict(self.request.POST))
296 form_result.update({'perm_user_name': User.DEFAULT_USER})
296 form_result.update({'perm_user_name': User.DEFAULT_USER})
297 PermissionModel().update_user_permissions(form_result)
297 PermissionModel().update_user_permissions(form_result)
298
298
299 Session().commit()
299 Session().commit()
300 h.flash(_('Global permissions updated successfully'),
300 h.flash(_('Global permissions updated successfully'),
301 category='success')
301 category='success')
302
302
303 except formencode.Invalid as errors:
303 except formencode.Invalid as errors:
304 defaults = errors.value
304 defaults = errors.value
305
305
306 data = render(
306 data = render(
307 'rhodecode:templates/admin/permissions/permissions.mako',
307 'rhodecode:templates/admin/permissions/permissions.mako',
308 self._get_template_context(c), self.request)
308 self._get_template_context(c), self.request)
309 html = formencode.htmlfill.render(
309 html = formencode.htmlfill.render(
310 data,
310 data,
311 defaults=defaults,
311 defaults=defaults,
312 errors=errors.error_dict or {},
312 errors=errors.error_dict or {},
313 prefix_error=False,
313 prefix_error=False,
314 encoding="UTF-8",
314 encoding="UTF-8",
315 force_defaults=False
315 force_defaults=False
316 )
316 )
317 return Response(html)
317 return Response(html)
318 except Exception:
318 except Exception:
319 log.exception("Exception during update of permissions")
319 log.exception("Exception during update of permissions")
320 h.flash(_('Error occurred during update of permissions'),
320 h.flash(_('Error occurred during update of permissions'),
321 category='error')
321 category='error')
322
322
323 affected_user_ids = [User.get_default_user().user_id]
323 affected_user_ids = [User.get_default_user_id()]
324 PermissionModel().trigger_permission_flush(affected_user_ids)
324 PermissionModel().trigger_permission_flush(affected_user_ids)
325
325
326 raise HTTPFound(h.route_path('admin_permissions_global'))
326 raise HTTPFound(h.route_path('admin_permissions_global'))
327
327
328 @LoginRequired()
328 @LoginRequired()
329 @HasPermissionAllDecorator('hg.admin')
329 @HasPermissionAllDecorator('hg.admin')
330 @view_config(
330 @view_config(
331 route_name='admin_permissions_ips', request_method='GET',
331 route_name='admin_permissions_ips', request_method='GET',
332 renderer='rhodecode:templates/admin/permissions/permissions.mako')
332 renderer='rhodecode:templates/admin/permissions/permissions.mako')
333 def permissions_ips(self):
333 def permissions_ips(self):
334 c = self.load_default_context()
334 c = self.load_default_context()
335 c.active = 'ips'
335 c.active = 'ips'
336
336
337 c.user = User.get_default_user(refresh=True)
337 c.user = User.get_default_user(refresh=True)
338 c.user_ip_map = (
338 c.user_ip_map = (
339 UserIpMap.query().filter(UserIpMap.user == c.user).all())
339 UserIpMap.query().filter(UserIpMap.user == c.user).all())
340
340
341 return self._get_template_context(c)
341 return self._get_template_context(c)
342
342
343 @LoginRequired()
343 @LoginRequired()
344 @HasPermissionAllDecorator('hg.admin')
344 @HasPermissionAllDecorator('hg.admin')
345 @view_config(
345 @view_config(
346 route_name='admin_permissions_overview', request_method='GET',
346 route_name='admin_permissions_overview', request_method='GET',
347 renderer='rhodecode:templates/admin/permissions/permissions.mako')
347 renderer='rhodecode:templates/admin/permissions/permissions.mako')
348 def permissions_overview(self):
348 def permissions_overview(self):
349 c = self.load_default_context()
349 c = self.load_default_context()
350 c.active = 'perms'
350 c.active = 'perms'
351
351
352 c.user = User.get_default_user(refresh=True)
352 c.user = User.get_default_user(refresh=True)
353 c.perm_user = c.user.AuthUser()
353 c.perm_user = c.user.AuthUser()
354 return self._get_template_context(c)
354 return self._get_template_context(c)
355
355
356 @LoginRequired()
356 @LoginRequired()
357 @HasPermissionAllDecorator('hg.admin')
357 @HasPermissionAllDecorator('hg.admin')
358 @view_config(
358 @view_config(
359 route_name='admin_permissions_auth_token_access', request_method='GET',
359 route_name='admin_permissions_auth_token_access', request_method='GET',
360 renderer='rhodecode:templates/admin/permissions/permissions.mako')
360 renderer='rhodecode:templates/admin/permissions/permissions.mako')
361 def auth_token_access(self):
361 def auth_token_access(self):
362 from rhodecode import CONFIG
362 from rhodecode import CONFIG
363
363
364 c = self.load_default_context()
364 c = self.load_default_context()
365 c.active = 'auth_token_access'
365 c.active = 'auth_token_access'
366
366
367 c.user = User.get_default_user(refresh=True)
367 c.user = User.get_default_user(refresh=True)
368 c.perm_user = c.user.AuthUser()
368 c.perm_user = c.user.AuthUser()
369
369
370 mapper = self.request.registry.queryUtility(IRoutesMapper)
370 mapper = self.request.registry.queryUtility(IRoutesMapper)
371 c.view_data = []
371 c.view_data = []
372
372
373 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
373 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
374 introspector = self.request.registry.introspector
374 introspector = self.request.registry.introspector
375
375
376 view_intr = {}
376 view_intr = {}
377 for view_data in introspector.get_category('views'):
377 for view_data in introspector.get_category('views'):
378 intr = view_data['introspectable']
378 intr = view_data['introspectable']
379
379
380 if 'route_name' in intr and intr['attr']:
380 if 'route_name' in intr and intr['attr']:
381 view_intr[intr['route_name']] = '{}:{}'.format(
381 view_intr[intr['route_name']] = '{}:{}'.format(
382 str(intr['derived_callable'].func_name), intr['attr']
382 str(intr['derived_callable'].func_name), intr['attr']
383 )
383 )
384
384
385 c.whitelist_key = 'api_access_controllers_whitelist'
385 c.whitelist_key = 'api_access_controllers_whitelist'
386 c.whitelist_file = CONFIG.get('__file__')
386 c.whitelist_file = CONFIG.get('__file__')
387 whitelist_views = aslist(
387 whitelist_views = aslist(
388 CONFIG.get(c.whitelist_key), sep=',')
388 CONFIG.get(c.whitelist_key), sep=',')
389
389
390 for route_info in mapper.get_routes():
390 for route_info in mapper.get_routes():
391 if not route_info.name.startswith('__'):
391 if not route_info.name.startswith('__'):
392 routepath = route_info.pattern
392 routepath = route_info.pattern
393
393
394 def replace(matchobj):
394 def replace(matchobj):
395 if matchobj.group(1):
395 if matchobj.group(1):
396 return "{%s}" % matchobj.group(1).split(':')[0]
396 return "{%s}" % matchobj.group(1).split(':')[0]
397 else:
397 else:
398 return "{%s}" % matchobj.group(2)
398 return "{%s}" % matchobj.group(2)
399
399
400 routepath = _argument_prog.sub(replace, routepath)
400 routepath = _argument_prog.sub(replace, routepath)
401
401
402 if not routepath.startswith('/'):
402 if not routepath.startswith('/'):
403 routepath = '/' + routepath
403 routepath = '/' + routepath
404
404
405 view_fqn = view_intr.get(route_info.name, 'NOT AVAILABLE')
405 view_fqn = view_intr.get(route_info.name, 'NOT AVAILABLE')
406 active = view_fqn in whitelist_views
406 active = view_fqn in whitelist_views
407 c.view_data.append((route_info.name, view_fqn, routepath, active))
407 c.view_data.append((route_info.name, view_fqn, routepath, active))
408
408
409 c.whitelist_views = whitelist_views
409 c.whitelist_views = whitelist_views
410 return self._get_template_context(c)
410 return self._get_template_context(c)
411
411
412 def ssh_enabled(self):
412 def ssh_enabled(self):
413 return self.request.registry.settings.get(
413 return self.request.registry.settings.get(
414 'ssh.generate_authorized_keyfile')
414 'ssh.generate_authorized_keyfile')
415
415
416 @LoginRequired()
416 @LoginRequired()
417 @HasPermissionAllDecorator('hg.admin')
417 @HasPermissionAllDecorator('hg.admin')
418 @view_config(
418 @view_config(
419 route_name='admin_permissions_ssh_keys', request_method='GET',
419 route_name='admin_permissions_ssh_keys', request_method='GET',
420 renderer='rhodecode:templates/admin/permissions/permissions.mako')
420 renderer='rhodecode:templates/admin/permissions/permissions.mako')
421 def ssh_keys(self):
421 def ssh_keys(self):
422 c = self.load_default_context()
422 c = self.load_default_context()
423 c.active = 'ssh_keys'
423 c.active = 'ssh_keys'
424 c.ssh_enabled = self.ssh_enabled()
424 c.ssh_enabled = self.ssh_enabled()
425 return self._get_template_context(c)
425 return self._get_template_context(c)
426
426
427 @LoginRequired()
427 @LoginRequired()
428 @HasPermissionAllDecorator('hg.admin')
428 @HasPermissionAllDecorator('hg.admin')
429 @view_config(
429 @view_config(
430 route_name='admin_permissions_ssh_keys_data', request_method='GET',
430 route_name='admin_permissions_ssh_keys_data', request_method='GET',
431 renderer='json_ext', xhr=True)
431 renderer='json_ext', xhr=True)
432 def ssh_keys_data(self):
432 def ssh_keys_data(self):
433 _ = self.request.translate
433 _ = self.request.translate
434 self.load_default_context()
434 self.load_default_context()
435 column_map = {
435 column_map = {
436 'fingerprint': 'ssh_key_fingerprint',
436 'fingerprint': 'ssh_key_fingerprint',
437 'username': User.username
437 'username': User.username
438 }
438 }
439 draw, start, limit = self._extract_chunk(self.request)
439 draw, start, limit = self._extract_chunk(self.request)
440 search_q, order_by, order_dir = self._extract_ordering(
440 search_q, order_by, order_dir = self._extract_ordering(
441 self.request, column_map=column_map)
441 self.request, column_map=column_map)
442
442
443 ssh_keys_data_total_count = UserSshKeys.query()\
443 ssh_keys_data_total_count = UserSshKeys.query()\
444 .count()
444 .count()
445
445
446 # json generate
446 # json generate
447 base_q = UserSshKeys.query().join(UserSshKeys.user)
447 base_q = UserSshKeys.query().join(UserSshKeys.user)
448
448
449 if search_q:
449 if search_q:
450 like_expression = u'%{}%'.format(safe_unicode(search_q))
450 like_expression = u'%{}%'.format(safe_unicode(search_q))
451 base_q = base_q.filter(or_(
451 base_q = base_q.filter(or_(
452 User.username.ilike(like_expression),
452 User.username.ilike(like_expression),
453 UserSshKeys.ssh_key_fingerprint.ilike(like_expression),
453 UserSshKeys.ssh_key_fingerprint.ilike(like_expression),
454 ))
454 ))
455
455
456 users_data_total_filtered_count = base_q.count()
456 users_data_total_filtered_count = base_q.count()
457
457
458 sort_col = self._get_order_col(order_by, UserSshKeys)
458 sort_col = self._get_order_col(order_by, UserSshKeys)
459 if sort_col:
459 if sort_col:
460 if order_dir == 'asc':
460 if order_dir == 'asc':
461 # handle null values properly to order by NULL last
461 # handle null values properly to order by NULL last
462 if order_by in ['created_on']:
462 if order_by in ['created_on']:
463 sort_col = coalesce(sort_col, datetime.date.max)
463 sort_col = coalesce(sort_col, datetime.date.max)
464 sort_col = sort_col.asc()
464 sort_col = sort_col.asc()
465 else:
465 else:
466 # handle null values properly to order by NULL last
466 # handle null values properly to order by NULL last
467 if order_by in ['created_on']:
467 if order_by in ['created_on']:
468 sort_col = coalesce(sort_col, datetime.date.min)
468 sort_col = coalesce(sort_col, datetime.date.min)
469 sort_col = sort_col.desc()
469 sort_col = sort_col.desc()
470
470
471 base_q = base_q.order_by(sort_col)
471 base_q = base_q.order_by(sort_col)
472 base_q = base_q.offset(start).limit(limit)
472 base_q = base_q.offset(start).limit(limit)
473
473
474 ssh_keys = base_q.all()
474 ssh_keys = base_q.all()
475
475
476 ssh_keys_data = []
476 ssh_keys_data = []
477 for ssh_key in ssh_keys:
477 for ssh_key in ssh_keys:
478 ssh_keys_data.append({
478 ssh_keys_data.append({
479 "username": h.gravatar_with_user(self.request, ssh_key.user.username),
479 "username": h.gravatar_with_user(self.request, ssh_key.user.username),
480 "fingerprint": ssh_key.ssh_key_fingerprint,
480 "fingerprint": ssh_key.ssh_key_fingerprint,
481 "description": ssh_key.description,
481 "description": ssh_key.description,
482 "created_on": h.format_date(ssh_key.created_on),
482 "created_on": h.format_date(ssh_key.created_on),
483 "accessed_on": h.format_date(ssh_key.accessed_on),
483 "accessed_on": h.format_date(ssh_key.accessed_on),
484 "action": h.link_to(
484 "action": h.link_to(
485 _('Edit'), h.route_path('edit_user_ssh_keys',
485 _('Edit'), h.route_path('edit_user_ssh_keys',
486 user_id=ssh_key.user.user_id))
486 user_id=ssh_key.user.user_id))
487 })
487 })
488
488
489 data = ({
489 data = ({
490 'draw': draw,
490 'draw': draw,
491 'data': ssh_keys_data,
491 'data': ssh_keys_data,
492 'recordsTotal': ssh_keys_data_total_count,
492 'recordsTotal': ssh_keys_data_total_count,
493 'recordsFiltered': users_data_total_filtered_count,
493 'recordsFiltered': users_data_total_filtered_count,
494 })
494 })
495
495
496 return data
496 return data
497
497
498 @LoginRequired()
498 @LoginRequired()
499 @HasPermissionAllDecorator('hg.admin')
499 @HasPermissionAllDecorator('hg.admin')
500 @CSRFRequired()
500 @CSRFRequired()
501 @view_config(
501 @view_config(
502 route_name='admin_permissions_ssh_keys_update', request_method='POST',
502 route_name='admin_permissions_ssh_keys_update', request_method='POST',
503 renderer='rhodecode:templates/admin/permissions/permissions.mako')
503 renderer='rhodecode:templates/admin/permissions/permissions.mako')
504 def ssh_keys_update(self):
504 def ssh_keys_update(self):
505 _ = self.request.translate
505 _ = self.request.translate
506 self.load_default_context()
506 self.load_default_context()
507
507
508 ssh_enabled = self.ssh_enabled()
508 ssh_enabled = self.ssh_enabled()
509 key_file = self.request.registry.settings.get(
509 key_file = self.request.registry.settings.get(
510 'ssh.authorized_keys_file_path')
510 'ssh.authorized_keys_file_path')
511 if ssh_enabled:
511 if ssh_enabled:
512 events.trigger(SshKeyFileChangeEvent(), self.request.registry)
512 events.trigger(SshKeyFileChangeEvent(), self.request.registry)
513 h.flash(_('Updated SSH keys file: {}').format(key_file),
513 h.flash(_('Updated SSH keys file: {}').format(key_file),
514 category='success')
514 category='success')
515 else:
515 else:
516 h.flash(_('SSH key support is disabled in .ini file'),
516 h.flash(_('SSH key support is disabled in .ini file'),
517 category='warning')
517 category='warning')
518
518
519 raise HTTPFound(h.route_path('admin_permissions_ssh_keys'))
519 raise HTTPFound(h.route_path('admin_permissions_ssh_keys'))
@@ -1,325 +1,325 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2020 RhodeCode GmbH
3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 from pyramid.view import view_config
23 from pyramid.view import view_config
24 from pyramid.httpexceptions import HTTPFound
24 from pyramid.httpexceptions import HTTPFound
25 from packaging.version import Version
25 from packaging.version import Version
26
26
27 from rhodecode import events
27 from rhodecode import events
28 from rhodecode.apps._base import RepoAppView
28 from rhodecode.apps._base import RepoAppView
29 from rhodecode.lib import helpers as h
29 from rhodecode.lib import helpers as h
30 from rhodecode.lib import audit_logger
30 from rhodecode.lib import audit_logger
31 from rhodecode.lib.auth import (
31 from rhodecode.lib.auth import (
32 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired,
32 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired,
33 HasRepoPermissionAny)
33 HasRepoPermissionAny)
34 from rhodecode.lib.exceptions import AttachedForksError, AttachedPullRequestsError
34 from rhodecode.lib.exceptions import AttachedForksError, AttachedPullRequestsError
35 from rhodecode.lib.utils2 import safe_int
35 from rhodecode.lib.utils2 import safe_int
36 from rhodecode.lib.vcs import RepositoryError
36 from rhodecode.lib.vcs import RepositoryError
37 from rhodecode.model.db import Session, UserFollowing, User, Repository
37 from rhodecode.model.db import Session, UserFollowing, User, Repository
38 from rhodecode.model.permission import PermissionModel
38 from rhodecode.model.permission import PermissionModel
39 from rhodecode.model.repo import RepoModel
39 from rhodecode.model.repo import RepoModel
40 from rhodecode.model.scm import ScmModel
40 from rhodecode.model.scm import ScmModel
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44
44
45 class RepoSettingsView(RepoAppView):
45 class RepoSettingsView(RepoAppView):
46
46
47 def load_default_context(self):
47 def load_default_context(self):
48 c = self._get_local_tmpl_context()
48 c = self._get_local_tmpl_context()
49 return c
49 return c
50
50
51 def _get_users_with_permissions(self):
51 def _get_users_with_permissions(self):
52 user_permissions = {}
52 user_permissions = {}
53 for perm in self.db_repo.permissions():
53 for perm in self.db_repo.permissions():
54 user_permissions[perm.user_id] = perm
54 user_permissions[perm.user_id] = perm
55
55
56 return user_permissions
56 return user_permissions
57
57
58 @LoginRequired()
58 @LoginRequired()
59 @HasRepoPermissionAnyDecorator('repository.admin')
59 @HasRepoPermissionAnyDecorator('repository.admin')
60 @view_config(
60 @view_config(
61 route_name='edit_repo_advanced', request_method='GET',
61 route_name='edit_repo_advanced', request_method='GET',
62 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
62 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
63 def edit_advanced(self):
63 def edit_advanced(self):
64 _ = self.request.translate
64 _ = self.request.translate
65 c = self.load_default_context()
65 c = self.load_default_context()
66 c.active = 'advanced'
66 c.active = 'advanced'
67
67
68 c.default_user_id = User.get_default_user().user_id
68 c.default_user_id = User.get_default_user_id()
69 c.in_public_journal = UserFollowing.query() \
69 c.in_public_journal = UserFollowing.query() \
70 .filter(UserFollowing.user_id == c.default_user_id) \
70 .filter(UserFollowing.user_id == c.default_user_id) \
71 .filter(UserFollowing.follows_repository == self.db_repo).scalar()
71 .filter(UserFollowing.follows_repository == self.db_repo).scalar()
72
72
73 c.ver_info_dict = self.rhodecode_vcs_repo.get_hooks_info()
73 c.ver_info_dict = self.rhodecode_vcs_repo.get_hooks_info()
74 c.hooks_outdated = False
74 c.hooks_outdated = False
75
75
76 try:
76 try:
77 if Version(c.ver_info_dict['pre_version']) < Version(c.rhodecode_version):
77 if Version(c.ver_info_dict['pre_version']) < Version(c.rhodecode_version):
78 c.hooks_outdated = True
78 c.hooks_outdated = True
79 except Exception:
79 except Exception:
80 pass
80 pass
81
81
82 # update commit cache if GET flag is present
82 # update commit cache if GET flag is present
83 if self.request.GET.get('update_commit_cache'):
83 if self.request.GET.get('update_commit_cache'):
84 self.db_repo.update_commit_cache()
84 self.db_repo.update_commit_cache()
85 h.flash(_('updated commit cache'), category='success')
85 h.flash(_('updated commit cache'), category='success')
86
86
87 return self._get_template_context(c)
87 return self._get_template_context(c)
88
88
89 @LoginRequired()
89 @LoginRequired()
90 @HasRepoPermissionAnyDecorator('repository.admin')
90 @HasRepoPermissionAnyDecorator('repository.admin')
91 @CSRFRequired()
91 @CSRFRequired()
92 @view_config(
92 @view_config(
93 route_name='edit_repo_advanced_archive', request_method='POST',
93 route_name='edit_repo_advanced_archive', request_method='POST',
94 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
94 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
95 def edit_advanced_archive(self):
95 def edit_advanced_archive(self):
96 """
96 """
97 Archives the repository. It will become read-only, and not visible in search
97 Archives the repository. It will become read-only, and not visible in search
98 or other queries. But still visible for super-admins.
98 or other queries. But still visible for super-admins.
99 """
99 """
100
100
101 _ = self.request.translate
101 _ = self.request.translate
102
102
103 try:
103 try:
104 old_data = self.db_repo.get_api_data()
104 old_data = self.db_repo.get_api_data()
105 RepoModel().archive(self.db_repo)
105 RepoModel().archive(self.db_repo)
106
106
107 repo = audit_logger.RepoWrap(repo_id=None, repo_name=self.db_repo.repo_name)
107 repo = audit_logger.RepoWrap(repo_id=None, repo_name=self.db_repo.repo_name)
108 audit_logger.store_web(
108 audit_logger.store_web(
109 'repo.archive', action_data={'old_data': old_data},
109 'repo.archive', action_data={'old_data': old_data},
110 user=self._rhodecode_user, repo=repo)
110 user=self._rhodecode_user, repo=repo)
111
111
112 ScmModel().mark_for_invalidation(self.db_repo_name, delete=True)
112 ScmModel().mark_for_invalidation(self.db_repo_name, delete=True)
113 h.flash(
113 h.flash(
114 _('Archived repository `%s`') % self.db_repo_name,
114 _('Archived repository `%s`') % self.db_repo_name,
115 category='success')
115 category='success')
116 Session().commit()
116 Session().commit()
117 except Exception:
117 except Exception:
118 log.exception("Exception during archiving of repository")
118 log.exception("Exception during archiving of repository")
119 h.flash(_('An error occurred during archiving of `%s`')
119 h.flash(_('An error occurred during archiving of `%s`')
120 % self.db_repo_name, category='error')
120 % self.db_repo_name, category='error')
121 # redirect to advanced for more deletion options
121 # redirect to advanced for more deletion options
122 raise HTTPFound(
122 raise HTTPFound(
123 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name,
123 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name,
124 _anchor='advanced-archive'))
124 _anchor='advanced-archive'))
125
125
126 # flush permissions for all users defined in permissions
126 # flush permissions for all users defined in permissions
127 affected_user_ids = self._get_users_with_permissions().keys()
127 affected_user_ids = self._get_users_with_permissions().keys()
128 PermissionModel().trigger_permission_flush(affected_user_ids)
128 PermissionModel().trigger_permission_flush(affected_user_ids)
129
129
130 raise HTTPFound(h.route_path('home'))
130 raise HTTPFound(h.route_path('home'))
131
131
132 @LoginRequired()
132 @LoginRequired()
133 @HasRepoPermissionAnyDecorator('repository.admin')
133 @HasRepoPermissionAnyDecorator('repository.admin')
134 @CSRFRequired()
134 @CSRFRequired()
135 @view_config(
135 @view_config(
136 route_name='edit_repo_advanced_delete', request_method='POST',
136 route_name='edit_repo_advanced_delete', request_method='POST',
137 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
137 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
138 def edit_advanced_delete(self):
138 def edit_advanced_delete(self):
139 """
139 """
140 Deletes the repository, or shows warnings if deletion is not possible
140 Deletes the repository, or shows warnings if deletion is not possible
141 because of attached forks or other errors.
141 because of attached forks or other errors.
142 """
142 """
143 _ = self.request.translate
143 _ = self.request.translate
144 handle_forks = self.request.POST.get('forks', None)
144 handle_forks = self.request.POST.get('forks', None)
145 if handle_forks == 'detach_forks':
145 if handle_forks == 'detach_forks':
146 handle_forks = 'detach'
146 handle_forks = 'detach'
147 elif handle_forks == 'delete_forks':
147 elif handle_forks == 'delete_forks':
148 handle_forks = 'delete'
148 handle_forks = 'delete'
149
149
150 try:
150 try:
151 old_data = self.db_repo.get_api_data()
151 old_data = self.db_repo.get_api_data()
152 RepoModel().delete(self.db_repo, forks=handle_forks)
152 RepoModel().delete(self.db_repo, forks=handle_forks)
153
153
154 _forks = self.db_repo.forks.count()
154 _forks = self.db_repo.forks.count()
155 if _forks and handle_forks:
155 if _forks and handle_forks:
156 if handle_forks == 'detach_forks':
156 if handle_forks == 'detach_forks':
157 h.flash(_('Detached %s forks') % _forks, category='success')
157 h.flash(_('Detached %s forks') % _forks, category='success')
158 elif handle_forks == 'delete_forks':
158 elif handle_forks == 'delete_forks':
159 h.flash(_('Deleted %s forks') % _forks, category='success')
159 h.flash(_('Deleted %s forks') % _forks, category='success')
160
160
161 repo = audit_logger.RepoWrap(repo_id=None, repo_name=self.db_repo.repo_name)
161 repo = audit_logger.RepoWrap(repo_id=None, repo_name=self.db_repo.repo_name)
162 audit_logger.store_web(
162 audit_logger.store_web(
163 'repo.delete', action_data={'old_data': old_data},
163 'repo.delete', action_data={'old_data': old_data},
164 user=self._rhodecode_user, repo=repo)
164 user=self._rhodecode_user, repo=repo)
165
165
166 ScmModel().mark_for_invalidation(self.db_repo_name, delete=True)
166 ScmModel().mark_for_invalidation(self.db_repo_name, delete=True)
167 h.flash(
167 h.flash(
168 _('Deleted repository `%s`') % self.db_repo_name,
168 _('Deleted repository `%s`') % self.db_repo_name,
169 category='success')
169 category='success')
170 Session().commit()
170 Session().commit()
171 except AttachedForksError:
171 except AttachedForksError:
172 repo_advanced_url = h.route_path(
172 repo_advanced_url = h.route_path(
173 'edit_repo_advanced', repo_name=self.db_repo_name,
173 'edit_repo_advanced', repo_name=self.db_repo_name,
174 _anchor='advanced-delete')
174 _anchor='advanced-delete')
175 delete_anchor = h.link_to(_('detach or delete'), repo_advanced_url)
175 delete_anchor = h.link_to(_('detach or delete'), repo_advanced_url)
176 h.flash(_('Cannot delete `{repo}` it still contains attached forks. '
176 h.flash(_('Cannot delete `{repo}` it still contains attached forks. '
177 'Try using {delete_or_detach} option.')
177 'Try using {delete_or_detach} option.')
178 .format(repo=self.db_repo_name, delete_or_detach=delete_anchor),
178 .format(repo=self.db_repo_name, delete_or_detach=delete_anchor),
179 category='warning')
179 category='warning')
180
180
181 # redirect to advanced for forks handle action ?
181 # redirect to advanced for forks handle action ?
182 raise HTTPFound(repo_advanced_url)
182 raise HTTPFound(repo_advanced_url)
183
183
184 except AttachedPullRequestsError:
184 except AttachedPullRequestsError:
185 repo_advanced_url = h.route_path(
185 repo_advanced_url = h.route_path(
186 'edit_repo_advanced', repo_name=self.db_repo_name,
186 'edit_repo_advanced', repo_name=self.db_repo_name,
187 _anchor='advanced-delete')
187 _anchor='advanced-delete')
188 attached_prs = len(self.db_repo.pull_requests_source +
188 attached_prs = len(self.db_repo.pull_requests_source +
189 self.db_repo.pull_requests_target)
189 self.db_repo.pull_requests_target)
190 h.flash(
190 h.flash(
191 _('Cannot delete `{repo}` it still contains {num} attached pull requests. '
191 _('Cannot delete `{repo}` it still contains {num} attached pull requests. '
192 'Consider archiving the repository instead.').format(
192 'Consider archiving the repository instead.').format(
193 repo=self.db_repo_name, num=attached_prs), category='warning')
193 repo=self.db_repo_name, num=attached_prs), category='warning')
194
194
195 # redirect to advanced for forks handle action ?
195 # redirect to advanced for forks handle action ?
196 raise HTTPFound(repo_advanced_url)
196 raise HTTPFound(repo_advanced_url)
197
197
198 except Exception:
198 except Exception:
199 log.exception("Exception during deletion of repository")
199 log.exception("Exception during deletion of repository")
200 h.flash(_('An error occurred during deletion of `%s`')
200 h.flash(_('An error occurred during deletion of `%s`')
201 % self.db_repo_name, category='error')
201 % self.db_repo_name, category='error')
202 # redirect to advanced for more deletion options
202 # redirect to advanced for more deletion options
203 raise HTTPFound(
203 raise HTTPFound(
204 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name,
204 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name,
205 _anchor='advanced-delete'))
205 _anchor='advanced-delete'))
206
206
207 raise HTTPFound(h.route_path('home'))
207 raise HTTPFound(h.route_path('home'))
208
208
209 @LoginRequired()
209 @LoginRequired()
210 @HasRepoPermissionAnyDecorator('repository.admin')
210 @HasRepoPermissionAnyDecorator('repository.admin')
211 @CSRFRequired()
211 @CSRFRequired()
212 @view_config(
212 @view_config(
213 route_name='edit_repo_advanced_journal', request_method='POST',
213 route_name='edit_repo_advanced_journal', request_method='POST',
214 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
214 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
215 def edit_advanced_journal(self):
215 def edit_advanced_journal(self):
216 """
216 """
217 Set's this repository to be visible in public journal,
217 Set's this repository to be visible in public journal,
218 in other words making default user to follow this repo
218 in other words making default user to follow this repo
219 """
219 """
220 _ = self.request.translate
220 _ = self.request.translate
221
221
222 try:
222 try:
223 user_id = User.get_default_user().user_id
223 user_id = User.get_default_user_id()
224 ScmModel().toggle_following_repo(self.db_repo.repo_id, user_id)
224 ScmModel().toggle_following_repo(self.db_repo.repo_id, user_id)
225 h.flash(_('Updated repository visibility in public journal'),
225 h.flash(_('Updated repository visibility in public journal'),
226 category='success')
226 category='success')
227 Session().commit()
227 Session().commit()
228 except Exception:
228 except Exception:
229 h.flash(_('An error occurred during setting this '
229 h.flash(_('An error occurred during setting this '
230 'repository in public journal'),
230 'repository in public journal'),
231 category='error')
231 category='error')
232
232
233 raise HTTPFound(
233 raise HTTPFound(
234 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
234 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
235
235
236 @LoginRequired()
236 @LoginRequired()
237 @HasRepoPermissionAnyDecorator('repository.admin')
237 @HasRepoPermissionAnyDecorator('repository.admin')
238 @CSRFRequired()
238 @CSRFRequired()
239 @view_config(
239 @view_config(
240 route_name='edit_repo_advanced_fork', request_method='POST',
240 route_name='edit_repo_advanced_fork', request_method='POST',
241 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
241 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
242 def edit_advanced_fork(self):
242 def edit_advanced_fork(self):
243 """
243 """
244 Mark given repository as a fork of another
244 Mark given repository as a fork of another
245 """
245 """
246 _ = self.request.translate
246 _ = self.request.translate
247
247
248 new_fork_id = safe_int(self.request.POST.get('id_fork_of'))
248 new_fork_id = safe_int(self.request.POST.get('id_fork_of'))
249
249
250 # valid repo, re-check permissions
250 # valid repo, re-check permissions
251 if new_fork_id:
251 if new_fork_id:
252 repo = Repository.get(new_fork_id)
252 repo = Repository.get(new_fork_id)
253 # ensure we have at least read access to the repo we mark
253 # ensure we have at least read access to the repo we mark
254 perm_check = HasRepoPermissionAny(
254 perm_check = HasRepoPermissionAny(
255 'repository.read', 'repository.write', 'repository.admin')
255 'repository.read', 'repository.write', 'repository.admin')
256
256
257 if repo and perm_check(repo_name=repo.repo_name):
257 if repo and perm_check(repo_name=repo.repo_name):
258 new_fork_id = repo.repo_id
258 new_fork_id = repo.repo_id
259 else:
259 else:
260 new_fork_id = None
260 new_fork_id = None
261
261
262 try:
262 try:
263 repo = ScmModel().mark_as_fork(
263 repo = ScmModel().mark_as_fork(
264 self.db_repo_name, new_fork_id, self._rhodecode_user.user_id)
264 self.db_repo_name, new_fork_id, self._rhodecode_user.user_id)
265 fork = repo.fork.repo_name if repo.fork else _('Nothing')
265 fork = repo.fork.repo_name if repo.fork else _('Nothing')
266 Session().commit()
266 Session().commit()
267 h.flash(
267 h.flash(
268 _('Marked repo %s as fork of %s') % (self.db_repo_name, fork),
268 _('Marked repo %s as fork of %s') % (self.db_repo_name, fork),
269 category='success')
269 category='success')
270 except RepositoryError as e:
270 except RepositoryError as e:
271 log.exception("Repository Error occurred")
271 log.exception("Repository Error occurred")
272 h.flash(str(e), category='error')
272 h.flash(str(e), category='error')
273 except Exception:
273 except Exception:
274 log.exception("Exception while editing fork")
274 log.exception("Exception while editing fork")
275 h.flash(_('An error occurred during this operation'),
275 h.flash(_('An error occurred during this operation'),
276 category='error')
276 category='error')
277
277
278 raise HTTPFound(
278 raise HTTPFound(
279 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
279 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
280
280
281 @LoginRequired()
281 @LoginRequired()
282 @HasRepoPermissionAnyDecorator('repository.admin')
282 @HasRepoPermissionAnyDecorator('repository.admin')
283 @CSRFRequired()
283 @CSRFRequired()
284 @view_config(
284 @view_config(
285 route_name='edit_repo_advanced_locking', request_method='POST',
285 route_name='edit_repo_advanced_locking', request_method='POST',
286 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
286 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
287 def edit_advanced_locking(self):
287 def edit_advanced_locking(self):
288 """
288 """
289 Toggle locking of repository
289 Toggle locking of repository
290 """
290 """
291 _ = self.request.translate
291 _ = self.request.translate
292 set_lock = self.request.POST.get('set_lock')
292 set_lock = self.request.POST.get('set_lock')
293 set_unlock = self.request.POST.get('set_unlock')
293 set_unlock = self.request.POST.get('set_unlock')
294
294
295 try:
295 try:
296 if set_lock:
296 if set_lock:
297 Repository.lock(self.db_repo, self._rhodecode_user.user_id,
297 Repository.lock(self.db_repo, self._rhodecode_user.user_id,
298 lock_reason=Repository.LOCK_WEB)
298 lock_reason=Repository.LOCK_WEB)
299 h.flash(_('Locked repository'), category='success')
299 h.flash(_('Locked repository'), category='success')
300 elif set_unlock:
300 elif set_unlock:
301 Repository.unlock(self.db_repo)
301 Repository.unlock(self.db_repo)
302 h.flash(_('Unlocked repository'), category='success')
302 h.flash(_('Unlocked repository'), category='success')
303 except Exception as e:
303 except Exception as e:
304 log.exception("Exception during unlocking")
304 log.exception("Exception during unlocking")
305 h.flash(_('An error occurred during unlocking'), category='error')
305 h.flash(_('An error occurred during unlocking'), category='error')
306
306
307 raise HTTPFound(
307 raise HTTPFound(
308 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
308 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
309
309
310 @LoginRequired()
310 @LoginRequired()
311 @HasRepoPermissionAnyDecorator('repository.admin')
311 @HasRepoPermissionAnyDecorator('repository.admin')
312 @view_config(
312 @view_config(
313 route_name='edit_repo_advanced_hooks', request_method='GET',
313 route_name='edit_repo_advanced_hooks', request_method='GET',
314 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
314 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
315 def edit_advanced_install_hooks(self):
315 def edit_advanced_install_hooks(self):
316 """
316 """
317 Install Hooks for repository
317 Install Hooks for repository
318 """
318 """
319 _ = self.request.translate
319 _ = self.request.translate
320 self.load_default_context()
320 self.load_default_context()
321 self.rhodecode_vcs_repo.install_hooks(force=True)
321 self.rhodecode_vcs_repo.install_hooks(force=True)
322 h.flash(_('installed updated hooks into this repository'),
322 h.flash(_('installed updated hooks into this repository'),
323 category='success')
323 category='success')
324 raise HTTPFound(
324 raise HTTPFound(
325 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
325 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
@@ -1,86 +1,87 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import logging
22 import logging
23 import rhodecode
23 import rhodecode
24
24
25 from rhodecode.config import utils
25 from rhodecode.config import utils
26
26
27 from rhodecode.lib.utils import load_rcextensions
27 from rhodecode.lib.utils import load_rcextensions
28 from rhodecode.lib.utils2 import str2bool
28 from rhodecode.lib.utils2 import str2bool
29 from rhodecode.lib.vcs import connect_vcs
29 from rhodecode.lib.vcs import connect_vcs
30
30
31 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
32
32
33
33
34 def load_pyramid_environment(global_config, settings):
34 def load_pyramid_environment(global_config, settings):
35 # Some parts of the code expect a merge of global and app settings.
35 # Some parts of the code expect a merge of global and app settings.
36 settings_merged = global_config.copy()
36 settings_merged = global_config.copy()
37 settings_merged.update(settings)
37 settings_merged.update(settings)
38
38
39 # TODO(marcink): probably not required anymore
39 # TODO(marcink): probably not required anymore
40 # configure channelstream,
40 # configure channelstream,
41 settings_merged['channelstream_config'] = {
41 settings_merged['channelstream_config'] = {
42 'enabled': str2bool(settings_merged.get('channelstream.enabled', False)),
42 'enabled': str2bool(settings_merged.get('channelstream.enabled', False)),
43 'server': settings_merged.get('channelstream.server'),
43 'server': settings_merged.get('channelstream.server'),
44 'secret': settings_merged.get('channelstream.secret')
44 'secret': settings_merged.get('channelstream.secret')
45 }
45 }
46
46
47 # If this is a test run we prepare the test environment like
47 # If this is a test run we prepare the test environment like
48 # creating a test database, test search index and test repositories.
48 # creating a test database, test search index and test repositories.
49 # This has to be done before the database connection is initialized.
49 # This has to be done before the database connection is initialized.
50 if settings['is_test']:
50 if settings['is_test']:
51 rhodecode.is_test = True
51 rhodecode.is_test = True
52 rhodecode.disable_error_handler = True
52 rhodecode.disable_error_handler = True
53 from rhodecode import authentication
53 from rhodecode import authentication
54 authentication.plugin_default_auth_ttl = 0
54 authentication.plugin_default_auth_ttl = 0
55
55
56 utils.initialize_test_environment(settings_merged)
56 utils.initialize_test_environment(settings_merged)
57
57
58 # Initialize the database connection.
58 # Initialize the database connection.
59 utils.initialize_database(settings_merged)
59 utils.initialize_database(settings_merged)
60
60
61 load_rcextensions(root_path=settings_merged['here'])
61 load_rcextensions(root_path=settings_merged['here'])
62
62
63 # Limit backends to `vcs.backends` from configuration, and preserve the order
63 # Limit backends to `vcs.backends` from configuration, and preserve the order
64 for alias in rhodecode.BACKENDS.keys():
64 for alias in rhodecode.BACKENDS.keys():
65 if alias not in settings['vcs.backends']:
65 if alias not in settings['vcs.backends']:
66 del rhodecode.BACKENDS[alias]
66 del rhodecode.BACKENDS[alias]
67
67
68 def sorter(item):
68 _sorted_backend = sorted(rhodecode.BACKENDS.items(),
69 return settings['vcs.backends'].index(item[0])
69 key=lambda item: settings['vcs.backends'].index(item[0]))
70 rhodecode.BACKENDS = rhodecode.OrderedDict(sorted(rhodecode.BACKENDS.items(), key=sorter))
70 rhodecode.BACKENDS = rhodecode.OrderedDict(_sorted_backend)
71
71
72 log.info('Enabled VCS backends: %s', rhodecode.BACKENDS.keys())
72 log.info('Enabled VCS backends: %s', rhodecode.BACKENDS.keys())
73
73
74 # initialize vcs client and optionally run the server if enabled
74 # initialize vcs client and optionally run the server if enabled
75 vcs_server_uri = settings['vcs.server']
75 vcs_server_uri = settings['vcs.server']
76 vcs_server_enabled = settings['vcs.server.enable']
76 vcs_server_enabled = settings['vcs.server.enable']
77
77
78 utils.configure_vcs(settings)
78 utils.configure_vcs(settings)
79
79
80 # Store the settings to make them available to other modules.
80 # Store the settings to make them available to other modules.
81
81
82 rhodecode.PYRAMID_SETTINGS = settings_merged
82 rhodecode.PYRAMID_SETTINGS = settings_merged
83 rhodecode.CONFIG = settings_merged
83 rhodecode.CONFIG = settings_merged
84 rhodecode.CONFIG['default_user_id'] = utils.get_default_user_id()
84
85
85 if vcs_server_enabled:
86 if vcs_server_enabled:
86 connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(settings))
87 connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(settings))
@@ -1,93 +1,101 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import shlex
23 import platform
22 import platform
24
23
25 from rhodecode.model import init_model
24 from rhodecode.model import init_model
26
25
27
26
28 def configure_vcs(config):
27 def configure_vcs(config):
29 """
28 """
30 Patch VCS config with some RhodeCode specific stuff
29 Patch VCS config with some RhodeCode specific stuff
31 """
30 """
32 from rhodecode.lib.vcs import conf
31 from rhodecode.lib.vcs import conf
33 import rhodecode.lib.vcs.conf.settings
32 import rhodecode.lib.vcs.conf.settings
34
33
35 conf.settings.BACKENDS = {
34 conf.settings.BACKENDS = {
36 'hg': 'rhodecode.lib.vcs.backends.hg.MercurialRepository',
35 'hg': 'rhodecode.lib.vcs.backends.hg.MercurialRepository',
37 'git': 'rhodecode.lib.vcs.backends.git.GitRepository',
36 'git': 'rhodecode.lib.vcs.backends.git.GitRepository',
38 'svn': 'rhodecode.lib.vcs.backends.svn.SubversionRepository',
37 'svn': 'rhodecode.lib.vcs.backends.svn.SubversionRepository',
39 }
38 }
40
39
41 conf.settings.HOOKS_PROTOCOL = config['vcs.hooks.protocol']
40 conf.settings.HOOKS_PROTOCOL = config['vcs.hooks.protocol']
42 conf.settings.HOOKS_HOST = config['vcs.hooks.host']
41 conf.settings.HOOKS_HOST = config['vcs.hooks.host']
43 conf.settings.HOOKS_DIRECT_CALLS = config['vcs.hooks.direct_calls']
42 conf.settings.HOOKS_DIRECT_CALLS = config['vcs.hooks.direct_calls']
44 conf.settings.DEFAULT_ENCODINGS = config['default_encoding']
43 conf.settings.DEFAULT_ENCODINGS = config['default_encoding']
45 conf.settings.ALIASES[:] = config['vcs.backends']
44 conf.settings.ALIASES[:] = config['vcs.backends']
46 conf.settings.SVN_COMPATIBLE_VERSION = config['vcs.svn.compatible_version']
45 conf.settings.SVN_COMPATIBLE_VERSION = config['vcs.svn.compatible_version']
47
46
48
47
49 def initialize_database(config):
48 def initialize_database(config):
50 from rhodecode.lib.utils2 import engine_from_config, get_encryption_key
49 from rhodecode.lib.utils2 import engine_from_config, get_encryption_key
51 engine = engine_from_config(config, 'sqlalchemy.db1.')
50 engine = engine_from_config(config, 'sqlalchemy.db1.')
52 init_model(engine, encryption_key=get_encryption_key(config))
51 init_model(engine, encryption_key=get_encryption_key(config))
53
52
54
53
55 def initialize_test_environment(settings, test_env=None):
54 def initialize_test_environment(settings, test_env=None):
56 if test_env is None:
55 if test_env is None:
57 test_env = not int(os.environ.get('RC_NO_TMP_PATH', 0))
56 test_env = not int(os.environ.get('RC_NO_TMP_PATH', 0))
58
57
59 from rhodecode.lib.utils import (
58 from rhodecode.lib.utils import (
60 create_test_directory, create_test_database, create_test_repositories,
59 create_test_directory, create_test_database, create_test_repositories,
61 create_test_index)
60 create_test_index)
62 from rhodecode.tests import TESTS_TMP_PATH
61 from rhodecode.tests import TESTS_TMP_PATH
63 from rhodecode.lib.vcs.backends.hg import largefiles_store
62 from rhodecode.lib.vcs.backends.hg import largefiles_store
64 from rhodecode.lib.vcs.backends.git import lfs_store
63 from rhodecode.lib.vcs.backends.git import lfs_store
65
64
66 # test repos
65 # test repos
67 if test_env:
66 if test_env:
68 create_test_directory(TESTS_TMP_PATH)
67 create_test_directory(TESTS_TMP_PATH)
69 # large object stores
68 # large object stores
70 create_test_directory(largefiles_store(TESTS_TMP_PATH))
69 create_test_directory(largefiles_store(TESTS_TMP_PATH))
71 create_test_directory(lfs_store(TESTS_TMP_PATH))
70 create_test_directory(lfs_store(TESTS_TMP_PATH))
72
71
73 create_test_database(TESTS_TMP_PATH, settings)
72 create_test_database(TESTS_TMP_PATH, settings)
74 create_test_repositories(TESTS_TMP_PATH, settings)
73 create_test_repositories(TESTS_TMP_PATH, settings)
75 create_test_index(TESTS_TMP_PATH, settings)
74 create_test_index(TESTS_TMP_PATH, settings)
76
75
77
76
78 def get_vcs_server_protocol(config):
77 def get_vcs_server_protocol(config):
79 return config['vcs.server.protocol']
78 return config['vcs.server.protocol']
80
79
81
80
82 def set_instance_id(config):
81 def set_instance_id(config):
83 """
82 """
84 Sets a dynamic generated config['instance_id'] if missing or '*'
83 Sets a dynamic generated config['instance_id'] if missing or '*'
85 E.g instance_id = *cluster-1 or instance_id = *
84 E.g instance_id = *cluster-1 or instance_id = *
86 """
85 """
87
86
88 config['instance_id'] = config.get('instance_id') or ''
87 config['instance_id'] = config.get('instance_id') or ''
89 instance_id = config['instance_id']
88 instance_id = config['instance_id']
90 if instance_id.startswith('*') or not instance_id:
89 if instance_id.startswith('*') or not instance_id:
91 prefix = instance_id.lstrip('*')
90 prefix = instance_id.lstrip('*')
92 _platform_id = platform.uname()[1] or 'instance'
91 _platform_id = platform.uname()[1] or 'instance'
93 config['instance_id'] = '%s%s-%s' % (prefix, _platform_id, os.getpid())
92 config['instance_id'] = '%s%s-%s' % (prefix, _platform_id, os.getpid())
93
94
95 def get_default_user_id():
96 from rhodecode.model.db import User, Session
97 user_id = Session()\
98 .query(User.user_id)\
99 .filter(User.username == User.DEFAULT_USER)\
100 .scalar()
101 return user_id
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,597 +1,597 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 permissions model for RhodeCode
22 permissions model for RhodeCode
23 """
23 """
24 import collections
24 import collections
25 import logging
25 import logging
26 import traceback
26 import traceback
27
27
28 from sqlalchemy.exc import DatabaseError
28 from sqlalchemy.exc import DatabaseError
29
29
30 from rhodecode import events
30 from rhodecode import events
31 from rhodecode.model import BaseModel
31 from rhodecode.model import BaseModel
32 from rhodecode.model.db import (
32 from rhodecode.model.db import (
33 User, Permission, UserToPerm, UserRepoToPerm, UserRepoGroupToPerm,
33 User, Permission, UserToPerm, UserRepoToPerm, UserRepoGroupToPerm,
34 UserUserGroupToPerm, UserGroup, UserGroupToPerm, UserToRepoBranchPermission)
34 UserUserGroupToPerm, UserGroup, UserGroupToPerm, UserToRepoBranchPermission)
35 from rhodecode.lib.utils2 import str2bool, safe_int
35 from rhodecode.lib.utils2 import str2bool, safe_int
36
36
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39
39
40 class PermissionModel(BaseModel):
40 class PermissionModel(BaseModel):
41 """
41 """
42 Permissions model for RhodeCode
42 Permissions model for RhodeCode
43 """
43 """
44
44
45 cls = Permission
45 cls = Permission
46 global_perms = {
46 global_perms = {
47 'default_repo_create': None,
47 'default_repo_create': None,
48 # special case for create repos on write access to group
48 # special case for create repos on write access to group
49 'default_repo_create_on_write': None,
49 'default_repo_create_on_write': None,
50 'default_repo_group_create': None,
50 'default_repo_group_create': None,
51 'default_user_group_create': None,
51 'default_user_group_create': None,
52 'default_fork_create': None,
52 'default_fork_create': None,
53 'default_inherit_default_permissions': None,
53 'default_inherit_default_permissions': None,
54 'default_register': None,
54 'default_register': None,
55 'default_password_reset': None,
55 'default_password_reset': None,
56 'default_extern_activate': None,
56 'default_extern_activate': None,
57
57
58 # object permissions below
58 # object permissions below
59 'default_repo_perm': None,
59 'default_repo_perm': None,
60 'default_group_perm': None,
60 'default_group_perm': None,
61 'default_user_group_perm': None,
61 'default_user_group_perm': None,
62
62
63 # branch
63 # branch
64 'default_branch_perm': None,
64 'default_branch_perm': None,
65 }
65 }
66
66
67 def set_global_permission_choices(self, c_obj, gettext_translator):
67 def set_global_permission_choices(self, c_obj, gettext_translator):
68 _ = gettext_translator
68 _ = gettext_translator
69
69
70 c_obj.repo_perms_choices = [
70 c_obj.repo_perms_choices = [
71 ('repository.none', _('None'),),
71 ('repository.none', _('None'),),
72 ('repository.read', _('Read'),),
72 ('repository.read', _('Read'),),
73 ('repository.write', _('Write'),),
73 ('repository.write', _('Write'),),
74 ('repository.admin', _('Admin'),)]
74 ('repository.admin', _('Admin'),)]
75
75
76 c_obj.group_perms_choices = [
76 c_obj.group_perms_choices = [
77 ('group.none', _('None'),),
77 ('group.none', _('None'),),
78 ('group.read', _('Read'),),
78 ('group.read', _('Read'),),
79 ('group.write', _('Write'),),
79 ('group.write', _('Write'),),
80 ('group.admin', _('Admin'),)]
80 ('group.admin', _('Admin'),)]
81
81
82 c_obj.user_group_perms_choices = [
82 c_obj.user_group_perms_choices = [
83 ('usergroup.none', _('None'),),
83 ('usergroup.none', _('None'),),
84 ('usergroup.read', _('Read'),),
84 ('usergroup.read', _('Read'),),
85 ('usergroup.write', _('Write'),),
85 ('usergroup.write', _('Write'),),
86 ('usergroup.admin', _('Admin'),)]
86 ('usergroup.admin', _('Admin'),)]
87
87
88 c_obj.branch_perms_choices = [
88 c_obj.branch_perms_choices = [
89 ('branch.none', _('Protected/No Access'),),
89 ('branch.none', _('Protected/No Access'),),
90 ('branch.merge', _('Web merge'),),
90 ('branch.merge', _('Web merge'),),
91 ('branch.push', _('Push'),),
91 ('branch.push', _('Push'),),
92 ('branch.push_force', _('Force Push'),)]
92 ('branch.push_force', _('Force Push'),)]
93
93
94 c_obj.register_choices = [
94 c_obj.register_choices = [
95 ('hg.register.none', _('Disabled')),
95 ('hg.register.none', _('Disabled')),
96 ('hg.register.manual_activate', _('Allowed with manual account activation')),
96 ('hg.register.manual_activate', _('Allowed with manual account activation')),
97 ('hg.register.auto_activate', _('Allowed with automatic account activation')),]
97 ('hg.register.auto_activate', _('Allowed with automatic account activation')),]
98
98
99 c_obj.password_reset_choices = [
99 c_obj.password_reset_choices = [
100 ('hg.password_reset.enabled', _('Allow password recovery')),
100 ('hg.password_reset.enabled', _('Allow password recovery')),
101 ('hg.password_reset.hidden', _('Hide password recovery link')),
101 ('hg.password_reset.hidden', _('Hide password recovery link')),
102 ('hg.password_reset.disabled', _('Disable password recovery')),]
102 ('hg.password_reset.disabled', _('Disable password recovery')),]
103
103
104 c_obj.extern_activate_choices = [
104 c_obj.extern_activate_choices = [
105 ('hg.extern_activate.manual', _('Manual activation of external account')),
105 ('hg.extern_activate.manual', _('Manual activation of external account')),
106 ('hg.extern_activate.auto', _('Automatic activation of external account')),]
106 ('hg.extern_activate.auto', _('Automatic activation of external account')),]
107
107
108 c_obj.repo_create_choices = [
108 c_obj.repo_create_choices = [
109 ('hg.create.none', _('Disabled')),
109 ('hg.create.none', _('Disabled')),
110 ('hg.create.repository', _('Enabled'))]
110 ('hg.create.repository', _('Enabled'))]
111
111
112 c_obj.repo_create_on_write_choices = [
112 c_obj.repo_create_on_write_choices = [
113 ('hg.create.write_on_repogroup.false', _('Disabled')),
113 ('hg.create.write_on_repogroup.false', _('Disabled')),
114 ('hg.create.write_on_repogroup.true', _('Enabled'))]
114 ('hg.create.write_on_repogroup.true', _('Enabled'))]
115
115
116 c_obj.user_group_create_choices = [
116 c_obj.user_group_create_choices = [
117 ('hg.usergroup.create.false', _('Disabled')),
117 ('hg.usergroup.create.false', _('Disabled')),
118 ('hg.usergroup.create.true', _('Enabled'))]
118 ('hg.usergroup.create.true', _('Enabled'))]
119
119
120 c_obj.repo_group_create_choices = [
120 c_obj.repo_group_create_choices = [
121 ('hg.repogroup.create.false', _('Disabled')),
121 ('hg.repogroup.create.false', _('Disabled')),
122 ('hg.repogroup.create.true', _('Enabled'))]
122 ('hg.repogroup.create.true', _('Enabled'))]
123
123
124 c_obj.fork_choices = [
124 c_obj.fork_choices = [
125 ('hg.fork.none', _('Disabled')),
125 ('hg.fork.none', _('Disabled')),
126 ('hg.fork.repository', _('Enabled'))]
126 ('hg.fork.repository', _('Enabled'))]
127
127
128 c_obj.inherit_default_permission_choices = [
128 c_obj.inherit_default_permission_choices = [
129 ('hg.inherit_default_perms.false', _('Disabled')),
129 ('hg.inherit_default_perms.false', _('Disabled')),
130 ('hg.inherit_default_perms.true', _('Enabled'))]
130 ('hg.inherit_default_perms.true', _('Enabled'))]
131
131
132 def get_default_perms(self, object_perms, suffix):
132 def get_default_perms(self, object_perms, suffix):
133 defaults = {}
133 defaults = {}
134 for perm in object_perms:
134 for perm in object_perms:
135 # perms
135 # perms
136 if perm.permission.permission_name.startswith('repository.'):
136 if perm.permission.permission_name.startswith('repository.'):
137 defaults['default_repo_perm' + suffix] = perm.permission.permission_name
137 defaults['default_repo_perm' + suffix] = perm.permission.permission_name
138
138
139 if perm.permission.permission_name.startswith('group.'):
139 if perm.permission.permission_name.startswith('group.'):
140 defaults['default_group_perm' + suffix] = perm.permission.permission_name
140 defaults['default_group_perm' + suffix] = perm.permission.permission_name
141
141
142 if perm.permission.permission_name.startswith('usergroup.'):
142 if perm.permission.permission_name.startswith('usergroup.'):
143 defaults['default_user_group_perm' + suffix] = perm.permission.permission_name
143 defaults['default_user_group_perm' + suffix] = perm.permission.permission_name
144
144
145 # branch
145 # branch
146 if perm.permission.permission_name.startswith('branch.'):
146 if perm.permission.permission_name.startswith('branch.'):
147 defaults['default_branch_perm' + suffix] = perm.permission.permission_name
147 defaults['default_branch_perm' + suffix] = perm.permission.permission_name
148
148
149 # creation of objects
149 # creation of objects
150 if perm.permission.permission_name.startswith('hg.create.write_on_repogroup'):
150 if perm.permission.permission_name.startswith('hg.create.write_on_repogroup'):
151 defaults['default_repo_create_on_write' + suffix] = perm.permission.permission_name
151 defaults['default_repo_create_on_write' + suffix] = perm.permission.permission_name
152
152
153 elif perm.permission.permission_name.startswith('hg.create.'):
153 elif perm.permission.permission_name.startswith('hg.create.'):
154 defaults['default_repo_create' + suffix] = perm.permission.permission_name
154 defaults['default_repo_create' + suffix] = perm.permission.permission_name
155
155
156 if perm.permission.permission_name.startswith('hg.fork.'):
156 if perm.permission.permission_name.startswith('hg.fork.'):
157 defaults['default_fork_create' + suffix] = perm.permission.permission_name
157 defaults['default_fork_create' + suffix] = perm.permission.permission_name
158
158
159 if perm.permission.permission_name.startswith('hg.inherit_default_perms.'):
159 if perm.permission.permission_name.startswith('hg.inherit_default_perms.'):
160 defaults['default_inherit_default_permissions' + suffix] = perm.permission.permission_name
160 defaults['default_inherit_default_permissions' + suffix] = perm.permission.permission_name
161
161
162 if perm.permission.permission_name.startswith('hg.repogroup.'):
162 if perm.permission.permission_name.startswith('hg.repogroup.'):
163 defaults['default_repo_group_create' + suffix] = perm.permission.permission_name
163 defaults['default_repo_group_create' + suffix] = perm.permission.permission_name
164
164
165 if perm.permission.permission_name.startswith('hg.usergroup.'):
165 if perm.permission.permission_name.startswith('hg.usergroup.'):
166 defaults['default_user_group_create' + suffix] = perm.permission.permission_name
166 defaults['default_user_group_create' + suffix] = perm.permission.permission_name
167
167
168 # registration and external account activation
168 # registration and external account activation
169 if perm.permission.permission_name.startswith('hg.register.'):
169 if perm.permission.permission_name.startswith('hg.register.'):
170 defaults['default_register' + suffix] = perm.permission.permission_name
170 defaults['default_register' + suffix] = perm.permission.permission_name
171
171
172 if perm.permission.permission_name.startswith('hg.password_reset.'):
172 if perm.permission.permission_name.startswith('hg.password_reset.'):
173 defaults['default_password_reset' + suffix] = perm.permission.permission_name
173 defaults['default_password_reset' + suffix] = perm.permission.permission_name
174
174
175 if perm.permission.permission_name.startswith('hg.extern_activate.'):
175 if perm.permission.permission_name.startswith('hg.extern_activate.'):
176 defaults['default_extern_activate' + suffix] = perm.permission.permission_name
176 defaults['default_extern_activate' + suffix] = perm.permission.permission_name
177
177
178 return defaults
178 return defaults
179
179
180 def _make_new_user_perm(self, user, perm_name):
180 def _make_new_user_perm(self, user, perm_name):
181 log.debug('Creating new user permission:%s', perm_name)
181 log.debug('Creating new user permission:%s', perm_name)
182 new = UserToPerm()
182 new = UserToPerm()
183 new.user = user
183 new.user = user
184 new.permission = Permission.get_by_key(perm_name)
184 new.permission = Permission.get_by_key(perm_name)
185 return new
185 return new
186
186
187 def _make_new_user_group_perm(self, user_group, perm_name):
187 def _make_new_user_group_perm(self, user_group, perm_name):
188 log.debug('Creating new user group permission:%s', perm_name)
188 log.debug('Creating new user group permission:%s', perm_name)
189 new = UserGroupToPerm()
189 new = UserGroupToPerm()
190 new.users_group = user_group
190 new.users_group = user_group
191 new.permission = Permission.get_by_key(perm_name)
191 new.permission = Permission.get_by_key(perm_name)
192 return new
192 return new
193
193
194 def _keep_perm(self, perm_name, keep_fields):
194 def _keep_perm(self, perm_name, keep_fields):
195 def get_pat(field_name):
195 def get_pat(field_name):
196 return {
196 return {
197 # global perms
197 # global perms
198 'default_repo_create': 'hg.create.',
198 'default_repo_create': 'hg.create.',
199 # special case for create repos on write access to group
199 # special case for create repos on write access to group
200 'default_repo_create_on_write': 'hg.create.write_on_repogroup.',
200 'default_repo_create_on_write': 'hg.create.write_on_repogroup.',
201 'default_repo_group_create': 'hg.repogroup.create.',
201 'default_repo_group_create': 'hg.repogroup.create.',
202 'default_user_group_create': 'hg.usergroup.create.',
202 'default_user_group_create': 'hg.usergroup.create.',
203 'default_fork_create': 'hg.fork.',
203 'default_fork_create': 'hg.fork.',
204 'default_inherit_default_permissions': 'hg.inherit_default_perms.',
204 'default_inherit_default_permissions': 'hg.inherit_default_perms.',
205
205
206 # application perms
206 # application perms
207 'default_register': 'hg.register.',
207 'default_register': 'hg.register.',
208 'default_password_reset': 'hg.password_reset.',
208 'default_password_reset': 'hg.password_reset.',
209 'default_extern_activate': 'hg.extern_activate.',
209 'default_extern_activate': 'hg.extern_activate.',
210
210
211 # object permissions below
211 # object permissions below
212 'default_repo_perm': 'repository.',
212 'default_repo_perm': 'repository.',
213 'default_group_perm': 'group.',
213 'default_group_perm': 'group.',
214 'default_user_group_perm': 'usergroup.',
214 'default_user_group_perm': 'usergroup.',
215 # branch
215 # branch
216 'default_branch_perm': 'branch.',
216 'default_branch_perm': 'branch.',
217
217
218 }[field_name]
218 }[field_name]
219 for field in keep_fields:
219 for field in keep_fields:
220 pat = get_pat(field)
220 pat = get_pat(field)
221 if perm_name.startswith(pat):
221 if perm_name.startswith(pat):
222 return True
222 return True
223 return False
223 return False
224
224
225 def _clear_object_perm(self, object_perms, preserve=None):
225 def _clear_object_perm(self, object_perms, preserve=None):
226 preserve = preserve or []
226 preserve = preserve or []
227 _deleted = []
227 _deleted = []
228 for perm in object_perms:
228 for perm in object_perms:
229 perm_name = perm.permission.permission_name
229 perm_name = perm.permission.permission_name
230 if not self._keep_perm(perm_name, keep_fields=preserve):
230 if not self._keep_perm(perm_name, keep_fields=preserve):
231 _deleted.append(perm_name)
231 _deleted.append(perm_name)
232 self.sa.delete(perm)
232 self.sa.delete(perm)
233 return _deleted
233 return _deleted
234
234
235 def _clear_user_perms(self, user_id, preserve=None):
235 def _clear_user_perms(self, user_id, preserve=None):
236 perms = self.sa.query(UserToPerm)\
236 perms = self.sa.query(UserToPerm)\
237 .filter(UserToPerm.user_id == user_id)\
237 .filter(UserToPerm.user_id == user_id)\
238 .all()
238 .all()
239 return self._clear_object_perm(perms, preserve=preserve)
239 return self._clear_object_perm(perms, preserve=preserve)
240
240
241 def _clear_user_group_perms(self, user_group_id, preserve=None):
241 def _clear_user_group_perms(self, user_group_id, preserve=None):
242 perms = self.sa.query(UserGroupToPerm)\
242 perms = self.sa.query(UserGroupToPerm)\
243 .filter(UserGroupToPerm.users_group_id == user_group_id)\
243 .filter(UserGroupToPerm.users_group_id == user_group_id)\
244 .all()
244 .all()
245 return self._clear_object_perm(perms, preserve=preserve)
245 return self._clear_object_perm(perms, preserve=preserve)
246
246
247 def _set_new_object_perms(self, obj_type, object, form_result, preserve=None):
247 def _set_new_object_perms(self, obj_type, object, form_result, preserve=None):
248 # clear current entries, to make this function idempotent
248 # clear current entries, to make this function idempotent
249 # it will fix even if we define more permissions or permissions
249 # it will fix even if we define more permissions or permissions
250 # are somehow missing
250 # are somehow missing
251 preserve = preserve or []
251 preserve = preserve or []
252 _global_perms = self.global_perms.copy()
252 _global_perms = self.global_perms.copy()
253 if obj_type not in ['user', 'user_group']:
253 if obj_type not in ['user', 'user_group']:
254 raise ValueError("obj_type must be on of 'user' or 'user_group'")
254 raise ValueError("obj_type must be on of 'user' or 'user_group'")
255 global_perms = len(_global_perms)
255 global_perms = len(_global_perms)
256 default_user_perms = len(Permission.DEFAULT_USER_PERMISSIONS)
256 default_user_perms = len(Permission.DEFAULT_USER_PERMISSIONS)
257 if global_perms != default_user_perms:
257 if global_perms != default_user_perms:
258 raise Exception(
258 raise Exception(
259 'Inconsistent permissions definition. Got {} vs {}'.format(
259 'Inconsistent permissions definition. Got {} vs {}'.format(
260 global_perms, default_user_perms))
260 global_perms, default_user_perms))
261
261
262 if obj_type == 'user':
262 if obj_type == 'user':
263 self._clear_user_perms(object.user_id, preserve)
263 self._clear_user_perms(object.user_id, preserve)
264 if obj_type == 'user_group':
264 if obj_type == 'user_group':
265 self._clear_user_group_perms(object.users_group_id, preserve)
265 self._clear_user_group_perms(object.users_group_id, preserve)
266
266
267 # now kill the keys that we want to preserve from the form.
267 # now kill the keys that we want to preserve from the form.
268 for key in preserve:
268 for key in preserve:
269 del _global_perms[key]
269 del _global_perms[key]
270
270
271 for k in _global_perms.copy():
271 for k in _global_perms.copy():
272 _global_perms[k] = form_result[k]
272 _global_perms[k] = form_result[k]
273
273
274 # at that stage we validate all are passed inside form_result
274 # at that stage we validate all are passed inside form_result
275 for _perm_key, perm_value in _global_perms.items():
275 for _perm_key, perm_value in _global_perms.items():
276 if perm_value is None:
276 if perm_value is None:
277 raise ValueError('Missing permission for %s' % (_perm_key,))
277 raise ValueError('Missing permission for %s' % (_perm_key,))
278
278
279 if obj_type == 'user':
279 if obj_type == 'user':
280 p = self._make_new_user_perm(object, perm_value)
280 p = self._make_new_user_perm(object, perm_value)
281 self.sa.add(p)
281 self.sa.add(p)
282 if obj_type == 'user_group':
282 if obj_type == 'user_group':
283 p = self._make_new_user_group_perm(object, perm_value)
283 p = self._make_new_user_group_perm(object, perm_value)
284 self.sa.add(p)
284 self.sa.add(p)
285
285
286 def _set_new_user_perms(self, user, form_result, preserve=None):
286 def _set_new_user_perms(self, user, form_result, preserve=None):
287 return self._set_new_object_perms(
287 return self._set_new_object_perms(
288 'user', user, form_result, preserve)
288 'user', user, form_result, preserve)
289
289
290 def _set_new_user_group_perms(self, user_group, form_result, preserve=None):
290 def _set_new_user_group_perms(self, user_group, form_result, preserve=None):
291 return self._set_new_object_perms(
291 return self._set_new_object_perms(
292 'user_group', user_group, form_result, preserve)
292 'user_group', user_group, form_result, preserve)
293
293
294 def set_new_user_perms(self, user, form_result):
294 def set_new_user_perms(self, user, form_result):
295 # calculate what to preserve from what is given in form_result
295 # calculate what to preserve from what is given in form_result
296 preserve = set(self.global_perms.keys()).difference(set(form_result.keys()))
296 preserve = set(self.global_perms.keys()).difference(set(form_result.keys()))
297 return self._set_new_user_perms(user, form_result, preserve)
297 return self._set_new_user_perms(user, form_result, preserve)
298
298
299 def set_new_user_group_perms(self, user_group, form_result):
299 def set_new_user_group_perms(self, user_group, form_result):
300 # calculate what to preserve from what is given in form_result
300 # calculate what to preserve from what is given in form_result
301 preserve = set(self.global_perms.keys()).difference(set(form_result.keys()))
301 preserve = set(self.global_perms.keys()).difference(set(form_result.keys()))
302 return self._set_new_user_group_perms(user_group, form_result, preserve)
302 return self._set_new_user_group_perms(user_group, form_result, preserve)
303
303
304 def create_permissions(self):
304 def create_permissions(self):
305 """
305 """
306 Create permissions for whole system
306 Create permissions for whole system
307 """
307 """
308 for p in Permission.PERMS:
308 for p in Permission.PERMS:
309 if not Permission.get_by_key(p[0]):
309 if not Permission.get_by_key(p[0]):
310 new_perm = Permission()
310 new_perm = Permission()
311 new_perm.permission_name = p[0]
311 new_perm.permission_name = p[0]
312 new_perm.permission_longname = p[0] # translation err with p[1]
312 new_perm.permission_longname = p[0] # translation err with p[1]
313 self.sa.add(new_perm)
313 self.sa.add(new_perm)
314
314
315 def _create_default_object_permission(self, obj_type, obj, obj_perms,
315 def _create_default_object_permission(self, obj_type, obj, obj_perms,
316 force=False):
316 force=False):
317 if obj_type not in ['user', 'user_group']:
317 if obj_type not in ['user', 'user_group']:
318 raise ValueError("obj_type must be on of 'user' or 'user_group'")
318 raise ValueError("obj_type must be on of 'user' or 'user_group'")
319
319
320 def _get_group(perm_name):
320 def _get_group(perm_name):
321 return '.'.join(perm_name.split('.')[:1])
321 return '.'.join(perm_name.split('.')[:1])
322
322
323 defined_perms_groups = map(
323 defined_perms_groups = map(
324 _get_group, (x.permission.permission_name for x in obj_perms))
324 _get_group, (x.permission.permission_name for x in obj_perms))
325 log.debug('GOT ALREADY DEFINED:%s', obj_perms)
325 log.debug('GOT ALREADY DEFINED:%s', obj_perms)
326
326
327 if force:
327 if force:
328 self._clear_object_perm(obj_perms)
328 self._clear_object_perm(obj_perms)
329 self.sa.commit()
329 self.sa.commit()
330 defined_perms_groups = []
330 defined_perms_groups = []
331 # for every default permission that needs to be created, we check if
331 # for every default permission that needs to be created, we check if
332 # it's group is already defined, if it's not we create default perm
332 # it's group is already defined, if it's not we create default perm
333 for perm_name in Permission.DEFAULT_USER_PERMISSIONS:
333 for perm_name in Permission.DEFAULT_USER_PERMISSIONS:
334 gr = _get_group(perm_name)
334 gr = _get_group(perm_name)
335 if gr not in defined_perms_groups:
335 if gr not in defined_perms_groups:
336 log.debug('GR:%s not found, creating permission %s',
336 log.debug('GR:%s not found, creating permission %s',
337 gr, perm_name)
337 gr, perm_name)
338 if obj_type == 'user':
338 if obj_type == 'user':
339 new_perm = self._make_new_user_perm(obj, perm_name)
339 new_perm = self._make_new_user_perm(obj, perm_name)
340 self.sa.add(new_perm)
340 self.sa.add(new_perm)
341 if obj_type == 'user_group':
341 if obj_type == 'user_group':
342 new_perm = self._make_new_user_group_perm(obj, perm_name)
342 new_perm = self._make_new_user_group_perm(obj, perm_name)
343 self.sa.add(new_perm)
343 self.sa.add(new_perm)
344
344
345 def create_default_user_permissions(self, user, force=False):
345 def create_default_user_permissions(self, user, force=False):
346 """
346 """
347 Creates only missing default permissions for user, if force is set it
347 Creates only missing default permissions for user, if force is set it
348 resets the default permissions for that user
348 resets the default permissions for that user
349
349
350 :param user:
350 :param user:
351 :param force:
351 :param force:
352 """
352 """
353 user = self._get_user(user)
353 user = self._get_user(user)
354 obj_perms = UserToPerm.query().filter(UserToPerm.user == user).all()
354 obj_perms = UserToPerm.query().filter(UserToPerm.user == user).all()
355 return self._create_default_object_permission(
355 return self._create_default_object_permission(
356 'user', user, obj_perms, force)
356 'user', user, obj_perms, force)
357
357
358 def create_default_user_group_permissions(self, user_group, force=False):
358 def create_default_user_group_permissions(self, user_group, force=False):
359 """
359 """
360 Creates only missing default permissions for user group, if force is
360 Creates only missing default permissions for user group, if force is
361 set it resets the default permissions for that user group
361 set it resets the default permissions for that user group
362
362
363 :param user_group:
363 :param user_group:
364 :param force:
364 :param force:
365 """
365 """
366 user_group = self._get_user_group(user_group)
366 user_group = self._get_user_group(user_group)
367 obj_perms = UserToPerm.query().filter(UserGroupToPerm.users_group == user_group).all()
367 obj_perms = UserToPerm.query().filter(UserGroupToPerm.users_group == user_group).all()
368 return self._create_default_object_permission(
368 return self._create_default_object_permission(
369 'user_group', user_group, obj_perms, force)
369 'user_group', user_group, obj_perms, force)
370
370
371 def update_application_permissions(self, form_result):
371 def update_application_permissions(self, form_result):
372 if 'perm_user_id' in form_result:
372 if 'perm_user_id' in form_result:
373 perm_user = User.get(safe_int(form_result['perm_user_id']))
373 perm_user = User.get(safe_int(form_result['perm_user_id']))
374 else:
374 else:
375 # used mostly to do lookup for default user
375 # used mostly to do lookup for default user
376 perm_user = User.get_by_username(form_result['perm_user_name'])
376 perm_user = User.get_by_username(form_result['perm_user_name'])
377
377
378 try:
378 try:
379 # stage 1 set anonymous access
379 # stage 1 set anonymous access
380 if perm_user.username == User.DEFAULT_USER:
380 if perm_user.username == User.DEFAULT_USER:
381 perm_user.active = str2bool(form_result['anonymous'])
381 perm_user.active = str2bool(form_result['anonymous'])
382 self.sa.add(perm_user)
382 self.sa.add(perm_user)
383
383
384 # stage 2 reset defaults and set them from form data
384 # stage 2 reset defaults and set them from form data
385 self._set_new_user_perms(perm_user, form_result, preserve=[
385 self._set_new_user_perms(perm_user, form_result, preserve=[
386 'default_repo_perm',
386 'default_repo_perm',
387 'default_group_perm',
387 'default_group_perm',
388 'default_user_group_perm',
388 'default_user_group_perm',
389 'default_branch_perm',
389 'default_branch_perm',
390
390
391 'default_repo_group_create',
391 'default_repo_group_create',
392 'default_user_group_create',
392 'default_user_group_create',
393 'default_repo_create_on_write',
393 'default_repo_create_on_write',
394 'default_repo_create',
394 'default_repo_create',
395 'default_fork_create',
395 'default_fork_create',
396 'default_inherit_default_permissions',])
396 'default_inherit_default_permissions',])
397
397
398 self.sa.commit()
398 self.sa.commit()
399 except (DatabaseError,):
399 except (DatabaseError,):
400 log.error(traceback.format_exc())
400 log.error(traceback.format_exc())
401 self.sa.rollback()
401 self.sa.rollback()
402 raise
402 raise
403
403
404 def update_user_permissions(self, form_result):
404 def update_user_permissions(self, form_result):
405 if 'perm_user_id' in form_result:
405 if 'perm_user_id' in form_result:
406 perm_user = User.get(safe_int(form_result['perm_user_id']))
406 perm_user = User.get(safe_int(form_result['perm_user_id']))
407 else:
407 else:
408 # used mostly to do lookup for default user
408 # used mostly to do lookup for default user
409 perm_user = User.get_by_username(form_result['perm_user_name'])
409 perm_user = User.get_by_username(form_result['perm_user_name'])
410 try:
410 try:
411 # stage 2 reset defaults and set them from form data
411 # stage 2 reset defaults and set them from form data
412 self._set_new_user_perms(perm_user, form_result, preserve=[
412 self._set_new_user_perms(perm_user, form_result, preserve=[
413 'default_repo_perm',
413 'default_repo_perm',
414 'default_group_perm',
414 'default_group_perm',
415 'default_user_group_perm',
415 'default_user_group_perm',
416 'default_branch_perm',
416 'default_branch_perm',
417
417
418 'default_register',
418 'default_register',
419 'default_password_reset',
419 'default_password_reset',
420 'default_extern_activate'])
420 'default_extern_activate'])
421 self.sa.commit()
421 self.sa.commit()
422 except (DatabaseError,):
422 except (DatabaseError,):
423 log.error(traceback.format_exc())
423 log.error(traceback.format_exc())
424 self.sa.rollback()
424 self.sa.rollback()
425 raise
425 raise
426
426
427 def update_user_group_permissions(self, form_result):
427 def update_user_group_permissions(self, form_result):
428 if 'perm_user_group_id' in form_result:
428 if 'perm_user_group_id' in form_result:
429 perm_user_group = UserGroup.get(safe_int(form_result['perm_user_group_id']))
429 perm_user_group = UserGroup.get(safe_int(form_result['perm_user_group_id']))
430 else:
430 else:
431 # used mostly to do lookup for default user
431 # used mostly to do lookup for default user
432 perm_user_group = UserGroup.get_by_group_name(form_result['perm_user_group_name'])
432 perm_user_group = UserGroup.get_by_group_name(form_result['perm_user_group_name'])
433 try:
433 try:
434 # stage 2 reset defaults and set them from form data
434 # stage 2 reset defaults and set them from form data
435 self._set_new_user_group_perms(perm_user_group, form_result, preserve=[
435 self._set_new_user_group_perms(perm_user_group, form_result, preserve=[
436 'default_repo_perm',
436 'default_repo_perm',
437 'default_group_perm',
437 'default_group_perm',
438 'default_user_group_perm',
438 'default_user_group_perm',
439 'default_branch_perm',
439 'default_branch_perm',
440
440
441 'default_register',
441 'default_register',
442 'default_password_reset',
442 'default_password_reset',
443 'default_extern_activate'])
443 'default_extern_activate'])
444 self.sa.commit()
444 self.sa.commit()
445 except (DatabaseError,):
445 except (DatabaseError,):
446 log.error(traceback.format_exc())
446 log.error(traceback.format_exc())
447 self.sa.rollback()
447 self.sa.rollback()
448 raise
448 raise
449
449
450 def update_object_permissions(self, form_result):
450 def update_object_permissions(self, form_result):
451 if 'perm_user_id' in form_result:
451 if 'perm_user_id' in form_result:
452 perm_user = User.get(safe_int(form_result['perm_user_id']))
452 perm_user = User.get(safe_int(form_result['perm_user_id']))
453 else:
453 else:
454 # used mostly to do lookup for default user
454 # used mostly to do lookup for default user
455 perm_user = User.get_by_username(form_result['perm_user_name'])
455 perm_user = User.get_by_username(form_result['perm_user_name'])
456 try:
456 try:
457
457
458 # stage 2 reset defaults and set them from form data
458 # stage 2 reset defaults and set them from form data
459 self._set_new_user_perms(perm_user, form_result, preserve=[
459 self._set_new_user_perms(perm_user, form_result, preserve=[
460 'default_repo_group_create',
460 'default_repo_group_create',
461 'default_user_group_create',
461 'default_user_group_create',
462 'default_repo_create_on_write',
462 'default_repo_create_on_write',
463 'default_repo_create',
463 'default_repo_create',
464 'default_fork_create',
464 'default_fork_create',
465 'default_inherit_default_permissions',
465 'default_inherit_default_permissions',
466 'default_branch_perm',
466 'default_branch_perm',
467
467
468 'default_register',
468 'default_register',
469 'default_password_reset',
469 'default_password_reset',
470 'default_extern_activate'])
470 'default_extern_activate'])
471
471
472 # overwrite default repo permissions
472 # overwrite default repo permissions
473 if form_result['overwrite_default_repo']:
473 if form_result['overwrite_default_repo']:
474 _def_name = form_result['default_repo_perm'].split('repository.')[-1]
474 _def_name = form_result['default_repo_perm'].split('repository.')[-1]
475 _def = Permission.get_by_key('repository.' + _def_name)
475 _def = Permission.get_by_key('repository.' + _def_name)
476 for r2p in self.sa.query(UserRepoToPerm)\
476 for r2p in self.sa.query(UserRepoToPerm)\
477 .filter(UserRepoToPerm.user == perm_user)\
477 .filter(UserRepoToPerm.user == perm_user)\
478 .all():
478 .all():
479 # don't reset PRIVATE repositories
479 # don't reset PRIVATE repositories
480 if not r2p.repository.private:
480 if not r2p.repository.private:
481 r2p.permission = _def
481 r2p.permission = _def
482 self.sa.add(r2p)
482 self.sa.add(r2p)
483
483
484 # overwrite default repo group permissions
484 # overwrite default repo group permissions
485 if form_result['overwrite_default_group']:
485 if form_result['overwrite_default_group']:
486 _def_name = form_result['default_group_perm'].split('group.')[-1]
486 _def_name = form_result['default_group_perm'].split('group.')[-1]
487 _def = Permission.get_by_key('group.' + _def_name)
487 _def = Permission.get_by_key('group.' + _def_name)
488 for g2p in self.sa.query(UserRepoGroupToPerm)\
488 for g2p in self.sa.query(UserRepoGroupToPerm)\
489 .filter(UserRepoGroupToPerm.user == perm_user)\
489 .filter(UserRepoGroupToPerm.user == perm_user)\
490 .all():
490 .all():
491 g2p.permission = _def
491 g2p.permission = _def
492 self.sa.add(g2p)
492 self.sa.add(g2p)
493
493
494 # overwrite default user group permissions
494 # overwrite default user group permissions
495 if form_result['overwrite_default_user_group']:
495 if form_result['overwrite_default_user_group']:
496 _def_name = form_result['default_user_group_perm'].split('usergroup.')[-1]
496 _def_name = form_result['default_user_group_perm'].split('usergroup.')[-1]
497 # user groups
497 # user groups
498 _def = Permission.get_by_key('usergroup.' + _def_name)
498 _def = Permission.get_by_key('usergroup.' + _def_name)
499 for g2p in self.sa.query(UserUserGroupToPerm)\
499 for g2p in self.sa.query(UserUserGroupToPerm)\
500 .filter(UserUserGroupToPerm.user == perm_user)\
500 .filter(UserUserGroupToPerm.user == perm_user)\
501 .all():
501 .all():
502 g2p.permission = _def
502 g2p.permission = _def
503 self.sa.add(g2p)
503 self.sa.add(g2p)
504
504
505 # COMMIT
505 # COMMIT
506 self.sa.commit()
506 self.sa.commit()
507 except (DatabaseError,):
507 except (DatabaseError,):
508 log.exception('Failed to set default object permissions')
508 log.exception('Failed to set default object permissions')
509 self.sa.rollback()
509 self.sa.rollback()
510 raise
510 raise
511
511
512 def update_branch_permissions(self, form_result):
512 def update_branch_permissions(self, form_result):
513 if 'perm_user_id' in form_result:
513 if 'perm_user_id' in form_result:
514 perm_user = User.get(safe_int(form_result['perm_user_id']))
514 perm_user = User.get(safe_int(form_result['perm_user_id']))
515 else:
515 else:
516 # used mostly to do lookup for default user
516 # used mostly to do lookup for default user
517 perm_user = User.get_by_username(form_result['perm_user_name'])
517 perm_user = User.get_by_username(form_result['perm_user_name'])
518 try:
518 try:
519
519
520 # stage 2 reset defaults and set them from form data
520 # stage 2 reset defaults and set them from form data
521 self._set_new_user_perms(perm_user, form_result, preserve=[
521 self._set_new_user_perms(perm_user, form_result, preserve=[
522 'default_repo_perm',
522 'default_repo_perm',
523 'default_group_perm',
523 'default_group_perm',
524 'default_user_group_perm',
524 'default_user_group_perm',
525
525
526 'default_repo_group_create',
526 'default_repo_group_create',
527 'default_user_group_create',
527 'default_user_group_create',
528 'default_repo_create_on_write',
528 'default_repo_create_on_write',
529 'default_repo_create',
529 'default_repo_create',
530 'default_fork_create',
530 'default_fork_create',
531 'default_inherit_default_permissions',
531 'default_inherit_default_permissions',
532
532
533 'default_register',
533 'default_register',
534 'default_password_reset',
534 'default_password_reset',
535 'default_extern_activate'])
535 'default_extern_activate'])
536
536
537 # overwrite default branch permissions
537 # overwrite default branch permissions
538 if form_result['overwrite_default_branch']:
538 if form_result['overwrite_default_branch']:
539 _def_name = \
539 _def_name = \
540 form_result['default_branch_perm'].split('branch.')[-1]
540 form_result['default_branch_perm'].split('branch.')[-1]
541
541
542 _def = Permission.get_by_key('branch.' + _def_name)
542 _def = Permission.get_by_key('branch.' + _def_name)
543
543
544 user_perms = UserToRepoBranchPermission.query()\
544 user_perms = UserToRepoBranchPermission.query()\
545 .join(UserToRepoBranchPermission.user_repo_to_perm)\
545 .join(UserToRepoBranchPermission.user_repo_to_perm)\
546 .filter(UserRepoToPerm.user == perm_user).all()
546 .filter(UserRepoToPerm.user == perm_user).all()
547
547
548 for g2p in user_perms:
548 for g2p in user_perms:
549 g2p.permission = _def
549 g2p.permission = _def
550 self.sa.add(g2p)
550 self.sa.add(g2p)
551
551
552 # COMMIT
552 # COMMIT
553 self.sa.commit()
553 self.sa.commit()
554 except (DatabaseError,):
554 except (DatabaseError,):
555 log.exception('Failed to set default branch permissions')
555 log.exception('Failed to set default branch permissions')
556 self.sa.rollback()
556 self.sa.rollback()
557 raise
557 raise
558
558
559 def get_users_with_repo_write(self, db_repo):
559 def get_users_with_repo_write(self, db_repo):
560 write_plus = ['repository.write', 'repository.admin']
560 write_plus = ['repository.write', 'repository.admin']
561 default_user_id = User.get_default_user().user_id
561 default_user_id = User.get_default_user_id()
562 user_write_permissions = collections.OrderedDict()
562 user_write_permissions = collections.OrderedDict()
563
563
564 # write+ and DEFAULT user for inheritance
564 # write+ and DEFAULT user for inheritance
565 for perm in db_repo.permissions():
565 for perm in db_repo.permissions():
566 if perm.permission in write_plus or perm.user_id == default_user_id:
566 if perm.permission in write_plus or perm.user_id == default_user_id:
567 user_write_permissions[perm.user_id] = perm
567 user_write_permissions[perm.user_id] = perm
568 return user_write_permissions
568 return user_write_permissions
569
569
570 def get_user_groups_with_repo_write(self, db_repo):
570 def get_user_groups_with_repo_write(self, db_repo):
571 write_plus = ['repository.write', 'repository.admin']
571 write_plus = ['repository.write', 'repository.admin']
572 user_group_write_permissions = collections.OrderedDict()
572 user_group_write_permissions = collections.OrderedDict()
573
573
574 # write+ and DEFAULT user for inheritance
574 # write+ and DEFAULT user for inheritance
575 for p in db_repo.permission_user_groups():
575 for p in db_repo.permission_user_groups():
576 if p.permission in write_plus:
576 if p.permission in write_plus:
577 user_group_write_permissions[p.users_group_id] = p
577 user_group_write_permissions[p.users_group_id] = p
578 return user_group_write_permissions
578 return user_group_write_permissions
579
579
580 def trigger_permission_flush(self, affected_user_ids):
580 def trigger_permission_flush(self, affected_user_ids):
581 events.trigger(events.UserPermissionsChange(affected_user_ids))
581 events.trigger(events.UserPermissionsChange(affected_user_ids))
582
582
583 def flush_user_permission_caches(self, changes, affected_user_ids=None):
583 def flush_user_permission_caches(self, changes, affected_user_ids=None):
584 affected_user_ids = affected_user_ids or []
584 affected_user_ids = affected_user_ids or []
585
585
586 for change in changes['added'] + changes['updated'] + changes['deleted']:
586 for change in changes['added'] + changes['updated'] + changes['deleted']:
587 if change['type'] == 'user':
587 if change['type'] == 'user':
588 affected_user_ids.append(change['id'])
588 affected_user_ids.append(change['id'])
589 if change['type'] == 'user_group':
589 if change['type'] == 'user_group':
590 user_group = UserGroup.get(safe_int(change['id']))
590 user_group = UserGroup.get(safe_int(change['id']))
591 if user_group:
591 if user_group:
592 group_members_ids = [x.user_id for x in user_group.members]
592 group_members_ids = [x.user_id for x in user_group.members]
593 affected_user_ids.extend(group_members_ids)
593 affected_user_ids.extend(group_members_ids)
594
594
595 self.trigger_permission_flush(affected_user_ids)
595 self.trigger_permission_flush(affected_user_ids)
596
596
597 return affected_user_ids
597 return affected_user_ids
@@ -1,1116 +1,1116 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Set of generic validators
22 Set of generic validators
23 """
23 """
24
24
25
25
26 import os
26 import os
27 import re
27 import re
28 import logging
28 import logging
29 import collections
29 import collections
30
30
31 import formencode
31 import formencode
32 import ipaddress
32 import ipaddress
33 from formencode.validators import (
33 from formencode.validators import (
34 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set,
34 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set,
35 NotEmpty, IPAddress, CIDR, String, FancyValidator
35 NotEmpty, IPAddress, CIDR, String, FancyValidator
36 )
36 )
37
37
38 from sqlalchemy.sql.expression import true
38 from sqlalchemy.sql.expression import true
39 from sqlalchemy.util import OrderedSet
39 from sqlalchemy.util import OrderedSet
40 from pyramid import compat
40 from pyramid import compat
41
41
42 from rhodecode.authentication import (
42 from rhodecode.authentication import (
43 legacy_plugin_prefix, _import_legacy_plugin)
43 legacy_plugin_prefix, _import_legacy_plugin)
44 from rhodecode.authentication.base import loadplugin
44 from rhodecode.authentication.base import loadplugin
45 from rhodecode.apps._base import ADMIN_PREFIX
45 from rhodecode.apps._base import ADMIN_PREFIX
46 from rhodecode.lib.auth import HasRepoGroupPermissionAny, HasPermissionAny
46 from rhodecode.lib.auth import HasRepoGroupPermissionAny, HasPermissionAny
47 from rhodecode.lib.utils import repo_name_slug, make_db_config
47 from rhodecode.lib.utils import repo_name_slug, make_db_config
48 from rhodecode.lib.utils2 import safe_int, str2bool, aslist, md5, safe_unicode
48 from rhodecode.lib.utils2 import safe_int, str2bool, aslist, md5, safe_unicode
49 from rhodecode.lib.vcs.backends.git.repository import GitRepository
49 from rhodecode.lib.vcs.backends.git.repository import GitRepository
50 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
50 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
51 from rhodecode.lib.vcs.backends.svn.repository import SubversionRepository
51 from rhodecode.lib.vcs.backends.svn.repository import SubversionRepository
52 from rhodecode.model.db import (
52 from rhodecode.model.db import (
53 RepoGroup, Repository, UserGroup, User, ChangesetStatus, Gist)
53 RepoGroup, Repository, UserGroup, User, ChangesetStatus, Gist)
54 from rhodecode.model.settings import VcsSettingsModel
54 from rhodecode.model.settings import VcsSettingsModel
55
55
56 # silence warnings and pylint
56 # silence warnings and pylint
57 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
57 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
58 NotEmpty, IPAddress, CIDR, String, FancyValidator
58 NotEmpty, IPAddress, CIDR, String, FancyValidator
59
59
60 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
61
61
62
62
63 class _Missing(object):
63 class _Missing(object):
64 pass
64 pass
65
65
66
66
67 Missing = _Missing()
67 Missing = _Missing()
68
68
69
69
70 def M(self, key, state, **kwargs):
70 def M(self, key, state, **kwargs):
71 """
71 """
72 returns string from self.message based on given key,
72 returns string from self.message based on given key,
73 passed kw params are used to substitute %(named)s params inside
73 passed kw params are used to substitute %(named)s params inside
74 translated strings
74 translated strings
75
75
76 :param msg:
76 :param msg:
77 :param state:
77 :param state:
78 """
78 """
79
79
80 #state._ = staticmethod(_)
80 #state._ = staticmethod(_)
81 # inject validator into state object
81 # inject validator into state object
82 return self.message(key, state, **kwargs)
82 return self.message(key, state, **kwargs)
83
83
84
84
85 def UniqueList(localizer, convert=None):
85 def UniqueList(localizer, convert=None):
86 _ = localizer
86 _ = localizer
87
87
88 class _validator(formencode.FancyValidator):
88 class _validator(formencode.FancyValidator):
89 """
89 """
90 Unique List !
90 Unique List !
91 """
91 """
92 messages = {
92 messages = {
93 'empty': _(u'Value cannot be an empty list'),
93 'empty': _(u'Value cannot be an empty list'),
94 'missing_value': _(u'Value cannot be an empty list'),
94 'missing_value': _(u'Value cannot be an empty list'),
95 }
95 }
96
96
97 def _to_python(self, value, state):
97 def _to_python(self, value, state):
98 ret_val = []
98 ret_val = []
99
99
100 def make_unique(value):
100 def make_unique(value):
101 seen = []
101 seen = []
102 return [c for c in value if not (c in seen or seen.append(c))]
102 return [c for c in value if not (c in seen or seen.append(c))]
103
103
104 if isinstance(value, list):
104 if isinstance(value, list):
105 ret_val = make_unique(value)
105 ret_val = make_unique(value)
106 elif isinstance(value, set):
106 elif isinstance(value, set):
107 ret_val = make_unique(list(value))
107 ret_val = make_unique(list(value))
108 elif isinstance(value, tuple):
108 elif isinstance(value, tuple):
109 ret_val = make_unique(list(value))
109 ret_val = make_unique(list(value))
110 elif value is None:
110 elif value is None:
111 ret_val = []
111 ret_val = []
112 else:
112 else:
113 ret_val = [value]
113 ret_val = [value]
114
114
115 if convert:
115 if convert:
116 ret_val = map(convert, ret_val)
116 ret_val = map(convert, ret_val)
117 return ret_val
117 return ret_val
118
118
119 def empty_value(self, value):
119 def empty_value(self, value):
120 return []
120 return []
121 return _validator
121 return _validator
122
122
123
123
124 def UniqueListFromString(localizer):
124 def UniqueListFromString(localizer):
125 _ = localizer
125 _ = localizer
126
126
127 class _validator(UniqueList(localizer)):
127 class _validator(UniqueList(localizer)):
128 def _to_python(self, value, state):
128 def _to_python(self, value, state):
129 if isinstance(value, compat.string_types):
129 if isinstance(value, compat.string_types):
130 value = aslist(value, ',')
130 value = aslist(value, ',')
131 return super(_validator, self)._to_python(value, state)
131 return super(_validator, self)._to_python(value, state)
132 return _validator
132 return _validator
133
133
134
134
135 def ValidSvnPattern(localizer, section, repo_name=None):
135 def ValidSvnPattern(localizer, section, repo_name=None):
136 _ = localizer
136 _ = localizer
137
137
138 class _validator(formencode.validators.FancyValidator):
138 class _validator(formencode.validators.FancyValidator):
139 messages = {
139 messages = {
140 'pattern_exists': _(u'Pattern already exists'),
140 'pattern_exists': _(u'Pattern already exists'),
141 }
141 }
142
142
143 def validate_python(self, value, state):
143 def validate_python(self, value, state):
144 if not value:
144 if not value:
145 return
145 return
146 model = VcsSettingsModel(repo=repo_name)
146 model = VcsSettingsModel(repo=repo_name)
147 ui_settings = model.get_svn_patterns(section=section)
147 ui_settings = model.get_svn_patterns(section=section)
148 for entry in ui_settings:
148 for entry in ui_settings:
149 if value == entry.value:
149 if value == entry.value:
150 msg = M(self, 'pattern_exists', state)
150 msg = M(self, 'pattern_exists', state)
151 raise formencode.Invalid(msg, value, state)
151 raise formencode.Invalid(msg, value, state)
152 return _validator
152 return _validator
153
153
154
154
155 def ValidUsername(localizer, edit=False, old_data=None):
155 def ValidUsername(localizer, edit=False, old_data=None):
156 _ = localizer
156 _ = localizer
157 old_data = old_data or {}
157 old_data = old_data or {}
158
158
159 class _validator(formencode.validators.FancyValidator):
159 class _validator(formencode.validators.FancyValidator):
160 messages = {
160 messages = {
161 'username_exists': _(u'Username "%(username)s" already exists'),
161 'username_exists': _(u'Username "%(username)s" already exists'),
162 'system_invalid_username':
162 'system_invalid_username':
163 _(u'Username "%(username)s" is forbidden'),
163 _(u'Username "%(username)s" is forbidden'),
164 'invalid_username':
164 'invalid_username':
165 _(u'Username may only contain alphanumeric characters '
165 _(u'Username may only contain alphanumeric characters '
166 u'underscores, periods or dashes and must begin with '
166 u'underscores, periods or dashes and must begin with '
167 u'alphanumeric character or underscore')
167 u'alphanumeric character or underscore')
168 }
168 }
169
169
170 def validate_python(self, value, state):
170 def validate_python(self, value, state):
171 if value in ['default', 'new_user']:
171 if value in ['default', 'new_user']:
172 msg = M(self, 'system_invalid_username', state, username=value)
172 msg = M(self, 'system_invalid_username', state, username=value)
173 raise formencode.Invalid(msg, value, state)
173 raise formencode.Invalid(msg, value, state)
174 # check if user is unique
174 # check if user is unique
175 old_un = None
175 old_un = None
176 if edit:
176 if edit:
177 old_un = User.get(old_data.get('user_id')).username
177 old_un = User.get(old_data.get('user_id')).username
178
178
179 if old_un != value or not edit:
179 if old_un != value or not edit:
180 if User.get_by_username(value, case_insensitive=True):
180 if User.get_by_username(value, case_insensitive=True):
181 msg = M(self, 'username_exists', state, username=value)
181 msg = M(self, 'username_exists', state, username=value)
182 raise formencode.Invalid(msg, value, state)
182 raise formencode.Invalid(msg, value, state)
183
183
184 if (re.match(r'^[\w]{1}[\w\-\.]{0,254}$', value)
184 if (re.match(r'^[\w]{1}[\w\-\.]{0,254}$', value)
185 is None):
185 is None):
186 msg = M(self, 'invalid_username', state)
186 msg = M(self, 'invalid_username', state)
187 raise formencode.Invalid(msg, value, state)
187 raise formencode.Invalid(msg, value, state)
188 return _validator
188 return _validator
189
189
190
190
191 def ValidRepoUser(localizer, allow_disabled=False):
191 def ValidRepoUser(localizer, allow_disabled=False):
192 _ = localizer
192 _ = localizer
193
193
194 class _validator(formencode.validators.FancyValidator):
194 class _validator(formencode.validators.FancyValidator):
195 messages = {
195 messages = {
196 'invalid_username': _(u'Username %(username)s is not valid'),
196 'invalid_username': _(u'Username %(username)s is not valid'),
197 'disabled_username': _(u'Username %(username)s is disabled')
197 'disabled_username': _(u'Username %(username)s is disabled')
198 }
198 }
199
199
200 def validate_python(self, value, state):
200 def validate_python(self, value, state):
201 try:
201 try:
202 user = User.query().filter(User.username == value).one()
202 user = User.query().filter(User.username == value).one()
203 except Exception:
203 except Exception:
204 msg = M(self, 'invalid_username', state, username=value)
204 msg = M(self, 'invalid_username', state, username=value)
205 raise formencode.Invalid(
205 raise formencode.Invalid(
206 msg, value, state, error_dict={'username': msg}
206 msg, value, state, error_dict={'username': msg}
207 )
207 )
208 if user and (not allow_disabled and not user.active):
208 if user and (not allow_disabled and not user.active):
209 msg = M(self, 'disabled_username', state, username=value)
209 msg = M(self, 'disabled_username', state, username=value)
210 raise formencode.Invalid(
210 raise formencode.Invalid(
211 msg, value, state, error_dict={'username': msg}
211 msg, value, state, error_dict={'username': msg}
212 )
212 )
213 return _validator
213 return _validator
214
214
215
215
216 def ValidUserGroup(localizer, edit=False, old_data=None):
216 def ValidUserGroup(localizer, edit=False, old_data=None):
217 _ = localizer
217 _ = localizer
218 old_data = old_data or {}
218 old_data = old_data or {}
219
219
220 class _validator(formencode.validators.FancyValidator):
220 class _validator(formencode.validators.FancyValidator):
221 messages = {
221 messages = {
222 'invalid_group': _(u'Invalid user group name'),
222 'invalid_group': _(u'Invalid user group name'),
223 'group_exist': _(u'User group `%(usergroup)s` already exists'),
223 'group_exist': _(u'User group `%(usergroup)s` already exists'),
224 'invalid_usergroup_name':
224 'invalid_usergroup_name':
225 _(u'user group name may only contain alphanumeric '
225 _(u'user group name may only contain alphanumeric '
226 u'characters underscores, periods or dashes and must begin '
226 u'characters underscores, periods or dashes and must begin '
227 u'with alphanumeric character')
227 u'with alphanumeric character')
228 }
228 }
229
229
230 def validate_python(self, value, state):
230 def validate_python(self, value, state):
231 if value in ['default']:
231 if value in ['default']:
232 msg = M(self, 'invalid_group', state)
232 msg = M(self, 'invalid_group', state)
233 raise formencode.Invalid(
233 raise formencode.Invalid(
234 msg, value, state, error_dict={'users_group_name': msg}
234 msg, value, state, error_dict={'users_group_name': msg}
235 )
235 )
236 # check if group is unique
236 # check if group is unique
237 old_ugname = None
237 old_ugname = None
238 if edit:
238 if edit:
239 old_id = old_data.get('users_group_id')
239 old_id = old_data.get('users_group_id')
240 old_ugname = UserGroup.get(old_id).users_group_name
240 old_ugname = UserGroup.get(old_id).users_group_name
241
241
242 if old_ugname != value or not edit:
242 if old_ugname != value or not edit:
243 is_existing_group = UserGroup.get_by_group_name(
243 is_existing_group = UserGroup.get_by_group_name(
244 value, case_insensitive=True)
244 value, case_insensitive=True)
245 if is_existing_group:
245 if is_existing_group:
246 msg = M(self, 'group_exist', state, usergroup=value)
246 msg = M(self, 'group_exist', state, usergroup=value)
247 raise formencode.Invalid(
247 raise formencode.Invalid(
248 msg, value, state, error_dict={'users_group_name': msg}
248 msg, value, state, error_dict={'users_group_name': msg}
249 )
249 )
250
250
251 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
251 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
252 msg = M(self, 'invalid_usergroup_name', state)
252 msg = M(self, 'invalid_usergroup_name', state)
253 raise formencode.Invalid(
253 raise formencode.Invalid(
254 msg, value, state, error_dict={'users_group_name': msg}
254 msg, value, state, error_dict={'users_group_name': msg}
255 )
255 )
256 return _validator
256 return _validator
257
257
258
258
259 def ValidRepoGroup(localizer, edit=False, old_data=None, can_create_in_root=False):
259 def ValidRepoGroup(localizer, edit=False, old_data=None, can_create_in_root=False):
260 _ = localizer
260 _ = localizer
261 old_data = old_data or {}
261 old_data = old_data or {}
262
262
263 class _validator(formencode.validators.FancyValidator):
263 class _validator(formencode.validators.FancyValidator):
264 messages = {
264 messages = {
265 'group_parent_id': _(u'Cannot assign this group as parent'),
265 'group_parent_id': _(u'Cannot assign this group as parent'),
266 'group_exists': _(u'Group "%(group_name)s" already exists'),
266 'group_exists': _(u'Group "%(group_name)s" already exists'),
267 'repo_exists': _(u'Repository with name "%(group_name)s" '
267 'repo_exists': _(u'Repository with name "%(group_name)s" '
268 u'already exists'),
268 u'already exists'),
269 'permission_denied': _(u"no permission to store repository group"
269 'permission_denied': _(u"no permission to store repository group"
270 u"in this location"),
270 u"in this location"),
271 'permission_denied_root': _(
271 'permission_denied_root': _(
272 u"no permission to store repository group "
272 u"no permission to store repository group "
273 u"in root location")
273 u"in root location")
274 }
274 }
275
275
276 def _to_python(self, value, state):
276 def _to_python(self, value, state):
277 group_name = repo_name_slug(value.get('group_name', ''))
277 group_name = repo_name_slug(value.get('group_name', ''))
278 group_parent_id = safe_int(value.get('group_parent_id'))
278 group_parent_id = safe_int(value.get('group_parent_id'))
279 gr = RepoGroup.get(group_parent_id)
279 gr = RepoGroup.get(group_parent_id)
280 if gr:
280 if gr:
281 parent_group_path = gr.full_path
281 parent_group_path = gr.full_path
282 # value needs to be aware of group name in order to check
282 # value needs to be aware of group name in order to check
283 # db key This is an actual just the name to store in the
283 # db key This is an actual just the name to store in the
284 # database
284 # database
285 group_name_full = (
285 group_name_full = (
286 parent_group_path + RepoGroup.url_sep() + group_name)
286 parent_group_path + RepoGroup.url_sep() + group_name)
287 else:
287 else:
288 group_name_full = group_name
288 group_name_full = group_name
289
289
290 value['group_name'] = group_name
290 value['group_name'] = group_name
291 value['group_name_full'] = group_name_full
291 value['group_name_full'] = group_name_full
292 value['group_parent_id'] = group_parent_id
292 value['group_parent_id'] = group_parent_id
293 return value
293 return value
294
294
295 def validate_python(self, value, state):
295 def validate_python(self, value, state):
296
296
297 old_group_name = None
297 old_group_name = None
298 group_name = value.get('group_name')
298 group_name = value.get('group_name')
299 group_name_full = value.get('group_name_full')
299 group_name_full = value.get('group_name_full')
300 group_parent_id = safe_int(value.get('group_parent_id'))
300 group_parent_id = safe_int(value.get('group_parent_id'))
301 if group_parent_id == -1:
301 if group_parent_id == -1:
302 group_parent_id = None
302 group_parent_id = None
303
303
304 group_obj = RepoGroup.get(old_data.get('group_id'))
304 group_obj = RepoGroup.get(old_data.get('group_id'))
305 parent_group_changed = False
305 parent_group_changed = False
306 if edit:
306 if edit:
307 old_group_name = group_obj.group_name
307 old_group_name = group_obj.group_name
308 old_group_parent_id = group_obj.group_parent_id
308 old_group_parent_id = group_obj.group_parent_id
309
309
310 if group_parent_id != old_group_parent_id:
310 if group_parent_id != old_group_parent_id:
311 parent_group_changed = True
311 parent_group_changed = True
312
312
313 # TODO: mikhail: the following if statement is not reached
313 # TODO: mikhail: the following if statement is not reached
314 # since group_parent_id's OneOf validation fails before.
314 # since group_parent_id's OneOf validation fails before.
315 # Can be removed.
315 # Can be removed.
316
316
317 # check against setting a parent of self
317 # check against setting a parent of self
318 parent_of_self = (
318 parent_of_self = (
319 old_data['group_id'] == group_parent_id
319 old_data['group_id'] == group_parent_id
320 if group_parent_id else False
320 if group_parent_id else False
321 )
321 )
322 if parent_of_self:
322 if parent_of_self:
323 msg = M(self, 'group_parent_id', state)
323 msg = M(self, 'group_parent_id', state)
324 raise formencode.Invalid(
324 raise formencode.Invalid(
325 msg, value, state, error_dict={'group_parent_id': msg}
325 msg, value, state, error_dict={'group_parent_id': msg}
326 )
326 )
327
327
328 # group we're moving current group inside
328 # group we're moving current group inside
329 child_group = None
329 child_group = None
330 if group_parent_id:
330 if group_parent_id:
331 child_group = RepoGroup.query().filter(
331 child_group = RepoGroup.query().filter(
332 RepoGroup.group_id == group_parent_id).scalar()
332 RepoGroup.group_id == group_parent_id).scalar()
333
333
334 # do a special check that we cannot move a group to one of
334 # do a special check that we cannot move a group to one of
335 # it's children
335 # it's children
336 if edit and child_group:
336 if edit and child_group:
337 parents = [x.group_id for x in child_group.parents]
337 parents = [x.group_id for x in child_group.parents]
338 move_to_children = old_data['group_id'] in parents
338 move_to_children = old_data['group_id'] in parents
339 if move_to_children:
339 if move_to_children:
340 msg = M(self, 'group_parent_id', state)
340 msg = M(self, 'group_parent_id', state)
341 raise formencode.Invalid(
341 raise formencode.Invalid(
342 msg, value, state, error_dict={'group_parent_id': msg})
342 msg, value, state, error_dict={'group_parent_id': msg})
343
343
344 # Check if we have permission to store in the parent.
344 # Check if we have permission to store in the parent.
345 # Only check if the parent group changed.
345 # Only check if the parent group changed.
346 if parent_group_changed:
346 if parent_group_changed:
347 if child_group is None:
347 if child_group is None:
348 if not can_create_in_root:
348 if not can_create_in_root:
349 msg = M(self, 'permission_denied_root', state)
349 msg = M(self, 'permission_denied_root', state)
350 raise formencode.Invalid(
350 raise formencode.Invalid(
351 msg, value, state,
351 msg, value, state,
352 error_dict={'group_parent_id': msg})
352 error_dict={'group_parent_id': msg})
353 else:
353 else:
354 valid = HasRepoGroupPermissionAny('group.admin')
354 valid = HasRepoGroupPermissionAny('group.admin')
355 forbidden = not valid(
355 forbidden = not valid(
356 child_group.group_name, 'can create group validator')
356 child_group.group_name, 'can create group validator')
357 if forbidden:
357 if forbidden:
358 msg = M(self, 'permission_denied', state)
358 msg = M(self, 'permission_denied', state)
359 raise formencode.Invalid(
359 raise formencode.Invalid(
360 msg, value, state,
360 msg, value, state,
361 error_dict={'group_parent_id': msg})
361 error_dict={'group_parent_id': msg})
362
362
363 # if we change the name or it's new group, check for existing names
363 # if we change the name or it's new group, check for existing names
364 # or repositories with the same name
364 # or repositories with the same name
365 if old_group_name != group_name_full or not edit:
365 if old_group_name != group_name_full or not edit:
366 # check group
366 # check group
367 gr = RepoGroup.get_by_group_name(group_name_full)
367 gr = RepoGroup.get_by_group_name(group_name_full)
368 if gr:
368 if gr:
369 msg = M(self, 'group_exists', state, group_name=group_name)
369 msg = M(self, 'group_exists', state, group_name=group_name)
370 raise formencode.Invalid(
370 raise formencode.Invalid(
371 msg, value, state, error_dict={'group_name': msg})
371 msg, value, state, error_dict={'group_name': msg})
372
372
373 # check for same repo
373 # check for same repo
374 repo = Repository.get_by_repo_name(group_name_full)
374 repo = Repository.get_by_repo_name(group_name_full)
375 if repo:
375 if repo:
376 msg = M(self, 'repo_exists', state, group_name=group_name)
376 msg = M(self, 'repo_exists', state, group_name=group_name)
377 raise formencode.Invalid(
377 raise formencode.Invalid(
378 msg, value, state, error_dict={'group_name': msg})
378 msg, value, state, error_dict={'group_name': msg})
379 return _validator
379 return _validator
380
380
381
381
382 def ValidPassword(localizer):
382 def ValidPassword(localizer):
383 _ = localizer
383 _ = localizer
384
384
385 class _validator(formencode.validators.FancyValidator):
385 class _validator(formencode.validators.FancyValidator):
386 messages = {
386 messages = {
387 'invalid_password':
387 'invalid_password':
388 _(u'Invalid characters (non-ascii) in password')
388 _(u'Invalid characters (non-ascii) in password')
389 }
389 }
390
390
391 def validate_python(self, value, state):
391 def validate_python(self, value, state):
392 try:
392 try:
393 (value or '').decode('ascii')
393 (value or '').decode('ascii')
394 except UnicodeError:
394 except UnicodeError:
395 msg = M(self, 'invalid_password', state)
395 msg = M(self, 'invalid_password', state)
396 raise formencode.Invalid(msg, value, state,)
396 raise formencode.Invalid(msg, value, state,)
397 return _validator
397 return _validator
398
398
399
399
400 def ValidPasswordsMatch(
400 def ValidPasswordsMatch(
401 localizer, passwd='new_password',
401 localizer, passwd='new_password',
402 passwd_confirmation='password_confirmation'):
402 passwd_confirmation='password_confirmation'):
403 _ = localizer
403 _ = localizer
404
404
405 class _validator(formencode.validators.FancyValidator):
405 class _validator(formencode.validators.FancyValidator):
406 messages = {
406 messages = {
407 'password_mismatch': _(u'Passwords do not match'),
407 'password_mismatch': _(u'Passwords do not match'),
408 }
408 }
409
409
410 def validate_python(self, value, state):
410 def validate_python(self, value, state):
411
411
412 pass_val = value.get('password') or value.get(passwd)
412 pass_val = value.get('password') or value.get(passwd)
413 if pass_val != value[passwd_confirmation]:
413 if pass_val != value[passwd_confirmation]:
414 msg = M(self, 'password_mismatch', state)
414 msg = M(self, 'password_mismatch', state)
415 raise formencode.Invalid(
415 raise formencode.Invalid(
416 msg, value, state,
416 msg, value, state,
417 error_dict={passwd: msg, passwd_confirmation: msg}
417 error_dict={passwd: msg, passwd_confirmation: msg}
418 )
418 )
419 return _validator
419 return _validator
420
420
421
421
422 def ValidAuth(localizer):
422 def ValidAuth(localizer):
423 _ = localizer
423 _ = localizer
424
424
425 class _validator(formencode.validators.FancyValidator):
425 class _validator(formencode.validators.FancyValidator):
426 messages = {
426 messages = {
427 'invalid_password': _(u'invalid password'),
427 'invalid_password': _(u'invalid password'),
428 'invalid_username': _(u'invalid user name'),
428 'invalid_username': _(u'invalid user name'),
429 'disabled_account': _(u'Your account is disabled')
429 'disabled_account': _(u'Your account is disabled')
430 }
430 }
431
431
432 def validate_python(self, value, state):
432 def validate_python(self, value, state):
433 from rhodecode.authentication.base import authenticate, HTTP_TYPE
433 from rhodecode.authentication.base import authenticate, HTTP_TYPE
434
434
435 password = value['password']
435 password = value['password']
436 username = value['username']
436 username = value['username']
437
437
438 if not authenticate(username, password, '', HTTP_TYPE,
438 if not authenticate(username, password, '', HTTP_TYPE,
439 skip_missing=True):
439 skip_missing=True):
440 user = User.get_by_username(username)
440 user = User.get_by_username(username)
441 if user and not user.active:
441 if user and not user.active:
442 log.warning('user %s is disabled', username)
442 log.warning('user %s is disabled', username)
443 msg = M(self, 'disabled_account', state)
443 msg = M(self, 'disabled_account', state)
444 raise formencode.Invalid(
444 raise formencode.Invalid(
445 msg, value, state, error_dict={'username': msg}
445 msg, value, state, error_dict={'username': msg}
446 )
446 )
447 else:
447 else:
448 log.warning('user `%s` failed to authenticate', username)
448 log.warning('user `%s` failed to authenticate', username)
449 msg = M(self, 'invalid_username', state)
449 msg = M(self, 'invalid_username', state)
450 msg2 = M(self, 'invalid_password', state)
450 msg2 = M(self, 'invalid_password', state)
451 raise formencode.Invalid(
451 raise formencode.Invalid(
452 msg, value, state,
452 msg, value, state,
453 error_dict={'username': msg, 'password': msg2}
453 error_dict={'username': msg, 'password': msg2}
454 )
454 )
455 return _validator
455 return _validator
456
456
457
457
458 def ValidRepoName(localizer, edit=False, old_data=None):
458 def ValidRepoName(localizer, edit=False, old_data=None):
459 old_data = old_data or {}
459 old_data = old_data or {}
460 _ = localizer
460 _ = localizer
461
461
462 class _validator(formencode.validators.FancyValidator):
462 class _validator(formencode.validators.FancyValidator):
463 messages = {
463 messages = {
464 'invalid_repo_name':
464 'invalid_repo_name':
465 _(u'Repository name %(repo)s is disallowed'),
465 _(u'Repository name %(repo)s is disallowed'),
466 # top level
466 # top level
467 'repository_exists': _(u'Repository with name %(repo)s '
467 'repository_exists': _(u'Repository with name %(repo)s '
468 u'already exists'),
468 u'already exists'),
469 'group_exists': _(u'Repository group with name "%(repo)s" '
469 'group_exists': _(u'Repository group with name "%(repo)s" '
470 u'already exists'),
470 u'already exists'),
471 # inside a group
471 # inside a group
472 'repository_in_group_exists': _(u'Repository with name %(repo)s '
472 'repository_in_group_exists': _(u'Repository with name %(repo)s '
473 u'exists in group "%(group)s"'),
473 u'exists in group "%(group)s"'),
474 'group_in_group_exists': _(
474 'group_in_group_exists': _(
475 u'Repository group with name "%(repo)s" '
475 u'Repository group with name "%(repo)s" '
476 u'exists in group "%(group)s"'),
476 u'exists in group "%(group)s"'),
477 }
477 }
478
478
479 def _to_python(self, value, state):
479 def _to_python(self, value, state):
480 repo_name = repo_name_slug(value.get('repo_name', ''))
480 repo_name = repo_name_slug(value.get('repo_name', ''))
481 repo_group = value.get('repo_group')
481 repo_group = value.get('repo_group')
482 if repo_group:
482 if repo_group:
483 gr = RepoGroup.get(repo_group)
483 gr = RepoGroup.get(repo_group)
484 group_path = gr.full_path
484 group_path = gr.full_path
485 group_name = gr.group_name
485 group_name = gr.group_name
486 # value needs to be aware of group name in order to check
486 # value needs to be aware of group name in order to check
487 # db key This is an actual just the name to store in the
487 # db key This is an actual just the name to store in the
488 # database
488 # database
489 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
489 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
490 else:
490 else:
491 group_name = group_path = ''
491 group_name = group_path = ''
492 repo_name_full = repo_name
492 repo_name_full = repo_name
493
493
494 value['repo_name'] = repo_name
494 value['repo_name'] = repo_name
495 value['repo_name_full'] = repo_name_full
495 value['repo_name_full'] = repo_name_full
496 value['group_path'] = group_path
496 value['group_path'] = group_path
497 value['group_name'] = group_name
497 value['group_name'] = group_name
498 return value
498 return value
499
499
500 def validate_python(self, value, state):
500 def validate_python(self, value, state):
501
501
502 repo_name = value.get('repo_name')
502 repo_name = value.get('repo_name')
503 repo_name_full = value.get('repo_name_full')
503 repo_name_full = value.get('repo_name_full')
504 group_path = value.get('group_path')
504 group_path = value.get('group_path')
505 group_name = value.get('group_name')
505 group_name = value.get('group_name')
506
506
507 if repo_name in [ADMIN_PREFIX, '']:
507 if repo_name in [ADMIN_PREFIX, '']:
508 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
508 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
509 raise formencode.Invalid(
509 raise formencode.Invalid(
510 msg, value, state, error_dict={'repo_name': msg})
510 msg, value, state, error_dict={'repo_name': msg})
511
511
512 rename = old_data.get('repo_name') != repo_name_full
512 rename = old_data.get('repo_name') != repo_name_full
513 create = not edit
513 create = not edit
514 if rename or create:
514 if rename or create:
515
515
516 if group_path:
516 if group_path:
517 if Repository.get_by_repo_name(repo_name_full):
517 if Repository.get_by_repo_name(repo_name_full):
518 msg = M(self, 'repository_in_group_exists', state,
518 msg = M(self, 'repository_in_group_exists', state,
519 repo=repo_name, group=group_name)
519 repo=repo_name, group=group_name)
520 raise formencode.Invalid(
520 raise formencode.Invalid(
521 msg, value, state, error_dict={'repo_name': msg})
521 msg, value, state, error_dict={'repo_name': msg})
522 if RepoGroup.get_by_group_name(repo_name_full):
522 if RepoGroup.get_by_group_name(repo_name_full):
523 msg = M(self, 'group_in_group_exists', state,
523 msg = M(self, 'group_in_group_exists', state,
524 repo=repo_name, group=group_name)
524 repo=repo_name, group=group_name)
525 raise formencode.Invalid(
525 raise formencode.Invalid(
526 msg, value, state, error_dict={'repo_name': msg})
526 msg, value, state, error_dict={'repo_name': msg})
527 else:
527 else:
528 if RepoGroup.get_by_group_name(repo_name_full):
528 if RepoGroup.get_by_group_name(repo_name_full):
529 msg = M(self, 'group_exists', state, repo=repo_name)
529 msg = M(self, 'group_exists', state, repo=repo_name)
530 raise formencode.Invalid(
530 raise formencode.Invalid(
531 msg, value, state, error_dict={'repo_name': msg})
531 msg, value, state, error_dict={'repo_name': msg})
532
532
533 if Repository.get_by_repo_name(repo_name_full):
533 if Repository.get_by_repo_name(repo_name_full):
534 msg = M(
534 msg = M(
535 self, 'repository_exists', state, repo=repo_name)
535 self, 'repository_exists', state, repo=repo_name)
536 raise formencode.Invalid(
536 raise formencode.Invalid(
537 msg, value, state, error_dict={'repo_name': msg})
537 msg, value, state, error_dict={'repo_name': msg})
538 return value
538 return value
539 return _validator
539 return _validator
540
540
541
541
542 def ValidForkName(localizer, *args, **kwargs):
542 def ValidForkName(localizer, *args, **kwargs):
543 _ = localizer
543 _ = localizer
544
544
545 return ValidRepoName(localizer, *args, **kwargs)
545 return ValidRepoName(localizer, *args, **kwargs)
546
546
547
547
548 def SlugifyName(localizer):
548 def SlugifyName(localizer):
549 _ = localizer
549 _ = localizer
550
550
551 class _validator(formencode.validators.FancyValidator):
551 class _validator(formencode.validators.FancyValidator):
552
552
553 def _to_python(self, value, state):
553 def _to_python(self, value, state):
554 return repo_name_slug(value)
554 return repo_name_slug(value)
555
555
556 def validate_python(self, value, state):
556 def validate_python(self, value, state):
557 pass
557 pass
558 return _validator
558 return _validator
559
559
560
560
561 def CannotHaveGitSuffix(localizer):
561 def CannotHaveGitSuffix(localizer):
562 _ = localizer
562 _ = localizer
563
563
564 class _validator(formencode.validators.FancyValidator):
564 class _validator(formencode.validators.FancyValidator):
565 messages = {
565 messages = {
566 'has_git_suffix':
566 'has_git_suffix':
567 _(u'Repository name cannot end with .git'),
567 _(u'Repository name cannot end with .git'),
568 }
568 }
569
569
570 def _to_python(self, value, state):
570 def _to_python(self, value, state):
571 return value
571 return value
572
572
573 def validate_python(self, value, state):
573 def validate_python(self, value, state):
574 if value and value.endswith('.git'):
574 if value and value.endswith('.git'):
575 msg = M(
575 msg = M(
576 self, 'has_git_suffix', state)
576 self, 'has_git_suffix', state)
577 raise formencode.Invalid(
577 raise formencode.Invalid(
578 msg, value, state, error_dict={'repo_name': msg})
578 msg, value, state, error_dict={'repo_name': msg})
579 return _validator
579 return _validator
580
580
581
581
582 def ValidCloneUri(localizer):
582 def ValidCloneUri(localizer):
583 _ = localizer
583 _ = localizer
584
584
585 class InvalidCloneUrl(Exception):
585 class InvalidCloneUrl(Exception):
586 allowed_prefixes = ()
586 allowed_prefixes = ()
587
587
588 def url_handler(repo_type, url):
588 def url_handler(repo_type, url):
589 config = make_db_config(clear_session=False)
589 config = make_db_config(clear_session=False)
590 if repo_type == 'hg':
590 if repo_type == 'hg':
591 allowed_prefixes = ('http', 'svn+http', 'git+http')
591 allowed_prefixes = ('http', 'svn+http', 'git+http')
592
592
593 if 'http' in url[:4]:
593 if 'http' in url[:4]:
594 # initially check if it's at least the proper URL
594 # initially check if it's at least the proper URL
595 # or does it pass basic auth
595 # or does it pass basic auth
596 MercurialRepository.check_url(url, config)
596 MercurialRepository.check_url(url, config)
597 elif 'svn+http' in url[:8]: # svn->hg import
597 elif 'svn+http' in url[:8]: # svn->hg import
598 SubversionRepository.check_url(url, config)
598 SubversionRepository.check_url(url, config)
599 elif 'git+http' in url[:8]: # git->hg import
599 elif 'git+http' in url[:8]: # git->hg import
600 raise NotImplementedError()
600 raise NotImplementedError()
601 else:
601 else:
602 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
602 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
603 'Allowed url must start with one of %s'
603 'Allowed url must start with one of %s'
604 % (url, ','.join(allowed_prefixes)))
604 % (url, ','.join(allowed_prefixes)))
605 exc.allowed_prefixes = allowed_prefixes
605 exc.allowed_prefixes = allowed_prefixes
606 raise exc
606 raise exc
607
607
608 elif repo_type == 'git':
608 elif repo_type == 'git':
609 allowed_prefixes = ('http', 'svn+http', 'hg+http')
609 allowed_prefixes = ('http', 'svn+http', 'hg+http')
610 if 'http' in url[:4]:
610 if 'http' in url[:4]:
611 # initially check if it's at least the proper URL
611 # initially check if it's at least the proper URL
612 # or does it pass basic auth
612 # or does it pass basic auth
613 GitRepository.check_url(url, config)
613 GitRepository.check_url(url, config)
614 elif 'svn+http' in url[:8]: # svn->git import
614 elif 'svn+http' in url[:8]: # svn->git import
615 raise NotImplementedError()
615 raise NotImplementedError()
616 elif 'hg+http' in url[:8]: # hg->git import
616 elif 'hg+http' in url[:8]: # hg->git import
617 raise NotImplementedError()
617 raise NotImplementedError()
618 else:
618 else:
619 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
619 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
620 'Allowed url must start with one of %s'
620 'Allowed url must start with one of %s'
621 % (url, ','.join(allowed_prefixes)))
621 % (url, ','.join(allowed_prefixes)))
622 exc.allowed_prefixes = allowed_prefixes
622 exc.allowed_prefixes = allowed_prefixes
623 raise exc
623 raise exc
624
624
625 class _validator(formencode.validators.FancyValidator):
625 class _validator(formencode.validators.FancyValidator):
626 messages = {
626 messages = {
627 'clone_uri': _(u'invalid clone url for %(rtype)s repository'),
627 'clone_uri': _(u'invalid clone url for %(rtype)s repository'),
628 'invalid_clone_uri': _(
628 'invalid_clone_uri': _(
629 u'Invalid clone url, provide a valid clone '
629 u'Invalid clone url, provide a valid clone '
630 u'url starting with one of %(allowed_prefixes)s')
630 u'url starting with one of %(allowed_prefixes)s')
631 }
631 }
632
632
633 def validate_python(self, value, state):
633 def validate_python(self, value, state):
634 repo_type = value.get('repo_type')
634 repo_type = value.get('repo_type')
635 url = value.get('clone_uri')
635 url = value.get('clone_uri')
636
636
637 if url:
637 if url:
638 try:
638 try:
639 url_handler(repo_type, url)
639 url_handler(repo_type, url)
640 except InvalidCloneUrl as e:
640 except InvalidCloneUrl as e:
641 log.warning(e)
641 log.warning(e)
642 msg = M(self, 'invalid_clone_uri', state, rtype=repo_type,
642 msg = M(self, 'invalid_clone_uri', state, rtype=repo_type,
643 allowed_prefixes=','.join(e.allowed_prefixes))
643 allowed_prefixes=','.join(e.allowed_prefixes))
644 raise formencode.Invalid(msg, value, state,
644 raise formencode.Invalid(msg, value, state,
645 error_dict={'clone_uri': msg})
645 error_dict={'clone_uri': msg})
646 except Exception:
646 except Exception:
647 log.exception('Url validation failed')
647 log.exception('Url validation failed')
648 msg = M(self, 'clone_uri', state, rtype=repo_type)
648 msg = M(self, 'clone_uri', state, rtype=repo_type)
649 raise formencode.Invalid(msg, value, state,
649 raise formencode.Invalid(msg, value, state,
650 error_dict={'clone_uri': msg})
650 error_dict={'clone_uri': msg})
651 return _validator
651 return _validator
652
652
653
653
654 def ValidForkType(localizer, old_data=None):
654 def ValidForkType(localizer, old_data=None):
655 _ = localizer
655 _ = localizer
656 old_data = old_data or {}
656 old_data = old_data or {}
657
657
658 class _validator(formencode.validators.FancyValidator):
658 class _validator(formencode.validators.FancyValidator):
659 messages = {
659 messages = {
660 'invalid_fork_type': _(u'Fork have to be the same type as parent')
660 'invalid_fork_type': _(u'Fork have to be the same type as parent')
661 }
661 }
662
662
663 def validate_python(self, value, state):
663 def validate_python(self, value, state):
664 if old_data['repo_type'] != value:
664 if old_data['repo_type'] != value:
665 msg = M(self, 'invalid_fork_type', state)
665 msg = M(self, 'invalid_fork_type', state)
666 raise formencode.Invalid(
666 raise formencode.Invalid(
667 msg, value, state, error_dict={'repo_type': msg}
667 msg, value, state, error_dict={'repo_type': msg}
668 )
668 )
669 return _validator
669 return _validator
670
670
671
671
672 def CanWriteGroup(localizer, old_data=None):
672 def CanWriteGroup(localizer, old_data=None):
673 _ = localizer
673 _ = localizer
674
674
675 class _validator(formencode.validators.FancyValidator):
675 class _validator(formencode.validators.FancyValidator):
676 messages = {
676 messages = {
677 'permission_denied': _(
677 'permission_denied': _(
678 u"You do not have the permission "
678 u"You do not have the permission "
679 u"to create repositories in this group."),
679 u"to create repositories in this group."),
680 'permission_denied_root': _(
680 'permission_denied_root': _(
681 u"You do not have the permission to store repositories in "
681 u"You do not have the permission to store repositories in "
682 u"the root location.")
682 u"the root location.")
683 }
683 }
684
684
685 def _to_python(self, value, state):
685 def _to_python(self, value, state):
686 # root location
686 # root location
687 if value in [-1, "-1"]:
687 if value in [-1, "-1"]:
688 return None
688 return None
689 return value
689 return value
690
690
691 def validate_python(self, value, state):
691 def validate_python(self, value, state):
692 gr = RepoGroup.get(value)
692 gr = RepoGroup.get(value)
693 gr_name = gr.group_name if gr else None # None means ROOT location
693 gr_name = gr.group_name if gr else None # None means ROOT location
694 # create repositories with write permission on group is set to true
694 # create repositories with write permission on group is set to true
695 create_on_write = HasPermissionAny(
695 create_on_write = HasPermissionAny(
696 'hg.create.write_on_repogroup.true')()
696 'hg.create.write_on_repogroup.true')()
697 group_admin = HasRepoGroupPermissionAny('group.admin')(
697 group_admin = HasRepoGroupPermissionAny('group.admin')(
698 gr_name, 'can write into group validator')
698 gr_name, 'can write into group validator')
699 group_write = HasRepoGroupPermissionAny('group.write')(
699 group_write = HasRepoGroupPermissionAny('group.write')(
700 gr_name, 'can write into group validator')
700 gr_name, 'can write into group validator')
701 forbidden = not (group_admin or (group_write and create_on_write))
701 forbidden = not (group_admin or (group_write and create_on_write))
702 can_create_repos = HasPermissionAny(
702 can_create_repos = HasPermissionAny(
703 'hg.admin', 'hg.create.repository')
703 'hg.admin', 'hg.create.repository')
704 gid = (old_data['repo_group'].get('group_id')
704 gid = (old_data['repo_group'].get('group_id')
705 if (old_data and 'repo_group' in old_data) else None)
705 if (old_data and 'repo_group' in old_data) else None)
706 value_changed = gid != safe_int(value)
706 value_changed = gid != safe_int(value)
707 new = not old_data
707 new = not old_data
708 # do check if we changed the value, there's a case that someone got
708 # do check if we changed the value, there's a case that someone got
709 # revoked write permissions to a repository, he still created, we
709 # revoked write permissions to a repository, he still created, we
710 # don't need to check permission if he didn't change the value of
710 # don't need to check permission if he didn't change the value of
711 # groups in form box
711 # groups in form box
712 if value_changed or new:
712 if value_changed or new:
713 # parent group need to be existing
713 # parent group need to be existing
714 if gr and forbidden:
714 if gr and forbidden:
715 msg = M(self, 'permission_denied', state)
715 msg = M(self, 'permission_denied', state)
716 raise formencode.Invalid(
716 raise formencode.Invalid(
717 msg, value, state, error_dict={'repo_type': msg}
717 msg, value, state, error_dict={'repo_type': msg}
718 )
718 )
719 # check if we can write to root location !
719 # check if we can write to root location !
720 elif gr is None and not can_create_repos():
720 elif gr is None and not can_create_repos():
721 msg = M(self, 'permission_denied_root', state)
721 msg = M(self, 'permission_denied_root', state)
722 raise formencode.Invalid(
722 raise formencode.Invalid(
723 msg, value, state, error_dict={'repo_type': msg}
723 msg, value, state, error_dict={'repo_type': msg}
724 )
724 )
725 return _validator
725 return _validator
726
726
727
727
728 def ValidPerms(localizer, type_='repo'):
728 def ValidPerms(localizer, type_='repo'):
729 _ = localizer
729 _ = localizer
730 if type_ == 'repo_group':
730 if type_ == 'repo_group':
731 EMPTY_PERM = 'group.none'
731 EMPTY_PERM = 'group.none'
732 elif type_ == 'repo':
732 elif type_ == 'repo':
733 EMPTY_PERM = 'repository.none'
733 EMPTY_PERM = 'repository.none'
734 elif type_ == 'user_group':
734 elif type_ == 'user_group':
735 EMPTY_PERM = 'usergroup.none'
735 EMPTY_PERM = 'usergroup.none'
736
736
737 class _validator(formencode.validators.FancyValidator):
737 class _validator(formencode.validators.FancyValidator):
738 messages = {
738 messages = {
739 'perm_new_member_name':
739 'perm_new_member_name':
740 _(u'This username or user group name is not valid')
740 _(u'This username or user group name is not valid')
741 }
741 }
742
742
743 def _to_python(self, value, state):
743 def _to_python(self, value, state):
744 perm_updates = OrderedSet()
744 perm_updates = OrderedSet()
745 perm_additions = OrderedSet()
745 perm_additions = OrderedSet()
746 perm_deletions = OrderedSet()
746 perm_deletions = OrderedSet()
747 # build a list of permission to update/delete and new permission
747 # build a list of permission to update/delete and new permission
748
748
749 # Read the perm_new_member/perm_del_member attributes and group
749 # Read the perm_new_member/perm_del_member attributes and group
750 # them by they IDs
750 # them by they IDs
751 new_perms_group = collections.defaultdict(dict)
751 new_perms_group = collections.defaultdict(dict)
752 del_perms_group = collections.defaultdict(dict)
752 del_perms_group = collections.defaultdict(dict)
753 for k, v in value.copy().iteritems():
753 for k, v in value.copy().iteritems():
754 if k.startswith('perm_del_member'):
754 if k.startswith('perm_del_member'):
755 # delete from org storage so we don't process that later
755 # delete from org storage so we don't process that later
756 del value[k]
756 del value[k]
757 # part is `id`, `type`
757 # part is `id`, `type`
758 _type, part = k.split('perm_del_member_')
758 _type, part = k.split('perm_del_member_')
759 args = part.split('_')
759 args = part.split('_')
760 if len(args) == 2:
760 if len(args) == 2:
761 _key, pos = args
761 _key, pos = args
762 del_perms_group[pos][_key] = v
762 del_perms_group[pos][_key] = v
763 if k.startswith('perm_new_member'):
763 if k.startswith('perm_new_member'):
764 # delete from org storage so we don't process that later
764 # delete from org storage so we don't process that later
765 del value[k]
765 del value[k]
766 # part is `id`, `type`, `perm`
766 # part is `id`, `type`, `perm`
767 _type, part = k.split('perm_new_member_')
767 _type, part = k.split('perm_new_member_')
768 args = part.split('_')
768 args = part.split('_')
769 if len(args) == 2:
769 if len(args) == 2:
770 _key, pos = args
770 _key, pos = args
771 new_perms_group[pos][_key] = v
771 new_perms_group[pos][_key] = v
772
772
773 # store the deletes
773 # store the deletes
774 for k in sorted(del_perms_group.keys()):
774 for k in sorted(del_perms_group.keys()):
775 perm_dict = del_perms_group[k]
775 perm_dict = del_perms_group[k]
776 del_member = perm_dict.get('id')
776 del_member = perm_dict.get('id')
777 del_type = perm_dict.get('type')
777 del_type = perm_dict.get('type')
778 if del_member and del_type:
778 if del_member and del_type:
779 perm_deletions.add(
779 perm_deletions.add(
780 (del_member, None, del_type))
780 (del_member, None, del_type))
781
781
782 # store additions in order of how they were added in web form
782 # store additions in order of how they were added in web form
783 for k in sorted(new_perms_group.keys()):
783 for k in sorted(new_perms_group.keys()):
784 perm_dict = new_perms_group[k]
784 perm_dict = new_perms_group[k]
785 new_member = perm_dict.get('id')
785 new_member = perm_dict.get('id')
786 new_type = perm_dict.get('type')
786 new_type = perm_dict.get('type')
787 new_perm = perm_dict.get('perm')
787 new_perm = perm_dict.get('perm')
788 if new_member and new_perm and new_type:
788 if new_member and new_perm and new_type:
789 perm_additions.add(
789 perm_additions.add(
790 (new_member, new_perm, new_type))
790 (new_member, new_perm, new_type))
791
791
792 # get updates of permissions
792 # get updates of permissions
793 # (read the existing radio button states)
793 # (read the existing radio button states)
794 default_user_id = User.get_default_user().user_id
794 default_user_id = User.get_default_user_id()
795
795
796 for k, update_value in value.iteritems():
796 for k, update_value in value.iteritems():
797 if k.startswith('u_perm_') or k.startswith('g_perm_'):
797 if k.startswith('u_perm_') or k.startswith('g_perm_'):
798 obj_type = k[0]
798 obj_type = k[0]
799 obj_id = k[7:]
799 obj_id = k[7:]
800 update_type = {'u': 'user',
800 update_type = {'u': 'user',
801 'g': 'user_group'}[obj_type]
801 'g': 'user_group'}[obj_type]
802
802
803 if obj_type == 'u' and safe_int(obj_id) == default_user_id:
803 if obj_type == 'u' and safe_int(obj_id) == default_user_id:
804 if str2bool(value.get('repo_private')):
804 if str2bool(value.get('repo_private')):
805 # prevent from updating default user permissions
805 # prevent from updating default user permissions
806 # when this repository is marked as private
806 # when this repository is marked as private
807 update_value = EMPTY_PERM
807 update_value = EMPTY_PERM
808
808
809 perm_updates.add(
809 perm_updates.add(
810 (obj_id, update_value, update_type))
810 (obj_id, update_value, update_type))
811
811
812 value['perm_additions'] = [] # propagated later
812 value['perm_additions'] = [] # propagated later
813 value['perm_updates'] = list(perm_updates)
813 value['perm_updates'] = list(perm_updates)
814 value['perm_deletions'] = list(perm_deletions)
814 value['perm_deletions'] = list(perm_deletions)
815
815
816 updates_map = dict(
816 updates_map = dict(
817 (x[0], (x[1], x[2])) for x in value['perm_updates'])
817 (x[0], (x[1], x[2])) for x in value['perm_updates'])
818 # make sure Additions don't override updates.
818 # make sure Additions don't override updates.
819 for member_id, perm, member_type in list(perm_additions):
819 for member_id, perm, member_type in list(perm_additions):
820 if member_id in updates_map:
820 if member_id in updates_map:
821 perm = updates_map[member_id][0]
821 perm = updates_map[member_id][0]
822 value['perm_additions'].append((member_id, perm, member_type))
822 value['perm_additions'].append((member_id, perm, member_type))
823
823
824 # on new entries validate users they exist and they are active !
824 # on new entries validate users they exist and they are active !
825 # this leaves feedback to the form
825 # this leaves feedback to the form
826 try:
826 try:
827 if member_type == 'user':
827 if member_type == 'user':
828 User.query()\
828 User.query()\
829 .filter(User.active == true())\
829 .filter(User.active == true())\
830 .filter(User.user_id == member_id).one()
830 .filter(User.user_id == member_id).one()
831 if member_type == 'user_group':
831 if member_type == 'user_group':
832 UserGroup.query()\
832 UserGroup.query()\
833 .filter(UserGroup.users_group_active == true())\
833 .filter(UserGroup.users_group_active == true())\
834 .filter(UserGroup.users_group_id == member_id)\
834 .filter(UserGroup.users_group_id == member_id)\
835 .one()
835 .one()
836
836
837 except Exception:
837 except Exception:
838 log.exception('Updated permission failed: org_exc:')
838 log.exception('Updated permission failed: org_exc:')
839 msg = M(self, 'perm_new_member_type', state)
839 msg = M(self, 'perm_new_member_type', state)
840 raise formencode.Invalid(
840 raise formencode.Invalid(
841 msg, value, state, error_dict={
841 msg, value, state, error_dict={
842 'perm_new_member_name': msg}
842 'perm_new_member_name': msg}
843 )
843 )
844 return value
844 return value
845 return _validator
845 return _validator
846
846
847
847
848 def ValidPath(localizer):
848 def ValidPath(localizer):
849 _ = localizer
849 _ = localizer
850
850
851 class _validator(formencode.validators.FancyValidator):
851 class _validator(formencode.validators.FancyValidator):
852 messages = {
852 messages = {
853 'invalid_path': _(u'This is not a valid path')
853 'invalid_path': _(u'This is not a valid path')
854 }
854 }
855
855
856 def validate_python(self, value, state):
856 def validate_python(self, value, state):
857 if not os.path.isdir(value):
857 if not os.path.isdir(value):
858 msg = M(self, 'invalid_path', state)
858 msg = M(self, 'invalid_path', state)
859 raise formencode.Invalid(
859 raise formencode.Invalid(
860 msg, value, state, error_dict={'paths_root_path': msg}
860 msg, value, state, error_dict={'paths_root_path': msg}
861 )
861 )
862 return _validator
862 return _validator
863
863
864
864
865 def UniqSystemEmail(localizer, old_data=None):
865 def UniqSystemEmail(localizer, old_data=None):
866 _ = localizer
866 _ = localizer
867 old_data = old_data or {}
867 old_data = old_data or {}
868
868
869 class _validator(formencode.validators.FancyValidator):
869 class _validator(formencode.validators.FancyValidator):
870 messages = {
870 messages = {
871 'email_taken': _(u'This e-mail address is already taken')
871 'email_taken': _(u'This e-mail address is already taken')
872 }
872 }
873
873
874 def _to_python(self, value, state):
874 def _to_python(self, value, state):
875 return value.lower()
875 return value.lower()
876
876
877 def validate_python(self, value, state):
877 def validate_python(self, value, state):
878 if (old_data.get('email') or '').lower() != value:
878 if (old_data.get('email') or '').lower() != value:
879 user = User.get_by_email(value, case_insensitive=True)
879 user = User.get_by_email(value, case_insensitive=True)
880 if user:
880 if user:
881 msg = M(self, 'email_taken', state)
881 msg = M(self, 'email_taken', state)
882 raise formencode.Invalid(
882 raise formencode.Invalid(
883 msg, value, state, error_dict={'email': msg}
883 msg, value, state, error_dict={'email': msg}
884 )
884 )
885 return _validator
885 return _validator
886
886
887
887
888 def ValidSystemEmail(localizer):
888 def ValidSystemEmail(localizer):
889 _ = localizer
889 _ = localizer
890
890
891 class _validator(formencode.validators.FancyValidator):
891 class _validator(formencode.validators.FancyValidator):
892 messages = {
892 messages = {
893 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
893 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
894 }
894 }
895
895
896 def _to_python(self, value, state):
896 def _to_python(self, value, state):
897 return value.lower()
897 return value.lower()
898
898
899 def validate_python(self, value, state):
899 def validate_python(self, value, state):
900 user = User.get_by_email(value, case_insensitive=True)
900 user = User.get_by_email(value, case_insensitive=True)
901 if user is None:
901 if user is None:
902 msg = M(self, 'non_existing_email', state, email=value)
902 msg = M(self, 'non_existing_email', state, email=value)
903 raise formencode.Invalid(
903 raise formencode.Invalid(
904 msg, value, state, error_dict={'email': msg}
904 msg, value, state, error_dict={'email': msg}
905 )
905 )
906 return _validator
906 return _validator
907
907
908
908
909 def NotReviewedRevisions(localizer, repo_id):
909 def NotReviewedRevisions(localizer, repo_id):
910 _ = localizer
910 _ = localizer
911 class _validator(formencode.validators.FancyValidator):
911 class _validator(formencode.validators.FancyValidator):
912 messages = {
912 messages = {
913 'rev_already_reviewed':
913 'rev_already_reviewed':
914 _(u'Revisions %(revs)s are already part of pull request '
914 _(u'Revisions %(revs)s are already part of pull request '
915 u'or have set status'),
915 u'or have set status'),
916 }
916 }
917
917
918 def validate_python(self, value, state):
918 def validate_python(self, value, state):
919 # check revisions if they are not reviewed, or a part of another
919 # check revisions if they are not reviewed, or a part of another
920 # pull request
920 # pull request
921 statuses = ChangesetStatus.query()\
921 statuses = ChangesetStatus.query()\
922 .filter(ChangesetStatus.revision.in_(value))\
922 .filter(ChangesetStatus.revision.in_(value))\
923 .filter(ChangesetStatus.repo_id == repo_id)\
923 .filter(ChangesetStatus.repo_id == repo_id)\
924 .all()
924 .all()
925
925
926 errors = []
926 errors = []
927 for status in statuses:
927 for status in statuses:
928 if status.pull_request_id:
928 if status.pull_request_id:
929 errors.append(['pull_req', status.revision[:12]])
929 errors.append(['pull_req', status.revision[:12]])
930 elif status.status:
930 elif status.status:
931 errors.append(['status', status.revision[:12]])
931 errors.append(['status', status.revision[:12]])
932
932
933 if errors:
933 if errors:
934 revs = ','.join([x[1] for x in errors])
934 revs = ','.join([x[1] for x in errors])
935 msg = M(self, 'rev_already_reviewed', state, revs=revs)
935 msg = M(self, 'rev_already_reviewed', state, revs=revs)
936 raise formencode.Invalid(
936 raise formencode.Invalid(
937 msg, value, state, error_dict={'revisions': revs})
937 msg, value, state, error_dict={'revisions': revs})
938
938
939 return _validator
939 return _validator
940
940
941
941
942 def ValidIp(localizer):
942 def ValidIp(localizer):
943 _ = localizer
943 _ = localizer
944
944
945 class _validator(CIDR):
945 class _validator(CIDR):
946 messages = {
946 messages = {
947 'badFormat': _(u'Please enter a valid IPv4 or IpV6 address'),
947 'badFormat': _(u'Please enter a valid IPv4 or IpV6 address'),
948 'illegalBits': _(
948 'illegalBits': _(
949 u'The network size (bits) must be within the range '
949 u'The network size (bits) must be within the range '
950 u'of 0-32 (not %(bits)r)'),
950 u'of 0-32 (not %(bits)r)'),
951 }
951 }
952
952
953 # we ovveride the default to_python() call
953 # we ovveride the default to_python() call
954 def to_python(self, value, state):
954 def to_python(self, value, state):
955 v = super(_validator, self).to_python(value, state)
955 v = super(_validator, self).to_python(value, state)
956 v = safe_unicode(v.strip())
956 v = safe_unicode(v.strip())
957 net = ipaddress.ip_network(address=v, strict=False)
957 net = ipaddress.ip_network(address=v, strict=False)
958 return str(net)
958 return str(net)
959
959
960 def validate_python(self, value, state):
960 def validate_python(self, value, state):
961 try:
961 try:
962 addr = safe_unicode(value.strip())
962 addr = safe_unicode(value.strip())
963 # this raises an ValueError if address is not IpV4 or IpV6
963 # this raises an ValueError if address is not IpV4 or IpV6
964 ipaddress.ip_network(addr, strict=False)
964 ipaddress.ip_network(addr, strict=False)
965 except ValueError:
965 except ValueError:
966 raise formencode.Invalid(self.message('badFormat', state),
966 raise formencode.Invalid(self.message('badFormat', state),
967 value, state)
967 value, state)
968 return _validator
968 return _validator
969
969
970
970
971 def FieldKey(localizer):
971 def FieldKey(localizer):
972 _ = localizer
972 _ = localizer
973
973
974 class _validator(formencode.validators.FancyValidator):
974 class _validator(formencode.validators.FancyValidator):
975 messages = {
975 messages = {
976 'badFormat': _(
976 'badFormat': _(
977 u'Key name can only consist of letters, '
977 u'Key name can only consist of letters, '
978 u'underscore, dash or numbers'),
978 u'underscore, dash or numbers'),
979 }
979 }
980
980
981 def validate_python(self, value, state):
981 def validate_python(self, value, state):
982 if not re.match('[a-zA-Z0-9_-]+$', value):
982 if not re.match('[a-zA-Z0-9_-]+$', value):
983 raise formencode.Invalid(self.message('badFormat', state),
983 raise formencode.Invalid(self.message('badFormat', state),
984 value, state)
984 value, state)
985 return _validator
985 return _validator
986
986
987
987
988 def ValidAuthPlugins(localizer):
988 def ValidAuthPlugins(localizer):
989 _ = localizer
989 _ = localizer
990
990
991 class _validator(formencode.validators.FancyValidator):
991 class _validator(formencode.validators.FancyValidator):
992 messages = {
992 messages = {
993 'import_duplicate': _(
993 'import_duplicate': _(
994 u'Plugins %(loaded)s and %(next_to_load)s '
994 u'Plugins %(loaded)s and %(next_to_load)s '
995 u'both export the same name'),
995 u'both export the same name'),
996 'missing_includeme': _(
996 'missing_includeme': _(
997 u'The plugin "%(plugin_id)s" is missing an includeme '
997 u'The plugin "%(plugin_id)s" is missing an includeme '
998 u'function.'),
998 u'function.'),
999 'import_error': _(
999 'import_error': _(
1000 u'Can not load plugin "%(plugin_id)s"'),
1000 u'Can not load plugin "%(plugin_id)s"'),
1001 'no_plugin': _(
1001 'no_plugin': _(
1002 u'No plugin available with ID "%(plugin_id)s"'),
1002 u'No plugin available with ID "%(plugin_id)s"'),
1003 }
1003 }
1004
1004
1005 def _to_python(self, value, state):
1005 def _to_python(self, value, state):
1006 # filter empty values
1006 # filter empty values
1007 return filter(lambda s: s not in [None, ''], value)
1007 return filter(lambda s: s not in [None, ''], value)
1008
1008
1009 def _validate_legacy_plugin_id(self, plugin_id, value, state):
1009 def _validate_legacy_plugin_id(self, plugin_id, value, state):
1010 """
1010 """
1011 Validates that the plugin import works. It also checks that the
1011 Validates that the plugin import works. It also checks that the
1012 plugin has an includeme attribute.
1012 plugin has an includeme attribute.
1013 """
1013 """
1014 try:
1014 try:
1015 plugin = _import_legacy_plugin(plugin_id)
1015 plugin = _import_legacy_plugin(plugin_id)
1016 except Exception as e:
1016 except Exception as e:
1017 log.exception(
1017 log.exception(
1018 'Exception during import of auth legacy plugin "{}"'
1018 'Exception during import of auth legacy plugin "{}"'
1019 .format(plugin_id))
1019 .format(plugin_id))
1020 msg = M(self, 'import_error', state, plugin_id=plugin_id)
1020 msg = M(self, 'import_error', state, plugin_id=plugin_id)
1021 raise formencode.Invalid(msg, value, state)
1021 raise formencode.Invalid(msg, value, state)
1022
1022
1023 if not hasattr(plugin, 'includeme'):
1023 if not hasattr(plugin, 'includeme'):
1024 msg = M(self, 'missing_includeme', state, plugin_id=plugin_id)
1024 msg = M(self, 'missing_includeme', state, plugin_id=plugin_id)
1025 raise formencode.Invalid(msg, value, state)
1025 raise formencode.Invalid(msg, value, state)
1026
1026
1027 return plugin
1027 return plugin
1028
1028
1029 def _validate_plugin_id(self, plugin_id, value, state):
1029 def _validate_plugin_id(self, plugin_id, value, state):
1030 """
1030 """
1031 Plugins are already imported during app start up. Therefore this
1031 Plugins are already imported during app start up. Therefore this
1032 validation only retrieves the plugin from the plugin registry and
1032 validation only retrieves the plugin from the plugin registry and
1033 if it returns something not None everything is OK.
1033 if it returns something not None everything is OK.
1034 """
1034 """
1035 plugin = loadplugin(plugin_id)
1035 plugin = loadplugin(plugin_id)
1036
1036
1037 if plugin is None:
1037 if plugin is None:
1038 msg = M(self, 'no_plugin', state, plugin_id=plugin_id)
1038 msg = M(self, 'no_plugin', state, plugin_id=plugin_id)
1039 raise formencode.Invalid(msg, value, state)
1039 raise formencode.Invalid(msg, value, state)
1040
1040
1041 return plugin
1041 return plugin
1042
1042
1043 def validate_python(self, value, state):
1043 def validate_python(self, value, state):
1044 unique_names = {}
1044 unique_names = {}
1045 for plugin_id in value:
1045 for plugin_id in value:
1046
1046
1047 # Validate legacy or normal plugin.
1047 # Validate legacy or normal plugin.
1048 if plugin_id.startswith(legacy_plugin_prefix):
1048 if plugin_id.startswith(legacy_plugin_prefix):
1049 plugin = self._validate_legacy_plugin_id(
1049 plugin = self._validate_legacy_plugin_id(
1050 plugin_id, value, state)
1050 plugin_id, value, state)
1051 else:
1051 else:
1052 plugin = self._validate_plugin_id(plugin_id, value, state)
1052 plugin = self._validate_plugin_id(plugin_id, value, state)
1053
1053
1054 # Only allow unique plugin names.
1054 # Only allow unique plugin names.
1055 if plugin.name in unique_names:
1055 if plugin.name in unique_names:
1056 msg = M(self, 'import_duplicate', state,
1056 msg = M(self, 'import_duplicate', state,
1057 loaded=unique_names[plugin.name],
1057 loaded=unique_names[plugin.name],
1058 next_to_load=plugin)
1058 next_to_load=plugin)
1059 raise formencode.Invalid(msg, value, state)
1059 raise formencode.Invalid(msg, value, state)
1060 unique_names[plugin.name] = plugin
1060 unique_names[plugin.name] = plugin
1061 return _validator
1061 return _validator
1062
1062
1063
1063
1064 def ValidPattern(localizer):
1064 def ValidPattern(localizer):
1065 _ = localizer
1065 _ = localizer
1066
1066
1067 class _validator(formencode.validators.FancyValidator):
1067 class _validator(formencode.validators.FancyValidator):
1068 messages = {
1068 messages = {
1069 'bad_format': _(u'Url must start with http or /'),
1069 'bad_format': _(u'Url must start with http or /'),
1070 }
1070 }
1071
1071
1072 def _to_python(self, value, state):
1072 def _to_python(self, value, state):
1073 patterns = []
1073 patterns = []
1074
1074
1075 prefix = 'new_pattern'
1075 prefix = 'new_pattern'
1076 for name, v in value.iteritems():
1076 for name, v in value.iteritems():
1077 pattern_name = '_'.join((prefix, 'pattern'))
1077 pattern_name = '_'.join((prefix, 'pattern'))
1078 if name.startswith(pattern_name):
1078 if name.startswith(pattern_name):
1079 new_item_id = name[len(pattern_name)+1:]
1079 new_item_id = name[len(pattern_name)+1:]
1080
1080
1081 def _field(name):
1081 def _field(name):
1082 return '%s_%s_%s' % (prefix, name, new_item_id)
1082 return '%s_%s_%s' % (prefix, name, new_item_id)
1083
1083
1084 values = {
1084 values = {
1085 'issuetracker_pat': value.get(_field('pattern')),
1085 'issuetracker_pat': value.get(_field('pattern')),
1086 'issuetracker_url': value.get(_field('url')),
1086 'issuetracker_url': value.get(_field('url')),
1087 'issuetracker_pref': value.get(_field('prefix')),
1087 'issuetracker_pref': value.get(_field('prefix')),
1088 'issuetracker_desc': value.get(_field('description'))
1088 'issuetracker_desc': value.get(_field('description'))
1089 }
1089 }
1090 new_uid = md5(values['issuetracker_pat'])
1090 new_uid = md5(values['issuetracker_pat'])
1091
1091
1092 has_required_fields = (
1092 has_required_fields = (
1093 values['issuetracker_pat']
1093 values['issuetracker_pat']
1094 and values['issuetracker_url'])
1094 and values['issuetracker_url'])
1095
1095
1096 if has_required_fields:
1096 if has_required_fields:
1097 # validate url that it starts with http or /
1097 # validate url that it starts with http or /
1098 # otherwise it can lead to JS injections
1098 # otherwise it can lead to JS injections
1099 # e.g specifig javascript:<malicios code>
1099 # e.g specifig javascript:<malicios code>
1100 if not values['issuetracker_url'].startswith(('http', '/')):
1100 if not values['issuetracker_url'].startswith(('http', '/')):
1101 raise formencode.Invalid(
1101 raise formencode.Invalid(
1102 self.message('bad_format', state),
1102 self.message('bad_format', state),
1103 value, state)
1103 value, state)
1104
1104
1105 settings = [
1105 settings = [
1106 ('_'.join((key, new_uid)), values[key], 'unicode')
1106 ('_'.join((key, new_uid)), values[key], 'unicode')
1107 for key in values]
1107 for key in values]
1108 patterns.append(settings)
1108 patterns.append(settings)
1109
1109
1110 value['patterns'] = patterns
1110 value['patterns'] = patterns
1111 delete_patterns = value.get('uid') or []
1111 delete_patterns = value.get('uid') or []
1112 if not isinstance(delete_patterns, (list, tuple)):
1112 if not isinstance(delete_patterns, (list, tuple)):
1113 delete_patterns = [delete_patterns]
1113 delete_patterns = [delete_patterns]
1114 value['delete_patterns'] = delete_patterns
1114 value['delete_patterns'] = delete_patterns
1115 return value
1115 return value
1116 return _validator
1116 return _validator
@@ -1,632 +1,632 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 from hashlib import sha1
22 from hashlib import sha1
23
23
24 import pytest
24 import pytest
25 from mock import patch
25 from mock import patch
26
26
27 from rhodecode.lib import auth
27 from rhodecode.lib import auth
28 from rhodecode.lib.utils2 import md5
28 from rhodecode.lib.utils2 import md5
29 from rhodecode.model.auth_token import AuthTokenModel
29 from rhodecode.model.auth_token import AuthTokenModel
30 from rhodecode.model.db import Session, User
30 from rhodecode.model.db import Session, User
31 from rhodecode.model.repo import RepoModel
31 from rhodecode.model.repo import RepoModel
32 from rhodecode.model.user import UserModel
32 from rhodecode.model.user import UserModel
33 from rhodecode.model.user_group import UserGroupModel
33 from rhodecode.model.user_group import UserGroupModel
34
34
35
35
36 def test_perm_origin_dict():
36 def test_perm_origin_dict():
37 pod = auth.PermOriginDict()
37 pod = auth.PermOriginDict()
38 pod['thing'] = 'read', 'default', 1
38 pod['thing'] = 'read', 'default', 1
39 assert pod['thing'] == 'read'
39 assert pod['thing'] == 'read'
40
40
41 assert pod.perm_origin_stack == {
41 assert pod.perm_origin_stack == {
42 'thing': [('read', 'default', 1)]}
42 'thing': [('read', 'default', 1)]}
43
43
44 pod['thing'] = 'write', 'admin', 1
44 pod['thing'] = 'write', 'admin', 1
45 assert pod['thing'] == 'write'
45 assert pod['thing'] == 'write'
46
46
47 assert pod.perm_origin_stack == {
47 assert pod.perm_origin_stack == {
48 'thing': [('read', 'default', 1), ('write', 'admin', 1)]}
48 'thing': [('read', 'default', 1), ('write', 'admin', 1)]}
49
49
50 pod['other'] = 'write', 'default', 8
50 pod['other'] = 'write', 'default', 8
51
51
52 assert pod.perm_origin_stack == {
52 assert pod.perm_origin_stack == {
53 'other': [('write', 'default', 8)],
53 'other': [('write', 'default', 8)],
54 'thing': [('read', 'default', 1), ('write', 'admin', 1)]}
54 'thing': [('read', 'default', 1), ('write', 'admin', 1)]}
55
55
56 pod['other'] = 'none', 'override', 8
56 pod['other'] = 'none', 'override', 8
57
57
58 assert pod.perm_origin_stack == {
58 assert pod.perm_origin_stack == {
59 'other': [('write', 'default', 8), ('none', 'override', 8)],
59 'other': [('write', 'default', 8), ('none', 'override', 8)],
60 'thing': [('read', 'default', 1), ('write', 'admin', 1)]}
60 'thing': [('read', 'default', 1), ('write', 'admin', 1)]}
61
61
62 with pytest.raises(ValueError):
62 with pytest.raises(ValueError):
63 pod['thing'] = 'read'
63 pod['thing'] = 'read'
64
64
65
65
66 def test_cached_perms_data(user_regular, backend_random):
66 def test_cached_perms_data(user_regular, backend_random):
67 permissions = get_permissions(user_regular)
67 permissions = get_permissions(user_regular)
68 repo_name = backend_random.repo.repo_name
68 repo_name = backend_random.repo.repo_name
69 expected_global_permissions = {
69 expected_global_permissions = {
70 'repository.read', 'group.read', 'usergroup.read'}
70 'repository.read', 'group.read', 'usergroup.read'}
71 assert expected_global_permissions.issubset(permissions['global'])
71 assert expected_global_permissions.issubset(permissions['global'])
72 assert permissions['repositories'][repo_name] == 'repository.read'
72 assert permissions['repositories'][repo_name] == 'repository.read'
73
73
74
74
75 def test_cached_perms_data_with_admin_user(user_regular, backend_random):
75 def test_cached_perms_data_with_admin_user(user_regular, backend_random):
76 permissions = get_permissions(user_regular, user_is_admin=True)
76 permissions = get_permissions(user_regular, user_is_admin=True)
77 repo_name = backend_random.repo.repo_name
77 repo_name = backend_random.repo.repo_name
78 assert 'hg.admin' in permissions['global']
78 assert 'hg.admin' in permissions['global']
79 assert permissions['repositories'][repo_name] == 'repository.admin'
79 assert permissions['repositories'][repo_name] == 'repository.admin'
80
80
81
81
82 def test_cached_perms_data_with_admin_user_extended_calculation(user_regular, backend_random):
82 def test_cached_perms_data_with_admin_user_extended_calculation(user_regular, backend_random):
83 permissions = get_permissions(user_regular, user_is_admin=True,
83 permissions = get_permissions(user_regular, user_is_admin=True,
84 calculate_super_admin=True)
84 calculate_super_admin=True)
85 repo_name = backend_random.repo.repo_name
85 repo_name = backend_random.repo.repo_name
86 assert 'hg.admin' in permissions['global']
86 assert 'hg.admin' in permissions['global']
87 assert permissions['repositories'][repo_name] == 'repository.admin'
87 assert permissions['repositories'][repo_name] == 'repository.admin'
88
88
89
89
90 def test_cached_perms_data_user_group_global_permissions(user_util):
90 def test_cached_perms_data_user_group_global_permissions(user_util):
91 user, user_group = user_util.create_user_with_group()
91 user, user_group = user_util.create_user_with_group()
92 user_group.inherit_default_permissions = False
92 user_group.inherit_default_permissions = False
93
93
94 granted_permission = 'repository.write'
94 granted_permission = 'repository.write'
95 UserGroupModel().grant_perm(user_group, granted_permission)
95 UserGroupModel().grant_perm(user_group, granted_permission)
96 Session().commit()
96 Session().commit()
97
97
98 permissions = get_permissions(user)
98 permissions = get_permissions(user)
99 assert granted_permission in permissions['global']
99 assert granted_permission in permissions['global']
100
100
101
101
102 @pytest.mark.xfail(reason="Not implemented, see TODO note")
102 @pytest.mark.xfail(reason="Not implemented, see TODO note")
103 def test_cached_perms_data_user_group_global_permissions_(user_util):
103 def test_cached_perms_data_user_group_global_permissions_(user_util):
104 user, user_group = user_util.create_user_with_group()
104 user, user_group = user_util.create_user_with_group()
105
105
106 granted_permission = 'repository.write'
106 granted_permission = 'repository.write'
107 UserGroupModel().grant_perm(user_group, granted_permission)
107 UserGroupModel().grant_perm(user_group, granted_permission)
108 Session().commit()
108 Session().commit()
109
109
110 permissions = get_permissions(user)
110 permissions = get_permissions(user)
111 assert granted_permission in permissions['global']
111 assert granted_permission in permissions['global']
112
112
113
113
114 def test_cached_perms_data_user_global_permissions(user_util):
114 def test_cached_perms_data_user_global_permissions(user_util):
115 user = user_util.create_user()
115 user = user_util.create_user()
116 UserModel().grant_perm(user, 'repository.none')
116 UserModel().grant_perm(user, 'repository.none')
117 Session().commit()
117 Session().commit()
118
118
119 permissions = get_permissions(user, user_inherit_default_permissions=True)
119 permissions = get_permissions(user, user_inherit_default_permissions=True)
120 assert 'repository.read' in permissions['global']
120 assert 'repository.read' in permissions['global']
121
121
122
122
123 def test_cached_perms_data_repository_permissions_on_private_repository(
123 def test_cached_perms_data_repository_permissions_on_private_repository(
124 backend_random, user_util):
124 backend_random, user_util):
125 user, user_group = user_util.create_user_with_group()
125 user, user_group = user_util.create_user_with_group()
126
126
127 repo = backend_random.create_repo()
127 repo = backend_random.create_repo()
128 repo.private = True
128 repo.private = True
129
129
130 granted_permission = 'repository.write'
130 granted_permission = 'repository.write'
131 RepoModel().grant_user_group_permission(
131 RepoModel().grant_user_group_permission(
132 repo, user_group.users_group_name, granted_permission)
132 repo, user_group.users_group_name, granted_permission)
133 Session().commit()
133 Session().commit()
134
134
135 permissions = get_permissions(user)
135 permissions = get_permissions(user)
136 assert permissions['repositories'][repo.repo_name] == granted_permission
136 assert permissions['repositories'][repo.repo_name] == granted_permission
137
137
138
138
139 def test_cached_perms_data_repository_permissions_for_owner(
139 def test_cached_perms_data_repository_permissions_for_owner(
140 backend_random, user_util):
140 backend_random, user_util):
141 user = user_util.create_user()
141 user = user_util.create_user()
142
142
143 repo = backend_random.create_repo()
143 repo = backend_random.create_repo()
144 repo.user_id = user.user_id
144 repo.user_id = user.user_id
145
145
146 permissions = get_permissions(user)
146 permissions = get_permissions(user)
147 assert permissions['repositories'][repo.repo_name] == 'repository.admin'
147 assert permissions['repositories'][repo.repo_name] == 'repository.admin'
148
148
149 # TODO: johbo: Make cleanup in UserUtility smarter, then remove this hack
149 # TODO: johbo: Make cleanup in UserUtility smarter, then remove this hack
150 repo.user_id = User.get_default_user().user_id
150 repo.user_id = User.get_default_user_id()
151
151
152
152
153 def test_cached_perms_data_repository_permissions_not_inheriting_defaults(
153 def test_cached_perms_data_repository_permissions_not_inheriting_defaults(
154 backend_random, user_util):
154 backend_random, user_util):
155 user = user_util.create_user()
155 user = user_util.create_user()
156 repo = backend_random.create_repo()
156 repo = backend_random.create_repo()
157
157
158 # Don't inherit default object permissions
158 # Don't inherit default object permissions
159 UserModel().grant_perm(user, 'hg.inherit_default_perms.false')
159 UserModel().grant_perm(user, 'hg.inherit_default_perms.false')
160 Session().commit()
160 Session().commit()
161
161
162 permissions = get_permissions(user)
162 permissions = get_permissions(user)
163 assert permissions['repositories'][repo.repo_name] == 'repository.none'
163 assert permissions['repositories'][repo.repo_name] == 'repository.none'
164
164
165
165
166 def test_cached_perms_data_default_permissions_on_repository_group(user_util):
166 def test_cached_perms_data_default_permissions_on_repository_group(user_util):
167 # Have a repository group with default permissions set
167 # Have a repository group with default permissions set
168 repo_group = user_util.create_repo_group()
168 repo_group = user_util.create_repo_group()
169 default_user = User.get_default_user()
169 default_user = User.get_default_user()
170 user_util.grant_user_permission_to_repo_group(
170 user_util.grant_user_permission_to_repo_group(
171 repo_group, default_user, 'repository.write')
171 repo_group, default_user, 'repository.write')
172 user = user_util.create_user()
172 user = user_util.create_user()
173
173
174 permissions = get_permissions(user)
174 permissions = get_permissions(user)
175 assert permissions['repositories_groups'][repo_group.group_name] == \
175 assert permissions['repositories_groups'][repo_group.group_name] == \
176 'repository.write'
176 'repository.write'
177
177
178
178
179 def test_cached_perms_data_default_permissions_on_repository_group_owner(
179 def test_cached_perms_data_default_permissions_on_repository_group_owner(
180 user_util):
180 user_util):
181 # Have a repository group
181 # Have a repository group
182 repo_group = user_util.create_repo_group()
182 repo_group = user_util.create_repo_group()
183 default_user = User.get_default_user()
183 default_user = User.get_default_user()
184
184
185 # Add a permission for the default user to hit the code path
185 # Add a permission for the default user to hit the code path
186 user_util.grant_user_permission_to_repo_group(
186 user_util.grant_user_permission_to_repo_group(
187 repo_group, default_user, 'repository.write')
187 repo_group, default_user, 'repository.write')
188
188
189 # Have an owner of the group
189 # Have an owner of the group
190 user = user_util.create_user()
190 user = user_util.create_user()
191 repo_group.user_id = user.user_id
191 repo_group.user_id = user.user_id
192
192
193 permissions = get_permissions(user)
193 permissions = get_permissions(user)
194 assert permissions['repositories_groups'][repo_group.group_name] == \
194 assert permissions['repositories_groups'][repo_group.group_name] == \
195 'group.admin'
195 'group.admin'
196
196
197
197
198 def test_cached_perms_data_default_permissions_on_repository_group_no_inherit(
198 def test_cached_perms_data_default_permissions_on_repository_group_no_inherit(
199 user_util):
199 user_util):
200 # Have a repository group
200 # Have a repository group
201 repo_group = user_util.create_repo_group()
201 repo_group = user_util.create_repo_group()
202 default_user = User.get_default_user()
202 default_user = User.get_default_user()
203
203
204 # Add a permission for the default user to hit the code path
204 # Add a permission for the default user to hit the code path
205 user_util.grant_user_permission_to_repo_group(
205 user_util.grant_user_permission_to_repo_group(
206 repo_group, default_user, 'repository.write')
206 repo_group, default_user, 'repository.write')
207
207
208 # Don't inherit default object permissions
208 # Don't inherit default object permissions
209 user = user_util.create_user()
209 user = user_util.create_user()
210 UserModel().grant_perm(user, 'hg.inherit_default_perms.false')
210 UserModel().grant_perm(user, 'hg.inherit_default_perms.false')
211 Session().commit()
211 Session().commit()
212
212
213 permissions = get_permissions(user)
213 permissions = get_permissions(user)
214 assert permissions['repositories_groups'][repo_group.group_name] == \
214 assert permissions['repositories_groups'][repo_group.group_name] == \
215 'group.none'
215 'group.none'
216
216
217
217
218 def test_cached_perms_data_repository_permissions_from_user_group(
218 def test_cached_perms_data_repository_permissions_from_user_group(
219 user_util, backend_random):
219 user_util, backend_random):
220 user, user_group = user_util.create_user_with_group()
220 user, user_group = user_util.create_user_with_group()
221
221
222 # Needs a second user group to make sure that we select the right
222 # Needs a second user group to make sure that we select the right
223 # permissions.
223 # permissions.
224 user_group2 = user_util.create_user_group()
224 user_group2 = user_util.create_user_group()
225 UserGroupModel().add_user_to_group(user_group2, user)
225 UserGroupModel().add_user_to_group(user_group2, user)
226
226
227 repo = backend_random.create_repo()
227 repo = backend_random.create_repo()
228
228
229 RepoModel().grant_user_group_permission(
229 RepoModel().grant_user_group_permission(
230 repo, user_group.users_group_name, 'repository.read')
230 repo, user_group.users_group_name, 'repository.read')
231 RepoModel().grant_user_group_permission(
231 RepoModel().grant_user_group_permission(
232 repo, user_group2.users_group_name, 'repository.write')
232 repo, user_group2.users_group_name, 'repository.write')
233 Session().commit()
233 Session().commit()
234
234
235 permissions = get_permissions(user)
235 permissions = get_permissions(user)
236 assert permissions['repositories'][repo.repo_name] == 'repository.write'
236 assert permissions['repositories'][repo.repo_name] == 'repository.write'
237
237
238
238
239 def test_cached_perms_data_repository_permissions_from_user_group_owner(
239 def test_cached_perms_data_repository_permissions_from_user_group_owner(
240 user_util, backend_random):
240 user_util, backend_random):
241 user, user_group = user_util.create_user_with_group()
241 user, user_group = user_util.create_user_with_group()
242
242
243 repo = backend_random.create_repo()
243 repo = backend_random.create_repo()
244 repo.user_id = user.user_id
244 repo.user_id = user.user_id
245
245
246 RepoModel().grant_user_group_permission(
246 RepoModel().grant_user_group_permission(
247 repo, user_group.users_group_name, 'repository.write')
247 repo, user_group.users_group_name, 'repository.write')
248 Session().commit()
248 Session().commit()
249
249
250 permissions = get_permissions(user)
250 permissions = get_permissions(user)
251 assert permissions['repositories'][repo.repo_name] == 'repository.admin'
251 assert permissions['repositories'][repo.repo_name] == 'repository.admin'
252
252
253
253
254 def test_cached_perms_data_user_repository_permissions(
254 def test_cached_perms_data_user_repository_permissions(
255 user_util, backend_random):
255 user_util, backend_random):
256 user = user_util.create_user()
256 user = user_util.create_user()
257 repo = backend_random.create_repo()
257 repo = backend_random.create_repo()
258 granted_permission = 'repository.write'
258 granted_permission = 'repository.write'
259 RepoModel().grant_user_permission(repo, user, granted_permission)
259 RepoModel().grant_user_permission(repo, user, granted_permission)
260 Session().commit()
260 Session().commit()
261
261
262 permissions = get_permissions(user)
262 permissions = get_permissions(user)
263 assert permissions['repositories'][repo.repo_name] == granted_permission
263 assert permissions['repositories'][repo.repo_name] == granted_permission
264
264
265
265
266 def test_cached_perms_data_user_repository_permissions_explicit(
266 def test_cached_perms_data_user_repository_permissions_explicit(
267 user_util, backend_random):
267 user_util, backend_random):
268 user = user_util.create_user()
268 user = user_util.create_user()
269 repo = backend_random.create_repo()
269 repo = backend_random.create_repo()
270 granted_permission = 'repository.none'
270 granted_permission = 'repository.none'
271 RepoModel().grant_user_permission(repo, user, granted_permission)
271 RepoModel().grant_user_permission(repo, user, granted_permission)
272 Session().commit()
272 Session().commit()
273
273
274 permissions = get_permissions(user, explicit=True)
274 permissions = get_permissions(user, explicit=True)
275 assert permissions['repositories'][repo.repo_name] == granted_permission
275 assert permissions['repositories'][repo.repo_name] == granted_permission
276
276
277
277
278 def test_cached_perms_data_user_repository_permissions_owner(
278 def test_cached_perms_data_user_repository_permissions_owner(
279 user_util, backend_random):
279 user_util, backend_random):
280 user = user_util.create_user()
280 user = user_util.create_user()
281 repo = backend_random.create_repo()
281 repo = backend_random.create_repo()
282 repo.user_id = user.user_id
282 repo.user_id = user.user_id
283 RepoModel().grant_user_permission(repo, user, 'repository.write')
283 RepoModel().grant_user_permission(repo, user, 'repository.write')
284 Session().commit()
284 Session().commit()
285
285
286 permissions = get_permissions(user)
286 permissions = get_permissions(user)
287 assert permissions['repositories'][repo.repo_name] == 'repository.admin'
287 assert permissions['repositories'][repo.repo_name] == 'repository.admin'
288
288
289
289
290 def test_cached_perms_data_repository_groups_permissions_inherited(
290 def test_cached_perms_data_repository_groups_permissions_inherited(
291 user_util, backend_random):
291 user_util, backend_random):
292 user, user_group = user_util.create_user_with_group()
292 user, user_group = user_util.create_user_with_group()
293
293
294 # Needs a second group to hit the last condition
294 # Needs a second group to hit the last condition
295 user_group2 = user_util.create_user_group()
295 user_group2 = user_util.create_user_group()
296 UserGroupModel().add_user_to_group(user_group2, user)
296 UserGroupModel().add_user_to_group(user_group2, user)
297
297
298 repo_group = user_util.create_repo_group()
298 repo_group = user_util.create_repo_group()
299
299
300 user_util.grant_user_group_permission_to_repo_group(
300 user_util.grant_user_group_permission_to_repo_group(
301 repo_group, user_group, 'group.read')
301 repo_group, user_group, 'group.read')
302 user_util.grant_user_group_permission_to_repo_group(
302 user_util.grant_user_group_permission_to_repo_group(
303 repo_group, user_group2, 'group.write')
303 repo_group, user_group2, 'group.write')
304
304
305 permissions = get_permissions(user)
305 permissions = get_permissions(user)
306 assert permissions['repositories_groups'][repo_group.group_name] == \
306 assert permissions['repositories_groups'][repo_group.group_name] == \
307 'group.write'
307 'group.write'
308
308
309
309
310 def test_cached_perms_data_repository_groups_permissions_inherited_owner(
310 def test_cached_perms_data_repository_groups_permissions_inherited_owner(
311 user_util, backend_random):
311 user_util, backend_random):
312 user, user_group = user_util.create_user_with_group()
312 user, user_group = user_util.create_user_with_group()
313 repo_group = user_util.create_repo_group()
313 repo_group = user_util.create_repo_group()
314 repo_group.user_id = user.user_id
314 repo_group.user_id = user.user_id
315
315
316 granted_permission = 'group.write'
316 granted_permission = 'group.write'
317 user_util.grant_user_group_permission_to_repo_group(
317 user_util.grant_user_group_permission_to_repo_group(
318 repo_group, user_group, granted_permission)
318 repo_group, user_group, granted_permission)
319
319
320 permissions = get_permissions(user)
320 permissions = get_permissions(user)
321 assert permissions['repositories_groups'][repo_group.group_name] == \
321 assert permissions['repositories_groups'][repo_group.group_name] == \
322 'group.admin'
322 'group.admin'
323
323
324
324
325 def test_cached_perms_data_repository_groups_permissions(
325 def test_cached_perms_data_repository_groups_permissions(
326 user_util, backend_random):
326 user_util, backend_random):
327 user = user_util.create_user()
327 user = user_util.create_user()
328
328
329 repo_group = user_util.create_repo_group()
329 repo_group = user_util.create_repo_group()
330
330
331 granted_permission = 'group.write'
331 granted_permission = 'group.write'
332 user_util.grant_user_permission_to_repo_group(
332 user_util.grant_user_permission_to_repo_group(
333 repo_group, user, granted_permission)
333 repo_group, user, granted_permission)
334
334
335 permissions = get_permissions(user)
335 permissions = get_permissions(user)
336 assert permissions['repositories_groups'][repo_group.group_name] == \
336 assert permissions['repositories_groups'][repo_group.group_name] == \
337 'group.write'
337 'group.write'
338
338
339
339
340 def test_cached_perms_data_repository_groups_permissions_explicit(
340 def test_cached_perms_data_repository_groups_permissions_explicit(
341 user_util, backend_random):
341 user_util, backend_random):
342 user = user_util.create_user()
342 user = user_util.create_user()
343
343
344 repo_group = user_util.create_repo_group()
344 repo_group = user_util.create_repo_group()
345
345
346 granted_permission = 'group.none'
346 granted_permission = 'group.none'
347 user_util.grant_user_permission_to_repo_group(
347 user_util.grant_user_permission_to_repo_group(
348 repo_group, user, granted_permission)
348 repo_group, user, granted_permission)
349
349
350 permissions = get_permissions(user, explicit=True)
350 permissions = get_permissions(user, explicit=True)
351 assert permissions['repositories_groups'][repo_group.group_name] == \
351 assert permissions['repositories_groups'][repo_group.group_name] == \
352 'group.none'
352 'group.none'
353
353
354
354
355 def test_cached_perms_data_repository_groups_permissions_owner(
355 def test_cached_perms_data_repository_groups_permissions_owner(
356 user_util, backend_random):
356 user_util, backend_random):
357 user = user_util.create_user()
357 user = user_util.create_user()
358
358
359 repo_group = user_util.create_repo_group()
359 repo_group = user_util.create_repo_group()
360 repo_group.user_id = user.user_id
360 repo_group.user_id = user.user_id
361
361
362 granted_permission = 'group.write'
362 granted_permission = 'group.write'
363 user_util.grant_user_permission_to_repo_group(
363 user_util.grant_user_permission_to_repo_group(
364 repo_group, user, granted_permission)
364 repo_group, user, granted_permission)
365
365
366 permissions = get_permissions(user)
366 permissions = get_permissions(user)
367 assert permissions['repositories_groups'][repo_group.group_name] == \
367 assert permissions['repositories_groups'][repo_group.group_name] == \
368 'group.admin'
368 'group.admin'
369
369
370
370
371 def test_cached_perms_data_user_group_permissions_inherited(
371 def test_cached_perms_data_user_group_permissions_inherited(
372 user_util, backend_random):
372 user_util, backend_random):
373 user, user_group = user_util.create_user_with_group()
373 user, user_group = user_util.create_user_with_group()
374 user_group2 = user_util.create_user_group()
374 user_group2 = user_util.create_user_group()
375 UserGroupModel().add_user_to_group(user_group2, user)
375 UserGroupModel().add_user_to_group(user_group2, user)
376
376
377 target_user_group = user_util.create_user_group()
377 target_user_group = user_util.create_user_group()
378
378
379 user_util.grant_user_group_permission_to_user_group(
379 user_util.grant_user_group_permission_to_user_group(
380 target_user_group, user_group, 'usergroup.read')
380 target_user_group, user_group, 'usergroup.read')
381 user_util.grant_user_group_permission_to_user_group(
381 user_util.grant_user_group_permission_to_user_group(
382 target_user_group, user_group2, 'usergroup.write')
382 target_user_group, user_group2, 'usergroup.write')
383
383
384 permissions = get_permissions(user)
384 permissions = get_permissions(user)
385 assert permissions['user_groups'][target_user_group.users_group_name] == \
385 assert permissions['user_groups'][target_user_group.users_group_name] == \
386 'usergroup.write'
386 'usergroup.write'
387
387
388
388
389 def test_cached_perms_data_user_group_permissions(
389 def test_cached_perms_data_user_group_permissions(
390 user_util, backend_random):
390 user_util, backend_random):
391 user = user_util.create_user()
391 user = user_util.create_user()
392 user_group = user_util.create_user_group()
392 user_group = user_util.create_user_group()
393 UserGroupModel().grant_user_permission(user_group, user, 'usergroup.write')
393 UserGroupModel().grant_user_permission(user_group, user, 'usergroup.write')
394 Session().commit()
394 Session().commit()
395
395
396 permissions = get_permissions(user)
396 permissions = get_permissions(user)
397 assert permissions['user_groups'][user_group.users_group_name] == \
397 assert permissions['user_groups'][user_group.users_group_name] == \
398 'usergroup.write'
398 'usergroup.write'
399
399
400
400
401 def test_cached_perms_data_user_group_permissions_explicit(
401 def test_cached_perms_data_user_group_permissions_explicit(
402 user_util, backend_random):
402 user_util, backend_random):
403 user = user_util.create_user()
403 user = user_util.create_user()
404 user_group = user_util.create_user_group()
404 user_group = user_util.create_user_group()
405 UserGroupModel().grant_user_permission(user_group, user, 'usergroup.none')
405 UserGroupModel().grant_user_permission(user_group, user, 'usergroup.none')
406 Session().commit()
406 Session().commit()
407
407
408 permissions = get_permissions(user, explicit=True)
408 permissions = get_permissions(user, explicit=True)
409 assert permissions['user_groups'][user_group.users_group_name] == \
409 assert permissions['user_groups'][user_group.users_group_name] == \
410 'usergroup.none'
410 'usergroup.none'
411
411
412
412
413 def test_cached_perms_data_user_group_permissions_not_inheriting_defaults(
413 def test_cached_perms_data_user_group_permissions_not_inheriting_defaults(
414 user_util, backend_random):
414 user_util, backend_random):
415 user = user_util.create_user()
415 user = user_util.create_user()
416 user_group = user_util.create_user_group()
416 user_group = user_util.create_user_group()
417
417
418 # Don't inherit default object permissions
418 # Don't inherit default object permissions
419 UserModel().grant_perm(user, 'hg.inherit_default_perms.false')
419 UserModel().grant_perm(user, 'hg.inherit_default_perms.false')
420 Session().commit()
420 Session().commit()
421
421
422 permissions = get_permissions(user)
422 permissions = get_permissions(user)
423 assert permissions['user_groups'][user_group.users_group_name] == \
423 assert permissions['user_groups'][user_group.users_group_name] == \
424 'usergroup.none'
424 'usergroup.none'
425
425
426
426
427 def test_permission_calculator_admin_permissions(
427 def test_permission_calculator_admin_permissions(
428 user_util, backend_random):
428 user_util, backend_random):
429 user = user_util.create_user()
429 user = user_util.create_user()
430 user_group = user_util.create_user_group()
430 user_group = user_util.create_user_group()
431 repo = backend_random.repo
431 repo = backend_random.repo
432 repo_group = user_util.create_repo_group()
432 repo_group = user_util.create_repo_group()
433
433
434 calculator = auth.PermissionCalculator(
434 calculator = auth.PermissionCalculator(
435 user.user_id, {}, False, False, True, 'higherwin')
435 user.user_id, {}, False, False, True, 'higherwin')
436 permissions = calculator._calculate_admin_permissions()
436 permissions = calculator._calculate_admin_permissions()
437
437
438 assert permissions['repositories_groups'][repo_group.group_name] == \
438 assert permissions['repositories_groups'][repo_group.group_name] == \
439 'group.admin'
439 'group.admin'
440 assert permissions['user_groups'][user_group.users_group_name] == \
440 assert permissions['user_groups'][user_group.users_group_name] == \
441 'usergroup.admin'
441 'usergroup.admin'
442 assert permissions['repositories'][repo.repo_name] == 'repository.admin'
442 assert permissions['repositories'][repo.repo_name] == 'repository.admin'
443 assert 'hg.admin' in permissions['global']
443 assert 'hg.admin' in permissions['global']
444
444
445
445
446 def test_permission_calculator_repository_permissions_robustness_from_group(
446 def test_permission_calculator_repository_permissions_robustness_from_group(
447 user_util, backend_random):
447 user_util, backend_random):
448 user, user_group = user_util.create_user_with_group()
448 user, user_group = user_util.create_user_with_group()
449
449
450 RepoModel().grant_user_group_permission(
450 RepoModel().grant_user_group_permission(
451 backend_random.repo, user_group.users_group_name, 'repository.write')
451 backend_random.repo, user_group.users_group_name, 'repository.write')
452
452
453 calculator = auth.PermissionCalculator(
453 calculator = auth.PermissionCalculator(
454 user.user_id, {}, False, False, False, 'higherwin')
454 user.user_id, {}, False, False, False, 'higherwin')
455 calculator._calculate_repository_permissions()
455 calculator._calculate_repository_permissions()
456
456
457
457
458 def test_permission_calculator_repository_permissions_robustness_from_user(
458 def test_permission_calculator_repository_permissions_robustness_from_user(
459 user_util, backend_random):
459 user_util, backend_random):
460 user = user_util.create_user()
460 user = user_util.create_user()
461
461
462 RepoModel().grant_user_permission(
462 RepoModel().grant_user_permission(
463 backend_random.repo, user, 'repository.write')
463 backend_random.repo, user, 'repository.write')
464 Session().commit()
464 Session().commit()
465
465
466 calculator = auth.PermissionCalculator(
466 calculator = auth.PermissionCalculator(
467 user.user_id, {}, False, False, False, 'higherwin')
467 user.user_id, {}, False, False, False, 'higherwin')
468 calculator._calculate_repository_permissions()
468 calculator._calculate_repository_permissions()
469
469
470
470
471 def test_permission_calculator_repo_group_permissions_robustness_from_group(
471 def test_permission_calculator_repo_group_permissions_robustness_from_group(
472 user_util, backend_random):
472 user_util, backend_random):
473 user, user_group = user_util.create_user_with_group()
473 user, user_group = user_util.create_user_with_group()
474 repo_group = user_util.create_repo_group()
474 repo_group = user_util.create_repo_group()
475
475
476 user_util.grant_user_group_permission_to_repo_group(
476 user_util.grant_user_group_permission_to_repo_group(
477 repo_group, user_group, 'group.write')
477 repo_group, user_group, 'group.write')
478
478
479 calculator = auth.PermissionCalculator(
479 calculator = auth.PermissionCalculator(
480 user.user_id, {}, False, False, False, 'higherwin')
480 user.user_id, {}, False, False, False, 'higherwin')
481 calculator._calculate_repository_group_permissions()
481 calculator._calculate_repository_group_permissions()
482
482
483
483
484 def test_permission_calculator_repo_group_permissions_robustness_from_user(
484 def test_permission_calculator_repo_group_permissions_robustness_from_user(
485 user_util, backend_random):
485 user_util, backend_random):
486 user = user_util.create_user()
486 user = user_util.create_user()
487 repo_group = user_util.create_repo_group()
487 repo_group = user_util.create_repo_group()
488
488
489 user_util.grant_user_permission_to_repo_group(
489 user_util.grant_user_permission_to_repo_group(
490 repo_group, user, 'group.write')
490 repo_group, user, 'group.write')
491
491
492 calculator = auth.PermissionCalculator(
492 calculator = auth.PermissionCalculator(
493 user.user_id, {}, False, False, False, 'higherwin')
493 user.user_id, {}, False, False, False, 'higherwin')
494 calculator._calculate_repository_group_permissions()
494 calculator._calculate_repository_group_permissions()
495
495
496
496
497 def test_permission_calculator_user_group_permissions_robustness_from_group(
497 def test_permission_calculator_user_group_permissions_robustness_from_group(
498 user_util, backend_random):
498 user_util, backend_random):
499 user, user_group = user_util.create_user_with_group()
499 user, user_group = user_util.create_user_with_group()
500 target_user_group = user_util.create_user_group()
500 target_user_group = user_util.create_user_group()
501
501
502 user_util.grant_user_group_permission_to_user_group(
502 user_util.grant_user_group_permission_to_user_group(
503 target_user_group, user_group, 'usergroup.write')
503 target_user_group, user_group, 'usergroup.write')
504
504
505 calculator = auth.PermissionCalculator(
505 calculator = auth.PermissionCalculator(
506 user.user_id, {}, False, False, False, 'higherwin')
506 user.user_id, {}, False, False, False, 'higherwin')
507 calculator._calculate_user_group_permissions()
507 calculator._calculate_user_group_permissions()
508
508
509
509
510 def test_permission_calculator_user_group_permissions_robustness_from_user(
510 def test_permission_calculator_user_group_permissions_robustness_from_user(
511 user_util, backend_random):
511 user_util, backend_random):
512 user = user_util.create_user()
512 user = user_util.create_user()
513 target_user_group = user_util.create_user_group()
513 target_user_group = user_util.create_user_group()
514
514
515 user_util.grant_user_permission_to_user_group(
515 user_util.grant_user_permission_to_user_group(
516 target_user_group, user, 'usergroup.write')
516 target_user_group, user, 'usergroup.write')
517
517
518 calculator = auth.PermissionCalculator(
518 calculator = auth.PermissionCalculator(
519 user.user_id, {}, False, False, False, 'higherwin')
519 user.user_id, {}, False, False, False, 'higherwin')
520 calculator._calculate_user_group_permissions()
520 calculator._calculate_user_group_permissions()
521
521
522
522
523 @pytest.mark.parametrize("algo, new_permission, old_permission, expected", [
523 @pytest.mark.parametrize("algo, new_permission, old_permission, expected", [
524 ('higherwin', 'repository.none', 'repository.none', 'repository.none'),
524 ('higherwin', 'repository.none', 'repository.none', 'repository.none'),
525 ('higherwin', 'repository.read', 'repository.none', 'repository.read'),
525 ('higherwin', 'repository.read', 'repository.none', 'repository.read'),
526 ('lowerwin', 'repository.write', 'repository.write', 'repository.write'),
526 ('lowerwin', 'repository.write', 'repository.write', 'repository.write'),
527 ('lowerwin', 'repository.read', 'repository.write', 'repository.read'),
527 ('lowerwin', 'repository.read', 'repository.write', 'repository.read'),
528 ])
528 ])
529 def test_permission_calculator_choose_permission(
529 def test_permission_calculator_choose_permission(
530 user_regular, algo, new_permission, old_permission, expected):
530 user_regular, algo, new_permission, old_permission, expected):
531 calculator = auth.PermissionCalculator(
531 calculator = auth.PermissionCalculator(
532 user_regular.user_id, {}, False, False, False, algo)
532 user_regular.user_id, {}, False, False, False, algo)
533 result = calculator._choose_permission(new_permission, old_permission)
533 result = calculator._choose_permission(new_permission, old_permission)
534 assert result == expected
534 assert result == expected
535
535
536
536
537 def test_permission_calculator_choose_permission_raises_on_wrong_algo(
537 def test_permission_calculator_choose_permission_raises_on_wrong_algo(
538 user_regular):
538 user_regular):
539 calculator = auth.PermissionCalculator(
539 calculator = auth.PermissionCalculator(
540 user_regular.user_id, {}, False, False, False, 'invalid')
540 user_regular.user_id, {}, False, False, False, 'invalid')
541 result = calculator._choose_permission(
541 result = calculator._choose_permission(
542 'repository.read', 'repository.read')
542 'repository.read', 'repository.read')
543 # TODO: johbo: This documents the existing behavior. Think of an
543 # TODO: johbo: This documents the existing behavior. Think of an
544 # improvement.
544 # improvement.
545 assert result is None
545 assert result is None
546
546
547
547
548 def test_auth_user_get_cookie_store_for_normal_user(user_util):
548 def test_auth_user_get_cookie_store_for_normal_user(user_util):
549 user = user_util.create_user()
549 user = user_util.create_user()
550 auth_user = auth.AuthUser(user_id=user.user_id)
550 auth_user = auth.AuthUser(user_id=user.user_id)
551 expected_data = {
551 expected_data = {
552 'username': user.username,
552 'username': user.username,
553 'user_id': user.user_id,
553 'user_id': user.user_id,
554 'password': md5(user.password),
554 'password': md5(user.password),
555 'is_authenticated': False
555 'is_authenticated': False
556 }
556 }
557 assert auth_user.get_cookie_store() == expected_data
557 assert auth_user.get_cookie_store() == expected_data
558
558
559
559
560 def test_auth_user_get_cookie_store_for_default_user():
560 def test_auth_user_get_cookie_store_for_default_user():
561 default_user = User.get_default_user()
561 default_user = User.get_default_user()
562 auth_user = auth.AuthUser()
562 auth_user = auth.AuthUser()
563 expected_data = {
563 expected_data = {
564 'username': User.DEFAULT_USER,
564 'username': User.DEFAULT_USER,
565 'user_id': default_user.user_id,
565 'user_id': default_user.user_id,
566 'password': md5(default_user.password),
566 'password': md5(default_user.password),
567 'is_authenticated': True
567 'is_authenticated': True
568 }
568 }
569 assert auth_user.get_cookie_store() == expected_data
569 assert auth_user.get_cookie_store() == expected_data
570
570
571
571
572 def get_permissions(user, **kwargs):
572 def get_permissions(user, **kwargs):
573 """
573 """
574 Utility filling in useful defaults into the call to `_cached_perms_data`.
574 Utility filling in useful defaults into the call to `_cached_perms_data`.
575
575
576 Fill in `**kwargs` if specific values are needed for a test.
576 Fill in `**kwargs` if specific values are needed for a test.
577 """
577 """
578 call_args = {
578 call_args = {
579 'user_id': user.user_id,
579 'user_id': user.user_id,
580 'scope': {},
580 'scope': {},
581 'user_is_admin': False,
581 'user_is_admin': False,
582 'user_inherit_default_permissions': False,
582 'user_inherit_default_permissions': False,
583 'explicit': False,
583 'explicit': False,
584 'algo': 'higherwin',
584 'algo': 'higherwin',
585 'calculate_super_admin': False,
585 'calculate_super_admin': False,
586 }
586 }
587 call_args.update(kwargs)
587 call_args.update(kwargs)
588 permissions = auth._cached_perms_data(**call_args)
588 permissions = auth._cached_perms_data(**call_args)
589 return permissions
589 return permissions
590
590
591
591
592 class TestGenerateAuthToken(object):
592 class TestGenerateAuthToken(object):
593 def test_salt_is_used_when_specified(self):
593 def test_salt_is_used_when_specified(self):
594 salt = 'abcde'
594 salt = 'abcde'
595 user_name = 'test_user'
595 user_name = 'test_user'
596 result = auth.generate_auth_token(user_name, salt)
596 result = auth.generate_auth_token(user_name, salt)
597 expected_result = sha1(user_name + salt).hexdigest()
597 expected_result = sha1(user_name + salt).hexdigest()
598 assert result == expected_result
598 assert result == expected_result
599
599
600 def test_salt_is_geneated_when_not_specified(self):
600 def test_salt_is_geneated_when_not_specified(self):
601 user_name = 'test_user'
601 user_name = 'test_user'
602 random_salt = os.urandom(16)
602 random_salt = os.urandom(16)
603 with patch.object(auth, 'os') as os_mock:
603 with patch.object(auth, 'os') as os_mock:
604 os_mock.urandom.return_value = random_salt
604 os_mock.urandom.return_value = random_salt
605 result = auth.generate_auth_token(user_name)
605 result = auth.generate_auth_token(user_name)
606 expected_result = sha1(user_name + random_salt).hexdigest()
606 expected_result = sha1(user_name + random_salt).hexdigest()
607 assert result == expected_result
607 assert result == expected_result
608
608
609
609
610 @pytest.mark.parametrize("test_token, test_roles, auth_result, expected_tokens", [
610 @pytest.mark.parametrize("test_token, test_roles, auth_result, expected_tokens", [
611 ('', None, False,
611 ('', None, False,
612 []),
612 []),
613 ('wrongtoken', None, False,
613 ('wrongtoken', None, False,
614 []),
614 []),
615 ('abracadabra_vcs', [AuthTokenModel.cls.ROLE_API], False,
615 ('abracadabra_vcs', [AuthTokenModel.cls.ROLE_API], False,
616 [('abracadabra_api', AuthTokenModel.cls.ROLE_API, -1)]),
616 [('abracadabra_api', AuthTokenModel.cls.ROLE_API, -1)]),
617 ('abracadabra_api', [AuthTokenModel.cls.ROLE_API], True,
617 ('abracadabra_api', [AuthTokenModel.cls.ROLE_API], True,
618 [('abracadabra_api', AuthTokenModel.cls.ROLE_API, -1)]),
618 [('abracadabra_api', AuthTokenModel.cls.ROLE_API, -1)]),
619 ('abracadabra_api', [AuthTokenModel.cls.ROLE_API], True,
619 ('abracadabra_api', [AuthTokenModel.cls.ROLE_API], True,
620 [('abracadabra_api', AuthTokenModel.cls.ROLE_API, -1),
620 [('abracadabra_api', AuthTokenModel.cls.ROLE_API, -1),
621 ('abracadabra_http', AuthTokenModel.cls.ROLE_HTTP, -1)]),
621 ('abracadabra_http', AuthTokenModel.cls.ROLE_HTTP, -1)]),
622 ])
622 ])
623 def test_auth_by_token(test_token, test_roles, auth_result, expected_tokens,
623 def test_auth_by_token(test_token, test_roles, auth_result, expected_tokens,
624 user_util):
624 user_util):
625 user = user_util.create_user()
625 user = user_util.create_user()
626 user_id = user.user_id
626 user_id = user.user_id
627 for token, role, expires in expected_tokens:
627 for token, role, expires in expected_tokens:
628 new_token = AuthTokenModel().create(user_id, u'test-token', expires, role)
628 new_token = AuthTokenModel().create(user_id, u'test-token', expires, role)
629 new_token.api_key = token # inject known name for testing...
629 new_token.api_key = token # inject known name for testing...
630
630
631 assert auth_result == user.authenticate_by_token(
631 assert auth_result == user.authenticate_by_token(
632 test_token, roles=test_roles)
632 test_token, roles=test_roles)
@@ -1,325 +1,325 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import functools
21 import functools
22
22
23 import pytest
23 import pytest
24
24
25 from rhodecode.model.db import RepoGroup, User
25 from rhodecode.model.db import RepoGroup, User
26 from rhodecode.model.meta import Session
26 from rhodecode.model.meta import Session
27 from rhodecode.model.repo_group import RepoGroupModel
27 from rhodecode.model.repo_group import RepoGroupModel
28 from rhodecode.tests.models.common import (
28 from rhodecode.tests.models.common import (
29 _create_project_tree, check_tree_perms, _get_perms, _check_expected_count,
29 _create_project_tree, check_tree_perms, _get_perms, _check_expected_count,
30 expected_count, _destroy_project_tree)
30 expected_count, _destroy_project_tree)
31
31
32
32
33 test_u1_id = None
33 test_u1_id = None
34 _get_repo_perms = None
34 _get_repo_perms = None
35 _get_group_perms = None
35 _get_group_perms = None
36
36
37
37
38 @pytest.fixture(autouse=True)
38 @pytest.fixture(autouse=True)
39 def setup_read_permission():
39 def setup_read_permission():
40 permissions_setup_func()
40 permissions_setup_func()
41
41
42
42
43 def permissions_setup_func(group_name='g0', perm='group.read', recursive='all',
43 def permissions_setup_func(group_name='g0', perm='group.read', recursive='all',
44 user_id=None):
44 user_id=None):
45 """
45 """
46 Resets all permissions to perm attribute
46 Resets all permissions to perm attribute
47 """
47 """
48 if not user_id:
48 if not user_id:
49 user_id = test_u1_id
49 user_id = test_u1_id
50 # called by the @with_setup decorator also reset the default user stuff
50 # called by the @with_setup decorator also reset the default user stuff
51 permissions_setup_func(group_name, perm, recursive,
51 permissions_setup_func(group_name, perm, recursive,
52 user_id=User.get_default_user().user_id)
52 user_id=User.get_default_user_id())
53
53
54 # TODO: DRY, compare test_user_group:permissions_setup_func
54 # TODO: DRY, compare test_user_group:permissions_setup_func
55 repo_group = RepoGroup.get_by_group_name(group_name=group_name)
55 repo_group = RepoGroup.get_by_group_name(group_name=group_name)
56 if not repo_group:
56 if not repo_group:
57 raise Exception('Cannot get group %s' % group_name)
57 raise Exception('Cannot get group %s' % group_name)
58
58
59 perm_updates = [[user_id, perm, 'user']]
59 perm_updates = [[user_id, perm, 'user']]
60 RepoGroupModel().update_permissions(repo_group,
60 RepoGroupModel().update_permissions(repo_group,
61 perm_updates=perm_updates,
61 perm_updates=perm_updates,
62 recursive=recursive, check_perms=False)
62 recursive=recursive, check_perms=False)
63 Session().commit()
63 Session().commit()
64
64
65
65
66 @pytest.fixture(scope='module', autouse=True)
66 @pytest.fixture(scope='module', autouse=True)
67 def prepare(request, baseapp):
67 def prepare(request, baseapp):
68 global test_u1_id, _get_repo_perms, _get_group_perms
68 global test_u1_id, _get_repo_perms, _get_group_perms
69 test_u1 = _create_project_tree()
69 test_u1 = _create_project_tree()
70 Session().commit()
70 Session().commit()
71 test_u1_id = test_u1.user_id
71 test_u1_id = test_u1.user_id
72 _get_repo_perms = functools.partial(_get_perms, key='repositories',
72 _get_repo_perms = functools.partial(_get_perms, key='repositories',
73 test_u1_id=test_u1_id)
73 test_u1_id=test_u1_id)
74 _get_group_perms = functools.partial(_get_perms, key='repositories_groups',
74 _get_group_perms = functools.partial(_get_perms, key='repositories_groups',
75 test_u1_id=test_u1_id)
75 test_u1_id=test_u1_id)
76
76
77 @request.addfinalizer
77 @request.addfinalizer
78 def cleanup():
78 def cleanup():
79 _destroy_project_tree(test_u1_id)
79 _destroy_project_tree(test_u1_id)
80
80
81
81
82 def test_user_permissions_on_group_without_recursive_mode():
82 def test_user_permissions_on_group_without_recursive_mode():
83 # set permission to g0 non-recursive mode
83 # set permission to g0 non-recursive mode
84 recursive = 'none'
84 recursive = 'none'
85 group = 'g0'
85 group = 'g0'
86 permissions_setup_func(group, 'group.write', recursive=recursive)
86 permissions_setup_func(group, 'group.write', recursive=recursive)
87
87
88 items = [x for x in _get_repo_perms(group, recursive)]
88 items = [x for x in _get_repo_perms(group, recursive)]
89 expected = 0
89 expected = 0
90 assert len(items) == expected, ' %s != %s' % (len(items), expected)
90 assert len(items) == expected, ' %s != %s' % (len(items), expected)
91 for name, perm in items:
91 for name, perm in items:
92 check_tree_perms(name, perm, group, 'repository.read')
92 check_tree_perms(name, perm, group, 'repository.read')
93
93
94 items = [x for x in _get_group_perms(group, recursive)]
94 items = [x for x in _get_group_perms(group, recursive)]
95 expected = 1
95 expected = 1
96 assert len(items) == expected, ' %s != %s' % (len(items), expected)
96 assert len(items) == expected, ' %s != %s' % (len(items), expected)
97 for name, perm in items:
97 for name, perm in items:
98 check_tree_perms(name, perm, group, 'group.write')
98 check_tree_perms(name, perm, group, 'group.write')
99
99
100
100
101 def test_user_permissions_on_group_without_recursive_mode_subgroup():
101 def test_user_permissions_on_group_without_recursive_mode_subgroup():
102 # set permission to g0 non-recursive mode
102 # set permission to g0 non-recursive mode
103 recursive = 'none'
103 recursive = 'none'
104 group = 'g0/g0_1'
104 group = 'g0/g0_1'
105 permissions_setup_func(group, 'group.write', recursive=recursive)
105 permissions_setup_func(group, 'group.write', recursive=recursive)
106
106
107 items = [x for x in _get_repo_perms(group, recursive)]
107 items = [x for x in _get_repo_perms(group, recursive)]
108 expected = 0
108 expected = 0
109 assert len(items) == expected, ' %s != %s' % (len(items), expected)
109 assert len(items) == expected, ' %s != %s' % (len(items), expected)
110 for name, perm in items:
110 for name, perm in items:
111 check_tree_perms(name, perm, group, 'repository.read')
111 check_tree_perms(name, perm, group, 'repository.read')
112
112
113 items = [x for x in _get_group_perms(group, recursive)]
113 items = [x for x in _get_group_perms(group, recursive)]
114 expected = 1
114 expected = 1
115 assert len(items) == expected, ' %s != %s' % (len(items), expected)
115 assert len(items) == expected, ' %s != %s' % (len(items), expected)
116 for name, perm in items:
116 for name, perm in items:
117 check_tree_perms(name, perm, group, 'group.write')
117 check_tree_perms(name, perm, group, 'group.write')
118
118
119
119
120 def test_user_permissions_on_group_with_recursive_mode():
120 def test_user_permissions_on_group_with_recursive_mode():
121
121
122 # set permission to g0 recursive mode, all children including
122 # set permission to g0 recursive mode, all children including
123 # other repos and groups should have this permission now set !
123 # other repos and groups should have this permission now set !
124 recursive = 'all'
124 recursive = 'all'
125 group = 'g0'
125 group = 'g0'
126 permissions_setup_func(group, 'group.write', recursive=recursive)
126 permissions_setup_func(group, 'group.write', recursive=recursive)
127
127
128 repo_items = [x for x in _get_repo_perms(group, recursive)]
128 repo_items = [x for x in _get_repo_perms(group, recursive)]
129 items = [x for x in _get_group_perms(group, recursive)]
129 items = [x for x in _get_group_perms(group, recursive)]
130 _check_expected_count(items, repo_items, expected_count(group, True))
130 _check_expected_count(items, repo_items, expected_count(group, True))
131
131
132 for name, perm in repo_items:
132 for name, perm in repo_items:
133 check_tree_perms(name, perm, group, 'repository.write')
133 check_tree_perms(name, perm, group, 'repository.write')
134
134
135 for name, perm in items:
135 for name, perm in items:
136 check_tree_perms(name, perm, group, 'group.write')
136 check_tree_perms(name, perm, group, 'group.write')
137
137
138
138
139 def test_user_permissions_on_group_with_recursive_mode_for_default_user():
139 def test_user_permissions_on_group_with_recursive_mode_for_default_user():
140
140
141 # set permission to g0 recursive mode, all children including
141 # set permission to g0 recursive mode, all children including
142 # other repos and groups should have this permission now set !
142 # other repos and groups should have this permission now set !
143 recursive = 'all'
143 recursive = 'all'
144 group = 'g0'
144 group = 'g0'
145 default_user_id = User.get_default_user().user_id
145 default_user_id = User.get_default_user_id()
146 permissions_setup_func(group, 'group.write', recursive=recursive,
146 permissions_setup_func(group, 'group.write', recursive=recursive,
147 user_id=default_user_id)
147 user_id=default_user_id)
148
148
149 # change default to get perms for default user
149 # change default to get perms for default user
150 _get_repo_perms = functools.partial(_get_perms, key='repositories',
150 _get_repo_perms = functools.partial(_get_perms, key='repositories',
151 test_u1_id=default_user_id)
151 test_u1_id=default_user_id)
152 _get_group_perms = functools.partial(_get_perms, key='repositories_groups',
152 _get_group_perms = functools.partial(_get_perms, key='repositories_groups',
153 test_u1_id=default_user_id)
153 test_u1_id=default_user_id)
154
154
155 repo_items = [x for x in _get_repo_perms(group, recursive)]
155 repo_items = [x for x in _get_repo_perms(group, recursive)]
156 items = [x for x in _get_group_perms(group, recursive)]
156 items = [x for x in _get_group_perms(group, recursive)]
157 _check_expected_count(items, repo_items, expected_count(group, True))
157 _check_expected_count(items, repo_items, expected_count(group, True))
158
158
159 for name, perm in repo_items:
159 for name, perm in repo_items:
160 check_tree_perms(name, perm, group, 'repository.write')
160 check_tree_perms(name, perm, group, 'repository.write')
161
161
162 for name, perm in items:
162 for name, perm in items:
163 check_tree_perms(name, perm, group, 'group.write')
163 check_tree_perms(name, perm, group, 'group.write')
164
164
165
165
166 def test_user_permissions_on_group_with_recursive_mode_inner_group():
166 def test_user_permissions_on_group_with_recursive_mode_inner_group():
167 # set permission to g0_3 group to none
167 # set permission to g0_3 group to none
168 recursive = 'all'
168 recursive = 'all'
169 group = 'g0/g0_3'
169 group = 'g0/g0_3'
170 permissions_setup_func(group, 'group.none', recursive=recursive)
170 permissions_setup_func(group, 'group.none', recursive=recursive)
171
171
172 repo_items = [x for x in _get_repo_perms(group, recursive)]
172 repo_items = [x for x in _get_repo_perms(group, recursive)]
173 items = [x for x in _get_group_perms(group, recursive)]
173 items = [x for x in _get_group_perms(group, recursive)]
174 _check_expected_count(items, repo_items, expected_count(group, True))
174 _check_expected_count(items, repo_items, expected_count(group, True))
175
175
176 for name, perm in repo_items:
176 for name, perm in repo_items:
177 check_tree_perms(name, perm, group, 'repository.none')
177 check_tree_perms(name, perm, group, 'repository.none')
178
178
179 for name, perm in items:
179 for name, perm in items:
180 check_tree_perms(name, perm, group, 'group.none')
180 check_tree_perms(name, perm, group, 'group.none')
181
181
182
182
183 def test_user_permissions_on_group_with_recursive_mode_deepest():
183 def test_user_permissions_on_group_with_recursive_mode_deepest():
184 # set permission to g0_3 group to none
184 # set permission to g0_3 group to none
185 recursive = 'all'
185 recursive = 'all'
186 group = 'g0/g0_1/g0_1_1'
186 group = 'g0/g0_1/g0_1_1'
187 permissions_setup_func(group, 'group.write', recursive=recursive)
187 permissions_setup_func(group, 'group.write', recursive=recursive)
188
188
189 repo_items = [x for x in _get_repo_perms(group, recursive)]
189 repo_items = [x for x in _get_repo_perms(group, recursive)]
190 items = [x for x in _get_group_perms(group, recursive)]
190 items = [x for x in _get_group_perms(group, recursive)]
191 _check_expected_count(items, repo_items, expected_count(group, True))
191 _check_expected_count(items, repo_items, expected_count(group, True))
192
192
193 for name, perm in repo_items:
193 for name, perm in repo_items:
194 check_tree_perms(name, perm, group, 'repository.write')
194 check_tree_perms(name, perm, group, 'repository.write')
195
195
196 for name, perm in items:
196 for name, perm in items:
197 check_tree_perms(name, perm, group, 'group.write')
197 check_tree_perms(name, perm, group, 'group.write')
198
198
199
199
200 def test_user_permissions_on_group_with_recursive_mode_only_with_repos():
200 def test_user_permissions_on_group_with_recursive_mode_only_with_repos():
201 # set permission to g0_3 group to none
201 # set permission to g0_3 group to none
202 recursive = 'all'
202 recursive = 'all'
203 group = 'g0/g0_2'
203 group = 'g0/g0_2'
204 permissions_setup_func(group, 'group.admin', recursive=recursive)
204 permissions_setup_func(group, 'group.admin', recursive=recursive)
205
205
206 repo_items = [x for x in _get_repo_perms(group, recursive)]
206 repo_items = [x for x in _get_repo_perms(group, recursive)]
207 items = [x for x in _get_group_perms(group, recursive)]
207 items = [x for x in _get_group_perms(group, recursive)]
208 _check_expected_count(items, repo_items, expected_count(group, True))
208 _check_expected_count(items, repo_items, expected_count(group, True))
209
209
210 for name, perm in repo_items:
210 for name, perm in repo_items:
211 check_tree_perms(name, perm, group, 'repository.admin')
211 check_tree_perms(name, perm, group, 'repository.admin')
212
212
213 for name, perm in items:
213 for name, perm in items:
214 check_tree_perms(name, perm, group, 'group.admin')
214 check_tree_perms(name, perm, group, 'group.admin')
215
215
216
216
217 def test_user_permissions_on_group_with_recursive_repo_mode_for_default_user():
217 def test_user_permissions_on_group_with_recursive_repo_mode_for_default_user():
218 # set permission to g0/g0_1 recursive repos only mode, all children
218 # set permission to g0/g0_1 recursive repos only mode, all children
219 # including other repos should have this permission now set, inner groups
219 # including other repos should have this permission now set, inner groups
220 # are excluded!
220 # are excluded!
221 recursive = 'repos'
221 recursive = 'repos'
222 group = 'g0/g0_1'
222 group = 'g0/g0_1'
223 perm = 'group.none'
223 perm = 'group.none'
224 default_user_id = User.get_default_user().user_id
224 default_user_id = User.get_default_user_id()
225
225
226 # TODO: workaround due to different setup calls, adept to py.test style
226 # TODO: workaround due to different setup calls, adept to py.test style
227 permissions_setup_func()
227 permissions_setup_func()
228 permissions_setup_func(group, perm, recursive=recursive,
228 permissions_setup_func(group, perm, recursive=recursive,
229 user_id=default_user_id)
229 user_id=default_user_id)
230
230
231 # change default to get perms for default user
231 # change default to get perms for default user
232 _get_repo_perms = functools.partial(_get_perms, key='repositories',
232 _get_repo_perms = functools.partial(_get_perms, key='repositories',
233 test_u1_id=default_user_id)
233 test_u1_id=default_user_id)
234 _get_group_perms = functools.partial(_get_perms, key='repositories_groups',
234 _get_group_perms = functools.partial(_get_perms, key='repositories_groups',
235 test_u1_id=default_user_id)
235 test_u1_id=default_user_id)
236
236
237 repo_items = [x for x in _get_repo_perms(group, recursive)]
237 repo_items = [x for x in _get_repo_perms(group, recursive)]
238 items = [x for x in _get_group_perms(group, recursive)]
238 items = [x for x in _get_group_perms(group, recursive)]
239 _check_expected_count(items, repo_items, expected_count(group, True))
239 _check_expected_count(items, repo_items, expected_count(group, True))
240
240
241 for name, perm in repo_items:
241 for name, perm in repo_items:
242 check_tree_perms(name, perm, group, 'repository.none')
242 check_tree_perms(name, perm, group, 'repository.none')
243
243
244 for name, perm in items:
244 for name, perm in items:
245 # permission is set with repos only mode, but we also change the
245 # permission is set with repos only mode, but we also change the
246 # permission on the group we trigger the apply to children from, thus
246 # permission on the group we trigger the apply to children from, thus
247 # we need to change its permission check
247 # we need to change its permission check
248 old_perm = 'group.read'
248 old_perm = 'group.read'
249 if name == group:
249 if name == group:
250 old_perm = perm
250 old_perm = perm
251 check_tree_perms(name, perm, group, old_perm)
251 check_tree_perms(name, perm, group, old_perm)
252
252
253
253
254 def test_user_permissions_on_group_with_recursive_repo_mode_inner_group():
254 def test_user_permissions_on_group_with_recursive_repo_mode_inner_group():
255 # set permission to g0_3 group to none, with recursive repos only
255 # set permission to g0_3 group to none, with recursive repos only
256 recursive = 'repos'
256 recursive = 'repos'
257 group = 'g0/g0_3'
257 group = 'g0/g0_3'
258 perm = 'group.none'
258 perm = 'group.none'
259 permissions_setup_func(group, perm, recursive=recursive)
259 permissions_setup_func(group, perm, recursive=recursive)
260
260
261 repo_items = [x for x in _get_repo_perms(group, recursive)]
261 repo_items = [x for x in _get_repo_perms(group, recursive)]
262 items = [x for x in _get_group_perms(group, recursive)]
262 items = [x for x in _get_group_perms(group, recursive)]
263 _check_expected_count(items, repo_items, expected_count(group, True))
263 _check_expected_count(items, repo_items, expected_count(group, True))
264
264
265 for name, perm in repo_items:
265 for name, perm in repo_items:
266 check_tree_perms(name, perm, group, 'repository.none')
266 check_tree_perms(name, perm, group, 'repository.none')
267
267
268 for name, perm in items:
268 for name, perm in items:
269 # permission is set with repos only mode, but we also change the
269 # permission is set with repos only mode, but we also change the
270 # permission on the group we trigger the apply to children from, thus
270 # permission on the group we trigger the apply to children from, thus
271 # we need to change its permission check
271 # we need to change its permission check
272 old_perm = 'group.read'
272 old_perm = 'group.read'
273 if name == group:
273 if name == group:
274 old_perm = perm
274 old_perm = perm
275 check_tree_perms(name, perm, group, old_perm)
275 check_tree_perms(name, perm, group, old_perm)
276
276
277
277
278 def test_user_permissions_on_group_with_rec_group_mode_for_default_user():
278 def test_user_permissions_on_group_with_rec_group_mode_for_default_user():
279 # set permission to g0/g0_1 with recursive groups only mode, all children
279 # set permission to g0/g0_1 with recursive groups only mode, all children
280 # sincluding other groups should have this permission now set. repositories
280 # sincluding other groups should have this permission now set. repositories
281 # should remain intact as we use groups only mode !
281 # should remain intact as we use groups only mode !
282 recursive = 'groups'
282 recursive = 'groups'
283 group = 'g0/g0_1'
283 group = 'g0/g0_1'
284 default_user_id = User.get_default_user().user_id
284 default_user_id = User.get_default_user_id()
285
285
286 # TODO: workaround due to different setup calls, adept to py.test style
286 # TODO: workaround due to different setup calls, adept to py.test style
287 permissions_setup_func()
287 permissions_setup_func()
288 permissions_setup_func(group, 'group.write', recursive=recursive,
288 permissions_setup_func(group, 'group.write', recursive=recursive,
289 user_id=default_user_id)
289 user_id=default_user_id)
290
290
291 # change default to get perms for default user
291 # change default to get perms for default user
292 _get_repo_perms = functools.partial(_get_perms, key='repositories',
292 _get_repo_perms = functools.partial(_get_perms, key='repositories',
293 test_u1_id=default_user_id)
293 test_u1_id=default_user_id)
294 _get_group_perms = functools.partial(_get_perms, key='repositories_groups',
294 _get_group_perms = functools.partial(_get_perms, key='repositories_groups',
295 test_u1_id=default_user_id)
295 test_u1_id=default_user_id)
296
296
297 repo_items = [x for x in _get_repo_perms(group, recursive)]
297 repo_items = [x for x in _get_repo_perms(group, recursive)]
298 items = [x for x in _get_group_perms(group, recursive)]
298 items = [x for x in _get_group_perms(group, recursive)]
299 _check_expected_count(items, repo_items, expected_count(group, True))
299 _check_expected_count(items, repo_items, expected_count(group, True))
300
300
301 for name, perm in repo_items:
301 for name, perm in repo_items:
302 check_tree_perms(name, perm, group, 'repository.read')
302 check_tree_perms(name, perm, group, 'repository.read')
303
303
304 for name, perm in items:
304 for name, perm in items:
305 check_tree_perms(name, perm, group, 'group.write')
305 check_tree_perms(name, perm, group, 'group.write')
306
306
307
307
308 def test_user_permissions_on_group_with_recursive_group_mode_inner_group():
308 def test_user_permissions_on_group_with_recursive_group_mode_inner_group():
309 # set permission to g0_3 group to none, with recursive mode for groups only
309 # set permission to g0_3 group to none, with recursive mode for groups only
310 recursive = 'groups'
310 recursive = 'groups'
311 group = 'g0/g0_3'
311 group = 'g0/g0_3'
312
312
313 # TODO: workaround due to different setup calls, adept to py.test style
313 # TODO: workaround due to different setup calls, adept to py.test style
314 permissions_setup_func()
314 permissions_setup_func()
315 permissions_setup_func(group, 'group.none', recursive=recursive)
315 permissions_setup_func(group, 'group.none', recursive=recursive)
316
316
317 repo_items = [x for x in _get_repo_perms(group, recursive)]
317 repo_items = [x for x in _get_repo_perms(group, recursive)]
318 items = [x for x in _get_group_perms(group, recursive)]
318 items = [x for x in _get_group_perms(group, recursive)]
319 _check_expected_count(items, repo_items, expected_count(group, True))
319 _check_expected_count(items, repo_items, expected_count(group, True))
320
320
321 for name, perm in repo_items:
321 for name, perm in repo_items:
322 check_tree_perms(name, perm, group, 'repository.read')
322 check_tree_perms(name, perm, group, 'repository.read')
323
323
324 for name, perm in items:
324 for name, perm in items:
325 check_tree_perms(name, perm, group, 'group.none')
325 check_tree_perms(name, perm, group, 'group.none')
General Comments 0
You need to be logged in to leave comments. Login now