Ticket #5640: 5640-hide-forbidden-changesets-r8198.patch
| File 5640-hide-forbidden-changesets-r8198.patch, 11.8 KB (added by rblank, 3 years ago) |
|---|
-
trac/versioncontrol/api.py
diff --git a/trac/versioncontrol/api.py b/trac/versioncontrol/api.py
a b 185 185 ((msg and '%s: ' % msg) or '', path, rev), 186 186 _('No such node')) 187 187 188 class ChangesetDenied(ResourceNotFound): 189 def __init__(self, rev): 190 ResourceNotFound.__init__(self, 191 _('Access to changeset %(rev)s is denied', 192 rev=rev), 193 _('Changeset not authorized')) 194 195 188 196 class Repository(object): 189 197 """Base class for a repository provided by a version control system.""" 190 198 … … 230 238 return [] 231 239 232 240 def get_changeset(self, rev): 233 """Retrieve a Changeset corresponding to the given revision `rev`."""241 """Retrieve a Changeset corresponding to the given revision `rev`.""" 234 242 raise NotImplementedError 235 243 236 244 def get_changesets(self, start, stop): -
trac/versioncontrol/cache.py
diff --git a/trac/versioncontrol/cache.py b/trac/versioncontrol/cache.py
a b 22 22 from trac.util.datefmt import utc, to_timestamp 23 23 from trac.util.translation import _ 24 24 from trac.versioncontrol import Changeset, Node, Repository, Authorizer, \ 25 NoSuchChangeset 25 NoSuchChangeset, ChangesetDenied 26 26 27 27 28 28 _kindmap = {'D': Node.DIRECTORY, 'F': Node.FILE} … … 136 136 if self.youngest != repos_youngest: 137 137 self.log.info("repos rev [%s] != cached rev [%s]" % 138 138 (repos_youngest, self.youngest)) 139 if self.youngest: 140 next_youngest = self.repos.next_rev(self.youngest) 141 else: 142 next_youngest = None 143 try: 144 next_youngest = self.repos.oldest_rev 145 # Ugly hack needed because doing that everytime in 146 # oldest_rev suffers from horrendeous performance (#5213) 147 if hasattr(self.repos, 'scope'): 148 if self.repos.scope != '/': 149 next_youngest = self.repos.next_rev(next_youngest, 150 find_initial_rev=True) 151 next_youngest = self.repos.normalize_rev(next_youngest) 152 except TracError: 153 return # can't normalize oldest_rev: repository was empty 139 authz = self.repos.authz 140 self.repos.authz = Authorizer() # remove permission checking 141 try: 142 if self.youngest: 143 next_youngest = self.repos.next_rev(self.youngest) 144 else: 145 next_youngest = None 146 try: 147 next_youngest = self.repos.oldest_rev 148 # Ugly hack needed because doing that everytime in 149 # oldest_rev suffers from horrendeous performance 150 # (#5213) 151 if hasattr(self.repos, 'scope'): 152 if self.repos.scope != '/': 153 next_youngest = self.repos.next_rev( 154 next_youngest, find_initial_rev=True) 155 next_youngest = self.repos.normalize_rev(next_youngest) 156 except TracError: 157 # can't normalize oldest_rev: repository was empty 158 return 159 160 if next_youngest is None: # nothing to cache yet 161 return 162 163 # 0. first check if there's no (obvious) resync in progress 164 cursor.execute("SELECT rev FROM revision WHERE rev=%s", 165 (str(next_youngest),)) 166 for rev, in cursor: 167 # already there, but in progress, so keep ''previous'' 168 # notion of 'youngest' 169 self.repos.clear(youngest_rev=self.youngest) 170 return 171 172 # 1. prepare for resyncing 173 # (there still might be a race condition at this point) 174 175 kindmap = dict(zip(_kindmap.values(), _kindmap.keys())) 176 actionmap = dict(zip(_actionmap.values(), _actionmap.keys())) 154 177 155 if next_youngest is None: # nothing to cache yet156 return157 158 # 0. first check if there's no (obvious) resync in progress159 cursor.execute("SELECT rev FROM revision WHERE rev=%s",160 (str(next_youngest),))161 for rev, in cursor:162 # already there, but in progress, so keep ''previous''163 # notion of 'youngest'164 self.repos.clear(youngest_rev=self.youngest)165 return166 167 # 1. prepare for resyncing168 # (there still might be a race condition at this point)169 170 authz = self.repos.authz171 self.repos.authz = Authorizer() # remove permission checking172 173 kindmap = dict(zip(_kindmap.values(), _kindmap.keys()))174 actionmap = dict(zip(_actionmap.values(), _actionmap.keys()))175 176 try:177 178 while next_youngest is not None: 178 179 179 180 # 1.1 Attempt to resync the 'revision' table … … 270 271 args.append(path) 271 272 sql += " OR " 272 273 # changes on path children 273 sql += "path " +db.like()274 sql += "path " + db.like() 274 275 args.append(db.like_escape(path+'/') + '%') 275 276 sql += " OR " 276 277 # deletion of path ancestors … … 282 283 sql += ")" 283 284 284 285 sql += " ORDER BY " + db.cast('rev', 'int') + \ 285 (direction == '<' and " DESC" or "") + " LIMIT 1"286 (direction == '<' and " DESC" or "") 286 287 287 288 cursor = db.cursor() 288 289 cursor.execute(sql, args) 289 290 for rev, in cursor: 290 return rev 291 if self.authz.has_permission_for_changeset(rev): 292 return rev 291 293 292 294 def rev_older_than(self, rev1, rev2): 293 295 return self.repos.rev_older_than(rev1, rev2) … … 332 334 cursor.execute("SELECT path,node_type,change_type,base_path,base_rev " 333 335 "FROM node_change WHERE rev=%s " 334 336 "ORDER BY path", (str(self.rev),)) 337 empty = True 338 any_returned = False 335 339 for path, kind, change, base_path, base_rev in cursor: 340 empty = False 336 341 if not self.authz.has_permission(posixpath.join(self.scope, 337 342 path.strip('/'))): 338 343 # FIXME: what about the base_path? 339 344 continue 345 any_returned = True 340 346 kind = _kindmap[kind] 341 347 change = _actionmap[change] 342 348 yield path, kind, change, base_path, base_rev 349 if not empty and not any_returned: 350 raise ChangesetDenied(self.rev) 343 351 344 352 def get_properties(self): 345 353 return self.repos.get_changeset(self.rev).get_properties() -
trac/versioncontrol/svn_authz.py
diff --git a/trac/versioncontrol/svn_authz.py b/trac/versioncontrol/svn_authz.py
a b 20 20 21 21 from trac.config import Option 22 22 from trac.core import * 23 from trac.versioncontrol import Authorizer 23 from trac.versioncontrol import Authorizer, ChangesetDenied 24 24 25 25 26 26 class SvnAuthzOptions(Component): … … 106 106 107 107 def has_permission_for_changeset(self, rev): 108 108 changeset = self.repos.get_changeset(rev) 109 for change in changeset.get_changes():110 # the repository checks permissions for each change, so just check111 # if any changes can be accessed109 try: 110 for change in changeset.get_changes(): 111 break 112 112 return 1 113 return 0 113 except ChangesetDenied: 114 return 0 114 115 115 116 # Internal API 116 117 -
trac/versioncontrol/svn_fs.py
diff --git a/trac/versioncontrol/svn_fs.py b/trac/versioncontrol/svn_fs.py
a b 56 56 from trac.core import * 57 57 from trac.versioncontrol import Changeset, Node, Repository, \ 58 58 IRepositoryConnector, \ 59 NoSuchChangeset, NoSuchNode 59 NoSuchChangeset, NoSuchNode, \ 60 ChangesetDenied 60 61 from trac.versioncontrol.cache import CachedRepository 61 62 from trac.versioncontrol.svn_authz import SubversionAuthorizer 62 63 from trac.versioncontrol.web_ui.browser import IPropertyRenderer … … 544 545 tmp1, tmp2 = tmp2, tmp1 545 546 if history_ptr: 546 547 path_utf8, rev = fs.history_location(history_ptr, tmp2()) 548 if not self.authz.has_permission_for_changeset(rev): 549 continue 547 550 tmp2.clear() 548 551 if rev < end: 549 552 break … … 560 563 return prev 561 564 return None 562 565 563 564 566 def get_oldest_rev(self): 565 567 if self.oldest is None: 566 568 self.oldest = 1 … … 909 911 copies, deletions = {}, {} 910 912 changes = [] 911 913 revroots = {} 914 empty = True 912 915 for path_utf8, change in editor.changes.items(): 913 path = _from_svn(path_utf8)916 change_path = _from_svn(path_utf8) 914 917 915 918 # Filtering on `path` 916 if not (_is_path_within_scope(self.scope, path) and 917 self.authz.has_permission(path)): 919 if not _is_path_within_scope(self.scope, change_path): 918 920 continue 919 921 920 922 path_utf8 = change.path … … 922 924 path = _from_svn(path_utf8) 923 925 base_path = _from_svn(base_path_utf8) 924 926 base_rev = change.base_rev 925 927 926 928 # Ensure `base_path` is within the scope 927 929 if not (_is_path_within_scope(self.scope, base_path) and 928 930 self.authz.has_permission(base_path)): 929 931 base_path, base_rev = None, -1 930 932 933 if not path and not base_path and self.scope != '/': 934 continue # deletion outside of scope, ignore 935 936 empty = False 937 if not self.authz.has_permission(change_path): 938 continue 939 931 940 # Determine the action 932 941 if not path: # deletion 933 942 if base_path: … … 937 946 deletions[base_path] = idx 938 947 elif self.scope == '/': # root property change 939 948 action = Changeset.EDIT 940 else: # deletion outside of scope, ignore941 continue942 949 elif change.added or not base_path: # add or copy 943 950 action = Changeset.ADD 944 951 if base_path and base_rev: … … 979 986 del changes[i - offset] 980 987 offset += 1 981 988 989 if not empty and not changes: 990 raise ChangesetDenied(self.rev) 982 991 changes.sort() 983 992 for change in changes: 984 993 yield tuple(change) -
trac/versioncontrol/web_ui/util.py
diff --git a/trac/versioncontrol/web_ui/util.py b/trac/versioncontrol/web_ui/util.py
a b 34 34 for rev in revs: 35 35 if rev in changes: 36 36 continue 37 try: 38 changeset = repos.get_changeset(rev) 39 except NoSuchChangeset: 40 changeset = {} 37 changeset = {} 38 if repos.authz.has_permission_for_changeset(rev): 39 try: 40 changeset = repos.get_changeset(rev) 41 except NoSuchChangeset: 42 pass 41 43 changes[rev] = changeset 42 44 return changes 43 45
