##// END OF EJS Templates
path-permissions: Initial support for path-based permissions
idlsoft -
r2618:940ad8b4 default
parent child Browse files
Show More
@@ -22,9 +22,9 b' import time'
22 22 import logging
23 23 import operator
24 24
25 from pyramid.httpexceptions import HTTPFound
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
26 26
27 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h, diffs
28 28 from rhodecode.lib.utils2 import StrictAttributeDict, safe_int, datetime_to_time
29 29 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
30 30 from rhodecode.model import repo
@@ -208,10 +208,14 b' class RepoAppView(BaseAppView):'
208 208 c.repository_requirements_missing = False
209 209 try:
210 210 self.rhodecode_vcs_repo = self.db_repo.scm_instance()
211 self.path_filter = PathFilter(self.rhodecode_vcs_repo.get_path_permissions(c.auth_user.username))
211 212 except RepositoryRequirementError as e:
212 213 c.repository_requirements_missing = True
213 214 self._handle_missing_requirements(e)
214 215 self.rhodecode_vcs_repo = None
216 self.path_filter = None
217
218 c.path_filter = self.path_filter # used by atom_feed_entry.mako
215 219
216 220 if (not c.repository_requirements_missing
217 221 and self.rhodecode_vcs_repo is None):
@@ -229,9 +233,57 b' class RepoAppView(BaseAppView):'
229 233 f_path = matchdict.get('f_path')
230 234 if f_path:
231 235 # fix for multiple initial slashes that causes errors for GIT
232 return f_path.lstrip('/')
236 return self.path_filter.assert_path_permissions(f_path.lstrip('/'))
237
238 return self.path_filter.assert_path_permissions(default)
239
240
241 class PathFilter(object):
242
243 # Expects and instance of BasePathPermissionChecker or None
244 def __init__(self, permission_checker):
245 self.permission_checker = permission_checker
246
247 def assert_path_permissions(self, path):
248 if path and self.permission_checker and not self.permission_checker.has_access(path):
249 raise HTTPForbidden()
250 return path
233 251
234 return default
252 def filter_patchset(self, patchset):
253 if not self.permission_checker or not patchset:
254 return patchset, False
255 had_filtered = False
256 filtered_patchset = []
257 for patch in patchset:
258 filename = patch.get('filename', None)
259 if not filename or self.permission_checker.has_access(filename):
260 filtered_patchset.append(patch)
261 else:
262 had_filtered = True
263 if had_filtered:
264 if isinstance(patchset, diffs.LimitedDiffContainer):
265 filtered_patchset = diffs.LimitedDiffContainer(patchset.diff_limit, patchset.cur_diff_size, filtered_patchset)
266 return filtered_patchset, True
267 else:
268 return patchset, False
269
270 def render_patchset_filtered(self, diffset, patchset, source_ref=None, target_ref=None):
271 filtered_patchset, has_hidden_changes = self.filter_patchset(patchset)
272 result = diffset.render_patchset(filtered_patchset, source_ref=source_ref, target_ref=target_ref)
273 result.has_hidden_changes = has_hidden_changes
274 return result
275
276 def get_raw_patch(self, diff_processor):
277 if self.permission_checker is None:
278 return diff_processor.as_raw()
279 elif self.permission_checker.has_full_access:
280 return diff_processor.as_raw()
281 else:
282 return '# Repository has user-specific filters, raw patch generation is disabled.'
283
284 @property
285 def is_enabled(self):
286 return self.permission_checker is not None
235 287
236 288
237 289 class RepoGroupAppView(BaseAppView):
@@ -272,13 +272,13 b' class RepoCommitsView(RepoAppView):'
272 272 source_node_getter=_node_getter(commit1),
273 273 target_node_getter=_node_getter(commit2),
274 274 comments=inline_comments)
275 diffset = diffset.render_patchset(
276 _parsed, commit1.raw_id, commit2.raw_id)
275 diffset = self.path_filter.render_patchset_filtered(
276 diffset, _parsed, commit1.raw_id, commit2.raw_id)
277 277
278 278 c.changes[commit.raw_id] = diffset
279 279 else:
280 280 # downloads/raw we only need RAW diff nothing else
281 diff = diff_processor.as_raw()
281 diff = self.path_filter.get_raw_patch(diff_processor)
282 282 c.changes[commit.raw_id] = [None, None, None, None, diff, None, None]
283 283
284 284 # sort comments by how they were generated
@@ -309,8 +309,8 b' class RepoCompareView(RepoAppView):'
309 309 source_node_getter=_node_getter(source_commit),
310 310 target_node_getter=_node_getter(target_commit),
311 311 )
312 c.diffset = diffset.render_patchset(
313 _parsed, source_ref, target_ref)
312 c.diffset = self.path_filter.render_patchset_filtered(
313 diffset, _parsed, source_ref, target_ref)
314 314
315 315 c.preview_mode = merge
316 316 c.source_commit = source_commit
@@ -90,13 +90,15 b' class RepoFeedView(RepoAppView):'
90 90 _renderer = self.request.get_partial_renderer(
91 91 'rhodecode:templates/feed/atom_feed_entry.mako')
92 92 diff_processor, parsed_diff, limited_diff = self._changes(commit)
93 filtered_parsed_diff, has_hidden_changes = self.path_filter.filter_patchset(parsed_diff)
93 94 return _renderer(
94 95 'body',
95 96 commit=commit,
96 parsed_diff=parsed_diff,
97 parsed_diff=filtered_parsed_diff,
97 98 limited_diff=limited_diff,
98 99 feed_include_diff=self.feed_include_diff,
99 100 diff_processor=diff_processor,
101 has_hidden_changes=has_hidden_changes
100 102 )
101 103
102 104 def _set_timezone(self, date, tzinfo=pytz.utc):
@@ -122,8 +124,7 b' class RepoFeedView(RepoAppView):'
122 124 """
123 125 self.load_default_context()
124 126
125 @cache_region('long_term')
126 def _generate_feed(cache_key):
127 def _generate_feed():
127 128 feed = Atom1Feed(
128 129 title=self.title % self.db_repo_name,
129 130 link=h.route_url('repo_summary', repo_name=self.db_repo_name),
@@ -146,12 +147,18 b' class RepoFeedView(RepoAppView):'
146 147
147 148 return feed.mime_type, feed.writeString('utf-8')
148 149
149 invalidator_context = CacheKey.repo_context_cache(
150 _generate_feed, self.db_repo_name, CacheKey.CACHE_TYPE_ATOM)
150 @cache_region('long_term')
151 def _generate_feed_and_cache(cache_key):
152 return _generate_feed()
151 153
152 with invalidator_context as context:
153 context.invalidate()
154 mime_type, feed = context.compute()
154 if self.path_filter.is_enabled:
155 invalidator_context = CacheKey.repo_context_cache(
156 _generate_feed_and_cache, self.db_repo_name, CacheKey.CACHE_TYPE_ATOM)
157 with invalidator_context as context:
158 context.invalidate()
159 mime_type, feed = context.compute()
160 else:
161 mime_type, feed = _generate_feed()
155 162
156 163 response = Response(feed)
157 164 response.content_type = mime_type
@@ -169,8 +176,7 b' class RepoFeedView(RepoAppView):'
169 176 """
170 177 self.load_default_context()
171 178
172 @cache_region('long_term')
173 def _generate_feed(cache_key):
179 def _generate_feed():
174 180 feed = Rss201rev2Feed(
175 181 title=self.title % self.db_repo_name,
176 182 link=h.route_url('repo_summary', repo_name=self.db_repo_name),
@@ -193,12 +199,19 b' class RepoFeedView(RepoAppView):'
193 199
194 200 return feed.mime_type, feed.writeString('utf-8')
195 201
196 invalidator_context = CacheKey.repo_context_cache(
197 _generate_feed, self.db_repo_name, CacheKey.CACHE_TYPE_RSS)
202 @cache_region('long_term')
203 def _generate_feed_and_cache(cache_key):
204 return _generate_feed()
198 205
199 with invalidator_context as context:
200 context.invalidate()
201 mime_type, feed = context.compute()
206 if self.path_filter.is_enabled:
207 invalidator_context = CacheKey.repo_context_cache(
208 _generate_feed_and_cache, self.db_repo_name, CacheKey.CACHE_TYPE_RSS)
209
210 with invalidator_context as context:
211 context.invalidate()
212 mime_type, feed = context.compute()
213 else:
214 mime_type, feed = _generate_feed()
202 215
203 216 response = Response(feed)
204 217 response.content_type = mime_type
@@ -426,7 +426,7 b' class RepoFilesView(RepoAppView):'
426 426 context=line_context)
427 427 diff = diffs.DiffProcessor(_diff, format='gitdiff')
428 428
429 response = Response(diff.as_raw())
429 response = Response(self.path_filter.get_raw_patch(diff))
430 430 response.content_type = 'text/plain'
431 431 response.content_disposition = (
432 432 'attachment; filename=%s_%s_vs_%s.diff' % (f_path, diff1, diff2)
@@ -442,7 +442,7 b' class RepoFilesView(RepoAppView):'
442 442 context=line_context)
443 443 diff = diffs.DiffProcessor(_diff, format='gitdiff')
444 444
445 response = Response(diff.as_raw())
445 response = Response(self.path_filter.get_raw_patch(diff))
446 446 response.content_type = 'text/plain'
447 447 charset = self._get_default_encoding(c)
448 448 if charset:
@@ -231,8 +231,8 b' class RepoPullRequestsView(RepoAppView, '
231 231 target_node_getter=_node_getter(source_commit),
232 232 comments=display_inline_comments
233 233 )
234 diffset = diffset.render_patchset(
235 _parsed, target_commit.raw_id, source_commit.raw_id)
234 diffset = self.path_filter.render_patchset_filtered(
235 diffset, _parsed, target_commit.raw_id, source_commit.raw_id)
236 236
237 237 return diffset
238 238
@@ -637,6 +637,17 b' class BaseRepository(object):'
637 637 warnings.warn("Use in_memory_commit instead", DeprecationWarning)
638 638 return self.in_memory_commit
639 639
640 #
641 def get_path_permissions(self, username):
642 """
643
644 Returns a path permission checker or None if not supported
645
646 :param username: session user name
647 :return: an instance of BasePathPermissionChecker or None
648 """
649 return None
650
640 651
641 652 class BaseCommit(object):
642 653 """
@@ -1618,3 +1629,13 b' class DiffChunk(object):'
1618 1629 self.header = match.groupdict()
1619 1630 self.diff = chunk[match.end():]
1620 1631 self.raw = chunk
1632
1633
1634 class BasePathPermissionChecker(object):
1635
1636 def __init__(self, username, has_full_access = False):
1637 self.username = username
1638 self.has_full_access = has_full_access
1639
1640 def has_access(self, path):
1641 raise NotImplemented()
@@ -132,7 +132,9 b' collapse_all = len(diffset.files) > coll'
132 132 </h2>
133 133 </div>
134 134
135 %if not diffset.files:
135 %if diffset.has_hidden_changes:
136 <p class="empty_data">${_('Some changes may be hidden')}</p>
137 %elif not diffset.files:
136 138 <p class="empty_data">${_('No files')}</p>
137 139 %endif
138 140
@@ -17,6 +17,10 b''
17 17 tag: ${tag} <br/>
18 18 % endfor
19 19
20 % if has_hidden_changes:
21 Has hidden changes<br/>
22 % endif
23
20 24 commit: <a href="${h.route_url('repo_commit', repo_name=c.rhodecode_db_repo.repo_name, commit_id=commit.raw_id)}">${h.show_id(commit)}</a>
21 25 <pre>
22 26 ${h.urlify_commit_message(commit.message)}
@@ -29,6 +33,6 b' commit: <a href="${h.route_url(\'repo_com'
29 33 % endfor
30 34
31 35 % if feed_include_diff:
32 ${diff_processor.as_raw()}
36 ${c.path_filter.get_raw_patch(diff_processor)}
33 37 % endif
34 38 </pre>
General Comments 0
You need to be logged in to leave comments. Login now