##// END OF EJS Templates
feat(vcs clients filtering): added changes related to vcs client filtering needed for EE. Fixes: RCCE-41
ilin.s -
r5522:33fc2e7c default
parent child Browse files
Show More
@@ -0,0 +1,62 b''
1 <style>
2 .form-group {
3 margin-bottom: 15px;
4 }
5
6 .form-group label {
7 display: flex;
8 align-items: left;
9 font-weight: bold;
10 }
11
12 .form-control {
13 width: 60%;
14 padding: 10px;
15 font-size: 1rem;
16 line-height: 1.5;
17 border: 1px solid #ced4da;
18 border-radius: 4px;
19 box-sizing: border-box;
20 }
21
22 .btn-primary {
23 background-color: #007bff;
24 border: none;
25 padding: 10px 20px;
26 color: white;
27 font-size: 1rem;
28 border-radius: 4px;
29 cursor: pointer;
30 }
31
32 .btn-primary:hover {
33 background-color: #0056b3;
34 }
35 .form-group .help_block {
36 display: block;
37 width: 100%;
38 margin-top: 10px;
39 text-align: left;
40 font-size: 0.875rem;
41 }
42 </style>
43
44 <div>
45 <div class="form-group">
46 ${h.secure_form(h.route_path('check_2fa'), request=request, id='allowed_clients_form')}
47 <p><label for="git">${_('git')}:</label>
48 ${h.text('git', class_="form-control", value=initial_git)}</p>
49 <p><label for="hg">${_('hg')}:</label>
50 ${h.text('hg', class_="form-control", value=initial_hg)}</p>
51 <p><label for="svn">${_('svn')}:</label>
52 ${h.text('svn', class_="form-control", value=initial_svn)}</p>
53 %for k, v in errors.items():
54 <span class="error-message">${k}: ${v}</span>
55 <br />
56 %endfor
57 <p class="help_block">${_('Set rules for allowed git, hg or svn client versions. You can set exact version (for example 2.0.9) or use comparison operators to set earliest or latest version (>=2.6.0)')}</p>
58
59 ${h.submit('send', _('Save'), class_="btn btn-primary")}
60 ${h.end_form()}
61 </div>
62 </div>
@@ -1,1115 +1,1124 b''
1 1 # Copyright (C) 2016-2023 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
5 5 # (only), as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU Affero General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 #
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19
20 20 from rhodecode.apps._base import ADMIN_PREFIX
21 21 from rhodecode.apps._base.navigation import includeme as nav_includeme
22 22 from rhodecode.apps.admin.views.main_views import AdminMainView
23 23
24 24
25 25 def admin_routes(config):
26 26 """
27 27 Admin prefixed routes
28 28 """
29 29 from rhodecode.apps.admin.views.audit_logs import AdminAuditLogsView
30 30 from rhodecode.apps.admin.views.artifacts import AdminArtifactsView
31 31 from rhodecode.apps.admin.views.automation import AdminAutomationView
32 32 from rhodecode.apps.admin.views.scheduler import AdminSchedulerView
33 33 from rhodecode.apps.admin.views.defaults import AdminDefaultSettingsView
34 34 from rhodecode.apps.admin.views.exception_tracker import ExceptionsTrackerView
35 35 from rhodecode.apps.admin.views.open_source_licenses import OpenSourceLicensesAdminSettingsView
36 36 from rhodecode.apps.admin.views.permissions import AdminPermissionsView
37 37 from rhodecode.apps.admin.views.process_management import AdminProcessManagementView
38 38 from rhodecode.apps.admin.views.repo_groups import AdminRepoGroupsView
39 39 from rhodecode.apps.admin.views.repositories import AdminReposView
40 40 from rhodecode.apps.admin.views.sessions import AdminSessionSettingsView
41 41 from rhodecode.apps.admin.views.settings import AdminSettingsView
42 42 from rhodecode.apps.admin.views.svn_config import AdminSvnConfigView
43 43 from rhodecode.apps.admin.views.system_info import AdminSystemInfoSettingsView
44 44 from rhodecode.apps.admin.views.user_groups import AdminUserGroupsView
45 45 from rhodecode.apps.admin.views.users import AdminUsersView, UsersView
46 46 from rhodecode.apps.admin.views.security import AdminSecurityView
47 47
48 48 # Security EE feature
49 49
50 50 config.add_route(
51 51 'admin_security',
52 pattern=ADMIN_PREFIX + '/security')
52 pattern='/security')
53 53 config.add_view(
54 54 AdminSecurityView,
55 attr='security' ,
55 attr='security',
56 56 route_name='admin_security', request_method='GET',
57 57 renderer='rhodecode:templates/admin/security/security.mako')
58 58
59 59 config.add_route(
60 60 name='admin_security_update',
61 pattern=ADMIN_PREFIX + '/security/update')
61 pattern='/security/update')
62 62 config.add_view(
63 63 AdminSecurityView,
64 64 attr='security_update',
65 65 route_name='admin_security_update', request_method='POST',
66 66 renderer='rhodecode:templates/admin/security/security.mako')
67 67
68 config.add_route(
69 name='admin_security_modify_allowed_vcs_client_versions',
70 pattern='/security/modify/allowed_vcs_client_versions')
71 config.add_view(
72 AdminSecurityView,
73 attr='vcs_whitelisted_client_versions_edit',
74 route_name='admin_security_modify_allowed_vcs_client_versions', request_method=('GET', 'POST'),
75 renderer='rhodecode:templates/admin/security/edit_allowed_vcs_client_versions.mako')
76
68 77
69 78 config.add_route(
70 79 name='admin_audit_logs',
71 80 pattern='/audit_logs')
72 81 config.add_view(
73 82 AdminAuditLogsView,
74 83 attr='admin_audit_logs',
75 84 route_name='admin_audit_logs', request_method='GET',
76 85 renderer='rhodecode:templates/admin/admin_audit_logs.mako')
77 86
78 87 config.add_route(
79 88 name='admin_audit_log_entry',
80 89 pattern='/audit_logs/{audit_log_id}')
81 90 config.add_view(
82 91 AdminAuditLogsView,
83 92 attr='admin_audit_log_entry',
84 93 route_name='admin_audit_log_entry', request_method='GET',
85 94 renderer='rhodecode:templates/admin/admin_audit_log_entry.mako')
86 95
87 96 # Artifacts EE feature
88 97 config.add_route(
89 98 'admin_artifacts',
90 99 pattern=ADMIN_PREFIX + '/artifacts')
91 100 config.add_route(
92 101 'admin_artifacts_show_all',
93 102 pattern=ADMIN_PREFIX + '/artifacts')
94 103 config.add_view(
95 104 AdminArtifactsView,
96 105 attr='artifacts',
97 106 route_name='admin_artifacts', request_method='GET',
98 107 renderer='rhodecode:templates/admin/artifacts/artifacts.mako')
99 108 config.add_view(
100 109 AdminArtifactsView,
101 110 attr='artifacts',
102 111 route_name='admin_artifacts_show_all', request_method='GET',
103 112 renderer='rhodecode:templates/admin/artifacts/artifacts.mako')
104 113
105 114 # EE views
106 115 config.add_route(
107 116 name='admin_artifacts_show_info',
108 117 pattern=ADMIN_PREFIX + '/artifacts/{uid}')
109 118 config.add_route(
110 119 name='admin_artifacts_delete',
111 120 pattern=ADMIN_PREFIX + '/artifacts/{uid}/delete')
112 121 config.add_route(
113 122 name='admin_artifacts_update',
114 123 pattern=ADMIN_PREFIX + '/artifacts/{uid}/update')
115 124
116 125 # Automation EE feature
117 126 config.add_route(
118 127 'admin_automation',
119 128 pattern=ADMIN_PREFIX + '/automation')
120 129 config.add_view(
121 130 AdminAutomationView,
122 131 attr='automation',
123 132 route_name='admin_automation', request_method='GET',
124 133 renderer='rhodecode:templates/admin/automation/automation.mako')
125 134
126 135 # Scheduler EE feature
127 136 config.add_route(
128 137 'admin_scheduler',
129 138 pattern=ADMIN_PREFIX + '/scheduler')
130 139 config.add_view(
131 140 AdminSchedulerView,
132 141 attr='scheduler',
133 142 route_name='admin_scheduler', request_method='GET',
134 143 renderer='rhodecode:templates/admin/scheduler/scheduler.mako')
135 144
136 145 config.add_route(
137 146 name='admin_settings_open_source',
138 147 pattern='/settings/open_source')
139 148 config.add_view(
140 149 OpenSourceLicensesAdminSettingsView,
141 150 attr='open_source_licenses',
142 151 route_name='admin_settings_open_source', request_method='GET',
143 152 renderer='rhodecode:templates/admin/settings/settings.mako')
144 153
145 154 config.add_route(
146 155 name='admin_settings_vcs_svn_generate_cfg',
147 156 pattern='/settings/vcs/svn_generate_cfg')
148 157 config.add_view(
149 158 AdminSvnConfigView,
150 159 attr='vcs_svn_generate_config',
151 160 route_name='admin_settings_vcs_svn_generate_cfg',
152 161 request_method='POST', renderer='json')
153 162
154 163 config.add_route(
155 164 name='admin_settings_system',
156 165 pattern='/settings/system')
157 166 config.add_view(
158 167 AdminSystemInfoSettingsView,
159 168 attr='settings_system_info',
160 169 route_name='admin_settings_system', request_method='GET',
161 170 renderer='rhodecode:templates/admin/settings/settings.mako')
162 171
163 172 config.add_route(
164 173 name='admin_settings_system_update',
165 174 pattern='/settings/system/updates')
166 175 config.add_view(
167 176 AdminSystemInfoSettingsView,
168 177 attr='settings_system_info_check_update',
169 178 route_name='admin_settings_system_update', request_method='GET',
170 179 renderer='rhodecode:templates/admin/settings/settings_system_update.mako')
171 180
172 181 config.add_route(
173 182 name='admin_settings_exception_tracker',
174 183 pattern='/settings/exceptions')
175 184 config.add_view(
176 185 ExceptionsTrackerView,
177 186 attr='browse_exceptions',
178 187 route_name='admin_settings_exception_tracker', request_method='GET',
179 188 renderer='rhodecode:templates/admin/settings/settings.mako')
180 189
181 190 config.add_route(
182 191 name='admin_settings_exception_tracker_delete_all',
183 192 pattern='/settings/exceptions_delete_all')
184 193 config.add_view(
185 194 ExceptionsTrackerView,
186 195 attr='exception_delete_all',
187 196 route_name='admin_settings_exception_tracker_delete_all', request_method='POST',
188 197 renderer='rhodecode:templates/admin/settings/settings.mako')
189 198
190 199 config.add_route(
191 200 name='admin_settings_exception_tracker_show',
192 201 pattern='/settings/exceptions/{exception_id}')
193 202 config.add_view(
194 203 ExceptionsTrackerView,
195 204 attr='exception_show',
196 205 route_name='admin_settings_exception_tracker_show', request_method='GET',
197 206 renderer='rhodecode:templates/admin/settings/settings.mako')
198 207
199 208 config.add_route(
200 209 name='admin_settings_exception_tracker_delete',
201 210 pattern='/settings/exceptions/{exception_id}/delete')
202 211 config.add_view(
203 212 ExceptionsTrackerView,
204 213 attr='exception_delete',
205 214 route_name='admin_settings_exception_tracker_delete', request_method='POST',
206 215 renderer='rhodecode:templates/admin/settings/settings.mako')
207 216
208 217 config.add_route(
209 218 name='admin_settings_sessions',
210 219 pattern='/settings/sessions')
211 220 config.add_view(
212 221 AdminSessionSettingsView,
213 222 attr='settings_sessions',
214 223 route_name='admin_settings_sessions', request_method='GET',
215 224 renderer='rhodecode:templates/admin/settings/settings.mako')
216 225
217 226 config.add_route(
218 227 name='admin_settings_sessions_cleanup',
219 228 pattern='/settings/sessions/cleanup')
220 229 config.add_view(
221 230 AdminSessionSettingsView,
222 231 attr='settings_sessions_cleanup',
223 232 route_name='admin_settings_sessions_cleanup', request_method='POST')
224 233
225 234 config.add_route(
226 235 name='admin_settings_process_management',
227 236 pattern='/settings/process_management')
228 237 config.add_view(
229 238 AdminProcessManagementView,
230 239 attr='process_management',
231 240 route_name='admin_settings_process_management', request_method='GET',
232 241 renderer='rhodecode:templates/admin/settings/settings.mako')
233 242
234 243 config.add_route(
235 244 name='admin_settings_process_management_data',
236 245 pattern='/settings/process_management/data')
237 246 config.add_view(
238 247 AdminProcessManagementView,
239 248 attr='process_management_data',
240 249 route_name='admin_settings_process_management_data', request_method='GET',
241 250 renderer='rhodecode:templates/admin/settings/settings_process_management_data.mako')
242 251
243 252 config.add_route(
244 253 name='admin_settings_process_management_signal',
245 254 pattern='/settings/process_management/signal')
246 255 config.add_view(
247 256 AdminProcessManagementView,
248 257 attr='process_management_signal',
249 258 route_name='admin_settings_process_management_signal',
250 259 request_method='POST', renderer='json_ext')
251 260
252 261 config.add_route(
253 262 name='admin_settings_process_management_master_signal',
254 263 pattern='/settings/process_management/master_signal')
255 264 config.add_view(
256 265 AdminProcessManagementView,
257 266 attr='process_management_master_signal',
258 267 route_name='admin_settings_process_management_master_signal',
259 268 request_method='POST', renderer='json_ext')
260 269
261 270 # default settings
262 271 config.add_route(
263 272 name='admin_defaults_repositories',
264 273 pattern='/defaults/repositories')
265 274 config.add_view(
266 275 AdminDefaultSettingsView,
267 276 attr='defaults_repository_show',
268 277 route_name='admin_defaults_repositories', request_method='GET',
269 278 renderer='rhodecode:templates/admin/defaults/defaults.mako')
270 279
271 280 config.add_route(
272 281 name='admin_defaults_repositories_update',
273 282 pattern='/defaults/repositories/update')
274 283 config.add_view(
275 284 AdminDefaultSettingsView,
276 285 attr='defaults_repository_update',
277 286 route_name='admin_defaults_repositories_update', request_method='POST',
278 287 renderer='rhodecode:templates/admin/defaults/defaults.mako')
279 288
280 289 # admin settings
281 290
282 291 config.add_route(
283 292 name='admin_settings',
284 293 pattern='/settings')
285 294 config.add_view(
286 295 AdminSettingsView,
287 296 attr='settings_global',
288 297 route_name='admin_settings', request_method='GET',
289 298 renderer='rhodecode:templates/admin/settings/settings.mako')
290 299
291 300 config.add_route(
292 301 name='admin_settings_update',
293 302 pattern='/settings/update')
294 303 config.add_view(
295 304 AdminSettingsView,
296 305 attr='settings_global_update',
297 306 route_name='admin_settings_update', request_method='POST',
298 307 renderer='rhodecode:templates/admin/settings/settings.mako')
299 308
300 309 config.add_route(
301 310 name='admin_settings_global',
302 311 pattern='/settings/global')
303 312 config.add_view(
304 313 AdminSettingsView,
305 314 attr='settings_global',
306 315 route_name='admin_settings_global', request_method='GET',
307 316 renderer='rhodecode:templates/admin/settings/settings.mako')
308 317
309 318 config.add_route(
310 319 name='admin_settings_global_update',
311 320 pattern='/settings/global/update')
312 321 config.add_view(
313 322 AdminSettingsView,
314 323 attr='settings_global_update',
315 324 route_name='admin_settings_global_update', request_method='POST',
316 325 renderer='rhodecode:templates/admin/settings/settings.mako')
317 326
318 327 config.add_route(
319 328 name='admin_settings_vcs',
320 329 pattern='/settings/vcs')
321 330 config.add_view(
322 331 AdminSettingsView,
323 332 attr='settings_vcs',
324 333 route_name='admin_settings_vcs', request_method='GET',
325 334 renderer='rhodecode:templates/admin/settings/settings.mako')
326 335
327 336 config.add_route(
328 337 name='admin_settings_vcs_update',
329 338 pattern='/settings/vcs/update')
330 339 config.add_view(
331 340 AdminSettingsView,
332 341 attr='settings_vcs_update',
333 342 route_name='admin_settings_vcs_update', request_method='POST',
334 343 renderer='rhodecode:templates/admin/settings/settings.mako')
335 344
336 345 config.add_route(
337 346 name='admin_settings_vcs_svn_pattern_delete',
338 347 pattern='/settings/vcs/svn_pattern_delete')
339 348 config.add_view(
340 349 AdminSettingsView,
341 350 attr='settings_vcs_delete_svn_pattern',
342 351 route_name='admin_settings_vcs_svn_pattern_delete', request_method='POST',
343 352 renderer='json_ext', xhr=True)
344 353
345 354 config.add_route(
346 355 name='admin_settings_mapping',
347 356 pattern='/settings/mapping')
348 357 config.add_view(
349 358 AdminSettingsView,
350 359 attr='settings_mapping',
351 360 route_name='admin_settings_mapping', request_method='GET',
352 361 renderer='rhodecode:templates/admin/settings/settings.mako')
353 362
354 363 config.add_route(
355 364 name='admin_settings_mapping_update',
356 365 pattern='/settings/mapping/update')
357 366 config.add_view(
358 367 AdminSettingsView,
359 368 attr='settings_mapping_update',
360 369 route_name='admin_settings_mapping_update', request_method='POST',
361 370 renderer='rhodecode:templates/admin/settings/settings.mako')
362 371
363 372 config.add_route(
364 373 name='admin_settings_visual',
365 374 pattern='/settings/visual')
366 375 config.add_view(
367 376 AdminSettingsView,
368 377 attr='settings_visual',
369 378 route_name='admin_settings_visual', request_method='GET',
370 379 renderer='rhodecode:templates/admin/settings/settings.mako')
371 380
372 381 config.add_route(
373 382 name='admin_settings_visual_update',
374 383 pattern='/settings/visual/update')
375 384 config.add_view(
376 385 AdminSettingsView,
377 386 attr='settings_visual_update',
378 387 route_name='admin_settings_visual_update', request_method='POST',
379 388 renderer='rhodecode:templates/admin/settings/settings.mako')
380 389
381 390 config.add_route(
382 391 name='admin_settings_issuetracker',
383 392 pattern='/settings/issue-tracker')
384 393 config.add_view(
385 394 AdminSettingsView,
386 395 attr='settings_issuetracker',
387 396 route_name='admin_settings_issuetracker', request_method='GET',
388 397 renderer='rhodecode:templates/admin/settings/settings.mako')
389 398
390 399 config.add_route(
391 400 name='admin_settings_issuetracker_update',
392 401 pattern='/settings/issue-tracker/update')
393 402 config.add_view(
394 403 AdminSettingsView,
395 404 attr='settings_issuetracker_update',
396 405 route_name='admin_settings_issuetracker_update', request_method='POST',
397 406 renderer='rhodecode:templates/admin/settings/settings.mako')
398 407
399 408 config.add_route(
400 409 name='admin_settings_issuetracker_test',
401 410 pattern='/settings/issue-tracker/test')
402 411 config.add_view(
403 412 AdminSettingsView,
404 413 attr='settings_issuetracker_test',
405 414 route_name='admin_settings_issuetracker_test', request_method='POST',
406 415 renderer='string', xhr=True)
407 416
408 417 config.add_route(
409 418 name='admin_settings_issuetracker_delete',
410 419 pattern='/settings/issue-tracker/delete')
411 420 config.add_view(
412 421 AdminSettingsView,
413 422 attr='settings_issuetracker_delete',
414 423 route_name='admin_settings_issuetracker_delete', request_method='POST',
415 424 renderer='json_ext', xhr=True)
416 425
417 426 config.add_route(
418 427 name='admin_settings_email',
419 428 pattern='/settings/email')
420 429 config.add_view(
421 430 AdminSettingsView,
422 431 attr='settings_email',
423 432 route_name='admin_settings_email', request_method='GET',
424 433 renderer='rhodecode:templates/admin/settings/settings.mako')
425 434
426 435 config.add_route(
427 436 name='admin_settings_email_update',
428 437 pattern='/settings/email/update')
429 438 config.add_view(
430 439 AdminSettingsView,
431 440 attr='settings_email_update',
432 441 route_name='admin_settings_email_update', request_method='POST',
433 442 renderer='rhodecode:templates/admin/settings/settings.mako')
434 443
435 444 config.add_route(
436 445 name='admin_settings_hooks',
437 446 pattern='/settings/hooks')
438 447 config.add_view(
439 448 AdminSettingsView,
440 449 attr='settings_hooks',
441 450 route_name='admin_settings_hooks', request_method='GET',
442 451 renderer='rhodecode:templates/admin/settings/settings.mako')
443 452
444 453 config.add_route(
445 454 name='admin_settings_hooks_update',
446 455 pattern='/settings/hooks/update')
447 456 config.add_view(
448 457 AdminSettingsView,
449 458 attr='settings_hooks_update',
450 459 route_name='admin_settings_hooks_update', request_method='POST',
451 460 renderer='rhodecode:templates/admin/settings/settings.mako')
452 461
453 462 config.add_route(
454 463 name='admin_settings_hooks_delete',
455 464 pattern='/settings/hooks/delete')
456 465 config.add_view(
457 466 AdminSettingsView,
458 467 attr='settings_hooks_update',
459 468 route_name='admin_settings_hooks_delete', request_method='POST',
460 469 renderer='rhodecode:templates/admin/settings/settings.mako')
461 470
462 471 config.add_route(
463 472 name='admin_settings_search',
464 473 pattern='/settings/search')
465 474 config.add_view(
466 475 AdminSettingsView,
467 476 attr='settings_search',
468 477 route_name='admin_settings_search', request_method='GET',
469 478 renderer='rhodecode:templates/admin/settings/settings.mako')
470 479
471 480 config.add_route(
472 481 name='admin_settings_labs',
473 482 pattern='/settings/labs')
474 483 config.add_view(
475 484 AdminSettingsView,
476 485 attr='settings_labs',
477 486 route_name='admin_settings_labs', request_method='GET',
478 487 renderer='rhodecode:templates/admin/settings/settings.mako')
479 488
480 489 config.add_route(
481 490 name='admin_settings_labs_update',
482 491 pattern='/settings/labs/update')
483 492 config.add_view(
484 493 AdminSettingsView,
485 494 attr='settings_labs_update',
486 495 route_name='admin_settings_labs_update', request_method='POST',
487 496 renderer='rhodecode:templates/admin/settings/settings.mako')
488 497
489 498 # global permissions
490 499
491 500 config.add_route(
492 501 name='admin_permissions_application',
493 502 pattern='/permissions/application')
494 503 config.add_view(
495 504 AdminPermissionsView,
496 505 attr='permissions_application',
497 506 route_name='admin_permissions_application', request_method='GET',
498 507 renderer='rhodecode:templates/admin/permissions/permissions.mako')
499 508
500 509 config.add_route(
501 510 name='admin_permissions_application_update',
502 511 pattern='/permissions/application/update')
503 512 config.add_view(
504 513 AdminPermissionsView,
505 514 attr='permissions_application_update',
506 515 route_name='admin_permissions_application_update', request_method='POST',
507 516 renderer='rhodecode:templates/admin/permissions/permissions.mako')
508 517
509 518 config.add_route(
510 519 name='admin_permissions_global',
511 520 pattern='/permissions/global')
512 521 config.add_view(
513 522 AdminPermissionsView,
514 523 attr='permissions_global',
515 524 route_name='admin_permissions_global', request_method='GET',
516 525 renderer='rhodecode:templates/admin/permissions/permissions.mako')
517 526
518 527 config.add_route(
519 528 name='admin_permissions_global_update',
520 529 pattern='/permissions/global/update')
521 530 config.add_view(
522 531 AdminPermissionsView,
523 532 attr='permissions_global_update',
524 533 route_name='admin_permissions_global_update', request_method='POST',
525 534 renderer='rhodecode:templates/admin/permissions/permissions.mako')
526 535
527 536 config.add_route(
528 537 name='admin_permissions_object',
529 538 pattern='/permissions/object')
530 539 config.add_view(
531 540 AdminPermissionsView,
532 541 attr='permissions_objects',
533 542 route_name='admin_permissions_object', request_method='GET',
534 543 renderer='rhodecode:templates/admin/permissions/permissions.mako')
535 544
536 545 config.add_route(
537 546 name='admin_permissions_object_update',
538 547 pattern='/permissions/object/update')
539 548 config.add_view(
540 549 AdminPermissionsView,
541 550 attr='permissions_objects_update',
542 551 route_name='admin_permissions_object_update', request_method='POST',
543 552 renderer='rhodecode:templates/admin/permissions/permissions.mako')
544 553
545 554 # Branch perms EE feature
546 555 config.add_route(
547 556 name='admin_permissions_branch',
548 557 pattern='/permissions/branch')
549 558 config.add_view(
550 559 AdminPermissionsView,
551 560 attr='permissions_branch',
552 561 route_name='admin_permissions_branch', request_method='GET',
553 562 renderer='rhodecode:templates/admin/permissions/permissions.mako')
554 563
555 564 config.add_route(
556 565 name='admin_permissions_ips',
557 566 pattern='/permissions/ips')
558 567 config.add_view(
559 568 AdminPermissionsView,
560 569 attr='permissions_ips',
561 570 route_name='admin_permissions_ips', request_method='GET',
562 571 renderer='rhodecode:templates/admin/permissions/permissions.mako')
563 572
564 573 config.add_route(
565 574 name='admin_permissions_overview',
566 575 pattern='/permissions/overview')
567 576 config.add_view(
568 577 AdminPermissionsView,
569 578 attr='permissions_overview',
570 579 route_name='admin_permissions_overview', request_method='GET',
571 580 renderer='rhodecode:templates/admin/permissions/permissions.mako')
572 581
573 582 config.add_route(
574 583 name='admin_permissions_auth_token_access',
575 584 pattern='/permissions/auth_token_access')
576 585 config.add_view(
577 586 AdminPermissionsView,
578 587 attr='auth_token_access',
579 588 route_name='admin_permissions_auth_token_access', request_method='GET',
580 589 renderer='rhodecode:templates/admin/permissions/permissions.mako')
581 590
582 591 config.add_route(
583 592 name='admin_permissions_ssh_keys',
584 593 pattern='/permissions/ssh_keys')
585 594 config.add_view(
586 595 AdminPermissionsView,
587 596 attr='ssh_keys',
588 597 route_name='admin_permissions_ssh_keys', request_method='GET',
589 598 renderer='rhodecode:templates/admin/permissions/permissions.mako')
590 599
591 600 config.add_route(
592 601 name='admin_permissions_ssh_keys_data',
593 602 pattern='/permissions/ssh_keys/data')
594 603 config.add_view(
595 604 AdminPermissionsView,
596 605 attr='ssh_keys_data',
597 606 route_name='admin_permissions_ssh_keys_data', request_method='GET',
598 607 renderer='json_ext', xhr=True)
599 608
600 609 config.add_route(
601 610 name='admin_permissions_ssh_keys_update',
602 611 pattern='/permissions/ssh_keys/update')
603 612 config.add_view(
604 613 AdminPermissionsView,
605 614 attr='ssh_keys_update',
606 615 route_name='admin_permissions_ssh_keys_update', request_method='POST',
607 616 renderer='rhodecode:templates/admin/permissions/permissions.mako')
608 617
609 618 # users admin
610 619 config.add_route(
611 620 name='users',
612 621 pattern='/users')
613 622 config.add_view(
614 623 AdminUsersView,
615 624 attr='users_list',
616 625 route_name='users', request_method='GET',
617 626 renderer='rhodecode:templates/admin/users/users.mako')
618 627
619 628 config.add_route(
620 629 name='users_data',
621 630 pattern='/users_data')
622 631 config.add_view(
623 632 AdminUsersView,
624 633 attr='users_list_data',
625 634 # renderer defined below
626 635 route_name='users_data', request_method='GET',
627 636 renderer='json_ext', xhr=True)
628 637
629 638 config.add_route(
630 639 name='users_create',
631 640 pattern='/users/create')
632 641 config.add_view(
633 642 AdminUsersView,
634 643 attr='users_create',
635 644 route_name='users_create', request_method='POST',
636 645 renderer='rhodecode:templates/admin/users/user_add.mako')
637 646
638 647 config.add_route(
639 648 name='users_new',
640 649 pattern='/users/new')
641 650 config.add_view(
642 651 AdminUsersView,
643 652 attr='users_new',
644 653 route_name='users_new', request_method='GET',
645 654 renderer='rhodecode:templates/admin/users/user_add.mako')
646 655
647 656 # user management
648 657 config.add_route(
649 658 name='user_edit',
650 659 pattern=r'/users/{user_id:\d+}/edit',
651 660 user_route=True)
652 661 config.add_view(
653 662 UsersView,
654 663 attr='user_edit',
655 664 route_name='user_edit', request_method='GET',
656 665 renderer='rhodecode:templates/admin/users/user_edit.mako')
657 666
658 667 config.add_route(
659 668 name='user_edit_advanced',
660 669 pattern=r'/users/{user_id:\d+}/edit/advanced',
661 670 user_route=True)
662 671 config.add_view(
663 672 UsersView,
664 673 attr='user_edit_advanced',
665 674 route_name='user_edit_advanced', request_method='GET',
666 675 renderer='rhodecode:templates/admin/users/user_edit.mako')
667 676
668 677 config.add_route(
669 678 name='user_edit_global_perms',
670 679 pattern=r'/users/{user_id:\d+}/edit/global_permissions',
671 680 user_route=True)
672 681 config.add_view(
673 682 UsersView,
674 683 attr='user_edit_global_perms',
675 684 route_name='user_edit_global_perms', request_method='GET',
676 685 renderer='rhodecode:templates/admin/users/user_edit.mako')
677 686
678 687 config.add_route(
679 688 name='user_edit_global_perms_update',
680 689 pattern=r'/users/{user_id:\d+}/edit/global_permissions/update',
681 690 user_route=True)
682 691 config.add_view(
683 692 UsersView,
684 693 attr='user_edit_global_perms_update',
685 694 route_name='user_edit_global_perms_update', request_method='POST',
686 695 renderer='rhodecode:templates/admin/users/user_edit.mako')
687 696
688 697 config.add_route(
689 698 name='user_update',
690 699 pattern=r'/users/{user_id:\d+}/update',
691 700 user_route=True)
692 701 config.add_view(
693 702 UsersView,
694 703 attr='user_update',
695 704 route_name='user_update', request_method='POST',
696 705 renderer='rhodecode:templates/admin/users/user_edit.mako')
697 706
698 707 config.add_route(
699 708 name='user_delete',
700 709 pattern=r'/users/{user_id:\d+}/delete',
701 710 user_route=True)
702 711 config.add_view(
703 712 UsersView,
704 713 attr='user_delete',
705 714 route_name='user_delete', request_method='POST',
706 715 renderer='rhodecode:templates/admin/users/user_edit.mako')
707 716
708 717 config.add_route(
709 718 name='user_enable_force_password_reset',
710 719 pattern=r'/users/{user_id:\d+}/password_reset_enable',
711 720 user_route=True)
712 721 config.add_view(
713 722 UsersView,
714 723 attr='user_enable_force_password_reset',
715 724 route_name='user_enable_force_password_reset', request_method='POST',
716 725 renderer='rhodecode:templates/admin/users/user_edit.mako')
717 726
718 727 config.add_route(
719 728 name='user_disable_force_password_reset',
720 729 pattern=r'/users/{user_id:\d+}/password_reset_disable',
721 730 user_route=True)
722 731 config.add_view(
723 732 UsersView,
724 733 attr='user_disable_force_password_reset',
725 734 route_name='user_disable_force_password_reset', request_method='POST',
726 735 renderer='rhodecode:templates/admin/users/user_edit.mako')
727 736
728 737 config.add_route(
729 738 name='user_create_personal_repo_group',
730 739 pattern=r'/users/{user_id:\d+}/create_repo_group',
731 740 user_route=True)
732 741 config.add_view(
733 742 UsersView,
734 743 attr='user_create_personal_repo_group',
735 744 route_name='user_create_personal_repo_group', request_method='POST',
736 745 renderer='rhodecode:templates/admin/users/user_edit.mako')
737 746
738 747 # user notice
739 748 config.add_route(
740 749 name='user_notice_dismiss',
741 750 pattern=r'/users/{user_id:\d+}/notice_dismiss',
742 751 user_route=True)
743 752 config.add_view(
744 753 UsersView,
745 754 attr='user_notice_dismiss',
746 755 route_name='user_notice_dismiss', request_method='POST',
747 756 renderer='json_ext', xhr=True)
748 757
749 758 # user auth tokens
750 759 config.add_route(
751 760 name='edit_user_auth_tokens',
752 761 pattern=r'/users/{user_id:\d+}/edit/auth_tokens',
753 762 user_route=True)
754 763 config.add_view(
755 764 UsersView,
756 765 attr='auth_tokens',
757 766 route_name='edit_user_auth_tokens', request_method='GET',
758 767 renderer='rhodecode:templates/admin/users/user_edit.mako')
759 768
760 769 config.add_route(
761 770 name='edit_user_auth_tokens_view',
762 771 pattern=r'/users/{user_id:\d+}/edit/auth_tokens/view',
763 772 user_route=True)
764 773 config.add_view(
765 774 UsersView,
766 775 attr='auth_tokens_view',
767 776 route_name='edit_user_auth_tokens_view', request_method='POST',
768 777 renderer='json_ext', xhr=True)
769 778
770 779 config.add_route(
771 780 name='edit_user_auth_tokens_add',
772 781 pattern=r'/users/{user_id:\d+}/edit/auth_tokens/new',
773 782 user_route=True)
774 783 config.add_view(
775 784 UsersView,
776 785 attr='auth_tokens_add',
777 786 route_name='edit_user_auth_tokens_add', request_method='POST')
778 787
779 788 config.add_route(
780 789 name='edit_user_auth_tokens_delete',
781 790 pattern=r'/users/{user_id:\d+}/edit/auth_tokens/delete',
782 791 user_route=True)
783 792 config.add_view(
784 793 UsersView,
785 794 attr='auth_tokens_delete',
786 795 route_name='edit_user_auth_tokens_delete', request_method='POST')
787 796
788 797 # user ssh keys
789 798 config.add_route(
790 799 name='edit_user_ssh_keys',
791 800 pattern=r'/users/{user_id:\d+}/edit/ssh_keys',
792 801 user_route=True)
793 802 config.add_view(
794 803 UsersView,
795 804 attr='ssh_keys',
796 805 route_name='edit_user_ssh_keys', request_method='GET',
797 806 renderer='rhodecode:templates/admin/users/user_edit.mako')
798 807
799 808 config.add_route(
800 809 name='edit_user_ssh_keys_generate_keypair',
801 810 pattern=r'/users/{user_id:\d+}/edit/ssh_keys/generate',
802 811 user_route=True)
803 812 config.add_view(
804 813 UsersView,
805 814 attr='ssh_keys_generate_keypair',
806 815 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
807 816 renderer='rhodecode:templates/admin/users/user_edit.mako')
808 817
809 818 config.add_route(
810 819 name='edit_user_ssh_keys_add',
811 820 pattern=r'/users/{user_id:\d+}/edit/ssh_keys/new',
812 821 user_route=True)
813 822 config.add_view(
814 823 UsersView,
815 824 attr='ssh_keys_add',
816 825 route_name='edit_user_ssh_keys_add', request_method='POST')
817 826
818 827 config.add_route(
819 828 name='edit_user_ssh_keys_delete',
820 829 pattern=r'/users/{user_id:\d+}/edit/ssh_keys/delete',
821 830 user_route=True)
822 831 config.add_view(
823 832 UsersView,
824 833 attr='ssh_keys_delete',
825 834 route_name='edit_user_ssh_keys_delete', request_method='POST')
826 835
827 836 # user emails
828 837 config.add_route(
829 838 name='edit_user_emails',
830 839 pattern=r'/users/{user_id:\d+}/edit/emails',
831 840 user_route=True)
832 841 config.add_view(
833 842 UsersView,
834 843 attr='emails',
835 844 route_name='edit_user_emails', request_method='GET',
836 845 renderer='rhodecode:templates/admin/users/user_edit.mako')
837 846
838 847 config.add_route(
839 848 name='edit_user_emails_add',
840 849 pattern=r'/users/{user_id:\d+}/edit/emails/new',
841 850 user_route=True)
842 851 config.add_view(
843 852 UsersView,
844 853 attr='emails_add',
845 854 route_name='edit_user_emails_add', request_method='POST')
846 855
847 856 config.add_route(
848 857 name='edit_user_emails_delete',
849 858 pattern=r'/users/{user_id:\d+}/edit/emails/delete',
850 859 user_route=True)
851 860 config.add_view(
852 861 UsersView,
853 862 attr='emails_delete',
854 863 route_name='edit_user_emails_delete', request_method='POST')
855 864
856 865 # user IPs
857 866 config.add_route(
858 867 name='edit_user_ips',
859 868 pattern=r'/users/{user_id:\d+}/edit/ips',
860 869 user_route=True)
861 870 config.add_view(
862 871 UsersView,
863 872 attr='ips',
864 873 route_name='edit_user_ips', request_method='GET',
865 874 renderer='rhodecode:templates/admin/users/user_edit.mako')
866 875
867 876 config.add_route(
868 877 name='edit_user_ips_add',
869 878 pattern=r'/users/{user_id:\d+}/edit/ips/new',
870 879 user_route_with_default=True) # enabled for default user too
871 880 config.add_view(
872 881 UsersView,
873 882 attr='ips_add',
874 883 route_name='edit_user_ips_add', request_method='POST')
875 884
876 885 config.add_route(
877 886 name='edit_user_ips_delete',
878 887 pattern=r'/users/{user_id:\d+}/edit/ips/delete',
879 888 user_route_with_default=True) # enabled for default user too
880 889 config.add_view(
881 890 UsersView,
882 891 attr='ips_delete',
883 892 route_name='edit_user_ips_delete', request_method='POST')
884 893
885 894 # user perms
886 895 config.add_route(
887 896 name='edit_user_perms_summary',
888 897 pattern=r'/users/{user_id:\d+}/edit/permissions_summary',
889 898 user_route=True)
890 899 config.add_view(
891 900 UsersView,
892 901 attr='user_perms_summary',
893 902 route_name='edit_user_perms_summary', request_method='GET',
894 903 renderer='rhodecode:templates/admin/users/user_edit.mako')
895 904
896 905 config.add_route(
897 906 name='edit_user_perms_summary_json',
898 907 pattern=r'/users/{user_id:\d+}/edit/permissions_summary/json',
899 908 user_route=True)
900 909 config.add_view(
901 910 UsersView,
902 911 attr='user_perms_summary_json',
903 912 route_name='edit_user_perms_summary_json', request_method='GET',
904 913 renderer='json_ext')
905 914
906 915 # user user groups management
907 916 config.add_route(
908 917 name='edit_user_groups_management',
909 918 pattern=r'/users/{user_id:\d+}/edit/groups_management',
910 919 user_route=True)
911 920 config.add_view(
912 921 UsersView,
913 922 attr='groups_management',
914 923 route_name='edit_user_groups_management', request_method='GET',
915 924 renderer='rhodecode:templates/admin/users/user_edit.mako')
916 925
917 926 config.add_route(
918 927 name='edit_user_groups_management_updates',
919 928 pattern=r'/users/{user_id:\d+}/edit/edit_user_groups_management/updates',
920 929 user_route=True)
921 930 config.add_view(
922 931 UsersView,
923 932 attr='groups_management_updates',
924 933 route_name='edit_user_groups_management_updates', request_method='POST')
925 934
926 935 # user audit logs
927 936 config.add_route(
928 937 name='edit_user_audit_logs',
929 938 pattern=r'/users/{user_id:\d+}/edit/audit', user_route=True)
930 939 config.add_view(
931 940 UsersView,
932 941 attr='user_audit_logs',
933 942 route_name='edit_user_audit_logs', request_method='GET',
934 943 renderer='rhodecode:templates/admin/users/user_edit.mako')
935 944
936 945 config.add_route(
937 946 name='edit_user_audit_logs_download',
938 947 pattern=r'/users/{user_id:\d+}/edit/audit/download', user_route=True)
939 948 config.add_view(
940 949 UsersView,
941 950 attr='user_audit_logs_download',
942 951 route_name='edit_user_audit_logs_download', request_method='GET',
943 952 renderer='string')
944 953
945 954 # user caches
946 955 config.add_route(
947 956 name='edit_user_caches',
948 957 pattern=r'/users/{user_id:\d+}/edit/caches',
949 958 user_route=True)
950 959 config.add_view(
951 960 UsersView,
952 961 attr='user_caches',
953 962 route_name='edit_user_caches', request_method='GET',
954 963 renderer='rhodecode:templates/admin/users/user_edit.mako')
955 964
956 965 config.add_route(
957 966 name='edit_user_caches_update',
958 967 pattern=r'/users/{user_id:\d+}/edit/caches/update',
959 968 user_route=True)
960 969 config.add_view(
961 970 UsersView,
962 971 attr='user_caches_update',
963 972 route_name='edit_user_caches_update', request_method='POST')
964 973
965 974 # user-groups admin
966 975 config.add_route(
967 976 name='user_groups',
968 977 pattern='/user_groups')
969 978 config.add_view(
970 979 AdminUserGroupsView,
971 980 attr='user_groups_list',
972 981 route_name='user_groups', request_method='GET',
973 982 renderer='rhodecode:templates/admin/user_groups/user_groups.mako')
974 983
975 984 config.add_route(
976 985 name='user_groups_data',
977 986 pattern='/user_groups_data')
978 987 config.add_view(
979 988 AdminUserGroupsView,
980 989 attr='user_groups_list_data',
981 990 route_name='user_groups_data', request_method='GET',
982 991 renderer='json_ext', xhr=True)
983 992
984 993 config.add_route(
985 994 name='user_groups_new',
986 995 pattern='/user_groups/new')
987 996 config.add_view(
988 997 AdminUserGroupsView,
989 998 attr='user_groups_new',
990 999 route_name='user_groups_new', request_method='GET',
991 1000 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
992 1001
993 1002 config.add_route(
994 1003 name='user_groups_create',
995 1004 pattern='/user_groups/create')
996 1005 config.add_view(
997 1006 AdminUserGroupsView,
998 1007 attr='user_groups_create',
999 1008 route_name='user_groups_create', request_method='POST',
1000 1009 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
1001 1010
1002 1011 # repos admin
1003 1012 config.add_route(
1004 1013 name='repos',
1005 1014 pattern='/repos')
1006 1015 config.add_view(
1007 1016 AdminReposView,
1008 1017 attr='repository_list',
1009 1018 route_name='repos', request_method='GET',
1010 1019 renderer='rhodecode:templates/admin/repos/repos.mako')
1011 1020
1012 1021 config.add_route(
1013 1022 name='repos_data',
1014 1023 pattern='/repos_data')
1015 1024 config.add_view(
1016 1025 AdminReposView,
1017 1026 attr='repository_list_data',
1018 1027 route_name='repos_data', request_method='GET',
1019 1028 renderer='json_ext', xhr=True)
1020 1029
1021 1030 config.add_route(
1022 1031 name='repo_new',
1023 1032 pattern='/repos/new')
1024 1033 config.add_view(
1025 1034 AdminReposView,
1026 1035 attr='repository_new',
1027 1036 route_name='repo_new', request_method='GET',
1028 1037 renderer='rhodecode:templates/admin/repos/repo_add.mako')
1029 1038
1030 1039 config.add_route(
1031 1040 name='repo_create',
1032 1041 pattern='/repos/create')
1033 1042 config.add_view(
1034 1043 AdminReposView,
1035 1044 attr='repository_create',
1036 1045 route_name='repo_create', request_method='POST',
1037 1046 renderer='rhodecode:templates/admin/repos/repos.mako')
1038 1047
1039 1048 # repo groups admin
1040 1049 config.add_route(
1041 1050 name='repo_groups',
1042 1051 pattern='/repo_groups')
1043 1052 config.add_view(
1044 1053 AdminRepoGroupsView,
1045 1054 attr='repo_group_list',
1046 1055 route_name='repo_groups', request_method='GET',
1047 1056 renderer='rhodecode:templates/admin/repo_groups/repo_groups.mako')
1048 1057
1049 1058 config.add_route(
1050 1059 name='repo_groups_data',
1051 1060 pattern='/repo_groups_data')
1052 1061 config.add_view(
1053 1062 AdminRepoGroupsView,
1054 1063 attr='repo_group_list_data',
1055 1064 route_name='repo_groups_data', request_method='GET',
1056 1065 renderer='json_ext', xhr=True)
1057 1066
1058 1067 config.add_route(
1059 1068 name='repo_group_new',
1060 1069 pattern='/repo_group/new')
1061 1070 config.add_view(
1062 1071 AdminRepoGroupsView,
1063 1072 attr='repo_group_new',
1064 1073 route_name='repo_group_new', request_method='GET',
1065 1074 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
1066 1075
1067 1076 config.add_route(
1068 1077 name='repo_group_create',
1069 1078 pattern='/repo_group/create')
1070 1079 config.add_view(
1071 1080 AdminRepoGroupsView,
1072 1081 attr='repo_group_create',
1073 1082 route_name='repo_group_create', request_method='POST',
1074 1083 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
1075 1084
1076 1085
1077 1086 def includeme(config):
1078 1087 # Create admin navigation registry and add it to the pyramid registry.
1079 1088 nav_includeme(config)
1080 1089
1081 1090 # main admin routes
1082 1091 config.add_route(
1083 1092 name='admin_home', pattern=ADMIN_PREFIX)
1084 1093 config.add_view(
1085 1094 AdminMainView,
1086 1095 attr='admin_main',
1087 1096 route_name='admin_home', request_method='GET',
1088 1097 renderer='rhodecode:templates/admin/main.mako')
1089 1098
1090 1099 # pr global redirect
1091 1100 config.add_route(
1092 1101 name='pull_requests_global_0', # backward compat
1093 1102 pattern=ADMIN_PREFIX + r'/pull_requests/{pull_request_id:\d+}')
1094 1103 config.add_view(
1095 1104 AdminMainView,
1096 1105 attr='pull_requests',
1097 1106 route_name='pull_requests_global_0', request_method='GET')
1098 1107
1099 1108 config.add_route(
1100 1109 name='pull_requests_global_1', # backward compat
1101 1110 pattern=ADMIN_PREFIX + r'/pull-requests/{pull_request_id:\d+}')
1102 1111 config.add_view(
1103 1112 AdminMainView,
1104 1113 attr='pull_requests',
1105 1114 route_name='pull_requests_global_1', request_method='GET')
1106 1115
1107 1116 config.add_route(
1108 1117 name='pull_requests_global',
1109 1118 pattern=ADMIN_PREFIX + r'/pull-request/{pull_request_id:\d+}')
1110 1119 config.add_view(
1111 1120 AdminMainView,
1112 1121 attr='pull_requests',
1113 1122 route_name='pull_requests_global', request_method='GET')
1114 1123
1115 1124 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
@@ -1,39 +1,72 b''
1 1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
5 5 # (only), as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU Affero General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 #
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 import logging
20 import formencode
20 21
22 from rhodecode import BACKENDS
21 23 from rhodecode.apps._base import BaseAppView
24 from rhodecode.model.meta import Session
25 from rhodecode.model.settings import SettingsModel
26 from rhodecode.model.forms import WhitelistedVcsClientsForm
22 27 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
23 28
24 29 log = logging.getLogger(__name__)
25 30
26 31
27 32 class AdminSecurityView(BaseAppView):
28 33
29 34 def load_default_context(self):
30 35 c = self._get_local_tmpl_context()
31 36 return c
32 37
33 38 @LoginRequired()
34 39 @HasPermissionAllDecorator('hg.admin')
35 40 def security(self):
36 41 c = self.load_default_context()
37 42 c.active = 'security'
38 43 return self._get_template_context(c)
39 44
45 @LoginRequired()
46 @HasPermissionAllDecorator('hg.admin')
47 def vcs_whitelisted_client_versions_edit(self):
48 _ = self.request.translate
49 c = self.load_default_context()
50 render_ctx = {}
51 settings = SettingsModel()
52 form = WhitelistedVcsClientsForm(_, )()
53 if self.request.method == 'POST':
54 try:
55 result = form.to_python(self.request.POST)
56 for k, v in result.items():
57 if v:
58 setting = settings.create_or_update_setting(name=f'{k}_allowed_clients', val=v)
59 Session().add(setting)
60 Session().commit()
61
62 except formencode.Invalid as errors:
63 render_ctx.update({
64 'errors': errors.error_dict
65 })
66 for key in BACKENDS.keys():
67 verbose_name = f"initial_{key}"
68 if existing := settings.get_setting_by_name(name=f'{key}_allowed_clients'):
69 render_ctx[verbose_name] = existing.app_settings_value
70 else:
71 render_ctx[verbose_name] = '*'
72 return self._get_template_context(c, **render_ctx)
@@ -1,209 +1,214 b''
1 1 # Copyright (C) 2010-2023 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
5 5 # (only), as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU Affero General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 #
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 """
20 20 Set of custom exceptions used in RhodeCode
21 21 """
22 22
23 23 from webob.exc import HTTPClientError
24 24 from pyramid.httpexceptions import HTTPBadGateway
25 25
26 26
27 27 class LdapUsernameError(Exception):
28 28 pass
29 29
30 30
31 31 class LdapPasswordError(Exception):
32 32 pass
33 33
34 34
35 35 class LdapConnectionError(Exception):
36 36 pass
37 37
38 38
39 39 class LdapImportError(Exception):
40 40 pass
41 41
42 42
43 43 class DefaultUserException(Exception):
44 44 pass
45 45
46 46
47 47 class UserOwnsReposException(Exception):
48 48 pass
49 49
50 50
51 51 class UserOwnsRepoGroupsException(Exception):
52 52 pass
53 53
54 54
55 55 class UserOwnsUserGroupsException(Exception):
56 56 pass
57 57
58 58
59 59 class UserOwnsPullRequestsException(Exception):
60 60 pass
61 61
62 62
63 63 class UserOwnsArtifactsException(Exception):
64 64 pass
65 65
66 66
67 67 class UserGroupAssignedException(Exception):
68 68 pass
69 69
70 70
71 71 class StatusChangeOnClosedPullRequestError(Exception):
72 72 pass
73 73
74 74
75 75 class AttachedForksError(Exception):
76 76 pass
77 77
78 78
79 79 class AttachedPullRequestsError(Exception):
80 80 pass
81 81
82 82
83 83 class AttachedArtifactsError(Exception):
84 84 pass
85 85
86 86
87 87 class RepoGroupAssignmentError(Exception):
88 88 pass
89 89
90 90
91 91 class NonRelativePathError(Exception):
92 92 pass
93 93
94 94
95 95 class HTTPRequirementError(HTTPClientError):
96 96 title = explanation = 'Repository Requirement Missing'
97 97 reason = None
98 98
99 99 def __init__(self, message, *args, **kwargs):
100 100 self.title = self.explanation = message
101 101 super().__init__(*args, **kwargs)
102 102 self.args = (message, )
103 103
104 104
105 class ClientNotSupportedError(HTTPRequirementError):
106 title = explanation = 'Client Not Supported'
107 reason = None
108
109
105 110 class HTTPLockedRC(HTTPClientError):
106 111 """
107 112 Special Exception For locked Repos in RhodeCode, the return code can
108 113 be overwritten by _code keyword argument passed into constructors
109 114 """
110 115 code = 423
111 116 title = explanation = 'Repository Locked'
112 117 reason = None
113 118
114 119 def __init__(self, message, *args, **kwargs):
115 120 import rhodecode
116 121
117 122 self.code = rhodecode.ConfigGet().get_int('lock_ret_code', missing=self.code)
118 123
119 124 self.title = self.explanation = message
120 125 super().__init__(*args, **kwargs)
121 126 self.args = (message, )
122 127
123 128
124 129 class HTTPBranchProtected(HTTPClientError):
125 130 """
126 131 Special Exception For Indicating that branch is protected in RhodeCode, the
127 132 return code can be overwritten by _code keyword argument passed into constructors
128 133 """
129 134 code = 403
130 135 title = explanation = 'Branch Protected'
131 136 reason = None
132 137
133 138 def __init__(self, message, *args, **kwargs):
134 139 self.title = self.explanation = message
135 140 super().__init__(*args, **kwargs)
136 141 self.args = (message, )
137 142
138 143
139 144 class IMCCommitError(Exception):
140 145 pass
141 146
142 147
143 148 class UserCreationError(Exception):
144 149 pass
145 150
146 151
147 152 class NotAllowedToCreateUserError(Exception):
148 153 pass
149 154
150 155
151 156 class DuplicateUpdateUserError(Exception):
152 157 pass
153 158
154 159
155 160 class RepositoryCreationError(Exception):
156 161 pass
157 162
158 163
159 164 class VCSServerUnavailable(HTTPBadGateway):
160 165 """ HTTP Exception class for VCS Server errors """
161 166 code = 502
162 167 title = 'VCS Server Error'
163 168 causes = [
164 169 'VCS Server is not running',
165 170 'Incorrect vcs.server=host:port',
166 171 'Incorrect vcs.server.protocol',
167 172 ]
168 173
169 174 def __init__(self, message=''):
170 175 self.explanation = 'Could not connect to VCS Server'
171 176 if message:
172 177 self.explanation += ': ' + message
173 178 super().__init__()
174 179
175 180
176 181 class ArtifactMetadataDuplicate(ValueError):
177 182
178 183 def __init__(self, *args, **kwargs):
179 184 self.err_section = kwargs.pop('err_section', None)
180 185 self.err_key = kwargs.pop('err_key', None)
181 186 super().__init__(*args, **kwargs)
182 187
183 188
184 189 class ArtifactMetadataBadValueType(ValueError):
185 190 pass
186 191
187 192
188 193 class CommentVersionMismatch(ValueError):
189 194 pass
190 195
191 196
192 197 class SignatureVerificationError(ValueError):
193 198 pass
194 199
195 200
196 201 def signature_verification_error(msg):
197 202 details = """
198 203 Encryption signature verification failed.
199 204 Please check your value of secret key, and/or encrypted value stored.
200 205 Secret key stored inside .ini file:
201 206 `rhodecode.encrypted_values.secret` or defaults to
202 207 `beaker.session.secret`
203 208
204 209 Probably the stored values were encrypted using a different secret then currently set in .ini file
205 210 """
206 211
207 212 final_msg = f'{msg}\n{details}'
208 213 return SignatureVerificationError(final_msg)
209 214
@@ -1,534 +1,548 b''
1 1 # Copyright (C) 2013-2023 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
5 5 # (only), as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU Affero General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 #
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19
20 20 """
21 21 Set of hooks run by RhodeCode Enterprise
22 22 """
23 23
24 24 import os
25 25 import logging
26 26
27 27 import rhodecode
28 28 from rhodecode import events
29 29 from rhodecode.lib import helpers as h
30 30 from rhodecode.lib import audit_logger
31 31 from rhodecode.lib.utils2 import safe_str, user_agent_normalizer
32 32 from rhodecode.lib.exceptions import (
33 HTTPLockedRC, HTTPBranchProtected, UserCreationError)
33 HTTPLockedRC, HTTPBranchProtected, UserCreationError, ClientNotSupportedError)
34 34 from rhodecode.model.db import Repository, User
35 35 from rhodecode.lib.statsd_client import StatsdClient
36 36
37 37 log = logging.getLogger(__name__)
38 38
39 39
40 40 class HookResponse(object):
41 41 def __init__(self, status, output):
42 42 self.status = status
43 43 self.output = output
44 44
45 45 def __add__(self, other):
46 46 other_status = getattr(other, 'status', 0)
47 47 new_status = max(self.status, other_status)
48 48 other_output = getattr(other, 'output', '')
49 49 new_output = self.output + other_output
50 50
51 51 return HookResponse(new_status, new_output)
52 52
53 53 def __bool__(self):
54 54 return self.status == 0
55 55
56 56 def to_json(self):
57 57 return {'status': self.status, 'output': self.output}
58 58
59 59
60 60 def is_shadow_repo(extras):
61 61 """
62 62 Returns ``True`` if this is an action executed against a shadow repository.
63 63 """
64 64 return extras['is_shadow_repo']
65 65
66 66
67 def check_vcs_client(extras):
68 """
69 Checks if vcs client is allowed (Only works in enterprise edition)
70 """
71 try:
72 from rc_ee.lib.security.utils import is_vcs_client_whitelisted
73 except ModuleNotFoundError:
74 is_vcs_client_whitelisted = lambda *x: True
75 backend = extras.get('scm')
76 if not is_vcs_client_whitelisted(extras.get('user_agent'), backend):
77 raise ClientNotSupportedError(f"Your {backend} client is forbidden")
78
67 79 def _get_scm_size(alias, root_path):
68 80
69 81 if not alias.startswith('.'):
70 82 alias += '.'
71 83
72 84 size_scm, size_root = 0, 0
73 85 for path, unused_dirs, files in os.walk(safe_str(root_path)):
74 86 if path.find(alias) != -1:
75 87 for f in files:
76 88 try:
77 89 size_scm += os.path.getsize(os.path.join(path, f))
78 90 except OSError:
79 91 pass
80 92 else:
81 93 for f in files:
82 94 try:
83 95 size_root += os.path.getsize(os.path.join(path, f))
84 96 except OSError:
85 97 pass
86 98
87 99 size_scm_f = h.format_byte_size_binary(size_scm)
88 100 size_root_f = h.format_byte_size_binary(size_root)
89 101 size_total_f = h.format_byte_size_binary(size_root + size_scm)
90 102
91 103 return size_scm_f, size_root_f, size_total_f
92 104
93 105
94 106 # actual hooks called by Mercurial internally, and GIT by our Python Hooks
95 107 def repo_size(extras):
96 108 """Present size of repository after push."""
97 109 repo = Repository.get_by_repo_name(extras.repository)
98 110 vcs_part = f'.{repo.repo_type}'
99 111 size_vcs, size_root, size_total = _get_scm_size(vcs_part, repo.repo_full_path)
100 112 msg = (f'RhodeCode: `{repo.repo_name}` size summary {vcs_part}:{size_vcs} repo:{size_root} total:{size_total}\n')
101 113 return HookResponse(0, msg)
102 114
103 115
104 116 def pre_push(extras):
105 117 """
106 118 Hook executed before pushing code.
107 119
108 120 It bans pushing when the repository is locked.
109 121 """
110 122
123 check_vcs_client(extras)
111 124 user = User.get_by_username(extras.username)
112 125 output = ''
113 126 if extras.locked_by[0] and user.user_id != int(extras.locked_by[0]):
114 127 locked_by = User.get(extras.locked_by[0]).username
115 128 reason = extras.locked_by[2]
116 129 # this exception is interpreted in git/hg middlewares and based
117 130 # on that proper return code is server to client
118 131 _http_ret = HTTPLockedRC(
119 132 _locked_by_explanation(extras.repository, locked_by, reason))
120 133 if str(_http_ret.code).startswith('2'):
121 134 # 2xx Codes don't raise exceptions
122 135 output = _http_ret.title
123 136 else:
124 137 raise _http_ret
125 138
126 139 hook_response = ''
127 140 if not is_shadow_repo(extras):
128 141
129 142 if extras.commit_ids and extras.check_branch_perms:
130 143 auth_user = user.AuthUser()
131 144 repo = Repository.get_by_repo_name(extras.repository)
132 145 affected_branches = []
133 146 if repo.repo_type == 'hg':
134 147 for entry in extras.commit_ids:
135 148 if entry['type'] == 'branch':
136 149 is_forced = bool(entry['multiple_heads'])
137 150 affected_branches.append([entry['name'], is_forced])
138 151 elif repo.repo_type == 'git':
139 152 for entry in extras.commit_ids:
140 153 if entry['type'] == 'heads':
141 154 is_forced = bool(entry['pruned_sha'])
142 155 affected_branches.append([entry['name'], is_forced])
143 156
144 157 for branch_name, is_forced in affected_branches:
145 158
146 159 rule, branch_perm = auth_user.get_rule_and_branch_permission(
147 160 extras.repository, branch_name)
148 161 if not branch_perm:
149 162 # no branch permission found for this branch, just keep checking
150 163 continue
151 164
152 165 if branch_perm == 'branch.push_force':
153 166 continue
154 167 elif branch_perm == 'branch.push' and is_forced is False:
155 168 continue
156 169 elif branch_perm == 'branch.push' and is_forced is True:
157 170 halt_message = f'Branch `{branch_name}` changes rejected by rule {rule}. ' \
158 171 f'FORCE PUSH FORBIDDEN.'
159 172 else:
160 173 halt_message = f'Branch `{branch_name}` changes rejected by rule {rule}.'
161 174
162 175 if halt_message:
163 176 _http_ret = HTTPBranchProtected(halt_message)
164 177 raise _http_ret
165 178
166 179 # Propagate to external components. This is done after checking the
167 180 # lock, for consistent behavior.
168 181 hook_response = pre_push_extension(
169 182 repo_store_path=Repository.base_path(), **extras)
170 183 events.trigger(events.RepoPrePushEvent(
171 184 repo_name=extras.repository, extras=extras))
172 185
173 186 return HookResponse(0, output) + hook_response
174 187
175 188
176 189 def pre_pull(extras):
177 190 """
178 191 Hook executed before pulling the code.
179 192
180 193 It bans pulling when the repository is locked.
181 194 """
182 195
196 check_vcs_client(extras)
183 197 output = ''
184 198 if extras.locked_by[0]:
185 199 locked_by = User.get(extras.locked_by[0]).username
186 200 reason = extras.locked_by[2]
187 201 # this exception is interpreted in git/hg middlewares and based
188 202 # on that proper return code is server to client
189 203 _http_ret = HTTPLockedRC(
190 204 _locked_by_explanation(extras.repository, locked_by, reason))
191 205 if str(_http_ret.code).startswith('2'):
192 206 # 2xx Codes don't raise exceptions
193 207 output = _http_ret.title
194 208 else:
195 209 raise _http_ret
196 210
197 211 # Propagate to external components. This is done after checking the
198 212 # lock, for consistent behavior.
199 213 hook_response = ''
200 214 if not is_shadow_repo(extras):
201 215 extras.hook_type = extras.hook_type or 'pre_pull'
202 216 hook_response = pre_pull_extension(
203 217 repo_store_path=Repository.base_path(), **extras)
204 218 events.trigger(events.RepoPrePullEvent(
205 219 repo_name=extras.repository, extras=extras))
206 220
207 221 return HookResponse(0, output) + hook_response
208 222
209 223
210 224 def post_pull(extras):
211 225 """Hook executed after client pulls the code."""
212 226
213 227 audit_user = audit_logger.UserWrap(
214 228 username=extras.username,
215 229 ip_addr=extras.ip)
216 230 repo = audit_logger.RepoWrap(repo_name=extras.repository)
217 231 audit_logger.store(
218 232 'user.pull', action_data={'user_agent': extras.user_agent},
219 233 user=audit_user, repo=repo, commit=True)
220 234
221 235 statsd = StatsdClient.statsd
222 236 if statsd:
223 237 statsd.incr('rhodecode_pull_total', tags=[
224 238 f'user-agent:{user_agent_normalizer(extras.user_agent)}',
225 239 ])
226 240 output = ''
227 241 # make lock is a tri state False, True, None. We only make lock on True
228 242 if extras.make_lock is True and not is_shadow_repo(extras):
229 243 user = User.get_by_username(extras.username)
230 244 Repository.lock(Repository.get_by_repo_name(extras.repository),
231 245 user.user_id,
232 246 lock_reason=Repository.LOCK_PULL)
233 247 msg = 'Made lock on repo `{}`'.format(extras.repository)
234 248 output += msg
235 249
236 250 if extras.locked_by[0]:
237 251 locked_by = User.get(extras.locked_by[0]).username
238 252 reason = extras.locked_by[2]
239 253 _http_ret = HTTPLockedRC(
240 254 _locked_by_explanation(extras.repository, locked_by, reason))
241 255 if str(_http_ret.code).startswith('2'):
242 256 # 2xx Codes don't raise exceptions
243 257 output += _http_ret.title
244 258
245 259 # Propagate to external components.
246 260 hook_response = ''
247 261 if not is_shadow_repo(extras):
248 262 extras.hook_type = extras.hook_type or 'post_pull'
249 263 hook_response = post_pull_extension(
250 264 repo_store_path=Repository.base_path(), **extras)
251 265 events.trigger(events.RepoPullEvent(
252 266 repo_name=extras.repository, extras=extras))
253 267
254 268 return HookResponse(0, output) + hook_response
255 269
256 270
257 271 def post_push(extras):
258 272 """Hook executed after user pushes to the repository."""
259 273 commit_ids = extras.commit_ids
260 274
261 275 # log the push call
262 276 audit_user = audit_logger.UserWrap(
263 277 username=extras.username, ip_addr=extras.ip)
264 278 repo = audit_logger.RepoWrap(repo_name=extras.repository)
265 279 audit_logger.store(
266 280 'user.push', action_data={
267 281 'user_agent': extras.user_agent,
268 282 'commit_ids': commit_ids[:400]},
269 283 user=audit_user, repo=repo, commit=True)
270 284
271 285 statsd = StatsdClient.statsd
272 286 if statsd:
273 287 statsd.incr('rhodecode_push_total', tags=[
274 288 f'user-agent:{user_agent_normalizer(extras.user_agent)}',
275 289 ])
276 290
277 291 # Propagate to external components.
278 292 output = ''
279 293 # make lock is a tri state False, True, None. We only release lock on False
280 294 if extras.make_lock is False and not is_shadow_repo(extras):
281 295 Repository.unlock(Repository.get_by_repo_name(extras.repository))
282 296 msg = f'Released lock on repo `{extras.repository}`\n'
283 297 output += msg
284 298
285 299 if extras.locked_by[0]:
286 300 locked_by = User.get(extras.locked_by[0]).username
287 301 reason = extras.locked_by[2]
288 302 _http_ret = HTTPLockedRC(
289 303 _locked_by_explanation(extras.repository, locked_by, reason))
290 304 # TODO: johbo: if not?
291 305 if str(_http_ret.code).startswith('2'):
292 306 # 2xx Codes don't raise exceptions
293 307 output += _http_ret.title
294 308
295 309 if extras.new_refs:
296 310 tmpl = '{}/{}/pull-request/new?{{ref_type}}={{ref_name}}'.format(
297 311 safe_str(extras.server_url), safe_str(extras.repository))
298 312
299 313 for branch_name in extras.new_refs['branches']:
300 314 pr_link = tmpl.format(ref_type='branch', ref_name=safe_str(branch_name))
301 315 output += f'RhodeCode: open pull request link: {pr_link}\n'
302 316
303 317 for book_name in extras.new_refs['bookmarks']:
304 318 pr_link = tmpl.format(ref_type='bookmark', ref_name=safe_str(book_name))
305 319 output += f'RhodeCode: open pull request link: {pr_link}\n'
306 320
307 321 hook_response = ''
308 322 if not is_shadow_repo(extras):
309 323 hook_response = post_push_extension(
310 324 repo_store_path=Repository.base_path(),
311 325 **extras)
312 326 events.trigger(events.RepoPushEvent(
313 327 repo_name=extras.repository, pushed_commit_ids=commit_ids, extras=extras))
314 328
315 329 output += 'RhodeCode: push completed\n'
316 330 return HookResponse(0, output) + hook_response
317 331
318 332
319 333 def _locked_by_explanation(repo_name, user_name, reason):
320 334 message = f'Repository `{repo_name}` locked by user `{user_name}`. Reason:`{reason}`'
321 335 return message
322 336
323 337
324 338 def check_allowed_create_user(user_dict, created_by, **kwargs):
325 339 # pre create hooks
326 340 if pre_create_user.is_active():
327 341 hook_result = pre_create_user(created_by=created_by, **user_dict)
328 342 allowed = hook_result.status == 0
329 343 if not allowed:
330 344 reason = hook_result.output
331 345 raise UserCreationError(reason)
332 346
333 347
334 348 class ExtensionCallback(object):
335 349 """
336 350 Forwards a given call to rcextensions, sanitizes keyword arguments.
337 351
338 352 Does check if there is an extension active for that hook. If it is
339 353 there, it will forward all `kwargs_keys` keyword arguments to the
340 354 extension callback.
341 355 """
342 356
343 357 def __init__(self, hook_name, kwargs_keys):
344 358 self._hook_name = hook_name
345 359 self._kwargs_keys = set(kwargs_keys)
346 360
347 361 def __call__(self, *args, **kwargs):
348 362 log.debug('Calling extension callback for `%s`', self._hook_name)
349 363 callback = self._get_callback()
350 364 if not callback:
351 365 log.debug('extension callback `%s` not found, skipping...', self._hook_name)
352 366 return
353 367
354 368 kwargs_to_pass = {}
355 369 for key in self._kwargs_keys:
356 370 try:
357 371 kwargs_to_pass[key] = kwargs[key]
358 372 except KeyError:
359 373 log.error('Failed to fetch %s key from given kwargs. '
360 374 'Expected keys: %s', key, self._kwargs_keys)
361 375 raise
362 376
363 377 # backward compat for removed api_key for old hooks. This was it works
364 378 # with older rcextensions that require api_key present
365 379 if self._hook_name in ['CREATE_USER_HOOK', 'DELETE_USER_HOOK']:
366 380 kwargs_to_pass['api_key'] = '_DEPRECATED_'
367 381 return callback(**kwargs_to_pass)
368 382
369 383 def is_active(self):
370 384 return hasattr(rhodecode.EXTENSIONS, self._hook_name)
371 385
372 386 def _get_callback(self):
373 387 return getattr(rhodecode.EXTENSIONS, self._hook_name, None)
374 388
375 389
376 390 pre_pull_extension = ExtensionCallback(
377 391 hook_name='PRE_PULL_HOOK',
378 392 kwargs_keys=(
379 393 'server_url', 'config', 'scm', 'username', 'ip', 'action',
380 394 'repository', 'hook_type', 'user_agent', 'repo_store_path',))
381 395
382 396
383 397 post_pull_extension = ExtensionCallback(
384 398 hook_name='PULL_HOOK',
385 399 kwargs_keys=(
386 400 'server_url', 'config', 'scm', 'username', 'ip', 'action',
387 401 'repository', 'hook_type', 'user_agent', 'repo_store_path',))
388 402
389 403
390 404 pre_push_extension = ExtensionCallback(
391 405 hook_name='PRE_PUSH_HOOK',
392 406 kwargs_keys=(
393 407 'server_url', 'config', 'scm', 'username', 'ip', 'action',
394 408 'repository', 'repo_store_path', 'commit_ids', 'hook_type', 'user_agent',))
395 409
396 410
397 411 post_push_extension = ExtensionCallback(
398 412 hook_name='PUSH_HOOK',
399 413 kwargs_keys=(
400 414 'server_url', 'config', 'scm', 'username', 'ip', 'action',
401 415 'repository', 'repo_store_path', 'commit_ids', 'hook_type', 'user_agent',))
402 416
403 417
404 418 pre_create_user = ExtensionCallback(
405 419 hook_name='PRE_CREATE_USER_HOOK',
406 420 kwargs_keys=(
407 421 'username', 'password', 'email', 'firstname', 'lastname', 'active',
408 422 'admin', 'created_by'))
409 423
410 424
411 425 create_pull_request = ExtensionCallback(
412 426 hook_name='CREATE_PULL_REQUEST',
413 427 kwargs_keys=(
414 428 'server_url', 'config', 'scm', 'username', 'ip', 'action',
415 429 'repository', 'pull_request_id', 'url', 'title', 'description',
416 430 'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
417 431 'mergeable', 'source', 'target', 'author', 'reviewers'))
418 432
419 433
420 434 merge_pull_request = ExtensionCallback(
421 435 hook_name='MERGE_PULL_REQUEST',
422 436 kwargs_keys=(
423 437 'server_url', 'config', 'scm', 'username', 'ip', 'action',
424 438 'repository', 'pull_request_id', 'url', 'title', 'description',
425 439 'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
426 440 'mergeable', 'source', 'target', 'author', 'reviewers'))
427 441
428 442
429 443 close_pull_request = ExtensionCallback(
430 444 hook_name='CLOSE_PULL_REQUEST',
431 445 kwargs_keys=(
432 446 'server_url', 'config', 'scm', 'username', 'ip', 'action',
433 447 'repository', 'pull_request_id', 'url', 'title', 'description',
434 448 'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
435 449 'mergeable', 'source', 'target', 'author', 'reviewers'))
436 450
437 451
438 452 review_pull_request = ExtensionCallback(
439 453 hook_name='REVIEW_PULL_REQUEST',
440 454 kwargs_keys=(
441 455 'server_url', 'config', 'scm', 'username', 'ip', 'action',
442 456 'repository', 'pull_request_id', 'url', 'title', 'description',
443 457 'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
444 458 'mergeable', 'source', 'target', 'author', 'reviewers'))
445 459
446 460
447 461 comment_pull_request = ExtensionCallback(
448 462 hook_name='COMMENT_PULL_REQUEST',
449 463 kwargs_keys=(
450 464 'server_url', 'config', 'scm', 'username', 'ip', 'action',
451 465 'repository', 'pull_request_id', 'url', 'title', 'description',
452 466 'status', 'comment', 'created_on', 'updated_on', 'commit_ids', 'review_status',
453 467 'mergeable', 'source', 'target', 'author', 'reviewers'))
454 468
455 469
456 470 comment_edit_pull_request = ExtensionCallback(
457 471 hook_name='COMMENT_EDIT_PULL_REQUEST',
458 472 kwargs_keys=(
459 473 'server_url', 'config', 'scm', 'username', 'ip', 'action',
460 474 'repository', 'pull_request_id', 'url', 'title', 'description',
461 475 'status', 'comment', 'created_on', 'updated_on', 'commit_ids', 'review_status',
462 476 'mergeable', 'source', 'target', 'author', 'reviewers'))
463 477
464 478
465 479 update_pull_request = ExtensionCallback(
466 480 hook_name='UPDATE_PULL_REQUEST',
467 481 kwargs_keys=(
468 482 'server_url', 'config', 'scm', 'username', 'ip', 'action',
469 483 'repository', 'pull_request_id', 'url', 'title', 'description',
470 484 'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
471 485 'mergeable', 'source', 'target', 'author', 'reviewers'))
472 486
473 487
474 488 create_user = ExtensionCallback(
475 489 hook_name='CREATE_USER_HOOK',
476 490 kwargs_keys=(
477 491 'username', 'full_name_or_username', 'full_contact', 'user_id',
478 492 'name', 'firstname', 'short_contact', 'admin', 'lastname',
479 493 'ip_addresses', 'extern_type', 'extern_name',
480 494 'email', 'api_keys', 'last_login',
481 495 'full_name', 'active', 'password', 'emails',
482 496 'inherit_default_permissions', 'created_by', 'created_on'))
483 497
484 498
485 499 delete_user = ExtensionCallback(
486 500 hook_name='DELETE_USER_HOOK',
487 501 kwargs_keys=(
488 502 'username', 'full_name_or_username', 'full_contact', 'user_id',
489 503 'name', 'firstname', 'short_contact', 'admin', 'lastname',
490 504 'ip_addresses',
491 505 'email', 'last_login',
492 506 'full_name', 'active', 'password', 'emails',
493 507 'inherit_default_permissions', 'deleted_by'))
494 508
495 509
496 510 create_repository = ExtensionCallback(
497 511 hook_name='CREATE_REPO_HOOK',
498 512 kwargs_keys=(
499 513 'repo_name', 'repo_type', 'description', 'private', 'created_on',
500 514 'enable_downloads', 'repo_id', 'user_id', 'enable_statistics',
501 515 'clone_uri', 'fork_id', 'group_id', 'created_by'))
502 516
503 517
504 518 delete_repository = ExtensionCallback(
505 519 hook_name='DELETE_REPO_HOOK',
506 520 kwargs_keys=(
507 521 'repo_name', 'repo_type', 'description', 'private', 'created_on',
508 522 'enable_downloads', 'repo_id', 'user_id', 'enable_statistics',
509 523 'clone_uri', 'fork_id', 'group_id', 'deleted_by', 'deleted_on'))
510 524
511 525
512 526 comment_commit_repository = ExtensionCallback(
513 527 hook_name='COMMENT_COMMIT_REPO_HOOK',
514 528 kwargs_keys=(
515 529 'repo_name', 'repo_type', 'description', 'private', 'created_on',
516 530 'enable_downloads', 'repo_id', 'user_id', 'enable_statistics',
517 531 'clone_uri', 'fork_id', 'group_id',
518 532 'repository', 'created_by', 'comment', 'commit'))
519 533
520 534 comment_edit_commit_repository = ExtensionCallback(
521 535 hook_name='COMMENT_EDIT_COMMIT_REPO_HOOK',
522 536 kwargs_keys=(
523 537 'repo_name', 'repo_type', 'description', 'private', 'created_on',
524 538 'enable_downloads', 'repo_id', 'user_id', 'enable_statistics',
525 539 'clone_uri', 'fork_id', 'group_id',
526 540 'repository', 'created_by', 'comment', 'commit'))
527 541
528 542
529 543 create_repository_group = ExtensionCallback(
530 544 hook_name='CREATE_REPO_GROUP_HOOK',
531 545 kwargs_keys=(
532 546 'group_name', 'group_parent_id', 'group_description',
533 547 'group_id', 'user_id', 'created_by', 'created_on',
534 548 'enable_locking'))
@@ -1,824 +1,827 b''
1 1 # Copyright (C) 2010-2023 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
5 5 # (only), as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU Affero General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 #
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 """
20 20 Utilities library for RhodeCode
21 21 """
22 22
23 23 import datetime
24 24
25 25 import decorator
26 26 import logging
27 27 import os
28 28 import re
29 29 import sys
30 30 import shutil
31 31 import socket
32 32 import tempfile
33 33 import traceback
34 34 import tarfile
35 35
36 36 from functools import wraps
37 37 from os.path import join as jn
38 38
39 39 import paste
40 40 import pkg_resources
41 41 from webhelpers2.text import collapse, strip_tags, convert_accented_entities, convert_misc_entities
42 42
43 43 from mako import exceptions
44 44
45 45 from rhodecode.lib.hash_utils import sha256_safe, md5, sha1
46 46 from rhodecode.lib.type_utils import AttributeDict
47 47 from rhodecode.lib.str_utils import safe_bytes, safe_str
48 48 from rhodecode.lib.vcs.backends.base import Config
49 49 from rhodecode.lib.vcs.exceptions import VCSError
50 50 from rhodecode.lib.vcs.utils.helpers import get_scm, get_scm_backend
51 51 from rhodecode.lib.ext_json import sjson as json
52 52 from rhodecode.model import meta
53 53 from rhodecode.model.db import (
54 54 Repository, User, RhodeCodeUi, UserLog, RepoGroup, UserGroup)
55 55 from rhodecode.model.meta import Session
56 56
57 57
58 58 log = logging.getLogger(__name__)
59 59
60 60 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
61 61
62 62 # String which contains characters that are not allowed in slug names for
63 63 # repositories or repository groups. It is properly escaped to use it in
64 64 # regular expressions.
65 65 SLUG_BAD_CHARS = re.escape(r'`?=[]\;\'"<>,/~!@#$%^&*()+{}|:')
66 66
67 67 # Regex that matches forbidden characters in repo/group slugs.
68 68 SLUG_BAD_CHAR_RE = re.compile(r'[{}\x00-\x08\x0b-\x0c\x0e-\x1f]'.format(SLUG_BAD_CHARS))
69 69
70 70 # Regex that matches allowed characters in repo/group slugs.
71 71 SLUG_GOOD_CHAR_RE = re.compile(r'[^{}]'.format(SLUG_BAD_CHARS))
72 72
73 73 # Regex that matches whole repo/group slugs.
74 74 SLUG_RE = re.compile(r'[^{}]+'.format(SLUG_BAD_CHARS))
75 75
76 76 _license_cache = None
77 77
78 78
79 79 def adopt_for_celery(func):
80 80 """
81 81 Decorator designed to adopt hooks (from rhodecode.lib.hooks_base)
82 82 for further usage as a celery tasks.
83 83 """
84 84 @wraps(func)
85 85 def wrapper(extras):
86 86 extras = AttributeDict(extras)
87 # HooksResponse implements to_json method which must be used there.
88 return func(extras).to_json()
87 try:
88 # HooksResponse implements to_json method which must be used there.
89 return func(extras).to_json()
90 except Exception as e:
91 return {'status': 128, 'exception': type(e).__name__, 'exception_args': e.args}
89 92 return wrapper
90 93
91 94
92 95 def repo_name_slug(value):
93 96 """
94 97 Return slug of name of repository
95 98 This function is called on each creation/modification
96 99 of repository to prevent bad names in repo
97 100 """
98 101
99 102 replacement_char = '-'
100 103
101 104 slug = strip_tags(value)
102 105 slug = convert_accented_entities(slug)
103 106 slug = convert_misc_entities(slug)
104 107
105 108 slug = SLUG_BAD_CHAR_RE.sub('', slug)
106 109 slug = re.sub(r'[\s]+', '-', slug)
107 110 slug = collapse(slug, replacement_char)
108 111
109 112 return slug
110 113
111 114
112 115 #==============================================================================
113 116 # PERM DECORATOR HELPERS FOR EXTRACTING NAMES FOR PERM CHECKS
114 117 #==============================================================================
115 118 def get_repo_slug(request):
116 119 _repo = ''
117 120
118 121 if hasattr(request, 'db_repo_name'):
119 122 # if our requests has set db reference use it for name, this
120 123 # translates the example.com/_<id> into proper repo names
121 124 _repo = request.db_repo_name
122 125 elif getattr(request, 'matchdict', None):
123 126 # pyramid
124 127 _repo = request.matchdict.get('repo_name')
125 128
126 129 if _repo:
127 130 _repo = _repo.rstrip('/')
128 131 return _repo
129 132
130 133
131 134 def get_repo_group_slug(request):
132 135 _group = ''
133 136 if hasattr(request, 'db_repo_group'):
134 137 # if our requests has set db reference use it for name, this
135 138 # translates the example.com/_<id> into proper repo group names
136 139 _group = request.db_repo_group.group_name
137 140 elif getattr(request, 'matchdict', None):
138 141 # pyramid
139 142 _group = request.matchdict.get('repo_group_name')
140 143
141 144 if _group:
142 145 _group = _group.rstrip('/')
143 146 return _group
144 147
145 148
146 149 def get_user_group_slug(request):
147 150 _user_group = ''
148 151
149 152 if hasattr(request, 'db_user_group'):
150 153 _user_group = request.db_user_group.users_group_name
151 154 elif getattr(request, 'matchdict', None):
152 155 # pyramid
153 156 _user_group = request.matchdict.get('user_group_id')
154 157 _user_group_name = request.matchdict.get('user_group_name')
155 158 try:
156 159 if _user_group:
157 160 _user_group = UserGroup.get(_user_group)
158 161 elif _user_group_name:
159 162 _user_group = UserGroup.get_by_group_name(_user_group_name)
160 163
161 164 if _user_group:
162 165 _user_group = _user_group.users_group_name
163 166 except Exception:
164 167 log.exception('Failed to get user group by id and name')
165 168 # catch all failures here
166 169 return None
167 170
168 171 return _user_group
169 172
170 173
171 174 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
172 175 """
173 176 Scans given path for repos and return (name,(type,path)) tuple
174 177
175 178 :param path: path to scan for repositories
176 179 :param recursive: recursive search and return names with subdirs in front
177 180 """
178 181
179 182 # remove ending slash for better results
180 183 path = path.rstrip(os.sep)
181 184 log.debug('now scanning in %s location recursive:%s...', path, recursive)
182 185
183 186 def _get_repos(p):
184 187 dirpaths = get_dirpaths(p)
185 188 if not _is_dir_writable(p):
186 189 log.warning('repo path without write access: %s', p)
187 190
188 191 for dirpath in dirpaths:
189 192 if os.path.isfile(os.path.join(p, dirpath)):
190 193 continue
191 194 cur_path = os.path.join(p, dirpath)
192 195
193 196 # skip removed repos
194 197 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
195 198 continue
196 199
197 200 #skip .<somethin> dirs
198 201 if dirpath.startswith('.'):
199 202 continue
200 203
201 204 try:
202 205 scm_info = get_scm(cur_path)
203 206 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
204 207 except VCSError:
205 208 if not recursive:
206 209 continue
207 210 #check if this dir containts other repos for recursive scan
208 211 rec_path = os.path.join(p, dirpath)
209 212 if os.path.isdir(rec_path):
210 213 yield from _get_repos(rec_path)
211 214
212 215 return _get_repos(path)
213 216
214 217
215 218 def get_dirpaths(p: str) -> list:
216 219 try:
217 220 # OS-independable way of checking if we have at least read-only
218 221 # access or not.
219 222 dirpaths = os.listdir(p)
220 223 except OSError:
221 224 log.warning('ignoring repo path without read access: %s', p)
222 225 return []
223 226
224 227 # os.listpath has a tweak: If a unicode is passed into it, then it tries to
225 228 # decode paths and suddenly returns unicode objects itself. The items it
226 229 # cannot decode are returned as strings and cause issues.
227 230 #
228 231 # Those paths are ignored here until a solid solution for path handling has
229 232 # been built.
230 233 expected_type = type(p)
231 234
232 235 def _has_correct_type(item):
233 236 if type(item) is not expected_type:
234 237 log.error(
235 238 "Ignoring path %s since it cannot be decoded into str.",
236 239 # Using "repr" to make sure that we see the byte value in case
237 240 # of support.
238 241 repr(item))
239 242 return False
240 243 return True
241 244
242 245 dirpaths = [item for item in dirpaths if _has_correct_type(item)]
243 246
244 247 return dirpaths
245 248
246 249
247 250 def _is_dir_writable(path):
248 251 """
249 252 Probe if `path` is writable.
250 253
251 254 Due to trouble on Cygwin / Windows, this is actually probing if it is
252 255 possible to create a file inside of `path`, stat does not produce reliable
253 256 results in this case.
254 257 """
255 258 try:
256 259 with tempfile.TemporaryFile(dir=path):
257 260 pass
258 261 except OSError:
259 262 return False
260 263 return True
261 264
262 265
263 266 def is_valid_repo(repo_name, base_path, expect_scm=None, explicit_scm=None, config=None):
264 267 """
265 268 Returns True if given path is a valid repository False otherwise.
266 269 If expect_scm param is given also, compare if given scm is the same
267 270 as expected from scm parameter. If explicit_scm is given don't try to
268 271 detect the scm, just use the given one to check if repo is valid
269 272
270 273 :param repo_name:
271 274 :param base_path:
272 275 :param expect_scm:
273 276 :param explicit_scm:
274 277 :param config:
275 278
276 279 :return True: if given path is a valid repository
277 280 """
278 281 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
279 282 log.debug('Checking if `%s` is a valid path for repository. '
280 283 'Explicit type: %s', repo_name, explicit_scm)
281 284
282 285 try:
283 286 if explicit_scm:
284 287 detected_scms = [get_scm_backend(explicit_scm)(
285 288 full_path, config=config).alias]
286 289 else:
287 290 detected_scms = get_scm(full_path)
288 291
289 292 if expect_scm:
290 293 return detected_scms[0] == expect_scm
291 294 log.debug('path: %s is an vcs object:%s', full_path, detected_scms)
292 295 return True
293 296 except VCSError:
294 297 log.debug('path: %s is not a valid repo !', full_path)
295 298 return False
296 299
297 300
298 301 def is_valid_repo_group(repo_group_name, base_path, skip_path_check=False):
299 302 """
300 303 Returns True if a given path is a repository group, False otherwise
301 304
302 305 :param repo_group_name:
303 306 :param base_path:
304 307 """
305 308 full_path = os.path.join(safe_str(base_path), safe_str(repo_group_name))
306 309 log.debug('Checking if `%s` is a valid path for repository group',
307 310 repo_group_name)
308 311
309 312 # check if it's not a repo
310 313 if is_valid_repo(repo_group_name, base_path):
311 314 log.debug('Repo called %s exist, it is not a valid repo group', repo_group_name)
312 315 return False
313 316
314 317 try:
315 318 # we need to check bare git repos at higher level
316 319 # since we might match branches/hooks/info/objects or possible
317 320 # other things inside bare git repo
318 321 maybe_repo = os.path.dirname(full_path)
319 322 if maybe_repo == base_path:
320 323 # skip root level repo check; we know root location CANNOT BE a repo group
321 324 return False
322 325
323 326 scm_ = get_scm(maybe_repo)
324 327 log.debug('path: %s is a vcs object:%s, not valid repo group', full_path, scm_)
325 328 return False
326 329 except VCSError:
327 330 pass
328 331
329 332 # check if it's a valid path
330 333 if skip_path_check or os.path.isdir(full_path):
331 334 log.debug('path: %s is a valid repo group !', full_path)
332 335 return True
333 336
334 337 log.debug('path: %s is not a valid repo group !', full_path)
335 338 return False
336 339
337 340
338 341 def ask_ok(prompt, retries=4, complaint='[y]es or [n]o please!'):
339 342 while True:
340 343 ok = input(prompt)
341 344 if ok.lower() in ('y', 'ye', 'yes'):
342 345 return True
343 346 if ok.lower() in ('n', 'no', 'nop', 'nope'):
344 347 return False
345 348 retries = retries - 1
346 349 if retries < 0:
347 350 raise OSError
348 351 print(complaint)
349 352
350 353 # propagated from mercurial documentation
351 354 ui_sections = [
352 355 'alias', 'auth',
353 356 'decode/encode', 'defaults',
354 357 'diff', 'email',
355 358 'extensions', 'format',
356 359 'merge-patterns', 'merge-tools',
357 360 'hooks', 'http_proxy',
358 361 'smtp', 'patch',
359 362 'paths', 'profiling',
360 363 'server', 'trusted',
361 364 'ui', 'web', ]
362 365
363 366
364 367 def config_data_from_db(clear_session=True, repo=None):
365 368 """
366 369 Read the configuration data from the database and return configuration
367 370 tuples.
368 371 """
369 372 from rhodecode.model.settings import VcsSettingsModel
370 373
371 374 config = []
372 375
373 376 sa = meta.Session()
374 377 settings_model = VcsSettingsModel(repo=repo, sa=sa)
375 378
376 379 ui_settings = settings_model.get_ui_settings()
377 380
378 381 ui_data = []
379 382 for setting in ui_settings:
380 383 if setting.active:
381 384 ui_data.append((setting.section, setting.key, setting.value))
382 385 config.append((
383 386 safe_str(setting.section), safe_str(setting.key),
384 387 safe_str(setting.value)))
385 388 if setting.key == 'push_ssl':
386 389 # force set push_ssl requirement to False, rhodecode
387 390 # handles that
388 391 config.append((
389 392 safe_str(setting.section), safe_str(setting.key), False))
390 393 log.debug(
391 394 'settings ui from db@repo[%s]: %s',
392 395 repo,
393 396 ','.join(['[{}] {}={}'.format(*s) for s in ui_data]))
394 397 if clear_session:
395 398 meta.Session.remove()
396 399
397 400 # TODO: mikhail: probably it makes no sense to re-read hooks information.
398 401 # It's already there and activated/deactivated
399 402 skip_entries = []
400 403 enabled_hook_classes = get_enabled_hook_classes(ui_settings)
401 404 if 'pull' not in enabled_hook_classes:
402 405 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PULL))
403 406 if 'push' not in enabled_hook_classes:
404 407 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRE_PUSH))
405 408 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PRETX_PUSH))
406 409 skip_entries.append(('hooks', RhodeCodeUi.HOOK_PUSH_KEY))
407 410
408 411 config = [entry for entry in config if entry[:2] not in skip_entries]
409 412
410 413 return config
411 414
412 415
413 416 def make_db_config(clear_session=True, repo=None):
414 417 """
415 418 Create a :class:`Config` instance based on the values in the database.
416 419 """
417 420 config = Config()
418 421 config_data = config_data_from_db(clear_session=clear_session, repo=repo)
419 422 for section, option, value in config_data:
420 423 config.set(section, option, value)
421 424 return config
422 425
423 426
424 427 def get_enabled_hook_classes(ui_settings):
425 428 """
426 429 Return the enabled hook classes.
427 430
428 431 :param ui_settings: List of ui_settings as returned
429 432 by :meth:`VcsSettingsModel.get_ui_settings`
430 433
431 434 :return: a list with the enabled hook classes. The order is not guaranteed.
432 435 :rtype: list
433 436 """
434 437 enabled_hooks = []
435 438 active_hook_keys = [
436 439 key for section, key, value, active in ui_settings
437 440 if section == 'hooks' and active]
438 441
439 442 hook_names = {
440 443 RhodeCodeUi.HOOK_PUSH: 'push',
441 444 RhodeCodeUi.HOOK_PULL: 'pull',
442 445 RhodeCodeUi.HOOK_REPO_SIZE: 'repo_size'
443 446 }
444 447
445 448 for key in active_hook_keys:
446 449 hook = hook_names.get(key)
447 450 if hook:
448 451 enabled_hooks.append(hook)
449 452
450 453 return enabled_hooks
451 454
452 455
453 456 def set_rhodecode_config(config):
454 457 """
455 458 Updates pyramid config with new settings from database
456 459
457 460 :param config:
458 461 """
459 462 from rhodecode.model.settings import SettingsModel
460 463 app_settings = SettingsModel().get_all_settings()
461 464
462 465 for k, v in list(app_settings.items()):
463 466 config[k] = v
464 467
465 468
466 469 def get_rhodecode_realm():
467 470 """
468 471 Return the rhodecode realm from database.
469 472 """
470 473 from rhodecode.model.settings import SettingsModel
471 474 realm = SettingsModel().get_setting_by_name('realm')
472 475 return safe_str(realm.app_settings_value)
473 476
474 477
475 478 def get_rhodecode_repo_store_path():
476 479 """
477 480 Returns the base path. The base path is the filesystem path which points
478 481 to the repository store.
479 482 """
480 483
481 484 import rhodecode
482 485 return rhodecode.CONFIG['repo_store.path']
483 486
484 487
485 488 def map_groups(path):
486 489 """
487 490 Given a full path to a repository, create all nested groups that this
488 491 repo is inside. This function creates parent-child relationships between
489 492 groups and creates default perms for all new groups.
490 493
491 494 :param paths: full path to repository
492 495 """
493 496 from rhodecode.model.repo_group import RepoGroupModel
494 497 sa = meta.Session()
495 498 groups = path.split(Repository.NAME_SEP)
496 499 parent = None
497 500 group = None
498 501
499 502 # last element is repo in nested groups structure
500 503 groups = groups[:-1]
501 504 rgm = RepoGroupModel(sa)
502 505 owner = User.get_first_super_admin()
503 506 for lvl, group_name in enumerate(groups):
504 507 group_name = '/'.join(groups[:lvl] + [group_name])
505 508 group = RepoGroup.get_by_group_name(group_name)
506 509 desc = '%s group' % group_name
507 510
508 511 # skip folders that are now removed repos
509 512 if REMOVED_REPO_PAT.match(group_name):
510 513 break
511 514
512 515 if group is None:
513 516 log.debug('creating group level: %s group_name: %s',
514 517 lvl, group_name)
515 518 group = RepoGroup(group_name, parent)
516 519 group.group_description = desc
517 520 group.user = owner
518 521 sa.add(group)
519 522 perm_obj = rgm._create_default_perms(group)
520 523 sa.add(perm_obj)
521 524 sa.flush()
522 525
523 526 parent = group
524 527 return group
525 528
526 529
527 530 def repo2db_mapper(initial_repo_list, remove_obsolete=False, force_hooks_rebuild=False):
528 531 """
529 532 maps all repos given in initial_repo_list, non existing repositories
530 533 are created, if remove_obsolete is True it also checks for db entries
531 534 that are not in initial_repo_list and removes them.
532 535
533 536 :param initial_repo_list: list of repositories found by scanning methods
534 537 :param remove_obsolete: check for obsolete entries in database
535 538 """
536 539 from rhodecode.model.repo import RepoModel
537 540 from rhodecode.model.repo_group import RepoGroupModel
538 541 from rhodecode.model.settings import SettingsModel
539 542
540 543 sa = meta.Session()
541 544 repo_model = RepoModel()
542 545 user = User.get_first_super_admin()
543 546 added = []
544 547
545 548 # creation defaults
546 549 defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
547 550 enable_statistics = defs.get('repo_enable_statistics')
548 551 enable_locking = defs.get('repo_enable_locking')
549 552 enable_downloads = defs.get('repo_enable_downloads')
550 553 private = defs.get('repo_private')
551 554
552 555 for name, repo in list(initial_repo_list.items()):
553 556 group = map_groups(name)
554 557 str_name = safe_str(name)
555 558 db_repo = repo_model.get_by_repo_name(str_name)
556 559
557 560 # found repo that is on filesystem not in RhodeCode database
558 561 if not db_repo:
559 562 log.info('repository `%s` not found in the database, creating now', name)
560 563 added.append(name)
561 564 desc = (repo.description
562 565 if repo.description != 'unknown'
563 566 else '%s repository' % name)
564 567
565 568 db_repo = repo_model._create_repo(
566 569 repo_name=name,
567 570 repo_type=repo.alias,
568 571 description=desc,
569 572 repo_group=getattr(group, 'group_id', None),
570 573 owner=user,
571 574 enable_locking=enable_locking,
572 575 enable_downloads=enable_downloads,
573 576 enable_statistics=enable_statistics,
574 577 private=private,
575 578 state=Repository.STATE_CREATED
576 579 )
577 580 sa.commit()
578 581 # we added that repo just now, and make sure we updated server info
579 582 if db_repo.repo_type == 'git':
580 583 git_repo = db_repo.scm_instance()
581 584 # update repository server-info
582 585 log.debug('Running update server info')
583 586 git_repo._update_server_info(force=True)
584 587
585 588 db_repo.update_commit_cache(recursive=False)
586 589
587 590 config = db_repo._config
588 591 config.set('extensions', 'largefiles', '')
589 592 repo = db_repo.scm_instance(config=config)
590 593 repo.install_hooks(force=force_hooks_rebuild)
591 594
592 595 removed = []
593 596 if remove_obsolete:
594 597 # remove from database those repositories that are not in the filesystem
595 598 for repo in sa.query(Repository).all():
596 599 if repo.repo_name not in list(initial_repo_list.keys()):
597 600 log.debug("Removing non-existing repository found in db `%s`",
598 601 repo.repo_name)
599 602 try:
600 603 RepoModel(sa).delete(repo, forks='detach', fs_remove=False)
601 604 sa.commit()
602 605 removed.append(repo.repo_name)
603 606 except Exception:
604 607 # don't hold further removals on error
605 608 log.error(traceback.format_exc())
606 609 sa.rollback()
607 610
608 611 def splitter(full_repo_name):
609 612 _parts = full_repo_name.rsplit(RepoGroup.url_sep(), 1)
610 613 gr_name = None
611 614 if len(_parts) == 2:
612 615 gr_name = _parts[0]
613 616 return gr_name
614 617
615 618 initial_repo_group_list = [splitter(x) for x in
616 619 list(initial_repo_list.keys()) if splitter(x)]
617 620
618 621 # remove from database those repository groups that are not in the
619 622 # filesystem due to parent child relationships we need to delete them
620 623 # in a specific order of most nested first
621 624 all_groups = [x.group_name for x in sa.query(RepoGroup).all()]
622 625 def nested_sort(gr):
623 626 return len(gr.split('/'))
624 627 for group_name in sorted(all_groups, key=nested_sort, reverse=True):
625 628 if group_name not in initial_repo_group_list:
626 629 repo_group = RepoGroup.get_by_group_name(group_name)
627 630 if (repo_group.children.all() or
628 631 not RepoGroupModel().check_exist_filesystem(
629 632 group_name=group_name, exc_on_failure=False)):
630 633 continue
631 634
632 635 log.info(
633 636 'Removing non-existing repository group found in db `%s`',
634 637 group_name)
635 638 try:
636 639 RepoGroupModel(sa).delete(group_name, fs_remove=False)
637 640 sa.commit()
638 641 removed.append(group_name)
639 642 except Exception:
640 643 # don't hold further removals on error
641 644 log.exception(
642 645 'Unable to remove repository group `%s`',
643 646 group_name)
644 647 sa.rollback()
645 648 raise
646 649
647 650 return added, removed
648 651
649 652
650 653 def load_rcextensions(root_path):
651 654 import rhodecode
652 655 from rhodecode.config import conf
653 656
654 657 path = os.path.join(root_path)
655 658 sys.path.append(path)
656 659
657 660 try:
658 661 rcextensions = __import__('rcextensions')
659 662 except ImportError:
660 663 if os.path.isdir(os.path.join(path, 'rcextensions')):
661 664 log.warning('Unable to load rcextensions from %s', path)
662 665 rcextensions = None
663 666
664 667 if rcextensions:
665 668 log.info('Loaded rcextensions from %s...', rcextensions)
666 669 rhodecode.EXTENSIONS = rcextensions
667 670
668 671 # Additional mappings that are not present in the pygments lexers
669 672 conf.LANGUAGES_EXTENSIONS_MAP.update(
670 673 getattr(rhodecode.EXTENSIONS, 'EXTRA_MAPPINGS', {}))
671 674
672 675
673 676 def get_custom_lexer(extension):
674 677 """
675 678 returns a custom lexer if it is defined in rcextensions module, or None
676 679 if there's no custom lexer defined
677 680 """
678 681 import rhodecode
679 682 from pygments import lexers
680 683
681 684 # custom override made by RhodeCode
682 685 if extension in ['mako']:
683 686 return lexers.get_lexer_by_name('html+mako')
684 687
685 688 # check if we didn't define this extension as other lexer
686 689 extensions = rhodecode.EXTENSIONS and getattr(rhodecode.EXTENSIONS, 'EXTRA_LEXERS', None)
687 690 if extensions and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
688 691 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
689 692 return lexers.get_lexer_by_name(_lexer_name)
690 693
691 694
692 695 #==============================================================================
693 696 # TEST FUNCTIONS AND CREATORS
694 697 #==============================================================================
695 698 def create_test_index(repo_location, config):
696 699 """
697 700 Makes default test index.
698 701 """
699 702 try:
700 703 import rc_testdata
701 704 except ImportError:
702 705 raise ImportError('Failed to import rc_testdata, '
703 706 'please make sure this package is installed from requirements_test.txt')
704 707 rc_testdata.extract_search_index(
705 708 'vcs_search_index', os.path.dirname(config['search.location']))
706 709
707 710
708 711 def create_test_directory(test_path):
709 712 """
710 713 Create test directory if it doesn't exist.
711 714 """
712 715 if not os.path.isdir(test_path):
713 716 log.debug('Creating testdir %s', test_path)
714 717 os.makedirs(test_path)
715 718
716 719
717 720 def create_test_database(test_path, config):
718 721 """
719 722 Makes a fresh database.
720 723 """
721 724 from rhodecode.lib.db_manage import DbManage
722 725 from rhodecode.lib.utils2 import get_encryption_key
723 726
724 727 # PART ONE create db
725 728 dbconf = config['sqlalchemy.db1.url']
726 729 enc_key = get_encryption_key(config)
727 730
728 731 log.debug('making test db %s', dbconf)
729 732
730 733 dbmanage = DbManage(log_sql=False, dbconf=dbconf, root=config['here'],
731 734 tests=True, cli_args={'force_ask': True}, enc_key=enc_key)
732 735 dbmanage.create_tables(override=True)
733 736 dbmanage.set_db_version()
734 737 # for tests dynamically set new root paths based on generated content
735 738 dbmanage.create_settings(dbmanage.config_prompt(test_path))
736 739 dbmanage.create_default_user()
737 740 dbmanage.create_test_admin_and_users()
738 741 dbmanage.create_permissions()
739 742 dbmanage.populate_default_permissions()
740 743 Session().commit()
741 744
742 745
743 746 def create_test_repositories(test_path, config):
744 747 """
745 748 Creates test repositories in the temporary directory. Repositories are
746 749 extracted from archives within the rc_testdata package.
747 750 """
748 751 import rc_testdata
749 752 from rhodecode.tests import HG_REPO, GIT_REPO, SVN_REPO
750 753
751 754 log.debug('making test vcs repositories')
752 755
753 756 idx_path = config['search.location']
754 757 data_path = config['cache_dir']
755 758
756 759 # clean index and data
757 760 if idx_path and os.path.exists(idx_path):
758 761 log.debug('remove %s', idx_path)
759 762 shutil.rmtree(idx_path)
760 763
761 764 if data_path and os.path.exists(data_path):
762 765 log.debug('remove %s', data_path)
763 766 shutil.rmtree(data_path)
764 767
765 768 rc_testdata.extract_hg_dump('vcs_test_hg', jn(test_path, HG_REPO))
766 769 rc_testdata.extract_git_dump('vcs_test_git', jn(test_path, GIT_REPO))
767 770
768 771 # Note: Subversion is in the process of being integrated with the system,
769 772 # until we have a properly packed version of the test svn repository, this
770 773 # tries to copy over the repo from a package "rc_testdata"
771 774 svn_repo_path = rc_testdata.get_svn_repo_archive()
772 775 with tarfile.open(svn_repo_path) as tar:
773 776 tar.extractall(jn(test_path, SVN_REPO))
774 777
775 778
776 779 def password_changed(auth_user, session):
777 780 # Never report password change in case of default user or anonymous user.
778 781 if auth_user.username == User.DEFAULT_USER or auth_user.user_id is None:
779 782 return False
780 783
781 784 password_hash = md5(safe_bytes(auth_user.password)) if auth_user.password else None
782 785 rhodecode_user = session.get('rhodecode_user', {})
783 786 session_password_hash = rhodecode_user.get('password', '')
784 787 return password_hash != session_password_hash
785 788
786 789
787 790 def read_opensource_licenses():
788 791 global _license_cache
789 792
790 793 if not _license_cache:
791 794 licenses = pkg_resources.resource_string(
792 795 'rhodecode', 'config/licenses.json')
793 796 _license_cache = json.loads(licenses)
794 797
795 798 return _license_cache
796 799
797 800
798 801 def generate_platform_uuid():
799 802 """
800 803 Generates platform UUID based on it's name
801 804 """
802 805 import platform
803 806
804 807 try:
805 808 uuid_list = [platform.platform()]
806 809 return sha256_safe(':'.join(uuid_list))
807 810 except Exception as e:
808 811 log.error('Failed to generate host uuid: %s', e)
809 812 return 'UNDEFINED'
810 813
811 814
812 815 def send_test_email(recipients, email_body='TEST EMAIL'):
813 816 """
814 817 Simple code for generating test emails.
815 818 Usage::
816 819
817 820 from rhodecode.lib import utils
818 821 utils.send_test_email()
819 822 """
820 823 from rhodecode.lib.celerylib import tasks, run_task
821 824
822 825 email_body = email_body_plaintext = email_body
823 826 subject = f'SUBJECT FROM: {socket.gethostname()}'
824 827 tasks.send_email(recipients, subject, email_body_plaintext, email_body)
@@ -1,655 +1,669 b''
1 1 # Copyright (C) 2010-2023 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
5 5 # (only), as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU Affero General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 #
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 """
20 20 this is forms validation classes
21 21 http://formencode.org/module-formencode.validators.html
22 22 for list off all availible validators
23 23
24 24 we can create our own validators
25 25
26 26 The table below outlines the options which can be used in a schema in addition to the validators themselves
27 27 pre_validators [] These validators will be applied before the schema
28 28 chained_validators [] These validators will be applied after the schema
29 29 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
30 30 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
31 31 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
32 32 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
33 33
34 34
35 35 <name> = formencode.validators.<name of validator>
36 36 <name> must equal form name
37 37 list=[1,2,3,4,5]
38 38 for SELECT use formencode.All(OneOf(list), Int())
39 39
40 40 """
41 41
42 42 import deform
43 43 import logging
44 44 import formencode
45 45
46 46 from pkg_resources import resource_filename
47 47 from formencode import All, Pipe
48 48
49 49 from pyramid.threadlocal import get_current_request
50 50
51 51 from rhodecode import BACKENDS
52 52 from rhodecode.lib import helpers
53 53 from rhodecode.model import validators as v
54 54
55 55 log = logging.getLogger(__name__)
56 56
57 57
58 58 deform_templates = resource_filename('deform', 'templates')
59 59 rhodecode_templates = resource_filename('rhodecode', 'templates/forms')
60 60 search_path = (rhodecode_templates, deform_templates)
61 61
62 62
63 63 class RhodecodeFormZPTRendererFactory(deform.ZPTRendererFactory):
64 64 """ Subclass of ZPTRendererFactory to add rhodecode context variables """
65 65 def __call__(self, template_name, **kw):
66 66 kw['h'] = helpers
67 67 kw['request'] = get_current_request()
68 68 return self.load(template_name)(**kw)
69 69
70 70
71 71 form_renderer = RhodecodeFormZPTRendererFactory(search_path)
72 72 deform.Form.set_default_renderer(form_renderer)
73 73
74 74
75 75 def LoginForm(localizer):
76 76 _ = localizer
77 77
78 78 class _LoginForm(formencode.Schema):
79 79 allow_extra_fields = True
80 80 filter_extra_fields = True
81 81 username = v.UnicodeString(
82 82 strip=True,
83 83 min=1,
84 84 not_empty=True,
85 85 messages={
86 86 'empty': _('Please enter a login'),
87 87 'tooShort': _('Enter a value %(min)i characters long or more')
88 88 }
89 89 )
90 90
91 91 password = v.UnicodeString(
92 92 strip=False,
93 93 min=3,
94 94 max=72,
95 95 not_empty=True,
96 96 messages={
97 97 'empty': _('Please enter a password'),
98 98 'tooShort': _('Enter %(min)i characters or more')}
99 99 )
100 100
101 101 remember = v.StringBoolean(if_missing=False)
102 102
103 103 chained_validators = [v.ValidAuth(localizer)]
104 104 return _LoginForm
105 105
106 106
107 107 def TOTPForm(localizer, user, allow_recovery_code_use=False):
108 108 _ = localizer
109 109
110 110 class _TOTPForm(formencode.Schema):
111 111 allow_extra_fields = True
112 112 filter_extra_fields = False
113 113 totp = v.Regex(r'^(?:\d{6}|[A-Z0-9]{32})$')
114 114 secret_totp = v.String()
115 115
116 116 def to_python(self, value, state=None):
117 117 validation_checks = [user.is_totp_valid]
118 118 if allow_recovery_code_use:
119 119 validation_checks.append(user.is_2fa_recovery_code_valid)
120 120 form_data = super().to_python(value, state)
121 121 received_code = form_data['totp']
122 122 secret = form_data.get('secret_totp')
123 123
124 124 if not any(map(lambda func: func(received_code, secret), validation_checks)):
125 125 error_msg = _('Code is invalid. Try again!')
126 126 raise formencode.Invalid(error_msg, v, state, error_dict={'totp': error_msg})
127 127 return form_data
128 128
129 129 return _TOTPForm
130 130
131 131
132 def WhitelistedVcsClientsForm(localizer):
133 _ = localizer
134
135 class _WhitelistedVcsClientsForm(formencode.Schema):
136 regexp = r'^(?:\s*[<>=~^!]*\s*\d{1,2}\.\d{1,2}(?:\.\d{1,2})?\s*|\*)\s*(?:,\s*[<>=~^!]*\s*\d{1,2}\.\d{1,2}(?:\.\d{1,2})?\s*|\s*\*\s*)*$'
137 allow_extra_fields = True
138 filter_extra_fields = True
139 git = v.Regex(regexp)
140 hg = v.Regex(regexp)
141 svn = v.Regex(regexp)
142
143 return _WhitelistedVcsClientsForm
144
145
132 146 def UserForm(localizer, edit=False, available_languages=None, old_data=None):
133 147 old_data = old_data or {}
134 148 available_languages = available_languages or []
135 149 _ = localizer
136 150
137 151 class _UserForm(formencode.Schema):
138 152 allow_extra_fields = True
139 153 filter_extra_fields = True
140 154 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
141 155 v.ValidUsername(localizer, edit, old_data))
142 156 if edit:
143 157 new_password = All(
144 158 v.ValidPassword(localizer),
145 159 v.UnicodeString(strip=False, min=6, max=72, not_empty=False)
146 160 )
147 161 password_confirmation = All(
148 162 v.ValidPassword(localizer),
149 163 v.UnicodeString(strip=False, min=6, max=72, not_empty=False),
150 164 )
151 165 admin = v.StringBoolean(if_missing=False)
152 166 else:
153 167 password = All(
154 168 v.ValidPassword(localizer),
155 169 v.UnicodeString(strip=False, min=6, max=72, not_empty=True)
156 170 )
157 171 password_confirmation = All(
158 172 v.ValidPassword(localizer),
159 173 v.UnicodeString(strip=False, min=6, max=72, not_empty=False)
160 174 )
161 175
162 176 password_change = v.StringBoolean(if_missing=False)
163 177 create_repo_group = v.StringBoolean(if_missing=False)
164 178
165 179 active = v.StringBoolean(if_missing=False)
166 180 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
167 181 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
168 182 email = All(v.UniqSystemEmail(localizer, old_data), v.Email(not_empty=True))
169 183 description = v.UnicodeString(strip=True, min=1, max=250, not_empty=False,
170 184 if_missing='')
171 185 extern_name = v.UnicodeString(strip=True)
172 186 extern_type = v.UnicodeString(strip=True)
173 187 language = v.OneOf(available_languages, hideList=False,
174 188 testValueList=True, if_missing=None)
175 189 chained_validators = [v.ValidPasswordsMatch(localizer)]
176 190 return _UserForm
177 191
178 192
179 193 def UserGroupForm(localizer, edit=False, old_data=None, allow_disabled=False):
180 194 old_data = old_data or {}
181 195 _ = localizer
182 196
183 197 class _UserGroupForm(formencode.Schema):
184 198 allow_extra_fields = True
185 199 filter_extra_fields = True
186 200
187 201 users_group_name = All(
188 202 v.UnicodeString(strip=True, min=1, not_empty=True),
189 203 v.ValidUserGroup(localizer, edit, old_data)
190 204 )
191 205 user_group_description = v.UnicodeString(strip=True, min=1,
192 206 not_empty=False)
193 207
194 208 users_group_active = v.StringBoolean(if_missing=False)
195 209
196 210 if edit:
197 211 # this is user group owner
198 212 user = All(
199 213 v.UnicodeString(not_empty=True),
200 214 v.ValidRepoUser(localizer, allow_disabled))
201 215 return _UserGroupForm
202 216
203 217
204 218 def RepoGroupForm(localizer, edit=False, old_data=None, available_groups=None,
205 219 can_create_in_root=False, allow_disabled=False):
206 220 _ = localizer
207 221 old_data = old_data or {}
208 222 available_groups = available_groups or []
209 223
210 224 class _RepoGroupForm(formencode.Schema):
211 225 allow_extra_fields = True
212 226 filter_extra_fields = False
213 227
214 228 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
215 229 v.SlugifyName(localizer),)
216 230 group_description = v.UnicodeString(strip=True, min=1,
217 231 not_empty=False)
218 232 group_copy_permissions = v.StringBoolean(if_missing=False)
219 233
220 234 group_parent_id = v.OneOf(available_groups, hideList=False,
221 235 testValueList=True, not_empty=True)
222 236 enable_locking = v.StringBoolean(if_missing=False)
223 237 chained_validators = [
224 238 v.ValidRepoGroup(localizer, edit, old_data, can_create_in_root)]
225 239
226 240 if edit:
227 241 # this is repo group owner
228 242 user = All(
229 243 v.UnicodeString(not_empty=True),
230 244 v.ValidRepoUser(localizer, allow_disabled))
231 245 return _RepoGroupForm
232 246
233 247
234 248 def RegisterForm(localizer, edit=False, old_data=None):
235 249 _ = localizer
236 250 old_data = old_data or {}
237 251
238 252 class _RegisterForm(formencode.Schema):
239 253 allow_extra_fields = True
240 254 filter_extra_fields = True
241 255 username = All(
242 256 v.ValidUsername(localizer, edit, old_data),
243 257 v.UnicodeString(strip=True, min=1, not_empty=True)
244 258 )
245 259 password = All(
246 260 v.ValidPassword(localizer),
247 261 v.UnicodeString(strip=False, min=6, max=72, not_empty=True)
248 262 )
249 263 password_confirmation = All(
250 264 v.ValidPassword(localizer),
251 265 v.UnicodeString(strip=False, min=6, max=72, not_empty=True)
252 266 )
253 267 active = v.StringBoolean(if_missing=False)
254 268 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
255 269 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
256 270 email = All(v.UniqSystemEmail(localizer, old_data), v.Email(not_empty=True))
257 271
258 272 chained_validators = [v.ValidPasswordsMatch(localizer)]
259 273 return _RegisterForm
260 274
261 275
262 276 def PasswordResetForm(localizer):
263 277 _ = localizer
264 278
265 279 class _PasswordResetForm(formencode.Schema):
266 280 allow_extra_fields = True
267 281 filter_extra_fields = True
268 282 email = All(v.ValidSystemEmail(localizer), v.Email(not_empty=True))
269 283 return _PasswordResetForm
270 284
271 285
272 286 def RepoForm(localizer, edit=False, old_data=None, repo_groups=None, allow_disabled=False):
273 287 _ = localizer
274 288 old_data = old_data or {}
275 289 repo_groups = repo_groups or []
276 290 supported_backends = BACKENDS.keys()
277 291
278 292 class _RepoForm(formencode.Schema):
279 293 allow_extra_fields = True
280 294 filter_extra_fields = False
281 295 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
282 296 v.SlugifyName(localizer), v.CannotHaveGitSuffix(localizer))
283 297 repo_group = All(v.CanWriteGroup(localizer, old_data),
284 298 v.OneOf(repo_groups, hideList=True))
285 299 repo_type = v.OneOf(supported_backends, required=False,
286 300 if_missing=old_data.get('repo_type'))
287 301 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
288 302 repo_private = v.StringBoolean(if_missing=False)
289 303 repo_copy_permissions = v.StringBoolean(if_missing=False)
290 304 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
291 305
292 306 repo_enable_statistics = v.StringBoolean(if_missing=False)
293 307 repo_enable_downloads = v.StringBoolean(if_missing=False)
294 308 repo_enable_locking = v.StringBoolean(if_missing=False)
295 309
296 310 if edit:
297 311 # this is repo owner
298 312 user = All(
299 313 v.UnicodeString(not_empty=True),
300 314 v.ValidRepoUser(localizer, allow_disabled))
301 315 clone_uri_change = v.UnicodeString(
302 316 not_empty=False, if_missing=v.Missing)
303 317
304 318 chained_validators = [v.ValidCloneUri(localizer),
305 319 v.ValidRepoName(localizer, edit, old_data)]
306 320 return _RepoForm
307 321
308 322
309 323 def RepoPermsForm(localizer):
310 324 _ = localizer
311 325
312 326 class _RepoPermsForm(formencode.Schema):
313 327 allow_extra_fields = True
314 328 filter_extra_fields = False
315 329 chained_validators = [v.ValidPerms(localizer, type_='repo')]
316 330 return _RepoPermsForm
317 331
318 332
319 333 def RepoGroupPermsForm(localizer, valid_recursive_choices):
320 334 _ = localizer
321 335
322 336 class _RepoGroupPermsForm(formencode.Schema):
323 337 allow_extra_fields = True
324 338 filter_extra_fields = False
325 339 recursive = v.OneOf(valid_recursive_choices)
326 340 chained_validators = [v.ValidPerms(localizer, type_='repo_group')]
327 341 return _RepoGroupPermsForm
328 342
329 343
330 344 def UserGroupPermsForm(localizer):
331 345 _ = localizer
332 346
333 347 class _UserPermsForm(formencode.Schema):
334 348 allow_extra_fields = True
335 349 filter_extra_fields = False
336 350 chained_validators = [v.ValidPerms(localizer, type_='user_group')]
337 351 return _UserPermsForm
338 352
339 353
340 354 def RepoFieldForm(localizer):
341 355 _ = localizer
342 356
343 357 class _RepoFieldForm(formencode.Schema):
344 358 filter_extra_fields = True
345 359 allow_extra_fields = True
346 360
347 361 new_field_key = All(v.FieldKey(localizer),
348 362 v.UnicodeString(strip=True, min=3, not_empty=True))
349 363 new_field_value = v.UnicodeString(not_empty=False, if_missing='')
350 364 new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'],
351 365 if_missing='str')
352 366 new_field_label = v.UnicodeString(not_empty=False)
353 367 new_field_desc = v.UnicodeString(not_empty=False)
354 368 return _RepoFieldForm
355 369
356 370
357 371 def RepoForkForm(localizer, edit=False, old_data=None,
358 372 supported_backends=BACKENDS.keys(), repo_groups=None):
359 373 _ = localizer
360 374 old_data = old_data or {}
361 375 repo_groups = repo_groups or []
362 376
363 377 class _RepoForkForm(formencode.Schema):
364 378 allow_extra_fields = True
365 379 filter_extra_fields = False
366 380 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
367 381 v.SlugifyName(localizer))
368 382 repo_group = All(v.CanWriteGroup(localizer, ),
369 383 v.OneOf(repo_groups, hideList=True))
370 384 repo_type = All(v.ValidForkType(localizer, old_data), v.OneOf(supported_backends))
371 385 description = v.UnicodeString(strip=True, min=1, not_empty=True)
372 386 private = v.StringBoolean(if_missing=False)
373 387 copy_permissions = v.StringBoolean(if_missing=False)
374 388 fork_parent_id = v.UnicodeString()
375 389 chained_validators = [v.ValidForkName(localizer, edit, old_data)]
376 390 return _RepoForkForm
377 391
378 392
379 393 def ApplicationSettingsForm(localizer):
380 394 _ = localizer
381 395
382 396 class _ApplicationSettingsForm(formencode.Schema):
383 397 allow_extra_fields = True
384 398 filter_extra_fields = False
385 399 rhodecode_title = v.UnicodeString(strip=True, max=40, not_empty=False)
386 400 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
387 401 rhodecode_pre_code = v.UnicodeString(strip=True, min=1, not_empty=False)
388 402 rhodecode_post_code = v.UnicodeString(strip=True, min=1, not_empty=False)
389 403 rhodecode_captcha_public_key = v.UnicodeString(strip=True, min=1, not_empty=False)
390 404 rhodecode_captcha_private_key = v.UnicodeString(strip=True, min=1, not_empty=False)
391 405 rhodecode_create_personal_repo_group = v.StringBoolean(if_missing=False)
392 406 rhodecode_personal_repo_group_pattern = v.UnicodeString(strip=True, min=1, not_empty=False)
393 407 return _ApplicationSettingsForm
394 408
395 409
396 410 def ApplicationVisualisationForm(localizer):
397 411 from rhodecode.model.db import Repository
398 412 _ = localizer
399 413
400 414 class _ApplicationVisualisationForm(formencode.Schema):
401 415 allow_extra_fields = True
402 416 filter_extra_fields = False
403 417 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
404 418 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
405 419 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
406 420
407 421 rhodecode_repository_fields = v.StringBoolean(if_missing=False)
408 422 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
409 423 rhodecode_dashboard_items = v.Int(min=5, not_empty=True)
410 424 rhodecode_admin_grid_items = v.Int(min=5, not_empty=True)
411 425 rhodecode_show_version = v.StringBoolean(if_missing=False)
412 426 rhodecode_use_gravatar = v.StringBoolean(if_missing=False)
413 427 rhodecode_markup_renderer = v.OneOf(['markdown', 'rst'])
414 428 rhodecode_gravatar_url = v.UnicodeString(min=3)
415 429 rhodecode_clone_uri_tmpl = v.UnicodeString(not_empty=False, if_empty=Repository.DEFAULT_CLONE_URI)
416 430 rhodecode_clone_uri_id_tmpl = v.UnicodeString(not_empty=False, if_empty=Repository.DEFAULT_CLONE_URI_ID)
417 431 rhodecode_clone_uri_ssh_tmpl = v.UnicodeString(not_empty=False, if_empty=Repository.DEFAULT_CLONE_URI_SSH)
418 432 rhodecode_support_url = v.UnicodeString()
419 433 rhodecode_show_revision_number = v.StringBoolean(if_missing=False)
420 434 rhodecode_show_sha_length = v.Int(min=4, not_empty=True)
421 435 return _ApplicationVisualisationForm
422 436
423 437
424 438 class _BaseVcsSettingsForm(formencode.Schema):
425 439
426 440 allow_extra_fields = True
427 441 filter_extra_fields = False
428 442 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
429 443 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
430 444 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
431 445
432 446 # PR/Code-review
433 447 rhodecode_pr_merge_enabled = v.StringBoolean(if_missing=False)
434 448 rhodecode_use_outdated_comments = v.StringBoolean(if_missing=False)
435 449
436 450 # hg
437 451 extensions_largefiles = v.StringBoolean(if_missing=False)
438 452 extensions_evolve = v.StringBoolean(if_missing=False)
439 453 phases_publish = v.StringBoolean(if_missing=False)
440 454
441 455 rhodecode_hg_use_rebase_for_merging = v.StringBoolean(if_missing=False)
442 456 rhodecode_hg_close_branch_before_merging = v.StringBoolean(if_missing=False)
443 457
444 458 # git
445 459 vcs_git_lfs_enabled = v.StringBoolean(if_missing=False)
446 460 rhodecode_git_use_rebase_for_merging = v.StringBoolean(if_missing=False)
447 461 rhodecode_git_close_branch_before_merging = v.StringBoolean(if_missing=False)
448 462
449 463 # cache
450 464 rhodecode_diff_cache = v.StringBoolean(if_missing=False)
451 465
452 466
453 467 def ApplicationUiSettingsForm(localizer):
454 468 _ = localizer
455 469
456 470 class _ApplicationUiSettingsForm(_BaseVcsSettingsForm):
457 471 web_push_ssl = v.StringBoolean(if_missing=False)
458 472 largefiles_usercache = All(
459 473 v.ValidPath(localizer),
460 474 v.UnicodeString(strip=True, min=2, not_empty=True))
461 475 vcs_git_lfs_store_location = All(
462 476 v.ValidPath(localizer),
463 477 v.UnicodeString(strip=True, min=2, not_empty=True))
464 478 extensions_hggit = v.StringBoolean(if_missing=False)
465 479 new_svn_branch = v.ValidSvnPattern(localizer, section='vcs_svn_branch')
466 480 new_svn_tag = v.ValidSvnPattern(localizer, section='vcs_svn_tag')
467 481 return _ApplicationUiSettingsForm
468 482
469 483
470 484 def RepoVcsSettingsForm(localizer, repo_name):
471 485 _ = localizer
472 486
473 487 class _RepoVcsSettingsForm(_BaseVcsSettingsForm):
474 488 inherit_global_settings = v.StringBoolean(if_missing=False)
475 489 new_svn_branch = v.ValidSvnPattern(localizer,
476 490 section='vcs_svn_branch', repo_name=repo_name)
477 491 new_svn_tag = v.ValidSvnPattern(localizer,
478 492 section='vcs_svn_tag', repo_name=repo_name)
479 493 return _RepoVcsSettingsForm
480 494
481 495
482 496 def LabsSettingsForm(localizer):
483 497 _ = localizer
484 498
485 499 class _LabSettingsForm(formencode.Schema):
486 500 allow_extra_fields = True
487 501 filter_extra_fields = False
488 502 return _LabSettingsForm
489 503
490 504
491 505 def ApplicationPermissionsForm(
492 506 localizer, register_choices, password_reset_choices,
493 507 extern_activate_choices):
494 508 _ = localizer
495 509
496 510 class _DefaultPermissionsForm(formencode.Schema):
497 511 allow_extra_fields = True
498 512 filter_extra_fields = True
499 513
500 514 anonymous = v.StringBoolean(if_missing=False)
501 515 default_register = v.OneOf(register_choices)
502 516 default_register_message = v.UnicodeString()
503 517 default_password_reset = v.OneOf(password_reset_choices)
504 518 default_extern_activate = v.OneOf(extern_activate_choices)
505 519 return _DefaultPermissionsForm
506 520
507 521
508 522 def ObjectPermissionsForm(localizer, repo_perms_choices, group_perms_choices,
509 523 user_group_perms_choices):
510 524 _ = localizer
511 525
512 526 class _ObjectPermissionsForm(formencode.Schema):
513 527 allow_extra_fields = True
514 528 filter_extra_fields = True
515 529 overwrite_default_repo = v.StringBoolean(if_missing=False)
516 530 overwrite_default_group = v.StringBoolean(if_missing=False)
517 531 overwrite_default_user_group = v.StringBoolean(if_missing=False)
518 532
519 533 default_repo_perm = v.OneOf(repo_perms_choices)
520 534 default_group_perm = v.OneOf(group_perms_choices)
521 535 default_user_group_perm = v.OneOf(user_group_perms_choices)
522 536
523 537 return _ObjectPermissionsForm
524 538
525 539
526 540 def BranchPermissionsForm(localizer, branch_perms_choices):
527 541 _ = localizer
528 542
529 543 class _BranchPermissionsForm(formencode.Schema):
530 544 allow_extra_fields = True
531 545 filter_extra_fields = True
532 546 overwrite_default_branch = v.StringBoolean(if_missing=False)
533 547 default_branch_perm = v.OneOf(branch_perms_choices)
534 548
535 549 return _BranchPermissionsForm
536 550
537 551
538 552 def UserPermissionsForm(localizer, create_choices, create_on_write_choices,
539 553 repo_group_create_choices, user_group_create_choices,
540 554 fork_choices, inherit_default_permissions_choices):
541 555 _ = localizer
542 556
543 557 class _DefaultPermissionsForm(formencode.Schema):
544 558 allow_extra_fields = True
545 559 filter_extra_fields = True
546 560
547 561 anonymous = v.StringBoolean(if_missing=False)
548 562
549 563 default_repo_create = v.OneOf(create_choices)
550 564 default_repo_create_on_write = v.OneOf(create_on_write_choices)
551 565 default_user_group_create = v.OneOf(user_group_create_choices)
552 566 default_repo_group_create = v.OneOf(repo_group_create_choices)
553 567 default_fork_create = v.OneOf(fork_choices)
554 568 default_inherit_default_permissions = v.OneOf(inherit_default_permissions_choices)
555 569 return _DefaultPermissionsForm
556 570
557 571
558 572 def UserIndividualPermissionsForm(localizer):
559 573 _ = localizer
560 574
561 575 class _DefaultPermissionsForm(formencode.Schema):
562 576 allow_extra_fields = True
563 577 filter_extra_fields = True
564 578
565 579 inherit_default_permissions = v.StringBoolean(if_missing=False)
566 580 return _DefaultPermissionsForm
567 581
568 582
569 583 def DefaultsForm(localizer, edit=False, old_data=None, supported_backends=BACKENDS.keys()):
570 584 _ = localizer
571 585 old_data = old_data or {}
572 586
573 587 class _DefaultsForm(formencode.Schema):
574 588 allow_extra_fields = True
575 589 filter_extra_fields = True
576 590 default_repo_type = v.OneOf(supported_backends)
577 591 default_repo_private = v.StringBoolean(if_missing=False)
578 592 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
579 593 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
580 594 default_repo_enable_locking = v.StringBoolean(if_missing=False)
581 595 return _DefaultsForm
582 596
583 597
584 598 def AuthSettingsForm(localizer):
585 599 _ = localizer
586 600
587 601 class _AuthSettingsForm(formencode.Schema):
588 602 allow_extra_fields = True
589 603 filter_extra_fields = True
590 604 auth_plugins = All(v.ValidAuthPlugins(localizer),
591 605 v.UniqueListFromString(localizer)(not_empty=True))
592 606 return _AuthSettingsForm
593 607
594 608
595 609 def UserExtraEmailForm(localizer):
596 610 _ = localizer
597 611
598 612 class _UserExtraEmailForm(formencode.Schema):
599 613 email = All(v.UniqSystemEmail(localizer), v.Email(not_empty=True))
600 614 return _UserExtraEmailForm
601 615
602 616
603 617 def UserExtraIpForm(localizer):
604 618 _ = localizer
605 619
606 620 class _UserExtraIpForm(formencode.Schema):
607 621 ip = v.ValidIp(localizer)(not_empty=True)
608 622 return _UserExtraIpForm
609 623
610 624
611 625 def PullRequestForm(localizer, repo_id):
612 626 _ = localizer
613 627
614 628 class ReviewerForm(formencode.Schema):
615 629 user_id = v.Int(not_empty=True)
616 630 reasons = All()
617 631 rules = All(v.UniqueList(localizer, convert=int)())
618 632 mandatory = v.StringBoolean()
619 633 role = v.String(if_missing='reviewer')
620 634
621 635 class ObserverForm(formencode.Schema):
622 636 user_id = v.Int(not_empty=True)
623 637 reasons = All()
624 638 rules = All(v.UniqueList(localizer, convert=int)())
625 639 mandatory = v.StringBoolean()
626 640 role = v.String(if_missing='observer')
627 641
628 642 class _PullRequestForm(formencode.Schema):
629 643 allow_extra_fields = True
630 644 filter_extra_fields = True
631 645
632 646 common_ancestor = v.UnicodeString(strip=True, required=True)
633 647 source_repo = v.UnicodeString(strip=True, required=True)
634 648 source_ref = v.UnicodeString(strip=True, required=True)
635 649 target_repo = v.UnicodeString(strip=True, required=True)
636 650 target_ref = v.UnicodeString(strip=True, required=True)
637 651 revisions = All(#v.NotReviewedRevisions(localizer, repo_id)(),
638 652 v.UniqueList(localizer)(not_empty=True))
639 653 review_members = formencode.ForEach(ReviewerForm())
640 654 observer_members = formencode.ForEach(ObserverForm())
641 655 pullrequest_title = v.UnicodeString(strip=True, required=True, min=1, max=255)
642 656 pullrequest_desc = v.UnicodeString(strip=True, required=False)
643 657 description_renderer = v.UnicodeString(strip=True, required=False)
644 658
645 659 return _PullRequestForm
646 660
647 661
648 662 def IssueTrackerPatternsForm(localizer):
649 663 _ = localizer
650 664
651 665 class _IssueTrackerPatternsForm(formencode.Schema):
652 666 allow_extra_fields = True
653 667 filter_extra_fields = False
654 668 chained_validators = [v.ValidPattern(localizer)]
655 669 return _IssueTrackerPatternsForm
@@ -1,421 +1,422 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('admin_artifacts', '/_admin/artifacts', []);
16 16 pyroutes.register('admin_artifacts_data', '/_admin/artifacts-data', []);
17 17 pyroutes.register('admin_artifacts_delete', '/_admin/artifacts/%(uid)s/delete', ['uid']);
18 18 pyroutes.register('admin_artifacts_show_all', '/_admin/artifacts', []);
19 19 pyroutes.register('admin_artifacts_show_info', '/_admin/artifacts/%(uid)s', ['uid']);
20 20 pyroutes.register('admin_artifacts_update', '/_admin/artifacts/%(uid)s/update', ['uid']);
21 21 pyroutes.register('admin_audit_log_entry', '/_admin/audit_logs/%(audit_log_id)s', ['audit_log_id']);
22 22 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
23 23 pyroutes.register('admin_automation', '/_admin/automation', []);
24 24 pyroutes.register('admin_automation_update', '/_admin/automation/%(entry_id)s/update', ['entry_id']);
25 25 pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []);
26 26 pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []);
27 27 pyroutes.register('admin_home', '/_admin', []);
28 28 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
29 29 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
30 30 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
31 31 pyroutes.register('admin_permissions_branch', '/_admin/permissions/branch', []);
32 32 pyroutes.register('admin_permissions_branch_update', '/_admin/permissions/branch/update', []);
33 33 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
34 34 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
35 35 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
36 36 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
37 37 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
38 38 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
39 39 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
40 40 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
41 41 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
42 42 pyroutes.register('admin_scheduler', '/_admin/scheduler', []);
43 43 pyroutes.register('admin_scheduler_show_tasks', '/_admin/scheduler/_tasks', []);
44 44 pyroutes.register('admin_settings', '/_admin/settings', []);
45 45 pyroutes.register('admin_settings_email', '/_admin/settings/email', []);
46 46 pyroutes.register('admin_settings_email_update', '/_admin/settings/email/update', []);
47 47 pyroutes.register('admin_settings_exception_tracker', '/_admin/settings/exceptions', []);
48 48 pyroutes.register('admin_settings_exception_tracker_delete', '/_admin/settings/exceptions/%(exception_id)s/delete', ['exception_id']);
49 49 pyroutes.register('admin_settings_exception_tracker_delete_all', '/_admin/settings/exceptions_delete_all', []);
50 50 pyroutes.register('admin_settings_exception_tracker_show', '/_admin/settings/exceptions/%(exception_id)s', ['exception_id']);
51 51 pyroutes.register('admin_settings_global', '/_admin/settings/global', []);
52 52 pyroutes.register('admin_settings_global_update', '/_admin/settings/global/update', []);
53 53 pyroutes.register('admin_settings_hooks', '/_admin/settings/hooks', []);
54 54 pyroutes.register('admin_settings_hooks_delete', '/_admin/settings/hooks/delete', []);
55 55 pyroutes.register('admin_settings_hooks_update', '/_admin/settings/hooks/update', []);
56 56 pyroutes.register('admin_settings_issuetracker', '/_admin/settings/issue-tracker', []);
57 57 pyroutes.register('admin_settings_issuetracker_delete', '/_admin/settings/issue-tracker/delete', []);
58 58 pyroutes.register('admin_settings_issuetracker_test', '/_admin/settings/issue-tracker/test', []);
59 59 pyroutes.register('admin_settings_issuetracker_update', '/_admin/settings/issue-tracker/update', []);
60 60 pyroutes.register('admin_settings_labs', '/_admin/settings/labs', []);
61 61 pyroutes.register('admin_settings_labs_update', '/_admin/settings/labs/update', []);
62 62 pyroutes.register('admin_settings_license', '/_admin/settings/license', []);
63 63 pyroutes.register('admin_settings_license_unlock', '/_admin/settings/license_unlock', []);
64 64 pyroutes.register('admin_settings_mapping', '/_admin/settings/mapping', []);
65 65 pyroutes.register('admin_settings_mapping_update', '/_admin/settings/mapping/update', []);
66 66 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
67 67 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
68 68 pyroutes.register('admin_settings_process_management_data', '/_admin/settings/process_management/data', []);
69 69 pyroutes.register('admin_settings_process_management_master_signal', '/_admin/settings/process_management/master_signal', []);
70 70 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
71 71 pyroutes.register('admin_settings_scheduler_create', '/_admin/scheduler/create', []);
72 72 pyroutes.register('admin_settings_scheduler_delete', '/_admin/scheduler/%(schedule_id)s/delete', ['schedule_id']);
73 73 pyroutes.register('admin_settings_scheduler_edit', '/_admin/scheduler/%(schedule_id)s', ['schedule_id']);
74 74 pyroutes.register('admin_settings_scheduler_execute', '/_admin/scheduler/%(schedule_id)s/execute', ['schedule_id']);
75 75 pyroutes.register('admin_settings_scheduler_new', '/_admin/scheduler/new', []);
76 76 pyroutes.register('admin_settings_scheduler_update', '/_admin/scheduler/%(schedule_id)s/update', ['schedule_id']);
77 77 pyroutes.register('admin_settings_search', '/_admin/settings/search', []);
78 78 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
79 79 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
80 80 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
81 81 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
82 82 pyroutes.register('admin_settings_update', '/_admin/settings/update', []);
83 83 pyroutes.register('admin_settings_vcs', '/_admin/settings/vcs', []);
84 84 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
85 85 pyroutes.register('admin_settings_vcs_svn_pattern_delete', '/_admin/settings/vcs/svn_pattern_delete', []);
86 86 pyroutes.register('admin_settings_vcs_update', '/_admin/settings/vcs/update', []);
87 87 pyroutes.register('admin_settings_visual', '/_admin/settings/visual', []);
88 88 pyroutes.register('admin_settings_visual_update', '/_admin/settings/visual/update', []);
89 pyroutes.register('admin_security_modify_allowed_vcs_client_versions', '/_admin/security/modify/allowed_vcs_client_versions', []);
89 90 pyroutes.register('apiv2', '/_admin/api', []);
90 91 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed-atom', ['repo_name']);
91 92 pyroutes.register('atom_feed_home_old', '/%(repo_name)s/feed/atom', ['repo_name']);
92 93 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
93 94 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
94 95 pyroutes.register('branch_remove', '/%(repo_name)s/branches/%(branch_name)s/remove', ['repo_name', 'branch_name']);
95 96 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
96 97 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
97 98 pyroutes.register('channelstream_proxy', '/_channelstream', []);
98 99 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
99 100 pyroutes.register('check_2fa', '/_admin/check_2fa', []);
100 101 pyroutes.register('commit_draft_comments_submit', '/%(repo_name)s/changeset/%(commit_id)s/draft_comments_submit', ['repo_name', 'commit_id']);
101 102 pyroutes.register('debug_style_email', '/_admin/debug_style/email/%(email_id)s', ['email_id']);
102 103 pyroutes.register('debug_style_email_plain_rendered', '/_admin/debug_style/email-rendered/%(email_id)s', ['email_id']);
103 104 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
104 105 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
105 106 pyroutes.register('download_file', '/_file_store/download/%(fid)s', ['fid']);
106 107 pyroutes.register('download_file_by_token', '/_file_store/token-download/%(_auth_token)s/%(fid)s', ['_auth_token', 'fid']);
107 108 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
108 109 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
109 110 pyroutes.register('edit_repo_advanced_archive', '/%(repo_name)s/settings/advanced/archive', ['repo_name']);
110 111 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
111 112 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
112 113 pyroutes.register('edit_repo_advanced_hooks', '/%(repo_name)s/settings/advanced/hooks', ['repo_name']);
113 114 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
114 115 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
115 116 pyroutes.register('edit_repo_audit_logs', '/%(repo_name)s/settings/audit_logs', ['repo_name']);
116 117 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
117 118 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
118 119 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
119 120 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
120 121 pyroutes.register('edit_repo_group', '/%(repo_group_name)s/_edit', ['repo_group_name']);
121 122 pyroutes.register('edit_repo_group_advanced', '/%(repo_group_name)s/_settings/advanced', ['repo_group_name']);
122 123 pyroutes.register('edit_repo_group_advanced_delete', '/%(repo_group_name)s/_settings/advanced/delete', ['repo_group_name']);
123 124 pyroutes.register('edit_repo_group_perms', '/%(repo_group_name)s/_settings/permissions', ['repo_group_name']);
124 125 pyroutes.register('edit_repo_group_perms_update', '/%(repo_group_name)s/_settings/permissions/update', ['repo_group_name']);
125 126 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
126 127 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
127 128 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
128 129 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
129 130 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
130 131 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
131 132 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
132 133 pyroutes.register('edit_repo_perms_branch', '/%(repo_name)s/settings/branch_permissions', ['repo_name']);
133 134 pyroutes.register('edit_repo_perms_branch_delete', '/%(repo_name)s/settings/branch_permissions/%(rule_id)s/delete', ['repo_name', 'rule_id']);
134 135 pyroutes.register('edit_repo_perms_set_private', '/%(repo_name)s/settings/permissions/set_private', ['repo_name']);
135 136 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
136 137 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
137 138 pyroutes.register('edit_repo_remote_push', '/%(repo_name)s/settings/remote/push', ['repo_name']);
138 139 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
139 140 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
140 141 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
141 142 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
142 143 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
143 144 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
144 145 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
145 146 pyroutes.register('edit_user_audit_logs_download', '/_admin/users/%(user_id)s/edit/audit/download', ['user_id']);
146 147 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
147 148 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
148 149 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
149 150 pyroutes.register('edit_user_auth_tokens_view', '/_admin/users/%(user_id)s/edit/auth_tokens/view', ['user_id']);
150 151 pyroutes.register('edit_user_caches', '/_admin/users/%(user_id)s/edit/caches', ['user_id']);
151 152 pyroutes.register('edit_user_caches_update', '/_admin/users/%(user_id)s/edit/caches/update', ['user_id']);
152 153 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
153 154 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
154 155 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
155 156 pyroutes.register('edit_user_group', '/_admin/user_groups/%(user_group_id)s/edit', ['user_group_id']);
156 157 pyroutes.register('edit_user_group_advanced', '/_admin/user_groups/%(user_group_id)s/edit/advanced', ['user_group_id']);
157 158 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
158 159 pyroutes.register('edit_user_group_global_perms', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions', ['user_group_id']);
159 160 pyroutes.register('edit_user_group_global_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions/update', ['user_group_id']);
160 161 pyroutes.register('edit_user_group_perms', '/_admin/user_groups/%(user_group_id)s/edit/permissions', ['user_group_id']);
161 162 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
162 163 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
163 164 pyroutes.register('edit_user_group_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/permissions/update', ['user_group_id']);
164 165 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
165 166 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
166 167 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
167 168 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
168 169 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
169 170 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
170 171 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
171 172 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
172 173 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
173 174 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
174 175 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
175 176 pyroutes.register('favicon', '/favicon.ico', []);
176 177 pyroutes.register('file_preview', '/_file_preview', []);
177 178 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
178 179 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
179 180 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
180 181 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
181 182 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/rev/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
182 183 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/rev/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
183 184 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/rev/%(revision)s', ['gist_id', 'revision']);
184 185 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
185 186 pyroutes.register('gists_create', '/_admin/gists/create', []);
186 187 pyroutes.register('gists_new', '/_admin/gists/new', []);
187 188 pyroutes.register('gists_show', '/_admin/gists', []);
188 189 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
189 190 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
190 191 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
191 192 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
192 193 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
193 194 pyroutes.register('goto_switcher_data', '/_goto_data', []);
194 195 pyroutes.register('home', '/', []);
195 196 pyroutes.register('hovercard_pull_request', '/_hovercard/pull_request/%(pull_request_id)s', ['pull_request_id']);
196 197 pyroutes.register('hovercard_repo_commit', '/_hovercard/commit/%(repo_name)s/%(commit_id)s', ['repo_name', 'commit_id']);
197 198 pyroutes.register('hovercard_user', '/_hovercard/user/%(user_id)s', ['user_id']);
198 199 pyroutes.register('hovercard_user_group', '/_hovercard/user_group/%(user_group_id)s', ['user_group_id']);
199 200 pyroutes.register('hovercard_username', '/_hovercard/username/%(username)s', ['username']);
200 201 pyroutes.register('journal', '/_admin/journal', []);
201 202 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
202 203 pyroutes.register('journal_public', '/_admin/public_journal', []);
203 204 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
204 205 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
205 206 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
206 207 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
207 208 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
208 209 pyroutes.register('login', '/_admin/login', []);
209 210 pyroutes.register('logout', '/_admin/logout', []);
210 211 pyroutes.register('main_page_repo_groups_data', '/_home_repo_groups', []);
211 212 pyroutes.register('main_page_repos_data', '/_home_repos', []);
212 213 pyroutes.register('markup_preview', '/_markup_preview', []);
213 214 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
214 215 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
215 216 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
216 217 pyroutes.register('my_account_auth_tokens_view', '/_admin/my_account/auth_tokens/view', []);
217 218 pyroutes.register('my_account_bookmarks', '/_admin/my_account/bookmarks', []);
218 219 pyroutes.register('my_account_bookmarks_update', '/_admin/my_account/bookmarks/update', []);
219 220 pyroutes.register('my_account_configure_2fa', '/_admin/my_account/configure_2fa', []);
220 221 pyroutes.register('my_account_configure_2fa_update', '/_admin/my_account/configure_2fa_update', []);
221 222 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
222 223 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
223 224 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
224 225 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
225 226 pyroutes.register('my_account_external_identity', '/_admin/my_account/external-identity', []);
226 227 pyroutes.register('my_account_external_identity_delete', '/_admin/my_account/external-identity/delete', []);
227 228 pyroutes.register('my_account_goto_bookmark', '/_admin/my_account/bookmark/%(bookmark_id)s', ['bookmark_id']);
228 229 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
229 230 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
230 231 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
231 232 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
232 233 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
233 234 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
234 235 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
235 236 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
236 237 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
237 238 pyroutes.register('my_account_regenerate_2fa_recovery_codes', '/_admin/my_account/regenerate_recovery_codes', []);
238 239 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
239 240 pyroutes.register('my_account_show_2fa_recovery_codes', '/_admin/my_account/recovery_codes', []);
240 241 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
241 242 pyroutes.register('my_account_ssh_keys_add', '/_admin/my_account/ssh_keys/new', []);
242 243 pyroutes.register('my_account_ssh_keys_delete', '/_admin/my_account/ssh_keys/delete', []);
243 244 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
244 245 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
245 246 pyroutes.register('my_account_user_group_membership', '/_admin/my_account/user_group_membership', []);
246 247 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
247 248 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
248 249 pyroutes.register('notifications_mark_all_read', '/_admin/notifications_mark_all_read', []);
249 250 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
250 251 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
251 252 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
252 253 pyroutes.register('ops_celery_error_test', '/_admin/ops/error-celery', []);
253 254 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
254 255 pyroutes.register('ops_healthcheck', '/_admin/ops/status', []);
255 256 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
256 257 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
257 258 pyroutes.register('plugin_admin_chat', '/_admin/plugin_admin_chat/%(action)s', ['action']);
258 259 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
259 260 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
260 261 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
261 262 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
262 263 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']);
263 264 pyroutes.register('pullrequest_comment_edit', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/edit', ['repo_name', 'pull_request_id', 'comment_id']);
264 265 pyroutes.register('pullrequest_comments', '/%(repo_name)s/pull-request/%(pull_request_id)s/comments', ['repo_name', 'pull_request_id']);
265 266 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
266 267 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
267 268 pyroutes.register('pullrequest_draft_comments_submit', '/%(repo_name)s/pull-request/%(pull_request_id)s/draft_comments_submit', ['repo_name', 'pull_request_id']);
268 269 pyroutes.register('pullrequest_drafts', '/%(repo_name)s/pull-request/%(pull_request_id)s/drafts', ['repo_name', 'pull_request_id']);
269 270 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
270 271 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
271 272 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
272 273 pyroutes.register('pullrequest_repo_targets', '/%(repo_name)s/pull-request/repo-targets', ['repo_name']);
273 274 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
274 275 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
275 276 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
276 277 pyroutes.register('pullrequest_todos', '/%(repo_name)s/pull-request/%(pull_request_id)s/todos', ['repo_name', 'pull_request_id']);
277 278 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
278 279 pyroutes.register('register', '/_admin/register', []);
279 280 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
280 281 pyroutes.register('repo_artifacts_data', '/%(repo_name)s/artifacts_data', ['repo_name']);
281 282 pyroutes.register('repo_artifacts_delete', '/%(repo_name)s/artifacts/delete/%(uid)s', ['repo_name', 'uid']);
282 283 pyroutes.register('repo_artifacts_get', '/%(repo_name)s/artifacts/download/%(uid)s', ['repo_name', 'uid']);
283 284 pyroutes.register('repo_artifacts_info', '/%(repo_name)s/artifacts/info/%(uid)s', ['repo_name', 'uid']);
284 285 pyroutes.register('repo_artifacts_list', '/%(repo_name)s/artifacts', ['repo_name']);
285 286 pyroutes.register('repo_artifacts_new', '/%(repo_name)s/artifacts/new', ['repo_name']);
286 287 pyroutes.register('repo_artifacts_store', '/%(repo_name)s/artifacts/store', ['repo_name']);
287 288 pyroutes.register('repo_artifacts_stream_script', '/_file_store/stream-upload-script', []);
288 289 pyroutes.register('repo_artifacts_stream_store', '/_file_store/stream-upload', []);
289 290 pyroutes.register('repo_artifacts_update', '/%(repo_name)s/artifacts/update/%(uid)s', ['repo_name', 'uid']);
290 291 pyroutes.register('repo_automation', '/%(repo_name)s/settings/automation', ['repo_name']);
291 292 pyroutes.register('repo_automation_update', '/%(repo_name)s/settings/automation/%(entry_id)s/update', ['repo_name', 'entry_id']);
292 293 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
293 294 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
294 295 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
295 296 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
296 297 pyroutes.register('repo_commit_comment_attachment_upload', '/%(repo_name)s/changeset/%(commit_id)s/comment/attachment_upload', ['repo_name', 'commit_id']);
297 298 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
298 299 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
299 300 pyroutes.register('repo_commit_comment_edit', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/edit', ['repo_name', 'commit_id', 'comment_id']);
300 301 pyroutes.register('repo_commit_comment_history_view', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/history_view/%(comment_history_id)s', ['repo_name', 'commit_id', 'comment_id', 'comment_history_id']);
301 302 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
302 303 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
303 304 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
304 305 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
305 306 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
306 307 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
307 308 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
308 309 pyroutes.register('repo_commits', '/%(repo_name)s/commits', ['repo_name']);
309 310 pyroutes.register('repo_commits_elements', '/%(repo_name)s/commits_elements', ['repo_name']);
310 311 pyroutes.register('repo_commits_elements_file', '/%(repo_name)s/commits_elements/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
311 312 pyroutes.register('repo_commits_file', '/%(repo_name)s/commits/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
312 313 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']);
313 314 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
314 315 pyroutes.register('repo_create', '/_admin/repos/create', []);
315 316 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
316 317 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
317 318 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
318 319 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
319 320 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
320 321 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
321 322 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
322 323 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
323 324 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
324 325 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
325 326 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
326 327 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
327 328 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
328 329 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
329 330 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
330 331 pyroutes.register('repo_files_check_head', '/%(repo_name)s/check_head/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
331 332 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
332 333 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
333 334 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
334 335 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
335 336 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
336 337 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
337 338 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
338 339 pyroutes.register('repo_files_replace_binary', '/%(repo_name)s/replace_binary/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
339 340 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
340 341 pyroutes.register('repo_files_upload_file', '/%(repo_name)s/upload_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
341 342 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
342 343 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
343 344 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
344 345 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
345 346 pyroutes.register('repo_group_create', '/_admin/repo_group/create', []);
346 347 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
347 348 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
348 349 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/_settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
349 350 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/_settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
350 351 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/_settings/integrations', ['repo_group_name']);
351 352 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/_settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
352 353 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/_settings/integrations/new', ['repo_group_name']);
353 354 pyroutes.register('repo_group_list_data', '/_repo_groups', []);
354 355 pyroutes.register('repo_group_new', '/_admin/repo_group/new', []);
355 356 pyroutes.register('repo_groups', '/_admin/repo_groups', []);
356 357 pyroutes.register('repo_groups_data', '/_admin/repo_groups_data', []);
357 358 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
358 359 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
359 360 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
360 361 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
361 362 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
362 363 pyroutes.register('repo_list_data', '/_repos', []);
363 364 pyroutes.register('repo_new', '/_admin/repos/new', []);
364 365 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
365 366 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
366 367 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
367 368 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
368 369 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
369 370 pyroutes.register('repo_reviewers_review_rule_delete', '/%(repo_name)s/settings/review/rules/%(rule_id)s/delete', ['repo_name', 'rule_id']);
370 371 pyroutes.register('repo_reviewers_review_rule_edit', '/%(repo_name)s/settings/review/rules/%(rule_id)s', ['repo_name', 'rule_id']);
371 372 pyroutes.register('repo_reviewers_review_rule_new', '/%(repo_name)s/settings/review/rules/new', ['repo_name']);
372 373 pyroutes.register('repo_settings_quick_actions', '/%(repo_name)s/settings/quick-action', ['repo_name']);
373 374 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
374 375 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
375 376 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
376 377 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
377 378 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
378 379 pyroutes.register('repos', '/_admin/repos', []);
379 380 pyroutes.register('repos_data', '/_admin/repos_data', []);
380 381 pyroutes.register('reset_password', '/_admin/password_reset', []);
381 382 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
382 383 pyroutes.register('robots', '/robots.txt', []);
383 384 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed-rss', ['repo_name']);
384 385 pyroutes.register('rss_feed_home_old', '/%(repo_name)s/feed/rss', ['repo_name']);
385 386 pyroutes.register('search', '/_admin/search', []);
386 387 pyroutes.register('search_repo', '/%(repo_name)s/_search', ['repo_name']);
387 388 pyroutes.register('search_repo_alt', '/%(repo_name)s/search', ['repo_name']);
388 389 pyroutes.register('search_repo_group', '/%(repo_group_name)s/_search', ['repo_group_name']);
389 390 pyroutes.register('setup_2fa', '/_admin/setup_2fa', []);
390 391 pyroutes.register('store_user_session_value', '/_store_session_attr', []);
391 392 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
392 393 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
393 394 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
394 395 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
395 396 pyroutes.register('upload_file', '/_file_store/upload', []);
396 397 pyroutes.register('user_autocomplete_data', '/_users', []);
397 398 pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']);
398 399 pyroutes.register('user_delete', '/_admin/users/%(user_id)s/delete', ['user_id']);
399 400 pyroutes.register('user_disable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_disable', ['user_id']);
400 401 pyroutes.register('user_edit', '/_admin/users/%(user_id)s/edit', ['user_id']);
401 402 pyroutes.register('user_edit_advanced', '/_admin/users/%(user_id)s/edit/advanced', ['user_id']);
402 403 pyroutes.register('user_edit_global_perms', '/_admin/users/%(user_id)s/edit/global_permissions', ['user_id']);
403 404 pyroutes.register('user_edit_global_perms_update', '/_admin/users/%(user_id)s/edit/global_permissions/update', ['user_id']);
404 405 pyroutes.register('user_enable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_enable', ['user_id']);
405 406 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
406 407 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
407 408 pyroutes.register('user_group_profile', '/_profile_user_group/%(user_group_name)s', ['user_group_name']);
408 409 pyroutes.register('user_groups', '/_admin/user_groups', []);
409 410 pyroutes.register('user_groups_create', '/_admin/user_groups/create', []);
410 411 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
411 412 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
412 413 pyroutes.register('user_groups_new', '/_admin/user_groups/new', []);
413 414 pyroutes.register('user_groups_update', '/_admin/user_groups/%(user_group_id)s/update', ['user_group_id']);
414 415 pyroutes.register('user_notice_dismiss', '/_admin/users/%(user_id)s/notice_dismiss', ['user_id']);
415 416 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
416 417 pyroutes.register('user_update', '/_admin/users/%(user_id)s/update', ['user_id']);
417 418 pyroutes.register('users', '/_admin/users', []);
418 419 pyroutes.register('users_create', '/_admin/users/create', []);
419 420 pyroutes.register('users_data', '/_admin/users_data', []);
420 421 pyroutes.register('users_new', '/_admin/users/new', []);
421 422 }
@@ -1,40 +1,79 b''
1 1 <%inherit file="/base/base.mako"/>
2 2
3 3 <%def name="title()">
4 4 ${_('Security Admin')}
5 5 %if c.rhodecode_name:
6 6 &middot; ${h.branding(c.rhodecode_name)}
7 7 %endif
8 8 </%def>
9 9
10 10 <%def name="breadcrumbs_links()"></%def>
11 11
12 12 <%def name="menu_bar_nav()">
13 13 ${self.menu_items(active='admin')}
14 14 </%def>
15 15
16 16 <%def name="menu_bar_subnav()">
17 17 ${self.admin_menu(active='security')}
18 18 </%def>
19 19
20 20 <%def name="main()">
21 21
22 22 <div class="box">
23 23
24 24 <div class="panel panel-default">
25 25 <div class="panel-heading">
26 26 <h3 class="panel-title">${_('Security Audit')}</h3>
27 27 </div>
28 28 <div class="panel-body">
29 29 <h4>${_('This feature is available in RhodeCode EE edition only. Contact {sales_email} to obtain a trial license.').format(sales_email='<a href="mailto:sales@rhodecode.com">sales@rhodecode.com</a>')|n}</h4>
30 30 <p>
31 You can scan your repositories for exposed secrets, passwords, etc
31 ${_('You can scan your repositories for exposed secrets, passwords, etc')}
32 32 </p>
33 33 </div>
34 34 </div>
35 35
36 <div class="panel panel-default">
37 <div class="panel-heading">
38 <h3 class="panel-title">${_('Allowed client versions')}</h3>
39 </div>
40 <div class="panel-body">
41 %if c.rhodecode_edition_id != 'EE':
42 <h4>${_('This feature is available in RhodeCode EE edition only. Contact {sales_email} to obtain a trial license.').format(sales_email='<a href="mailto:sales@rhodecode.com">sales@rhodecode.com</a>')|n}</h4>
43 <p>
44 ${_('Some outdated client versions may have security vulnerabilities. This section have rules for whitelisting versions of clients for Git, Mercurial and SVN.')}
45 </p>
46 %else:
47 <div class="inner form" id="container">
48 </div>
49 %endif
50 </div>
51
36 52 </div>
37 53
54 <script>
55 $(document).ready(function() {
56 $.ajax({
57 url: pyroutes.url('admin_security_modify_allowed_vcs_client_versions'),
58 type: 'GET',
59 success: function(response) {
60 $('#container').html(response);
61 },
62 });
63 $(document).on('submit', '#allowed_clients_form', function(event) {
64 event.preventDefault();
65 var formData = $(this).serialize();
66
67 $.ajax({
68 url: pyroutes.url('admin_security_modify_allowed_vcs_client_versions'),
69 type: 'POST',
70 data: formData,
71 success: function(response) {
72 $('#container').html(response);
73 },
74 });
75 });
76 });
77 </script>
38 78
39 79 </%def>
40
General Comments 0
You need to be logged in to leave comments. Login now