##// END OF EJS Templates
routing: detect invalid repo/group names before we hit DB checks to prevent some errors for bad repo names
super-admin -
r4841:bbd9cbf6 default
parent child Browse files
Show More
@@ -1,821 +1,829 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import time
22 22 import logging
23 23 import operator
24 24
25 25 from pyramid import compat
26 26 from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPBadRequest
27 27
28 28 from rhodecode.lib import helpers as h, diffs, rc_cache
29 from rhodecode.lib.utils import repo_name_slug
29 30 from rhodecode.lib.utils2 import (
30 31 StrictAttributeDict, str2bool, safe_int, datetime_to_time, safe_unicode)
31 32 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
32 33 from rhodecode.lib.vcs.backends.base import EmptyCommit
33 34 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
34 35 from rhodecode.model import repo
35 36 from rhodecode.model import repo_group
36 37 from rhodecode.model import user_group
37 38 from rhodecode.model import user
38 39 from rhodecode.model.db import User
39 40 from rhodecode.model.scm import ScmModel
40 41 from rhodecode.model.settings import VcsSettingsModel, IssueTrackerSettingsModel
41 42 from rhodecode.model.repo import ReadmeFinder
42 43
43 44 log = logging.getLogger(__name__)
44 45
45 46
46 47 ADMIN_PREFIX = '/_admin'
47 48 STATIC_FILE_PREFIX = '/_static'
48 49
49 50 URL_NAME_REQUIREMENTS = {
50 51 # group name can have a slash in them, but they must not end with a slash
51 52 'group_name': r'.*?[^/]',
52 53 'repo_group_name': r'.*?[^/]',
53 54 # repo names can have a slash in them, but they must not end with a slash
54 55 'repo_name': r'.*?[^/]',
55 56 # file path eats up everything at the end
56 57 'f_path': r'.*',
57 58 # reference types
58 59 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
59 60 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
60 61 }
61 62
62 63
63 64 def add_route_with_slash(config,name, pattern, **kw):
64 65 config.add_route(name, pattern, **kw)
65 66 if not pattern.endswith('/'):
66 67 config.add_route(name + '_slash', pattern + '/', **kw)
67 68
68 69
69 70 def add_route_requirements(route_path, requirements=None):
70 71 """
71 72 Adds regex requirements to pyramid routes using a mapping dict
72 73 e.g::
73 74 add_route_requirements('{repo_name}/settings')
74 75 """
75 76 requirements = requirements or URL_NAME_REQUIREMENTS
76 77 for key, regex in requirements.items():
77 78 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
78 79 return route_path
79 80
80 81
81 82 def get_format_ref_id(repo):
82 83 """Returns a `repo` specific reference formatter function"""
83 84 if h.is_svn(repo):
84 85 return _format_ref_id_svn
85 86 else:
86 87 return _format_ref_id
87 88
88 89
89 90 def _format_ref_id(name, raw_id):
90 91 """Default formatting of a given reference `name`"""
91 92 return name
92 93
93 94
94 95 def _format_ref_id_svn(name, raw_id):
95 96 """Special way of formatting a reference for Subversion including path"""
96 97 return '%s@%s' % (name, raw_id)
97 98
98 99
99 100 class TemplateArgs(StrictAttributeDict):
100 101 pass
101 102
102 103
103 104 class BaseAppView(object):
104 105
105 106 def __init__(self, context, request):
106 107 self.request = request
107 108 self.context = context
108 109 self.session = request.session
109 110 if not hasattr(request, 'user'):
110 111 # NOTE(marcink): edge case, we ended up in matched route
111 112 # but probably of web-app context, e.g API CALL/VCS CALL
112 113 if hasattr(request, 'vcs_call') or hasattr(request, 'rpc_method'):
113 114 log.warning('Unable to process request `%s` in this scope', request)
114 115 raise HTTPBadRequest()
115 116
116 117 self._rhodecode_user = request.user # auth user
117 118 self._rhodecode_db_user = self._rhodecode_user.get_instance()
118 119 self._maybe_needs_password_change(
119 120 request.matched_route.name, self._rhodecode_db_user)
120 121
121 122 def _maybe_needs_password_change(self, view_name, user_obj):
122 123
123 124 dont_check_views = [
124 125 'channelstream_connect'
125 126 ]
126 127 if view_name in dont_check_views:
127 128 return
128 129
129 130 log.debug('Checking if user %s needs password change on view %s',
130 131 user_obj, view_name)
131 132
132 133 skip_user_views = [
133 134 'logout', 'login',
134 135 'my_account_password', 'my_account_password_update'
135 136 ]
136 137
137 138 if not user_obj:
138 139 return
139 140
140 141 if user_obj.username == User.DEFAULT_USER:
141 142 return
142 143
143 144 now = time.time()
144 145 should_change = user_obj.user_data.get('force_password_change')
145 146 change_after = safe_int(should_change) or 0
146 147 if should_change and now > change_after:
147 148 log.debug('User %s requires password change', user_obj)
148 149 h.flash('You are required to change your password', 'warning',
149 150 ignore_duplicate=True)
150 151
151 152 if view_name not in skip_user_views:
152 153 raise HTTPFound(
153 154 self.request.route_path('my_account_password'))
154 155
155 156 def _log_creation_exception(self, e, repo_name):
156 157 _ = self.request.translate
157 158 reason = None
158 159 if len(e.args) == 2:
159 160 reason = e.args[1]
160 161
161 162 if reason == 'INVALID_CERTIFICATE':
162 163 log.exception(
163 164 'Exception creating a repository: invalid certificate')
164 165 msg = (_('Error creating repository %s: invalid certificate')
165 166 % repo_name)
166 167 else:
167 168 log.exception("Exception creating a repository")
168 169 msg = (_('Error creating repository %s')
169 170 % repo_name)
170 171 return msg
171 172
172 173 def _get_local_tmpl_context(self, include_app_defaults=True):
173 174 c = TemplateArgs()
174 175 c.auth_user = self.request.user
175 176 # TODO(marcink): migrate the usage of c.rhodecode_user to c.auth_user
176 177 c.rhodecode_user = self.request.user
177 178
178 179 if include_app_defaults:
179 180 from rhodecode.lib.base import attach_context_attributes
180 181 attach_context_attributes(c, self.request, self.request.user.user_id)
181 182
182 183 c.is_super_admin = c.auth_user.is_admin
183 184
184 185 c.can_create_repo = c.is_super_admin
185 186 c.can_create_repo_group = c.is_super_admin
186 187 c.can_create_user_group = c.is_super_admin
187 188
188 189 c.is_delegated_admin = False
189 190
190 191 if not c.auth_user.is_default and not c.is_super_admin:
191 192 c.can_create_repo = h.HasPermissionAny('hg.create.repository')(
192 193 user=self.request.user)
193 194 repositories = c.auth_user.repositories_admin or c.can_create_repo
194 195
195 196 c.can_create_repo_group = h.HasPermissionAny('hg.repogroup.create.true')(
196 197 user=self.request.user)
197 198 repository_groups = c.auth_user.repository_groups_admin or c.can_create_repo_group
198 199
199 200 c.can_create_user_group = h.HasPermissionAny('hg.usergroup.create.true')(
200 201 user=self.request.user)
201 202 user_groups = c.auth_user.user_groups_admin or c.can_create_user_group
202 203 # delegated admin can create, or manage some objects
203 204 c.is_delegated_admin = repositories or repository_groups or user_groups
204 205 return c
205 206
206 207 def _get_template_context(self, tmpl_args, **kwargs):
207 208
208 209 local_tmpl_args = {
209 210 'defaults': {},
210 211 'errors': {},
211 212 'c': tmpl_args
212 213 }
213 214 local_tmpl_args.update(kwargs)
214 215 return local_tmpl_args
215 216
216 217 def load_default_context(self):
217 218 """
218 219 example:
219 220
220 221 def load_default_context(self):
221 222 c = self._get_local_tmpl_context()
222 223 c.custom_var = 'foobar'
223 224
224 225 return c
225 226 """
226 227 raise NotImplementedError('Needs implementation in view class')
227 228
228 229
229 230 class RepoAppView(BaseAppView):
230 231
231 232 def __init__(self, context, request):
232 233 super(RepoAppView, self).__init__(context, request)
233 234 self.db_repo = request.db_repo
234 235 self.db_repo_name = self.db_repo.repo_name
235 236 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
236 237 self.db_repo_artifacts = ScmModel().get_artifacts(self.db_repo)
237 238 self.db_repo_patterns = IssueTrackerSettingsModel(repo=self.db_repo)
238 239
239 240 def _handle_missing_requirements(self, error):
240 241 log.error(
241 242 'Requirements are missing for repository %s: %s',
242 243 self.db_repo_name, safe_unicode(error))
243 244
244 245 def _get_local_tmpl_context(self, include_app_defaults=True):
245 246 _ = self.request.translate
246 247 c = super(RepoAppView, self)._get_local_tmpl_context(
247 248 include_app_defaults=include_app_defaults)
248 249
249 250 # register common vars for this type of view
250 251 c.rhodecode_db_repo = self.db_repo
251 252 c.repo_name = self.db_repo_name
252 253 c.repository_pull_requests = self.db_repo_pull_requests
253 254 c.repository_artifacts = self.db_repo_artifacts
254 255 c.repository_is_user_following = ScmModel().is_following_repo(
255 256 self.db_repo_name, self._rhodecode_user.user_id)
256 257 self.path_filter = PathFilter(None)
257 258
258 259 c.repository_requirements_missing = {}
259 260 try:
260 261 self.rhodecode_vcs_repo = self.db_repo.scm_instance()
261 262 # NOTE(marcink):
262 263 # comparison to None since if it's an object __bool__ is expensive to
263 264 # calculate
264 265 if self.rhodecode_vcs_repo is not None:
265 266 path_perms = self.rhodecode_vcs_repo.get_path_permissions(
266 267 c.auth_user.username)
267 268 self.path_filter = PathFilter(path_perms)
268 269 except RepositoryRequirementError as e:
269 270 c.repository_requirements_missing = {'error': str(e)}
270 271 self._handle_missing_requirements(e)
271 272 self.rhodecode_vcs_repo = None
272 273
273 274 c.path_filter = self.path_filter # used by atom_feed_entry.mako
274 275
275 276 if self.rhodecode_vcs_repo is None:
276 277 # unable to fetch this repo as vcs instance, report back to user
277 278 h.flash(_(
278 279 "The repository `%(repo_name)s` cannot be loaded in filesystem. "
279 280 "Please check if it exist, or is not damaged.") %
280 281 {'repo_name': c.repo_name},
281 282 category='error', ignore_duplicate=True)
282 283 if c.repository_requirements_missing:
283 284 route = self.request.matched_route.name
284 285 if route.startswith(('edit_repo', 'repo_summary')):
285 286 # allow summary and edit repo on missing requirements
286 287 return c
287 288
288 289 raise HTTPFound(
289 290 h.route_path('repo_summary', repo_name=self.db_repo_name))
290 291
291 292 else: # redirect if we don't show missing requirements
292 293 raise HTTPFound(h.route_path('home'))
293 294
294 295 c.has_origin_repo_read_perm = False
295 296 if self.db_repo.fork:
296 297 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
297 298 'repository.write', 'repository.read', 'repository.admin')(
298 299 self.db_repo.fork.repo_name, 'summary fork link')
299 300
300 301 return c
301 302
302 303 def _get_f_path_unchecked(self, matchdict, default=None):
303 304 """
304 305 Should only be used by redirects, everything else should call _get_f_path
305 306 """
306 307 f_path = matchdict.get('f_path')
307 308 if f_path:
308 309 # fix for multiple initial slashes that causes errors for GIT
309 310 return f_path.lstrip('/')
310 311
311 312 return default
312 313
313 314 def _get_f_path(self, matchdict, default=None):
314 315 f_path_match = self._get_f_path_unchecked(matchdict, default)
315 316 return self.path_filter.assert_path_permissions(f_path_match)
316 317
317 318 def _get_general_setting(self, target_repo, settings_key, default=False):
318 319 settings_model = VcsSettingsModel(repo=target_repo)
319 320 settings = settings_model.get_general_settings()
320 321 return settings.get(settings_key, default)
321 322
322 323 def _get_repo_setting(self, target_repo, settings_key, default=False):
323 324 settings_model = VcsSettingsModel(repo=target_repo)
324 325 settings = settings_model.get_repo_settings_inherited()
325 326 return settings.get(settings_key, default)
326 327
327 328 def _get_readme_data(self, db_repo, renderer_type, commit_id=None, path='/'):
328 329 log.debug('Looking for README file at path %s', path)
329 330 if commit_id:
330 331 landing_commit_id = commit_id
331 332 else:
332 333 landing_commit = db_repo.get_landing_commit()
333 334 if isinstance(landing_commit, EmptyCommit):
334 335 return None, None
335 336 landing_commit_id = landing_commit.raw_id
336 337
337 338 cache_namespace_uid = 'cache_repo.{}'.format(db_repo.repo_id)
338 339 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
339 340 start = time.time()
340 341
341 342 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
342 343 def generate_repo_readme(repo_id, _commit_id, _repo_name, _readme_search_path, _renderer_type):
343 344 readme_data = None
344 345 readme_filename = None
345 346
346 347 commit = db_repo.get_commit(_commit_id)
347 348 log.debug("Searching for a README file at commit %s.", _commit_id)
348 349 readme_node = ReadmeFinder(_renderer_type).search(commit, path=_readme_search_path)
349 350
350 351 if readme_node:
351 352 log.debug('Found README node: %s', readme_node)
352 353 relative_urls = {
353 354 'raw': h.route_path(
354 355 'repo_file_raw', repo_name=_repo_name,
355 356 commit_id=commit.raw_id, f_path=readme_node.path),
356 357 'standard': h.route_path(
357 358 'repo_files', repo_name=_repo_name,
358 359 commit_id=commit.raw_id, f_path=readme_node.path),
359 360 }
360 361 readme_data = self._render_readme_or_none(commit, readme_node, relative_urls)
361 362 readme_filename = readme_node.unicode_path
362 363
363 364 return readme_data, readme_filename
364 365
365 366 readme_data, readme_filename = generate_repo_readme(
366 367 db_repo.repo_id, landing_commit_id, db_repo.repo_name, path, renderer_type,)
367 368 compute_time = time.time() - start
368 369 log.debug('Repo README for path %s generated and computed in %.4fs',
369 370 path, compute_time)
370 371 return readme_data, readme_filename
371 372
372 373 def _render_readme_or_none(self, commit, readme_node, relative_urls):
373 374 log.debug('Found README file `%s` rendering...', readme_node.path)
374 375 renderer = MarkupRenderer()
375 376 try:
376 377 html_source = renderer.render(
377 378 readme_node.content, filename=readme_node.path)
378 379 if relative_urls:
379 380 return relative_links(html_source, relative_urls)
380 381 return html_source
381 382 except Exception:
382 383 log.exception(
383 384 "Exception while trying to render the README")
384 385
385 386 def get_recache_flag(self):
386 387 for flag_name in ['force_recache', 'force-recache', 'no-cache']:
387 388 flag_val = self.request.GET.get(flag_name)
388 389 if str2bool(flag_val):
389 390 return True
390 391 return False
391 392
392 393 def get_commit_preload_attrs(cls):
393 394 pre_load = ['author', 'branch', 'date', 'message', 'parents',
394 395 'obsolete', 'phase', 'hidden']
395 396 return pre_load
396 397
397 398
398 399 class PathFilter(object):
399 400
400 401 # Expects and instance of BasePathPermissionChecker or None
401 402 def __init__(self, permission_checker):
402 403 self.permission_checker = permission_checker
403 404
404 405 def assert_path_permissions(self, path):
405 406 if self.path_access_allowed(path):
406 407 return path
407 408 raise HTTPForbidden()
408 409
409 410 def path_access_allowed(self, path):
410 411 log.debug('Checking ACL permissions for PathFilter for `%s`', path)
411 412 if self.permission_checker:
412 413 has_access = path and self.permission_checker.has_access(path)
413 414 log.debug('ACL Permissions checker enabled, ACL Check has_access: %s', has_access)
414 415 return has_access
415 416
416 417 log.debug('ACL permissions checker not enabled, skipping...')
417 418 return True
418 419
419 420 def filter_patchset(self, patchset):
420 421 if not self.permission_checker or not patchset:
421 422 return patchset, False
422 423 had_filtered = False
423 424 filtered_patchset = []
424 425 for patch in patchset:
425 426 filename = patch.get('filename', None)
426 427 if not filename or self.permission_checker.has_access(filename):
427 428 filtered_patchset.append(patch)
428 429 else:
429 430 had_filtered = True
430 431 if had_filtered:
431 432 if isinstance(patchset, diffs.LimitedDiffContainer):
432 433 filtered_patchset = diffs.LimitedDiffContainer(patchset.diff_limit, patchset.cur_diff_size, filtered_patchset)
433 434 return filtered_patchset, True
434 435 else:
435 436 return patchset, False
436 437
437 438 def render_patchset_filtered(self, diffset, patchset, source_ref=None, target_ref=None):
438 439 filtered_patchset, has_hidden_changes = self.filter_patchset(patchset)
439 440 result = diffset.render_patchset(
440 441 filtered_patchset, source_ref=source_ref, target_ref=target_ref)
441 442 result.has_hidden_changes = has_hidden_changes
442 443 return result
443 444
444 445 def get_raw_patch(self, diff_processor):
445 446 if self.permission_checker is None:
446 447 return diff_processor.as_raw()
447 448 elif self.permission_checker.has_full_access:
448 449 return diff_processor.as_raw()
449 450 else:
450 451 return '# Repository has user-specific filters, raw patch generation is disabled.'
451 452
452 453 @property
453 454 def is_enabled(self):
454 455 return self.permission_checker is not None
455 456
456 457
457 458 class RepoGroupAppView(BaseAppView):
458 459 def __init__(self, context, request):
459 460 super(RepoGroupAppView, self).__init__(context, request)
460 461 self.db_repo_group = request.db_repo_group
461 462 self.db_repo_group_name = self.db_repo_group.group_name
462 463
463 464 def _get_local_tmpl_context(self, include_app_defaults=True):
464 465 _ = self.request.translate
465 466 c = super(RepoGroupAppView, self)._get_local_tmpl_context(
466 467 include_app_defaults=include_app_defaults)
467 468 c.repo_group = self.db_repo_group
468 469 return c
469 470
470 471 def _revoke_perms_on_yourself(self, form_result):
471 472 _updates = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
472 473 form_result['perm_updates'])
473 474 _additions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
474 475 form_result['perm_additions'])
475 476 _deletions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
476 477 form_result['perm_deletions'])
477 478 admin_perm = 'group.admin'
478 479 if _updates and _updates[0][1] != admin_perm or \
479 480 _additions and _additions[0][1] != admin_perm or \
480 481 _deletions and _deletions[0][1] != admin_perm:
481 482 return True
482 483 return False
483 484
484 485
485 486 class UserGroupAppView(BaseAppView):
486 487 def __init__(self, context, request):
487 488 super(UserGroupAppView, self).__init__(context, request)
488 489 self.db_user_group = request.db_user_group
489 490 self.db_user_group_name = self.db_user_group.users_group_name
490 491
491 492
492 493 class UserAppView(BaseAppView):
493 494 def __init__(self, context, request):
494 495 super(UserAppView, self).__init__(context, request)
495 496 self.db_user = request.db_user
496 497 self.db_user_id = self.db_user.user_id
497 498
498 499 _ = self.request.translate
499 500 if not request.db_user_supports_default:
500 501 if self.db_user.username == User.DEFAULT_USER:
501 502 h.flash(_("Editing user `{}` is disabled.".format(
502 503 User.DEFAULT_USER)), category='warning')
503 504 raise HTTPFound(h.route_path('users'))
504 505
505 506
506 507 class DataGridAppView(object):
507 508 """
508 509 Common class to have re-usable grid rendering components
509 510 """
510 511
511 512 def _extract_ordering(self, request, column_map=None):
512 513 column_map = column_map or {}
513 514 column_index = safe_int(request.GET.get('order[0][column]'))
514 515 order_dir = request.GET.get(
515 516 'order[0][dir]', 'desc')
516 517 order_by = request.GET.get(
517 518 'columns[%s][data][sort]' % column_index, 'name_raw')
518 519
519 520 # translate datatable to DB columns
520 521 order_by = column_map.get(order_by) or order_by
521 522
522 523 search_q = request.GET.get('search[value]')
523 524 return search_q, order_by, order_dir
524 525
525 526 def _extract_chunk(self, request):
526 527 start = safe_int(request.GET.get('start'), 0)
527 528 length = safe_int(request.GET.get('length'), 25)
528 529 draw = safe_int(request.GET.get('draw'))
529 530 return draw, start, length
530 531
531 532 def _get_order_col(self, order_by, model):
532 533 if isinstance(order_by, compat.string_types):
533 534 try:
534 535 return operator.attrgetter(order_by)(model)
535 536 except AttributeError:
536 537 return None
537 538 else:
538 539 return order_by
539 540
540 541
541 542 class BaseReferencesView(RepoAppView):
542 543 """
543 544 Base for reference view for branches, tags and bookmarks.
544 545 """
545 546 def load_default_context(self):
546 547 c = self._get_local_tmpl_context()
547 548 return c
548 549
549 550 def load_refs_context(self, ref_items, partials_template):
550 551 _render = self.request.get_partial_renderer(partials_template)
551 552 pre_load = ["author", "date", "message", "parents"]
552 553
553 554 is_svn = h.is_svn(self.rhodecode_vcs_repo)
554 555 is_hg = h.is_hg(self.rhodecode_vcs_repo)
555 556
556 557 format_ref_id = get_format_ref_id(self.rhodecode_vcs_repo)
557 558
558 559 closed_refs = {}
559 560 if is_hg:
560 561 closed_refs = self.rhodecode_vcs_repo.branches_closed
561 562
562 563 data = []
563 564 for ref_name, commit_id in ref_items:
564 565 commit = self.rhodecode_vcs_repo.get_commit(
565 566 commit_id=commit_id, pre_load=pre_load)
566 567 closed = ref_name in closed_refs
567 568
568 569 # TODO: johbo: Unify generation of reference links
569 570 use_commit_id = '/' in ref_name or is_svn
570 571
571 572 if use_commit_id:
572 573 files_url = h.route_path(
573 574 'repo_files',
574 575 repo_name=self.db_repo_name,
575 576 f_path=ref_name if is_svn else '',
576 577 commit_id=commit_id,
577 578 _query=dict(at=ref_name)
578 579 )
579 580
580 581 else:
581 582 files_url = h.route_path(
582 583 'repo_files',
583 584 repo_name=self.db_repo_name,
584 585 f_path=ref_name if is_svn else '',
585 586 commit_id=ref_name,
586 587 _query=dict(at=ref_name)
587 588 )
588 589
589 590 data.append({
590 591 "name": _render('name', ref_name, files_url, closed),
591 592 "name_raw": ref_name,
592 593 "date": _render('date', commit.date),
593 594 "date_raw": datetime_to_time(commit.date),
594 595 "author": _render('author', commit.author),
595 596 "commit": _render(
596 597 'commit', commit.message, commit.raw_id, commit.idx),
597 598 "commit_raw": commit.idx,
598 599 "compare": _render(
599 600 'compare', format_ref_id(ref_name, commit.raw_id)),
600 601 })
601 602
602 603 return data
603 604
604 605
605 606 class RepoRoutePredicate(object):
606 607 def __init__(self, val, config):
607 608 self.val = val
608 609
609 610 def text(self):
610 611 return 'repo_route = %s' % self.val
611 612
612 613 phash = text
613 614
614 615 def __call__(self, info, request):
615 616 if hasattr(request, 'vcs_call'):
616 617 # skip vcs calls
617 618 return
618 619
619 620 repo_name = info['match']['repo_name']
621 if repo_name != repo_name_slug(repo_name):
622 # short-skip if the repo-name doesn't follow slug rule
623 return False
624
620 625 repo_model = repo.RepoModel()
621 626
622 627 by_name_match = repo_model.get_by_repo_name(repo_name, cache=False)
623 628
624 629 def redirect_if_creating(route_info, db_repo):
625 630 skip_views = ['edit_repo_advanced_delete']
626 631 route = route_info['route']
627 632 # we should skip delete view so we can actually "remove" repositories
628 633 # if they get stuck in creating state.
629 634 if route.name in skip_views:
630 635 return
631 636
632 637 if db_repo.repo_state in [repo.Repository.STATE_PENDING]:
633 638 repo_creating_url = request.route_path(
634 639 'repo_creating', repo_name=db_repo.repo_name)
635 640 raise HTTPFound(repo_creating_url)
636 641
637 642 if by_name_match:
638 643 # register this as request object we can re-use later
639 644 request.db_repo = by_name_match
640 645 redirect_if_creating(info, by_name_match)
641 646 return True
642 647
643 648 by_id_match = repo_model.get_repo_by_id(repo_name)
644 649 if by_id_match:
645 650 request.db_repo = by_id_match
646 651 redirect_if_creating(info, by_id_match)
647 652 return True
648 653
649 654 return False
650 655
651 656
652 657 class RepoForbidArchivedRoutePredicate(object):
653 658 def __init__(self, val, config):
654 659 self.val = val
655 660
656 661 def text(self):
657 662 return 'repo_forbid_archived = %s' % self.val
658 663
659 664 phash = text
660 665
661 666 def __call__(self, info, request):
662 667 _ = request.translate
663 668 rhodecode_db_repo = request.db_repo
664 669
665 670 log.debug(
666 671 '%s checking if archived flag for repo for %s',
667 672 self.__class__.__name__, rhodecode_db_repo.repo_name)
668 673
669 674 if rhodecode_db_repo.archived:
670 675 log.warning('Current view is not supported for archived repo:%s',
671 676 rhodecode_db_repo.repo_name)
672 677
673 678 h.flash(
674 679 h.literal(_('Action not supported for archived repository.')),
675 680 category='warning')
676 681 summary_url = request.route_path(
677 682 'repo_summary', repo_name=rhodecode_db_repo.repo_name)
678 683 raise HTTPFound(summary_url)
679 684 return True
680 685
681 686
682 687 class RepoTypeRoutePredicate(object):
683 688 def __init__(self, val, config):
684 689 self.val = val or ['hg', 'git', 'svn']
685 690
686 691 def text(self):
687 692 return 'repo_accepted_type = %s' % self.val
688 693
689 694 phash = text
690 695
691 696 def __call__(self, info, request):
692 697 if hasattr(request, 'vcs_call'):
693 698 # skip vcs calls
694 699 return
695 700
696 701 rhodecode_db_repo = request.db_repo
697 702
698 703 log.debug(
699 704 '%s checking repo type for %s in %s',
700 705 self.__class__.__name__, rhodecode_db_repo.repo_type, self.val)
701 706
702 707 if rhodecode_db_repo.repo_type in self.val:
703 708 return True
704 709 else:
705 710 log.warning('Current view is not supported for repo type:%s',
706 711 rhodecode_db_repo.repo_type)
707 712 return False
708 713
709 714
710 715 class RepoGroupRoutePredicate(object):
711 716 def __init__(self, val, config):
712 717 self.val = val
713 718
714 719 def text(self):
715 720 return 'repo_group_route = %s' % self.val
716 721
717 722 phash = text
718 723
719 724 def __call__(self, info, request):
720 725 if hasattr(request, 'vcs_call'):
721 726 # skip vcs calls
722 727 return
723 728
724 729 repo_group_name = info['match']['repo_group_name']
730 if repo_group_name != repo_name_slug(repo_group_name):
731 return False
732
725 733 repo_group_model = repo_group.RepoGroupModel()
726 734 by_name_match = repo_group_model.get_by_group_name(repo_group_name, cache=False)
727 735
728 736 if by_name_match:
729 737 # register this as request object we can re-use later
730 738 request.db_repo_group = by_name_match
731 739 return True
732 740
733 741 return False
734 742
735 743
736 744 class UserGroupRoutePredicate(object):
737 745 def __init__(self, val, config):
738 746 self.val = val
739 747
740 748 def text(self):
741 749 return 'user_group_route = %s' % self.val
742 750
743 751 phash = text
744 752
745 753 def __call__(self, info, request):
746 754 if hasattr(request, 'vcs_call'):
747 755 # skip vcs calls
748 756 return
749 757
750 758 user_group_id = info['match']['user_group_id']
751 759 user_group_model = user_group.UserGroup()
752 760 by_id_match = user_group_model.get(user_group_id, cache=False)
753 761
754 762 if by_id_match:
755 763 # register this as request object we can re-use later
756 764 request.db_user_group = by_id_match
757 765 return True
758 766
759 767 return False
760 768
761 769
762 770 class UserRoutePredicateBase(object):
763 771 supports_default = None
764 772
765 773 def __init__(self, val, config):
766 774 self.val = val
767 775
768 776 def text(self):
769 777 raise NotImplementedError()
770 778
771 779 def __call__(self, info, request):
772 780 if hasattr(request, 'vcs_call'):
773 781 # skip vcs calls
774 782 return
775 783
776 784 user_id = info['match']['user_id']
777 785 user_model = user.User()
778 786 by_id_match = user_model.get(user_id, cache=False)
779 787
780 788 if by_id_match:
781 789 # register this as request object we can re-use later
782 790 request.db_user = by_id_match
783 791 request.db_user_supports_default = self.supports_default
784 792 return True
785 793
786 794 return False
787 795
788 796
789 797 class UserRoutePredicate(UserRoutePredicateBase):
790 798 supports_default = False
791 799
792 800 def text(self):
793 801 return 'user_route = %s' % self.val
794 802
795 803 phash = text
796 804
797 805
798 806 class UserRouteWithDefaultPredicate(UserRoutePredicateBase):
799 807 supports_default = True
800 808
801 809 def text(self):
802 810 return 'user_with_default_route = %s' % self.val
803 811
804 812 phash = text
805 813
806 814
807 815 def includeme(config):
808 816 config.add_route_predicate(
809 817 'repo_route', RepoRoutePredicate)
810 818 config.add_route_predicate(
811 819 'repo_accepted_types', RepoTypeRoutePredicate)
812 820 config.add_route_predicate(
813 821 'repo_forbid_when_archived', RepoForbidArchivedRoutePredicate)
814 822 config.add_route_predicate(
815 823 'repo_group_route', RepoGroupRoutePredicate)
816 824 config.add_route_predicate(
817 825 'user_group_route', UserGroupRoutePredicate)
818 826 config.add_route_predicate(
819 827 'user_route_with_default', UserRouteWithDefaultPredicate)
820 828 config.add_route_predicate(
821 829 'user_route', UserRoutePredicate)
General Comments 0
You need to be logged in to leave comments. Login now