##// END OF EJS Templates
users: use two distinct actions for user password reset....
marcink -
r3378:f96b7591 default
parent child Browse files
Show More
@@ -1,442 +1,446 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 from rhodecode.apps._base import ADMIN_PREFIX
23 23
24 24
25 25 def admin_routes(config):
26 26 """
27 27 Admin prefixed routes
28 28 """
29 29
30 30 config.add_route(
31 31 name='admin_audit_logs',
32 32 pattern='/audit_logs')
33 33
34 34 config.add_route(
35 35 name='admin_audit_log_entry',
36 36 pattern='/audit_logs/{audit_log_id}')
37 37
38 38 config.add_route(
39 39 name='pull_requests_global_0', # backward compat
40 40 pattern='/pull_requests/{pull_request_id:\d+}')
41 41 config.add_route(
42 42 name='pull_requests_global_1', # backward compat
43 43 pattern='/pull-requests/{pull_request_id:\d+}')
44 44 config.add_route(
45 45 name='pull_requests_global',
46 46 pattern='/pull-request/{pull_request_id:\d+}')
47 47
48 48 config.add_route(
49 49 name='admin_settings_open_source',
50 50 pattern='/settings/open_source')
51 51 config.add_route(
52 52 name='admin_settings_vcs_svn_generate_cfg',
53 53 pattern='/settings/vcs/svn_generate_cfg')
54 54
55 55 config.add_route(
56 56 name='admin_settings_system',
57 57 pattern='/settings/system')
58 58 config.add_route(
59 59 name='admin_settings_system_update',
60 60 pattern='/settings/system/updates')
61 61
62 62 config.add_route(
63 63 name='admin_settings_exception_tracker',
64 64 pattern='/settings/exceptions')
65 65 config.add_route(
66 66 name='admin_settings_exception_tracker_delete_all',
67 67 pattern='/settings/exceptions/delete')
68 68 config.add_route(
69 69 name='admin_settings_exception_tracker_show',
70 70 pattern='/settings/exceptions/{exception_id}')
71 71 config.add_route(
72 72 name='admin_settings_exception_tracker_delete',
73 73 pattern='/settings/exceptions/{exception_id}/delete')
74 74
75 75 config.add_route(
76 76 name='admin_settings_sessions',
77 77 pattern='/settings/sessions')
78 78 config.add_route(
79 79 name='admin_settings_sessions_cleanup',
80 80 pattern='/settings/sessions/cleanup')
81 81
82 82 config.add_route(
83 83 name='admin_settings_process_management',
84 84 pattern='/settings/process_management')
85 85 config.add_route(
86 86 name='admin_settings_process_management_data',
87 87 pattern='/settings/process_management/data')
88 88 config.add_route(
89 89 name='admin_settings_process_management_signal',
90 90 pattern='/settings/process_management/signal')
91 91 config.add_route(
92 92 name='admin_settings_process_management_master_signal',
93 93 pattern='/settings/process_management/master_signal')
94 94
95 95 # default settings
96 96 config.add_route(
97 97 name='admin_defaults_repositories',
98 98 pattern='/defaults/repositories')
99 99 config.add_route(
100 100 name='admin_defaults_repositories_update',
101 101 pattern='/defaults/repositories/update')
102 102
103 103 # admin settings
104 104
105 105 config.add_route(
106 106 name='admin_settings',
107 107 pattern='/settings')
108 108 config.add_route(
109 109 name='admin_settings_update',
110 110 pattern='/settings/update')
111 111
112 112 config.add_route(
113 113 name='admin_settings_global',
114 114 pattern='/settings/global')
115 115 config.add_route(
116 116 name='admin_settings_global_update',
117 117 pattern='/settings/global/update')
118 118
119 119 config.add_route(
120 120 name='admin_settings_vcs',
121 121 pattern='/settings/vcs')
122 122 config.add_route(
123 123 name='admin_settings_vcs_update',
124 124 pattern='/settings/vcs/update')
125 125 config.add_route(
126 126 name='admin_settings_vcs_svn_pattern_delete',
127 127 pattern='/settings/vcs/svn_pattern_delete')
128 128
129 129 config.add_route(
130 130 name='admin_settings_mapping',
131 131 pattern='/settings/mapping')
132 132 config.add_route(
133 133 name='admin_settings_mapping_update',
134 134 pattern='/settings/mapping/update')
135 135
136 136 config.add_route(
137 137 name='admin_settings_visual',
138 138 pattern='/settings/visual')
139 139 config.add_route(
140 140 name='admin_settings_visual_update',
141 141 pattern='/settings/visual/update')
142 142
143 143
144 144 config.add_route(
145 145 name='admin_settings_issuetracker',
146 146 pattern='/settings/issue-tracker')
147 147 config.add_route(
148 148 name='admin_settings_issuetracker_update',
149 149 pattern='/settings/issue-tracker/update')
150 150 config.add_route(
151 151 name='admin_settings_issuetracker_test',
152 152 pattern='/settings/issue-tracker/test')
153 153 config.add_route(
154 154 name='admin_settings_issuetracker_delete',
155 155 pattern='/settings/issue-tracker/delete')
156 156
157 157 config.add_route(
158 158 name='admin_settings_email',
159 159 pattern='/settings/email')
160 160 config.add_route(
161 161 name='admin_settings_email_update',
162 162 pattern='/settings/email/update')
163 163
164 164 config.add_route(
165 165 name='admin_settings_hooks',
166 166 pattern='/settings/hooks')
167 167 config.add_route(
168 168 name='admin_settings_hooks_update',
169 169 pattern='/settings/hooks/update')
170 170 config.add_route(
171 171 name='admin_settings_hooks_delete',
172 172 pattern='/settings/hooks/delete')
173 173
174 174 config.add_route(
175 175 name='admin_settings_search',
176 176 pattern='/settings/search')
177 177
178 178 config.add_route(
179 179 name='admin_settings_labs',
180 180 pattern='/settings/labs')
181 181 config.add_route(
182 182 name='admin_settings_labs_update',
183 183 pattern='/settings/labs/update')
184 184
185 185 # Automation EE feature
186 186 config.add_route(
187 187 'admin_settings_automation',
188 188 pattern=ADMIN_PREFIX + '/settings/automation')
189 189
190 190 # global permissions
191 191
192 192 config.add_route(
193 193 name='admin_permissions_application',
194 194 pattern='/permissions/application')
195 195 config.add_route(
196 196 name='admin_permissions_application_update',
197 197 pattern='/permissions/application/update')
198 198
199 199 config.add_route(
200 200 name='admin_permissions_global',
201 201 pattern='/permissions/global')
202 202 config.add_route(
203 203 name='admin_permissions_global_update',
204 204 pattern='/permissions/global/update')
205 205
206 206 config.add_route(
207 207 name='admin_permissions_object',
208 208 pattern='/permissions/object')
209 209 config.add_route(
210 210 name='admin_permissions_object_update',
211 211 pattern='/permissions/object/update')
212 212
213 213 # Branch perms EE feature
214 214 config.add_route(
215 215 name='admin_permissions_branch',
216 216 pattern='/permissions/branch')
217 217
218 218 config.add_route(
219 219 name='admin_permissions_ips',
220 220 pattern='/permissions/ips')
221 221
222 222 config.add_route(
223 223 name='admin_permissions_overview',
224 224 pattern='/permissions/overview')
225 225
226 226 config.add_route(
227 227 name='admin_permissions_auth_token_access',
228 228 pattern='/permissions/auth_token_access')
229 229
230 230 config.add_route(
231 231 name='admin_permissions_ssh_keys',
232 232 pattern='/permissions/ssh_keys')
233 233 config.add_route(
234 234 name='admin_permissions_ssh_keys_data',
235 235 pattern='/permissions/ssh_keys/data')
236 236 config.add_route(
237 237 name='admin_permissions_ssh_keys_update',
238 238 pattern='/permissions/ssh_keys/update')
239 239
240 240 # users admin
241 241 config.add_route(
242 242 name='users',
243 243 pattern='/users')
244 244
245 245 config.add_route(
246 246 name='users_data',
247 247 pattern='/users_data')
248 248
249 249 config.add_route(
250 250 name='users_create',
251 251 pattern='/users/create')
252 252
253 253 config.add_route(
254 254 name='users_new',
255 255 pattern='/users/new')
256 256
257 257 # user management
258 258 config.add_route(
259 259 name='user_edit',
260 260 pattern='/users/{user_id:\d+}/edit',
261 261 user_route=True)
262 262 config.add_route(
263 263 name='user_edit_advanced',
264 264 pattern='/users/{user_id:\d+}/edit/advanced',
265 265 user_route=True)
266 266 config.add_route(
267 267 name='user_edit_global_perms',
268 268 pattern='/users/{user_id:\d+}/edit/global_permissions',
269 269 user_route=True)
270 270 config.add_route(
271 271 name='user_edit_global_perms_update',
272 272 pattern='/users/{user_id:\d+}/edit/global_permissions/update',
273 273 user_route=True)
274 274 config.add_route(
275 275 name='user_update',
276 276 pattern='/users/{user_id:\d+}/update',
277 277 user_route=True)
278 278 config.add_route(
279 279 name='user_delete',
280 280 pattern='/users/{user_id:\d+}/delete',
281 281 user_route=True)
282 282 config.add_route(
283 name='user_force_password_reset',
284 pattern='/users/{user_id:\d+}/password_reset',
283 name='user_enable_force_password_reset',
284 pattern='/users/{user_id:\d+}/password_reset_enable',
285 user_route=True)
286 config.add_route(
287 name='user_disable_force_password_reset',
288 pattern='/users/{user_id:\d+}/password_reset_disable',
285 289 user_route=True)
286 290 config.add_route(
287 291 name='user_create_personal_repo_group',
288 292 pattern='/users/{user_id:\d+}/create_repo_group',
289 293 user_route=True)
290 294
291 295 # user auth tokens
292 296 config.add_route(
293 297 name='edit_user_auth_tokens',
294 298 pattern='/users/{user_id:\d+}/edit/auth_tokens',
295 299 user_route=True)
296 300 config.add_route(
297 301 name='edit_user_auth_tokens_add',
298 302 pattern='/users/{user_id:\d+}/edit/auth_tokens/new',
299 303 user_route=True)
300 304 config.add_route(
301 305 name='edit_user_auth_tokens_delete',
302 306 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete',
303 307 user_route=True)
304 308
305 309 # user ssh keys
306 310 config.add_route(
307 311 name='edit_user_ssh_keys',
308 312 pattern='/users/{user_id:\d+}/edit/ssh_keys',
309 313 user_route=True)
310 314 config.add_route(
311 315 name='edit_user_ssh_keys_generate_keypair',
312 316 pattern='/users/{user_id:\d+}/edit/ssh_keys/generate',
313 317 user_route=True)
314 318 config.add_route(
315 319 name='edit_user_ssh_keys_add',
316 320 pattern='/users/{user_id:\d+}/edit/ssh_keys/new',
317 321 user_route=True)
318 322 config.add_route(
319 323 name='edit_user_ssh_keys_delete',
320 324 pattern='/users/{user_id:\d+}/edit/ssh_keys/delete',
321 325 user_route=True)
322 326
323 327 # user emails
324 328 config.add_route(
325 329 name='edit_user_emails',
326 330 pattern='/users/{user_id:\d+}/edit/emails',
327 331 user_route=True)
328 332 config.add_route(
329 333 name='edit_user_emails_add',
330 334 pattern='/users/{user_id:\d+}/edit/emails/new',
331 335 user_route=True)
332 336 config.add_route(
333 337 name='edit_user_emails_delete',
334 338 pattern='/users/{user_id:\d+}/edit/emails/delete',
335 339 user_route=True)
336 340
337 341 # user IPs
338 342 config.add_route(
339 343 name='edit_user_ips',
340 344 pattern='/users/{user_id:\d+}/edit/ips',
341 345 user_route=True)
342 346 config.add_route(
343 347 name='edit_user_ips_add',
344 348 pattern='/users/{user_id:\d+}/edit/ips/new',
345 349 user_route_with_default=True) # enabled for default user too
346 350 config.add_route(
347 351 name='edit_user_ips_delete',
348 352 pattern='/users/{user_id:\d+}/edit/ips/delete',
349 353 user_route_with_default=True) # enabled for default user too
350 354
351 355 # user perms
352 356 config.add_route(
353 357 name='edit_user_perms_summary',
354 358 pattern='/users/{user_id:\d+}/edit/permissions_summary',
355 359 user_route=True)
356 360 config.add_route(
357 361 name='edit_user_perms_summary_json',
358 362 pattern='/users/{user_id:\d+}/edit/permissions_summary/json',
359 363 user_route=True)
360 364
361 365 # user user groups management
362 366 config.add_route(
363 367 name='edit_user_groups_management',
364 368 pattern='/users/{user_id:\d+}/edit/groups_management',
365 369 user_route=True)
366 370
367 371 config.add_route(
368 372 name='edit_user_groups_management_updates',
369 373 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates',
370 374 user_route=True)
371 375
372 376 # user audit logs
373 377 config.add_route(
374 378 name='edit_user_audit_logs',
375 379 pattern='/users/{user_id:\d+}/edit/audit', user_route=True)
376 380
377 381 # user caches
378 382 config.add_route(
379 383 name='edit_user_caches',
380 384 pattern='/users/{user_id:\d+}/edit/caches',
381 385 user_route=True)
382 386 config.add_route(
383 387 name='edit_user_caches_update',
384 388 pattern='/users/{user_id:\d+}/edit/caches/update',
385 389 user_route=True)
386 390
387 391 # user-groups admin
388 392 config.add_route(
389 393 name='user_groups',
390 394 pattern='/user_groups')
391 395
392 396 config.add_route(
393 397 name='user_groups_data',
394 398 pattern='/user_groups_data')
395 399
396 400 config.add_route(
397 401 name='user_groups_new',
398 402 pattern='/user_groups/new')
399 403
400 404 config.add_route(
401 405 name='user_groups_create',
402 406 pattern='/user_groups/create')
403 407
404 408 # repos admin
405 409 config.add_route(
406 410 name='repos',
407 411 pattern='/repos')
408 412
409 413 config.add_route(
410 414 name='repo_new',
411 415 pattern='/repos/new')
412 416
413 417 config.add_route(
414 418 name='repo_create',
415 419 pattern='/repos/create')
416 420
417 421 # repo groups admin
418 422 config.add_route(
419 423 name='repo_groups',
420 424 pattern='/repo_groups')
421 425
422 426 config.add_route(
423 427 name='repo_group_new',
424 428 pattern='/repo_group/new')
425 429
426 430 config.add_route(
427 431 name='repo_group_create',
428 432 pattern='/repo_group/create')
429 433
430 434
431 435 def includeme(config):
432 436 from rhodecode.apps._base.navigation import includeme as nav_includeme
433 437
434 438 # Create admin navigation registry and add it to the pyramid registry.
435 439 nav_includeme(config)
436 440
437 441 # main admin routes
438 442 config.add_route(name='admin_home', pattern=ADMIN_PREFIX)
439 443 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
440 444
441 445 # Scan module for configuration decorators.
442 446 config.scan('.views', ignore='.tests')
@@ -1,783 +1,781 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import pytest
22 22 from sqlalchemy.orm.exc import NoResultFound
23 23
24 24 from rhodecode.lib import auth
25 25 from rhodecode.lib import helpers as h
26 26 from rhodecode.model.db import User, UserApiKeys, UserEmailMap, Repository
27 27 from rhodecode.model.meta import Session
28 28 from rhodecode.model.user import UserModel
29 29
30 30 from rhodecode.tests import (
31 31 TestController, TEST_USER_REGULAR_LOGIN, assert_session_flash)
32 32 from rhodecode.tests.fixture import Fixture
33 33
34 34 fixture = Fixture()
35 35
36 36
37 37 def route_path(name, params=None, **kwargs):
38 38 import urllib
39 39 from rhodecode.apps._base import ADMIN_PREFIX
40 40
41 41 base_url = {
42 42 'users':
43 43 ADMIN_PREFIX + '/users',
44 44 'users_data':
45 45 ADMIN_PREFIX + '/users_data',
46 46 'users_create':
47 47 ADMIN_PREFIX + '/users/create',
48 48 'users_new':
49 49 ADMIN_PREFIX + '/users/new',
50 50 'user_edit':
51 51 ADMIN_PREFIX + '/users/{user_id}/edit',
52 52 'user_edit_advanced':
53 53 ADMIN_PREFIX + '/users/{user_id}/edit/advanced',
54 54 'user_edit_global_perms':
55 55 ADMIN_PREFIX + '/users/{user_id}/edit/global_permissions',
56 56 'user_edit_global_perms_update':
57 57 ADMIN_PREFIX + '/users/{user_id}/edit/global_permissions/update',
58 58 'user_update':
59 59 ADMIN_PREFIX + '/users/{user_id}/update',
60 60 'user_delete':
61 61 ADMIN_PREFIX + '/users/{user_id}/delete',
62 'user_force_password_reset':
63 ADMIN_PREFIX + '/users/{user_id}/password_reset',
64 62 'user_create_personal_repo_group':
65 63 ADMIN_PREFIX + '/users/{user_id}/create_repo_group',
66 64
67 65 'edit_user_auth_tokens':
68 66 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens',
69 67 'edit_user_auth_tokens_add':
70 68 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens/new',
71 69 'edit_user_auth_tokens_delete':
72 70 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens/delete',
73 71
74 72 'edit_user_emails':
75 73 ADMIN_PREFIX + '/users/{user_id}/edit/emails',
76 74 'edit_user_emails_add':
77 75 ADMIN_PREFIX + '/users/{user_id}/edit/emails/new',
78 76 'edit_user_emails_delete':
79 77 ADMIN_PREFIX + '/users/{user_id}/edit/emails/delete',
80 78
81 79 'edit_user_ips':
82 80 ADMIN_PREFIX + '/users/{user_id}/edit/ips',
83 81 'edit_user_ips_add':
84 82 ADMIN_PREFIX + '/users/{user_id}/edit/ips/new',
85 83 'edit_user_ips_delete':
86 84 ADMIN_PREFIX + '/users/{user_id}/edit/ips/delete',
87 85
88 86 'edit_user_perms_summary':
89 87 ADMIN_PREFIX + '/users/{user_id}/edit/permissions_summary',
90 88 'edit_user_perms_summary_json':
91 89 ADMIN_PREFIX + '/users/{user_id}/edit/permissions_summary/json',
92 90
93 91 'edit_user_audit_logs':
94 92 ADMIN_PREFIX + '/users/{user_id}/edit/audit',
95 93
96 94 }[name].format(**kwargs)
97 95
98 96 if params:
99 97 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
100 98 return base_url
101 99
102 100
103 101 class TestAdminUsersView(TestController):
104 102
105 103 def test_show_users(self):
106 104 self.log_user()
107 105 self.app.get(route_path('users'))
108 106
109 107 def test_show_users_data(self, xhr_header):
110 108 self.log_user()
111 109 response = self.app.get(route_path(
112 110 'users_data'), extra_environ=xhr_header)
113 111
114 112 all_users = User.query().filter(
115 113 User.username != User.DEFAULT_USER).count()
116 114 assert response.json['recordsTotal'] == all_users
117 115
118 116 def test_show_users_data_filtered(self, xhr_header):
119 117 self.log_user()
120 118 response = self.app.get(route_path(
121 119 'users_data', params={'search[value]': 'empty_search'}),
122 120 extra_environ=xhr_header)
123 121
124 122 all_users = User.query().filter(
125 123 User.username != User.DEFAULT_USER).count()
126 124 assert response.json['recordsTotal'] == all_users
127 125 assert response.json['recordsFiltered'] == 0
128 126
129 127 def test_auth_tokens_default_user(self):
130 128 self.log_user()
131 129 user = User.get_default_user()
132 130 response = self.app.get(
133 131 route_path('edit_user_auth_tokens', user_id=user.user_id),
134 132 status=302)
135 133
136 134 def test_auth_tokens(self):
137 135 self.log_user()
138 136
139 137 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
140 138 user_id = user.user_id
141 139 auth_tokens = user.auth_tokens
142 140 response = self.app.get(
143 141 route_path('edit_user_auth_tokens', user_id=user_id))
144 142 for token in auth_tokens:
145 143 response.mustcontain(token)
146 144 response.mustcontain('never')
147 145
148 146 @pytest.mark.parametrize("desc, lifetime", [
149 147 ('forever', -1),
150 148 ('5mins', 60*5),
151 149 ('30days', 60*60*24*30),
152 150 ])
153 151 def test_add_auth_token(self, desc, lifetime, user_util):
154 152 self.log_user()
155 153 user = user_util.create_user()
156 154 user_id = user.user_id
157 155
158 156 response = self.app.post(
159 157 route_path('edit_user_auth_tokens_add', user_id=user_id),
160 158 {'description': desc, 'lifetime': lifetime,
161 159 'csrf_token': self.csrf_token})
162 160 assert_session_flash(response, 'Auth token successfully created')
163 161
164 162 response = response.follow()
165 163 user = User.get(user_id)
166 164 for auth_token in user.auth_tokens:
167 165 response.mustcontain(auth_token)
168 166
169 167 def test_delete_auth_token(self, user_util):
170 168 self.log_user()
171 169 user = user_util.create_user()
172 170 user_id = user.user_id
173 171 keys = user.auth_tokens
174 172 assert 2 == len(keys)
175 173
176 174 response = self.app.post(
177 175 route_path('edit_user_auth_tokens_add', user_id=user_id),
178 176 {'description': 'desc', 'lifetime': -1,
179 177 'csrf_token': self.csrf_token})
180 178 assert_session_flash(response, 'Auth token successfully created')
181 179 response.follow()
182 180
183 181 # now delete our key
184 182 keys = UserApiKeys.query().filter(UserApiKeys.user_id == user_id).all()
185 183 assert 3 == len(keys)
186 184
187 185 response = self.app.post(
188 186 route_path('edit_user_auth_tokens_delete', user_id=user_id),
189 187 {'del_auth_token': keys[0].user_api_key_id,
190 188 'csrf_token': self.csrf_token})
191 189
192 190 assert_session_flash(response, 'Auth token successfully deleted')
193 191 keys = UserApiKeys.query().filter(UserApiKeys.user_id == user_id).all()
194 192 assert 2 == len(keys)
195 193
196 194 def test_ips(self):
197 195 self.log_user()
198 196 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
199 197 response = self.app.get(route_path('edit_user_ips', user_id=user.user_id))
200 198 response.mustcontain('All IP addresses are allowed')
201 199
202 200 @pytest.mark.parametrize("test_name, ip, ip_range, failure", [
203 201 ('127/24', '127.0.0.1/24', '127.0.0.0 - 127.0.0.255', False),
204 202 ('10/32', '10.0.0.10/32', '10.0.0.10 - 10.0.0.10', False),
205 203 ('0/16', '0.0.0.0/16', '0.0.0.0 - 0.0.255.255', False),
206 204 ('0/8', '0.0.0.0/8', '0.0.0.0 - 0.255.255.255', False),
207 205 ('127_bad_mask', '127.0.0.1/99', '127.0.0.1 - 127.0.0.1', True),
208 206 ('127_bad_ip', 'foobar', 'foobar', True),
209 207 ])
210 208 def test_ips_add(self, user_util, test_name, ip, ip_range, failure):
211 209 self.log_user()
212 210 user = user_util.create_user(username=test_name)
213 211 user_id = user.user_id
214 212
215 213 response = self.app.post(
216 214 route_path('edit_user_ips_add', user_id=user_id),
217 215 params={'new_ip': ip, 'csrf_token': self.csrf_token})
218 216
219 217 if failure:
220 218 assert_session_flash(
221 219 response, 'Please enter a valid IPv4 or IpV6 address')
222 220 response = self.app.get(route_path('edit_user_ips', user_id=user_id))
223 221
224 222 response.mustcontain(no=[ip])
225 223 response.mustcontain(no=[ip_range])
226 224
227 225 else:
228 226 response = self.app.get(route_path('edit_user_ips', user_id=user_id))
229 227 response.mustcontain(ip)
230 228 response.mustcontain(ip_range)
231 229
232 230 def test_ips_delete(self, user_util):
233 231 self.log_user()
234 232 user = user_util.create_user()
235 233 user_id = user.user_id
236 234 ip = '127.0.0.1/32'
237 235 ip_range = '127.0.0.1 - 127.0.0.1'
238 236 new_ip = UserModel().add_extra_ip(user_id, ip)
239 237 Session().commit()
240 238 new_ip_id = new_ip.ip_id
241 239
242 240 response = self.app.get(route_path('edit_user_ips', user_id=user_id))
243 241 response.mustcontain(ip)
244 242 response.mustcontain(ip_range)
245 243
246 244 self.app.post(
247 245 route_path('edit_user_ips_delete', user_id=user_id),
248 246 params={'del_ip_id': new_ip_id, 'csrf_token': self.csrf_token})
249 247
250 248 response = self.app.get(route_path('edit_user_ips', user_id=user_id))
251 249 response.mustcontain('All IP addresses are allowed')
252 250 response.mustcontain(no=[ip])
253 251 response.mustcontain(no=[ip_range])
254 252
255 253 def test_emails(self):
256 254 self.log_user()
257 255 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
258 256 response = self.app.get(
259 257 route_path('edit_user_emails', user_id=user.user_id))
260 258 response.mustcontain('No additional emails specified')
261 259
262 260 def test_emails_add(self, user_util):
263 261 self.log_user()
264 262 user = user_util.create_user()
265 263 user_id = user.user_id
266 264
267 265 self.app.post(
268 266 route_path('edit_user_emails_add', user_id=user_id),
269 267 params={'new_email': 'example@rhodecode.com',
270 268 'csrf_token': self.csrf_token})
271 269
272 270 response = self.app.get(
273 271 route_path('edit_user_emails', user_id=user_id))
274 272 response.mustcontain('example@rhodecode.com')
275 273
276 274 def test_emails_add_existing_email(self, user_util, user_regular):
277 275 existing_email = user_regular.email
278 276
279 277 self.log_user()
280 278 user = user_util.create_user()
281 279 user_id = user.user_id
282 280
283 281 response = self.app.post(
284 282 route_path('edit_user_emails_add', user_id=user_id),
285 283 params={'new_email': existing_email,
286 284 'csrf_token': self.csrf_token})
287 285 assert_session_flash(
288 286 response, 'This e-mail address is already taken')
289 287
290 288 response = self.app.get(
291 289 route_path('edit_user_emails', user_id=user_id))
292 290 response.mustcontain(no=[existing_email])
293 291
294 292 def test_emails_delete(self, user_util):
295 293 self.log_user()
296 294 user = user_util.create_user()
297 295 user_id = user.user_id
298 296
299 297 self.app.post(
300 298 route_path('edit_user_emails_add', user_id=user_id),
301 299 params={'new_email': 'example@rhodecode.com',
302 300 'csrf_token': self.csrf_token})
303 301
304 302 response = self.app.get(
305 303 route_path('edit_user_emails', user_id=user_id))
306 304 response.mustcontain('example@rhodecode.com')
307 305
308 306 user_email = UserEmailMap.query()\
309 307 .filter(UserEmailMap.email == 'example@rhodecode.com') \
310 308 .filter(UserEmailMap.user_id == user_id)\
311 309 .one()
312 310
313 311 del_email_id = user_email.email_id
314 312 self.app.post(
315 313 route_path('edit_user_emails_delete', user_id=user_id),
316 314 params={'del_email_id': del_email_id,
317 315 'csrf_token': self.csrf_token})
318 316
319 317 response = self.app.get(
320 318 route_path('edit_user_emails', user_id=user_id))
321 319 response.mustcontain(no=['example@rhodecode.com'])
322 320
323 321
324 322 def test_create(self, request, xhr_header):
325 323 self.log_user()
326 324 username = 'newtestuser'
327 325 password = 'test12'
328 326 password_confirmation = password
329 327 name = 'name'
330 328 lastname = 'lastname'
331 329 email = 'mail@mail.com'
332 330
333 331 self.app.get(route_path('users_new'))
334 332
335 333 response = self.app.post(route_path('users_create'), params={
336 334 'username': username,
337 335 'password': password,
338 336 'password_confirmation': password_confirmation,
339 337 'firstname': name,
340 338 'active': True,
341 339 'lastname': lastname,
342 340 'extern_name': 'rhodecode',
343 341 'extern_type': 'rhodecode',
344 342 'email': email,
345 343 'csrf_token': self.csrf_token,
346 344 })
347 345 user_link = h.link_to(
348 346 username,
349 347 route_path(
350 348 'user_edit', user_id=User.get_by_username(username).user_id))
351 349 assert_session_flash(response, 'Created user %s' % (user_link,))
352 350
353 351 @request.addfinalizer
354 352 def cleanup():
355 353 fixture.destroy_user(username)
356 354 Session().commit()
357 355
358 356 new_user = User.query().filter(User.username == username).one()
359 357
360 358 assert new_user.username == username
361 359 assert auth.check_password(password, new_user.password)
362 360 assert new_user.name == name
363 361 assert new_user.lastname == lastname
364 362 assert new_user.email == email
365 363
366 364 response = self.app.get(route_path('users_data'),
367 365 extra_environ=xhr_header)
368 366 response.mustcontain(username)
369 367
370 368 def test_create_err(self):
371 369 self.log_user()
372 370 username = 'new_user'
373 371 password = ''
374 372 name = 'name'
375 373 lastname = 'lastname'
376 374 email = 'errmail.com'
377 375
378 376 self.app.get(route_path('users_new'))
379 377
380 378 response = self.app.post(route_path('users_create'), params={
381 379 'username': username,
382 380 'password': password,
383 381 'name': name,
384 382 'active': False,
385 383 'lastname': lastname,
386 384 'email': email,
387 385 'csrf_token': self.csrf_token,
388 386 })
389 387
390 388 msg = u'Username "%(username)s" is forbidden'
391 389 msg = h.html_escape(msg % {'username': 'new_user'})
392 390 response.mustcontain('<span class="error-message">%s</span>' % msg)
393 391 response.mustcontain(
394 392 '<span class="error-message">Please enter a value</span>')
395 393 response.mustcontain(
396 394 '<span class="error-message">An email address must contain a'
397 395 ' single @</span>')
398 396
399 397 def get_user():
400 398 Session().query(User).filter(User.username == username).one()
401 399
402 400 with pytest.raises(NoResultFound):
403 401 get_user()
404 402
405 403 def test_new(self):
406 404 self.log_user()
407 405 self.app.get(route_path('users_new'))
408 406
409 407 @pytest.mark.parametrize("name, attrs", [
410 408 ('firstname', {'firstname': 'new_username'}),
411 409 ('lastname', {'lastname': 'new_username'}),
412 410 ('admin', {'admin': True}),
413 411 ('admin', {'admin': False}),
414 412 ('extern_type', {'extern_type': 'ldap'}),
415 413 ('extern_type', {'extern_type': None}),
416 414 ('extern_name', {'extern_name': 'test'}),
417 415 ('extern_name', {'extern_name': None}),
418 416 ('active', {'active': False}),
419 417 ('active', {'active': True}),
420 418 ('email', {'email': 'some@email.com'}),
421 419 ('language', {'language': 'de'}),
422 420 ('language', {'language': 'en'}),
423 421 # ('new_password', {'new_password': 'foobar123',
424 422 # 'password_confirmation': 'foobar123'})
425 423 ])
426 424 def test_update(self, name, attrs, user_util):
427 425 self.log_user()
428 426 usr = user_util.create_user(
429 427 password='qweqwe',
430 428 email='testme@rhodecode.org',
431 429 extern_type='rhodecode',
432 430 extern_name='xxx',
433 431 )
434 432 user_id = usr.user_id
435 433 Session().commit()
436 434
437 435 params = usr.get_api_data()
438 436 cur_lang = params['language'] or 'en'
439 437 params.update({
440 438 'password_confirmation': '',
441 439 'new_password': '',
442 440 'language': cur_lang,
443 441 'csrf_token': self.csrf_token,
444 442 })
445 443 params.update({'new_password': ''})
446 444 params.update(attrs)
447 445 if name == 'email':
448 446 params['emails'] = [attrs['email']]
449 447 elif name == 'extern_type':
450 448 # cannot update this via form, expected value is original one
451 449 params['extern_type'] = "rhodecode"
452 450 elif name == 'extern_name':
453 451 # cannot update this via form, expected value is original one
454 452 params['extern_name'] = 'xxx'
455 453 # special case since this user is not
456 454 # logged in yet his data is not filled
457 455 # so we use creation data
458 456
459 457 response = self.app.post(
460 458 route_path('user_update', user_id=usr.user_id), params)
461 459 assert response.status_int == 302
462 460 assert_session_flash(response, 'User updated successfully')
463 461
464 462 updated_user = User.get(user_id)
465 463 updated_params = updated_user.get_api_data()
466 464 updated_params.update({'password_confirmation': ''})
467 465 updated_params.update({'new_password': ''})
468 466
469 467 del params['csrf_token']
470 468 assert params == updated_params
471 469
472 470 def test_update_and_migrate_password(
473 471 self, autologin_user, real_crypto_backend, user_util):
474 472
475 473 user = user_util.create_user()
476 474 temp_user = user.username
477 475 user.password = auth._RhodeCodeCryptoSha256().hash_create(
478 476 b'test123')
479 477 Session().add(user)
480 478 Session().commit()
481 479
482 480 params = user.get_api_data()
483 481
484 482 params.update({
485 483 'password_confirmation': 'qweqwe123',
486 484 'new_password': 'qweqwe123',
487 485 'language': 'en',
488 486 'csrf_token': autologin_user.csrf_token,
489 487 })
490 488
491 489 response = self.app.post(
492 490 route_path('user_update', user_id=user.user_id), params)
493 491 assert response.status_int == 302
494 492 assert_session_flash(response, 'User updated successfully')
495 493
496 494 # new password should be bcrypted, after log-in and transfer
497 495 user = User.get_by_username(temp_user)
498 496 assert user.password.startswith('$')
499 497
500 498 updated_user = User.get_by_username(temp_user)
501 499 updated_params = updated_user.get_api_data()
502 500 updated_params.update({'password_confirmation': 'qweqwe123'})
503 501 updated_params.update({'new_password': 'qweqwe123'})
504 502
505 503 del params['csrf_token']
506 504 assert params == updated_params
507 505
508 506 def test_delete(self):
509 507 self.log_user()
510 508 username = 'newtestuserdeleteme'
511 509
512 510 fixture.create_user(name=username)
513 511
514 512 new_user = Session().query(User)\
515 513 .filter(User.username == username).one()
516 514 response = self.app.post(
517 515 route_path('user_delete', user_id=new_user.user_id),
518 516 params={'csrf_token': self.csrf_token})
519 517
520 518 assert_session_flash(response, 'Successfully deleted user')
521 519
522 520 def test_delete_owner_of_repository(self, request, user_util):
523 521 self.log_user()
524 522 obj_name = 'test_repo'
525 523 usr = user_util.create_user()
526 524 username = usr.username
527 525 fixture.create_repo(obj_name, cur_user=usr.username)
528 526
529 527 new_user = Session().query(User)\
530 528 .filter(User.username == username).one()
531 529 response = self.app.post(
532 530 route_path('user_delete', user_id=new_user.user_id),
533 531 params={'csrf_token': self.csrf_token})
534 532
535 533 msg = 'user "%s" still owns 1 repositories and cannot be removed. ' \
536 534 'Switch owners or remove those repositories:%s' % (username,
537 535 obj_name)
538 536 assert_session_flash(response, msg)
539 537 fixture.destroy_repo(obj_name)
540 538
541 539 def test_delete_owner_of_repository_detaching(self, request, user_util):
542 540 self.log_user()
543 541 obj_name = 'test_repo'
544 542 usr = user_util.create_user(auto_cleanup=False)
545 543 username = usr.username
546 544 fixture.create_repo(obj_name, cur_user=usr.username)
547 545
548 546 new_user = Session().query(User)\
549 547 .filter(User.username == username).one()
550 548 response = self.app.post(
551 549 route_path('user_delete', user_id=new_user.user_id),
552 550 params={'user_repos': 'detach', 'csrf_token': self.csrf_token})
553 551
554 552 msg = 'Detached 1 repositories'
555 553 assert_session_flash(response, msg)
556 554 fixture.destroy_repo(obj_name)
557 555
558 556 def test_delete_owner_of_repository_deleting(self, request, user_util):
559 557 self.log_user()
560 558 obj_name = 'test_repo'
561 559 usr = user_util.create_user(auto_cleanup=False)
562 560 username = usr.username
563 561 fixture.create_repo(obj_name, cur_user=usr.username)
564 562
565 563 new_user = Session().query(User)\
566 564 .filter(User.username == username).one()
567 565 response = self.app.post(
568 566 route_path('user_delete', user_id=new_user.user_id),
569 567 params={'user_repos': 'delete', 'csrf_token': self.csrf_token})
570 568
571 569 msg = 'Deleted 1 repositories'
572 570 assert_session_flash(response, msg)
573 571
574 572 def test_delete_owner_of_repository_group(self, request, user_util):
575 573 self.log_user()
576 574 obj_name = 'test_group'
577 575 usr = user_util.create_user()
578 576 username = usr.username
579 577 fixture.create_repo_group(obj_name, cur_user=usr.username)
580 578
581 579 new_user = Session().query(User)\
582 580 .filter(User.username == username).one()
583 581 response = self.app.post(
584 582 route_path('user_delete', user_id=new_user.user_id),
585 583 params={'csrf_token': self.csrf_token})
586 584
587 585 msg = 'user "%s" still owns 1 repository groups and cannot be removed. ' \
588 586 'Switch owners or remove those repository groups:%s' % (username,
589 587 obj_name)
590 588 assert_session_flash(response, msg)
591 589 fixture.destroy_repo_group(obj_name)
592 590
593 591 def test_delete_owner_of_repository_group_detaching(self, request, user_util):
594 592 self.log_user()
595 593 obj_name = 'test_group'
596 594 usr = user_util.create_user(auto_cleanup=False)
597 595 username = usr.username
598 596 fixture.create_repo_group(obj_name, cur_user=usr.username)
599 597
600 598 new_user = Session().query(User)\
601 599 .filter(User.username == username).one()
602 600 response = self.app.post(
603 601 route_path('user_delete', user_id=new_user.user_id),
604 602 params={'user_repo_groups': 'delete', 'csrf_token': self.csrf_token})
605 603
606 604 msg = 'Deleted 1 repository groups'
607 605 assert_session_flash(response, msg)
608 606
609 607 def test_delete_owner_of_repository_group_deleting(self, request, user_util):
610 608 self.log_user()
611 609 obj_name = 'test_group'
612 610 usr = user_util.create_user(auto_cleanup=False)
613 611 username = usr.username
614 612 fixture.create_repo_group(obj_name, cur_user=usr.username)
615 613
616 614 new_user = Session().query(User)\
617 615 .filter(User.username == username).one()
618 616 response = self.app.post(
619 617 route_path('user_delete', user_id=new_user.user_id),
620 618 params={'user_repo_groups': 'detach', 'csrf_token': self.csrf_token})
621 619
622 620 msg = 'Detached 1 repository groups'
623 621 assert_session_flash(response, msg)
624 622 fixture.destroy_repo_group(obj_name)
625 623
626 624 def test_delete_owner_of_user_group(self, request, user_util):
627 625 self.log_user()
628 626 obj_name = 'test_user_group'
629 627 usr = user_util.create_user()
630 628 username = usr.username
631 629 fixture.create_user_group(obj_name, cur_user=usr.username)
632 630
633 631 new_user = Session().query(User)\
634 632 .filter(User.username == username).one()
635 633 response = self.app.post(
636 634 route_path('user_delete', user_id=new_user.user_id),
637 635 params={'csrf_token': self.csrf_token})
638 636
639 637 msg = 'user "%s" still owns 1 user groups and cannot be removed. ' \
640 638 'Switch owners or remove those user groups:%s' % (username,
641 639 obj_name)
642 640 assert_session_flash(response, msg)
643 641 fixture.destroy_user_group(obj_name)
644 642
645 643 def test_delete_owner_of_user_group_detaching(self, request, user_util):
646 644 self.log_user()
647 645 obj_name = 'test_user_group'
648 646 usr = user_util.create_user(auto_cleanup=False)
649 647 username = usr.username
650 648 fixture.create_user_group(obj_name, cur_user=usr.username)
651 649
652 650 new_user = Session().query(User)\
653 651 .filter(User.username == username).one()
654 652 try:
655 653 response = self.app.post(
656 654 route_path('user_delete', user_id=new_user.user_id),
657 655 params={'user_user_groups': 'detach',
658 656 'csrf_token': self.csrf_token})
659 657
660 658 msg = 'Detached 1 user groups'
661 659 assert_session_flash(response, msg)
662 660 finally:
663 661 fixture.destroy_user_group(obj_name)
664 662
665 663 def test_delete_owner_of_user_group_deleting(self, request, user_util):
666 664 self.log_user()
667 665 obj_name = 'test_user_group'
668 666 usr = user_util.create_user(auto_cleanup=False)
669 667 username = usr.username
670 668 fixture.create_user_group(obj_name, cur_user=usr.username)
671 669
672 670 new_user = Session().query(User)\
673 671 .filter(User.username == username).one()
674 672 response = self.app.post(
675 673 route_path('user_delete', user_id=new_user.user_id),
676 674 params={'user_user_groups': 'delete', 'csrf_token': self.csrf_token})
677 675
678 676 msg = 'Deleted 1 user groups'
679 677 assert_session_flash(response, msg)
680 678
681 679 def test_edit(self, user_util):
682 680 self.log_user()
683 681 user = user_util.create_user()
684 682 self.app.get(route_path('user_edit', user_id=user.user_id))
685 683
686 684 def test_edit_default_user_redirect(self):
687 685 self.log_user()
688 686 user = User.get_default_user()
689 687 self.app.get(route_path('user_edit', user_id=user.user_id), status=302)
690 688
691 689 @pytest.mark.parametrize(
692 690 'repo_create, repo_create_write, user_group_create, repo_group_create,'
693 691 'fork_create, inherit_default_permissions, expect_error,'
694 692 'expect_form_error', [
695 693 ('hg.create.none', 'hg.create.write_on_repogroup.false',
696 694 'hg.usergroup.create.false', 'hg.repogroup.create.false',
697 695 'hg.fork.none', 'hg.inherit_default_perms.false', False, False),
698 696 ('hg.create.repository', 'hg.create.write_on_repogroup.false',
699 697 'hg.usergroup.create.false', 'hg.repogroup.create.false',
700 698 'hg.fork.none', 'hg.inherit_default_perms.false', False, False),
701 699 ('hg.create.repository', 'hg.create.write_on_repogroup.true',
702 700 'hg.usergroup.create.true', 'hg.repogroup.create.true',
703 701 'hg.fork.repository', 'hg.inherit_default_perms.false', False,
704 702 False),
705 703 ('hg.create.XXX', 'hg.create.write_on_repogroup.true',
706 704 'hg.usergroup.create.true', 'hg.repogroup.create.true',
707 705 'hg.fork.repository', 'hg.inherit_default_perms.false', False,
708 706 True),
709 707 ('', '', '', '', '', '', True, False),
710 708 ])
711 709 def test_global_perms_on_user(
712 710 self, repo_create, repo_create_write, user_group_create,
713 711 repo_group_create, fork_create, expect_error, expect_form_error,
714 712 inherit_default_permissions, user_util):
715 713 self.log_user()
716 714 user = user_util.create_user()
717 715 uid = user.user_id
718 716
719 717 # ENABLE REPO CREATE ON A GROUP
720 718 perm_params = {
721 719 'inherit_default_permissions': False,
722 720 'default_repo_create': repo_create,
723 721 'default_repo_create_on_write': repo_create_write,
724 722 'default_user_group_create': user_group_create,
725 723 'default_repo_group_create': repo_group_create,
726 724 'default_fork_create': fork_create,
727 725 'default_inherit_default_permissions': inherit_default_permissions,
728 726 'csrf_token': self.csrf_token,
729 727 }
730 728 response = self.app.post(
731 729 route_path('user_edit_global_perms_update', user_id=uid),
732 730 params=perm_params)
733 731
734 732 if expect_form_error:
735 733 assert response.status_int == 200
736 734 response.mustcontain('Value must be one of')
737 735 else:
738 736 if expect_error:
739 737 msg = 'An error occurred during permissions saving'
740 738 else:
741 739 msg = 'User global permissions updated successfully'
742 740 ug = User.get(uid)
743 741 del perm_params['inherit_default_permissions']
744 742 del perm_params['csrf_token']
745 743 assert perm_params == ug.get_default_perms()
746 744 assert_session_flash(response, msg)
747 745
748 746 def test_global_permissions_initial_values(self, user_util):
749 747 self.log_user()
750 748 user = user_util.create_user()
751 749 uid = user.user_id
752 750 response = self.app.get(
753 751 route_path('user_edit_global_perms', user_id=uid))
754 752 default_user = User.get_default_user()
755 753 default_permissions = default_user.get_default_perms()
756 754 assert_response = response.assert_response()
757 755 expected_permissions = (
758 756 'default_repo_create', 'default_repo_create_on_write',
759 757 'default_fork_create', 'default_repo_group_create',
760 758 'default_user_group_create', 'default_inherit_default_permissions')
761 759 for permission in expected_permissions:
762 760 css_selector = '[name={}][checked=checked]'.format(permission)
763 761 element = assert_response.get_element(css_selector)
764 762 assert element.value == default_permissions[permission]
765 763
766 764 def test_perms_summary_page(self):
767 765 user = self.log_user()
768 766 response = self.app.get(
769 767 route_path('edit_user_perms_summary', user_id=user['user_id']))
770 768 for repo in Repository.query().all():
771 769 response.mustcontain(repo.repo_name)
772 770
773 771 def test_perms_summary_page_json(self):
774 772 user = self.log_user()
775 773 response = self.app.get(
776 774 route_path('edit_user_perms_summary_json', user_id=user['user_id']))
777 775 for repo in Repository.query().all():
778 776 response.mustcontain(repo.repo_name)
779 777
780 778 def test_audit_log_page(self):
781 779 user = self.log_user()
782 780 self.app.get(
783 781 route_path('edit_user_audit_logs', user_id=user['user_id']))
@@ -1,1242 +1,1261 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import datetime
23 23 import formencode
24 24 import formencode.htmlfill
25 25
26 26 from pyramid.httpexceptions import HTTPFound
27 27 from pyramid.view import view_config
28 28 from pyramid.renderers import render
29 29 from pyramid.response import Response
30 30
31 31 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
32 32 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
33 33 from rhodecode.authentication.plugins import auth_rhodecode
34 34 from rhodecode.events import trigger
35 35 from rhodecode.model.db import true
36 36
37 37 from rhodecode.lib import audit_logger, rc_cache
38 38 from rhodecode.lib.exceptions import (
39 39 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
40 40 UserOwnsUserGroupsException, DefaultUserException)
41 41 from rhodecode.lib.ext_json import json
42 42 from rhodecode.lib.auth import (
43 43 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
44 44 from rhodecode.lib import helpers as h
45 45 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
46 46 from rhodecode.model.auth_token import AuthTokenModel
47 47 from rhodecode.model.forms import (
48 48 UserForm, UserIndividualPermissionsForm, UserPermissionsForm,
49 49 UserExtraEmailForm, UserExtraIpForm)
50 50 from rhodecode.model.permission import PermissionModel
51 51 from rhodecode.model.repo_group import RepoGroupModel
52 52 from rhodecode.model.ssh_key import SshKeyModel
53 53 from rhodecode.model.user import UserModel
54 54 from rhodecode.model.user_group import UserGroupModel
55 55 from rhodecode.model.db import (
56 56 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
57 57 UserApiKeys, UserSshKeys, RepoGroup)
58 58 from rhodecode.model.meta import Session
59 59
60 60 log = logging.getLogger(__name__)
61 61
62 62
63 63 class AdminUsersView(BaseAppView, DataGridAppView):
64 64
65 65 def load_default_context(self):
66 66 c = self._get_local_tmpl_context()
67 67 return c
68 68
69 69 @LoginRequired()
70 70 @HasPermissionAllDecorator('hg.admin')
71 71 @view_config(
72 72 route_name='users', request_method='GET',
73 73 renderer='rhodecode:templates/admin/users/users.mako')
74 74 def users_list(self):
75 75 c = self.load_default_context()
76 76 return self._get_template_context(c)
77 77
78 78 @LoginRequired()
79 79 @HasPermissionAllDecorator('hg.admin')
80 80 @view_config(
81 81 # renderer defined below
82 82 route_name='users_data', request_method='GET',
83 83 renderer='json_ext', xhr=True)
84 84 def users_list_data(self):
85 85 self.load_default_context()
86 86 column_map = {
87 87 'first_name': 'name',
88 88 'last_name': 'lastname',
89 89 }
90 90 draw, start, limit = self._extract_chunk(self.request)
91 91 search_q, order_by, order_dir = self._extract_ordering(
92 92 self.request, column_map=column_map)
93 93 _render = self.request.get_partial_renderer(
94 94 'rhodecode:templates/data_table/_dt_elements.mako')
95 95
96 96 def user_actions(user_id, username):
97 97 return _render("user_actions", user_id, username)
98 98
99 99 users_data_total_count = User.query()\
100 100 .filter(User.username != User.DEFAULT_USER) \
101 101 .count()
102 102
103 103 users_data_total_inactive_count = User.query()\
104 104 .filter(User.username != User.DEFAULT_USER) \
105 105 .filter(User.active != true())\
106 106 .count()
107 107
108 108 # json generate
109 109 base_q = User.query().filter(User.username != User.DEFAULT_USER)
110 110 base_inactive_q = base_q.filter(User.active != true())
111 111
112 112 if search_q:
113 113 like_expression = u'%{}%'.format(safe_unicode(search_q))
114 114 base_q = base_q.filter(or_(
115 115 User.username.ilike(like_expression),
116 116 User._email.ilike(like_expression),
117 117 User.name.ilike(like_expression),
118 118 User.lastname.ilike(like_expression),
119 119 ))
120 120 base_inactive_q = base_q.filter(User.active != true())
121 121
122 122 users_data_total_filtered_count = base_q.count()
123 123 users_data_total_filtered_inactive_count = base_inactive_q.count()
124 124
125 125 sort_col = getattr(User, order_by, None)
126 126 if sort_col:
127 127 if order_dir == 'asc':
128 128 # handle null values properly to order by NULL last
129 129 if order_by in ['last_activity']:
130 130 sort_col = coalesce(sort_col, datetime.date.max)
131 131 sort_col = sort_col.asc()
132 132 else:
133 133 # handle null values properly to order by NULL last
134 134 if order_by in ['last_activity']:
135 135 sort_col = coalesce(sort_col, datetime.date.min)
136 136 sort_col = sort_col.desc()
137 137
138 138 base_q = base_q.order_by(sort_col)
139 139 base_q = base_q.offset(start).limit(limit)
140 140
141 141 users_list = base_q.all()
142 142
143 143 users_data = []
144 144 for user in users_list:
145 145 users_data.append({
146 146 "username": h.gravatar_with_user(self.request, user.username),
147 147 "email": user.email,
148 148 "first_name": user.first_name,
149 149 "last_name": user.last_name,
150 150 "last_login": h.format_date(user.last_login),
151 151 "last_activity": h.format_date(user.last_activity),
152 152 "active": h.bool2icon(user.active),
153 153 "active_raw": user.active,
154 154 "admin": h.bool2icon(user.admin),
155 155 "extern_type": user.extern_type,
156 156 "extern_name": user.extern_name,
157 157 "action": user_actions(user.user_id, user.username),
158 158 })
159 159 data = ({
160 160 'draw': draw,
161 161 'data': users_data,
162 162 'recordsTotal': users_data_total_count,
163 163 'recordsFiltered': users_data_total_filtered_count,
164 164 'recordsTotalInactive': users_data_total_inactive_count,
165 165 'recordsFilteredInactive': users_data_total_filtered_inactive_count
166 166 })
167 167
168 168 return data
169 169
170 170 def _set_personal_repo_group_template_vars(self, c_obj):
171 171 DummyUser = AttributeDict({
172 172 'username': '${username}',
173 173 'user_id': '${user_id}',
174 174 })
175 175 c_obj.default_create_repo_group = RepoGroupModel() \
176 176 .get_default_create_personal_repo_group()
177 177 c_obj.personal_repo_group_name = RepoGroupModel() \
178 178 .get_personal_group_name(DummyUser)
179 179
180 180 @LoginRequired()
181 181 @HasPermissionAllDecorator('hg.admin')
182 182 @view_config(
183 183 route_name='users_new', request_method='GET',
184 184 renderer='rhodecode:templates/admin/users/user_add.mako')
185 185 def users_new(self):
186 186 _ = self.request.translate
187 187 c = self.load_default_context()
188 188 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
189 189 self._set_personal_repo_group_template_vars(c)
190 190 return self._get_template_context(c)
191 191
192 192 @LoginRequired()
193 193 @HasPermissionAllDecorator('hg.admin')
194 194 @CSRFRequired()
195 195 @view_config(
196 196 route_name='users_create', request_method='POST',
197 197 renderer='rhodecode:templates/admin/users/user_add.mako')
198 198 def users_create(self):
199 199 _ = self.request.translate
200 200 c = self.load_default_context()
201 201 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
202 202 user_model = UserModel()
203 203 user_form = UserForm(self.request.translate)()
204 204 try:
205 205 form_result = user_form.to_python(dict(self.request.POST))
206 206 user = user_model.create(form_result)
207 207 Session().flush()
208 208 creation_data = user.get_api_data()
209 209 username = form_result['username']
210 210
211 211 audit_logger.store_web(
212 212 'user.create', action_data={'data': creation_data},
213 213 user=c.rhodecode_user)
214 214
215 215 user_link = h.link_to(
216 216 h.escape(username),
217 217 h.route_path('user_edit', user_id=user.user_id))
218 218 h.flash(h.literal(_('Created user %(user_link)s')
219 219 % {'user_link': user_link}), category='success')
220 220 Session().commit()
221 221 except formencode.Invalid as errors:
222 222 self._set_personal_repo_group_template_vars(c)
223 223 data = render(
224 224 'rhodecode:templates/admin/users/user_add.mako',
225 225 self._get_template_context(c), self.request)
226 226 html = formencode.htmlfill.render(
227 227 data,
228 228 defaults=errors.value,
229 229 errors=errors.error_dict or {},
230 230 prefix_error=False,
231 231 encoding="UTF-8",
232 232 force_defaults=False
233 233 )
234 234 return Response(html)
235 235 except UserCreationError as e:
236 236 h.flash(e, 'error')
237 237 except Exception:
238 238 log.exception("Exception creation of user")
239 239 h.flash(_('Error occurred during creation of user %s')
240 240 % self.request.POST.get('username'), category='error')
241 241 raise HTTPFound(h.route_path('users'))
242 242
243 243
244 244 class UsersView(UserAppView):
245 245 ALLOW_SCOPED_TOKENS = False
246 246 """
247 247 This view has alternative version inside EE, if modified please take a look
248 248 in there as well.
249 249 """
250 250
251 251 def load_default_context(self):
252 252 c = self._get_local_tmpl_context()
253 253 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
254 254 c.allowed_languages = [
255 255 ('en', 'English (en)'),
256 256 ('de', 'German (de)'),
257 257 ('fr', 'French (fr)'),
258 258 ('it', 'Italian (it)'),
259 259 ('ja', 'Japanese (ja)'),
260 260 ('pl', 'Polish (pl)'),
261 261 ('pt', 'Portuguese (pt)'),
262 262 ('ru', 'Russian (ru)'),
263 263 ('zh', 'Chinese (zh)'),
264 264 ]
265 265 req = self.request
266 266
267 267 c.available_permissions = req.registry.settings['available_permissions']
268 268 PermissionModel().set_global_permission_choices(
269 269 c, gettext_translator=req.translate)
270 270
271 271 return c
272 272
273 273 @LoginRequired()
274 274 @HasPermissionAllDecorator('hg.admin')
275 275 @CSRFRequired()
276 276 @view_config(
277 277 route_name='user_update', request_method='POST',
278 278 renderer='rhodecode:templates/admin/users/user_edit.mako')
279 279 def user_update(self):
280 280 _ = self.request.translate
281 281 c = self.load_default_context()
282 282
283 283 user_id = self.db_user_id
284 284 c.user = self.db_user
285 285
286 286 c.active = 'profile'
287 287 c.extern_type = c.user.extern_type
288 288 c.extern_name = c.user.extern_name
289 289 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
290 290 available_languages = [x[0] for x in c.allowed_languages]
291 291 _form = UserForm(self.request.translate, edit=True,
292 292 available_languages=available_languages,
293 293 old_data={'user_id': user_id,
294 294 'email': c.user.email})()
295 295 form_result = {}
296 296 old_values = c.user.get_api_data()
297 297 try:
298 298 form_result = _form.to_python(dict(self.request.POST))
299 299 skip_attrs = ['extern_type', 'extern_name']
300 300 # TODO: plugin should define if username can be updated
301 301 if c.extern_type != "rhodecode":
302 302 # forbid updating username for external accounts
303 303 skip_attrs.append('username')
304 304
305 305 UserModel().update_user(
306 306 user_id, skip_attrs=skip_attrs, **form_result)
307 307
308 308 audit_logger.store_web(
309 309 'user.edit', action_data={'old_data': old_values},
310 310 user=c.rhodecode_user)
311 311
312 312 Session().commit()
313 313 h.flash(_('User updated successfully'), category='success')
314 314 except formencode.Invalid as errors:
315 315 data = render(
316 316 'rhodecode:templates/admin/users/user_edit.mako',
317 317 self._get_template_context(c), self.request)
318 318 html = formencode.htmlfill.render(
319 319 data,
320 320 defaults=errors.value,
321 321 errors=errors.error_dict or {},
322 322 prefix_error=False,
323 323 encoding="UTF-8",
324 324 force_defaults=False
325 325 )
326 326 return Response(html)
327 327 except UserCreationError as e:
328 328 h.flash(e, 'error')
329 329 except Exception:
330 330 log.exception("Exception updating user")
331 331 h.flash(_('Error occurred during update of user %s')
332 332 % form_result.get('username'), category='error')
333 333 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
334 334
335 335 @LoginRequired()
336 336 @HasPermissionAllDecorator('hg.admin')
337 337 @CSRFRequired()
338 338 @view_config(
339 339 route_name='user_delete', request_method='POST',
340 340 renderer='rhodecode:templates/admin/users/user_edit.mako')
341 341 def user_delete(self):
342 342 _ = self.request.translate
343 343 c = self.load_default_context()
344 344 c.user = self.db_user
345 345
346 346 _repos = c.user.repositories
347 347 _repo_groups = c.user.repository_groups
348 348 _user_groups = c.user.user_groups
349 349
350 350 handle_repos = None
351 351 handle_repo_groups = None
352 352 handle_user_groups = None
353 353 # dummy call for flash of handle
354 354 set_handle_flash_repos = lambda: None
355 355 set_handle_flash_repo_groups = lambda: None
356 356 set_handle_flash_user_groups = lambda: None
357 357
358 358 if _repos and self.request.POST.get('user_repos'):
359 359 do = self.request.POST['user_repos']
360 360 if do == 'detach':
361 361 handle_repos = 'detach'
362 362 set_handle_flash_repos = lambda: h.flash(
363 363 _('Detached %s repositories') % len(_repos),
364 364 category='success')
365 365 elif do == 'delete':
366 366 handle_repos = 'delete'
367 367 set_handle_flash_repos = lambda: h.flash(
368 368 _('Deleted %s repositories') % len(_repos),
369 369 category='success')
370 370
371 371 if _repo_groups and self.request.POST.get('user_repo_groups'):
372 372 do = self.request.POST['user_repo_groups']
373 373 if do == 'detach':
374 374 handle_repo_groups = 'detach'
375 375 set_handle_flash_repo_groups = lambda: h.flash(
376 376 _('Detached %s repository groups') % len(_repo_groups),
377 377 category='success')
378 378 elif do == 'delete':
379 379 handle_repo_groups = 'delete'
380 380 set_handle_flash_repo_groups = lambda: h.flash(
381 381 _('Deleted %s repository groups') % len(_repo_groups),
382 382 category='success')
383 383
384 384 if _user_groups and self.request.POST.get('user_user_groups'):
385 385 do = self.request.POST['user_user_groups']
386 386 if do == 'detach':
387 387 handle_user_groups = 'detach'
388 388 set_handle_flash_user_groups = lambda: h.flash(
389 389 _('Detached %s user groups') % len(_user_groups),
390 390 category='success')
391 391 elif do == 'delete':
392 392 handle_user_groups = 'delete'
393 393 set_handle_flash_user_groups = lambda: h.flash(
394 394 _('Deleted %s user groups') % len(_user_groups),
395 395 category='success')
396 396
397 397 old_values = c.user.get_api_data()
398 398 try:
399 399 UserModel().delete(c.user, handle_repos=handle_repos,
400 400 handle_repo_groups=handle_repo_groups,
401 401 handle_user_groups=handle_user_groups)
402 402
403 403 audit_logger.store_web(
404 404 'user.delete', action_data={'old_data': old_values},
405 405 user=c.rhodecode_user)
406 406
407 407 Session().commit()
408 408 set_handle_flash_repos()
409 409 set_handle_flash_repo_groups()
410 410 set_handle_flash_user_groups()
411 411 h.flash(_('Successfully deleted user'), category='success')
412 412 except (UserOwnsReposException, UserOwnsRepoGroupsException,
413 413 UserOwnsUserGroupsException, DefaultUserException) as e:
414 414 h.flash(e, category='warning')
415 415 except Exception:
416 416 log.exception("Exception during deletion of user")
417 417 h.flash(_('An error occurred during deletion of user'),
418 418 category='error')
419 419 raise HTTPFound(h.route_path('users'))
420 420
421 421 @LoginRequired()
422 422 @HasPermissionAllDecorator('hg.admin')
423 423 @view_config(
424 424 route_name='user_edit', request_method='GET',
425 425 renderer='rhodecode:templates/admin/users/user_edit.mako')
426 426 def user_edit(self):
427 427 _ = self.request.translate
428 428 c = self.load_default_context()
429 429 c.user = self.db_user
430 430
431 431 c.active = 'profile'
432 432 c.extern_type = c.user.extern_type
433 433 c.extern_name = c.user.extern_name
434 434 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
435 435
436 436 defaults = c.user.get_dict()
437 437 defaults.update({'language': c.user.user_data.get('language')})
438 438
439 439 data = render(
440 440 'rhodecode:templates/admin/users/user_edit.mako',
441 441 self._get_template_context(c), self.request)
442 442 html = formencode.htmlfill.render(
443 443 data,
444 444 defaults=defaults,
445 445 encoding="UTF-8",
446 446 force_defaults=False
447 447 )
448 448 return Response(html)
449 449
450 450 @LoginRequired()
451 451 @HasPermissionAllDecorator('hg.admin')
452 452 @view_config(
453 453 route_name='user_edit_advanced', request_method='GET',
454 454 renderer='rhodecode:templates/admin/users/user_edit.mako')
455 455 def user_edit_advanced(self):
456 456 _ = self.request.translate
457 457 c = self.load_default_context()
458 458
459 459 user_id = self.db_user_id
460 460 c.user = self.db_user
461 461
462 462 c.active = 'advanced'
463 463 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
464 464 c.personal_repo_group_name = RepoGroupModel()\
465 465 .get_personal_group_name(c.user)
466 466
467 467 c.user_to_review_rules = sorted(
468 468 (x.user for x in c.user.user_review_rules),
469 469 key=lambda u: u.username.lower())
470 470
471 471 c.first_admin = User.get_first_super_admin()
472 472 defaults = c.user.get_dict()
473 473
474 474 # Interim workaround if the user participated on any pull requests as a
475 475 # reviewer.
476 476 has_review = len(c.user.reviewer_pull_requests)
477 477 c.can_delete_user = not has_review
478 478 c.can_delete_user_message = ''
479 479 inactive_link = h.link_to(
480 480 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
481 481 if has_review == 1:
482 482 c.can_delete_user_message = h.literal(_(
483 483 'The user participates as reviewer in {} pull request and '
484 484 'cannot be deleted. \nYou can set the user to '
485 485 '"{}" instead of deleting it.').format(
486 486 has_review, inactive_link))
487 487 elif has_review:
488 488 c.can_delete_user_message = h.literal(_(
489 489 'The user participates as reviewer in {} pull requests and '
490 490 'cannot be deleted. \nYou can set the user to '
491 491 '"{}" instead of deleting it.').format(
492 492 has_review, inactive_link))
493 493
494 494 data = render(
495 495 'rhodecode:templates/admin/users/user_edit.mako',
496 496 self._get_template_context(c), self.request)
497 497 html = formencode.htmlfill.render(
498 498 data,
499 499 defaults=defaults,
500 500 encoding="UTF-8",
501 501 force_defaults=False
502 502 )
503 503 return Response(html)
504 504
505 505 @LoginRequired()
506 506 @HasPermissionAllDecorator('hg.admin')
507 507 @view_config(
508 508 route_name='user_edit_global_perms', request_method='GET',
509 509 renderer='rhodecode:templates/admin/users/user_edit.mako')
510 510 def user_edit_global_perms(self):
511 511 _ = self.request.translate
512 512 c = self.load_default_context()
513 513 c.user = self.db_user
514 514
515 515 c.active = 'global_perms'
516 516
517 517 c.default_user = User.get_default_user()
518 518 defaults = c.user.get_dict()
519 519 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
520 520 defaults.update(c.default_user.get_default_perms())
521 521 defaults.update(c.user.get_default_perms())
522 522
523 523 data = render(
524 524 'rhodecode:templates/admin/users/user_edit.mako',
525 525 self._get_template_context(c), self.request)
526 526 html = formencode.htmlfill.render(
527 527 data,
528 528 defaults=defaults,
529 529 encoding="UTF-8",
530 530 force_defaults=False
531 531 )
532 532 return Response(html)
533 533
534 534 @LoginRequired()
535 535 @HasPermissionAllDecorator('hg.admin')
536 536 @CSRFRequired()
537 537 @view_config(
538 538 route_name='user_edit_global_perms_update', request_method='POST',
539 539 renderer='rhodecode:templates/admin/users/user_edit.mako')
540 540 def user_edit_global_perms_update(self):
541 541 _ = self.request.translate
542 542 c = self.load_default_context()
543 543
544 544 user_id = self.db_user_id
545 545 c.user = self.db_user
546 546
547 547 c.active = 'global_perms'
548 548 try:
549 549 # first stage that verifies the checkbox
550 550 _form = UserIndividualPermissionsForm(self.request.translate)
551 551 form_result = _form.to_python(dict(self.request.POST))
552 552 inherit_perms = form_result['inherit_default_permissions']
553 553 c.user.inherit_default_permissions = inherit_perms
554 554 Session().add(c.user)
555 555
556 556 if not inherit_perms:
557 557 # only update the individual ones if we un check the flag
558 558 _form = UserPermissionsForm(
559 559 self.request.translate,
560 560 [x[0] for x in c.repo_create_choices],
561 561 [x[0] for x in c.repo_create_on_write_choices],
562 562 [x[0] for x in c.repo_group_create_choices],
563 563 [x[0] for x in c.user_group_create_choices],
564 564 [x[0] for x in c.fork_choices],
565 565 [x[0] for x in c.inherit_default_permission_choices])()
566 566
567 567 form_result = _form.to_python(dict(self.request.POST))
568 568 form_result.update({'perm_user_id': c.user.user_id})
569 569
570 570 PermissionModel().update_user_permissions(form_result)
571 571
572 572 # TODO(marcink): implement global permissions
573 573 # audit_log.store_web('user.edit.permissions')
574 574
575 575 Session().commit()
576 576 h.flash(_('User global permissions updated successfully'),
577 577 category='success')
578 578
579 579 except formencode.Invalid as errors:
580 580 data = render(
581 581 'rhodecode:templates/admin/users/user_edit.mako',
582 582 self._get_template_context(c), self.request)
583 583 html = formencode.htmlfill.render(
584 584 data,
585 585 defaults=errors.value,
586 586 errors=errors.error_dict or {},
587 587 prefix_error=False,
588 588 encoding="UTF-8",
589 589 force_defaults=False
590 590 )
591 591 return Response(html)
592 592 except Exception:
593 593 log.exception("Exception during permissions saving")
594 594 h.flash(_('An error occurred during permissions saving'),
595 595 category='error')
596 596 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
597 597
598 598 @LoginRequired()
599 599 @HasPermissionAllDecorator('hg.admin')
600 600 @CSRFRequired()
601 601 @view_config(
602 route_name='user_force_password_reset', request_method='POST',
602 route_name='user_enable_force_password_reset', request_method='POST',
603 603 renderer='rhodecode:templates/admin/users/user_edit.mako')
604 def user_force_password_reset(self):
605 """
606 toggle reset password flag for this user
607 """
604 def user_enable_force_password_reset(self):
608 605 _ = self.request.translate
609 606 c = self.load_default_context()
610 607
611 608 user_id = self.db_user_id
612 609 c.user = self.db_user
613 610
614 611 try:
615 old_value = c.user.user_data.get('force_password_change')
616 c.user.update_userdata(force_password_change=not old_value)
612 c.user.update_userdata(force_password_change=True)
613
614 msg = _('Force password change enabled for user')
615 audit_logger.store_web('user.edit.password_reset.enabled',
616 user=c.rhodecode_user)
617
618 Session().commit()
619 h.flash(msg, category='success')
620 except Exception:
621 log.exception("Exception during password reset for user")
622 h.flash(_('An error occurred during password reset for user'),
623 category='error')
624
625 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
617 626
618 if old_value:
619 msg = _('Force password change disabled for user')
620 audit_logger.store_web(
621 'user.edit.password_reset.disabled',
622 user=c.rhodecode_user)
623 else:
624 msg = _('Force password change enabled for user')
625 audit_logger.store_web(
626 'user.edit.password_reset.enabled',
627 user=c.rhodecode_user)
627 @LoginRequired()
628 @HasPermissionAllDecorator('hg.admin')
629 @CSRFRequired()
630 @view_config(
631 route_name='user_disable_force_password_reset', request_method='POST',
632 renderer='rhodecode:templates/admin/users/user_edit.mako')
633 def user_disable_force_password_reset(self):
634 _ = self.request.translate
635 c = self.load_default_context()
636
637 user_id = self.db_user_id
638 c.user = self.db_user
639
640 try:
641 c.user.update_userdata(force_password_change=False)
642
643 msg = _('Force password change disabled for user')
644 audit_logger.store_web(
645 'user.edit.password_reset.disabled',
646 user=c.rhodecode_user)
628 647
629 648 Session().commit()
630 649 h.flash(msg, category='success')
631 650 except Exception:
632 651 log.exception("Exception during password reset for user")
633 652 h.flash(_('An error occurred during password reset for user'),
634 653 category='error')
635 654
636 655 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
637 656
638 657 @LoginRequired()
639 658 @HasPermissionAllDecorator('hg.admin')
640 659 @CSRFRequired()
641 660 @view_config(
642 661 route_name='user_create_personal_repo_group', request_method='POST',
643 662 renderer='rhodecode:templates/admin/users/user_edit.mako')
644 663 def user_create_personal_repo_group(self):
645 664 """
646 665 Create personal repository group for this user
647 666 """
648 667 from rhodecode.model.repo_group import RepoGroupModel
649 668
650 669 _ = self.request.translate
651 670 c = self.load_default_context()
652 671
653 672 user_id = self.db_user_id
654 673 c.user = self.db_user
655 674
656 675 personal_repo_group = RepoGroup.get_user_personal_repo_group(
657 676 c.user.user_id)
658 677 if personal_repo_group:
659 678 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
660 679
661 680 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
662 681 c.user)
663 682 named_personal_group = RepoGroup.get_by_group_name(
664 683 personal_repo_group_name)
665 684 try:
666 685
667 686 if named_personal_group and named_personal_group.user_id == c.user.user_id:
668 687 # migrate the same named group, and mark it as personal
669 688 named_personal_group.personal = True
670 689 Session().add(named_personal_group)
671 690 Session().commit()
672 691 msg = _('Linked repository group `%s` as personal' % (
673 692 personal_repo_group_name,))
674 693 h.flash(msg, category='success')
675 694 elif not named_personal_group:
676 695 RepoGroupModel().create_personal_repo_group(c.user)
677 696
678 697 msg = _('Created repository group `%s`' % (
679 698 personal_repo_group_name,))
680 699 h.flash(msg, category='success')
681 700 else:
682 701 msg = _('Repository group `%s` is already taken' % (
683 702 personal_repo_group_name,))
684 703 h.flash(msg, category='warning')
685 704 except Exception:
686 705 log.exception("Exception during repository group creation")
687 706 msg = _(
688 707 'An error occurred during repository group creation for user')
689 708 h.flash(msg, category='error')
690 709 Session().rollback()
691 710
692 711 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
693 712
694 713 @LoginRequired()
695 714 @HasPermissionAllDecorator('hg.admin')
696 715 @view_config(
697 716 route_name='edit_user_auth_tokens', request_method='GET',
698 717 renderer='rhodecode:templates/admin/users/user_edit.mako')
699 718 def auth_tokens(self):
700 719 _ = self.request.translate
701 720 c = self.load_default_context()
702 721 c.user = self.db_user
703 722
704 723 c.active = 'auth_tokens'
705 724
706 725 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
707 726 c.role_values = [
708 727 (x, AuthTokenModel.cls._get_role_name(x))
709 728 for x in AuthTokenModel.cls.ROLES]
710 729 c.role_options = [(c.role_values, _("Role"))]
711 730 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
712 731 c.user.user_id, show_expired=True)
713 732 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
714 733 return self._get_template_context(c)
715 734
716 735 def maybe_attach_token_scope(self, token):
717 736 # implemented in EE edition
718 737 pass
719 738
720 739 @LoginRequired()
721 740 @HasPermissionAllDecorator('hg.admin')
722 741 @CSRFRequired()
723 742 @view_config(
724 743 route_name='edit_user_auth_tokens_add', request_method='POST')
725 744 def auth_tokens_add(self):
726 745 _ = self.request.translate
727 746 c = self.load_default_context()
728 747
729 748 user_id = self.db_user_id
730 749 c.user = self.db_user
731 750
732 751 user_data = c.user.get_api_data()
733 752 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
734 753 description = self.request.POST.get('description')
735 754 role = self.request.POST.get('role')
736 755
737 756 token = UserModel().add_auth_token(
738 757 user=c.user.user_id,
739 758 lifetime_minutes=lifetime, role=role, description=description,
740 759 scope_callback=self.maybe_attach_token_scope)
741 760 token_data = token.get_api_data()
742 761
743 762 audit_logger.store_web(
744 763 'user.edit.token.add', action_data={
745 764 'data': {'token': token_data, 'user': user_data}},
746 765 user=self._rhodecode_user, )
747 766 Session().commit()
748 767
749 768 h.flash(_("Auth token successfully created"), category='success')
750 769 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
751 770
752 771 @LoginRequired()
753 772 @HasPermissionAllDecorator('hg.admin')
754 773 @CSRFRequired()
755 774 @view_config(
756 775 route_name='edit_user_auth_tokens_delete', request_method='POST')
757 776 def auth_tokens_delete(self):
758 777 _ = self.request.translate
759 778 c = self.load_default_context()
760 779
761 780 user_id = self.db_user_id
762 781 c.user = self.db_user
763 782
764 783 user_data = c.user.get_api_data()
765 784
766 785 del_auth_token = self.request.POST.get('del_auth_token')
767 786
768 787 if del_auth_token:
769 788 token = UserApiKeys.get_or_404(del_auth_token)
770 789 token_data = token.get_api_data()
771 790
772 791 AuthTokenModel().delete(del_auth_token, c.user.user_id)
773 792 audit_logger.store_web(
774 793 'user.edit.token.delete', action_data={
775 794 'data': {'token': token_data, 'user': user_data}},
776 795 user=self._rhodecode_user,)
777 796 Session().commit()
778 797 h.flash(_("Auth token successfully deleted"), category='success')
779 798
780 799 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
781 800
782 801 @LoginRequired()
783 802 @HasPermissionAllDecorator('hg.admin')
784 803 @view_config(
785 804 route_name='edit_user_ssh_keys', request_method='GET',
786 805 renderer='rhodecode:templates/admin/users/user_edit.mako')
787 806 def ssh_keys(self):
788 807 _ = self.request.translate
789 808 c = self.load_default_context()
790 809 c.user = self.db_user
791 810
792 811 c.active = 'ssh_keys'
793 812 c.default_key = self.request.GET.get('default_key')
794 813 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
795 814 return self._get_template_context(c)
796 815
797 816 @LoginRequired()
798 817 @HasPermissionAllDecorator('hg.admin')
799 818 @view_config(
800 819 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
801 820 renderer='rhodecode:templates/admin/users/user_edit.mako')
802 821 def ssh_keys_generate_keypair(self):
803 822 _ = self.request.translate
804 823 c = self.load_default_context()
805 824
806 825 c.user = self.db_user
807 826
808 827 c.active = 'ssh_keys_generate'
809 828 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
810 829 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
811 830
812 831 return self._get_template_context(c)
813 832
814 833 @LoginRequired()
815 834 @HasPermissionAllDecorator('hg.admin')
816 835 @CSRFRequired()
817 836 @view_config(
818 837 route_name='edit_user_ssh_keys_add', request_method='POST')
819 838 def ssh_keys_add(self):
820 839 _ = self.request.translate
821 840 c = self.load_default_context()
822 841
823 842 user_id = self.db_user_id
824 843 c.user = self.db_user
825 844
826 845 user_data = c.user.get_api_data()
827 846 key_data = self.request.POST.get('key_data')
828 847 description = self.request.POST.get('description')
829 848
830 849 fingerprint = 'unknown'
831 850 try:
832 851 if not key_data:
833 852 raise ValueError('Please add a valid public key')
834 853
835 854 key = SshKeyModel().parse_key(key_data.strip())
836 855 fingerprint = key.hash_md5()
837 856
838 857 ssh_key = SshKeyModel().create(
839 858 c.user.user_id, fingerprint, key.keydata, description)
840 859 ssh_key_data = ssh_key.get_api_data()
841 860
842 861 audit_logger.store_web(
843 862 'user.edit.ssh_key.add', action_data={
844 863 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
845 864 user=self._rhodecode_user, )
846 865 Session().commit()
847 866
848 867 # Trigger an event on change of keys.
849 868 trigger(SshKeyFileChangeEvent(), self.request.registry)
850 869
851 870 h.flash(_("Ssh Key successfully created"), category='success')
852 871
853 872 except IntegrityError:
854 873 log.exception("Exception during ssh key saving")
855 874 err = 'Such key with fingerprint `{}` already exists, ' \
856 875 'please use a different one'.format(fingerprint)
857 876 h.flash(_('An error occurred during ssh key saving: {}').format(err),
858 877 category='error')
859 878 except Exception as e:
860 879 log.exception("Exception during ssh key saving")
861 880 h.flash(_('An error occurred during ssh key saving: {}').format(e),
862 881 category='error')
863 882
864 883 return HTTPFound(
865 884 h.route_path('edit_user_ssh_keys', user_id=user_id))
866 885
867 886 @LoginRequired()
868 887 @HasPermissionAllDecorator('hg.admin')
869 888 @CSRFRequired()
870 889 @view_config(
871 890 route_name='edit_user_ssh_keys_delete', request_method='POST')
872 891 def ssh_keys_delete(self):
873 892 _ = self.request.translate
874 893 c = self.load_default_context()
875 894
876 895 user_id = self.db_user_id
877 896 c.user = self.db_user
878 897
879 898 user_data = c.user.get_api_data()
880 899
881 900 del_ssh_key = self.request.POST.get('del_ssh_key')
882 901
883 902 if del_ssh_key:
884 903 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
885 904 ssh_key_data = ssh_key.get_api_data()
886 905
887 906 SshKeyModel().delete(del_ssh_key, c.user.user_id)
888 907 audit_logger.store_web(
889 908 'user.edit.ssh_key.delete', action_data={
890 909 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
891 910 user=self._rhodecode_user,)
892 911 Session().commit()
893 912 # Trigger an event on change of keys.
894 913 trigger(SshKeyFileChangeEvent(), self.request.registry)
895 914 h.flash(_("Ssh key successfully deleted"), category='success')
896 915
897 916 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
898 917
899 918 @LoginRequired()
900 919 @HasPermissionAllDecorator('hg.admin')
901 920 @view_config(
902 921 route_name='edit_user_emails', request_method='GET',
903 922 renderer='rhodecode:templates/admin/users/user_edit.mako')
904 923 def emails(self):
905 924 _ = self.request.translate
906 925 c = self.load_default_context()
907 926 c.user = self.db_user
908 927
909 928 c.active = 'emails'
910 929 c.user_email_map = UserEmailMap.query() \
911 930 .filter(UserEmailMap.user == c.user).all()
912 931
913 932 return self._get_template_context(c)
914 933
915 934 @LoginRequired()
916 935 @HasPermissionAllDecorator('hg.admin')
917 936 @CSRFRequired()
918 937 @view_config(
919 938 route_name='edit_user_emails_add', request_method='POST')
920 939 def emails_add(self):
921 940 _ = self.request.translate
922 941 c = self.load_default_context()
923 942
924 943 user_id = self.db_user_id
925 944 c.user = self.db_user
926 945
927 946 email = self.request.POST.get('new_email')
928 947 user_data = c.user.get_api_data()
929 948 try:
930 949
931 950 form = UserExtraEmailForm(self.request.translate)()
932 951 data = form.to_python({'email': email})
933 952 email = data['email']
934 953
935 954 UserModel().add_extra_email(c.user.user_id, email)
936 955 audit_logger.store_web(
937 956 'user.edit.email.add',
938 957 action_data={'email': email, 'user': user_data},
939 958 user=self._rhodecode_user)
940 959 Session().commit()
941 960 h.flash(_("Added new email address `%s` for user account") % email,
942 961 category='success')
943 962 except formencode.Invalid as error:
944 963 h.flash(h.escape(error.error_dict['email']), category='error')
945 964 except IntegrityError:
946 965 log.warning("Email %s already exists", email)
947 966 h.flash(_('Email `{}` is already registered for another user.').format(email),
948 967 category='error')
949 968 except Exception:
950 969 log.exception("Exception during email saving")
951 970 h.flash(_('An error occurred during email saving'),
952 971 category='error')
953 972 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
954 973
955 974 @LoginRequired()
956 975 @HasPermissionAllDecorator('hg.admin')
957 976 @CSRFRequired()
958 977 @view_config(
959 978 route_name='edit_user_emails_delete', request_method='POST')
960 979 def emails_delete(self):
961 980 _ = self.request.translate
962 981 c = self.load_default_context()
963 982
964 983 user_id = self.db_user_id
965 984 c.user = self.db_user
966 985
967 986 email_id = self.request.POST.get('del_email_id')
968 987 user_model = UserModel()
969 988
970 989 email = UserEmailMap.query().get(email_id).email
971 990 user_data = c.user.get_api_data()
972 991 user_model.delete_extra_email(c.user.user_id, email_id)
973 992 audit_logger.store_web(
974 993 'user.edit.email.delete',
975 994 action_data={'email': email, 'user': user_data},
976 995 user=self._rhodecode_user)
977 996 Session().commit()
978 997 h.flash(_("Removed email address from user account"),
979 998 category='success')
980 999 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
981 1000
982 1001 @LoginRequired()
983 1002 @HasPermissionAllDecorator('hg.admin')
984 1003 @view_config(
985 1004 route_name='edit_user_ips', request_method='GET',
986 1005 renderer='rhodecode:templates/admin/users/user_edit.mako')
987 1006 def ips(self):
988 1007 _ = self.request.translate
989 1008 c = self.load_default_context()
990 1009 c.user = self.db_user
991 1010
992 1011 c.active = 'ips'
993 1012 c.user_ip_map = UserIpMap.query() \
994 1013 .filter(UserIpMap.user == c.user).all()
995 1014
996 1015 c.inherit_default_ips = c.user.inherit_default_permissions
997 1016 c.default_user_ip_map = UserIpMap.query() \
998 1017 .filter(UserIpMap.user == User.get_default_user()).all()
999 1018
1000 1019 return self._get_template_context(c)
1001 1020
1002 1021 @LoginRequired()
1003 1022 @HasPermissionAllDecorator('hg.admin')
1004 1023 @CSRFRequired()
1005 1024 @view_config(
1006 1025 route_name='edit_user_ips_add', request_method='POST')
1007 1026 # NOTE(marcink): this view is allowed for default users, as we can
1008 1027 # edit their IP white list
1009 1028 def ips_add(self):
1010 1029 _ = self.request.translate
1011 1030 c = self.load_default_context()
1012 1031
1013 1032 user_id = self.db_user_id
1014 1033 c.user = self.db_user
1015 1034
1016 1035 user_model = UserModel()
1017 1036 desc = self.request.POST.get('description')
1018 1037 try:
1019 1038 ip_list = user_model.parse_ip_range(
1020 1039 self.request.POST.get('new_ip'))
1021 1040 except Exception as e:
1022 1041 ip_list = []
1023 1042 log.exception("Exception during ip saving")
1024 1043 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1025 1044 category='error')
1026 1045 added = []
1027 1046 user_data = c.user.get_api_data()
1028 1047 for ip in ip_list:
1029 1048 try:
1030 1049 form = UserExtraIpForm(self.request.translate)()
1031 1050 data = form.to_python({'ip': ip})
1032 1051 ip = data['ip']
1033 1052
1034 1053 user_model.add_extra_ip(c.user.user_id, ip, desc)
1035 1054 audit_logger.store_web(
1036 1055 'user.edit.ip.add',
1037 1056 action_data={'ip': ip, 'user': user_data},
1038 1057 user=self._rhodecode_user)
1039 1058 Session().commit()
1040 1059 added.append(ip)
1041 1060 except formencode.Invalid as error:
1042 1061 msg = error.error_dict['ip']
1043 1062 h.flash(msg, category='error')
1044 1063 except Exception:
1045 1064 log.exception("Exception during ip saving")
1046 1065 h.flash(_('An error occurred during ip saving'),
1047 1066 category='error')
1048 1067 if added:
1049 1068 h.flash(
1050 1069 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1051 1070 category='success')
1052 1071 if 'default_user' in self.request.POST:
1053 1072 # case for editing global IP list we do it for 'DEFAULT' user
1054 1073 raise HTTPFound(h.route_path('admin_permissions_ips'))
1055 1074 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1056 1075
1057 1076 @LoginRequired()
1058 1077 @HasPermissionAllDecorator('hg.admin')
1059 1078 @CSRFRequired()
1060 1079 @view_config(
1061 1080 route_name='edit_user_ips_delete', request_method='POST')
1062 1081 # NOTE(marcink): this view is allowed for default users, as we can
1063 1082 # edit their IP white list
1064 1083 def ips_delete(self):
1065 1084 _ = self.request.translate
1066 1085 c = self.load_default_context()
1067 1086
1068 1087 user_id = self.db_user_id
1069 1088 c.user = self.db_user
1070 1089
1071 1090 ip_id = self.request.POST.get('del_ip_id')
1072 1091 user_model = UserModel()
1073 1092 user_data = c.user.get_api_data()
1074 1093 ip = UserIpMap.query().get(ip_id).ip_addr
1075 1094 user_model.delete_extra_ip(c.user.user_id, ip_id)
1076 1095 audit_logger.store_web(
1077 1096 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1078 1097 user=self._rhodecode_user)
1079 1098 Session().commit()
1080 1099 h.flash(_("Removed ip address from user whitelist"), category='success')
1081 1100
1082 1101 if 'default_user' in self.request.POST:
1083 1102 # case for editing global IP list we do it for 'DEFAULT' user
1084 1103 raise HTTPFound(h.route_path('admin_permissions_ips'))
1085 1104 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1086 1105
1087 1106 @LoginRequired()
1088 1107 @HasPermissionAllDecorator('hg.admin')
1089 1108 @view_config(
1090 1109 route_name='edit_user_groups_management', request_method='GET',
1091 1110 renderer='rhodecode:templates/admin/users/user_edit.mako')
1092 1111 def groups_management(self):
1093 1112 c = self.load_default_context()
1094 1113 c.user = self.db_user
1095 1114 c.data = c.user.group_member
1096 1115
1097 1116 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1098 1117 for group in c.user.group_member]
1099 1118 c.groups = json.dumps(groups)
1100 1119 c.active = 'groups'
1101 1120
1102 1121 return self._get_template_context(c)
1103 1122
1104 1123 @LoginRequired()
1105 1124 @HasPermissionAllDecorator('hg.admin')
1106 1125 @CSRFRequired()
1107 1126 @view_config(
1108 1127 route_name='edit_user_groups_management_updates', request_method='POST')
1109 1128 def groups_management_updates(self):
1110 1129 _ = self.request.translate
1111 1130 c = self.load_default_context()
1112 1131
1113 1132 user_id = self.db_user_id
1114 1133 c.user = self.db_user
1115 1134
1116 1135 user_groups = set(self.request.POST.getall('users_group_id'))
1117 1136 user_groups_objects = []
1118 1137
1119 1138 for ugid in user_groups:
1120 1139 user_groups_objects.append(
1121 1140 UserGroupModel().get_group(safe_int(ugid)))
1122 1141 user_group_model = UserGroupModel()
1123 1142 added_to_groups, removed_from_groups = \
1124 1143 user_group_model.change_groups(c.user, user_groups_objects)
1125 1144
1126 1145 user_data = c.user.get_api_data()
1127 1146 for user_group_id in added_to_groups:
1128 1147 user_group = UserGroup.get(user_group_id)
1129 1148 old_values = user_group.get_api_data()
1130 1149 audit_logger.store_web(
1131 1150 'user_group.edit.member.add',
1132 1151 action_data={'user': user_data, 'old_data': old_values},
1133 1152 user=self._rhodecode_user)
1134 1153
1135 1154 for user_group_id in removed_from_groups:
1136 1155 user_group = UserGroup.get(user_group_id)
1137 1156 old_values = user_group.get_api_data()
1138 1157 audit_logger.store_web(
1139 1158 'user_group.edit.member.delete',
1140 1159 action_data={'user': user_data, 'old_data': old_values},
1141 1160 user=self._rhodecode_user)
1142 1161
1143 1162 Session().commit()
1144 1163 c.active = 'user_groups_management'
1145 1164 h.flash(_("Groups successfully changed"), category='success')
1146 1165
1147 1166 return HTTPFound(h.route_path(
1148 1167 'edit_user_groups_management', user_id=user_id))
1149 1168
1150 1169 @LoginRequired()
1151 1170 @HasPermissionAllDecorator('hg.admin')
1152 1171 @view_config(
1153 1172 route_name='edit_user_audit_logs', request_method='GET',
1154 1173 renderer='rhodecode:templates/admin/users/user_edit.mako')
1155 1174 def user_audit_logs(self):
1156 1175 _ = self.request.translate
1157 1176 c = self.load_default_context()
1158 1177 c.user = self.db_user
1159 1178
1160 1179 c.active = 'audit'
1161 1180
1162 1181 p = safe_int(self.request.GET.get('page', 1), 1)
1163 1182
1164 1183 filter_term = self.request.GET.get('filter')
1165 1184 user_log = UserModel().get_user_log(c.user, filter_term)
1166 1185
1167 1186 def url_generator(**kw):
1168 1187 if filter_term:
1169 1188 kw['filter'] = filter_term
1170 1189 return self.request.current_route_path(_query=kw)
1171 1190
1172 1191 c.audit_logs = h.Page(
1173 1192 user_log, page=p, items_per_page=10, url=url_generator)
1174 1193 c.filter_term = filter_term
1175 1194 return self._get_template_context(c)
1176 1195
1177 1196 @LoginRequired()
1178 1197 @HasPermissionAllDecorator('hg.admin')
1179 1198 @view_config(
1180 1199 route_name='edit_user_perms_summary', request_method='GET',
1181 1200 renderer='rhodecode:templates/admin/users/user_edit.mako')
1182 1201 def user_perms_summary(self):
1183 1202 _ = self.request.translate
1184 1203 c = self.load_default_context()
1185 1204 c.user = self.db_user
1186 1205
1187 1206 c.active = 'perms_summary'
1188 1207 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1189 1208
1190 1209 return self._get_template_context(c)
1191 1210
1192 1211 @LoginRequired()
1193 1212 @HasPermissionAllDecorator('hg.admin')
1194 1213 @view_config(
1195 1214 route_name='edit_user_perms_summary_json', request_method='GET',
1196 1215 renderer='json_ext')
1197 1216 def user_perms_summary_json(self):
1198 1217 self.load_default_context()
1199 1218 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1200 1219
1201 1220 return perm_user.permissions
1202 1221
1203 1222 @LoginRequired()
1204 1223 @HasPermissionAllDecorator('hg.admin')
1205 1224 @view_config(
1206 1225 route_name='edit_user_caches', request_method='GET',
1207 1226 renderer='rhodecode:templates/admin/users/user_edit.mako')
1208 1227 def user_caches(self):
1209 1228 _ = self.request.translate
1210 1229 c = self.load_default_context()
1211 1230 c.user = self.db_user
1212 1231
1213 1232 c.active = 'caches'
1214 1233 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1215 1234
1216 1235 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1217 1236 c.region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1218 1237 c.backend = c.region.backend
1219 1238 c.user_keys = sorted(c.region.backend.list_keys(prefix=cache_namespace_uid))
1220 1239
1221 1240 return self._get_template_context(c)
1222 1241
1223 1242 @LoginRequired()
1224 1243 @HasPermissionAllDecorator('hg.admin')
1225 1244 @CSRFRequired()
1226 1245 @view_config(
1227 1246 route_name='edit_user_caches_update', request_method='POST')
1228 1247 def user_caches_update(self):
1229 1248 _ = self.request.translate
1230 1249 c = self.load_default_context()
1231 1250 c.user = self.db_user
1232 1251
1233 1252 c.active = 'caches'
1234 1253 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1235 1254
1236 1255 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1237 1256 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid)
1238 1257
1239 1258 h.flash(_("Deleted {} cache keys").format(del_keys), category='success')
1240 1259
1241 1260 return HTTPFound(h.route_path(
1242 1261 'edit_user_caches', user_id=c.user.user_id))
@@ -1,355 +1,356 b''
1 1
2 2 /******************************************************************************
3 3 * *
4 4 * DO NOT CHANGE THIS FILE MANUALLY *
5 5 * *
6 6 * *
7 7 * This file is automatically generated when the app starts up with *
8 8 * generate_js_files = true *
9 9 * *
10 10 * To add a route here pass jsroute=True to the route definition in the app *
11 11 * *
12 12 ******************************************************************************/
13 13 function registerRCRoutes() {
14 14 // routes registration
15 15 pyroutes.register('favicon', '/favicon.ico', []);
16 16 pyroutes.register('robots', '/robots.txt', []);
17 17 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
18 18 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
19 19 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
20 20 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
21 21 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
22 22 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/_settings/integrations', ['repo_group_name']);
23 23 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/_settings/integrations/new', ['repo_group_name']);
24 24 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/_settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
25 25 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/_settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
26 26 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/_settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
27 27 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
28 28 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
29 29 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
30 30 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
31 31 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
32 32 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
33 33 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
34 34 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
35 35 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
36 36 pyroutes.register('ops_ping_legacy', '/_admin/ping', []);
37 37 pyroutes.register('ops_error_test_legacy', '/_admin/error_test', []);
38 38 pyroutes.register('admin_home', '/_admin', []);
39 39 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
40 40 pyroutes.register('admin_audit_log_entry', '/_admin/audit_logs/%(audit_log_id)s', ['audit_log_id']);
41 41 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
42 42 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
43 43 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
44 44 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
45 45 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
46 46 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
47 47 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
48 48 pyroutes.register('admin_settings_exception_tracker', '/_admin/settings/exceptions', []);
49 49 pyroutes.register('admin_settings_exception_tracker_delete_all', '/_admin/settings/exceptions/delete', []);
50 50 pyroutes.register('admin_settings_exception_tracker_show', '/_admin/settings/exceptions/%(exception_id)s', ['exception_id']);
51 51 pyroutes.register('admin_settings_exception_tracker_delete', '/_admin/settings/exceptions/%(exception_id)s/delete', ['exception_id']);
52 52 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
53 53 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
54 54 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
55 55 pyroutes.register('admin_settings_process_management_data', '/_admin/settings/process_management/data', []);
56 56 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
57 57 pyroutes.register('admin_settings_process_management_master_signal', '/_admin/settings/process_management/master_signal', []);
58 58 pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []);
59 59 pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []);
60 60 pyroutes.register('admin_settings', '/_admin/settings', []);
61 61 pyroutes.register('admin_settings_update', '/_admin/settings/update', []);
62 62 pyroutes.register('admin_settings_global', '/_admin/settings/global', []);
63 63 pyroutes.register('admin_settings_global_update', '/_admin/settings/global/update', []);
64 64 pyroutes.register('admin_settings_vcs', '/_admin/settings/vcs', []);
65 65 pyroutes.register('admin_settings_vcs_update', '/_admin/settings/vcs/update', []);
66 66 pyroutes.register('admin_settings_vcs_svn_pattern_delete', '/_admin/settings/vcs/svn_pattern_delete', []);
67 67 pyroutes.register('admin_settings_mapping', '/_admin/settings/mapping', []);
68 68 pyroutes.register('admin_settings_mapping_update', '/_admin/settings/mapping/update', []);
69 69 pyroutes.register('admin_settings_visual', '/_admin/settings/visual', []);
70 70 pyroutes.register('admin_settings_visual_update', '/_admin/settings/visual/update', []);
71 71 pyroutes.register('admin_settings_issuetracker', '/_admin/settings/issue-tracker', []);
72 72 pyroutes.register('admin_settings_issuetracker_update', '/_admin/settings/issue-tracker/update', []);
73 73 pyroutes.register('admin_settings_issuetracker_test', '/_admin/settings/issue-tracker/test', []);
74 74 pyroutes.register('admin_settings_issuetracker_delete', '/_admin/settings/issue-tracker/delete', []);
75 75 pyroutes.register('admin_settings_email', '/_admin/settings/email', []);
76 76 pyroutes.register('admin_settings_email_update', '/_admin/settings/email/update', []);
77 77 pyroutes.register('admin_settings_hooks', '/_admin/settings/hooks', []);
78 78 pyroutes.register('admin_settings_hooks_update', '/_admin/settings/hooks/update', []);
79 79 pyroutes.register('admin_settings_hooks_delete', '/_admin/settings/hooks/delete', []);
80 80 pyroutes.register('admin_settings_search', '/_admin/settings/search', []);
81 81 pyroutes.register('admin_settings_labs', '/_admin/settings/labs', []);
82 82 pyroutes.register('admin_settings_labs_update', '/_admin/settings/labs/update', []);
83 83 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
84 84 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
85 85 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
86 86 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
87 87 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
88 88 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
89 89 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
90 90 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
91 91 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
92 92 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
93 93 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
94 94 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
95 95 pyroutes.register('users', '/_admin/users', []);
96 96 pyroutes.register('users_data', '/_admin/users_data', []);
97 97 pyroutes.register('users_create', '/_admin/users/create', []);
98 98 pyroutes.register('users_new', '/_admin/users/new', []);
99 99 pyroutes.register('user_edit', '/_admin/users/%(user_id)s/edit', ['user_id']);
100 100 pyroutes.register('user_edit_advanced', '/_admin/users/%(user_id)s/edit/advanced', ['user_id']);
101 101 pyroutes.register('user_edit_global_perms', '/_admin/users/%(user_id)s/edit/global_permissions', ['user_id']);
102 102 pyroutes.register('user_edit_global_perms_update', '/_admin/users/%(user_id)s/edit/global_permissions/update', ['user_id']);
103 103 pyroutes.register('user_update', '/_admin/users/%(user_id)s/update', ['user_id']);
104 104 pyroutes.register('user_delete', '/_admin/users/%(user_id)s/delete', ['user_id']);
105 pyroutes.register('user_force_password_reset', '/_admin/users/%(user_id)s/password_reset', ['user_id']);
105 pyroutes.register('user_enable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_enable', ['user_id']);
106 pyroutes.register('user_disable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_disable', ['user_id']);
106 107 pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']);
107 108 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
108 109 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
109 110 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
110 111 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
111 112 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
112 113 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
113 114 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
114 115 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
115 116 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
116 117 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
117 118 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
118 119 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
119 120 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
120 121 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
121 122 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
122 123 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
123 124 pyroutes.register('edit_user_caches', '/_admin/users/%(user_id)s/edit/caches', ['user_id']);
124 125 pyroutes.register('edit_user_caches_update', '/_admin/users/%(user_id)s/edit/caches/update', ['user_id']);
125 126 pyroutes.register('user_groups', '/_admin/user_groups', []);
126 127 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
127 128 pyroutes.register('user_groups_new', '/_admin/user_groups/new', []);
128 129 pyroutes.register('user_groups_create', '/_admin/user_groups/create', []);
129 130 pyroutes.register('repos', '/_admin/repos', []);
130 131 pyroutes.register('repo_new', '/_admin/repos/new', []);
131 132 pyroutes.register('repo_create', '/_admin/repos/create', []);
132 133 pyroutes.register('repo_groups', '/_admin/repo_groups', []);
133 134 pyroutes.register('repo_group_new', '/_admin/repo_group/new', []);
134 135 pyroutes.register('repo_group_create', '/_admin/repo_group/create', []);
135 136 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
136 137 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
137 138 pyroutes.register('channelstream_proxy', '/_channelstream', []);
138 139 pyroutes.register('logout', '/_admin/logout', []);
139 140 pyroutes.register('reset_password', '/_admin/password_reset', []);
140 141 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
141 142 pyroutes.register('home', '/', []);
142 143 pyroutes.register('user_autocomplete_data', '/_users', []);
143 144 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
144 145 pyroutes.register('repo_list_data', '/_repos', []);
145 146 pyroutes.register('goto_switcher_data', '/_goto_data', []);
146 147 pyroutes.register('markup_preview', '/_markup_preview', []);
147 148 pyroutes.register('store_user_session_value', '/_store_session_attr', []);
148 149 pyroutes.register('journal', '/_admin/journal', []);
149 150 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
150 151 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
151 152 pyroutes.register('journal_public', '/_admin/public_journal', []);
152 153 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
153 154 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
154 155 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
155 156 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
156 157 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
157 158 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
158 159 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
159 160 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
160 161 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
161 162 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
162 163 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
163 164 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
164 165 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
165 166 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
166 167 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
167 168 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
168 169 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
169 170 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
170 171 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
171 172 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
172 173 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
173 174 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
174 175 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
175 176 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
176 177 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
177 178 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
178 179 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
179 180 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
180 181 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
181 182 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
182 183 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
183 184 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
184 185 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
185 186 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
186 187 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
187 188 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
188 189 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
189 190 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
190 191 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
191 192 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
192 193 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
193 194 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
194 195 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
195 196 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
196 197 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
197 198 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
198 199 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
199 200 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
200 201 pyroutes.register('repo_changelog_elements', '/%(repo_name)s/changelog_elements', ['repo_name']);
201 202 pyroutes.register('repo_changelog_elements_file', '/%(repo_name)s/changelog_elements/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
202 203 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
203 204 pyroutes.register('repo_compare', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
204 205 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
205 206 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
206 207 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
207 208 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
208 209 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
209 210 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
210 211 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
211 212 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
212 213 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
213 214 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
214 215 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
215 216 pyroutes.register('pullrequest_repo_targets', '/%(repo_name)s/pull-request/repo-targets', ['repo_name']);
216 217 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
217 218 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
218 219 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
219 220 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
220 221 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
221 222 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
222 223 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
223 224 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
224 225 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
225 226 pyroutes.register('edit_repo_advanced_archive', '/%(repo_name)s/settings/advanced/archive', ['repo_name']);
226 227 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
227 228 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
228 229 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
229 230 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
230 231 pyroutes.register('edit_repo_advanced_hooks', '/%(repo_name)s/settings/advanced/hooks', ['repo_name']);
231 232 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
232 233 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
233 234 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
234 235 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
235 236 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
236 237 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
237 238 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
238 239 pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
239 240 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
240 241 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
241 242 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
242 243 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
243 244 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
244 245 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
245 246 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
246 247 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
247 248 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
248 249 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
249 250 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
250 251 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
251 252 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
252 253 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
253 254 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
254 255 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
255 256 pyroutes.register('edit_repo_audit_logs', '/%(repo_name)s/settings/audit_logs', ['repo_name']);
256 257 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
257 258 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
258 259 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
259 260 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
260 261 pyroutes.register('edit_repo_group', '/%(repo_group_name)s/_edit', ['repo_group_name']);
261 262 pyroutes.register('edit_repo_group_advanced', '/%(repo_group_name)s/_settings/advanced', ['repo_group_name']);
262 263 pyroutes.register('edit_repo_group_advanced_delete', '/%(repo_group_name)s/_settings/advanced/delete', ['repo_group_name']);
263 264 pyroutes.register('edit_repo_group_perms', '/%(repo_group_name)s/_settings/permissions', ['repo_group_name']);
264 265 pyroutes.register('edit_repo_group_perms_update', '/%(repo_group_name)s/_settings/permissions/update', ['repo_group_name']);
265 266 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
266 267 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
267 268 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
268 269 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
269 270 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
270 271 pyroutes.register('edit_user_group', '/_admin/user_groups/%(user_group_id)s/edit', ['user_group_id']);
271 272 pyroutes.register('user_groups_update', '/_admin/user_groups/%(user_group_id)s/update', ['user_group_id']);
272 273 pyroutes.register('edit_user_group_global_perms', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions', ['user_group_id']);
273 274 pyroutes.register('edit_user_group_global_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions/update', ['user_group_id']);
274 275 pyroutes.register('edit_user_group_perms', '/_admin/user_groups/%(user_group_id)s/edit/permissions', ['user_group_id']);
275 276 pyroutes.register('edit_user_group_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/permissions/update', ['user_group_id']);
276 277 pyroutes.register('edit_user_group_advanced', '/_admin/user_groups/%(user_group_id)s/edit/advanced', ['user_group_id']);
277 278 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
278 279 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
279 280 pyroutes.register('search', '/_admin/search', []);
280 281 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
281 282 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
282 283 pyroutes.register('user_group_profile', '/_profile_user_group/%(user_group_name)s', ['user_group_name']);
283 284 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
284 285 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
285 286 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
286 287 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
287 288 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
288 289 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
289 290 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
290 291 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
291 292 pyroutes.register('my_account_ssh_keys_add', '/_admin/my_account/ssh_keys/new', []);
292 293 pyroutes.register('my_account_ssh_keys_delete', '/_admin/my_account/ssh_keys/delete', []);
293 294 pyroutes.register('my_account_user_group_membership', '/_admin/my_account/user_group_membership', []);
294 295 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
295 296 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
296 297 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
297 298 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
298 299 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
299 300 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
300 301 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
301 302 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
302 303 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
303 304 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
304 305 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
305 306 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
306 307 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
307 308 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
308 309 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
309 310 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
310 311 pyroutes.register('gists_show', '/_admin/gists', []);
311 312 pyroutes.register('gists_new', '/_admin/gists/new', []);
312 313 pyroutes.register('gists_create', '/_admin/gists/create', []);
313 314 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
314 315 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
315 316 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
316 317 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
317 318 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
318 319 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
319 320 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
320 321 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
321 322 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
322 323 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
323 324 pyroutes.register('apiv2', '/_admin/api', []);
324 325 pyroutes.register('admin_settings_license', '/_admin/settings/license', []);
325 326 pyroutes.register('admin_settings_license_unlock', '/_admin/settings/license_unlock', []);
326 327 pyroutes.register('login', '/_admin/login', []);
327 328 pyroutes.register('register', '/_admin/register', []);
328 329 pyroutes.register('repo_reviewers_review_rule_new', '/%(repo_name)s/settings/review/rules/new', ['repo_name']);
329 330 pyroutes.register('repo_reviewers_review_rule_edit', '/%(repo_name)s/settings/review/rules/%(rule_id)s', ['repo_name', 'rule_id']);
330 331 pyroutes.register('repo_reviewers_review_rule_delete', '/%(repo_name)s/settings/review/rules/%(rule_id)s/delete', ['repo_name', 'rule_id']);
331 332 pyroutes.register('plugin_admin_chat', '/_admin/plugin_admin_chat/%(action)s', ['action']);
332 333 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
333 334 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
334 335 pyroutes.register('admin_settings_scheduler_show_tasks', '/_admin/settings/scheduler/_tasks', []);
335 336 pyroutes.register('admin_settings_scheduler_show_all', '/_admin/settings/scheduler', []);
336 337 pyroutes.register('admin_settings_scheduler_new', '/_admin/settings/scheduler/new', []);
337 338 pyroutes.register('admin_settings_scheduler_create', '/_admin/settings/scheduler/create', []);
338 339 pyroutes.register('admin_settings_scheduler_edit', '/_admin/settings/scheduler/%(schedule_id)s', ['schedule_id']);
339 340 pyroutes.register('admin_settings_scheduler_update', '/_admin/settings/scheduler/%(schedule_id)s/update', ['schedule_id']);
340 341 pyroutes.register('admin_settings_scheduler_delete', '/_admin/settings/scheduler/%(schedule_id)s/delete', ['schedule_id']);
341 342 pyroutes.register('admin_settings_scheduler_execute', '/_admin/settings/scheduler/%(schedule_id)s/execute', ['schedule_id']);
342 343 pyroutes.register('admin_settings_automation', '/_admin/settings/automation', []);
343 344 pyroutes.register('admin_settings_automation_update', '/_admin/settings/automation/%(entry_id)s/update', ['entry_id']);
344 345 pyroutes.register('admin_permissions_branch', '/_admin/permissions/branch', []);
345 346 pyroutes.register('admin_permissions_branch_update', '/_admin/permissions/branch/update', []);
346 347 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
347 348 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
348 349 pyroutes.register('my_account_external_identity', '/_admin/my_account/external-identity', []);
349 350 pyroutes.register('my_account_external_identity_delete', '/_admin/my_account/external-identity/delete', []);
350 351 pyroutes.register('repo_automation', '/%(repo_name)s/settings/automation', ['repo_name']);
351 352 pyroutes.register('repo_automation_update', '/%(repo_name)s/settings/automation/%(entry_id)s/update', ['repo_name', 'entry_id']);
352 353 pyroutes.register('edit_repo_remote_push', '/%(repo_name)s/settings/remote/push', ['repo_name']);
353 354 pyroutes.register('edit_repo_perms_branch', '/%(repo_name)s/settings/branch_permissions', ['repo_name']);
354 355 pyroutes.register('edit_repo_perms_branch_delete', '/%(repo_name)s/settings/branch_permissions/%(rule_id)s/delete', ['repo_name', 'rule_id']);
355 356 }
@@ -1,329 +1,329 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%namespace name="base" file="/base/base.mako"/>
3 3
4 4 <div class="panel panel-default">
5 5 <div class="panel-heading">
6 6 <h3 class="panel-title">${_('Settings for Repository: %s') % c.rhodecode_db_repo.repo_name}</h3>
7 7 </div>
8 8 <div class="panel-body">
9 9 ${h.secure_form(h.route_path('edit_repo', repo_name=c.rhodecode_db_repo.repo_name), request=request)}
10 10 <div class="form">
11 11 <!-- fields -->
12 12 <div class="fields">
13 13
14 14 <div class="field">
15 15 <div class="label">
16 16 <label for="repo_name">${_('Name')}:</label>
17 17 </div>
18 18 <div class="input">
19 19 ${c.form['repo_name'].render(css_class='medium', oid='repo_name')|n}
20 20 ${c.form.render_error(request, c.form['repo_name'])|n}
21 21
22 22 <p class="help-block">${_('permalink id')}: `_${c.rhodecode_db_repo.repo_id}` <span><a href="#" onclick="$('#clone_id').toggle();return false">${_('what is that ?')}</a></span></p>
23 23 <p id="clone_id" style="display:none;">
24 24 ${_('URL by id')}: `${c.rhodecode_db_repo.clone_url(with_id=True)}` <br/>
25 25 ${_('''In case this repository is renamed or moved into another group the repository url changes.
26 26 Using above url guarantees that this repository will always be accessible under such url.
27 27 Useful for CI systems, or any other cases that you need to hardcode the url into 3rd party service.''')}</p>
28 28 </div>
29 29 </div>
30 30
31 31 <div class="field">
32 32 <div class="label">
33 33 <label for="repo_group">${_('Repository group')}:</label>
34 34 </div>
35 35 <div class="select">
36 36 ${c.form['repo_group'].render(css_class='medium', oid='repo_group')|n}
37 37 ${c.form.render_error(request, c.form['repo_group'])|n}
38 38
39 39 % if c.personal_repo_group:
40 40 <a class="btn" href="#" data-personal-group-name="${c.personal_repo_group.group_name}" data-personal-group-id="${c.personal_repo_group.group_id}" onclick="selectMyGroup(this); return false">
41 41 ${_('Select my personal group (`%(repo_group_name)s`)') % {'repo_group_name': c.personal_repo_group.group_name}}
42 42 </a>
43 43 % endif
44 44 <p class="help-block">${_('Optional select a group to put this repository into.')}</p>
45 45 </div>
46 46 </div>
47 47
48 48 % if c.rhodecode_db_repo.repo_type != 'svn':
49 49 <% sync_link = h.literal(h.link_to('remote sync', h.route_path('edit_repo_remote', repo_name=c.repo_name))) %>
50 50 <div class="field">
51 51 <div class="label">
52 52 <label for="clone_uri">${_('Remote pull uri')}:</label>
53 53 </div>
54 54 <div class="input">
55 55 %if c.rhodecode_db_repo.clone_uri:
56 56 ## display, if we don't have any errors
57 57 % if not c.form['repo_clone_uri'].error:
58 58 <div id="clone_uri_hidden" class='text-as-placeholder'>
59 59 <span id="clone_uri_hidden_value">${c.rhodecode_db_repo.clone_uri_hidden}</span>
60 60 <span class="link" id="edit_clone_uri"><i class="icon-edit"></i>${_('edit')}</span>
61 61 </div>
62 62 % endif
63 63
64 64 ## alter field
65 65 <div id="alter_clone_uri" style="${'' if c.form['repo_clone_uri'].error else 'display: none'}">
66 66 ${c.form['repo_clone_uri'].render(css_class='medium', oid='clone_uri', placeholder=_('enter new value, or leave empty to remove'))|n}
67 67 ${c.form.render_error(request, c.form['repo_clone_uri'])|n}
68 68 % if c.form['repo_clone_uri'].error:
69 69 ## we got error from form subit, means we modify the url
70 70 ${h.hidden('repo_clone_uri_change', 'MOD')}
71 71 % else:
72 72 ${h.hidden('repo_clone_uri_change', 'OLD')}
73 73 % endif
74 74
75 75 % if not c.form['repo_clone_uri'].error:
76 76 <span class="link" id="cancel_edit_clone_uri">${_('cancel')}</span>
77 77 % endif
78 78
79 79 </div>
80 80 %else:
81 81 ## not set yet, display form to set it
82 82 ${c.form['repo_clone_uri'].render(css_class='medium', oid='clone_uri')|n}
83 83 ${c.form.render_error(request, c.form['repo_clone_uri'])|n}
84 84 ${h.hidden('repo_clone_uri_change', 'NEW')}
85 85 %endif
86 86 <p id="alter_clone_uri_help_block" class="help-block">
87 87 ${_('http[s] url where from repository was imported. This field can used for doing {sync_link}.').format(sync_link=sync_link)|n} <br/>
88 88 ${_('This field is stored encrypted inside Database, a format of http://user:password@server.com/repo_name can be used and will be hidden from display.')}
89 89 </p>
90 90 </div>
91 91 </div>
92 92 <div class="field">
93 93 <div class="label">
94 94 <label for="push_uri">${_('Remote push uri')}:</label>
95 95 </div>
96 96 <div class="input">
97 97 %if c.rhodecode_db_repo.push_uri:
98 98 ## display, if we don't have any errors
99 99 % if not c.form['repo_push_uri'].error:
100 100 <div id="push_uri_hidden" class='text-as-placeholder'>
101 101 <span id="push_uri_hidden_value">${c.rhodecode_db_repo.push_uri_hidden}</span>
102 102 <span class="link" id="edit_push_uri"><i class="icon-edit"></i>${_('edit')}</span>
103 103 </div>
104 104 % endif
105 105
106 106 ## alter field
107 107 <div id="alter_push_uri" style="${'' if c.form['repo_push_uri'].error else 'display: none'}">
108 108 ${c.form['repo_push_uri'].render(css_class='medium', oid='push_uri', placeholder=_('enter new value, or leave empty to remove'))|n}
109 109 ${c.form.render_error(request, c.form['repo_push_uri'])|n}
110 110 % if c.form['repo_push_uri'].error:
111 111 ## we got error from form subit, means we modify the url
112 112 ${h.hidden('repo_push_uri_change', 'MOD')}
113 113 % else:
114 114 ${h.hidden('repo_push_uri_change', 'OLD')}
115 115 % endif
116 116
117 117 % if not c.form['repo_push_uri'].error:
118 118 <span class="link" id="cancel_edit_push_uri">${_('cancel')}</span>
119 119 % endif
120 120
121 121 </div>
122 122 %else:
123 123 ## not set yet, display form to set it
124 124 ${c.form['repo_push_uri'].render(css_class='medium', oid='push_uri')|n}
125 125 ${c.form.render_error(request, c.form['repo_push_uri'])|n}
126 126 ${h.hidden('repo_push_uri_change', 'NEW')}
127 127 %endif
128 128 <p id="alter_push_uri_help_block" class="help-block">
129 129 ${_('http[s] url to sync data back. This field can used for doing {sync_link}.').format(sync_link=sync_link)|n} <br/>
130 130 ${_('This field is stored encrypted inside Database, a format of http://user:password@server.com/repo_name can be used and will be hidden from display.')}
131 131 </p>
132 132 </div>
133 133 </div>
134 134 % else:
135 135 ${h.hidden('repo_clone_uri', '')}
136 136 ${h.hidden('repo_push_uri', '')}
137 137 % endif
138 138
139 139 <div class="field">
140 140 <div class="label">
141 141 <label for="repo_landing_commit_ref">${_('Landing commit')}:</label>
142 142 </div>
143 143 <div class="select">
144 144 ${c.form['repo_landing_commit_ref'].render(css_class='medium', oid='repo_landing_commit_ref')|n}
145 145 ${c.form.render_error(request, c.form['repo_landing_commit_ref'])|n}
146 <p class="help-block">${_('Default commit for files page, downloads, full text search index and readme')}</p>
146 <p class="help-block">${_('The default commit for file pages, downloads, full text search index, and README generation.')}</p>
147 147 </div>
148 148 </div>
149 149
150 150 <div class="field badged-field">
151 151 <div class="label">
152 152 <label for="repo_owner">${_('Owner')}:</label>
153 153 </div>
154 154 <div class="input">
155 155 <div class="badge-input-container">
156 156 <div class="user-badge">
157 157 ${base.gravatar_with_user(c.rhodecode_db_repo.user.email or c.rhodecode_db_repo.user.username, show_disabled=not c.rhodecode_db_repo.user.active)}
158 158 </div>
159 159 <div class="badge-input-wrap">
160 160 ${c.form['repo_owner'].render(css_class='medium', oid='repo_owner')|n}
161 161 </div>
162 162 </div>
163 163 ${c.form.render_error(request, c.form['repo_owner'])|n}
164 164 <p class="help-block">${_('Change owner of this repository.')}</p>
165 165 </div>
166 166 </div>
167 167
168 168 <div class="field">
169 169 <div class="label label-textarea">
170 170 <label for="repo_description">${_('Description')}:</label>
171 171 </div>
172 172 <div class="textarea text-area editor">
173 173 ${c.form['repo_description'].render(css_class='medium', oid='repo_description')|n}
174 174 ${c.form.render_error(request, c.form['repo_description'])|n}
175 175
176 176 <% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %>
177 177 <span class="help-block">${_('Plain text format with support of {metatags}. Add a README file for longer descriptions').format(metatags=metatags_url)|n}</span>
178 178 <span id="meta-tags-desc" style="display: none">
179 179 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
180 180 ${dt.metatags_help()}
181 181 </span>
182 182 </div>
183 183 </div>
184 184
185 185 <div class="field">
186 186 <div class="label label-checkbox">
187 187 <label for="${c.form['repo_private'].oid}">${_('Private repository')}:</label>
188 188 </div>
189 189 <div class="checkboxes">
190 190 ${c.form['repo_private'].render(css_class='medium')|n}
191 191 ${c.form.render_error(request, c.form['repo_private'])|n}
192 192 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
193 193 </div>
194 194 </div>
195 195 <div class="field">
196 196 <div class="label label-checkbox">
197 197 <label for="${c.form['repo_enable_statistics'].oid}">${_('Enable statistics')}:</label>
198 198 </div>
199 199 <div class="checkboxes">
200 200 ${c.form['repo_enable_statistics'].render(css_class='medium')|n}
201 201 ${c.form.render_error(request, c.form['repo_enable_statistics'])|n}
202 202 <span class="help-block">${_('Enable statistics window on summary page.')}</span>
203 203 </div>
204 204 </div>
205 205 <div class="field">
206 206 <div class="label label-checkbox">
207 207 <label for="${c.form['repo_enable_downloads'].oid}">${_('Enable downloads')}:</label>
208 208 </div>
209 209 <div class="checkboxes">
210 210 ${c.form['repo_enable_downloads'].render(css_class='medium')|n}
211 211 ${c.form.render_error(request, c.form['repo_enable_downloads'])|n}
212 212 <span class="help-block">${_('Enable download menu on summary page.')}</span>
213 213 </div>
214 214 </div>
215 215 <div class="field">
216 216 <div class="label label-checkbox">
217 217 <label for="${c.form['repo_enable_locking'].oid}">${_('Enable automatic locking')}:</label>
218 218 </div>
219 219 <div class="checkboxes">
220 220 ${c.form['repo_enable_locking'].render(css_class='medium')|n}
221 221 ${c.form.render_error(request, c.form['repo_enable_locking'])|n}
222 222 <span class="help-block">${_('Enable automatic locking on repository. Pulling from this repository creates a lock that can be released by pushing back by the same user')}</span>
223 223 </div>
224 224 </div>
225 225
226 226 %if c.visual.repository_fields:
227 227 ## EXTRA FIELDS
228 228 %for field in c.repo_fields:
229 229 <div class="field">
230 230 <div class="label">
231 231 <label for="${field.field_key_prefixed}">${field.field_label} (${field.field_key}):</label>
232 232 </div>
233 233 <div class="input input-medium">
234 234 ${h.text(field.field_key_prefixed, field.field_value, class_='medium')}
235 235 %if field.field_desc:
236 236 <span class="help-block">${field.field_desc}</span>
237 237 %endif
238 238 </div>
239 239 </div>
240 240 %endfor
241 241 %endif
242 242 <div class="buttons">
243 243 ${h.submit('save',_('Save'),class_="btn")}
244 244 ${h.reset('reset',_('Reset'),class_="btn")}
245 245 </div>
246 246 </div>
247 247 </div>
248 248 ${h.end_form()}
249 249 </div>
250 250 </div>
251 251
252 252 <script>
253 253 $(document).ready(function () {
254 254 var cloneUrl = function (
255 255 alterButton, editButton, cancelEditButton,
256 256 hiddenUrl, hiddenUrlValue, input, helpBlock, changedFlag) {
257 257
258 258 var originalText = helpBlock.html();
259 259 var obfuscatedUrl = hiddenUrlValue.html();
260 260
261 261 var edit = function(e) {
262 262 alterButton.show();
263 263 editButton.hide();
264 264 hiddenUrl.hide();
265 265
266 266 //add the old value next to input for verification
267 267 helpBlock.html("(" + obfuscatedUrl + ")" + "<br\>" + originalText);
268 268 changedFlag.val('MOD');
269 269 };
270 270
271 271 var cancelEdit = function(e) {
272 272 alterButton.hide();
273 273 editButton.show();
274 274 hiddenUrl.show();
275 275
276 276 helpBlock.html(originalText);
277 277 changedFlag.val('OLD');
278 278 input.val('');
279 279 };
280 280
281 281 var initEvents = function() {
282 282 editButton.on('click', edit);
283 283 cancelEditButton.on('click', cancelEdit);
284 284 };
285 285
286 286 var setInitialState = function() {
287 287 if (input.hasClass('error')) {
288 288 alterButton.show();
289 289 editButton.hide();
290 290 hiddenUrl.hide();
291 291 }
292 292 };
293 293
294 294 setInitialState();
295 295 initEvents();
296 296 };
297 297
298 298
299 299 var alterButton = $('#alter_clone_uri');
300 300 var editButton = $('#edit_clone_uri');
301 301 var cancelEditButton = $('#cancel_edit_clone_uri');
302 302 var hiddenUrl = $('#clone_uri_hidden');
303 303 var hiddenUrlValue = $('#clone_uri_hidden_value');
304 304 var input = $('#clone_uri');
305 305 var helpBlock = $('#alter_clone_uri_help_block');
306 306 var changedFlag = $('#repo_clone_uri_change');
307 307 cloneUrl(
308 308 alterButton, editButton, cancelEditButton, hiddenUrl,
309 309 hiddenUrlValue, input, helpBlock, changedFlag);
310 310
311 311 var alterButton = $('#alter_push_uri');
312 312 var editButton = $('#edit_push_uri');
313 313 var cancelEditButton = $('#cancel_edit_push_uri');
314 314 var hiddenUrl = $('#push_uri_hidden');
315 315 var hiddenUrlValue = $('#push_uri_hidden_value');
316 316 var input = $('#push_uri');
317 317 var helpBlock = $('#alter_push_uri_help_block');
318 318 var changedFlag = $('#repo_push_uri_change');
319 319 cloneUrl(
320 320 alterButton, editButton, cancelEditButton, hiddenUrl,
321 321 hiddenUrlValue, input, helpBlock, changedFlag);
322 322
323 323 selectMyGroup = function(element) {
324 324 $("#repo_group").val($(element).data('personalGroupId')).trigger("change");
325 325 };
326 326
327 327 UsersAutoComplete('repo_owner', '${c.rhodecode_user.user_id}');
328 328 });
329 329 </script>
@@ -1,162 +1,171 b''
1 1 <%namespace name="base" file="/base/base.mako"/>
2 2
3 3 <%
4 4 elems = [
5 5 (_('User ID'), c.user.user_id, '', ''),
6 6 (_('Created on'), h.format_date(c.user.created_on), '', ''),
7 7 (_('Source of Record'), c.user.extern_type, '', ''),
8 8
9 9 (_('Last login'), c.user.last_login or '-', '', ''),
10 10 (_('Last activity'), c.user.last_activity, '', ''),
11 11
12 12 (_('Repositories'), len(c.user.repositories), '', [x.repo_name for x in c.user.repositories]),
13 13 (_('Repository groups'), len(c.user.repository_groups), '', [x.group_name for x in c.user.repository_groups]),
14 14 (_('User groups'), len(c.user.user_groups), '', [x.users_group_name for x in c.user.user_groups]),
15 15
16 16 (_('Reviewer of pull requests'), len(c.user.reviewer_pull_requests), '', ['Pull Request #{}'.format(x.pull_request.pull_request_id) for x in c.user.reviewer_pull_requests]),
17 17 (_('Assigned to review rules'), len(c.user_to_review_rules), '', [x for x in c.user_to_review_rules]),
18 18
19 19 (_('Member of User groups'), len(c.user.group_member), '', [x.users_group.users_group_name for x in c.user.group_member]),
20 20 (_('Force password change'), c.user.user_data.get('force_password_change', 'False'), '', ''),
21 21 ]
22 22 %>
23 23
24 24 <div class="panel panel-default">
25 25 <div class="panel-heading">
26 26 <h3 class="panel-title">${_('User: %s') % c.user.username}</h3>
27 27 </div>
28 28 <div class="panel-body">
29 29 ${base.dt_info_panel(elems)}
30 30 </div>
31 31 </div>
32 32
33 33 <div class="panel panel-default">
34 34 <div class="panel-heading">
35 35 <h3 class="panel-title">${_('Force Password Reset')}</h3>
36 36 </div>
37 37 <div class="panel-body">
38 ${h.secure_form(h.route_path('user_force_password_reset', user_id=c.user.user_id), request=request)}
38 ${h.secure_form(h.route_path('user_disable_force_password_reset', user_id=c.user.user_id), request=request)}
39 39 <div class="field">
40 40 <button class="btn btn-default" type="submit">
41 <i class="icon-lock"></i>
42 %if c.user.user_data.get('force_password_change'):
43 ${_('Disable forced password reset')}
44 %else:
45 ${_('Enable forced password reset')}
46 %endif
41 <i class="icon-unlock"></i> ${_('Disable forced password reset')}
42 </button>
43 </div>
44 <div class="field">
45 <span class="help-block">
46 ${_("Clear the forced password change flag.")}
47 </span>
48 </div>
49 ${h.end_form()}
50
51 ${h.secure_form(h.route_path('user_enable_force_password_reset', user_id=c.user.user_id), request=request)}
52 <div class="field">
53 <button class="btn btn-default" type="submit" onclick="return confirm('${_('Confirm to enable forced password change')}');">
54 <i class="icon-lock"></i> ${_('Enable forced password reset')}
47 55 </button>
48 56 </div>
49 57 <div class="field">
50 58 <span class="help-block">
51 59 ${_("When this is enabled user will have to change they password when they next use RhodeCode system. This will also forbid vcs operations until someone makes a password change in the web interface")}
52 60 </span>
53 61 </div>
54 62 ${h.end_form()}
63
55 64 </div>
56 65 </div>
57 66
58 67 <div class="panel panel-default">
59 68 <div class="panel-heading">
60 69 <h3 class="panel-title">${_('Personal Repository Group')}</h3>
61 70 </div>
62 71 <div class="panel-body">
63 72 ${h.secure_form(h.route_path('user_create_personal_repo_group', user_id=c.user.user_id), request=request)}
64 73
65 74 %if c.personal_repo_group:
66 75 <div class="panel-body-title-text">${_('Users personal repository group')} : ${h.link_to(c.personal_repo_group.group_name, h.route_path('repo_group_home', repo_group_name=c.personal_repo_group.group_name))}</div>
67 76 %else:
68 77 <div class="panel-body-title-text">
69 78 ${_('This user currently does not have a personal repository group')}
70 79 <br/>
71 80 ${_('New group will be created at: `/%(path)s`') % {'path': c.personal_repo_group_name}}
72 81 </div>
73 82 %endif
74 83 <button class="btn btn-default" type="submit" ${'disabled="disabled"' if c.personal_repo_group else ''}>
75 84 <i class="icon-folder-close"></i>
76 85 ${_('Create personal repository group')}
77 86 </button>
78 87 ${h.end_form()}
79 88 </div>
80 89 </div>
81 90
82 91
83 92 <div class="panel panel-danger">
84 93 <div class="panel-heading">
85 94 <h3 class="panel-title">${_('Delete User')}</h3>
86 95 </div>
87 96 <div class="panel-body">
88 97 ${h.secure_form(h.route_path('user_delete', user_id=c.user.user_id), request=request)}
89 98
90 99 <table class="display">
91 100 <tr>
92 101 <td>
93 102 ${_ungettext('This user owns %s repository.', 'This user owns %s repositories.', len(c.user.repositories)) % len(c.user.repositories)}
94 103 </td>
95 104 <td>
96 105 %if len(c.user.repositories) > 0:
97 106 <input type="radio" id="user_repos_1" name="user_repos" value="detach" checked="checked"/> <label for="user_repos_1">${_('Detach repositories')}</label>
98 107 %endif
99 108 </td>
100 109 <td>
101 110 %if len(c.user.repositories) > 0:
102 111 <input type="radio" id="user_repos_2" name="user_repos" value="delete" /> <label for="user_repos_2">${_('Delete repositories')}</label>
103 112 %endif
104 113 </td>
105 114 </tr>
106 115
107 116 <tr>
108 117 <td>
109 118 ${_ungettext('This user owns %s repository group.', 'This user owns %s repository groups.', len(c.user.repository_groups)) % len(c.user.repository_groups)}
110 119 </td>
111 120 <td>
112 121 %if len(c.user.repository_groups) > 0:
113 122 <input type="radio" id="user_repo_groups_1" name="user_repo_groups" value="detach" checked="checked"/> <label for="user_repo_groups_1">${_('Detach repository groups')}</label>
114 123 %endif
115 124 </td>
116 125 <td>
117 126 %if len(c.user.repository_groups) > 0:
118 127 <input type="radio" id="user_repo_groups_2" name="user_repo_groups" value="delete" /> <label for="user_repo_groups_2">${_('Delete repositories')}</label>
119 128 %endif
120 129 </td>
121 130 </tr>
122 131
123 132 <tr>
124 133 <td>
125 134 ${_ungettext('This user owns %s user group.', 'This user owns %s user groups.', len(c.user.user_groups)) % len(c.user.user_groups)}
126 135 </td>
127 136 <td>
128 137 %if len(c.user.user_groups) > 0:
129 138 <input type="radio" id="user_user_groups_1" name="user_user_groups" value="detach" checked="checked"/> <label for="user_user_groups_1">${_('Detach user groups')}</label>
130 139 %endif
131 140 </td>
132 141 <td>
133 142 %if len(c.user.user_groups) > 0:
134 143 <input type="radio" id="user_user_groups_2" name="user_user_groups" value="delete" /> <label for="user_user_groups_2">${_('Delete repositories')}</label>
135 144 %endif
136 145 </td>
137 146 </tr>
138 147 </table>
139 148 <div style="margin: 0 0 20px 0" class="fake-space"></div>
140 149
141 150 <div class="field">
142 151 <button class="btn btn-small btn-danger" type="submit"
143 152 onclick="return confirm('${_('Confirm to delete this user: %s') % c.user.username}');"
144 153 ${"disabled" if not c.can_delete_user else ""}>
145 154 ${_('Delete this user')}
146 155 </button>
147 156 </div>
148 157 % if c.can_delete_user_message:
149 158 <p class="help-block pre-formatting">${c.can_delete_user_message}</p>
150 159 % endif
151 160
152 161 <div class="field">
153 162 <span class="help-block">
154 163 %if len(c.user.repositories) > 0 or len(c.user.repository_groups) > 0 or len(c.user.user_groups) > 0:
155 164 <p class="help-block">${_("When selecting the detach option, the depending objects owned by this user will be assigned to the `%s` super admin in the system. The delete option will delete the user's repositories!") % (c.first_admin.full_name)}</p>
156 165 %endif
157 166 </span>
158 167 </div>
159 168
160 169 ${h.end_form()}
161 170 </div>
162 171 </div>
General Comments 0
You need to be logged in to leave comments. Login now