##// END OF EJS Templates
grids: fixed tests and added one for permissions to repository group.
marcink -
r4151:f04de416 default
parent child Browse files
Show More
@@ -1,512 +1,514 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 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 urllib
21 import urllib
22
22
23 import mock
23 import mock
24 import pytest
24 import pytest
25
25
26 from rhodecode.apps._base import ADMIN_PREFIX
26 from rhodecode.apps._base import ADMIN_PREFIX
27 from rhodecode.lib import auth
27 from rhodecode.lib import auth
28 from rhodecode.lib.utils2 import safe_str
28 from rhodecode.lib.utils2 import safe_str
29 from rhodecode.lib import helpers as h
29 from rhodecode.lib import helpers as h
30 from rhodecode.model.db import (
30 from rhodecode.model.db import (
31 Repository, RepoGroup, UserRepoToPerm, User, Permission)
31 Repository, RepoGroup, UserRepoToPerm, User, Permission)
32 from rhodecode.model.meta import Session
32 from rhodecode.model.meta import Session
33 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.repo import RepoModel
34 from rhodecode.model.repo_group import RepoGroupModel
34 from rhodecode.model.repo_group import RepoGroupModel
35 from rhodecode.model.user import UserModel
35 from rhodecode.model.user import UserModel
36 from rhodecode.tests import (
36 from rhodecode.tests import (
37 login_user_session, assert_session_flash, TEST_USER_ADMIN_LOGIN,
37 login_user_session, assert_session_flash, TEST_USER_ADMIN_LOGIN,
38 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
38 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
39 from rhodecode.tests.fixture import Fixture, error_function
39 from rhodecode.tests.fixture import Fixture, error_function
40 from rhodecode.tests.utils import AssertResponse, repo_on_filesystem
40 from rhodecode.tests.utils import AssertResponse, repo_on_filesystem
41
41
42 fixture = Fixture()
42 fixture = Fixture()
43
43
44
44
45 def route_path(name, params=None, **kwargs):
45 def route_path(name, params=None, **kwargs):
46 import urllib
46 import urllib
47
47
48 base_url = {
48 base_url = {
49 'repos': ADMIN_PREFIX + '/repos',
49 'repos': ADMIN_PREFIX + '/repos',
50 'repos_data': ADMIN_PREFIX + '/repos_data',
50 'repo_new': ADMIN_PREFIX + '/repos/new',
51 'repo_new': ADMIN_PREFIX + '/repos/new',
51 'repo_create': ADMIN_PREFIX + '/repos/create',
52 'repo_create': ADMIN_PREFIX + '/repos/create',
52
53
53 'repo_creating_check': '/{repo_name}/repo_creating_check',
54 'repo_creating_check': '/{repo_name}/repo_creating_check',
54 }[name].format(**kwargs)
55 }[name].format(**kwargs)
55
56
56 if params:
57 if params:
57 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
58 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
58 return base_url
59 return base_url
59
60
60
61
61 def _get_permission_for_user(user, repo):
62 def _get_permission_for_user(user, repo):
62 perm = UserRepoToPerm.query()\
63 perm = UserRepoToPerm.query()\
63 .filter(UserRepoToPerm.repository ==
64 .filter(UserRepoToPerm.repository ==
64 Repository.get_by_repo_name(repo))\
65 Repository.get_by_repo_name(repo))\
65 .filter(UserRepoToPerm.user == User.get_by_username(user))\
66 .filter(UserRepoToPerm.user == User.get_by_username(user))\
66 .all()
67 .all()
67 return perm
68 return perm
68
69
69
70
70 @pytest.mark.usefixtures("app")
71 @pytest.mark.usefixtures("app")
71 class TestAdminRepos(object):
72 class TestAdminRepos(object):
72
73
73 def test_repo_list(self, autologin_user, user_util):
74 def test_repo_list(self, autologin_user, user_util, xhr_header):
74 repo = user_util.create_repo()
75 repo = user_util.create_repo()
75 repo_name = repo.repo_name
76 repo_name = repo.repo_name
76 response = self.app.get(
77 response = self.app.get(
77 route_path('repos'), status=200)
78 route_path('repos_data'), status=200,
79 extra_environ=xhr_header)
78
80
79 response.mustcontain(repo_name)
81 response.mustcontain(repo_name)
80
82
81 def test_create_page_restricted_to_single_backend(self, autologin_user, backend):
83 def test_create_page_restricted_to_single_backend(self, autologin_user, backend):
82 with mock.patch('rhodecode.BACKENDS', {'git': 'git'}):
84 with mock.patch('rhodecode.BACKENDS', {'git': 'git'}):
83 response = self.app.get(route_path('repo_new'), status=200)
85 response = self.app.get(route_path('repo_new'), status=200)
84 assert_response = response.assert_response()
86 assert_response = response.assert_response()
85 element = assert_response.get_element('#repo_type')
87 element = assert_response.get_element('#repo_type')
86 assert element.text_content() == '\ngit\n'
88 assert element.text_content() == '\ngit\n'
87
89
88 def test_create_page_non_restricted_backends(self, autologin_user, backend):
90 def test_create_page_non_restricted_backends(self, autologin_user, backend):
89 response = self.app.get(route_path('repo_new'), status=200)
91 response = self.app.get(route_path('repo_new'), status=200)
90 assert_response = response.assert_response()
92 assert_response = response.assert_response()
91 assert_response.element_contains('#repo_type', 'git')
93 assert_response.element_contains('#repo_type', 'git')
92 assert_response.element_contains('#repo_type', 'svn')
94 assert_response.element_contains('#repo_type', 'svn')
93 assert_response.element_contains('#repo_type', 'hg')
95 assert_response.element_contains('#repo_type', 'hg')
94
96
95 @pytest.mark.parametrize(
97 @pytest.mark.parametrize(
96 "suffix", [u'', u'xxa'], ids=['', 'non-ascii'])
98 "suffix", [u'', u'xxa'], ids=['', 'non-ascii'])
97 def test_create(self, autologin_user, backend, suffix, csrf_token):
99 def test_create(self, autologin_user, backend, suffix, csrf_token):
98 repo_name_unicode = backend.new_repo_name(suffix=suffix)
100 repo_name_unicode = backend.new_repo_name(suffix=suffix)
99 repo_name = repo_name_unicode.encode('utf8')
101 repo_name = repo_name_unicode.encode('utf8')
100 description_unicode = u'description for newly created repo' + suffix
102 description_unicode = u'description for newly created repo' + suffix
101 description = description_unicode.encode('utf8')
103 description = description_unicode.encode('utf8')
102 response = self.app.post(
104 response = self.app.post(
103 route_path('repo_create'),
105 route_path('repo_create'),
104 fixture._get_repo_create_params(
106 fixture._get_repo_create_params(
105 repo_private=False,
107 repo_private=False,
106 repo_name=repo_name,
108 repo_name=repo_name,
107 repo_type=backend.alias,
109 repo_type=backend.alias,
108 repo_description=description,
110 repo_description=description,
109 csrf_token=csrf_token),
111 csrf_token=csrf_token),
110 status=302)
112 status=302)
111
113
112 self.assert_repository_is_created_correctly(
114 self.assert_repository_is_created_correctly(
113 repo_name, description, backend)
115 repo_name, description, backend)
114
116
115 def test_create_numeric_name(self, autologin_user, backend, csrf_token):
117 def test_create_numeric_name(self, autologin_user, backend, csrf_token):
116 numeric_repo = '1234'
118 numeric_repo = '1234'
117 repo_name = numeric_repo
119 repo_name = numeric_repo
118 description = 'description for newly created repo' + numeric_repo
120 description = 'description for newly created repo' + numeric_repo
119 self.app.post(
121 self.app.post(
120 route_path('repo_create'),
122 route_path('repo_create'),
121 fixture._get_repo_create_params(
123 fixture._get_repo_create_params(
122 repo_private=False,
124 repo_private=False,
123 repo_name=repo_name,
125 repo_name=repo_name,
124 repo_type=backend.alias,
126 repo_type=backend.alias,
125 repo_description=description,
127 repo_description=description,
126 csrf_token=csrf_token))
128 csrf_token=csrf_token))
127
129
128 self.assert_repository_is_created_correctly(
130 self.assert_repository_is_created_correctly(
129 repo_name, description, backend)
131 repo_name, description, backend)
130
132
131 @pytest.mark.parametrize("suffix", [u'', u'Δ…Δ‡Δ™'], ids=['', 'non-ascii'])
133 @pytest.mark.parametrize("suffix", [u'', u'Δ…Δ‡Δ™'], ids=['', 'non-ascii'])
132 def test_create_in_group(
134 def test_create_in_group(
133 self, autologin_user, backend, suffix, csrf_token):
135 self, autologin_user, backend, suffix, csrf_token):
134 # create GROUP
136 # create GROUP
135 group_name = 'sometest_%s' % backend.alias
137 group_name = 'sometest_%s' % backend.alias
136 gr = RepoGroupModel().create(group_name=group_name,
138 gr = RepoGroupModel().create(group_name=group_name,
137 group_description='test',
139 group_description='test',
138 owner=TEST_USER_ADMIN_LOGIN)
140 owner=TEST_USER_ADMIN_LOGIN)
139 Session().commit()
141 Session().commit()
140
142
141 repo_name = u'ingroup' + suffix
143 repo_name = u'ingroup' + suffix
142 repo_name_full = RepoGroup.url_sep().join(
144 repo_name_full = RepoGroup.url_sep().join(
143 [group_name, repo_name])
145 [group_name, repo_name])
144 description = u'description for newly created repo'
146 description = u'description for newly created repo'
145 self.app.post(
147 self.app.post(
146 route_path('repo_create'),
148 route_path('repo_create'),
147 fixture._get_repo_create_params(
149 fixture._get_repo_create_params(
148 repo_private=False,
150 repo_private=False,
149 repo_name=safe_str(repo_name),
151 repo_name=safe_str(repo_name),
150 repo_type=backend.alias,
152 repo_type=backend.alias,
151 repo_description=description,
153 repo_description=description,
152 repo_group=gr.group_id,
154 repo_group=gr.group_id,
153 csrf_token=csrf_token))
155 csrf_token=csrf_token))
154
156
155 # TODO: johbo: Cleanup work to fixture
157 # TODO: johbo: Cleanup work to fixture
156 try:
158 try:
157 self.assert_repository_is_created_correctly(
159 self.assert_repository_is_created_correctly(
158 repo_name_full, description, backend)
160 repo_name_full, description, backend)
159
161
160 new_repo = RepoModel().get_by_repo_name(repo_name_full)
162 new_repo = RepoModel().get_by_repo_name(repo_name_full)
161 inherited_perms = UserRepoToPerm.query().filter(
163 inherited_perms = UserRepoToPerm.query().filter(
162 UserRepoToPerm.repository_id == new_repo.repo_id).all()
164 UserRepoToPerm.repository_id == new_repo.repo_id).all()
163 assert len(inherited_perms) == 1
165 assert len(inherited_perms) == 1
164 finally:
166 finally:
165 RepoModel().delete(repo_name_full)
167 RepoModel().delete(repo_name_full)
166 RepoGroupModel().delete(group_name)
168 RepoGroupModel().delete(group_name)
167 Session().commit()
169 Session().commit()
168
170
169 def test_create_in_group_numeric_name(
171 def test_create_in_group_numeric_name(
170 self, autologin_user, backend, csrf_token):
172 self, autologin_user, backend, csrf_token):
171 # create GROUP
173 # create GROUP
172 group_name = 'sometest_%s' % backend.alias
174 group_name = 'sometest_%s' % backend.alias
173 gr = RepoGroupModel().create(group_name=group_name,
175 gr = RepoGroupModel().create(group_name=group_name,
174 group_description='test',
176 group_description='test',
175 owner=TEST_USER_ADMIN_LOGIN)
177 owner=TEST_USER_ADMIN_LOGIN)
176 Session().commit()
178 Session().commit()
177
179
178 repo_name = '12345'
180 repo_name = '12345'
179 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
181 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
180 description = 'description for newly created repo'
182 description = 'description for newly created repo'
181 self.app.post(
183 self.app.post(
182 route_path('repo_create'),
184 route_path('repo_create'),
183 fixture._get_repo_create_params(
185 fixture._get_repo_create_params(
184 repo_private=False,
186 repo_private=False,
185 repo_name=repo_name,
187 repo_name=repo_name,
186 repo_type=backend.alias,
188 repo_type=backend.alias,
187 repo_description=description,
189 repo_description=description,
188 repo_group=gr.group_id,
190 repo_group=gr.group_id,
189 csrf_token=csrf_token))
191 csrf_token=csrf_token))
190
192
191 # TODO: johbo: Cleanup work to fixture
193 # TODO: johbo: Cleanup work to fixture
192 try:
194 try:
193 self.assert_repository_is_created_correctly(
195 self.assert_repository_is_created_correctly(
194 repo_name_full, description, backend)
196 repo_name_full, description, backend)
195
197
196 new_repo = RepoModel().get_by_repo_name(repo_name_full)
198 new_repo = RepoModel().get_by_repo_name(repo_name_full)
197 inherited_perms = UserRepoToPerm.query()\
199 inherited_perms = UserRepoToPerm.query()\
198 .filter(UserRepoToPerm.repository_id == new_repo.repo_id).all()
200 .filter(UserRepoToPerm.repository_id == new_repo.repo_id).all()
199 assert len(inherited_perms) == 1
201 assert len(inherited_perms) == 1
200 finally:
202 finally:
201 RepoModel().delete(repo_name_full)
203 RepoModel().delete(repo_name_full)
202 RepoGroupModel().delete(group_name)
204 RepoGroupModel().delete(group_name)
203 Session().commit()
205 Session().commit()
204
206
205 def test_create_in_group_without_needed_permissions(self, backend):
207 def test_create_in_group_without_needed_permissions(self, backend):
206 session = login_user_session(
208 session = login_user_session(
207 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
209 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
208 csrf_token = auth.get_csrf_token(session)
210 csrf_token = auth.get_csrf_token(session)
209 # revoke
211 # revoke
210 user_model = UserModel()
212 user_model = UserModel()
211 # disable fork and create on default user
213 # disable fork and create on default user
212 user_model.revoke_perm(User.DEFAULT_USER, 'hg.create.repository')
214 user_model.revoke_perm(User.DEFAULT_USER, 'hg.create.repository')
213 user_model.grant_perm(User.DEFAULT_USER, 'hg.create.none')
215 user_model.grant_perm(User.DEFAULT_USER, 'hg.create.none')
214 user_model.revoke_perm(User.DEFAULT_USER, 'hg.fork.repository')
216 user_model.revoke_perm(User.DEFAULT_USER, 'hg.fork.repository')
215 user_model.grant_perm(User.DEFAULT_USER, 'hg.fork.none')
217 user_model.grant_perm(User.DEFAULT_USER, 'hg.fork.none')
216
218
217 # disable on regular user
219 # disable on regular user
218 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.repository')
220 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.repository')
219 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.none')
221 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.none')
220 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.repository')
222 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.repository')
221 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.none')
223 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.none')
222 Session().commit()
224 Session().commit()
223
225
224 # create GROUP
226 # create GROUP
225 group_name = 'reg_sometest_%s' % backend.alias
227 group_name = 'reg_sometest_%s' % backend.alias
226 gr = RepoGroupModel().create(group_name=group_name,
228 gr = RepoGroupModel().create(group_name=group_name,
227 group_description='test',
229 group_description='test',
228 owner=TEST_USER_ADMIN_LOGIN)
230 owner=TEST_USER_ADMIN_LOGIN)
229 Session().commit()
231 Session().commit()
230 repo_group_id = gr.group_id
232 repo_group_id = gr.group_id
231
233
232 group_name_allowed = 'reg_sometest_allowed_%s' % backend.alias
234 group_name_allowed = 'reg_sometest_allowed_%s' % backend.alias
233 gr_allowed = RepoGroupModel().create(
235 gr_allowed = RepoGroupModel().create(
234 group_name=group_name_allowed,
236 group_name=group_name_allowed,
235 group_description='test',
237 group_description='test',
236 owner=TEST_USER_REGULAR_LOGIN)
238 owner=TEST_USER_REGULAR_LOGIN)
237 allowed_repo_group_id = gr_allowed.group_id
239 allowed_repo_group_id = gr_allowed.group_id
238 Session().commit()
240 Session().commit()
239
241
240 repo_name = 'ingroup'
242 repo_name = 'ingroup'
241 description = 'description for newly created repo'
243 description = 'description for newly created repo'
242 response = self.app.post(
244 response = self.app.post(
243 route_path('repo_create'),
245 route_path('repo_create'),
244 fixture._get_repo_create_params(
246 fixture._get_repo_create_params(
245 repo_private=False,
247 repo_private=False,
246 repo_name=repo_name,
248 repo_name=repo_name,
247 repo_type=backend.alias,
249 repo_type=backend.alias,
248 repo_description=description,
250 repo_description=description,
249 repo_group=repo_group_id,
251 repo_group=repo_group_id,
250 csrf_token=csrf_token))
252 csrf_token=csrf_token))
251
253
252 response.mustcontain('Invalid value')
254 response.mustcontain('Invalid value')
253
255
254 # user is allowed to create in this group
256 # user is allowed to create in this group
255 repo_name = 'ingroup'
257 repo_name = 'ingroup'
256 repo_name_full = RepoGroup.url_sep().join(
258 repo_name_full = RepoGroup.url_sep().join(
257 [group_name_allowed, repo_name])
259 [group_name_allowed, repo_name])
258 description = 'description for newly created repo'
260 description = 'description for newly created repo'
259 response = self.app.post(
261 response = self.app.post(
260 route_path('repo_create'),
262 route_path('repo_create'),
261 fixture._get_repo_create_params(
263 fixture._get_repo_create_params(
262 repo_private=False,
264 repo_private=False,
263 repo_name=repo_name,
265 repo_name=repo_name,
264 repo_type=backend.alias,
266 repo_type=backend.alias,
265 repo_description=description,
267 repo_description=description,
266 repo_group=allowed_repo_group_id,
268 repo_group=allowed_repo_group_id,
267 csrf_token=csrf_token))
269 csrf_token=csrf_token))
268
270
269 # TODO: johbo: Cleanup in pytest fixture
271 # TODO: johbo: Cleanup in pytest fixture
270 try:
272 try:
271 self.assert_repository_is_created_correctly(
273 self.assert_repository_is_created_correctly(
272 repo_name_full, description, backend)
274 repo_name_full, description, backend)
273
275
274 new_repo = RepoModel().get_by_repo_name(repo_name_full)
276 new_repo = RepoModel().get_by_repo_name(repo_name_full)
275 inherited_perms = UserRepoToPerm.query().filter(
277 inherited_perms = UserRepoToPerm.query().filter(
276 UserRepoToPerm.repository_id == new_repo.repo_id).all()
278 UserRepoToPerm.repository_id == new_repo.repo_id).all()
277 assert len(inherited_perms) == 1
279 assert len(inherited_perms) == 1
278
280
279 assert repo_on_filesystem(repo_name_full)
281 assert repo_on_filesystem(repo_name_full)
280 finally:
282 finally:
281 RepoModel().delete(repo_name_full)
283 RepoModel().delete(repo_name_full)
282 RepoGroupModel().delete(group_name)
284 RepoGroupModel().delete(group_name)
283 RepoGroupModel().delete(group_name_allowed)
285 RepoGroupModel().delete(group_name_allowed)
284 Session().commit()
286 Session().commit()
285
287
286 def test_create_in_group_inherit_permissions(self, autologin_user, backend,
288 def test_create_in_group_inherit_permissions(self, autologin_user, backend,
287 csrf_token):
289 csrf_token):
288 # create GROUP
290 # create GROUP
289 group_name = 'sometest_%s' % backend.alias
291 group_name = 'sometest_%s' % backend.alias
290 gr = RepoGroupModel().create(group_name=group_name,
292 gr = RepoGroupModel().create(group_name=group_name,
291 group_description='test',
293 group_description='test',
292 owner=TEST_USER_ADMIN_LOGIN)
294 owner=TEST_USER_ADMIN_LOGIN)
293 perm = Permission.get_by_key('repository.write')
295 perm = Permission.get_by_key('repository.write')
294 RepoGroupModel().grant_user_permission(
296 RepoGroupModel().grant_user_permission(
295 gr, TEST_USER_REGULAR_LOGIN, perm)
297 gr, TEST_USER_REGULAR_LOGIN, perm)
296
298
297 # add repo permissions
299 # add repo permissions
298 Session().commit()
300 Session().commit()
299 repo_group_id = gr.group_id
301 repo_group_id = gr.group_id
300 repo_name = 'ingroup_inherited_%s' % backend.alias
302 repo_name = 'ingroup_inherited_%s' % backend.alias
301 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
303 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
302 description = 'description for newly created repo'
304 description = 'description for newly created repo'
303 self.app.post(
305 self.app.post(
304 route_path('repo_create'),
306 route_path('repo_create'),
305 fixture._get_repo_create_params(
307 fixture._get_repo_create_params(
306 repo_private=False,
308 repo_private=False,
307 repo_name=repo_name,
309 repo_name=repo_name,
308 repo_type=backend.alias,
310 repo_type=backend.alias,
309 repo_description=description,
311 repo_description=description,
310 repo_group=repo_group_id,
312 repo_group=repo_group_id,
311 repo_copy_permissions=True,
313 repo_copy_permissions=True,
312 csrf_token=csrf_token))
314 csrf_token=csrf_token))
313
315
314 # TODO: johbo: Cleanup to pytest fixture
316 # TODO: johbo: Cleanup to pytest fixture
315 try:
317 try:
316 self.assert_repository_is_created_correctly(
318 self.assert_repository_is_created_correctly(
317 repo_name_full, description, backend)
319 repo_name_full, description, backend)
318 except Exception:
320 except Exception:
319 RepoGroupModel().delete(group_name)
321 RepoGroupModel().delete(group_name)
320 Session().commit()
322 Session().commit()
321 raise
323 raise
322
324
323 # check if inherited permissions are applied
325 # check if inherited permissions are applied
324 new_repo = RepoModel().get_by_repo_name(repo_name_full)
326 new_repo = RepoModel().get_by_repo_name(repo_name_full)
325 inherited_perms = UserRepoToPerm.query().filter(
327 inherited_perms = UserRepoToPerm.query().filter(
326 UserRepoToPerm.repository_id == new_repo.repo_id).all()
328 UserRepoToPerm.repository_id == new_repo.repo_id).all()
327 assert len(inherited_perms) == 2
329 assert len(inherited_perms) == 2
328
330
329 assert TEST_USER_REGULAR_LOGIN in [
331 assert TEST_USER_REGULAR_LOGIN in [
330 x.user.username for x in inherited_perms]
332 x.user.username for x in inherited_perms]
331 assert 'repository.write' in [
333 assert 'repository.write' in [
332 x.permission.permission_name for x in inherited_perms]
334 x.permission.permission_name for x in inherited_perms]
333
335
334 RepoModel().delete(repo_name_full)
336 RepoModel().delete(repo_name_full)
335 RepoGroupModel().delete(group_name)
337 RepoGroupModel().delete(group_name)
336 Session().commit()
338 Session().commit()
337
339
338 @pytest.mark.xfail_backends(
340 @pytest.mark.xfail_backends(
339 "git", "hg", reason="Missing reposerver support")
341 "git", "hg", reason="Missing reposerver support")
340 def test_create_with_clone_uri(self, autologin_user, backend, reposerver,
342 def test_create_with_clone_uri(self, autologin_user, backend, reposerver,
341 csrf_token):
343 csrf_token):
342 source_repo = backend.create_repo(number_of_commits=2)
344 source_repo = backend.create_repo(number_of_commits=2)
343 source_repo_name = source_repo.repo_name
345 source_repo_name = source_repo.repo_name
344 reposerver.serve(source_repo.scm_instance())
346 reposerver.serve(source_repo.scm_instance())
345
347
346 repo_name = backend.new_repo_name()
348 repo_name = backend.new_repo_name()
347 response = self.app.post(
349 response = self.app.post(
348 route_path('repo_create'),
350 route_path('repo_create'),
349 fixture._get_repo_create_params(
351 fixture._get_repo_create_params(
350 repo_private=False,
352 repo_private=False,
351 repo_name=repo_name,
353 repo_name=repo_name,
352 repo_type=backend.alias,
354 repo_type=backend.alias,
353 repo_description='',
355 repo_description='',
354 clone_uri=reposerver.url,
356 clone_uri=reposerver.url,
355 csrf_token=csrf_token),
357 csrf_token=csrf_token),
356 status=302)
358 status=302)
357
359
358 # Should be redirected to the creating page
360 # Should be redirected to the creating page
359 response.mustcontain('repo_creating')
361 response.mustcontain('repo_creating')
360
362
361 # Expecting that both repositories have same history
363 # Expecting that both repositories have same history
362 source_repo = RepoModel().get_by_repo_name(source_repo_name)
364 source_repo = RepoModel().get_by_repo_name(source_repo_name)
363 source_vcs = source_repo.scm_instance()
365 source_vcs = source_repo.scm_instance()
364 repo = RepoModel().get_by_repo_name(repo_name)
366 repo = RepoModel().get_by_repo_name(repo_name)
365 repo_vcs = repo.scm_instance()
367 repo_vcs = repo.scm_instance()
366 assert source_vcs[0].message == repo_vcs[0].message
368 assert source_vcs[0].message == repo_vcs[0].message
367 assert source_vcs.count() == repo_vcs.count()
369 assert source_vcs.count() == repo_vcs.count()
368 assert source_vcs.commit_ids == repo_vcs.commit_ids
370 assert source_vcs.commit_ids == repo_vcs.commit_ids
369
371
370 @pytest.mark.xfail_backends("svn", reason="Depends on import support")
372 @pytest.mark.xfail_backends("svn", reason="Depends on import support")
371 def test_create_remote_repo_wrong_clone_uri(self, autologin_user, backend,
373 def test_create_remote_repo_wrong_clone_uri(self, autologin_user, backend,
372 csrf_token):
374 csrf_token):
373 repo_name = backend.new_repo_name()
375 repo_name = backend.new_repo_name()
374 description = 'description for newly created repo'
376 description = 'description for newly created repo'
375 response = self.app.post(
377 response = self.app.post(
376 route_path('repo_create'),
378 route_path('repo_create'),
377 fixture._get_repo_create_params(
379 fixture._get_repo_create_params(
378 repo_private=False,
380 repo_private=False,
379 repo_name=repo_name,
381 repo_name=repo_name,
380 repo_type=backend.alias,
382 repo_type=backend.alias,
381 repo_description=description,
383 repo_description=description,
382 clone_uri='http://repo.invalid/repo',
384 clone_uri='http://repo.invalid/repo',
383 csrf_token=csrf_token))
385 csrf_token=csrf_token))
384 response.mustcontain('invalid clone url')
386 response.mustcontain('invalid clone url')
385
387
386 @pytest.mark.xfail_backends("svn", reason="Depends on import support")
388 @pytest.mark.xfail_backends("svn", reason="Depends on import support")
387 def test_create_remote_repo_wrong_clone_uri_hg_svn(
389 def test_create_remote_repo_wrong_clone_uri_hg_svn(
388 self, autologin_user, backend, csrf_token):
390 self, autologin_user, backend, csrf_token):
389 repo_name = backend.new_repo_name()
391 repo_name = backend.new_repo_name()
390 description = 'description for newly created repo'
392 description = 'description for newly created repo'
391 response = self.app.post(
393 response = self.app.post(
392 route_path('repo_create'),
394 route_path('repo_create'),
393 fixture._get_repo_create_params(
395 fixture._get_repo_create_params(
394 repo_private=False,
396 repo_private=False,
395 repo_name=repo_name,
397 repo_name=repo_name,
396 repo_type=backend.alias,
398 repo_type=backend.alias,
397 repo_description=description,
399 repo_description=description,
398 clone_uri='svn+http://svn.invalid/repo',
400 clone_uri='svn+http://svn.invalid/repo',
399 csrf_token=csrf_token))
401 csrf_token=csrf_token))
400 response.mustcontain('invalid clone url')
402 response.mustcontain('invalid clone url')
401
403
402 def test_create_with_git_suffix(
404 def test_create_with_git_suffix(
403 self, autologin_user, backend, csrf_token):
405 self, autologin_user, backend, csrf_token):
404 repo_name = backend.new_repo_name() + ".git"
406 repo_name = backend.new_repo_name() + ".git"
405 description = 'description for newly created repo'
407 description = 'description for newly created repo'
406 response = self.app.post(
408 response = self.app.post(
407 route_path('repo_create'),
409 route_path('repo_create'),
408 fixture._get_repo_create_params(
410 fixture._get_repo_create_params(
409 repo_private=False,
411 repo_private=False,
410 repo_name=repo_name,
412 repo_name=repo_name,
411 repo_type=backend.alias,
413 repo_type=backend.alias,
412 repo_description=description,
414 repo_description=description,
413 csrf_token=csrf_token))
415 csrf_token=csrf_token))
414 response.mustcontain('Repository name cannot end with .git')
416 response.mustcontain('Repository name cannot end with .git')
415
417
416 def test_default_user_cannot_access_private_repo_in_a_group(
418 def test_default_user_cannot_access_private_repo_in_a_group(
417 self, autologin_user, user_util, backend):
419 self, autologin_user, user_util, backend):
418
420
419 group = user_util.create_repo_group()
421 group = user_util.create_repo_group()
420
422
421 repo = backend.create_repo(
423 repo = backend.create_repo(
422 repo_private=True, repo_group=group, repo_copy_permissions=True)
424 repo_private=True, repo_group=group, repo_copy_permissions=True)
423
425
424 permissions = _get_permission_for_user(
426 permissions = _get_permission_for_user(
425 user='default', repo=repo.repo_name)
427 user='default', repo=repo.repo_name)
426 assert len(permissions) == 1
428 assert len(permissions) == 1
427 assert permissions[0].permission.permission_name == 'repository.none'
429 assert permissions[0].permission.permission_name == 'repository.none'
428 assert permissions[0].repository.private is True
430 assert permissions[0].repository.private is True
429
431
430 def test_create_on_top_level_without_permissions(self, backend):
432 def test_create_on_top_level_without_permissions(self, backend):
431 session = login_user_session(
433 session = login_user_session(
432 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
434 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
433 csrf_token = auth.get_csrf_token(session)
435 csrf_token = auth.get_csrf_token(session)
434
436
435 # revoke
437 # revoke
436 user_model = UserModel()
438 user_model = UserModel()
437 # disable fork and create on default user
439 # disable fork and create on default user
438 user_model.revoke_perm(User.DEFAULT_USER, 'hg.create.repository')
440 user_model.revoke_perm(User.DEFAULT_USER, 'hg.create.repository')
439 user_model.grant_perm(User.DEFAULT_USER, 'hg.create.none')
441 user_model.grant_perm(User.DEFAULT_USER, 'hg.create.none')
440 user_model.revoke_perm(User.DEFAULT_USER, 'hg.fork.repository')
442 user_model.revoke_perm(User.DEFAULT_USER, 'hg.fork.repository')
441 user_model.grant_perm(User.DEFAULT_USER, 'hg.fork.none')
443 user_model.grant_perm(User.DEFAULT_USER, 'hg.fork.none')
442
444
443 # disable on regular user
445 # disable on regular user
444 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.repository')
446 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.repository')
445 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.none')
447 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.none')
446 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.repository')
448 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.repository')
447 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.none')
449 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.none')
448 Session().commit()
450 Session().commit()
449
451
450 repo_name = backend.new_repo_name()
452 repo_name = backend.new_repo_name()
451 description = 'description for newly created repo'
453 description = 'description for newly created repo'
452 response = self.app.post(
454 response = self.app.post(
453 route_path('repo_create'),
455 route_path('repo_create'),
454 fixture._get_repo_create_params(
456 fixture._get_repo_create_params(
455 repo_private=False,
457 repo_private=False,
456 repo_name=repo_name,
458 repo_name=repo_name,
457 repo_type=backend.alias,
459 repo_type=backend.alias,
458 repo_description=description,
460 repo_description=description,
459 csrf_token=csrf_token))
461 csrf_token=csrf_token))
460
462
461 response.mustcontain(
463 response.mustcontain(
462 u"You do not have the permission to store repositories in "
464 u"You do not have the permission to store repositories in "
463 u"the root location.")
465 u"the root location.")
464
466
465 @mock.patch.object(RepoModel, '_create_filesystem_repo', error_function)
467 @mock.patch.object(RepoModel, '_create_filesystem_repo', error_function)
466 def test_create_repo_when_filesystem_op_fails(
468 def test_create_repo_when_filesystem_op_fails(
467 self, autologin_user, backend, csrf_token):
469 self, autologin_user, backend, csrf_token):
468 repo_name = backend.new_repo_name()
470 repo_name = backend.new_repo_name()
469 description = 'description for newly created repo'
471 description = 'description for newly created repo'
470
472
471 response = self.app.post(
473 response = self.app.post(
472 route_path('repo_create'),
474 route_path('repo_create'),
473 fixture._get_repo_create_params(
475 fixture._get_repo_create_params(
474 repo_private=False,
476 repo_private=False,
475 repo_name=repo_name,
477 repo_name=repo_name,
476 repo_type=backend.alias,
478 repo_type=backend.alias,
477 repo_description=description,
479 repo_description=description,
478 csrf_token=csrf_token))
480 csrf_token=csrf_token))
479
481
480 assert_session_flash(
482 assert_session_flash(
481 response, 'Error creating repository %s' % repo_name)
483 response, 'Error creating repository %s' % repo_name)
482 # repo must not be in db
484 # repo must not be in db
483 assert backend.repo is None
485 assert backend.repo is None
484 # repo must not be in filesystem !
486 # repo must not be in filesystem !
485 assert not repo_on_filesystem(repo_name)
487 assert not repo_on_filesystem(repo_name)
486
488
487 def assert_repository_is_created_correctly(
489 def assert_repository_is_created_correctly(
488 self, repo_name, description, backend):
490 self, repo_name, description, backend):
489 repo_name_utf8 = safe_str(repo_name)
491 repo_name_utf8 = safe_str(repo_name)
490
492
491 # run the check page that triggers the flash message
493 # run the check page that triggers the flash message
492 response = self.app.get(
494 response = self.app.get(
493 route_path('repo_creating_check', repo_name=safe_str(repo_name)))
495 route_path('repo_creating_check', repo_name=safe_str(repo_name)))
494 assert response.json == {u'result': True}
496 assert response.json == {u'result': True}
495
497
496 flash_msg = u'Created repository <a href="/{}">{}</a>'.format(
498 flash_msg = u'Created repository <a href="/{}">{}</a>'.format(
497 urllib.quote(repo_name_utf8), repo_name)
499 urllib.quote(repo_name_utf8), repo_name)
498 assert_session_flash(response, flash_msg)
500 assert_session_flash(response, flash_msg)
499
501
500 # test if the repo was created in the database
502 # test if the repo was created in the database
501 new_repo = RepoModel().get_by_repo_name(repo_name)
503 new_repo = RepoModel().get_by_repo_name(repo_name)
502
504
503 assert new_repo.repo_name == repo_name
505 assert new_repo.repo_name == repo_name
504 assert new_repo.description == description
506 assert new_repo.description == description
505
507
506 # test if the repository is visible in the list ?
508 # test if the repository is visible in the list ?
507 response = self.app.get(
509 response = self.app.get(
508 h.route_path('repo_summary', repo_name=safe_str(repo_name)))
510 h.route_path('repo_summary', repo_name=safe_str(repo_name)))
509 response.mustcontain(repo_name)
511 response.mustcontain(repo_name)
510 response.mustcontain(backend.alias)
512 response.mustcontain(backend.alias)
511
513
512 assert repo_on_filesystem(repo_name)
514 assert repo_on_filesystem(repo_name)
@@ -1,194 +1,194 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 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 pytest
22 import pytest
23
23
24 from rhodecode.apps._base import ADMIN_PREFIX
24 from rhodecode.apps._base import ADMIN_PREFIX
25 from rhodecode.lib import helpers as h
25 from rhodecode.lib import helpers as h
26 from rhodecode.model.db import Repository, UserRepoToPerm, User, RepoGroup
26 from rhodecode.model.db import Repository, UserRepoToPerm, User, RepoGroup
27 from rhodecode.model.meta import Session
27 from rhodecode.model.meta import Session
28 from rhodecode.model.repo_group import RepoGroupModel
28 from rhodecode.model.repo_group import RepoGroupModel
29 from rhodecode.tests import (
29 from rhodecode.tests import (
30 assert_session_flash, TEST_USER_REGULAR_LOGIN, TESTS_TMP_PATH)
30 assert_session_flash, TEST_USER_REGULAR_LOGIN, TESTS_TMP_PATH)
31 from rhodecode.tests.fixture import Fixture
31 from rhodecode.tests.fixture import Fixture
32
32
33 fixture = Fixture()
33 fixture = Fixture()
34
34
35
35
36 def route_path(name, params=None, **kwargs):
36 def route_path(name, params=None, **kwargs):
37 import urllib
37 import urllib
38
38
39 base_url = {
39 base_url = {
40 'repo_groups': ADMIN_PREFIX + '/repo_groups',
40 'repo_groups': ADMIN_PREFIX + '/repo_groups',
41 'repo_groups_data': ADMIN_PREFIX + '/repo_groups_data',
41 'repo_groups_data': ADMIN_PREFIX + '/repo_groups_data',
42 'repo_group_new': ADMIN_PREFIX + '/repo_group/new',
42 'repo_group_new': ADMIN_PREFIX + '/repo_group/new',
43 'repo_group_create': ADMIN_PREFIX + '/repo_group/create',
43 'repo_group_create': ADMIN_PREFIX + '/repo_group/create',
44
44
45 }[name].format(**kwargs)
45 }[name].format(**kwargs)
46
46
47 if params:
47 if params:
48 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
48 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
49 return base_url
49 return base_url
50
50
51
51
52 def _get_permission_for_user(user, repo):
52 def _get_permission_for_user(user, repo):
53 perm = UserRepoToPerm.query()\
53 perm = UserRepoToPerm.query()\
54 .filter(UserRepoToPerm.repository ==
54 .filter(UserRepoToPerm.repository ==
55 Repository.get_by_repo_name(repo))\
55 Repository.get_by_repo_name(repo))\
56 .filter(UserRepoToPerm.user == User.get_by_username(user))\
56 .filter(UserRepoToPerm.user == User.get_by_username(user))\
57 .all()
57 .all()
58 return perm
58 return perm
59
59
60
60
61 @pytest.mark.usefixtures("app")
61 @pytest.mark.usefixtures("app")
62 class TestAdminRepositoryGroups(object):
62 class TestAdminRepositoryGroups(object):
63
63
64 def test_show_repo_groups(self, autologin_user):
64 def test_show_repo_groups(self, autologin_user):
65 self.app.get(route_path('repo_groups'))
65 self.app.get(route_path('repo_groups'))
66
66
67 def test_show_repo_groups_data(self, autologin_user, xhr_header):
67 def test_show_repo_groups_data(self, autologin_user, xhr_header):
68 response = self.app.get(route_path(
68 response = self.app.get(route_path(
69 'repo_groups_data'), extra_environ=xhr_header)
69 'repo_groups_data'), extra_environ=xhr_header)
70
70
71 all_repo_groups = RepoGroup.query().count()
71 all_repo_groups = RepoGroup.query().count()
72 assert response.json['recordsTotal'] == all_repo_groups
72 assert response.json['recordsTotal'] == all_repo_groups
73
73
74 def test_show_repo_groups_data_filtered(self, autologin_user, xhr_header):
74 def test_show_repo_groups_data_filtered(self, autologin_user, xhr_header):
75 response = self.app.get(route_path(
75 response = self.app.get(route_path(
76 'repo_groups_data', params={'search[value]': 'empty_search'}),
76 'repo_groups_data', params={'search[value]': 'empty_search'}),
77 extra_environ=xhr_header)
77 extra_environ=xhr_header)
78
78
79 all_repo_groups = RepoGroup.query().count()
79 all_repo_groups = RepoGroup.query().count()
80 assert response.json['recordsTotal'] == all_repo_groups
80 assert response.json['recordsTotal'] == all_repo_groups
81 assert response.json['recordsFiltered'] == 0
81 assert response.json['recordsFiltered'] == 0
82
82
83 def test_show_repo_groups_after_creating_group(self, autologin_user, xhr_header):
83 def test_show_repo_groups_after_creating_group(self, autologin_user, xhr_header):
84 fixture.create_repo_group('test_repo_group')
84 fixture.create_repo_group('test_repo_group')
85 response = self.app.get(route_path(
85 response = self.app.get(route_path(
86 'repo_groups_data'), extra_environ=xhr_header)
86 'repo_groups_data'), extra_environ=xhr_header)
87 response.mustcontain('"name_raw": "test_repo_group"')
87 response.mustcontain('<a href=\\"/{}/_edit\\" title=\\"Edit\\">Edit</a>'.format('test_repo_group'))
88 fixture.destroy_repo_group('test_repo_group')
88 fixture.destroy_repo_group('test_repo_group')
89
89
90 def test_new(self, autologin_user):
90 def test_new(self, autologin_user):
91 self.app.get(route_path('repo_group_new'))
91 self.app.get(route_path('repo_group_new'))
92
92
93 def test_new_with_parent_group(self, autologin_user, user_util):
93 def test_new_with_parent_group(self, autologin_user, user_util):
94 gr = user_util.create_repo_group()
94 gr = user_util.create_repo_group()
95
95
96 self.app.get(route_path('repo_group_new'),
96 self.app.get(route_path('repo_group_new'),
97 params=dict(parent_group=gr.group_name))
97 params=dict(parent_group=gr.group_name))
98
98
99 def test_new_by_regular_user_no_permission(self, autologin_regular_user):
99 def test_new_by_regular_user_no_permission(self, autologin_regular_user):
100 self.app.get(route_path('repo_group_new'), status=403)
100 self.app.get(route_path('repo_group_new'), status=403)
101
101
102 @pytest.mark.parametrize('repo_group_name', [
102 @pytest.mark.parametrize('repo_group_name', [
103 'git_repo',
103 'git_repo',
104 'git_repo_Δ…Δ‡',
104 'git_repo_Δ…Δ‡',
105 'hg_repo',
105 'hg_repo',
106 '12345',
106 '12345',
107 'hg_repo_Δ…Δ‡',
107 'hg_repo_Δ…Δ‡',
108 ])
108 ])
109 def test_create(self, autologin_user, repo_group_name, csrf_token):
109 def test_create(self, autologin_user, repo_group_name, csrf_token):
110 repo_group_name_unicode = repo_group_name.decode('utf8')
110 repo_group_name_unicode = repo_group_name.decode('utf8')
111 description = 'description for newly created repo group'
111 description = 'description for newly created repo group'
112
112
113 response = self.app.post(
113 response = self.app.post(
114 route_path('repo_group_create'),
114 route_path('repo_group_create'),
115 fixture._get_group_create_params(
115 fixture._get_group_create_params(
116 group_name=repo_group_name,
116 group_name=repo_group_name,
117 group_description=description,
117 group_description=description,
118 csrf_token=csrf_token))
118 csrf_token=csrf_token))
119
119
120 # run the check page that triggers the flash message
120 # run the check page that triggers the flash message
121 repo_gr_url = h.route_path(
121 repo_gr_url = h.route_path(
122 'repo_group_home', repo_group_name=repo_group_name)
122 'repo_group_home', repo_group_name=repo_group_name)
123
123
124 assert_session_flash(
124 assert_session_flash(
125 response,
125 response,
126 'Created repository group <a href="%s">%s</a>' % (
126 'Created repository group <a href="%s">%s</a>' % (
127 repo_gr_url, repo_group_name_unicode))
127 repo_gr_url, repo_group_name_unicode))
128
128
129 # # test if the repo group was created in the database
129 # # test if the repo group was created in the database
130 new_repo_group = RepoGroupModel()._get_repo_group(
130 new_repo_group = RepoGroupModel()._get_repo_group(
131 repo_group_name_unicode)
131 repo_group_name_unicode)
132 assert new_repo_group is not None
132 assert new_repo_group is not None
133
133
134 assert new_repo_group.group_name == repo_group_name_unicode
134 assert new_repo_group.group_name == repo_group_name_unicode
135 assert new_repo_group.group_description == description
135 assert new_repo_group.group_description == description
136
136
137 # test if the repository is visible in the list ?
137 # test if the repository is visible in the list ?
138 response = self.app.get(repo_gr_url)
138 response = self.app.get(repo_gr_url)
139 response.mustcontain(repo_group_name)
139 response.mustcontain(repo_group_name)
140
140
141 # test if the repository group was created on filesystem
141 # test if the repository group was created on filesystem
142 is_on_filesystem = os.path.isdir(
142 is_on_filesystem = os.path.isdir(
143 os.path.join(TESTS_TMP_PATH, repo_group_name))
143 os.path.join(TESTS_TMP_PATH, repo_group_name))
144 if not is_on_filesystem:
144 if not is_on_filesystem:
145 self.fail('no repo group %s in filesystem' % repo_group_name)
145 self.fail('no repo group %s in filesystem' % repo_group_name)
146
146
147 RepoGroupModel().delete(repo_group_name_unicode)
147 RepoGroupModel().delete(repo_group_name_unicode)
148 Session().commit()
148 Session().commit()
149
149
150 @pytest.mark.parametrize('repo_group_name', [
150 @pytest.mark.parametrize('repo_group_name', [
151 'git_repo',
151 'git_repo',
152 'git_repo_Δ…Δ‡',
152 'git_repo_Δ…Δ‡',
153 'hg_repo',
153 'hg_repo',
154 '12345',
154 '12345',
155 'hg_repo_Δ…Δ‡',
155 'hg_repo_Δ…Δ‡',
156 ])
156 ])
157 def test_create_subgroup(self, autologin_user, user_util, repo_group_name, csrf_token):
157 def test_create_subgroup(self, autologin_user, user_util, repo_group_name, csrf_token):
158 parent_group = user_util.create_repo_group()
158 parent_group = user_util.create_repo_group()
159 parent_group_name = parent_group.group_name
159 parent_group_name = parent_group.group_name
160
160
161 expected_group_name = '{}/{}'.format(
161 expected_group_name = '{}/{}'.format(
162 parent_group_name, repo_group_name)
162 parent_group_name, repo_group_name)
163 expected_group_name_unicode = expected_group_name.decode('utf8')
163 expected_group_name_unicode = expected_group_name.decode('utf8')
164
164
165 try:
165 try:
166 response = self.app.post(
166 response = self.app.post(
167 route_path('repo_group_create'),
167 route_path('repo_group_create'),
168 fixture._get_group_create_params(
168 fixture._get_group_create_params(
169 group_name=repo_group_name,
169 group_name=repo_group_name,
170 group_parent_id=parent_group.group_id,
170 group_parent_id=parent_group.group_id,
171 group_description='Test desciption',
171 group_description='Test desciption',
172 csrf_token=csrf_token))
172 csrf_token=csrf_token))
173
173
174 assert_session_flash(
174 assert_session_flash(
175 response,
175 response,
176 u'Created repository group <a href="%s">%s</a>' % (
176 u'Created repository group <a href="%s">%s</a>' % (
177 h.route_path('repo_group_home',
177 h.route_path('repo_group_home',
178 repo_group_name=expected_group_name),
178 repo_group_name=expected_group_name),
179 expected_group_name_unicode))
179 expected_group_name_unicode))
180 finally:
180 finally:
181 RepoGroupModel().delete(expected_group_name_unicode)
181 RepoGroupModel().delete(expected_group_name_unicode)
182 Session().commit()
182 Session().commit()
183
183
184 def test_user_with_creation_permissions_cannot_create_subgroups(
184 def test_user_with_creation_permissions_cannot_create_subgroups(
185 self, autologin_regular_user, user_util):
185 self, autologin_regular_user, user_util):
186
186
187 user_util.grant_user_permission(
187 user_util.grant_user_permission(
188 TEST_USER_REGULAR_LOGIN, 'hg.repogroup.create.true')
188 TEST_USER_REGULAR_LOGIN, 'hg.repogroup.create.true')
189 parent_group = user_util.create_repo_group()
189 parent_group = user_util.create_repo_group()
190 parent_group_id = parent_group.group_id
190 parent_group_id = parent_group.group_id
191 self.app.get(
191 self.app.get(
192 route_path('repo_group_new',
192 route_path('repo_group_new',
193 params=dict(parent_group=parent_group_id), ),
193 params=dict(parent_group=parent_group_id), ),
194 status=403)
194 status=403)
@@ -1,793 +1,794 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 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 pytest
21 import pytest
22 from sqlalchemy.orm.exc import NoResultFound
22 from sqlalchemy.orm.exc import NoResultFound
23
23
24 from rhodecode.lib import auth
24 from rhodecode.lib import auth
25 from rhodecode.lib import helpers as h
25 from rhodecode.lib import helpers as h
26 from rhodecode.model.db import User, UserApiKeys, UserEmailMap, Repository
26 from rhodecode.model.db import User, UserApiKeys, UserEmailMap, Repository
27 from rhodecode.model.meta import Session
27 from rhodecode.model.meta import Session
28 from rhodecode.model.user import UserModel
28 from rhodecode.model.user import UserModel
29
29
30 from rhodecode.tests import (
30 from rhodecode.tests import (
31 TestController, TEST_USER_REGULAR_LOGIN, assert_session_flash)
31 TestController, TEST_USER_REGULAR_LOGIN, assert_session_flash)
32 from rhodecode.tests.fixture import Fixture
32 from rhodecode.tests.fixture import Fixture
33
33
34 fixture = Fixture()
34 fixture = Fixture()
35
35
36
36
37 def route_path(name, params=None, **kwargs):
37 def route_path(name, params=None, **kwargs):
38 import urllib
38 import urllib
39 from rhodecode.apps._base import ADMIN_PREFIX
39 from rhodecode.apps._base import ADMIN_PREFIX
40
40
41 base_url = {
41 base_url = {
42 'users':
42 'users':
43 ADMIN_PREFIX + '/users',
43 ADMIN_PREFIX + '/users',
44 'users_data':
44 'users_data':
45 ADMIN_PREFIX + '/users_data',
45 ADMIN_PREFIX + '/users_data',
46 'users_create':
46 'users_create':
47 ADMIN_PREFIX + '/users/create',
47 ADMIN_PREFIX + '/users/create',
48 'users_new':
48 'users_new':
49 ADMIN_PREFIX + '/users/new',
49 ADMIN_PREFIX + '/users/new',
50 'user_edit':
50 'user_edit':
51 ADMIN_PREFIX + '/users/{user_id}/edit',
51 ADMIN_PREFIX + '/users/{user_id}/edit',
52 'user_edit_advanced':
52 'user_edit_advanced':
53 ADMIN_PREFIX + '/users/{user_id}/edit/advanced',
53 ADMIN_PREFIX + '/users/{user_id}/edit/advanced',
54 'user_edit_global_perms':
54 'user_edit_global_perms':
55 ADMIN_PREFIX + '/users/{user_id}/edit/global_permissions',
55 ADMIN_PREFIX + '/users/{user_id}/edit/global_permissions',
56 'user_edit_global_perms_update':
56 'user_edit_global_perms_update':
57 ADMIN_PREFIX + '/users/{user_id}/edit/global_permissions/update',
57 ADMIN_PREFIX + '/users/{user_id}/edit/global_permissions/update',
58 'user_update':
58 'user_update':
59 ADMIN_PREFIX + '/users/{user_id}/update',
59 ADMIN_PREFIX + '/users/{user_id}/update',
60 'user_delete':
60 'user_delete':
61 ADMIN_PREFIX + '/users/{user_id}/delete',
61 ADMIN_PREFIX + '/users/{user_id}/delete',
62 'user_create_personal_repo_group':
62 'user_create_personal_repo_group':
63 ADMIN_PREFIX + '/users/{user_id}/create_repo_group',
63 ADMIN_PREFIX + '/users/{user_id}/create_repo_group',
64
64
65 'edit_user_auth_tokens':
65 'edit_user_auth_tokens':
66 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens',
66 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens',
67 'edit_user_auth_tokens_add':
67 'edit_user_auth_tokens_add':
68 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens/new',
68 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens/new',
69 'edit_user_auth_tokens_delete':
69 'edit_user_auth_tokens_delete':
70 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens/delete',
70 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens/delete',
71
71
72 'edit_user_emails':
72 'edit_user_emails':
73 ADMIN_PREFIX + '/users/{user_id}/edit/emails',
73 ADMIN_PREFIX + '/users/{user_id}/edit/emails',
74 'edit_user_emails_add':
74 'edit_user_emails_add':
75 ADMIN_PREFIX + '/users/{user_id}/edit/emails/new',
75 ADMIN_PREFIX + '/users/{user_id}/edit/emails/new',
76 'edit_user_emails_delete':
76 'edit_user_emails_delete':
77 ADMIN_PREFIX + '/users/{user_id}/edit/emails/delete',
77 ADMIN_PREFIX + '/users/{user_id}/edit/emails/delete',
78
78
79 'edit_user_ips':
79 'edit_user_ips':
80 ADMIN_PREFIX + '/users/{user_id}/edit/ips',
80 ADMIN_PREFIX + '/users/{user_id}/edit/ips',
81 'edit_user_ips_add':
81 'edit_user_ips_add':
82 ADMIN_PREFIX + '/users/{user_id}/edit/ips/new',
82 ADMIN_PREFIX + '/users/{user_id}/edit/ips/new',
83 'edit_user_ips_delete':
83 'edit_user_ips_delete':
84 ADMIN_PREFIX + '/users/{user_id}/edit/ips/delete',
84 ADMIN_PREFIX + '/users/{user_id}/edit/ips/delete',
85
85
86 'edit_user_perms_summary':
86 'edit_user_perms_summary':
87 ADMIN_PREFIX + '/users/{user_id}/edit/permissions_summary',
87 ADMIN_PREFIX + '/users/{user_id}/edit/permissions_summary',
88 'edit_user_perms_summary_json':
88 'edit_user_perms_summary_json':
89 ADMIN_PREFIX + '/users/{user_id}/edit/permissions_summary/json',
89 ADMIN_PREFIX + '/users/{user_id}/edit/permissions_summary/json',
90
90
91 'edit_user_audit_logs':
91 'edit_user_audit_logs':
92 ADMIN_PREFIX + '/users/{user_id}/edit/audit',
92 ADMIN_PREFIX + '/users/{user_id}/edit/audit',
93
93
94 'edit_user_audit_logs_download':
94 'edit_user_audit_logs_download':
95 ADMIN_PREFIX + '/users/{user_id}/edit/audit/download',
95 ADMIN_PREFIX + '/users/{user_id}/edit/audit/download',
96
96
97 }[name].format(**kwargs)
97 }[name].format(**kwargs)
98
98
99 if params:
99 if params:
100 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
100 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
101 return base_url
101 return base_url
102
102
103
103
104 class TestAdminUsersView(TestController):
104 class TestAdminUsersView(TestController):
105
105
106 def test_show_users(self):
106 def test_show_users(self):
107 self.log_user()
107 self.log_user()
108 self.app.get(route_path('users'))
108 self.app.get(route_path('users'))
109
109
110 def test_show_users_data(self, xhr_header):
110 def test_show_users_data(self, xhr_header):
111 self.log_user()
111 self.log_user()
112 response = self.app.get(route_path(
112 response = self.app.get(route_path(
113 'users_data'), extra_environ=xhr_header)
113 'users_data'), extra_environ=xhr_header)
114
114
115 all_users = User.query().filter(
115 all_users = User.query().filter(
116 User.username != User.DEFAULT_USER).count()
116 User.username != User.DEFAULT_USER).count()
117 assert response.json['recordsTotal'] == all_users
117 assert response.json['recordsTotal'] == all_users
118
118
119 def test_show_users_data_filtered(self, xhr_header):
119 def test_show_users_data_filtered(self, xhr_header):
120 self.log_user()
120 self.log_user()
121 response = self.app.get(route_path(
121 response = self.app.get(route_path(
122 'users_data', params={'search[value]': 'empty_search'}),
122 'users_data', params={'search[value]': 'empty_search'}),
123 extra_environ=xhr_header)
123 extra_environ=xhr_header)
124
124
125 all_users = User.query().filter(
125 all_users = User.query().filter(
126 User.username != User.DEFAULT_USER).count()
126 User.username != User.DEFAULT_USER).count()
127 assert response.json['recordsTotal'] == all_users
127 assert response.json['recordsTotal'] == all_users
128 assert response.json['recordsFiltered'] == 0
128 assert response.json['recordsFiltered'] == 0
129
129
130 def test_auth_tokens_default_user(self):
130 def test_auth_tokens_default_user(self):
131 self.log_user()
131 self.log_user()
132 user = User.get_default_user()
132 user = User.get_default_user()
133 response = self.app.get(
133 response = self.app.get(
134 route_path('edit_user_auth_tokens', user_id=user.user_id),
134 route_path('edit_user_auth_tokens', user_id=user.user_id),
135 status=302)
135 status=302)
136
136
137 def test_auth_tokens(self):
137 def test_auth_tokens(self):
138 self.log_user()
138 self.log_user()
139
139
140 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
140 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
141 user_id = user.user_id
141 user_id = user.user_id
142 auth_tokens = user.auth_tokens
142 auth_tokens = user.auth_tokens
143 response = self.app.get(
143 response = self.app.get(
144 route_path('edit_user_auth_tokens', user_id=user_id))
144 route_path('edit_user_auth_tokens', user_id=user_id))
145 for token in auth_tokens:
145 for token in auth_tokens:
146 response.mustcontain(token)
146 response.mustcontain(token)
147 response.mustcontain('never')
147 response.mustcontain('never')
148
148
149 @pytest.mark.parametrize("desc, lifetime", [
149 @pytest.mark.parametrize("desc, lifetime", [
150 ('forever', -1),
150 ('forever', -1),
151 ('5mins', 60*5),
151 ('5mins', 60*5),
152 ('30days', 60*60*24*30),
152 ('30days', 60*60*24*30),
153 ])
153 ])
154 def test_add_auth_token(self, desc, lifetime, user_util):
154 def test_add_auth_token(self, desc, lifetime, user_util):
155 self.log_user()
155 self.log_user()
156 user = user_util.create_user()
156 user = user_util.create_user()
157 user_id = user.user_id
157 user_id = user.user_id
158
158
159 response = self.app.post(
159 response = self.app.post(
160 route_path('edit_user_auth_tokens_add', user_id=user_id),
160 route_path('edit_user_auth_tokens_add', user_id=user_id),
161 {'description': desc, 'lifetime': lifetime,
161 {'description': desc, 'lifetime': lifetime,
162 'csrf_token': self.csrf_token})
162 'csrf_token': self.csrf_token})
163 assert_session_flash(response, 'Auth token successfully created')
163 assert_session_flash(response, 'Auth token successfully created')
164
164
165 response = response.follow()
165 response = response.follow()
166 user = User.get(user_id)
166 user = User.get(user_id)
167 for auth_token in user.auth_tokens:
167 for auth_token in user.auth_tokens:
168 response.mustcontain(auth_token)
168 response.mustcontain(auth_token)
169
169
170 def test_delete_auth_token(self, user_util):
170 def test_delete_auth_token(self, user_util):
171 self.log_user()
171 self.log_user()
172 user = user_util.create_user()
172 user = user_util.create_user()
173 user_id = user.user_id
173 user_id = user.user_id
174 keys = user.auth_tokens
174 keys = user.auth_tokens
175 assert 2 == len(keys)
175 assert 2 == len(keys)
176
176
177 response = self.app.post(
177 response = self.app.post(
178 route_path('edit_user_auth_tokens_add', user_id=user_id),
178 route_path('edit_user_auth_tokens_add', user_id=user_id),
179 {'description': 'desc', 'lifetime': -1,
179 {'description': 'desc', 'lifetime': -1,
180 'csrf_token': self.csrf_token})
180 'csrf_token': self.csrf_token})
181 assert_session_flash(response, 'Auth token successfully created')
181 assert_session_flash(response, 'Auth token successfully created')
182 response.follow()
182 response.follow()
183
183
184 # now delete our key
184 # now delete our key
185 keys = UserApiKeys.query().filter(UserApiKeys.user_id == user_id).all()
185 keys = UserApiKeys.query().filter(UserApiKeys.user_id == user_id).all()
186 assert 3 == len(keys)
186 assert 3 == len(keys)
187
187
188 response = self.app.post(
188 response = self.app.post(
189 route_path('edit_user_auth_tokens_delete', user_id=user_id),
189 route_path('edit_user_auth_tokens_delete', user_id=user_id),
190 {'del_auth_token': keys[0].user_api_key_id,
190 {'del_auth_token': keys[0].user_api_key_id,
191 'csrf_token': self.csrf_token})
191 'csrf_token': self.csrf_token})
192
192
193 assert_session_flash(response, 'Auth token successfully deleted')
193 assert_session_flash(response, 'Auth token successfully deleted')
194 keys = UserApiKeys.query().filter(UserApiKeys.user_id == user_id).all()
194 keys = UserApiKeys.query().filter(UserApiKeys.user_id == user_id).all()
195 assert 2 == len(keys)
195 assert 2 == len(keys)
196
196
197 def test_ips(self):
197 def test_ips(self):
198 self.log_user()
198 self.log_user()
199 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
199 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
200 response = self.app.get(route_path('edit_user_ips', user_id=user.user_id))
200 response = self.app.get(route_path('edit_user_ips', user_id=user.user_id))
201 response.mustcontain('All IP addresses are allowed')
201 response.mustcontain('All IP addresses are allowed')
202
202
203 @pytest.mark.parametrize("test_name, ip, ip_range, failure", [
203 @pytest.mark.parametrize("test_name, ip, ip_range, failure", [
204 ('127/24', '127.0.0.1/24', '127.0.0.0 - 127.0.0.255', False),
204 ('127/24', '127.0.0.1/24', '127.0.0.0 - 127.0.0.255', False),
205 ('10/32', '10.0.0.10/32', '10.0.0.10 - 10.0.0.10', False),
205 ('10/32', '10.0.0.10/32', '10.0.0.10 - 10.0.0.10', False),
206 ('0/16', '0.0.0.0/16', '0.0.0.0 - 0.0.255.255', False),
206 ('0/16', '0.0.0.0/16', '0.0.0.0 - 0.0.255.255', False),
207 ('0/8', '0.0.0.0/8', '0.0.0.0 - 0.255.255.255', False),
207 ('0/8', '0.0.0.0/8', '0.0.0.0 - 0.255.255.255', False),
208 ('127_bad_mask', '127.0.0.1/99', '127.0.0.1 - 127.0.0.1', True),
208 ('127_bad_mask', '127.0.0.1/99', '127.0.0.1 - 127.0.0.1', True),
209 ('127_bad_ip', 'foobar', 'foobar', True),
209 ('127_bad_ip', 'foobar', 'foobar', True),
210 ])
210 ])
211 def test_ips_add(self, user_util, test_name, ip, ip_range, failure):
211 def test_ips_add(self, user_util, test_name, ip, ip_range, failure):
212 self.log_user()
212 self.log_user()
213 user = user_util.create_user(username=test_name)
213 user = user_util.create_user(username=test_name)
214 user_id = user.user_id
214 user_id = user.user_id
215
215
216 response = self.app.post(
216 response = self.app.post(
217 route_path('edit_user_ips_add', user_id=user_id),
217 route_path('edit_user_ips_add', user_id=user_id),
218 params={'new_ip': ip, 'csrf_token': self.csrf_token})
218 params={'new_ip': ip, 'csrf_token': self.csrf_token})
219
219
220 if failure:
220 if failure:
221 assert_session_flash(
221 assert_session_flash(
222 response, 'Please enter a valid IPv4 or IpV6 address')
222 response, 'Please enter a valid IPv4 or IpV6 address')
223 response = self.app.get(route_path('edit_user_ips', user_id=user_id))
223 response = self.app.get(route_path('edit_user_ips', user_id=user_id))
224
224
225 response.mustcontain(no=[ip])
225 response.mustcontain(no=[ip])
226 response.mustcontain(no=[ip_range])
226 response.mustcontain(no=[ip_range])
227
227
228 else:
228 else:
229 response = self.app.get(route_path('edit_user_ips', user_id=user_id))
229 response = self.app.get(route_path('edit_user_ips', user_id=user_id))
230 response.mustcontain(ip)
230 response.mustcontain(ip)
231 response.mustcontain(ip_range)
231 response.mustcontain(ip_range)
232
232
233 def test_ips_delete(self, user_util):
233 def test_ips_delete(self, user_util):
234 self.log_user()
234 self.log_user()
235 user = user_util.create_user()
235 user = user_util.create_user()
236 user_id = user.user_id
236 user_id = user.user_id
237 ip = '127.0.0.1/32'
237 ip = '127.0.0.1/32'
238 ip_range = '127.0.0.1 - 127.0.0.1'
238 ip_range = '127.0.0.1 - 127.0.0.1'
239 new_ip = UserModel().add_extra_ip(user_id, ip)
239 new_ip = UserModel().add_extra_ip(user_id, ip)
240 Session().commit()
240 Session().commit()
241 new_ip_id = new_ip.ip_id
241 new_ip_id = new_ip.ip_id
242
242
243 response = self.app.get(route_path('edit_user_ips', user_id=user_id))
243 response = self.app.get(route_path('edit_user_ips', user_id=user_id))
244 response.mustcontain(ip)
244 response.mustcontain(ip)
245 response.mustcontain(ip_range)
245 response.mustcontain(ip_range)
246
246
247 self.app.post(
247 self.app.post(
248 route_path('edit_user_ips_delete', user_id=user_id),
248 route_path('edit_user_ips_delete', user_id=user_id),
249 params={'del_ip_id': new_ip_id, 'csrf_token': self.csrf_token})
249 params={'del_ip_id': new_ip_id, 'csrf_token': self.csrf_token})
250
250
251 response = self.app.get(route_path('edit_user_ips', user_id=user_id))
251 response = self.app.get(route_path('edit_user_ips', user_id=user_id))
252 response.mustcontain('All IP addresses are allowed')
252 response.mustcontain('All IP addresses are allowed')
253 response.mustcontain(no=[ip])
253 response.mustcontain(no=[ip])
254 response.mustcontain(no=[ip_range])
254 response.mustcontain(no=[ip_range])
255
255
256 def test_emails(self):
256 def test_emails(self):
257 self.log_user()
257 self.log_user()
258 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
258 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
259 response = self.app.get(
259 response = self.app.get(
260 route_path('edit_user_emails', user_id=user.user_id))
260 route_path('edit_user_emails', user_id=user.user_id))
261 response.mustcontain('No additional emails specified')
261 response.mustcontain('No additional emails specified')
262
262
263 def test_emails_add(self, user_util):
263 def test_emails_add(self, user_util):
264 self.log_user()
264 self.log_user()
265 user = user_util.create_user()
265 user = user_util.create_user()
266 user_id = user.user_id
266 user_id = user.user_id
267
267
268 self.app.post(
268 self.app.post(
269 route_path('edit_user_emails_add', user_id=user_id),
269 route_path('edit_user_emails_add', user_id=user_id),
270 params={'new_email': 'example@rhodecode.com',
270 params={'new_email': 'example@rhodecode.com',
271 'csrf_token': self.csrf_token})
271 'csrf_token': self.csrf_token})
272
272
273 response = self.app.get(
273 response = self.app.get(
274 route_path('edit_user_emails', user_id=user_id))
274 route_path('edit_user_emails', user_id=user_id))
275 response.mustcontain('example@rhodecode.com')
275 response.mustcontain('example@rhodecode.com')
276
276
277 def test_emails_add_existing_email(self, user_util, user_regular):
277 def test_emails_add_existing_email(self, user_util, user_regular):
278 existing_email = user_regular.email
278 existing_email = user_regular.email
279
279
280 self.log_user()
280 self.log_user()
281 user = user_util.create_user()
281 user = user_util.create_user()
282 user_id = user.user_id
282 user_id = user.user_id
283
283
284 response = self.app.post(
284 response = self.app.post(
285 route_path('edit_user_emails_add', user_id=user_id),
285 route_path('edit_user_emails_add', user_id=user_id),
286 params={'new_email': existing_email,
286 params={'new_email': existing_email,
287 'csrf_token': self.csrf_token})
287 'csrf_token': self.csrf_token})
288 assert_session_flash(
288 assert_session_flash(
289 response, 'This e-mail address is already taken')
289 response, 'This e-mail address is already taken')
290
290
291 response = self.app.get(
291 response = self.app.get(
292 route_path('edit_user_emails', user_id=user_id))
292 route_path('edit_user_emails', user_id=user_id))
293 response.mustcontain(no=[existing_email])
293 response.mustcontain(no=[existing_email])
294
294
295 def test_emails_delete(self, user_util):
295 def test_emails_delete(self, user_util):
296 self.log_user()
296 self.log_user()
297 user = user_util.create_user()
297 user = user_util.create_user()
298 user_id = user.user_id
298 user_id = user.user_id
299
299
300 self.app.post(
300 self.app.post(
301 route_path('edit_user_emails_add', user_id=user_id),
301 route_path('edit_user_emails_add', user_id=user_id),
302 params={'new_email': 'example@rhodecode.com',
302 params={'new_email': 'example@rhodecode.com',
303 'csrf_token': self.csrf_token})
303 'csrf_token': self.csrf_token})
304
304
305 response = self.app.get(
305 response = self.app.get(
306 route_path('edit_user_emails', user_id=user_id))
306 route_path('edit_user_emails', user_id=user_id))
307 response.mustcontain('example@rhodecode.com')
307 response.mustcontain('example@rhodecode.com')
308
308
309 user_email = UserEmailMap.query()\
309 user_email = UserEmailMap.query()\
310 .filter(UserEmailMap.email == 'example@rhodecode.com') \
310 .filter(UserEmailMap.email == 'example@rhodecode.com') \
311 .filter(UserEmailMap.user_id == user_id)\
311 .filter(UserEmailMap.user_id == user_id)\
312 .one()
312 .one()
313
313
314 del_email_id = user_email.email_id
314 del_email_id = user_email.email_id
315 self.app.post(
315 self.app.post(
316 route_path('edit_user_emails_delete', user_id=user_id),
316 route_path('edit_user_emails_delete', user_id=user_id),
317 params={'del_email_id': del_email_id,
317 params={'del_email_id': del_email_id,
318 'csrf_token': self.csrf_token})
318 'csrf_token': self.csrf_token})
319
319
320 response = self.app.get(
320 response = self.app.get(
321 route_path('edit_user_emails', user_id=user_id))
321 route_path('edit_user_emails', user_id=user_id))
322 response.mustcontain(no=['example@rhodecode.com'])
322 response.mustcontain(no=['example@rhodecode.com'])
323
323
324 def test_create(self, request, xhr_header):
324 def test_create(self, request, xhr_header):
325 self.log_user()
325 self.log_user()
326 username = 'newtestuser'
326 username = 'newtestuser'
327 password = 'test12'
327 password = 'test12'
328 password_confirmation = password
328 password_confirmation = password
329 name = 'name'
329 name = 'name'
330 lastname = 'lastname'
330 lastname = 'lastname'
331 email = 'mail@mail.com'
331 email = 'mail@mail.com'
332
332
333 self.app.get(route_path('users_new'))
333 self.app.get(route_path('users_new'))
334
334
335 response = self.app.post(route_path('users_create'), params={
335 response = self.app.post(route_path('users_create'), params={
336 'username': username,
336 'username': username,
337 'password': password,
337 'password': password,
338 'description': 'mr CTO',
338 'description': 'mr CTO',
339 'password_confirmation': password_confirmation,
339 'password_confirmation': password_confirmation,
340 'firstname': name,
340 'firstname': name,
341 'active': True,
341 'active': True,
342 'lastname': lastname,
342 'lastname': lastname,
343 'extern_name': 'rhodecode',
343 'extern_name': 'rhodecode',
344 'extern_type': 'rhodecode',
344 'extern_type': 'rhodecode',
345 'email': email,
345 'email': email,
346 'csrf_token': self.csrf_token,
346 'csrf_token': self.csrf_token,
347 })
347 })
348 user_link = h.link_to(
348 user_link = h.link_to(
349 username,
349 username,
350 route_path(
350 route_path(
351 'user_edit', user_id=User.get_by_username(username).user_id))
351 'user_edit', user_id=User.get_by_username(username).user_id))
352 assert_session_flash(response, 'Created user %s' % (user_link,))
352 assert_session_flash(response, 'Created user %s' % (user_link,))
353
353
354 @request.addfinalizer
354 @request.addfinalizer
355 def cleanup():
355 def cleanup():
356 fixture.destroy_user(username)
356 fixture.destroy_user(username)
357 Session().commit()
357 Session().commit()
358
358
359 new_user = User.query().filter(User.username == username).one()
359 new_user = User.query().filter(User.username == username).one()
360
360
361 assert new_user.username == username
361 assert new_user.username == username
362 assert auth.check_password(password, new_user.password)
362 assert auth.check_password(password, new_user.password)
363 assert new_user.name == name
363 assert new_user.name == name
364 assert new_user.lastname == lastname
364 assert new_user.lastname == lastname
365 assert new_user.email == email
365 assert new_user.email == email
366
366
367 response = self.app.get(route_path('users_data'),
367 response = self.app.get(route_path('users_data'),
368 extra_environ=xhr_header)
368 extra_environ=xhr_header)
369 response.mustcontain(username)
369 response.mustcontain(username)
370
370
371 def test_create_err(self):
371 def test_create_err(self):
372 self.log_user()
372 self.log_user()
373 username = 'new_user'
373 username = 'new_user'
374 password = ''
374 password = ''
375 name = 'name'
375 name = 'name'
376 lastname = 'lastname'
376 lastname = 'lastname'
377 email = 'errmail.com'
377 email = 'errmail.com'
378
378
379 self.app.get(route_path('users_new'))
379 self.app.get(route_path('users_new'))
380
380
381 response = self.app.post(route_path('users_create'), params={
381 response = self.app.post(route_path('users_create'), params={
382 'username': username,
382 'username': username,
383 'password': password,
383 'password': password,
384 'name': name,
384 'name': name,
385 'active': False,
385 'active': False,
386 'lastname': lastname,
386 'lastname': lastname,
387 'description': 'mr CTO',
387 'description': 'mr CTO',
388 'email': email,
388 'email': email,
389 'csrf_token': self.csrf_token,
389 'csrf_token': self.csrf_token,
390 })
390 })
391
391
392 msg = u'Username "%(username)s" is forbidden'
392 msg = u'Username "%(username)s" is forbidden'
393 msg = h.html_escape(msg % {'username': 'new_user'})
393 msg = h.html_escape(msg % {'username': 'new_user'})
394 response.mustcontain('<span class="error-message">%s</span>' % msg)
394 response.mustcontain('<span class="error-message">%s</span>' % msg)
395 response.mustcontain(
395 response.mustcontain(
396 '<span class="error-message">Please enter a value</span>')
396 '<span class="error-message">Please enter a value</span>')
397 response.mustcontain(
397 response.mustcontain(
398 '<span class="error-message">An email address must contain a'
398 '<span class="error-message">An email address must contain a'
399 ' single @</span>')
399 ' single @</span>')
400
400
401 def get_user():
401 def get_user():
402 Session().query(User).filter(User.username == username).one()
402 Session().query(User).filter(User.username == username).one()
403
403
404 with pytest.raises(NoResultFound):
404 with pytest.raises(NoResultFound):
405 get_user()
405 get_user()
406
406
407 def test_new(self):
407 def test_new(self):
408 self.log_user()
408 self.log_user()
409 self.app.get(route_path('users_new'))
409 self.app.get(route_path('users_new'))
410
410
411 @pytest.mark.parametrize("name, attrs", [
411 @pytest.mark.parametrize("name, attrs", [
412 ('firstname', {'firstname': 'new_username'}),
412 ('firstname', {'firstname': 'new_username'}),
413 ('lastname', {'lastname': 'new_username'}),
413 ('lastname', {'lastname': 'new_username'}),
414 ('admin', {'admin': True}),
414 ('admin', {'admin': True}),
415 ('admin', {'admin': False}),
415 ('admin', {'admin': False}),
416 ('extern_type', {'extern_type': 'ldap'}),
416 ('extern_type', {'extern_type': 'ldap'}),
417 ('extern_type', {'extern_type': None}),
417 ('extern_type', {'extern_type': None}),
418 ('extern_name', {'extern_name': 'test'}),
418 ('extern_name', {'extern_name': 'test'}),
419 ('extern_name', {'extern_name': None}),
419 ('extern_name', {'extern_name': None}),
420 ('active', {'active': False}),
420 ('active', {'active': False}),
421 ('active', {'active': True}),
421 ('active', {'active': True}),
422 ('email', {'email': 'some@email.com'}),
422 ('email', {'email': 'some@email.com'}),
423 ('language', {'language': 'de'}),
423 ('language', {'language': 'de'}),
424 ('language', {'language': 'en'}),
424 ('language', {'language': 'en'}),
425 ('description', {'description': 'hello CTO'}),
425 ('description', {'description': 'hello CTO'}),
426 # ('new_password', {'new_password': 'foobar123',
426 # ('new_password', {'new_password': 'foobar123',
427 # 'password_confirmation': 'foobar123'})
427 # 'password_confirmation': 'foobar123'})
428 ])
428 ])
429 def test_update(self, name, attrs, user_util):
429 def test_update(self, name, attrs, user_util):
430 self.log_user()
430 self.log_user()
431 usr = user_util.create_user(
431 usr = user_util.create_user(
432 password='qweqwe',
432 password='qweqwe',
433 email='testme@rhodecode.org',
433 email='testme@rhodecode.org',
434 extern_type='rhodecode',
434 extern_type='rhodecode',
435 extern_name='xxx',
435 extern_name='xxx',
436 )
436 )
437 user_id = usr.user_id
437 user_id = usr.user_id
438 Session().commit()
438 Session().commit()
439
439
440 params = usr.get_api_data()
440 params = usr.get_api_data()
441 cur_lang = params['language'] or 'en'
441 cur_lang = params['language'] or 'en'
442 params.update({
442 params.update({
443 'password_confirmation': '',
443 'password_confirmation': '',
444 'new_password': '',
444 'new_password': '',
445 'language': cur_lang,
445 'language': cur_lang,
446 'csrf_token': self.csrf_token,
446 'csrf_token': self.csrf_token,
447 })
447 })
448 params.update({'new_password': ''})
448 params.update({'new_password': ''})
449 params.update(attrs)
449 params.update(attrs)
450 if name == 'email':
450 if name == 'email':
451 params['emails'] = [attrs['email']]
451 params['emails'] = [attrs['email']]
452 elif name == 'extern_type':
452 elif name == 'extern_type':
453 # cannot update this via form, expected value is original one
453 # cannot update this via form, expected value is original one
454 params['extern_type'] = "rhodecode"
454 params['extern_type'] = "rhodecode"
455 elif name == 'extern_name':
455 elif name == 'extern_name':
456 # cannot update this via form, expected value is original one
456 # cannot update this via form, expected value is original one
457 params['extern_name'] = 'xxx'
457 params['extern_name'] = 'xxx'
458 # special case since this user is not
458 # special case since this user is not
459 # logged in yet his data is not filled
459 # logged in yet his data is not filled
460 # so we use creation data
460 # so we use creation data
461
461
462 response = self.app.post(
462 response = self.app.post(
463 route_path('user_update', user_id=usr.user_id), params)
463 route_path('user_update', user_id=usr.user_id), params)
464 assert response.status_int == 302
464 assert response.status_int == 302
465 assert_session_flash(response, 'User updated successfully')
465 assert_session_flash(response, 'User updated successfully')
466
466
467 updated_user = User.get(user_id)
467 updated_user = User.get(user_id)
468 updated_params = updated_user.get_api_data()
468 updated_params = updated_user.get_api_data()
469 updated_params.update({'password_confirmation': ''})
469 updated_params.update({'password_confirmation': ''})
470 updated_params.update({'new_password': ''})
470 updated_params.update({'new_password': ''})
471
471
472 del params['csrf_token']
472 del params['csrf_token']
473 assert params == updated_params
473 assert params == updated_params
474
474
475 def test_update_and_migrate_password(
475 def test_update_and_migrate_password(
476 self, autologin_user, real_crypto_backend, user_util):
476 self, autologin_user, real_crypto_backend, user_util):
477
477
478 user = user_util.create_user()
478 user = user_util.create_user()
479 temp_user = user.username
479 temp_user = user.username
480 user.password = auth._RhodeCodeCryptoSha256().hash_create(
480 user.password = auth._RhodeCodeCryptoSha256().hash_create(
481 b'test123')
481 b'test123')
482 Session().add(user)
482 Session().add(user)
483 Session().commit()
483 Session().commit()
484
484
485 params = user.get_api_data()
485 params = user.get_api_data()
486
486
487 params.update({
487 params.update({
488 'password_confirmation': 'qweqwe123',
488 'password_confirmation': 'qweqwe123',
489 'new_password': 'qweqwe123',
489 'new_password': 'qweqwe123',
490 'language': 'en',
490 'language': 'en',
491 'csrf_token': autologin_user.csrf_token,
491 'csrf_token': autologin_user.csrf_token,
492 })
492 })
493
493
494 response = self.app.post(
494 response = self.app.post(
495 route_path('user_update', user_id=user.user_id), params)
495 route_path('user_update', user_id=user.user_id), params)
496 assert response.status_int == 302
496 assert response.status_int == 302
497 assert_session_flash(response, 'User updated successfully')
497 assert_session_flash(response, 'User updated successfully')
498
498
499 # new password should be bcrypted, after log-in and transfer
499 # new password should be bcrypted, after log-in and transfer
500 user = User.get_by_username(temp_user)
500 user = User.get_by_username(temp_user)
501 assert user.password.startswith('$')
501 assert user.password.startswith('$')
502
502
503 updated_user = User.get_by_username(temp_user)
503 updated_user = User.get_by_username(temp_user)
504 updated_params = updated_user.get_api_data()
504 updated_params = updated_user.get_api_data()
505 updated_params.update({'password_confirmation': 'qweqwe123'})
505 updated_params.update({'password_confirmation': 'qweqwe123'})
506 updated_params.update({'new_password': 'qweqwe123'})
506 updated_params.update({'new_password': 'qweqwe123'})
507
507
508 del params['csrf_token']
508 del params['csrf_token']
509 assert params == updated_params
509 assert params == updated_params
510
510
511 def test_delete(self):
511 def test_delete(self):
512 self.log_user()
512 self.log_user()
513 username = 'newtestuserdeleteme'
513 username = 'newtestuserdeleteme'
514
514
515 fixture.create_user(name=username)
515 fixture.create_user(name=username)
516
516
517 new_user = Session().query(User)\
517 new_user = Session().query(User)\
518 .filter(User.username == username).one()
518 .filter(User.username == username).one()
519 response = self.app.post(
519 response = self.app.post(
520 route_path('user_delete', user_id=new_user.user_id),
520 route_path('user_delete', user_id=new_user.user_id),
521 params={'csrf_token': self.csrf_token})
521 params={'csrf_token': self.csrf_token})
522
522
523 assert_session_flash(response, 'Successfully deleted user `{}`'.format(username))
523 assert_session_flash(response, 'Successfully deleted user `{}`'.format(username))
524
524
525 def test_delete_owner_of_repository(self, request, user_util):
525 def test_delete_owner_of_repository(self, request, user_util):
526 self.log_user()
526 self.log_user()
527 obj_name = 'test_repo'
527 obj_name = 'test_repo'
528 usr = user_util.create_user()
528 usr = user_util.create_user()
529 username = usr.username
529 username = usr.username
530 fixture.create_repo(obj_name, cur_user=usr.username)
530 fixture.create_repo(obj_name, cur_user=usr.username)
531
531
532 new_user = Session().query(User)\
532 new_user = Session().query(User)\
533 .filter(User.username == username).one()
533 .filter(User.username == username).one()
534 response = self.app.post(
534 response = self.app.post(
535 route_path('user_delete', user_id=new_user.user_id),
535 route_path('user_delete', user_id=new_user.user_id),
536 params={'csrf_token': self.csrf_token})
536 params={'csrf_token': self.csrf_token})
537
537
538 msg = 'user "%s" still owns 1 repositories and cannot be removed. ' \
538 msg = 'user "%s" still owns 1 repositories and cannot be removed. ' \
539 'Switch owners or remove those repositories:%s' % (username, obj_name)
539 'Switch owners or remove those repositories:%s' % (username, obj_name)
540 assert_session_flash(response, msg)
540 assert_session_flash(response, msg)
541 fixture.destroy_repo(obj_name)
541 fixture.destroy_repo(obj_name)
542
542
543 def test_delete_owner_of_repository_detaching(self, request, user_util):
543 def test_delete_owner_of_repository_detaching(self, request, user_util):
544 self.log_user()
544 self.log_user()
545 obj_name = 'test_repo'
545 obj_name = 'test_repo'
546 usr = user_util.create_user(auto_cleanup=False)
546 usr = user_util.create_user(auto_cleanup=False)
547 username = usr.username
547 username = usr.username
548 fixture.create_repo(obj_name, cur_user=usr.username)
548 fixture.create_repo(obj_name, cur_user=usr.username)
549 Session().commit()
549
550
550 new_user = Session().query(User)\
551 new_user = Session().query(User)\
551 .filter(User.username == username).one()
552 .filter(User.username == username).one()
552 response = self.app.post(
553 response = self.app.post(
553 route_path('user_delete', user_id=new_user.user_id),
554 route_path('user_delete', user_id=new_user.user_id),
554 params={'user_repos': 'detach', 'csrf_token': self.csrf_token})
555 params={'user_repos': 'detach', 'csrf_token': self.csrf_token})
555
556
556 msg = 'Detached 1 repositories'
557 msg = 'Detached 1 repositories'
557 assert_session_flash(response, msg)
558 assert_session_flash(response, msg)
558 fixture.destroy_repo(obj_name)
559 fixture.destroy_repo(obj_name)
559
560
560 def test_delete_owner_of_repository_deleting(self, request, user_util):
561 def test_delete_owner_of_repository_deleting(self, request, user_util):
561 self.log_user()
562 self.log_user()
562 obj_name = 'test_repo'
563 obj_name = 'test_repo'
563 usr = user_util.create_user(auto_cleanup=False)
564 usr = user_util.create_user(auto_cleanup=False)
564 username = usr.username
565 username = usr.username
565 fixture.create_repo(obj_name, cur_user=usr.username)
566 fixture.create_repo(obj_name, cur_user=usr.username)
566
567
567 new_user = Session().query(User)\
568 new_user = Session().query(User)\
568 .filter(User.username == username).one()
569 .filter(User.username == username).one()
569 response = self.app.post(
570 response = self.app.post(
570 route_path('user_delete', user_id=new_user.user_id),
571 route_path('user_delete', user_id=new_user.user_id),
571 params={'user_repos': 'delete', 'csrf_token': self.csrf_token})
572 params={'user_repos': 'delete', 'csrf_token': self.csrf_token})
572
573
573 msg = 'Deleted 1 repositories'
574 msg = 'Deleted 1 repositories'
574 assert_session_flash(response, msg)
575 assert_session_flash(response, msg)
575
576
576 def test_delete_owner_of_repository_group(self, request, user_util):
577 def test_delete_owner_of_repository_group(self, request, user_util):
577 self.log_user()
578 self.log_user()
578 obj_name = 'test_group'
579 obj_name = 'test_group'
579 usr = user_util.create_user()
580 usr = user_util.create_user()
580 username = usr.username
581 username = usr.username
581 fixture.create_repo_group(obj_name, cur_user=usr.username)
582 fixture.create_repo_group(obj_name, cur_user=usr.username)
582
583
583 new_user = Session().query(User)\
584 new_user = Session().query(User)\
584 .filter(User.username == username).one()
585 .filter(User.username == username).one()
585 response = self.app.post(
586 response = self.app.post(
586 route_path('user_delete', user_id=new_user.user_id),
587 route_path('user_delete', user_id=new_user.user_id),
587 params={'csrf_token': self.csrf_token})
588 params={'csrf_token': self.csrf_token})
588
589
589 msg = 'user "%s" still owns 1 repository groups and cannot be removed. ' \
590 msg = 'user "%s" still owns 1 repository groups and cannot be removed. ' \
590 'Switch owners or remove those repository groups:%s' % (username, obj_name)
591 'Switch owners or remove those repository groups:%s' % (username, obj_name)
591 assert_session_flash(response, msg)
592 assert_session_flash(response, msg)
592 fixture.destroy_repo_group(obj_name)
593 fixture.destroy_repo_group(obj_name)
593
594
594 def test_delete_owner_of_repository_group_detaching(self, request, user_util):
595 def test_delete_owner_of_repository_group_detaching(self, request, user_util):
595 self.log_user()
596 self.log_user()
596 obj_name = 'test_group'
597 obj_name = 'test_group'
597 usr = user_util.create_user(auto_cleanup=False)
598 usr = user_util.create_user(auto_cleanup=False)
598 username = usr.username
599 username = usr.username
599 fixture.create_repo_group(obj_name, cur_user=usr.username)
600 fixture.create_repo_group(obj_name, cur_user=usr.username)
600
601
601 new_user = Session().query(User)\
602 new_user = Session().query(User)\
602 .filter(User.username == username).one()
603 .filter(User.username == username).one()
603 response = self.app.post(
604 response = self.app.post(
604 route_path('user_delete', user_id=new_user.user_id),
605 route_path('user_delete', user_id=new_user.user_id),
605 params={'user_repo_groups': 'delete', 'csrf_token': self.csrf_token})
606 params={'user_repo_groups': 'delete', 'csrf_token': self.csrf_token})
606
607
607 msg = 'Deleted 1 repository groups'
608 msg = 'Deleted 1 repository groups'
608 assert_session_flash(response, msg)
609 assert_session_flash(response, msg)
609
610
610 def test_delete_owner_of_repository_group_deleting(self, request, user_util):
611 def test_delete_owner_of_repository_group_deleting(self, request, user_util):
611 self.log_user()
612 self.log_user()
612 obj_name = 'test_group'
613 obj_name = 'test_group'
613 usr = user_util.create_user(auto_cleanup=False)
614 usr = user_util.create_user(auto_cleanup=False)
614 username = usr.username
615 username = usr.username
615 fixture.create_repo_group(obj_name, cur_user=usr.username)
616 fixture.create_repo_group(obj_name, cur_user=usr.username)
616
617
617 new_user = Session().query(User)\
618 new_user = Session().query(User)\
618 .filter(User.username == username).one()
619 .filter(User.username == username).one()
619 response = self.app.post(
620 response = self.app.post(
620 route_path('user_delete', user_id=new_user.user_id),
621 route_path('user_delete', user_id=new_user.user_id),
621 params={'user_repo_groups': 'detach', 'csrf_token': self.csrf_token})
622 params={'user_repo_groups': 'detach', 'csrf_token': self.csrf_token})
622
623
623 msg = 'Detached 1 repository groups'
624 msg = 'Detached 1 repository groups'
624 assert_session_flash(response, msg)
625 assert_session_flash(response, msg)
625 fixture.destroy_repo_group(obj_name)
626 fixture.destroy_repo_group(obj_name)
626
627
627 def test_delete_owner_of_user_group(self, request, user_util):
628 def test_delete_owner_of_user_group(self, request, user_util):
628 self.log_user()
629 self.log_user()
629 obj_name = 'test_user_group'
630 obj_name = 'test_user_group'
630 usr = user_util.create_user()
631 usr = user_util.create_user()
631 username = usr.username
632 username = usr.username
632 fixture.create_user_group(obj_name, cur_user=usr.username)
633 fixture.create_user_group(obj_name, cur_user=usr.username)
633
634
634 new_user = Session().query(User)\
635 new_user = Session().query(User)\
635 .filter(User.username == username).one()
636 .filter(User.username == username).one()
636 response = self.app.post(
637 response = self.app.post(
637 route_path('user_delete', user_id=new_user.user_id),
638 route_path('user_delete', user_id=new_user.user_id),
638 params={'csrf_token': self.csrf_token})
639 params={'csrf_token': self.csrf_token})
639
640
640 msg = 'user "%s" still owns 1 user groups and cannot be removed. ' \
641 msg = 'user "%s" still owns 1 user groups and cannot be removed. ' \
641 'Switch owners or remove those user groups:%s' % (username, obj_name)
642 'Switch owners or remove those user groups:%s' % (username, obj_name)
642 assert_session_flash(response, msg)
643 assert_session_flash(response, msg)
643 fixture.destroy_user_group(obj_name)
644 fixture.destroy_user_group(obj_name)
644
645
645 def test_delete_owner_of_user_group_detaching(self, request, user_util):
646 def test_delete_owner_of_user_group_detaching(self, request, user_util):
646 self.log_user()
647 self.log_user()
647 obj_name = 'test_user_group'
648 obj_name = 'test_user_group'
648 usr = user_util.create_user(auto_cleanup=False)
649 usr = user_util.create_user(auto_cleanup=False)
649 username = usr.username
650 username = usr.username
650 fixture.create_user_group(obj_name, cur_user=usr.username)
651 fixture.create_user_group(obj_name, cur_user=usr.username)
651
652
652 new_user = Session().query(User)\
653 new_user = Session().query(User)\
653 .filter(User.username == username).one()
654 .filter(User.username == username).one()
654 try:
655 try:
655 response = self.app.post(
656 response = self.app.post(
656 route_path('user_delete', user_id=new_user.user_id),
657 route_path('user_delete', user_id=new_user.user_id),
657 params={'user_user_groups': 'detach',
658 params={'user_user_groups': 'detach',
658 'csrf_token': self.csrf_token})
659 'csrf_token': self.csrf_token})
659
660
660 msg = 'Detached 1 user groups'
661 msg = 'Detached 1 user groups'
661 assert_session_flash(response, msg)
662 assert_session_flash(response, msg)
662 finally:
663 finally:
663 fixture.destroy_user_group(obj_name)
664 fixture.destroy_user_group(obj_name)
664
665
665 def test_delete_owner_of_user_group_deleting(self, request, user_util):
666 def test_delete_owner_of_user_group_deleting(self, request, user_util):
666 self.log_user()
667 self.log_user()
667 obj_name = 'test_user_group'
668 obj_name = 'test_user_group'
668 usr = user_util.create_user(auto_cleanup=False)
669 usr = user_util.create_user(auto_cleanup=False)
669 username = usr.username
670 username = usr.username
670 fixture.create_user_group(obj_name, cur_user=usr.username)
671 fixture.create_user_group(obj_name, cur_user=usr.username)
671
672
672 new_user = Session().query(User)\
673 new_user = Session().query(User)\
673 .filter(User.username == username).one()
674 .filter(User.username == username).one()
674 response = self.app.post(
675 response = self.app.post(
675 route_path('user_delete', user_id=new_user.user_id),
676 route_path('user_delete', user_id=new_user.user_id),
676 params={'user_user_groups': 'delete', 'csrf_token': self.csrf_token})
677 params={'user_user_groups': 'delete', 'csrf_token': self.csrf_token})
677
678
678 msg = 'Deleted 1 user groups'
679 msg = 'Deleted 1 user groups'
679 assert_session_flash(response, msg)
680 assert_session_flash(response, msg)
680
681
681 def test_edit(self, user_util):
682 def test_edit(self, user_util):
682 self.log_user()
683 self.log_user()
683 user = user_util.create_user()
684 user = user_util.create_user()
684 self.app.get(route_path('user_edit', user_id=user.user_id))
685 self.app.get(route_path('user_edit', user_id=user.user_id))
685
686
686 def test_edit_default_user_redirect(self):
687 def test_edit_default_user_redirect(self):
687 self.log_user()
688 self.log_user()
688 user = User.get_default_user()
689 user = User.get_default_user()
689 self.app.get(route_path('user_edit', user_id=user.user_id), status=302)
690 self.app.get(route_path('user_edit', user_id=user.user_id), status=302)
690
691
691 @pytest.mark.parametrize(
692 @pytest.mark.parametrize(
692 'repo_create, repo_create_write, user_group_create, repo_group_create,'
693 'repo_create, repo_create_write, user_group_create, repo_group_create,'
693 'fork_create, inherit_default_permissions, expect_error,'
694 'fork_create, inherit_default_permissions, expect_error,'
694 'expect_form_error', [
695 'expect_form_error', [
695 ('hg.create.none', 'hg.create.write_on_repogroup.false',
696 ('hg.create.none', 'hg.create.write_on_repogroup.false',
696 'hg.usergroup.create.false', 'hg.repogroup.create.false',
697 'hg.usergroup.create.false', 'hg.repogroup.create.false',
697 'hg.fork.none', 'hg.inherit_default_perms.false', False, False),
698 'hg.fork.none', 'hg.inherit_default_perms.false', False, False),
698 ('hg.create.repository', 'hg.create.write_on_repogroup.false',
699 ('hg.create.repository', 'hg.create.write_on_repogroup.false',
699 'hg.usergroup.create.false', 'hg.repogroup.create.false',
700 'hg.usergroup.create.false', 'hg.repogroup.create.false',
700 'hg.fork.none', 'hg.inherit_default_perms.false', False, False),
701 'hg.fork.none', 'hg.inherit_default_perms.false', False, False),
701 ('hg.create.repository', 'hg.create.write_on_repogroup.true',
702 ('hg.create.repository', 'hg.create.write_on_repogroup.true',
702 'hg.usergroup.create.true', 'hg.repogroup.create.true',
703 'hg.usergroup.create.true', 'hg.repogroup.create.true',
703 'hg.fork.repository', 'hg.inherit_default_perms.false', False,
704 'hg.fork.repository', 'hg.inherit_default_perms.false', False,
704 False),
705 False),
705 ('hg.create.XXX', 'hg.create.write_on_repogroup.true',
706 ('hg.create.XXX', 'hg.create.write_on_repogroup.true',
706 'hg.usergroup.create.true', 'hg.repogroup.create.true',
707 'hg.usergroup.create.true', 'hg.repogroup.create.true',
707 'hg.fork.repository', 'hg.inherit_default_perms.false', False,
708 'hg.fork.repository', 'hg.inherit_default_perms.false', False,
708 True),
709 True),
709 ('', '', '', '', '', '', True, False),
710 ('', '', '', '', '', '', True, False),
710 ])
711 ])
711 def test_global_perms_on_user(
712 def test_global_perms_on_user(
712 self, repo_create, repo_create_write, user_group_create,
713 self, repo_create, repo_create_write, user_group_create,
713 repo_group_create, fork_create, expect_error, expect_form_error,
714 repo_group_create, fork_create, expect_error, expect_form_error,
714 inherit_default_permissions, user_util):
715 inherit_default_permissions, user_util):
715 self.log_user()
716 self.log_user()
716 user = user_util.create_user()
717 user = user_util.create_user()
717 uid = user.user_id
718 uid = user.user_id
718
719
719 # ENABLE REPO CREATE ON A GROUP
720 # ENABLE REPO CREATE ON A GROUP
720 perm_params = {
721 perm_params = {
721 'inherit_default_permissions': False,
722 'inherit_default_permissions': False,
722 'default_repo_create': repo_create,
723 'default_repo_create': repo_create,
723 'default_repo_create_on_write': repo_create_write,
724 'default_repo_create_on_write': repo_create_write,
724 'default_user_group_create': user_group_create,
725 'default_user_group_create': user_group_create,
725 'default_repo_group_create': repo_group_create,
726 'default_repo_group_create': repo_group_create,
726 'default_fork_create': fork_create,
727 'default_fork_create': fork_create,
727 'default_inherit_default_permissions': inherit_default_permissions,
728 'default_inherit_default_permissions': inherit_default_permissions,
728 'csrf_token': self.csrf_token,
729 'csrf_token': self.csrf_token,
729 }
730 }
730 response = self.app.post(
731 response = self.app.post(
731 route_path('user_edit_global_perms_update', user_id=uid),
732 route_path('user_edit_global_perms_update', user_id=uid),
732 params=perm_params)
733 params=perm_params)
733
734
734 if expect_form_error:
735 if expect_form_error:
735 assert response.status_int == 200
736 assert response.status_int == 200
736 response.mustcontain('Value must be one of')
737 response.mustcontain('Value must be one of')
737 else:
738 else:
738 if expect_error:
739 if expect_error:
739 msg = 'An error occurred during permissions saving'
740 msg = 'An error occurred during permissions saving'
740 else:
741 else:
741 msg = 'User global permissions updated successfully'
742 msg = 'User global permissions updated successfully'
742 ug = User.get(uid)
743 ug = User.get(uid)
743 del perm_params['inherit_default_permissions']
744 del perm_params['inherit_default_permissions']
744 del perm_params['csrf_token']
745 del perm_params['csrf_token']
745 assert perm_params == ug.get_default_perms()
746 assert perm_params == ug.get_default_perms()
746 assert_session_flash(response, msg)
747 assert_session_flash(response, msg)
747
748
748 def test_global_permissions_initial_values(self, user_util):
749 def test_global_permissions_initial_values(self, user_util):
749 self.log_user()
750 self.log_user()
750 user = user_util.create_user()
751 user = user_util.create_user()
751 uid = user.user_id
752 uid = user.user_id
752 response = self.app.get(
753 response = self.app.get(
753 route_path('user_edit_global_perms', user_id=uid))
754 route_path('user_edit_global_perms', user_id=uid))
754 default_user = User.get_default_user()
755 default_user = User.get_default_user()
755 default_permissions = default_user.get_default_perms()
756 default_permissions = default_user.get_default_perms()
756 assert_response = response.assert_response()
757 assert_response = response.assert_response()
757 expected_permissions = (
758 expected_permissions = (
758 'default_repo_create', 'default_repo_create_on_write',
759 'default_repo_create', 'default_repo_create_on_write',
759 'default_fork_create', 'default_repo_group_create',
760 'default_fork_create', 'default_repo_group_create',
760 'default_user_group_create', 'default_inherit_default_permissions')
761 'default_user_group_create', 'default_inherit_default_permissions')
761 for permission in expected_permissions:
762 for permission in expected_permissions:
762 css_selector = '[name={}][checked=checked]'.format(permission)
763 css_selector = '[name={}][checked=checked]'.format(permission)
763 element = assert_response.get_element(css_selector)
764 element = assert_response.get_element(css_selector)
764 assert element.value == default_permissions[permission]
765 assert element.value == default_permissions[permission]
765
766
766 def test_perms_summary_page(self):
767 def test_perms_summary_page(self):
767 user = self.log_user()
768 user = self.log_user()
768 response = self.app.get(
769 response = self.app.get(
769 route_path('edit_user_perms_summary', user_id=user['user_id']))
770 route_path('edit_user_perms_summary', user_id=user['user_id']))
770 for repo in Repository.query().all():
771 for repo in Repository.query().all():
771 response.mustcontain(repo.repo_name)
772 response.mustcontain(repo.repo_name)
772
773
773 def test_perms_summary_page_json(self):
774 def test_perms_summary_page_json(self):
774 user = self.log_user()
775 user = self.log_user()
775 response = self.app.get(
776 response = self.app.get(
776 route_path('edit_user_perms_summary_json', user_id=user['user_id']))
777 route_path('edit_user_perms_summary_json', user_id=user['user_id']))
777 for repo in Repository.query().all():
778 for repo in Repository.query().all():
778 response.mustcontain(repo.repo_name)
779 response.mustcontain(repo.repo_name)
779
780
780 def test_audit_log_page(self):
781 def test_audit_log_page(self):
781 user = self.log_user()
782 user = self.log_user()
782 self.app.get(
783 self.app.get(
783 route_path('edit_user_audit_logs', user_id=user['user_id']))
784 route_path('edit_user_audit_logs', user_id=user['user_id']))
784
785
785 def test_audit_log_page_download(self):
786 def test_audit_log_page_download(self):
786 user = self.log_user()
787 user = self.log_user()
787 user_id = user['user_id']
788 user_id = user['user_id']
788 response = self.app.get(
789 response = self.app.get(
789 route_path('edit_user_audit_logs_download', user_id=user_id))
790 route_path('edit_user_audit_logs_download', user_id=user_id))
790
791
791 assert response.content_disposition == \
792 assert response.content_disposition == \
792 'attachment; filename=user_{}_audit_logs.json'.format(user_id)
793 'attachment; filename=user_{}_audit_logs.json'.format(user_id)
793 assert response.content_type == "application/json"
794 assert response.content_type == "application/json"
@@ -1,127 +1,179 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 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 import pytest
22 import pytest
23
23
24 import rhodecode
24 import rhodecode
25 from rhodecode.model.db import Repository
25 from rhodecode.model.db import Repository, RepoGroup, User
26 from rhodecode.model.meta import Session
26 from rhodecode.model.meta import Session
27 from rhodecode.model.repo import RepoModel
27 from rhodecode.model.repo import RepoModel
28 from rhodecode.model.repo_group import RepoGroupModel
28 from rhodecode.model.repo_group import RepoGroupModel
29 from rhodecode.model.settings import SettingsModel
29 from rhodecode.model.settings import SettingsModel
30 from rhodecode.tests import TestController
30 from rhodecode.tests import TestController
31 from rhodecode.tests.fixture import Fixture
31 from rhodecode.tests.fixture import Fixture
32 from rhodecode.lib import helpers as h
32 from rhodecode.lib import helpers as h
33
33
34 fixture = Fixture()
34 fixture = Fixture()
35
35
36
36
37 def route_path(name, **kwargs):
37 def route_path(name, **kwargs):
38 return {
38 return {
39 'home': '/',
39 'home': '/',
40 'main_page_repos_data': '/_home_repos',
41 'main_page_repo_groups_data': '/_home_repo_groups',
40 'repo_group_home': '/{repo_group_name}'
42 'repo_group_home': '/{repo_group_name}'
41 }[name].format(**kwargs)
43 }[name].format(**kwargs)
42
44
43
45
44 class TestHomeController(TestController):
46 class TestHomeController(TestController):
45
47
46 def test_index(self):
48 def test_index(self):
47 self.log_user()
49 self.log_user()
48 response = self.app.get(route_path('home'))
50 response = self.app.get(route_path('home'))
49 # if global permission is set
51 # if global permission is set
50 response.mustcontain('New Repository')
52 response.mustcontain('New Repository')
51
53
54 def test_index_grid_repos(self, xhr_header):
55 self.log_user()
56 response = self.app.get(route_path('main_page_repos_data'), extra_environ=xhr_header)
52 # search for objects inside the JavaScript JSON
57 # search for objects inside the JavaScript JSON
53 for repo in Repository.getAll():
58 for obj in Repository.getAll():
54 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
59 response.mustcontain('<a href=\\"/{}\\">'.format(obj.repo_name))
60
61 def test_index_grid_repo_groups(self, xhr_header):
62 self.log_user()
63 response = self.app.get(route_path('main_page_repo_groups_data'),
64 extra_environ=xhr_header,)
65
66 # search for objects inside the JavaScript JSON
67 for obj in RepoGroup.getAll():
68 response.mustcontain('<a href=\\"/{}\\">'.format(obj.group_name))
69
70 def test_index_grid_repo_groups_without_access(self, xhr_header, user_util):
71 user = user_util.create_user(password='qweqwe')
72 group_ok = user_util.create_repo_group(owner=user)
73 group_id_ok = group_ok.group_id
74
75 group_forbidden = user_util.create_repo_group(owner=User.get_first_super_admin())
76 group_id_forbidden = group_forbidden.group_id
77
78 user_util.grant_user_permission_to_repo_group(group_forbidden, user, 'group.none')
79 self.log_user(user.username, 'qweqwe')
80
81 self.app.get(route_path('main_page_repo_groups_data'),
82 extra_environ=xhr_header,
83 params={'repo_group_id': group_id_ok}, status=200)
84
85 self.app.get(route_path('main_page_repo_groups_data'),
86 extra_environ=xhr_header,
87 params={'repo_group_id': group_id_forbidden}, status=404)
55
88
56 def test_index_contains_statics_with_ver(self):
89 def test_index_contains_statics_with_ver(self):
57 from rhodecode.lib.base import calculate_version_hash
90 from rhodecode.lib.base import calculate_version_hash
58
91
59 self.log_user()
92 self.log_user()
60 response = self.app.get(route_path('home'))
93 response = self.app.get(route_path('home'))
61
94
62 rhodecode_version_hash = calculate_version_hash(
95 rhodecode_version_hash = calculate_version_hash(
63 {'beaker.session.secret': 'test-rc-uytcxaz'})
96 {'beaker.session.secret': 'test-rc-uytcxaz'})
64 response.mustcontain('style.css?ver={0}'.format(rhodecode_version_hash))
97 response.mustcontain('style.css?ver={0}'.format(rhodecode_version_hash))
65 response.mustcontain('scripts.min.js?ver={0}'.format(rhodecode_version_hash))
98 response.mustcontain('scripts.min.js?ver={0}'.format(rhodecode_version_hash))
66
99
67 def test_index_contains_backend_specific_details(self, backend):
100 def test_index_contains_backend_specific_details(self, backend, xhr_header):
68 self.log_user()
101 self.log_user()
69 response = self.app.get(route_path('home'))
102 response = self.app.get(route_path('main_page_repos_data'), extra_environ=xhr_header)
70 tip = backend.repo.get_commit().raw_id
103 tip = backend.repo.get_commit().raw_id
71
104
72 # html in javascript variable:
105 # html in javascript variable:
73 response.mustcontain(r'<i class=\"icon-%s\"' % (backend.alias, ))
106 response.mustcontain(r'<i class=\"icon-%s\"' % (backend.alias, ))
74 response.mustcontain(r'href=\"/%s\"' % (backend.repo_name, ))
107 response.mustcontain(r'href=\"/%s\"' % (backend.repo_name, ))
75
108
76 response.mustcontain("""/%s/changeset/%s""" % (backend.repo_name, tip))
109 response.mustcontain("""/%s/changeset/%s""" % (backend.repo_name, tip))
77 response.mustcontain("""Added a symlink""")
110 response.mustcontain("""Added a symlink""")
78
111
79 def test_index_with_anonymous_access_disabled(self):
112 def test_index_with_anonymous_access_disabled(self):
80 with fixture.anon_access(False):
113 with fixture.anon_access(False):
81 response = self.app.get(route_path('home'), status=302)
114 response = self.app.get(route_path('home'), status=302)
82 assert 'login' in response.location
115 assert 'login' in response.location
83
116
84 def test_index_page_on_groups(self, autologin_user, repo_group):
117 def test_index_page_on_groups_with_wrong_group_id(self, autologin_user, xhr_header):
85 response = self.app.get(route_path('repo_group_home', repo_group_name='gr1'))
118 group_id = 918123
86 response.mustcontain("gr1/repo_in_group")
119 self.app.get(
120 route_path('main_page_repo_groups_data'),
121 params={'repo_group_id': group_id},
122 status=404, extra_environ=xhr_header)
87
123
88 def test_index_page_on_group_with_trailing_slash(
124 def test_index_page_on_groups(self, autologin_user, user_util, xhr_header):
89 self, autologin_user, repo_group):
125 gr = user_util.create_repo_group()
90 response = self.app.get(route_path('repo_group_home', repo_group_name='gr1') + '/')
126 repo = user_util.create_repo(parent=gr)
91 response.mustcontain("gr1/repo_in_group")
127 repo_name = repo.repo_name
128 group_id = gr.group_id
129
130 response = self.app.get(route_path(
131 'repo_group_home', repo_group_name=gr.group_name))
132 response.mustcontain('d.repo_group_id = {}'.format(group_id))
92
133
93 @pytest.fixture(scope='class')
134 response = self.app.get(
94 def repo_group(self, request):
135 route_path('main_page_repos_data'),
95 gr = fixture.create_repo_group('gr1')
136 params={'repo_group_id': group_id},
96 fixture.create_repo(name='gr1/repo_in_group', repo_group=gr)
137 extra_environ=xhr_header,)
138 response.mustcontain(repo_name)
97
139
98 @request.addfinalizer
140 def test_index_page_on_group_with_trailing_slash(self, autologin_user, user_util, xhr_header):
99 def cleanup():
141 gr = user_util.create_repo_group()
100 RepoModel().delete('gr1/repo_in_group')
142 repo = user_util.create_repo(parent=gr)
101 RepoGroupModel().delete(repo_group='gr1', force_delete=True)
143 repo_name = repo.repo_name
102 Session().commit()
144 group_id = gr.group_id
145
146 response = self.app.get(route_path(
147 'repo_group_home', repo_group_name=gr.group_name+'/'))
148 response.mustcontain('d.repo_group_id = {}'.format(group_id))
149
150 response = self.app.get(
151 route_path('main_page_repos_data'),
152 params={'repo_group_id': group_id},
153 extra_environ=xhr_header, )
154 response.mustcontain(repo_name)
103
155
104 @pytest.mark.parametrize("name, state", [
156 @pytest.mark.parametrize("name, state", [
105 ('Disabled', False),
157 ('Disabled', False),
106 ('Enabled', True),
158 ('Enabled', True),
107 ])
159 ])
108 def test_index_show_version(self, autologin_user, name, state):
160 def test_index_show_version(self, autologin_user, name, state):
109 version_string = 'RhodeCode Enterprise %s' % rhodecode.__version__
161 version_string = 'RhodeCode Enterprise %s' % rhodecode.__version__
110
162
111 sett = SettingsModel().create_or_update_setting(
163 sett = SettingsModel().create_or_update_setting(
112 'show_version', state, 'bool')
164 'show_version', state, 'bool')
113 Session().add(sett)
165 Session().add(sett)
114 Session().commit()
166 Session().commit()
115 SettingsModel().invalidate_settings_cache()
167 SettingsModel().invalidate_settings_cache()
116
168
117 response = self.app.get(route_path('home'))
169 response = self.app.get(route_path('home'))
118 if state is True:
170 if state is True:
119 response.mustcontain(version_string)
171 response.mustcontain(version_string)
120 if state is False:
172 if state is False:
121 response.mustcontain(no=[version_string])
173 response.mustcontain(no=[version_string])
122
174
123 def test_logout_form_contains_csrf(self, autologin_user, csrf_token):
175 def test_logout_form_contains_csrf(self, autologin_user, csrf_token):
124 response = self.app.get(route_path('home'))
176 response = self.app.get(route_path('home'))
125 assert_response = response.assert_response()
177 assert_response = response.assert_response()
126 element = assert_response.get_element('.logout [name=csrf_token]')
178 element = assert_response.get_element('.logout [name=csrf_token]')
127 assert element.value == csrf_token
179 assert element.value == csrf_token
@@ -1,823 +1,822 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2019 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 collections
23 import collections
24
24
25 from pyramid.httpexceptions import HTTPNotFound
25 from pyramid.httpexceptions import HTTPNotFound
26 from pyramid.view import view_config
26 from pyramid.view import view_config
27
27
28 from rhodecode.apps._base import BaseAppView, DataGridAppView
28 from rhodecode.apps._base import BaseAppView, DataGridAppView
29 from rhodecode.lib import helpers as h
29 from rhodecode.lib import helpers as h
30 from rhodecode.lib.auth import (
30 from rhodecode.lib.auth import (
31 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator, CSRFRequired,
31 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator, CSRFRequired,
32 HasRepoGroupPermissionAny)
32 HasRepoGroupPermissionAny, AuthUser)
33 from rhodecode.lib.codeblocks import filenode_as_lines_tokens
33 from rhodecode.lib.codeblocks import filenode_as_lines_tokens
34 from rhodecode.lib.index import searcher_from_config
34 from rhodecode.lib.index import searcher_from_config
35 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
35 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
36 from rhodecode.lib.vcs.nodes import FileNode
36 from rhodecode.lib.vcs.nodes import FileNode
37 from rhodecode.model.db import (
37 from rhodecode.model.db import (
38 func, true, or_, case, in_filter_generator, Session,
38 func, true, or_, case, in_filter_generator, Session,
39 Repository, RepoGroup, User, UserGroup)
39 Repository, RepoGroup, User, UserGroup)
40 from rhodecode.model.repo import RepoModel
40 from rhodecode.model.repo import RepoModel
41 from rhodecode.model.repo_group import RepoGroupModel
41 from rhodecode.model.repo_group import RepoGroupModel
42 from rhodecode.model.user import UserModel
42 from rhodecode.model.user import UserModel
43 from rhodecode.model.user_group import UserGroupModel
43 from rhodecode.model.user_group import UserGroupModel
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47
47
48 class HomeView(BaseAppView, DataGridAppView):
48 class HomeView(BaseAppView, DataGridAppView):
49
49
50 def load_default_context(self):
50 def load_default_context(self):
51 c = self._get_local_tmpl_context()
51 c = self._get_local_tmpl_context()
52 c.user = c.auth_user.get_instance()
52 c.user = c.auth_user.get_instance()
53
53
54 return c
54 return c
55
55
56 @LoginRequired()
56 @LoginRequired()
57 @view_config(
57 @view_config(
58 route_name='user_autocomplete_data', request_method='GET',
58 route_name='user_autocomplete_data', request_method='GET',
59 renderer='json_ext', xhr=True)
59 renderer='json_ext', xhr=True)
60 def user_autocomplete_data(self):
60 def user_autocomplete_data(self):
61 self.load_default_context()
61 self.load_default_context()
62 query = self.request.GET.get('query')
62 query = self.request.GET.get('query')
63 active = str2bool(self.request.GET.get('active') or True)
63 active = str2bool(self.request.GET.get('active') or True)
64 include_groups = str2bool(self.request.GET.get('user_groups'))
64 include_groups = str2bool(self.request.GET.get('user_groups'))
65 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
65 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
66 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
66 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
67
67
68 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
68 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
69 query, active, include_groups)
69 query, active, include_groups)
70
70
71 _users = UserModel().get_users(
71 _users = UserModel().get_users(
72 name_contains=query, only_active=active)
72 name_contains=query, only_active=active)
73
73
74 def maybe_skip_default_user(usr):
74 def maybe_skip_default_user(usr):
75 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
75 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
76 return False
76 return False
77 return True
77 return True
78 _users = filter(maybe_skip_default_user, _users)
78 _users = filter(maybe_skip_default_user, _users)
79
79
80 if include_groups:
80 if include_groups:
81 # extend with user groups
81 # extend with user groups
82 _user_groups = UserGroupModel().get_user_groups(
82 _user_groups = UserGroupModel().get_user_groups(
83 name_contains=query, only_active=active,
83 name_contains=query, only_active=active,
84 expand_groups=expand_groups)
84 expand_groups=expand_groups)
85 _users = _users + _user_groups
85 _users = _users + _user_groups
86
86
87 return {'suggestions': _users}
87 return {'suggestions': _users}
88
88
89 @LoginRequired()
89 @LoginRequired()
90 @NotAnonymous()
90 @NotAnonymous()
91 @view_config(
91 @view_config(
92 route_name='user_group_autocomplete_data', request_method='GET',
92 route_name='user_group_autocomplete_data', request_method='GET',
93 renderer='json_ext', xhr=True)
93 renderer='json_ext', xhr=True)
94 def user_group_autocomplete_data(self):
94 def user_group_autocomplete_data(self):
95 self.load_default_context()
95 self.load_default_context()
96 query = self.request.GET.get('query')
96 query = self.request.GET.get('query')
97 active = str2bool(self.request.GET.get('active') or True)
97 active = str2bool(self.request.GET.get('active') or True)
98 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
98 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
99
99
100 log.debug('generating user group list, query:%s, active:%s',
100 log.debug('generating user group list, query:%s, active:%s',
101 query, active)
101 query, active)
102
102
103 _user_groups = UserGroupModel().get_user_groups(
103 _user_groups = UserGroupModel().get_user_groups(
104 name_contains=query, only_active=active,
104 name_contains=query, only_active=active,
105 expand_groups=expand_groups)
105 expand_groups=expand_groups)
106 _user_groups = _user_groups
106 _user_groups = _user_groups
107
107
108 return {'suggestions': _user_groups}
108 return {'suggestions': _user_groups}
109
109
110 def _get_repo_list(self, name_contains=None, repo_type=None, repo_group_name='', limit=20):
110 def _get_repo_list(self, name_contains=None, repo_type=None, repo_group_name='', limit=20):
111 org_query = name_contains
111 org_query = name_contains
112 allowed_ids = self._rhodecode_user.repo_acl_ids(
112 allowed_ids = self._rhodecode_user.repo_acl_ids(
113 ['repository.read', 'repository.write', 'repository.admin'],
113 ['repository.read', 'repository.write', 'repository.admin'],
114 cache=False, name_filter=name_contains) or [-1]
114 cache=False, name_filter=name_contains) or [-1]
115
115
116 query = Repository.query()\
116 query = Repository.query()\
117 .filter(Repository.archived.isnot(true()))\
117 .filter(Repository.archived.isnot(true()))\
118 .filter(or_(
118 .filter(or_(
119 # generate multiple IN to fix limitation problems
119 # generate multiple IN to fix limitation problems
120 *in_filter_generator(Repository.repo_id, allowed_ids)
120 *in_filter_generator(Repository.repo_id, allowed_ids)
121 ))
121 ))
122
122
123 query = query.order_by(case(
123 query = query.order_by(case(
124 [
124 [
125 (Repository.repo_name.startswith(repo_group_name), repo_group_name+'/'),
125 (Repository.repo_name.startswith(repo_group_name), repo_group_name+'/'),
126 ],
126 ],
127 ))
127 ))
128 query = query.order_by(func.length(Repository.repo_name))
128 query = query.order_by(func.length(Repository.repo_name))
129 query = query.order_by(Repository.repo_name)
129 query = query.order_by(Repository.repo_name)
130
130
131 if repo_type:
131 if repo_type:
132 query = query.filter(Repository.repo_type == repo_type)
132 query = query.filter(Repository.repo_type == repo_type)
133
133
134 if name_contains:
134 if name_contains:
135 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
135 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
136 query = query.filter(
136 query = query.filter(
137 Repository.repo_name.ilike(ilike_expression))
137 Repository.repo_name.ilike(ilike_expression))
138 query = query.limit(limit)
138 query = query.limit(limit)
139
139
140 acl_iter = query
140 acl_iter = query
141
141
142 return [
142 return [
143 {
143 {
144 'id': obj.repo_name,
144 'id': obj.repo_name,
145 'value': org_query,
145 'value': org_query,
146 'value_display': obj.repo_name,
146 'value_display': obj.repo_name,
147 'text': obj.repo_name,
147 'text': obj.repo_name,
148 'type': 'repo',
148 'type': 'repo',
149 'repo_id': obj.repo_id,
149 'repo_id': obj.repo_id,
150 'repo_type': obj.repo_type,
150 'repo_type': obj.repo_type,
151 'private': obj.private,
151 'private': obj.private,
152 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
152 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
153 }
153 }
154 for obj in acl_iter]
154 for obj in acl_iter]
155
155
156 def _get_repo_group_list(self, name_contains=None, repo_group_name='', limit=20):
156 def _get_repo_group_list(self, name_contains=None, repo_group_name='', limit=20):
157 org_query = name_contains
157 org_query = name_contains
158 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
158 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
159 ['group.read', 'group.write', 'group.admin'],
159 ['group.read', 'group.write', 'group.admin'],
160 cache=False, name_filter=name_contains) or [-1]
160 cache=False, name_filter=name_contains) or [-1]
161
161
162 query = RepoGroup.query()\
162 query = RepoGroup.query()\
163 .filter(or_(
163 .filter(or_(
164 # generate multiple IN to fix limitation problems
164 # generate multiple IN to fix limitation problems
165 *in_filter_generator(RepoGroup.group_id, allowed_ids)
165 *in_filter_generator(RepoGroup.group_id, allowed_ids)
166 ))
166 ))
167
167
168 query = query.order_by(case(
168 query = query.order_by(case(
169 [
169 [
170 (RepoGroup.group_name.startswith(repo_group_name), repo_group_name+'/'),
170 (RepoGroup.group_name.startswith(repo_group_name), repo_group_name+'/'),
171 ],
171 ],
172 ))
172 ))
173 query = query.order_by(func.length(RepoGroup.group_name))
173 query = query.order_by(func.length(RepoGroup.group_name))
174 query = query.order_by(RepoGroup.group_name)
174 query = query.order_by(RepoGroup.group_name)
175
175
176 if name_contains:
176 if name_contains:
177 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
177 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
178 query = query.filter(
178 query = query.filter(
179 RepoGroup.group_name.ilike(ilike_expression))
179 RepoGroup.group_name.ilike(ilike_expression))
180 query = query.limit(limit)
180 query = query.limit(limit)
181
181
182 acl_iter = query
182 acl_iter = query
183
183
184 return [
184 return [
185 {
185 {
186 'id': obj.group_name,
186 'id': obj.group_name,
187 'value': org_query,
187 'value': org_query,
188 'value_display': obj.group_name,
188 'value_display': obj.group_name,
189 'text': obj.group_name,
189 'text': obj.group_name,
190 'type': 'repo_group',
190 'type': 'repo_group',
191 'repo_group_id': obj.group_id,
191 'repo_group_id': obj.group_id,
192 'url': h.route_path(
192 'url': h.route_path(
193 'repo_group_home', repo_group_name=obj.group_name)
193 'repo_group_home', repo_group_name=obj.group_name)
194 }
194 }
195 for obj in acl_iter]
195 for obj in acl_iter]
196
196
197 def _get_user_list(self, name_contains=None, limit=20):
197 def _get_user_list(self, name_contains=None, limit=20):
198 org_query = name_contains
198 org_query = name_contains
199 if not name_contains:
199 if not name_contains:
200 return [], False
200 return [], False
201
201
202 # TODO(marcink): should all logged in users be allowed to search others?
202 # TODO(marcink): should all logged in users be allowed to search others?
203 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
203 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
204 if not allowed_user_search:
204 if not allowed_user_search:
205 return [], False
205 return [], False
206
206
207 name_contains = re.compile('(?:user:[ ]?)(.+)').findall(name_contains)
207 name_contains = re.compile('(?:user:[ ]?)(.+)').findall(name_contains)
208 if len(name_contains) != 1:
208 if len(name_contains) != 1:
209 return [], False
209 return [], False
210
210
211 name_contains = name_contains[0]
211 name_contains = name_contains[0]
212
212
213 query = User.query()\
213 query = User.query()\
214 .order_by(func.length(User.username))\
214 .order_by(func.length(User.username))\
215 .order_by(User.username) \
215 .order_by(User.username) \
216 .filter(User.username != User.DEFAULT_USER)
216 .filter(User.username != User.DEFAULT_USER)
217
217
218 if name_contains:
218 if name_contains:
219 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
219 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
220 query = query.filter(
220 query = query.filter(
221 User.username.ilike(ilike_expression))
221 User.username.ilike(ilike_expression))
222 query = query.limit(limit)
222 query = query.limit(limit)
223
223
224 acl_iter = query
224 acl_iter = query
225
225
226 return [
226 return [
227 {
227 {
228 'id': obj.user_id,
228 'id': obj.user_id,
229 'value': org_query,
229 'value': org_query,
230 'value_display': 'user: `{}`'.format(obj.username),
230 'value_display': 'user: `{}`'.format(obj.username),
231 'type': 'user',
231 'type': 'user',
232 'icon_link': h.gravatar_url(obj.email, 30),
232 'icon_link': h.gravatar_url(obj.email, 30),
233 'url': h.route_path(
233 'url': h.route_path(
234 'user_profile', username=obj.username)
234 'user_profile', username=obj.username)
235 }
235 }
236 for obj in acl_iter], True
236 for obj in acl_iter], True
237
237
238 def _get_user_groups_list(self, name_contains=None, limit=20):
238 def _get_user_groups_list(self, name_contains=None, limit=20):
239 org_query = name_contains
239 org_query = name_contains
240 if not name_contains:
240 if not name_contains:
241 return [], False
241 return [], False
242
242
243 # TODO(marcink): should all logged in users be allowed to search others?
243 # TODO(marcink): should all logged in users be allowed to search others?
244 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
244 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
245 if not allowed_user_search:
245 if not allowed_user_search:
246 return [], False
246 return [], False
247
247
248 name_contains = re.compile('(?:user_group:[ ]?)(.+)').findall(name_contains)
248 name_contains = re.compile('(?:user_group:[ ]?)(.+)').findall(name_contains)
249 if len(name_contains) != 1:
249 if len(name_contains) != 1:
250 return [], False
250 return [], False
251
251
252 name_contains = name_contains[0]
252 name_contains = name_contains[0]
253
253
254 query = UserGroup.query()\
254 query = UserGroup.query()\
255 .order_by(func.length(UserGroup.users_group_name))\
255 .order_by(func.length(UserGroup.users_group_name))\
256 .order_by(UserGroup.users_group_name)
256 .order_by(UserGroup.users_group_name)
257
257
258 if name_contains:
258 if name_contains:
259 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
259 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
260 query = query.filter(
260 query = query.filter(
261 UserGroup.users_group_name.ilike(ilike_expression))
261 UserGroup.users_group_name.ilike(ilike_expression))
262 query = query.limit(limit)
262 query = query.limit(limit)
263
263
264 acl_iter = query
264 acl_iter = query
265
265
266 return [
266 return [
267 {
267 {
268 'id': obj.users_group_id,
268 'id': obj.users_group_id,
269 'value': org_query,
269 'value': org_query,
270 'value_display': 'user_group: `{}`'.format(obj.users_group_name),
270 'value_display': 'user_group: `{}`'.format(obj.users_group_name),
271 'type': 'user_group',
271 'type': 'user_group',
272 'url': h.route_path(
272 'url': h.route_path(
273 'user_group_profile', user_group_name=obj.users_group_name)
273 'user_group_profile', user_group_name=obj.users_group_name)
274 }
274 }
275 for obj in acl_iter], True
275 for obj in acl_iter], True
276
276
277 def _get_hash_commit_list(self, auth_user, searcher, query, repo=None, repo_group=None):
277 def _get_hash_commit_list(self, auth_user, searcher, query, repo=None, repo_group=None):
278 repo_name = repo_group_name = None
278 repo_name = repo_group_name = None
279 if repo:
279 if repo:
280 repo_name = repo.repo_name
280 repo_name = repo.repo_name
281 if repo_group:
281 if repo_group:
282 repo_group_name = repo_group.group_name
282 repo_group_name = repo_group.group_name
283
283
284 org_query = query
284 org_query = query
285 if not query or len(query) < 3 or not searcher:
285 if not query or len(query) < 3 or not searcher:
286 return [], False
286 return [], False
287
287
288 commit_hashes = re.compile('(?:commit:[ ]?)([0-9a-f]{2,40})').findall(query)
288 commit_hashes = re.compile('(?:commit:[ ]?)([0-9a-f]{2,40})').findall(query)
289
289
290 if len(commit_hashes) != 1:
290 if len(commit_hashes) != 1:
291 return [], False
291 return [], False
292
292
293 commit_hash = commit_hashes[0]
293 commit_hash = commit_hashes[0]
294
294
295 result = searcher.search(
295 result = searcher.search(
296 'commit_id:{}*'.format(commit_hash), 'commit', auth_user,
296 'commit_id:{}*'.format(commit_hash), 'commit', auth_user,
297 repo_name, repo_group_name, raise_on_exc=False)
297 repo_name, repo_group_name, raise_on_exc=False)
298
298
299 commits = []
299 commits = []
300 for entry in result['results']:
300 for entry in result['results']:
301 repo_data = {
301 repo_data = {
302 'repository_id': entry.get('repository_id'),
302 'repository_id': entry.get('repository_id'),
303 'repository_type': entry.get('repo_type'),
303 'repository_type': entry.get('repo_type'),
304 'repository_name': entry.get('repository'),
304 'repository_name': entry.get('repository'),
305 }
305 }
306
306
307 commit_entry = {
307 commit_entry = {
308 'id': entry['commit_id'],
308 'id': entry['commit_id'],
309 'value': org_query,
309 'value': org_query,
310 'value_display': '`{}` commit: {}'.format(
310 'value_display': '`{}` commit: {}'.format(
311 entry['repository'], entry['commit_id']),
311 entry['repository'], entry['commit_id']),
312 'type': 'commit',
312 'type': 'commit',
313 'repo': entry['repository'],
313 'repo': entry['repository'],
314 'repo_data': repo_data,
314 'repo_data': repo_data,
315
315
316 'url': h.route_path(
316 'url': h.route_path(
317 'repo_commit',
317 'repo_commit',
318 repo_name=entry['repository'], commit_id=entry['commit_id'])
318 repo_name=entry['repository'], commit_id=entry['commit_id'])
319 }
319 }
320
320
321 commits.append(commit_entry)
321 commits.append(commit_entry)
322 return commits, True
322 return commits, True
323
323
324 def _get_path_list(self, auth_user, searcher, query, repo=None, repo_group=None):
324 def _get_path_list(self, auth_user, searcher, query, repo=None, repo_group=None):
325 repo_name = repo_group_name = None
325 repo_name = repo_group_name = None
326 if repo:
326 if repo:
327 repo_name = repo.repo_name
327 repo_name = repo.repo_name
328 if repo_group:
328 if repo_group:
329 repo_group_name = repo_group.group_name
329 repo_group_name = repo_group.group_name
330
330
331 org_query = query
331 org_query = query
332 if not query or len(query) < 3 or not searcher:
332 if not query or len(query) < 3 or not searcher:
333 return [], False
333 return [], False
334
334
335 paths_re = re.compile('(?:file:[ ]?)(.+)').findall(query)
335 paths_re = re.compile('(?:file:[ ]?)(.+)').findall(query)
336 if len(paths_re) != 1:
336 if len(paths_re) != 1:
337 return [], False
337 return [], False
338
338
339 file_path = paths_re[0]
339 file_path = paths_re[0]
340
340
341 search_path = searcher.escape_specials(file_path)
341 search_path = searcher.escape_specials(file_path)
342 result = searcher.search(
342 result = searcher.search(
343 'file.raw:*{}*'.format(search_path), 'path', auth_user,
343 'file.raw:*{}*'.format(search_path), 'path', auth_user,
344 repo_name, repo_group_name, raise_on_exc=False)
344 repo_name, repo_group_name, raise_on_exc=False)
345
345
346 files = []
346 files = []
347 for entry in result['results']:
347 for entry in result['results']:
348 repo_data = {
348 repo_data = {
349 'repository_id': entry.get('repository_id'),
349 'repository_id': entry.get('repository_id'),
350 'repository_type': entry.get('repo_type'),
350 'repository_type': entry.get('repo_type'),
351 'repository_name': entry.get('repository'),
351 'repository_name': entry.get('repository'),
352 }
352 }
353
353
354 file_entry = {
354 file_entry = {
355 'id': entry['commit_id'],
355 'id': entry['commit_id'],
356 'value': org_query,
356 'value': org_query,
357 'value_display': '`{}` file: {}'.format(
357 'value_display': '`{}` file: {}'.format(
358 entry['repository'], entry['file']),
358 entry['repository'], entry['file']),
359 'type': 'file',
359 'type': 'file',
360 'repo': entry['repository'],
360 'repo': entry['repository'],
361 'repo_data': repo_data,
361 'repo_data': repo_data,
362
362
363 'url': h.route_path(
363 'url': h.route_path(
364 'repo_files',
364 'repo_files',
365 repo_name=entry['repository'], commit_id=entry['commit_id'],
365 repo_name=entry['repository'], commit_id=entry['commit_id'],
366 f_path=entry['file'])
366 f_path=entry['file'])
367 }
367 }
368
368
369 files.append(file_entry)
369 files.append(file_entry)
370 return files, True
370 return files, True
371
371
372 @LoginRequired()
372 @LoginRequired()
373 @view_config(
373 @view_config(
374 route_name='repo_list_data', request_method='GET',
374 route_name='repo_list_data', request_method='GET',
375 renderer='json_ext', xhr=True)
375 renderer='json_ext', xhr=True)
376 def repo_list_data(self):
376 def repo_list_data(self):
377 _ = self.request.translate
377 _ = self.request.translate
378 self.load_default_context()
378 self.load_default_context()
379
379
380 query = self.request.GET.get('query')
380 query = self.request.GET.get('query')
381 repo_type = self.request.GET.get('repo_type')
381 repo_type = self.request.GET.get('repo_type')
382 log.debug('generating repo list, query:%s, repo_type:%s',
382 log.debug('generating repo list, query:%s, repo_type:%s',
383 query, repo_type)
383 query, repo_type)
384
384
385 res = []
385 res = []
386 repos = self._get_repo_list(query, repo_type=repo_type)
386 repos = self._get_repo_list(query, repo_type=repo_type)
387 if repos:
387 if repos:
388 res.append({
388 res.append({
389 'text': _('Repositories'),
389 'text': _('Repositories'),
390 'children': repos
390 'children': repos
391 })
391 })
392
392
393 data = {
393 data = {
394 'more': False,
394 'more': False,
395 'results': res
395 'results': res
396 }
396 }
397 return data
397 return data
398
398
399 @LoginRequired()
399 @LoginRequired()
400 @view_config(
400 @view_config(
401 route_name='repo_group_list_data', request_method='GET',
401 route_name='repo_group_list_data', request_method='GET',
402 renderer='json_ext', xhr=True)
402 renderer='json_ext', xhr=True)
403 def repo_group_list_data(self):
403 def repo_group_list_data(self):
404 _ = self.request.translate
404 _ = self.request.translate
405 self.load_default_context()
405 self.load_default_context()
406
406
407 query = self.request.GET.get('query')
407 query = self.request.GET.get('query')
408
408
409 log.debug('generating repo group list, query:%s',
409 log.debug('generating repo group list, query:%s',
410 query)
410 query)
411
411
412 res = []
412 res = []
413 repo_groups = self._get_repo_group_list(query)
413 repo_groups = self._get_repo_group_list(query)
414 if repo_groups:
414 if repo_groups:
415 res.append({
415 res.append({
416 'text': _('Repository Groups'),
416 'text': _('Repository Groups'),
417 'children': repo_groups
417 'children': repo_groups
418 })
418 })
419
419
420 data = {
420 data = {
421 'more': False,
421 'more': False,
422 'results': res
422 'results': res
423 }
423 }
424 return data
424 return data
425
425
426 def _get_default_search_queries(self, search_context, searcher, query):
426 def _get_default_search_queries(self, search_context, searcher, query):
427 if not searcher:
427 if not searcher:
428 return []
428 return []
429
429
430 is_es_6 = searcher.is_es_6
430 is_es_6 = searcher.is_es_6
431
431
432 queries = []
432 queries = []
433 repo_group_name, repo_name, repo_context = None, None, None
433 repo_group_name, repo_name, repo_context = None, None, None
434
434
435 # repo group context
435 # repo group context
436 if search_context.get('search_context[repo_group_name]'):
436 if search_context.get('search_context[repo_group_name]'):
437 repo_group_name = search_context.get('search_context[repo_group_name]')
437 repo_group_name = search_context.get('search_context[repo_group_name]')
438 if search_context.get('search_context[repo_name]'):
438 if search_context.get('search_context[repo_name]'):
439 repo_name = search_context.get('search_context[repo_name]')
439 repo_name = search_context.get('search_context[repo_name]')
440 repo_context = search_context.get('search_context[repo_view_type]')
440 repo_context = search_context.get('search_context[repo_view_type]')
441
441
442 if is_es_6 and repo_name:
442 if is_es_6 and repo_name:
443 # files
443 # files
444 def query_modifier():
444 def query_modifier():
445 qry = query
445 qry = query
446 return {'q': qry, 'type': 'content'}
446 return {'q': qry, 'type': 'content'}
447
447
448 label = u'File search for `{}`'.format(h.escape(query))
448 label = u'File search for `{}`'.format(h.escape(query))
449 file_qry = {
449 file_qry = {
450 'id': -10,
450 'id': -10,
451 'value': query,
451 'value': query,
452 'value_display': label,
452 'value_display': label,
453 'value_icon': '<i class="icon-code"></i>',
453 'value_icon': '<i class="icon-code"></i>',
454 'type': 'search',
454 'type': 'search',
455 'subtype': 'repo',
455 'subtype': 'repo',
456 'url': h.route_path('search_repo',
456 'url': h.route_path('search_repo',
457 repo_name=repo_name,
457 repo_name=repo_name,
458 _query=query_modifier())
458 _query=query_modifier())
459 }
459 }
460
460
461 # commits
461 # commits
462 def query_modifier():
462 def query_modifier():
463 qry = query
463 qry = query
464 return {'q': qry, 'type': 'commit'}
464 return {'q': qry, 'type': 'commit'}
465
465
466 label = u'Commit search for `{}`'.format(h.escape(query))
466 label = u'Commit search for `{}`'.format(h.escape(query))
467 commit_qry = {
467 commit_qry = {
468 'id': -20,
468 'id': -20,
469 'value': query,
469 'value': query,
470 'value_display': label,
470 'value_display': label,
471 'value_icon': '<i class="icon-history"></i>',
471 'value_icon': '<i class="icon-history"></i>',
472 'type': 'search',
472 'type': 'search',
473 'subtype': 'repo',
473 'subtype': 'repo',
474 'url': h.route_path('search_repo',
474 'url': h.route_path('search_repo',
475 repo_name=repo_name,
475 repo_name=repo_name,
476 _query=query_modifier())
476 _query=query_modifier())
477 }
477 }
478
478
479 if repo_context in ['commit', 'commits']:
479 if repo_context in ['commit', 'commits']:
480 queries.extend([commit_qry, file_qry])
480 queries.extend([commit_qry, file_qry])
481 elif repo_context in ['files', 'summary']:
481 elif repo_context in ['files', 'summary']:
482 queries.extend([file_qry, commit_qry])
482 queries.extend([file_qry, commit_qry])
483 else:
483 else:
484 queries.extend([commit_qry, file_qry])
484 queries.extend([commit_qry, file_qry])
485
485
486 elif is_es_6 and repo_group_name:
486 elif is_es_6 and repo_group_name:
487 # files
487 # files
488 def query_modifier():
488 def query_modifier():
489 qry = query
489 qry = query
490 return {'q': qry, 'type': 'content'}
490 return {'q': qry, 'type': 'content'}
491
491
492 label = u'File search for `{}`'.format(query)
492 label = u'File search for `{}`'.format(query)
493 file_qry = {
493 file_qry = {
494 'id': -30,
494 'id': -30,
495 'value': query,
495 'value': query,
496 'value_display': label,
496 'value_display': label,
497 'value_icon': '<i class="icon-code"></i>',
497 'value_icon': '<i class="icon-code"></i>',
498 'type': 'search',
498 'type': 'search',
499 'subtype': 'repo_group',
499 'subtype': 'repo_group',
500 'url': h.route_path('search_repo_group',
500 'url': h.route_path('search_repo_group',
501 repo_group_name=repo_group_name,
501 repo_group_name=repo_group_name,
502 _query=query_modifier())
502 _query=query_modifier())
503 }
503 }
504
504
505 # commits
505 # commits
506 def query_modifier():
506 def query_modifier():
507 qry = query
507 qry = query
508 return {'q': qry, 'type': 'commit'}
508 return {'q': qry, 'type': 'commit'}
509
509
510 label = u'Commit search for `{}`'.format(query)
510 label = u'Commit search for `{}`'.format(query)
511 commit_qry = {
511 commit_qry = {
512 'id': -40,
512 'id': -40,
513 'value': query,
513 'value': query,
514 'value_display': label,
514 'value_display': label,
515 'value_icon': '<i class="icon-history"></i>',
515 'value_icon': '<i class="icon-history"></i>',
516 'type': 'search',
516 'type': 'search',
517 'subtype': 'repo_group',
517 'subtype': 'repo_group',
518 'url': h.route_path('search_repo_group',
518 'url': h.route_path('search_repo_group',
519 repo_group_name=repo_group_name,
519 repo_group_name=repo_group_name,
520 _query=query_modifier())
520 _query=query_modifier())
521 }
521 }
522
522
523 if repo_context in ['commit', 'commits']:
523 if repo_context in ['commit', 'commits']:
524 queries.extend([commit_qry, file_qry])
524 queries.extend([commit_qry, file_qry])
525 elif repo_context in ['files', 'summary']:
525 elif repo_context in ['files', 'summary']:
526 queries.extend([file_qry, commit_qry])
526 queries.extend([file_qry, commit_qry])
527 else:
527 else:
528 queries.extend([commit_qry, file_qry])
528 queries.extend([commit_qry, file_qry])
529
529
530 # Global, not scoped
530 # Global, not scoped
531 if not queries:
531 if not queries:
532 queries.append(
532 queries.append(
533 {
533 {
534 'id': -1,
534 'id': -1,
535 'value': query,
535 'value': query,
536 'value_display': u'File search for: `{}`'.format(query),
536 'value_display': u'File search for: `{}`'.format(query),
537 'value_icon': '<i class="icon-code"></i>',
537 'value_icon': '<i class="icon-code"></i>',
538 'type': 'search',
538 'type': 'search',
539 'subtype': 'global',
539 'subtype': 'global',
540 'url': h.route_path('search',
540 'url': h.route_path('search',
541 _query={'q': query, 'type': 'content'})
541 _query={'q': query, 'type': 'content'})
542 })
542 })
543 queries.append(
543 queries.append(
544 {
544 {
545 'id': -2,
545 'id': -2,
546 'value': query,
546 'value': query,
547 'value_display': u'Commit search for: `{}`'.format(query),
547 'value_display': u'Commit search for: `{}`'.format(query),
548 'value_icon': '<i class="icon-history"></i>',
548 'value_icon': '<i class="icon-history"></i>',
549 'type': 'search',
549 'type': 'search',
550 'subtype': 'global',
550 'subtype': 'global',
551 'url': h.route_path('search',
551 'url': h.route_path('search',
552 _query={'q': query, 'type': 'commit'})
552 _query={'q': query, 'type': 'commit'})
553 })
553 })
554
554
555 return queries
555 return queries
556
556
557 @LoginRequired()
557 @LoginRequired()
558 @view_config(
558 @view_config(
559 route_name='goto_switcher_data', request_method='GET',
559 route_name='goto_switcher_data', request_method='GET',
560 renderer='json_ext', xhr=True)
560 renderer='json_ext', xhr=True)
561 def goto_switcher_data(self):
561 def goto_switcher_data(self):
562 c = self.load_default_context()
562 c = self.load_default_context()
563
563
564 _ = self.request.translate
564 _ = self.request.translate
565
565
566 query = self.request.GET.get('query')
566 query = self.request.GET.get('query')
567 log.debug('generating main filter data, query %s', query)
567 log.debug('generating main filter data, query %s', query)
568
568
569 res = []
569 res = []
570 if not query:
570 if not query:
571 return {'suggestions': res}
571 return {'suggestions': res}
572
572
573 def no_match(name):
573 def no_match(name):
574 return {
574 return {
575 'id': -1,
575 'id': -1,
576 'value': "",
576 'value': "",
577 'value_display': name,
577 'value_display': name,
578 'type': 'text',
578 'type': 'text',
579 'url': ""
579 'url': ""
580 }
580 }
581 searcher = searcher_from_config(self.request.registry.settings)
581 searcher = searcher_from_config(self.request.registry.settings)
582 has_specialized_search = False
582 has_specialized_search = False
583
583
584 # set repo context
584 # set repo context
585 repo = None
585 repo = None
586 repo_id = safe_int(self.request.GET.get('search_context[repo_id]'))
586 repo_id = safe_int(self.request.GET.get('search_context[repo_id]'))
587 if repo_id:
587 if repo_id:
588 repo = Repository.get(repo_id)
588 repo = Repository.get(repo_id)
589
589
590 # set group context
590 # set group context
591 repo_group = None
591 repo_group = None
592 repo_group_id = safe_int(self.request.GET.get('search_context[repo_group_id]'))
592 repo_group_id = safe_int(self.request.GET.get('search_context[repo_group_id]'))
593 if repo_group_id:
593 if repo_group_id:
594 repo_group = RepoGroup.get(repo_group_id)
594 repo_group = RepoGroup.get(repo_group_id)
595 prefix_match = False
595 prefix_match = False
596
596
597 # user: type search
597 # user: type search
598 if not prefix_match:
598 if not prefix_match:
599 users, prefix_match = self._get_user_list(query)
599 users, prefix_match = self._get_user_list(query)
600 if users:
600 if users:
601 has_specialized_search = True
601 has_specialized_search = True
602 for serialized_user in users:
602 for serialized_user in users:
603 res.append(serialized_user)
603 res.append(serialized_user)
604 elif prefix_match:
604 elif prefix_match:
605 has_specialized_search = True
605 has_specialized_search = True
606 res.append(no_match('No matching users found'))
606 res.append(no_match('No matching users found'))
607
607
608 # user_group: type search
608 # user_group: type search
609 if not prefix_match:
609 if not prefix_match:
610 user_groups, prefix_match = self._get_user_groups_list(query)
610 user_groups, prefix_match = self._get_user_groups_list(query)
611 if user_groups:
611 if user_groups:
612 has_specialized_search = True
612 has_specialized_search = True
613 for serialized_user_group in user_groups:
613 for serialized_user_group in user_groups:
614 res.append(serialized_user_group)
614 res.append(serialized_user_group)
615 elif prefix_match:
615 elif prefix_match:
616 has_specialized_search = True
616 has_specialized_search = True
617 res.append(no_match('No matching user groups found'))
617 res.append(no_match('No matching user groups found'))
618
618
619 # FTS commit: type search
619 # FTS commit: type search
620 if not prefix_match:
620 if not prefix_match:
621 commits, prefix_match = self._get_hash_commit_list(
621 commits, prefix_match = self._get_hash_commit_list(
622 c.auth_user, searcher, query, repo, repo_group)
622 c.auth_user, searcher, query, repo, repo_group)
623 if commits:
623 if commits:
624 has_specialized_search = True
624 has_specialized_search = True
625 unique_repos = collections.OrderedDict()
625 unique_repos = collections.OrderedDict()
626 for commit in commits:
626 for commit in commits:
627 repo_name = commit['repo']
627 repo_name = commit['repo']
628 unique_repos.setdefault(repo_name, []).append(commit)
628 unique_repos.setdefault(repo_name, []).append(commit)
629
629
630 for _repo, commits in unique_repos.items():
630 for _repo, commits in unique_repos.items():
631 for commit in commits:
631 for commit in commits:
632 res.append(commit)
632 res.append(commit)
633 elif prefix_match:
633 elif prefix_match:
634 has_specialized_search = True
634 has_specialized_search = True
635 res.append(no_match('No matching commits found'))
635 res.append(no_match('No matching commits found'))
636
636
637 # FTS file: type search
637 # FTS file: type search
638 if not prefix_match:
638 if not prefix_match:
639 paths, prefix_match = self._get_path_list(
639 paths, prefix_match = self._get_path_list(
640 c.auth_user, searcher, query, repo, repo_group)
640 c.auth_user, searcher, query, repo, repo_group)
641 if paths:
641 if paths:
642 has_specialized_search = True
642 has_specialized_search = True
643 unique_repos = collections.OrderedDict()
643 unique_repos = collections.OrderedDict()
644 for path in paths:
644 for path in paths:
645 repo_name = path['repo']
645 repo_name = path['repo']
646 unique_repos.setdefault(repo_name, []).append(path)
646 unique_repos.setdefault(repo_name, []).append(path)
647
647
648 for repo, paths in unique_repos.items():
648 for repo, paths in unique_repos.items():
649 for path in paths:
649 for path in paths:
650 res.append(path)
650 res.append(path)
651 elif prefix_match:
651 elif prefix_match:
652 has_specialized_search = True
652 has_specialized_search = True
653 res.append(no_match('No matching files found'))
653 res.append(no_match('No matching files found'))
654
654
655 # main suggestions
655 # main suggestions
656 if not has_specialized_search:
656 if not has_specialized_search:
657 repo_group_name = ''
657 repo_group_name = ''
658 if repo_group:
658 if repo_group:
659 repo_group_name = repo_group.group_name
659 repo_group_name = repo_group.group_name
660
660
661 for _q in self._get_default_search_queries(self.request.GET, searcher, query):
661 for _q in self._get_default_search_queries(self.request.GET, searcher, query):
662 res.append(_q)
662 res.append(_q)
663
663
664 repo_groups = self._get_repo_group_list(query, repo_group_name=repo_group_name)
664 repo_groups = self._get_repo_group_list(query, repo_group_name=repo_group_name)
665 for serialized_repo_group in repo_groups:
665 for serialized_repo_group in repo_groups:
666 res.append(serialized_repo_group)
666 res.append(serialized_repo_group)
667
667
668 repos = self._get_repo_list(query, repo_group_name=repo_group_name)
668 repos = self._get_repo_list(query, repo_group_name=repo_group_name)
669 for serialized_repo in repos:
669 for serialized_repo in repos:
670 res.append(serialized_repo)
670 res.append(serialized_repo)
671
671
672 if not repos and not repo_groups:
672 if not repos and not repo_groups:
673 res.append(no_match('No matches found'))
673 res.append(no_match('No matches found'))
674
674
675 return {'suggestions': res}
675 return {'suggestions': res}
676
676
677 @LoginRequired()
677 @LoginRequired()
678 @view_config(
678 @view_config(
679 route_name='home', request_method='GET',
679 route_name='home', request_method='GET',
680 renderer='rhodecode:templates/index.mako')
680 renderer='rhodecode:templates/index.mako')
681 def main_page(self):
681 def main_page(self):
682 c = self.load_default_context()
682 c = self.load_default_context()
683 c.repo_group = None
683 c.repo_group = None
684 return self._get_template_context(c)
684 return self._get_template_context(c)
685
685
686 def _main_page_repo_groups_data(self, repo_group_id):
686 def _main_page_repo_groups_data(self, repo_group_id):
687 column_map = {
687 column_map = {
688 'name': 'group_name_hash',
688 'name': 'group_name_hash',
689 'desc': 'group_description',
689 'desc': 'group_description',
690 'last_change': 'updated_on',
690 'last_change': 'updated_on',
691 'owner': 'user_username',
691 'owner': 'user_username',
692 }
692 }
693 draw, start, limit = self._extract_chunk(self.request)
693 draw, start, limit = self._extract_chunk(self.request)
694 search_q, order_by, order_dir = self._extract_ordering(
694 search_q, order_by, order_dir = self._extract_ordering(
695 self.request, column_map=column_map)
695 self.request, column_map=column_map)
696 return RepoGroupModel().get_repo_groups_data_table(
696 return RepoGroupModel().get_repo_groups_data_table(
697 draw, start, limit,
697 draw, start, limit,
698 search_q, order_by, order_dir,
698 search_q, order_by, order_dir,
699 self._rhodecode_user, repo_group_id)
699 self._rhodecode_user, repo_group_id)
700
700
701 def _main_page_repos_data(self, repo_group_id):
701 def _main_page_repos_data(self, repo_group_id):
702 column_map = {
702 column_map = {
703 'name': 'repo_name',
703 'name': 'repo_name',
704 'desc': 'description',
704 'desc': 'description',
705 'last_change': 'updated_on',
705 'last_change': 'updated_on',
706 'owner': 'user_username',
706 'owner': 'user_username',
707 }
707 }
708 draw, start, limit = self._extract_chunk(self.request)
708 draw, start, limit = self._extract_chunk(self.request)
709 search_q, order_by, order_dir = self._extract_ordering(
709 search_q, order_by, order_dir = self._extract_ordering(
710 self.request, column_map=column_map)
710 self.request, column_map=column_map)
711 return RepoModel().get_repos_data_table(
711 return RepoModel().get_repos_data_table(
712 draw, start, limit,
712 draw, start, limit,
713 search_q, order_by, order_dir,
713 search_q, order_by, order_dir,
714 self._rhodecode_user, repo_group_id)
714 self._rhodecode_user, repo_group_id)
715
715
716 @LoginRequired()
716 @LoginRequired()
717 @view_config(
717 @view_config(
718 route_name='main_page_repo_groups_data',
718 route_name='main_page_repo_groups_data',
719 request_method='GET', renderer='json_ext', xhr=True)
719 request_method='GET', renderer='json_ext', xhr=True)
720 def main_page_repo_groups_data(self):
720 def main_page_repo_groups_data(self):
721 self.load_default_context()
721 self.load_default_context()
722 repo_group_id = safe_int(self.request.GET.get('repo_group_id'))
722 repo_group_id = safe_int(self.request.GET.get('repo_group_id'))
723
723
724 if repo_group_id:
724 if repo_group_id:
725 group = RepoGroup.get_or_404(repo_group_id)
725 group = RepoGroup.get_or_404(repo_group_id)
726 _perms = ['group.read', 'group.write', 'group.admin']
726 _perms = AuthUser.repo_group_read_perms
727 if not HasRepoGroupPermissionAny(*_perms)(
727 if not HasRepoGroupPermissionAny(*_perms)(
728 group.group_name, 'user is allowed to list repo group children'):
728 group.group_name, 'user is allowed to list repo group children'):
729 raise HTTPNotFound()
729 raise HTTPNotFound()
730
730
731 return self._main_page_repo_groups_data(repo_group_id)
731 return self._main_page_repo_groups_data(repo_group_id)
732
732
733 @LoginRequired()
733 @LoginRequired()
734 @view_config(
734 @view_config(
735 route_name='main_page_repos_data',
735 route_name='main_page_repos_data',
736 request_method='GET', renderer='json_ext', xhr=True)
736 request_method='GET', renderer='json_ext', xhr=True)
737 def main_page_repos_data(self):
737 def main_page_repos_data(self):
738 self.load_default_context()
738 self.load_default_context()
739 repo_group_id = safe_int(self.request.GET.get('repo_group_id'))
739 repo_group_id = safe_int(self.request.GET.get('repo_group_id'))
740
740
741 if repo_group_id:
741 if repo_group_id:
742 group = RepoGroup.get_or_404(repo_group_id)
742 group = RepoGroup.get_or_404(repo_group_id)
743 _perms = ['group.read', 'group.write', 'group.admin']
743 _perms = AuthUser.repo_group_read_perms
744 if not HasRepoGroupPermissionAny(*_perms)(
744 if not HasRepoGroupPermissionAny(*_perms)(
745 group.group_name, 'user is allowed to list repo group children'):
745 group.group_name, 'user is allowed to list repo group children'):
746 raise HTTPNotFound()
746 raise HTTPNotFound()
747
747
748 return self._main_page_repos_data(repo_group_id)
748 return self._main_page_repos_data(repo_group_id)
749
749
750 @LoginRequired()
750 @LoginRequired()
751 @HasRepoGroupPermissionAnyDecorator(
751 @HasRepoGroupPermissionAnyDecorator(*AuthUser.repo_group_read_perms)
752 'group.read', 'group.write', 'group.admin')
753 @view_config(
752 @view_config(
754 route_name='repo_group_home', request_method='GET',
753 route_name='repo_group_home', request_method='GET',
755 renderer='rhodecode:templates/index_repo_group.mako')
754 renderer='rhodecode:templates/index_repo_group.mako')
756 @view_config(
755 @view_config(
757 route_name='repo_group_home_slash', request_method='GET',
756 route_name='repo_group_home_slash', request_method='GET',
758 renderer='rhodecode:templates/index_repo_group.mako')
757 renderer='rhodecode:templates/index_repo_group.mako')
759 def repo_group_main_page(self):
758 def repo_group_main_page(self):
760 c = self.load_default_context()
759 c = self.load_default_context()
761 c.repo_group = self.request.db_repo_group
760 c.repo_group = self.request.db_repo_group
762 return self._get_template_context(c)
761 return self._get_template_context(c)
763
762
764 @LoginRequired()
763 @LoginRequired()
765 @CSRFRequired()
764 @CSRFRequired()
766 @view_config(
765 @view_config(
767 route_name='markup_preview', request_method='POST',
766 route_name='markup_preview', request_method='POST',
768 renderer='string', xhr=True)
767 renderer='string', xhr=True)
769 def markup_preview(self):
768 def markup_preview(self):
770 # Technically a CSRF token is not needed as no state changes with this
769 # Technically a CSRF token is not needed as no state changes with this
771 # call. However, as this is a POST is better to have it, so automated
770 # call. However, as this is a POST is better to have it, so automated
772 # tools don't flag it as potential CSRF.
771 # tools don't flag it as potential CSRF.
773 # Post is required because the payload could be bigger than the maximum
772 # Post is required because the payload could be bigger than the maximum
774 # allowed by GET.
773 # allowed by GET.
775
774
776 text = self.request.POST.get('text')
775 text = self.request.POST.get('text')
777 renderer = self.request.POST.get('renderer') or 'rst'
776 renderer = self.request.POST.get('renderer') or 'rst'
778 if text:
777 if text:
779 return h.render(text, renderer=renderer, mentions=True)
778 return h.render(text, renderer=renderer, mentions=True)
780 return ''
779 return ''
781
780
782 @LoginRequired()
781 @LoginRequired()
783 @CSRFRequired()
782 @CSRFRequired()
784 @view_config(
783 @view_config(
785 route_name='file_preview', request_method='POST',
784 route_name='file_preview', request_method='POST',
786 renderer='string', xhr=True)
785 renderer='string', xhr=True)
787 def file_preview(self):
786 def file_preview(self):
788 # Technically a CSRF token is not needed as no state changes with this
787 # Technically a CSRF token is not needed as no state changes with this
789 # call. However, as this is a POST is better to have it, so automated
788 # call. However, as this is a POST is better to have it, so automated
790 # tools don't flag it as potential CSRF.
789 # tools don't flag it as potential CSRF.
791 # Post is required because the payload could be bigger than the maximum
790 # Post is required because the payload could be bigger than the maximum
792 # allowed by GET.
791 # allowed by GET.
793
792
794 text = self.request.POST.get('text')
793 text = self.request.POST.get('text')
795 file_path = self.request.POST.get('file_path')
794 file_path = self.request.POST.get('file_path')
796
795
797 renderer = h.renderer_from_filename(file_path)
796 renderer = h.renderer_from_filename(file_path)
798
797
799 if renderer:
798 if renderer:
800 return h.render(text, renderer=renderer, mentions=True)
799 return h.render(text, renderer=renderer, mentions=True)
801 else:
800 else:
802 self.load_default_context()
801 self.load_default_context()
803 _render = self.request.get_partial_renderer(
802 _render = self.request.get_partial_renderer(
804 'rhodecode:templates/files/file_content.mako')
803 'rhodecode:templates/files/file_content.mako')
805
804
806 lines = filenode_as_lines_tokens(FileNode(file_path, text))
805 lines = filenode_as_lines_tokens(FileNode(file_path, text))
807
806
808 return _render('render_lines', lines)
807 return _render('render_lines', lines)
809
808
810 @LoginRequired()
809 @LoginRequired()
811 @CSRFRequired()
810 @CSRFRequired()
812 @view_config(
811 @view_config(
813 route_name='store_user_session_value', request_method='POST',
812 route_name='store_user_session_value', request_method='POST',
814 renderer='string', xhr=True)
813 renderer='string', xhr=True)
815 def store_user_session_attr(self):
814 def store_user_session_attr(self):
816 key = self.request.POST.get('key')
815 key = self.request.POST.get('key')
817 val = self.request.POST.get('val')
816 val = self.request.POST.get('val')
818
817
819 existing_value = self.request.session.get(key)
818 existing_value = self.request.session.get(key)
820 if existing_value != val:
819 if existing_value != val:
821 self.request.session[key] = val
820 self.request.session[key] = val
822
821
823 return 'stored:{}:{}'.format(key, val)
822 return 'stored:{}:{}'.format(key, val)
@@ -1,579 +1,578 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 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 urlparse
21 import urlparse
22
22
23 import mock
23 import mock
24 import pytest
24 import pytest
25
25
26 from rhodecode.tests import (
26 from rhodecode.tests import (
27 assert_session_flash, HG_REPO, TEST_USER_ADMIN_LOGIN,
27 assert_session_flash, HG_REPO, TEST_USER_ADMIN_LOGIN,
28 no_newline_id_generator)
28 no_newline_id_generator)
29 from rhodecode.tests.fixture import Fixture
29 from rhodecode.tests.fixture import Fixture
30 from rhodecode.lib.auth import check_password
30 from rhodecode.lib.auth import check_password
31 from rhodecode.lib import helpers as h
31 from rhodecode.lib import helpers as h
32 from rhodecode.model.auth_token import AuthTokenModel
32 from rhodecode.model.auth_token import AuthTokenModel
33 from rhodecode.model.db import User, Notification, UserApiKeys
33 from rhodecode.model.db import User, Notification, UserApiKeys
34 from rhodecode.model.meta import Session
34 from rhodecode.model.meta import Session
35
35
36 fixture = Fixture()
36 fixture = Fixture()
37
37
38 whitelist_view = ['RepoCommitsView:repo_commit_raw']
38 whitelist_view = ['RepoCommitsView:repo_commit_raw']
39
39
40
40
41 def route_path(name, params=None, **kwargs):
41 def route_path(name, params=None, **kwargs):
42 import urllib
42 import urllib
43 from rhodecode.apps._base import ADMIN_PREFIX
43 from rhodecode.apps._base import ADMIN_PREFIX
44
44
45 base_url = {
45 base_url = {
46 'login': ADMIN_PREFIX + '/login',
46 'login': ADMIN_PREFIX + '/login',
47 'logout': ADMIN_PREFIX + '/logout',
47 'logout': ADMIN_PREFIX + '/logout',
48 'register': ADMIN_PREFIX + '/register',
48 'register': ADMIN_PREFIX + '/register',
49 'reset_password':
49 'reset_password':
50 ADMIN_PREFIX + '/password_reset',
50 ADMIN_PREFIX + '/password_reset',
51 'reset_password_confirmation':
51 'reset_password_confirmation':
52 ADMIN_PREFIX + '/password_reset_confirmation',
52 ADMIN_PREFIX + '/password_reset_confirmation',
53
53
54 'admin_permissions_application':
54 'admin_permissions_application':
55 ADMIN_PREFIX + '/permissions/application',
55 ADMIN_PREFIX + '/permissions/application',
56 'admin_permissions_application_update':
56 'admin_permissions_application_update':
57 ADMIN_PREFIX + '/permissions/application/update',
57 ADMIN_PREFIX + '/permissions/application/update',
58
58
59 'repo_commit_raw': '/{repo_name}/raw-changeset/{commit_id}'
59 'repo_commit_raw': '/{repo_name}/raw-changeset/{commit_id}'
60
60
61 }[name].format(**kwargs)
61 }[name].format(**kwargs)
62
62
63 if params:
63 if params:
64 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
64 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
65 return base_url
65 return base_url
66
66
67
67
68 @pytest.mark.usefixtures('app')
68 @pytest.mark.usefixtures('app')
69 class TestLoginController(object):
69 class TestLoginController(object):
70 destroy_users = set()
70 destroy_users = set()
71
71
72 @classmethod
72 @classmethod
73 def teardown_class(cls):
73 def teardown_class(cls):
74 fixture.destroy_users(cls.destroy_users)
74 fixture.destroy_users(cls.destroy_users)
75
75
76 def teardown_method(self, method):
76 def teardown_method(self, method):
77 for n in Notification.query().all():
77 for n in Notification.query().all():
78 Session().delete(n)
78 Session().delete(n)
79
79
80 Session().commit()
80 Session().commit()
81 assert Notification.query().all() == []
81 assert Notification.query().all() == []
82
82
83 def test_index(self):
83 def test_index(self):
84 response = self.app.get(route_path('login'))
84 response = self.app.get(route_path('login'))
85 assert response.status == '200 OK'
85 assert response.status == '200 OK'
86 # Test response...
86 # Test response...
87
87
88 def test_login_admin_ok(self):
88 def test_login_admin_ok(self):
89 response = self.app.post(route_path('login'),
89 response = self.app.post(route_path('login'),
90 {'username': 'test_admin',
90 {'username': 'test_admin',
91 'password': 'test12'}, status=302)
91 'password': 'test12'}, status=302)
92 response = response.follow()
92 response = response.follow()
93 session = response.get_session_from_response()
93 session = response.get_session_from_response()
94 username = session['rhodecode_user'].get('username')
94 username = session['rhodecode_user'].get('username')
95 assert username == 'test_admin'
95 assert username == 'test_admin'
96 response.mustcontain('/%s' % HG_REPO)
96 response.mustcontain('logout')
97
97
98 def test_login_regular_ok(self):
98 def test_login_regular_ok(self):
99 response = self.app.post(route_path('login'),
99 response = self.app.post(route_path('login'),
100 {'username': 'test_regular',
100 {'username': 'test_regular',
101 'password': 'test12'}, status=302)
101 'password': 'test12'}, status=302)
102
102
103 response = response.follow()
103 response = response.follow()
104 session = response.get_session_from_response()
104 session = response.get_session_from_response()
105 username = session['rhodecode_user'].get('username')
105 username = session['rhodecode_user'].get('username')
106 assert username == 'test_regular'
106 assert username == 'test_regular'
107
107 response.mustcontain('logout')
108 response.mustcontain('/%s' % HG_REPO)
109
108
110 def test_login_regular_forbidden_when_super_admin_restriction(self):
109 def test_login_regular_forbidden_when_super_admin_restriction(self):
111 from rhodecode.authentication.plugins.auth_rhodecode import RhodeCodeAuthPlugin
110 from rhodecode.authentication.plugins.auth_rhodecode import RhodeCodeAuthPlugin
112 with fixture.auth_restriction(RhodeCodeAuthPlugin.AUTH_RESTRICTION_SUPER_ADMIN):
111 with fixture.auth_restriction(RhodeCodeAuthPlugin.AUTH_RESTRICTION_SUPER_ADMIN):
113 response = self.app.post(route_path('login'),
112 response = self.app.post(route_path('login'),
114 {'username': 'test_regular',
113 {'username': 'test_regular',
115 'password': 'test12'})
114 'password': 'test12'})
116
115
117 response.mustcontain('invalid user name')
116 response.mustcontain('invalid user name')
118 response.mustcontain('invalid password')
117 response.mustcontain('invalid password')
119
118
120 def test_login_regular_forbidden_when_scope_restriction(self):
119 def test_login_regular_forbidden_when_scope_restriction(self):
121 from rhodecode.authentication.plugins.auth_rhodecode import RhodeCodeAuthPlugin
120 from rhodecode.authentication.plugins.auth_rhodecode import RhodeCodeAuthPlugin
122 with fixture.scope_restriction(RhodeCodeAuthPlugin.AUTH_RESTRICTION_SCOPE_VCS):
121 with fixture.scope_restriction(RhodeCodeAuthPlugin.AUTH_RESTRICTION_SCOPE_VCS):
123 response = self.app.post(route_path('login'),
122 response = self.app.post(route_path('login'),
124 {'username': 'test_regular',
123 {'username': 'test_regular',
125 'password': 'test12'})
124 'password': 'test12'})
126
125
127 response.mustcontain('invalid user name')
126 response.mustcontain('invalid user name')
128 response.mustcontain('invalid password')
127 response.mustcontain('invalid password')
129
128
130 def test_login_ok_came_from(self):
129 def test_login_ok_came_from(self):
131 test_came_from = '/_admin/users?branch=stable'
130 test_came_from = '/_admin/users?branch=stable'
132 _url = '{}?came_from={}'.format(route_path('login'), test_came_from)
131 _url = '{}?came_from={}'.format(route_path('login'), test_came_from)
133 response = self.app.post(
132 response = self.app.post(
134 _url, {'username': 'test_admin', 'password': 'test12'}, status=302)
133 _url, {'username': 'test_admin', 'password': 'test12'}, status=302)
135
134
136 assert 'branch=stable' in response.location
135 assert 'branch=stable' in response.location
137 response = response.follow()
136 response = response.follow()
138
137
139 assert response.status == '200 OK'
138 assert response.status == '200 OK'
140 response.mustcontain('Users administration')
139 response.mustcontain('Users administration')
141
140
142 def test_redirect_to_login_with_get_args(self):
141 def test_redirect_to_login_with_get_args(self):
143 with fixture.anon_access(False):
142 with fixture.anon_access(False):
144 kwargs = {'branch': 'stable'}
143 kwargs = {'branch': 'stable'}
145 response = self.app.get(
144 response = self.app.get(
146 h.route_path('repo_summary', repo_name=HG_REPO, _query=kwargs),
145 h.route_path('repo_summary', repo_name=HG_REPO, _query=kwargs),
147 status=302)
146 status=302)
148
147
149 response_query = urlparse.parse_qsl(response.location)
148 response_query = urlparse.parse_qsl(response.location)
150 assert 'branch=stable' in response_query[0][1]
149 assert 'branch=stable' in response_query[0][1]
151
150
152 def test_login_form_with_get_args(self):
151 def test_login_form_with_get_args(self):
153 _url = '{}?came_from=/_admin/users,branch=stable'.format(route_path('login'))
152 _url = '{}?came_from=/_admin/users,branch=stable'.format(route_path('login'))
154 response = self.app.get(_url)
153 response = self.app.get(_url)
155 assert 'branch%3Dstable' in response.form.action
154 assert 'branch%3Dstable' in response.form.action
156
155
157 @pytest.mark.parametrize("url_came_from", [
156 @pytest.mark.parametrize("url_came_from", [
158 'data:text/html,<script>window.alert("xss")</script>',
157 'data:text/html,<script>window.alert("xss")</script>',
159 'mailto:test@rhodecode.org',
158 'mailto:test@rhodecode.org',
160 'file:///etc/passwd',
159 'file:///etc/passwd',
161 'ftp://some.ftp.server',
160 'ftp://some.ftp.server',
162 'http://other.domain',
161 'http://other.domain',
163 '/\r\nX-Forwarded-Host: http://example.org',
162 '/\r\nX-Forwarded-Host: http://example.org',
164 ], ids=no_newline_id_generator)
163 ], ids=no_newline_id_generator)
165 def test_login_bad_came_froms(self, url_came_from):
164 def test_login_bad_came_froms(self, url_came_from):
166 _url = '{}?came_from={}'.format(route_path('login'), url_came_from)
165 _url = '{}?came_from={}'.format(route_path('login'), url_came_from)
167 response = self.app.post(
166 response = self.app.post(
168 _url,
167 _url,
169 {'username': 'test_admin', 'password': 'test12'})
168 {'username': 'test_admin', 'password': 'test12'})
170 assert response.status == '302 Found'
169 assert response.status == '302 Found'
171 response = response.follow()
170 response = response.follow()
172 assert response.status == '200 OK'
171 assert response.status == '200 OK'
173 assert response.request.path == '/'
172 assert response.request.path == '/'
174
173
175 def test_login_short_password(self):
174 def test_login_short_password(self):
176 response = self.app.post(route_path('login'),
175 response = self.app.post(route_path('login'),
177 {'username': 'test_admin',
176 {'username': 'test_admin',
178 'password': 'as'})
177 'password': 'as'})
179 assert response.status == '200 OK'
178 assert response.status == '200 OK'
180
179
181 response.mustcontain('Enter 3 characters or more')
180 response.mustcontain('Enter 3 characters or more')
182
181
183 def test_login_wrong_non_ascii_password(self, user_regular):
182 def test_login_wrong_non_ascii_password(self, user_regular):
184 response = self.app.post(
183 response = self.app.post(
185 route_path('login'),
184 route_path('login'),
186 {'username': user_regular.username,
185 {'username': user_regular.username,
187 'password': u'invalid-non-asci\xe4'.encode('utf8')})
186 'password': u'invalid-non-asci\xe4'.encode('utf8')})
188
187
189 response.mustcontain('invalid user name')
188 response.mustcontain('invalid user name')
190 response.mustcontain('invalid password')
189 response.mustcontain('invalid password')
191
190
192 def test_login_with_non_ascii_password(self, user_util):
191 def test_login_with_non_ascii_password(self, user_util):
193 password = u'valid-non-ascii\xe4'
192 password = u'valid-non-ascii\xe4'
194 user = user_util.create_user(password=password)
193 user = user_util.create_user(password=password)
195 response = self.app.post(
194 response = self.app.post(
196 route_path('login'),
195 route_path('login'),
197 {'username': user.username,
196 {'username': user.username,
198 'password': password.encode('utf-8')})
197 'password': password.encode('utf-8')})
199 assert response.status_code == 302
198 assert response.status_code == 302
200
199
201 def test_login_wrong_username_password(self):
200 def test_login_wrong_username_password(self):
202 response = self.app.post(route_path('login'),
201 response = self.app.post(route_path('login'),
203 {'username': 'error',
202 {'username': 'error',
204 'password': 'test12'})
203 'password': 'test12'})
205
204
206 response.mustcontain('invalid user name')
205 response.mustcontain('invalid user name')
207 response.mustcontain('invalid password')
206 response.mustcontain('invalid password')
208
207
209 def test_login_admin_ok_password_migration(self, real_crypto_backend):
208 def test_login_admin_ok_password_migration(self, real_crypto_backend):
210 from rhodecode.lib import auth
209 from rhodecode.lib import auth
211
210
212 # create new user, with sha256 password
211 # create new user, with sha256 password
213 temp_user = 'test_admin_sha256'
212 temp_user = 'test_admin_sha256'
214 user = fixture.create_user(temp_user)
213 user = fixture.create_user(temp_user)
215 user.password = auth._RhodeCodeCryptoSha256().hash_create(
214 user.password = auth._RhodeCodeCryptoSha256().hash_create(
216 b'test123')
215 b'test123')
217 Session().add(user)
216 Session().add(user)
218 Session().commit()
217 Session().commit()
219 self.destroy_users.add(temp_user)
218 self.destroy_users.add(temp_user)
220 response = self.app.post(route_path('login'),
219 response = self.app.post(route_path('login'),
221 {'username': temp_user,
220 {'username': temp_user,
222 'password': 'test123'}, status=302)
221 'password': 'test123'}, status=302)
223
222
224 response = response.follow()
223 response = response.follow()
225 session = response.get_session_from_response()
224 session = response.get_session_from_response()
226 username = session['rhodecode_user'].get('username')
225 username = session['rhodecode_user'].get('username')
227 assert username == temp_user
226 assert username == temp_user
228 response.mustcontain('/%s' % HG_REPO)
227 response.mustcontain('logout')
229
228
230 # new password should be bcrypted, after log-in and transfer
229 # new password should be bcrypted, after log-in and transfer
231 user = User.get_by_username(temp_user)
230 user = User.get_by_username(temp_user)
232 assert user.password.startswith('$')
231 assert user.password.startswith('$')
233
232
234 # REGISTRATIONS
233 # REGISTRATIONS
235 def test_register(self):
234 def test_register(self):
236 response = self.app.get(route_path('register'))
235 response = self.app.get(route_path('register'))
237 response.mustcontain('Create an Account')
236 response.mustcontain('Create an Account')
238
237
239 def test_register_err_same_username(self):
238 def test_register_err_same_username(self):
240 uname = 'test_admin'
239 uname = 'test_admin'
241 response = self.app.post(
240 response = self.app.post(
242 route_path('register'),
241 route_path('register'),
243 {
242 {
244 'username': uname,
243 'username': uname,
245 'password': 'test12',
244 'password': 'test12',
246 'password_confirmation': 'test12',
245 'password_confirmation': 'test12',
247 'email': 'goodmail@domain.com',
246 'email': 'goodmail@domain.com',
248 'firstname': 'test',
247 'firstname': 'test',
249 'lastname': 'test'
248 'lastname': 'test'
250 }
249 }
251 )
250 )
252
251
253 assertr = response.assert_response()
252 assertr = response.assert_response()
254 msg = 'Username "%(username)s" already exists'
253 msg = 'Username "%(username)s" already exists'
255 msg = msg % {'username': uname}
254 msg = msg % {'username': uname}
256 assertr.element_contains('#username+.error-message', msg)
255 assertr.element_contains('#username+.error-message', msg)
257
256
258 def test_register_err_same_email(self):
257 def test_register_err_same_email(self):
259 response = self.app.post(
258 response = self.app.post(
260 route_path('register'),
259 route_path('register'),
261 {
260 {
262 'username': 'test_admin_0',
261 'username': 'test_admin_0',
263 'password': 'test12',
262 'password': 'test12',
264 'password_confirmation': 'test12',
263 'password_confirmation': 'test12',
265 'email': 'test_admin@mail.com',
264 'email': 'test_admin@mail.com',
266 'firstname': 'test',
265 'firstname': 'test',
267 'lastname': 'test'
266 'lastname': 'test'
268 }
267 }
269 )
268 )
270
269
271 assertr = response.assert_response()
270 assertr = response.assert_response()
272 msg = u'This e-mail address is already taken'
271 msg = u'This e-mail address is already taken'
273 assertr.element_contains('#email+.error-message', msg)
272 assertr.element_contains('#email+.error-message', msg)
274
273
275 def test_register_err_same_email_case_sensitive(self):
274 def test_register_err_same_email_case_sensitive(self):
276 response = self.app.post(
275 response = self.app.post(
277 route_path('register'),
276 route_path('register'),
278 {
277 {
279 'username': 'test_admin_1',
278 'username': 'test_admin_1',
280 'password': 'test12',
279 'password': 'test12',
281 'password_confirmation': 'test12',
280 'password_confirmation': 'test12',
282 'email': 'TesT_Admin@mail.COM',
281 'email': 'TesT_Admin@mail.COM',
283 'firstname': 'test',
282 'firstname': 'test',
284 'lastname': 'test'
283 'lastname': 'test'
285 }
284 }
286 )
285 )
287 assertr = response.assert_response()
286 assertr = response.assert_response()
288 msg = u'This e-mail address is already taken'
287 msg = u'This e-mail address is already taken'
289 assertr.element_contains('#email+.error-message', msg)
288 assertr.element_contains('#email+.error-message', msg)
290
289
291 def test_register_err_wrong_data(self):
290 def test_register_err_wrong_data(self):
292 response = self.app.post(
291 response = self.app.post(
293 route_path('register'),
292 route_path('register'),
294 {
293 {
295 'username': 'xs',
294 'username': 'xs',
296 'password': 'test',
295 'password': 'test',
297 'password_confirmation': 'test',
296 'password_confirmation': 'test',
298 'email': 'goodmailm',
297 'email': 'goodmailm',
299 'firstname': 'test',
298 'firstname': 'test',
300 'lastname': 'test'
299 'lastname': 'test'
301 }
300 }
302 )
301 )
303 assert response.status == '200 OK'
302 assert response.status == '200 OK'
304 response.mustcontain('An email address must contain a single @')
303 response.mustcontain('An email address must contain a single @')
305 response.mustcontain('Enter a value 6 characters long or more')
304 response.mustcontain('Enter a value 6 characters long or more')
306
305
307 def test_register_err_username(self):
306 def test_register_err_username(self):
308 response = self.app.post(
307 response = self.app.post(
309 route_path('register'),
308 route_path('register'),
310 {
309 {
311 'username': 'error user',
310 'username': 'error user',
312 'password': 'test12',
311 'password': 'test12',
313 'password_confirmation': 'test12',
312 'password_confirmation': 'test12',
314 'email': 'goodmailm',
313 'email': 'goodmailm',
315 'firstname': 'test',
314 'firstname': 'test',
316 'lastname': 'test'
315 'lastname': 'test'
317 }
316 }
318 )
317 )
319
318
320 response.mustcontain('An email address must contain a single @')
319 response.mustcontain('An email address must contain a single @')
321 response.mustcontain(
320 response.mustcontain(
322 'Username may only contain '
321 'Username may only contain '
323 'alphanumeric characters underscores, '
322 'alphanumeric characters underscores, '
324 'periods or dashes and must begin with '
323 'periods or dashes and must begin with '
325 'alphanumeric character')
324 'alphanumeric character')
326
325
327 def test_register_err_case_sensitive(self):
326 def test_register_err_case_sensitive(self):
328 usr = 'Test_Admin'
327 usr = 'Test_Admin'
329 response = self.app.post(
328 response = self.app.post(
330 route_path('register'),
329 route_path('register'),
331 {
330 {
332 'username': usr,
331 'username': usr,
333 'password': 'test12',
332 'password': 'test12',
334 'password_confirmation': 'test12',
333 'password_confirmation': 'test12',
335 'email': 'goodmailm',
334 'email': 'goodmailm',
336 'firstname': 'test',
335 'firstname': 'test',
337 'lastname': 'test'
336 'lastname': 'test'
338 }
337 }
339 )
338 )
340
339
341 assertr = response.assert_response()
340 assertr = response.assert_response()
342 msg = u'Username "%(username)s" already exists'
341 msg = u'Username "%(username)s" already exists'
343 msg = msg % {'username': usr}
342 msg = msg % {'username': usr}
344 assertr.element_contains('#username+.error-message', msg)
343 assertr.element_contains('#username+.error-message', msg)
345
344
346 def test_register_special_chars(self):
345 def test_register_special_chars(self):
347 response = self.app.post(
346 response = self.app.post(
348 route_path('register'),
347 route_path('register'),
349 {
348 {
350 'username': 'xxxaxn',
349 'username': 'xxxaxn',
351 'password': 'Δ…Δ‡ΕΊΕΌΔ…Ε›Ε›Ε›Ε›',
350 'password': 'Δ…Δ‡ΕΊΕΌΔ…Ε›Ε›Ε›Ε›',
352 'password_confirmation': 'Δ…Δ‡ΕΊΕΌΔ…Ε›Ε›Ε›Ε›',
351 'password_confirmation': 'Δ…Δ‡ΕΊΕΌΔ…Ε›Ε›Ε›Ε›',
353 'email': 'goodmailm@test.plx',
352 'email': 'goodmailm@test.plx',
354 'firstname': 'test',
353 'firstname': 'test',
355 'lastname': 'test'
354 'lastname': 'test'
356 }
355 }
357 )
356 )
358
357
359 msg = u'Invalid characters (non-ascii) in password'
358 msg = u'Invalid characters (non-ascii) in password'
360 response.mustcontain(msg)
359 response.mustcontain(msg)
361
360
362 def test_register_password_mismatch(self):
361 def test_register_password_mismatch(self):
363 response = self.app.post(
362 response = self.app.post(
364 route_path('register'),
363 route_path('register'),
365 {
364 {
366 'username': 'xs',
365 'username': 'xs',
367 'password': '123qwe',
366 'password': '123qwe',
368 'password_confirmation': 'qwe123',
367 'password_confirmation': 'qwe123',
369 'email': 'goodmailm@test.plxa',
368 'email': 'goodmailm@test.plxa',
370 'firstname': 'test',
369 'firstname': 'test',
371 'lastname': 'test'
370 'lastname': 'test'
372 }
371 }
373 )
372 )
374 msg = u'Passwords do not match'
373 msg = u'Passwords do not match'
375 response.mustcontain(msg)
374 response.mustcontain(msg)
376
375
377 def test_register_ok(self):
376 def test_register_ok(self):
378 username = 'test_regular4'
377 username = 'test_regular4'
379 password = 'qweqwe'
378 password = 'qweqwe'
380 email = 'marcin@test.com'
379 email = 'marcin@test.com'
381 name = 'testname'
380 name = 'testname'
382 lastname = 'testlastname'
381 lastname = 'testlastname'
383
382
384 # this initializes a session
383 # this initializes a session
385 response = self.app.get(route_path('register'))
384 response = self.app.get(route_path('register'))
386 response.mustcontain('Create an Account')
385 response.mustcontain('Create an Account')
387
386
388
387
389 response = self.app.post(
388 response = self.app.post(
390 route_path('register'),
389 route_path('register'),
391 {
390 {
392 'username': username,
391 'username': username,
393 'password': password,
392 'password': password,
394 'password_confirmation': password,
393 'password_confirmation': password,
395 'email': email,
394 'email': email,
396 'firstname': name,
395 'firstname': name,
397 'lastname': lastname,
396 'lastname': lastname,
398 'admin': True
397 'admin': True
399 },
398 },
400 status=302
399 status=302
401 ) # This should be overridden
400 ) # This should be overridden
402
401
403 assert_session_flash(
402 assert_session_flash(
404 response, 'You have successfully registered with RhodeCode. You can log-in now.')
403 response, 'You have successfully registered with RhodeCode. You can log-in now.')
405
404
406 ret = Session().query(User).filter(
405 ret = Session().query(User).filter(
407 User.username == 'test_regular4').one()
406 User.username == 'test_regular4').one()
408 assert ret.username == username
407 assert ret.username == username
409 assert check_password(password, ret.password)
408 assert check_password(password, ret.password)
410 assert ret.email == email
409 assert ret.email == email
411 assert ret.name == name
410 assert ret.name == name
412 assert ret.lastname == lastname
411 assert ret.lastname == lastname
413 assert ret.auth_tokens is not None
412 assert ret.auth_tokens is not None
414 assert not ret.admin
413 assert not ret.admin
415
414
416 def test_forgot_password_wrong_mail(self):
415 def test_forgot_password_wrong_mail(self):
417 bad_email = 'marcin@wrongmail.org'
416 bad_email = 'marcin@wrongmail.org'
418 # this initializes a session
417 # this initializes a session
419 self.app.get(route_path('reset_password'))
418 self.app.get(route_path('reset_password'))
420
419
421 response = self.app.post(
420 response = self.app.post(
422 route_path('reset_password'), {'email': bad_email, }
421 route_path('reset_password'), {'email': bad_email, }
423 )
422 )
424 assert_session_flash(response,
423 assert_session_flash(response,
425 'If such email exists, a password reset link was sent to it.')
424 'If such email exists, a password reset link was sent to it.')
426
425
427 def test_forgot_password(self, user_util):
426 def test_forgot_password(self, user_util):
428 # this initializes a session
427 # this initializes a session
429 self.app.get(route_path('reset_password'))
428 self.app.get(route_path('reset_password'))
430
429
431 user = user_util.create_user()
430 user = user_util.create_user()
432 user_id = user.user_id
431 user_id = user.user_id
433 email = user.email
432 email = user.email
434
433
435 response = self.app.post(route_path('reset_password'), {'email': email, })
434 response = self.app.post(route_path('reset_password'), {'email': email, })
436
435
437 assert_session_flash(response,
436 assert_session_flash(response,
438 'If such email exists, a password reset link was sent to it.')
437 'If such email exists, a password reset link was sent to it.')
439
438
440 # BAD KEY
439 # BAD KEY
441 confirm_url = '{}?key={}'.format(route_path('reset_password_confirmation'), 'badkey')
440 confirm_url = '{}?key={}'.format(route_path('reset_password_confirmation'), 'badkey')
442 response = self.app.get(confirm_url, status=302)
441 response = self.app.get(confirm_url, status=302)
443 assert response.location.endswith(route_path('reset_password'))
442 assert response.location.endswith(route_path('reset_password'))
444 assert_session_flash(response, 'Given reset token is invalid')
443 assert_session_flash(response, 'Given reset token is invalid')
445
444
446 response.follow() # cleanup flash
445 response.follow() # cleanup flash
447
446
448 # GOOD KEY
447 # GOOD KEY
449 key = UserApiKeys.query()\
448 key = UserApiKeys.query()\
450 .filter(UserApiKeys.user_id == user_id)\
449 .filter(UserApiKeys.user_id == user_id)\
451 .filter(UserApiKeys.role == UserApiKeys.ROLE_PASSWORD_RESET)\
450 .filter(UserApiKeys.role == UserApiKeys.ROLE_PASSWORD_RESET)\
452 .first()
451 .first()
453
452
454 assert key
453 assert key
455
454
456 confirm_url = '{}?key={}'.format(route_path('reset_password_confirmation'), key.api_key)
455 confirm_url = '{}?key={}'.format(route_path('reset_password_confirmation'), key.api_key)
457 response = self.app.get(confirm_url)
456 response = self.app.get(confirm_url)
458 assert response.status == '302 Found'
457 assert response.status == '302 Found'
459 assert response.location.endswith(route_path('login'))
458 assert response.location.endswith(route_path('login'))
460
459
461 assert_session_flash(
460 assert_session_flash(
462 response,
461 response,
463 'Your password reset was successful, '
462 'Your password reset was successful, '
464 'a new password has been sent to your email')
463 'a new password has been sent to your email')
465
464
466 response.follow()
465 response.follow()
467
466
468 def _get_api_whitelist(self, values=None):
467 def _get_api_whitelist(self, values=None):
469 config = {'api_access_controllers_whitelist': values or []}
468 config = {'api_access_controllers_whitelist': values or []}
470 return config
469 return config
471
470
472 @pytest.mark.parametrize("test_name, auth_token", [
471 @pytest.mark.parametrize("test_name, auth_token", [
473 ('none', None),
472 ('none', None),
474 ('empty_string', ''),
473 ('empty_string', ''),
475 ('fake_number', '123456'),
474 ('fake_number', '123456'),
476 ('proper_auth_token', None)
475 ('proper_auth_token', None)
477 ])
476 ])
478 def test_access_not_whitelisted_page_via_auth_token(
477 def test_access_not_whitelisted_page_via_auth_token(
479 self, test_name, auth_token, user_admin):
478 self, test_name, auth_token, user_admin):
480
479
481 whitelist = self._get_api_whitelist([])
480 whitelist = self._get_api_whitelist([])
482 with mock.patch.dict('rhodecode.CONFIG', whitelist):
481 with mock.patch.dict('rhodecode.CONFIG', whitelist):
483 assert [] == whitelist['api_access_controllers_whitelist']
482 assert [] == whitelist['api_access_controllers_whitelist']
484 if test_name == 'proper_auth_token':
483 if test_name == 'proper_auth_token':
485 # use builtin if api_key is None
484 # use builtin if api_key is None
486 auth_token = user_admin.api_key
485 auth_token = user_admin.api_key
487
486
488 with fixture.anon_access(False):
487 with fixture.anon_access(False):
489 self.app.get(
488 self.app.get(
490 route_path('repo_commit_raw',
489 route_path('repo_commit_raw',
491 repo_name=HG_REPO, commit_id='tip',
490 repo_name=HG_REPO, commit_id='tip',
492 params=dict(api_key=auth_token)),
491 params=dict(api_key=auth_token)),
493 status=302)
492 status=302)
494
493
495 @pytest.mark.parametrize("test_name, auth_token, code", [
494 @pytest.mark.parametrize("test_name, auth_token, code", [
496 ('none', None, 302),
495 ('none', None, 302),
497 ('empty_string', '', 302),
496 ('empty_string', '', 302),
498 ('fake_number', '123456', 302),
497 ('fake_number', '123456', 302),
499 ('proper_auth_token', None, 200)
498 ('proper_auth_token', None, 200)
500 ])
499 ])
501 def test_access_whitelisted_page_via_auth_token(
500 def test_access_whitelisted_page_via_auth_token(
502 self, test_name, auth_token, code, user_admin):
501 self, test_name, auth_token, code, user_admin):
503
502
504 whitelist = self._get_api_whitelist(whitelist_view)
503 whitelist = self._get_api_whitelist(whitelist_view)
505
504
506 with mock.patch.dict('rhodecode.CONFIG', whitelist):
505 with mock.patch.dict('rhodecode.CONFIG', whitelist):
507 assert whitelist_view == whitelist['api_access_controllers_whitelist']
506 assert whitelist_view == whitelist['api_access_controllers_whitelist']
508
507
509 if test_name == 'proper_auth_token':
508 if test_name == 'proper_auth_token':
510 auth_token = user_admin.api_key
509 auth_token = user_admin.api_key
511 assert auth_token
510 assert auth_token
512
511
513 with fixture.anon_access(False):
512 with fixture.anon_access(False):
514 self.app.get(
513 self.app.get(
515 route_path('repo_commit_raw',
514 route_path('repo_commit_raw',
516 repo_name=HG_REPO, commit_id='tip',
515 repo_name=HG_REPO, commit_id='tip',
517 params=dict(api_key=auth_token)),
516 params=dict(api_key=auth_token)),
518 status=code)
517 status=code)
519
518
520 @pytest.mark.parametrize("test_name, auth_token, code", [
519 @pytest.mark.parametrize("test_name, auth_token, code", [
521 ('proper_auth_token', None, 200),
520 ('proper_auth_token', None, 200),
522 ('wrong_auth_token', '123456', 302),
521 ('wrong_auth_token', '123456', 302),
523 ])
522 ])
524 def test_access_whitelisted_page_via_auth_token_bound_to_token(
523 def test_access_whitelisted_page_via_auth_token_bound_to_token(
525 self, test_name, auth_token, code, user_admin):
524 self, test_name, auth_token, code, user_admin):
526
525
527 expected_token = auth_token
526 expected_token = auth_token
528 if test_name == 'proper_auth_token':
527 if test_name == 'proper_auth_token':
529 auth_token = user_admin.api_key
528 auth_token = user_admin.api_key
530 expected_token = auth_token
529 expected_token = auth_token
531 assert auth_token
530 assert auth_token
532
531
533 whitelist = self._get_api_whitelist([
532 whitelist = self._get_api_whitelist([
534 'RepoCommitsView:repo_commit_raw@{}'.format(expected_token)])
533 'RepoCommitsView:repo_commit_raw@{}'.format(expected_token)])
535
534
536 with mock.patch.dict('rhodecode.CONFIG', whitelist):
535 with mock.patch.dict('rhodecode.CONFIG', whitelist):
537
536
538 with fixture.anon_access(False):
537 with fixture.anon_access(False):
539 self.app.get(
538 self.app.get(
540 route_path('repo_commit_raw',
539 route_path('repo_commit_raw',
541 repo_name=HG_REPO, commit_id='tip',
540 repo_name=HG_REPO, commit_id='tip',
542 params=dict(api_key=auth_token)),
541 params=dict(api_key=auth_token)),
543 status=code)
542 status=code)
544
543
545 def test_access_page_via_extra_auth_token(self):
544 def test_access_page_via_extra_auth_token(self):
546 whitelist = self._get_api_whitelist(whitelist_view)
545 whitelist = self._get_api_whitelist(whitelist_view)
547 with mock.patch.dict('rhodecode.CONFIG', whitelist):
546 with mock.patch.dict('rhodecode.CONFIG', whitelist):
548 assert whitelist_view == \
547 assert whitelist_view == \
549 whitelist['api_access_controllers_whitelist']
548 whitelist['api_access_controllers_whitelist']
550
549
551 new_auth_token = AuthTokenModel().create(
550 new_auth_token = AuthTokenModel().create(
552 TEST_USER_ADMIN_LOGIN, 'test')
551 TEST_USER_ADMIN_LOGIN, 'test')
553 Session().commit()
552 Session().commit()
554 with fixture.anon_access(False):
553 with fixture.anon_access(False):
555 self.app.get(
554 self.app.get(
556 route_path('repo_commit_raw',
555 route_path('repo_commit_raw',
557 repo_name=HG_REPO, commit_id='tip',
556 repo_name=HG_REPO, commit_id='tip',
558 params=dict(api_key=new_auth_token.api_key)),
557 params=dict(api_key=new_auth_token.api_key)),
559 status=200)
558 status=200)
560
559
561 def test_access_page_via_expired_auth_token(self):
560 def test_access_page_via_expired_auth_token(self):
562 whitelist = self._get_api_whitelist(whitelist_view)
561 whitelist = self._get_api_whitelist(whitelist_view)
563 with mock.patch.dict('rhodecode.CONFIG', whitelist):
562 with mock.patch.dict('rhodecode.CONFIG', whitelist):
564 assert whitelist_view == \
563 assert whitelist_view == \
565 whitelist['api_access_controllers_whitelist']
564 whitelist['api_access_controllers_whitelist']
566
565
567 new_auth_token = AuthTokenModel().create(
566 new_auth_token = AuthTokenModel().create(
568 TEST_USER_ADMIN_LOGIN, 'test')
567 TEST_USER_ADMIN_LOGIN, 'test')
569 Session().commit()
568 Session().commit()
570 # patch the api key and make it expired
569 # patch the api key and make it expired
571 new_auth_token.expires = 0
570 new_auth_token.expires = 0
572 Session().add(new_auth_token)
571 Session().add(new_auth_token)
573 Session().commit()
572 Session().commit()
574 with fixture.anon_access(False):
573 with fixture.anon_access(False):
575 self.app.get(
574 self.app.get(
576 route_path('repo_commit_raw',
575 route_path('repo_commit_raw',
577 repo_name=HG_REPO, commit_id='tip',
576 repo_name=HG_REPO, commit_id='tip',
578 params=dict(api_key=new_auth_token.api_key)),
577 params=dict(api_key=new_auth_token.api_key)),
579 status=302)
578 status=302)
@@ -1,152 +1,157 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2019 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 pytest
21 import pytest
22
22
23 from rhodecode.tests import TestController
23 from rhodecode.tests import TestController
24 from rhodecode.tests.fixture import Fixture
24 from rhodecode.tests.fixture import Fixture
25
25
26
26
27 def route_path(name, params=None, **kwargs):
27 def route_path(name, params=None, **kwargs):
28 import urllib
28 import urllib
29 from rhodecode.apps._base import ADMIN_PREFIX
29 from rhodecode.apps._base import ADMIN_PREFIX
30
30
31 base_url = {
31 base_url = {
32 'home': '/',
32 'home': '/',
33 'admin_home': ADMIN_PREFIX,
33 'admin_home': ADMIN_PREFIX,
34 'repos':
34 'repos':
35 ADMIN_PREFIX + '/repos',
35 ADMIN_PREFIX + '/repos',
36 'repos_data':
37 ADMIN_PREFIX + '/repos_data',
36 'repo_groups':
38 'repo_groups':
37 ADMIN_PREFIX + '/repo_groups',
39 ADMIN_PREFIX + '/repo_groups',
38 'repo_groups_data':
40 'repo_groups_data':
39 ADMIN_PREFIX + '/repo_groups_data',
41 ADMIN_PREFIX + '/repo_groups_data',
40 'user_groups':
42 'user_groups':
41 ADMIN_PREFIX + '/user_groups',
43 ADMIN_PREFIX + '/user_groups',
42 'user_groups_data':
44 'user_groups_data':
43 ADMIN_PREFIX + '/user_groups_data',
45 ADMIN_PREFIX + '/user_groups_data',
44 }[name].format(**kwargs)
46 }[name].format(**kwargs)
45
47
46 if params:
48 if params:
47 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
49 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
48 return base_url
50 return base_url
49
51
50
52
51 fixture = Fixture()
53 fixture = Fixture()
52
54
53
55
54 class TestAdminDelegatedUser(TestController):
56 class TestAdminDelegatedUser(TestController):
55
57
56 def test_regular_user_cannot_see_admin_interfaces(self, user_util, xhr_header):
58 def test_regular_user_cannot_see_admin_interfaces(self, user_util, xhr_header):
57 user = user_util.create_user(password='qweqwe')
59 user = user_util.create_user(password='qweqwe')
58 user_util.inherit_default_user_permissions(user.username, False)
60 user_util.inherit_default_user_permissions(user.username, False)
59
61
60 self.log_user(user.username, 'qweqwe')
62 self.log_user(user.username, 'qweqwe')
61
63
62 # user doesn't have any access to resources so main admin page should 404
64 # user doesn't have any access to resources so main admin page should 404
63 self.app.get(route_path('admin_home'), status=404)
65 self.app.get(route_path('admin_home'), status=404)
64
66
65 response = self.app.get(route_path('repos'), status=200)
67 response = self.app.get(route_path('repos_data'),
66 response.mustcontain('data: []')
68 status=200, extra_environ=xhr_header)
69 assert response.json['data'] == []
67
70
68 response = self.app.get(route_path('repo_groups_data'),
71 response = self.app.get(route_path('repo_groups_data'),
69 status=200, extra_environ=xhr_header)
72 status=200, extra_environ=xhr_header)
70 assert response.json['data'] == []
73 assert response.json['data'] == []
71
74
72 response = self.app.get(route_path('user_groups_data'),
75 response = self.app.get(route_path('user_groups_data'),
73 status=200, extra_environ=xhr_header)
76 status=200, extra_environ=xhr_header)
74 assert response.json['data'] == []
77 assert response.json['data'] == []
75
78
76 def test_regular_user_can_see_admin_interfaces_if_owner(self, user_util, xhr_header):
79 def test_regular_user_can_see_admin_interfaces_if_owner(self, user_util, xhr_header):
77 user = user_util.create_user(password='qweqwe')
80 user = user_util.create_user(password='qweqwe')
78 username = user.username
81 username = user.username
79
82
80 repo = user_util.create_repo(owner=username)
83 repo = user_util.create_repo(owner=username)
81 repo_name = repo.repo_name
84 repo_name = repo.repo_name
82
85
83 repo_group = user_util.create_repo_group(owner=username)
86 repo_group = user_util.create_repo_group(owner=username)
84 repo_group_name = repo_group.group_name
87 repo_group_name = repo_group.group_name
85
88
86 user_group = user_util.create_user_group(owner=username)
89 user_group = user_util.create_user_group(owner=username)
87 user_group_name = user_group.users_group_name
90 user_group_name = user_group.users_group_name
88
91
89 self.log_user(username, 'qweqwe')
92 self.log_user(username, 'qweqwe')
90
93
91 response = self.app.get(route_path('admin_home'))
94 response = self.app.get(route_path('admin_home'))
92
95
93 assert_response = response.assert_response()
96 assert_response = response.assert_response()
94
97
95 assert_response.element_contains('td.delegated-admin-repos', '1')
98 assert_response.element_contains('td.delegated-admin-repos', '1')
96 assert_response.element_contains('td.delegated-admin-repo-groups', '1')
99 assert_response.element_contains('td.delegated-admin-repo-groups', '1')
97 assert_response.element_contains('td.delegated-admin-user-groups', '1')
100 assert_response.element_contains('td.delegated-admin-user-groups', '1')
98
101
99 # admin interfaces have visible elements
102 # admin interfaces have visible elements
100 response = self.app.get(route_path('repos'), status=200)
103 response = self.app.get(route_path('repos_data'),
101 response.mustcontain('"name_raw": "{}"'.format(repo_name))
104 extra_environ=xhr_header, status=200)
105 response.mustcontain('<a href=\\"/{}\\">'.format(repo_name))
102
106
103 response = self.app.get(route_path('repo_groups_data'),
107 response = self.app.get(route_path('repo_groups_data'),
104 extra_environ=xhr_header, status=200)
108 extra_environ=xhr_header, status=200)
105 response.mustcontain('"name_raw": "{}"'.format(repo_group_name))
109 response.mustcontain('<a href=\\"/{}\\">'.format(repo_group_name))
106
110
107 response = self.app.get(route_path('user_groups_data'),
111 response = self.app.get(route_path('user_groups_data'),
108 extra_environ=xhr_header, status=200)
112 extra_environ=xhr_header, status=200)
109 response.mustcontain('"name_raw": "{}"'.format(user_group_name))
113 response.mustcontain('<a href=\\"/_profile_user_group/{}\\">'.format(user_group_name))
110
114
111 def test_regular_user_can_see_admin_interfaces_if_admin_perm(
115 def test_regular_user_can_see_admin_interfaces_if_admin_perm(
112 self, user_util, xhr_header):
116 self, user_util, xhr_header):
113 user = user_util.create_user(password='qweqwe')
117 user = user_util.create_user(password='qweqwe')
114 username = user.username
118 username = user.username
115
119
116 repo = user_util.create_repo()
120 repo = user_util.create_repo()
117 repo_name = repo.repo_name
121 repo_name = repo.repo_name
118
122
119 repo_group = user_util.create_repo_group()
123 repo_group = user_util.create_repo_group()
120 repo_group_name = repo_group.group_name
124 repo_group_name = repo_group.group_name
121
125
122 user_group = user_util.create_user_group()
126 user_group = user_util.create_user_group()
123 user_group_name = user_group.users_group_name
127 user_group_name = user_group.users_group_name
124
128
125 user_util.grant_user_permission_to_repo(
129 user_util.grant_user_permission_to_repo(
126 repo, user, 'repository.admin')
130 repo, user, 'repository.admin')
127 user_util.grant_user_permission_to_repo_group(
131 user_util.grant_user_permission_to_repo_group(
128 repo_group, user, 'group.admin')
132 repo_group, user, 'group.admin')
129 user_util.grant_user_permission_to_user_group(
133 user_util.grant_user_permission_to_user_group(
130 user_group, user, 'usergroup.admin')
134 user_group, user, 'usergroup.admin')
131
135
132 self.log_user(username, 'qweqwe')
136 self.log_user(username, 'qweqwe')
133 # check if in home view, such user doesn't see the "admin" menus
137 # check if in home view, such user doesn't see the "admin" menus
134 response = self.app.get(route_path('admin_home'))
138 response = self.app.get(route_path('admin_home'))
135
139
136 assert_response = response.assert_response()
140 assert_response = response.assert_response()
137
141
138 assert_response.element_contains('td.delegated-admin-repos', '1')
142 assert_response.element_contains('td.delegated-admin-repos', '1')
139 assert_response.element_contains('td.delegated-admin-repo-groups', '1')
143 assert_response.element_contains('td.delegated-admin-repo-groups', '1')
140 assert_response.element_contains('td.delegated-admin-user-groups', '1')
144 assert_response.element_contains('td.delegated-admin-user-groups', '1')
141
145
142 # admin interfaces have visible elements
146 # admin interfaces have visible elements
143 response = self.app.get(route_path('repos'), status=200)
147 response = self.app.get(route_path('repos_data'),
144 response.mustcontain('"name_raw": "{}"'.format(repo_name))
148 extra_environ=xhr_header, status=200)
149 response.mustcontain('<a href=\\"/{}\\">'.format(repo_name))
145
150
146 response = self.app.get(route_path('repo_groups_data'),
151 response = self.app.get(route_path('repo_groups_data'),
147 extra_environ=xhr_header, status=200)
152 extra_environ=xhr_header, status=200)
148 response.mustcontain('"name_raw": "{}"'.format(repo_group_name))
153 response.mustcontain('<a href=\\"/{}\\">'.format(repo_group_name))
149
154
150 response = self.app.get(route_path('user_groups_data'),
155 response = self.app.get(route_path('user_groups_data'),
151 extra_environ=xhr_header, status=200)
156 extra_environ=xhr_header, status=200)
152 response.mustcontain('"name_raw": "{}"'.format(user_group_name))
157 response.mustcontain('<a href=\\"/_profile_user_group/{}\\">'.format(user_group_name))
General Comments 0
You need to be logged in to leave comments. Login now