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