##// END OF EJS Templates
core: don't check channelstream connections for faster handling of this route.
super-admin -
r4700:ba59a558 stable
parent child
Show More
@@ -1,808 +1,816
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2020 RhodeCode GmbH
3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import time
21 import time
22 import logging
22 import logging
23 import operator
23 import operator
24
24
25 from pyramid import compat
25 from pyramid import compat
26 from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPBadRequest
26 from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPBadRequest
27
27
28 from rhodecode.lib import helpers as h, diffs, rc_cache
28 from rhodecode.lib import helpers as h, diffs, rc_cache
29 from rhodecode.lib.utils2 import (
29 from rhodecode.lib.utils2 import (
30 StrictAttributeDict, str2bool, safe_int, datetime_to_time, safe_unicode)
30 StrictAttributeDict, str2bool, safe_int, datetime_to_time, safe_unicode)
31 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
31 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
32 from rhodecode.lib.vcs.backends.base import EmptyCommit
32 from rhodecode.lib.vcs.backends.base import EmptyCommit
33 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
33 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
34 from rhodecode.model import repo
34 from rhodecode.model import repo
35 from rhodecode.model import repo_group
35 from rhodecode.model import repo_group
36 from rhodecode.model import user_group
36 from rhodecode.model import user_group
37 from rhodecode.model import user
37 from rhodecode.model import user
38 from rhodecode.model.db import User
38 from rhodecode.model.db import User
39 from rhodecode.model.scm import ScmModel
39 from rhodecode.model.scm import ScmModel
40 from rhodecode.model.settings import VcsSettingsModel, IssueTrackerSettingsModel
40 from rhodecode.model.settings import VcsSettingsModel, IssueTrackerSettingsModel
41 from rhodecode.model.repo import ReadmeFinder
41 from rhodecode.model.repo import ReadmeFinder
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45
45
46 ADMIN_PREFIX = '/_admin'
46 ADMIN_PREFIX = '/_admin'
47 STATIC_FILE_PREFIX = '/_static'
47 STATIC_FILE_PREFIX = '/_static'
48
48
49 URL_NAME_REQUIREMENTS = {
49 URL_NAME_REQUIREMENTS = {
50 # group name can have a slash in them, but they must not end with a slash
50 # group name can have a slash in them, but they must not end with a slash
51 'group_name': r'.*?[^/]',
51 'group_name': r'.*?[^/]',
52 'repo_group_name': r'.*?[^/]',
52 'repo_group_name': r'.*?[^/]',
53 # repo names can have a slash in them, but they must not end with a slash
53 # repo names can have a slash in them, but they must not end with a slash
54 'repo_name': r'.*?[^/]',
54 'repo_name': r'.*?[^/]',
55 # file path eats up everything at the end
55 # file path eats up everything at the end
56 'f_path': r'.*',
56 'f_path': r'.*',
57 # reference types
57 # reference types
58 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
58 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
59 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
59 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
60 }
60 }
61
61
62
62
63 def add_route_with_slash(config,name, pattern, **kw):
63 def add_route_with_slash(config,name, pattern, **kw):
64 config.add_route(name, pattern, **kw)
64 config.add_route(name, pattern, **kw)
65 if not pattern.endswith('/'):
65 if not pattern.endswith('/'):
66 config.add_route(name + '_slash', pattern + '/', **kw)
66 config.add_route(name + '_slash', pattern + '/', **kw)
67
67
68
68
69 def add_route_requirements(route_path, requirements=None):
69 def add_route_requirements(route_path, requirements=None):
70 """
70 """
71 Adds regex requirements to pyramid routes using a mapping dict
71 Adds regex requirements to pyramid routes using a mapping dict
72 e.g::
72 e.g::
73 add_route_requirements('{repo_name}/settings')
73 add_route_requirements('{repo_name}/settings')
74 """
74 """
75 requirements = requirements or URL_NAME_REQUIREMENTS
75 requirements = requirements or URL_NAME_REQUIREMENTS
76 for key, regex in requirements.items():
76 for key, regex in requirements.items():
77 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
77 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
78 return route_path
78 return route_path
79
79
80
80
81 def get_format_ref_id(repo):
81 def get_format_ref_id(repo):
82 """Returns a `repo` specific reference formatter function"""
82 """Returns a `repo` specific reference formatter function"""
83 if h.is_svn(repo):
83 if h.is_svn(repo):
84 return _format_ref_id_svn
84 return _format_ref_id_svn
85 else:
85 else:
86 return _format_ref_id
86 return _format_ref_id
87
87
88
88
89 def _format_ref_id(name, raw_id):
89 def _format_ref_id(name, raw_id):
90 """Default formatting of a given reference `name`"""
90 """Default formatting of a given reference `name`"""
91 return name
91 return name
92
92
93
93
94 def _format_ref_id_svn(name, raw_id):
94 def _format_ref_id_svn(name, raw_id):
95 """Special way of formatting a reference for Subversion including path"""
95 """Special way of formatting a reference for Subversion including path"""
96 return '%s@%s' % (name, raw_id)
96 return '%s@%s' % (name, raw_id)
97
97
98
98
99 class TemplateArgs(StrictAttributeDict):
99 class TemplateArgs(StrictAttributeDict):
100 pass
100 pass
101
101
102
102
103 class BaseAppView(object):
103 class BaseAppView(object):
104
104
105 def __init__(self, context, request):
105 def __init__(self, context, request):
106 self.request = request
106 self.request = request
107 self.context = context
107 self.context = context
108 self.session = request.session
108 self.session = request.session
109 if not hasattr(request, 'user'):
109 if not hasattr(request, 'user'):
110 # NOTE(marcink): edge case, we ended up in matched route
110 # NOTE(marcink): edge case, we ended up in matched route
111 # but probably of web-app context, e.g API CALL/VCS CALL
111 # but probably of web-app context, e.g API CALL/VCS CALL
112 if hasattr(request, 'vcs_call') or hasattr(request, 'rpc_method'):
112 if hasattr(request, 'vcs_call') or hasattr(request, 'rpc_method'):
113 log.warning('Unable to process request `%s` in this scope', request)
113 log.warning('Unable to process request `%s` in this scope', request)
114 raise HTTPBadRequest()
114 raise HTTPBadRequest()
115
115
116 self._rhodecode_user = request.user # auth user
116 self._rhodecode_user = request.user # auth user
117 self._rhodecode_db_user = self._rhodecode_user.get_instance()
117 self._rhodecode_db_user = self._rhodecode_user.get_instance()
118 self._maybe_needs_password_change(
118 self._maybe_needs_password_change(
119 request.matched_route.name, self._rhodecode_db_user)
119 request.matched_route.name, self._rhodecode_db_user)
120
120
121 def _maybe_needs_password_change(self, view_name, user_obj):
121 def _maybe_needs_password_change(self, view_name, user_obj):
122
123 dont_check_views = [
124 'channelstream_connect'
125 ]
126 if view_name in dont_check_views:
127 return
128
122 log.debug('Checking if user %s needs password change on view %s',
129 log.debug('Checking if user %s needs password change on view %s',
123 user_obj, view_name)
130 user_obj, view_name)
131
124 skip_user_views = [
132 skip_user_views = [
125 'logout', 'login',
133 'logout', 'login',
126 'my_account_password', 'my_account_password_update'
134 'my_account_password', 'my_account_password_update'
127 ]
135 ]
128
136
129 if not user_obj:
137 if not user_obj:
130 return
138 return
131
139
132 if user_obj.username == User.DEFAULT_USER:
140 if user_obj.username == User.DEFAULT_USER:
133 return
141 return
134
142
135 now = time.time()
143 now = time.time()
136 should_change = user_obj.user_data.get('force_password_change')
144 should_change = user_obj.user_data.get('force_password_change')
137 change_after = safe_int(should_change) or 0
145 change_after = safe_int(should_change) or 0
138 if should_change and now > change_after:
146 if should_change and now > change_after:
139 log.debug('User %s requires password change', user_obj)
147 log.debug('User %s requires password change', user_obj)
140 h.flash('You are required to change your password', 'warning',
148 h.flash('You are required to change your password', 'warning',
141 ignore_duplicate=True)
149 ignore_duplicate=True)
142
150
143 if view_name not in skip_user_views:
151 if view_name not in skip_user_views:
144 raise HTTPFound(
152 raise HTTPFound(
145 self.request.route_path('my_account_password'))
153 self.request.route_path('my_account_password'))
146
154
147 def _log_creation_exception(self, e, repo_name):
155 def _log_creation_exception(self, e, repo_name):
148 _ = self.request.translate
156 _ = self.request.translate
149 reason = None
157 reason = None
150 if len(e.args) == 2:
158 if len(e.args) == 2:
151 reason = e.args[1]
159 reason = e.args[1]
152
160
153 if reason == 'INVALID_CERTIFICATE':
161 if reason == 'INVALID_CERTIFICATE':
154 log.exception(
162 log.exception(
155 'Exception creating a repository: invalid certificate')
163 'Exception creating a repository: invalid certificate')
156 msg = (_('Error creating repository %s: invalid certificate')
164 msg = (_('Error creating repository %s: invalid certificate')
157 % repo_name)
165 % repo_name)
158 else:
166 else:
159 log.exception("Exception creating a repository")
167 log.exception("Exception creating a repository")
160 msg = (_('Error creating repository %s')
168 msg = (_('Error creating repository %s')
161 % repo_name)
169 % repo_name)
162 return msg
170 return msg
163
171
164 def _get_local_tmpl_context(self, include_app_defaults=True):
172 def _get_local_tmpl_context(self, include_app_defaults=True):
165 c = TemplateArgs()
173 c = TemplateArgs()
166 c.auth_user = self.request.user
174 c.auth_user = self.request.user
167 # TODO(marcink): migrate the usage of c.rhodecode_user to c.auth_user
175 # TODO(marcink): migrate the usage of c.rhodecode_user to c.auth_user
168 c.rhodecode_user = self.request.user
176 c.rhodecode_user = self.request.user
169
177
170 if include_app_defaults:
178 if include_app_defaults:
171 from rhodecode.lib.base import attach_context_attributes
179 from rhodecode.lib.base import attach_context_attributes
172 attach_context_attributes(c, self.request, self.request.user.user_id)
180 attach_context_attributes(c, self.request, self.request.user.user_id)
173
181
174 c.is_super_admin = c.auth_user.is_admin
182 c.is_super_admin = c.auth_user.is_admin
175
183
176 c.can_create_repo = c.is_super_admin
184 c.can_create_repo = c.is_super_admin
177 c.can_create_repo_group = c.is_super_admin
185 c.can_create_repo_group = c.is_super_admin
178 c.can_create_user_group = c.is_super_admin
186 c.can_create_user_group = c.is_super_admin
179
187
180 c.is_delegated_admin = False
188 c.is_delegated_admin = False
181
189
182 if not c.auth_user.is_default and not c.is_super_admin:
190 if not c.auth_user.is_default and not c.is_super_admin:
183 c.can_create_repo = h.HasPermissionAny('hg.create.repository')(
191 c.can_create_repo = h.HasPermissionAny('hg.create.repository')(
184 user=self.request.user)
192 user=self.request.user)
185 repositories = c.auth_user.repositories_admin or c.can_create_repo
193 repositories = c.auth_user.repositories_admin or c.can_create_repo
186
194
187 c.can_create_repo_group = h.HasPermissionAny('hg.repogroup.create.true')(
195 c.can_create_repo_group = h.HasPermissionAny('hg.repogroup.create.true')(
188 user=self.request.user)
196 user=self.request.user)
189 repository_groups = c.auth_user.repository_groups_admin or c.can_create_repo_group
197 repository_groups = c.auth_user.repository_groups_admin or c.can_create_repo_group
190
198
191 c.can_create_user_group = h.HasPermissionAny('hg.usergroup.create.true')(
199 c.can_create_user_group = h.HasPermissionAny('hg.usergroup.create.true')(
192 user=self.request.user)
200 user=self.request.user)
193 user_groups = c.auth_user.user_groups_admin or c.can_create_user_group
201 user_groups = c.auth_user.user_groups_admin or c.can_create_user_group
194 # delegated admin can create, or manage some objects
202 # delegated admin can create, or manage some objects
195 c.is_delegated_admin = repositories or repository_groups or user_groups
203 c.is_delegated_admin = repositories or repository_groups or user_groups
196 return c
204 return c
197
205
198 def _get_template_context(self, tmpl_args, **kwargs):
206 def _get_template_context(self, tmpl_args, **kwargs):
199
207
200 local_tmpl_args = {
208 local_tmpl_args = {
201 'defaults': {},
209 'defaults': {},
202 'errors': {},
210 'errors': {},
203 'c': tmpl_args
211 'c': tmpl_args
204 }
212 }
205 local_tmpl_args.update(kwargs)
213 local_tmpl_args.update(kwargs)
206 return local_tmpl_args
214 return local_tmpl_args
207
215
208 def load_default_context(self):
216 def load_default_context(self):
209 """
217 """
210 example:
218 example:
211
219
212 def load_default_context(self):
220 def load_default_context(self):
213 c = self._get_local_tmpl_context()
221 c = self._get_local_tmpl_context()
214 c.custom_var = 'foobar'
222 c.custom_var = 'foobar'
215
223
216 return c
224 return c
217 """
225 """
218 raise NotImplementedError('Needs implementation in view class')
226 raise NotImplementedError('Needs implementation in view class')
219
227
220
228
221 class RepoAppView(BaseAppView):
229 class RepoAppView(BaseAppView):
222
230
223 def __init__(self, context, request):
231 def __init__(self, context, request):
224 super(RepoAppView, self).__init__(context, request)
232 super(RepoAppView, self).__init__(context, request)
225 self.db_repo = request.db_repo
233 self.db_repo = request.db_repo
226 self.db_repo_name = self.db_repo.repo_name
234 self.db_repo_name = self.db_repo.repo_name
227 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
235 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
228 self.db_repo_artifacts = ScmModel().get_artifacts(self.db_repo)
236 self.db_repo_artifacts = ScmModel().get_artifacts(self.db_repo)
229 self.db_repo_patterns = IssueTrackerSettingsModel(repo=self.db_repo)
237 self.db_repo_patterns = IssueTrackerSettingsModel(repo=self.db_repo)
230
238
231 def _handle_missing_requirements(self, error):
239 def _handle_missing_requirements(self, error):
232 log.error(
240 log.error(
233 'Requirements are missing for repository %s: %s',
241 'Requirements are missing for repository %s: %s',
234 self.db_repo_name, safe_unicode(error))
242 self.db_repo_name, safe_unicode(error))
235
243
236 def _get_local_tmpl_context(self, include_app_defaults=True):
244 def _get_local_tmpl_context(self, include_app_defaults=True):
237 _ = self.request.translate
245 _ = self.request.translate
238 c = super(RepoAppView, self)._get_local_tmpl_context(
246 c = super(RepoAppView, self)._get_local_tmpl_context(
239 include_app_defaults=include_app_defaults)
247 include_app_defaults=include_app_defaults)
240
248
241 # register common vars for this type of view
249 # register common vars for this type of view
242 c.rhodecode_db_repo = self.db_repo
250 c.rhodecode_db_repo = self.db_repo
243 c.repo_name = self.db_repo_name
251 c.repo_name = self.db_repo_name
244 c.repository_pull_requests = self.db_repo_pull_requests
252 c.repository_pull_requests = self.db_repo_pull_requests
245 c.repository_artifacts = self.db_repo_artifacts
253 c.repository_artifacts = self.db_repo_artifacts
246 c.repository_is_user_following = ScmModel().is_following_repo(
254 c.repository_is_user_following = ScmModel().is_following_repo(
247 self.db_repo_name, self._rhodecode_user.user_id)
255 self.db_repo_name, self._rhodecode_user.user_id)
248 self.path_filter = PathFilter(None)
256 self.path_filter = PathFilter(None)
249
257
250 c.repository_requirements_missing = {}
258 c.repository_requirements_missing = {}
251 try:
259 try:
252 self.rhodecode_vcs_repo = self.db_repo.scm_instance()
260 self.rhodecode_vcs_repo = self.db_repo.scm_instance()
253 # NOTE(marcink):
261 # NOTE(marcink):
254 # comparison to None since if it's an object __bool__ is expensive to
262 # comparison to None since if it's an object __bool__ is expensive to
255 # calculate
263 # calculate
256 if self.rhodecode_vcs_repo is not None:
264 if self.rhodecode_vcs_repo is not None:
257 path_perms = self.rhodecode_vcs_repo.get_path_permissions(
265 path_perms = self.rhodecode_vcs_repo.get_path_permissions(
258 c.auth_user.username)
266 c.auth_user.username)
259 self.path_filter = PathFilter(path_perms)
267 self.path_filter = PathFilter(path_perms)
260 except RepositoryRequirementError as e:
268 except RepositoryRequirementError as e:
261 c.repository_requirements_missing = {'error': str(e)}
269 c.repository_requirements_missing = {'error': str(e)}
262 self._handle_missing_requirements(e)
270 self._handle_missing_requirements(e)
263 self.rhodecode_vcs_repo = None
271 self.rhodecode_vcs_repo = None
264
272
265 c.path_filter = self.path_filter # used by atom_feed_entry.mako
273 c.path_filter = self.path_filter # used by atom_feed_entry.mako
266
274
267 if self.rhodecode_vcs_repo is None:
275 if self.rhodecode_vcs_repo is None:
268 # unable to fetch this repo as vcs instance, report back to user
276 # unable to fetch this repo as vcs instance, report back to user
269 h.flash(_(
277 h.flash(_(
270 "The repository `%(repo_name)s` cannot be loaded in filesystem. "
278 "The repository `%(repo_name)s` cannot be loaded in filesystem. "
271 "Please check if it exist, or is not damaged.") %
279 "Please check if it exist, or is not damaged.") %
272 {'repo_name': c.repo_name},
280 {'repo_name': c.repo_name},
273 category='error', ignore_duplicate=True)
281 category='error', ignore_duplicate=True)
274 if c.repository_requirements_missing:
282 if c.repository_requirements_missing:
275 route = self.request.matched_route.name
283 route = self.request.matched_route.name
276 if route.startswith(('edit_repo', 'repo_summary')):
284 if route.startswith(('edit_repo', 'repo_summary')):
277 # allow summary and edit repo on missing requirements
285 # allow summary and edit repo on missing requirements
278 return c
286 return c
279
287
280 raise HTTPFound(
288 raise HTTPFound(
281 h.route_path('repo_summary', repo_name=self.db_repo_name))
289 h.route_path('repo_summary', repo_name=self.db_repo_name))
282
290
283 else: # redirect if we don't show missing requirements
291 else: # redirect if we don't show missing requirements
284 raise HTTPFound(h.route_path('home'))
292 raise HTTPFound(h.route_path('home'))
285
293
286 c.has_origin_repo_read_perm = False
294 c.has_origin_repo_read_perm = False
287 if self.db_repo.fork:
295 if self.db_repo.fork:
288 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
296 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
289 'repository.write', 'repository.read', 'repository.admin')(
297 'repository.write', 'repository.read', 'repository.admin')(
290 self.db_repo.fork.repo_name, 'summary fork link')
298 self.db_repo.fork.repo_name, 'summary fork link')
291
299
292 return c
300 return c
293
301
294 def _get_f_path_unchecked(self, matchdict, default=None):
302 def _get_f_path_unchecked(self, matchdict, default=None):
295 """
303 """
296 Should only be used by redirects, everything else should call _get_f_path
304 Should only be used by redirects, everything else should call _get_f_path
297 """
305 """
298 f_path = matchdict.get('f_path')
306 f_path = matchdict.get('f_path')
299 if f_path:
307 if f_path:
300 # fix for multiple initial slashes that causes errors for GIT
308 # fix for multiple initial slashes that causes errors for GIT
301 return f_path.lstrip('/')
309 return f_path.lstrip('/')
302
310
303 return default
311 return default
304
312
305 def _get_f_path(self, matchdict, default=None):
313 def _get_f_path(self, matchdict, default=None):
306 f_path_match = self._get_f_path_unchecked(matchdict, default)
314 f_path_match = self._get_f_path_unchecked(matchdict, default)
307 return self.path_filter.assert_path_permissions(f_path_match)
315 return self.path_filter.assert_path_permissions(f_path_match)
308
316
309 def _get_general_setting(self, target_repo, settings_key, default=False):
317 def _get_general_setting(self, target_repo, settings_key, default=False):
310 settings_model = VcsSettingsModel(repo=target_repo)
318 settings_model = VcsSettingsModel(repo=target_repo)
311 settings = settings_model.get_general_settings()
319 settings = settings_model.get_general_settings()
312 return settings.get(settings_key, default)
320 return settings.get(settings_key, default)
313
321
314 def _get_repo_setting(self, target_repo, settings_key, default=False):
322 def _get_repo_setting(self, target_repo, settings_key, default=False):
315 settings_model = VcsSettingsModel(repo=target_repo)
323 settings_model = VcsSettingsModel(repo=target_repo)
316 settings = settings_model.get_repo_settings_inherited()
324 settings = settings_model.get_repo_settings_inherited()
317 return settings.get(settings_key, default)
325 return settings.get(settings_key, default)
318
326
319 def _get_readme_data(self, db_repo, renderer_type, commit_id=None, path='/'):
327 def _get_readme_data(self, db_repo, renderer_type, commit_id=None, path='/'):
320 log.debug('Looking for README file at path %s', path)
328 log.debug('Looking for README file at path %s', path)
321 if commit_id:
329 if commit_id:
322 landing_commit_id = commit_id
330 landing_commit_id = commit_id
323 else:
331 else:
324 landing_commit = db_repo.get_landing_commit()
332 landing_commit = db_repo.get_landing_commit()
325 if isinstance(landing_commit, EmptyCommit):
333 if isinstance(landing_commit, EmptyCommit):
326 return None, None
334 return None, None
327 landing_commit_id = landing_commit.raw_id
335 landing_commit_id = landing_commit.raw_id
328
336
329 cache_namespace_uid = 'cache_repo.{}'.format(db_repo.repo_id)
337 cache_namespace_uid = 'cache_repo.{}'.format(db_repo.repo_id)
330 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
338 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
331 start = time.time()
339 start = time.time()
332
340
333 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
341 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
334 def generate_repo_readme(repo_id, _commit_id, _repo_name, _readme_search_path, _renderer_type):
342 def generate_repo_readme(repo_id, _commit_id, _repo_name, _readme_search_path, _renderer_type):
335 readme_data = None
343 readme_data = None
336 readme_filename = None
344 readme_filename = None
337
345
338 commit = db_repo.get_commit(_commit_id)
346 commit = db_repo.get_commit(_commit_id)
339 log.debug("Searching for a README file at commit %s.", _commit_id)
347 log.debug("Searching for a README file at commit %s.", _commit_id)
340 readme_node = ReadmeFinder(_renderer_type).search(commit, path=_readme_search_path)
348 readme_node = ReadmeFinder(_renderer_type).search(commit, path=_readme_search_path)
341
349
342 if readme_node:
350 if readme_node:
343 log.debug('Found README node: %s', readme_node)
351 log.debug('Found README node: %s', readme_node)
344 relative_urls = {
352 relative_urls = {
345 'raw': h.route_path(
353 'raw': h.route_path(
346 'repo_file_raw', repo_name=_repo_name,
354 'repo_file_raw', repo_name=_repo_name,
347 commit_id=commit.raw_id, f_path=readme_node.path),
355 commit_id=commit.raw_id, f_path=readme_node.path),
348 'standard': h.route_path(
356 'standard': h.route_path(
349 'repo_files', repo_name=_repo_name,
357 'repo_files', repo_name=_repo_name,
350 commit_id=commit.raw_id, f_path=readme_node.path),
358 commit_id=commit.raw_id, f_path=readme_node.path),
351 }
359 }
352 readme_data = self._render_readme_or_none(commit, readme_node, relative_urls)
360 readme_data = self._render_readme_or_none(commit, readme_node, relative_urls)
353 readme_filename = readme_node.unicode_path
361 readme_filename = readme_node.unicode_path
354
362
355 return readme_data, readme_filename
363 return readme_data, readme_filename
356
364
357 readme_data, readme_filename = generate_repo_readme(
365 readme_data, readme_filename = generate_repo_readme(
358 db_repo.repo_id, landing_commit_id, db_repo.repo_name, path, renderer_type,)
366 db_repo.repo_id, landing_commit_id, db_repo.repo_name, path, renderer_type,)
359 compute_time = time.time() - start
367 compute_time = time.time() - start
360 log.debug('Repo README for path %s generated and computed in %.4fs',
368 log.debug('Repo README for path %s generated and computed in %.4fs',
361 path, compute_time)
369 path, compute_time)
362 return readme_data, readme_filename
370 return readme_data, readme_filename
363
371
364 def _render_readme_or_none(self, commit, readme_node, relative_urls):
372 def _render_readme_or_none(self, commit, readme_node, relative_urls):
365 log.debug('Found README file `%s` rendering...', readme_node.path)
373 log.debug('Found README file `%s` rendering...', readme_node.path)
366 renderer = MarkupRenderer()
374 renderer = MarkupRenderer()
367 try:
375 try:
368 html_source = renderer.render(
376 html_source = renderer.render(
369 readme_node.content, filename=readme_node.path)
377 readme_node.content, filename=readme_node.path)
370 if relative_urls:
378 if relative_urls:
371 return relative_links(html_source, relative_urls)
379 return relative_links(html_source, relative_urls)
372 return html_source
380 return html_source
373 except Exception:
381 except Exception:
374 log.exception(
382 log.exception(
375 "Exception while trying to render the README")
383 "Exception while trying to render the README")
376
384
377 def get_recache_flag(self):
385 def get_recache_flag(self):
378 for flag_name in ['force_recache', 'force-recache', 'no-cache']:
386 for flag_name in ['force_recache', 'force-recache', 'no-cache']:
379 flag_val = self.request.GET.get(flag_name)
387 flag_val = self.request.GET.get(flag_name)
380 if str2bool(flag_val):
388 if str2bool(flag_val):
381 return True
389 return True
382 return False
390 return False
383
391
384
392
385 class PathFilter(object):
393 class PathFilter(object):
386
394
387 # Expects and instance of BasePathPermissionChecker or None
395 # Expects and instance of BasePathPermissionChecker or None
388 def __init__(self, permission_checker):
396 def __init__(self, permission_checker):
389 self.permission_checker = permission_checker
397 self.permission_checker = permission_checker
390
398
391 def assert_path_permissions(self, path):
399 def assert_path_permissions(self, path):
392 if self.path_access_allowed(path):
400 if self.path_access_allowed(path):
393 return path
401 return path
394 raise HTTPForbidden()
402 raise HTTPForbidden()
395
403
396 def path_access_allowed(self, path):
404 def path_access_allowed(self, path):
397 log.debug('Checking ACL permissions for PathFilter for `%s`', path)
405 log.debug('Checking ACL permissions for PathFilter for `%s`', path)
398 if self.permission_checker:
406 if self.permission_checker:
399 has_access = path and self.permission_checker.has_access(path)
407 has_access = path and self.permission_checker.has_access(path)
400 log.debug('ACL Permissions checker enabled, ACL Check has_access: %s', has_access)
408 log.debug('ACL Permissions checker enabled, ACL Check has_access: %s', has_access)
401 return has_access
409 return has_access
402
410
403 log.debug('ACL permissions checker not enabled, skipping...')
411 log.debug('ACL permissions checker not enabled, skipping...')
404 return True
412 return True
405
413
406 def filter_patchset(self, patchset):
414 def filter_patchset(self, patchset):
407 if not self.permission_checker or not patchset:
415 if not self.permission_checker or not patchset:
408 return patchset, False
416 return patchset, False
409 had_filtered = False
417 had_filtered = False
410 filtered_patchset = []
418 filtered_patchset = []
411 for patch in patchset:
419 for patch in patchset:
412 filename = patch.get('filename', None)
420 filename = patch.get('filename', None)
413 if not filename or self.permission_checker.has_access(filename):
421 if not filename or self.permission_checker.has_access(filename):
414 filtered_patchset.append(patch)
422 filtered_patchset.append(patch)
415 else:
423 else:
416 had_filtered = True
424 had_filtered = True
417 if had_filtered:
425 if had_filtered:
418 if isinstance(patchset, diffs.LimitedDiffContainer):
426 if isinstance(patchset, diffs.LimitedDiffContainer):
419 filtered_patchset = diffs.LimitedDiffContainer(patchset.diff_limit, patchset.cur_diff_size, filtered_patchset)
427 filtered_patchset = diffs.LimitedDiffContainer(patchset.diff_limit, patchset.cur_diff_size, filtered_patchset)
420 return filtered_patchset, True
428 return filtered_patchset, True
421 else:
429 else:
422 return patchset, False
430 return patchset, False
423
431
424 def render_patchset_filtered(self, diffset, patchset, source_ref=None, target_ref=None):
432 def render_patchset_filtered(self, diffset, patchset, source_ref=None, target_ref=None):
425 filtered_patchset, has_hidden_changes = self.filter_patchset(patchset)
433 filtered_patchset, has_hidden_changes = self.filter_patchset(patchset)
426 result = diffset.render_patchset(
434 result = diffset.render_patchset(
427 filtered_patchset, source_ref=source_ref, target_ref=target_ref)
435 filtered_patchset, source_ref=source_ref, target_ref=target_ref)
428 result.has_hidden_changes = has_hidden_changes
436 result.has_hidden_changes = has_hidden_changes
429 return result
437 return result
430
438
431 def get_raw_patch(self, diff_processor):
439 def get_raw_patch(self, diff_processor):
432 if self.permission_checker is None:
440 if self.permission_checker is None:
433 return diff_processor.as_raw()
441 return diff_processor.as_raw()
434 elif self.permission_checker.has_full_access:
442 elif self.permission_checker.has_full_access:
435 return diff_processor.as_raw()
443 return diff_processor.as_raw()
436 else:
444 else:
437 return '# Repository has user-specific filters, raw patch generation is disabled.'
445 return '# Repository has user-specific filters, raw patch generation is disabled.'
438
446
439 @property
447 @property
440 def is_enabled(self):
448 def is_enabled(self):
441 return self.permission_checker is not None
449 return self.permission_checker is not None
442
450
443
451
444 class RepoGroupAppView(BaseAppView):
452 class RepoGroupAppView(BaseAppView):
445 def __init__(self, context, request):
453 def __init__(self, context, request):
446 super(RepoGroupAppView, self).__init__(context, request)
454 super(RepoGroupAppView, self).__init__(context, request)
447 self.db_repo_group = request.db_repo_group
455 self.db_repo_group = request.db_repo_group
448 self.db_repo_group_name = self.db_repo_group.group_name
456 self.db_repo_group_name = self.db_repo_group.group_name
449
457
450 def _get_local_tmpl_context(self, include_app_defaults=True):
458 def _get_local_tmpl_context(self, include_app_defaults=True):
451 _ = self.request.translate
459 _ = self.request.translate
452 c = super(RepoGroupAppView, self)._get_local_tmpl_context(
460 c = super(RepoGroupAppView, self)._get_local_tmpl_context(
453 include_app_defaults=include_app_defaults)
461 include_app_defaults=include_app_defaults)
454 c.repo_group = self.db_repo_group
462 c.repo_group = self.db_repo_group
455 return c
463 return c
456
464
457 def _revoke_perms_on_yourself(self, form_result):
465 def _revoke_perms_on_yourself(self, form_result):
458 _updates = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
466 _updates = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
459 form_result['perm_updates'])
467 form_result['perm_updates'])
460 _additions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
468 _additions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
461 form_result['perm_additions'])
469 form_result['perm_additions'])
462 _deletions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
470 _deletions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
463 form_result['perm_deletions'])
471 form_result['perm_deletions'])
464 admin_perm = 'group.admin'
472 admin_perm = 'group.admin'
465 if _updates and _updates[0][1] != admin_perm or \
473 if _updates and _updates[0][1] != admin_perm or \
466 _additions and _additions[0][1] != admin_perm or \
474 _additions and _additions[0][1] != admin_perm or \
467 _deletions and _deletions[0][1] != admin_perm:
475 _deletions and _deletions[0][1] != admin_perm:
468 return True
476 return True
469 return False
477 return False
470
478
471
479
472 class UserGroupAppView(BaseAppView):
480 class UserGroupAppView(BaseAppView):
473 def __init__(self, context, request):
481 def __init__(self, context, request):
474 super(UserGroupAppView, self).__init__(context, request)
482 super(UserGroupAppView, self).__init__(context, request)
475 self.db_user_group = request.db_user_group
483 self.db_user_group = request.db_user_group
476 self.db_user_group_name = self.db_user_group.users_group_name
484 self.db_user_group_name = self.db_user_group.users_group_name
477
485
478
486
479 class UserAppView(BaseAppView):
487 class UserAppView(BaseAppView):
480 def __init__(self, context, request):
488 def __init__(self, context, request):
481 super(UserAppView, self).__init__(context, request)
489 super(UserAppView, self).__init__(context, request)
482 self.db_user = request.db_user
490 self.db_user = request.db_user
483 self.db_user_id = self.db_user.user_id
491 self.db_user_id = self.db_user.user_id
484
492
485 _ = self.request.translate
493 _ = self.request.translate
486 if not request.db_user_supports_default:
494 if not request.db_user_supports_default:
487 if self.db_user.username == User.DEFAULT_USER:
495 if self.db_user.username == User.DEFAULT_USER:
488 h.flash(_("Editing user `{}` is disabled.".format(
496 h.flash(_("Editing user `{}` is disabled.".format(
489 User.DEFAULT_USER)), category='warning')
497 User.DEFAULT_USER)), category='warning')
490 raise HTTPFound(h.route_path('users'))
498 raise HTTPFound(h.route_path('users'))
491
499
492
500
493 class DataGridAppView(object):
501 class DataGridAppView(object):
494 """
502 """
495 Common class to have re-usable grid rendering components
503 Common class to have re-usable grid rendering components
496 """
504 """
497
505
498 def _extract_ordering(self, request, column_map=None):
506 def _extract_ordering(self, request, column_map=None):
499 column_map = column_map or {}
507 column_map = column_map or {}
500 column_index = safe_int(request.GET.get('order[0][column]'))
508 column_index = safe_int(request.GET.get('order[0][column]'))
501 order_dir = request.GET.get(
509 order_dir = request.GET.get(
502 'order[0][dir]', 'desc')
510 'order[0][dir]', 'desc')
503 order_by = request.GET.get(
511 order_by = request.GET.get(
504 'columns[%s][data][sort]' % column_index, 'name_raw')
512 'columns[%s][data][sort]' % column_index, 'name_raw')
505
513
506 # translate datatable to DB columns
514 # translate datatable to DB columns
507 order_by = column_map.get(order_by) or order_by
515 order_by = column_map.get(order_by) or order_by
508
516
509 search_q = request.GET.get('search[value]')
517 search_q = request.GET.get('search[value]')
510 return search_q, order_by, order_dir
518 return search_q, order_by, order_dir
511
519
512 def _extract_chunk(self, request):
520 def _extract_chunk(self, request):
513 start = safe_int(request.GET.get('start'), 0)
521 start = safe_int(request.GET.get('start'), 0)
514 length = safe_int(request.GET.get('length'), 25)
522 length = safe_int(request.GET.get('length'), 25)
515 draw = safe_int(request.GET.get('draw'))
523 draw = safe_int(request.GET.get('draw'))
516 return draw, start, length
524 return draw, start, length
517
525
518 def _get_order_col(self, order_by, model):
526 def _get_order_col(self, order_by, model):
519 if isinstance(order_by, compat.string_types):
527 if isinstance(order_by, compat.string_types):
520 try:
528 try:
521 return operator.attrgetter(order_by)(model)
529 return operator.attrgetter(order_by)(model)
522 except AttributeError:
530 except AttributeError:
523 return None
531 return None
524 else:
532 else:
525 return order_by
533 return order_by
526
534
527
535
528 class BaseReferencesView(RepoAppView):
536 class BaseReferencesView(RepoAppView):
529 """
537 """
530 Base for reference view for branches, tags and bookmarks.
538 Base for reference view for branches, tags and bookmarks.
531 """
539 """
532 def load_default_context(self):
540 def load_default_context(self):
533 c = self._get_local_tmpl_context()
541 c = self._get_local_tmpl_context()
534 return c
542 return c
535
543
536 def load_refs_context(self, ref_items, partials_template):
544 def load_refs_context(self, ref_items, partials_template):
537 _render = self.request.get_partial_renderer(partials_template)
545 _render = self.request.get_partial_renderer(partials_template)
538 pre_load = ["author", "date", "message", "parents"]
546 pre_load = ["author", "date", "message", "parents"]
539
547
540 is_svn = h.is_svn(self.rhodecode_vcs_repo)
548 is_svn = h.is_svn(self.rhodecode_vcs_repo)
541 is_hg = h.is_hg(self.rhodecode_vcs_repo)
549 is_hg = h.is_hg(self.rhodecode_vcs_repo)
542
550
543 format_ref_id = get_format_ref_id(self.rhodecode_vcs_repo)
551 format_ref_id = get_format_ref_id(self.rhodecode_vcs_repo)
544
552
545 closed_refs = {}
553 closed_refs = {}
546 if is_hg:
554 if is_hg:
547 closed_refs = self.rhodecode_vcs_repo.branches_closed
555 closed_refs = self.rhodecode_vcs_repo.branches_closed
548
556
549 data = []
557 data = []
550 for ref_name, commit_id in ref_items:
558 for ref_name, commit_id in ref_items:
551 commit = self.rhodecode_vcs_repo.get_commit(
559 commit = self.rhodecode_vcs_repo.get_commit(
552 commit_id=commit_id, pre_load=pre_load)
560 commit_id=commit_id, pre_load=pre_load)
553 closed = ref_name in closed_refs
561 closed = ref_name in closed_refs
554
562
555 # TODO: johbo: Unify generation of reference links
563 # TODO: johbo: Unify generation of reference links
556 use_commit_id = '/' in ref_name or is_svn
564 use_commit_id = '/' in ref_name or is_svn
557
565
558 if use_commit_id:
566 if use_commit_id:
559 files_url = h.route_path(
567 files_url = h.route_path(
560 'repo_files',
568 'repo_files',
561 repo_name=self.db_repo_name,
569 repo_name=self.db_repo_name,
562 f_path=ref_name if is_svn else '',
570 f_path=ref_name if is_svn else '',
563 commit_id=commit_id,
571 commit_id=commit_id,
564 _query=dict(at=ref_name)
572 _query=dict(at=ref_name)
565 )
573 )
566
574
567 else:
575 else:
568 files_url = h.route_path(
576 files_url = h.route_path(
569 'repo_files',
577 'repo_files',
570 repo_name=self.db_repo_name,
578 repo_name=self.db_repo_name,
571 f_path=ref_name if is_svn else '',
579 f_path=ref_name if is_svn else '',
572 commit_id=ref_name,
580 commit_id=ref_name,
573 _query=dict(at=ref_name)
581 _query=dict(at=ref_name)
574 )
582 )
575
583
576 data.append({
584 data.append({
577 "name": _render('name', ref_name, files_url, closed),
585 "name": _render('name', ref_name, files_url, closed),
578 "name_raw": ref_name,
586