diff --git a/trac/versioncontrol/svn_authz.py b/trac/versioncontrol/svn_authz.py
|
a
|
b
|
|
| 30 | 30 | |
| 31 | 31 | |
| 32 | 32 | def parent_iter(path): |
| 33 | | path = path.strip('/') |
| 34 | | if path: |
| 35 | | path = '/' + path + '/' |
| 36 | | else: |
| 37 | | path = '/' |
| 38 | | |
| 39 | 33 | while 1: |
| 40 | 34 | yield path |
| 41 | 35 | if path == '/': |
| … |
… |
|
| 141 | 135 | # IPermissionPolicy methods |
| 142 | 136 | |
| 143 | 137 | def check_permission(self, action, username, resource, perm): |
| | 138 | if username == 'anonymous': |
| | 139 | usernames = ('$anonymous', '*') |
| | 140 | else: |
| | 141 | usernames = (username, '$authenticated', '*') |
| 144 | 142 | if action == 'FILE_VIEW' or action == 'BROWSER_VIEW': |
| 145 | 143 | authz, users = self._get_authz_info() |
| 146 | 144 | if authz is None: |
| 147 | 145 | return False |
| 148 | 146 | if resource is None: |
| 149 | | return users is True or username in users |
| | 147 | return bool(users & set(usernames)) |
| 150 | 148 | if resource.realm == 'source': |
| 151 | 149 | modules = [resource.parent.id or self.authz_module_name] |
| 152 | 150 | if modules[0]: |
| 153 | 151 | modules.append('') |
| 154 | | for p in parent_iter(resource.id): |
| | 152 | path = '/' + resource.id.strip('/') |
| | 153 | if path != '/': |
| | 154 | path += '/' |
| | 155 | |
| | 156 | # Walk from resource up parent directories |
| | 157 | for spath in parent_iter(path): |
| 155 | 158 | for module in modules: |
| 156 | | section = authz.get(module, {}).get(p, {}) |
| 157 | | result = section.get(username) |
| 158 | | if result is not None: |
| 159 | | return result |
| 160 | | result = section.get('*') |
| 161 | | if result is not None: |
| 162 | | return result |
| 163 | | return False |
| | 159 | section = authz.get(module, {}).get(spath) |
| | 160 | if section: |
| | 161 | for user in usernames: |
| | 162 | result = section.get(user) |
| | 163 | if result is not None: |
| | 164 | return result |
| | 165 | |
| | 166 | # Allow access to parent directories of allowed resources |
| | 167 | if any(section.get(user) is True |
| | 168 | for module in modules |
| | 169 | for spath, section in authz.get(module, {}).iteritems() |
| | 170 | if spath.startswith(path) |
| | 171 | for user in usernames): |
| | 172 | return True |
| | 173 | |
| 164 | 174 | elif action == 'CHANGESET_VIEW': |
| 165 | 175 | authz, users = self._get_authz_info() |
| 166 | 176 | if authz is None: |
| 167 | 177 | return False |
| 168 | 178 | if resource is None: |
| 169 | | return users is True or username in users |
| | 179 | return bool(users & set(usernames)) |
| 170 | 180 | if resource.realm == 'changeset': |
| 171 | 181 | rm = RepositoryManager(self.env) |
| 172 | 182 | repos = rm.get_repository(resource.parent.id) |
| … |
… |
|
| 193 | 203 | self.log.info('Parsing authz file: %s' % self.authz_file) |
| 194 | 204 | try: |
| 195 | 205 | self._authz = parse(read_file(self.authz_file)) |
| 196 | | users = set(user for module in self._authz.itervalues() |
| 197 | | for path in module.itervalues() |
| 198 | | for user, result in path.iteritems() if result) |
| 199 | | self._users = '*' in users or users |
| | 206 | self._users = set(user for module in self._authz.itervalues() |
| | 207 | for path in module.itervalues() |
| | 208 | for user, result in path.iteritems() |
| | 209 | if result) |
| 200 | 210 | except Exception, e: |
| 201 | 211 | self._authz = None |
| 202 | 212 | self._users = set() |
diff --git a/trac/versioncontrol/tests/svn_authz.py b/trac/versioncontrol/tests/svn_authz.py
|
a
|
b
|
|
| 148 | 148 | [/wildcard] |
| 149 | 149 | * = r |
| 150 | 150 | |
| | 151 | # Special tokens |
| | 152 | [/special/anonymous] |
| | 153 | $anonymous = r |
| | 154 | [/special/authenticated] |
| | 155 | $authenticated = r |
| | 156 | |
| 151 | 157 | # Groups |
| 152 | 158 | [/groups_a] |
| 153 | 159 | @group1 = r |
| … |
… |
|
| 198 | 204 | self.assertEqual(result, check) |
| 199 | 205 | |
| 200 | 206 | def test_default_permission(self): |
| 201 | | # By default, no permission is granted |
| 202 | | self.assertPermission(False, 'joe', '', '/not_defined') |
| 203 | | self.assertPermission(False, 'jane', 'repo', '/not/defined/either') |
| | 207 | # By default, permissions are undecided |
| | 208 | self.assertPermission(None, 'joe', '', '/not_defined') |
| | 209 | self.assertPermission(None, 'jane', 'repo', '/not/defined/either') |
| 204 | 210 | |
| 205 | 211 | def test_read_write(self): |
| 206 | 212 | # Allow 'r' and 'rw' entries, deny 'w' and empty entries |
| … |
… |
|
| 225 | 231 | def test_module_usage(self): |
| 226 | 232 | # If a module name is specified, the rules are specific to the module |
| 227 | 233 | self.assertPermission(True, 'user', 'module', '/module_a') |
| 228 | | self.assertPermission(False, 'user', 'module', '/module_b') |
| | 234 | self.assertPermission(None, 'user', 'module', '/module_b') |
| 229 | 235 | # If a module is specified, but the configuration contains a non-module |
| 230 | 236 | # path, the non-module path can still apply |
| 231 | 237 | self.assertPermission(True, 'user', 'module', '/module_c') |
| … |
… |
|
| 233 | 239 | self.assertPermission(False, 'user', 'module', '/module_d') |
| 234 | 240 | |
| 235 | 241 | def test_wildcard(self): |
| 236 | | # The * wildcard matches all users |
| | 242 | # The * wildcard matches all users, including anonymous |
| | 243 | self.assertPermission(True, 'anonymous', '', '/wildcard') |
| 237 | 244 | self.assertPermission(True, 'joe', '', '/wildcard') |
| 238 | 245 | self.assertPermission(True, 'jane', '', '/wildcard') |
| 239 | 246 | |
| | 247 | def test_special_tokens(self): |
| | 248 | # The $anonymous token matches only anonymous users |
| | 249 | self.assertPermission(True, 'anonymous', '', '/special/anonymous') |
| | 250 | self.assertPermission(None, 'user', '', '/special/anonymous') |
| | 251 | # The $authenticated token matches all authenticated users |
| | 252 | self.assertPermission(None, 'anonymous', '', '/special/authenticated') |
| | 253 | self.assertPermission(True, 'joe', '', '/special/authenticated') |
| | 254 | self.assertPermission(True, 'jane', '', '/special/authenticated') |
| | 255 | |
| 240 | 256 | def test_groups(self): |
| 241 | 257 | # Groups are specified in a separate section and used with an @ prefix |
| 242 | 258 | self.assertPermission(True, 'user', '', '/groups_a') |
diff --git a/trac/versioncontrol/web_ui/browser.py b/trac/versioncontrol/web_ui/browser.py
|
a
|
b
|
|
| 26 | 26 | from trac.mimeview.api import Mimeview, is_binary, \ |
| 27 | 27 | IHTMLPreviewAnnotator, Context |
| 28 | 28 | from trac.perm import IPermissionRequestor |
| 29 | | from trac.resource import ResourceNotFound |
| | 29 | from trac.resource import Resource, ResourceNotFound |
| 30 | 30 | from trac.util import embedded_numbers |
| 31 | 31 | from trac.util.compat import any |
| 32 | 32 | from trac.util.datefmt import http_date, utc |
| … |
… |
|
| 505 | 505 | except TracError, err: |
| 506 | 506 | entry = (reponame, repoinfo, None, None, |
| 507 | 507 | exception_to_unicode(err)) |
| | 508 | if entry[-1] is not None: # Check permission in case of error |
| | 509 | root = Resource('repository', reponame).child('source', '/') |
| | 510 | if 'BROWSER_VIEW' not in context.perm(root): |
| | 511 | continue |
| 508 | 512 | repositories.append(entry) |
| 509 | 513 | |
| 510 | 514 | # Ordering of repositories |