--- trac/wiki/web_ui.py.orig 2006-02-13 15:32:40.000000000 -0800 +++ trac/wiki/web_ui.py 2006-02-13 15:32:09.000000000 -0800 @@ -32,13 +32,15 @@ from trac.web import IRequestHandler from trac.wiki.model import WikiPage from trac.wiki.formatter import wiki_to_html, wiki_to_oneliner - +from trac.wiki.rbac import WikiAuthorizer class WikiModule(Component): implements(INavigationContributor, IPermissionRequestor, IRequestHandler, ITimelineEventProvider, ISearchSource) + authz = property(fget=lambda self: self._get_authz()) + # INavigationContributor methods def get_active_navigation_item(self, req): @@ -74,6 +76,7 @@ pagename = req.args.get('page', 'WikiStart') version = req.args.get('version') + self.authz.assert_authorization(req.perm, req.authname, pagename, 'WIKI_VIEW') db = self.env.get_db_cnx() page = WikiPage(self.env, pagename, version, db) @@ -118,6 +121,13 @@ req.hdf['wiki.action'] = action req.hdf['wiki.page_name'] = page.name req.hdf['wiki.current_href'] = self.env.href.wiki(page.name) + for permission in self.get_permission_actions(): + if isinstance(permission, (list, tuple)): + permission = permission[0] + self.log.debug("PERMISSION: %s", permission) + req.hdf['trac.acl.' + permission] =\ + self.authz.has_authorization(req.perm, req.authname, page.name, permission) + return 'wiki.cs', None # ITimelineEventProvider methods @@ -135,24 +145,29 @@ "FROM wiki WHERE time>=%s AND time<=%s", (start, stop)) for t,name,comment,author in cursor: - title = Markup('%s edited by %s', name, author) - if format == 'rss': - href = self.env.abs_href.wiki(name) - comment = wiki_to_html(comment or '--', self.env, req, db, - absurls=True) - else: - href = self.env.href.wiki(name) - comment = wiki_to_oneliner(comment, self.env, db, - shorten=True) - yield 'wiki', href, title, t, author, comment + if self.authz.has_authorization(req.perm, req.authname, name, 'WIKI_VIEW'): + title = Markup('%s edited by %s', name, author) + if format == 'rss': + href = self.env.abs_href.wiki(name) + comment = wiki_to_html(comment or '--', self.env, req, db, + absurls=True) + else: + href = self.env.href.wiki(name) + comment = wiki_to_oneliner(comment, self.env, db, + shorten=True) + yield 'wiki', href, title, t, author, comment # Internal methods + def _get_authz(self): + return WikiAuthorizer(self.env) + def _do_delete(self, req, db, page): if page.readonly: req.perm.assert_permission('WIKI_ADMIN') else: req.perm.assert_permission('WIKI_DELETE') + self.authz.assert_authorization(req.perm, req.authname, page.name, 'WIKI_DELETE') if req.args.has_key('cancel'): req.redirect(self.env.href.wiki(page.name)) @@ -174,8 +189,10 @@ req.perm.assert_permission('WIKI_ADMIN') elif not page.exists: req.perm.assert_permission('WIKI_CREATE') + self.authz.assert_authorization(req.perm, req.authname, page.name, 'WIKI_CREATE') else: req.perm.assert_permission('WIKI_MODIFY') + self.authz.assert_authorization(req.perm, req.authname, page.name, 'WIKI_MODIFY') page.text = req.args.get('text') if req.perm.has_permission('WIKI_ADMIN'): @@ -192,6 +209,7 @@ req.perm.assert_permission('WIKI_ADMIN') else: req.perm.assert_permission('WIKI_DELETE') + self.authz.assert_authorization(req.perm, req.authname, page.name, 'WIKI_DELETE') version = None if req.args.has_key('delete_version'): @@ -210,6 +228,7 @@ def _render_diff(self, req, db, page): req.perm.assert_permission('WIKI_VIEW') + self.authz.assert_authorization(req.perm, req.authname, page.name, 'WIKI_VIEW') if not page.exists: raise TracError, "Version %s of page %s does not exist" \ @@ -277,6 +296,7 @@ def _render_editor(self, req, db, page, preview=False): req.perm.assert_permission('WIKI_MODIFY') + self.authz.assert_authorization(req.perm, req.authname, page.name, 'WIKI_MODIFY') if req.args.has_key('text'): page.text = req.args.get('text') @@ -320,6 +340,7 @@ page. """ req.perm.assert_permission('WIKI_VIEW') + self.authz.assert_authorization(req.perm, req.authname, page.name, 'WIKI_VIEW') if not page.exists: raise TracError, "Page %s does not exist" % page.name @@ -344,6 +365,7 @@ def _render_view(self, req, db, page): req.perm.assert_permission('WIKI_VIEW') + self.authz.assert_authorization(req.perm, req.authname, page.name, 'WIKI_VIEW') if page.name == 'WikiStart': req.hdf['title'] = '' @@ -365,7 +387,8 @@ history_href = self.env.href.wiki(page.name, action='history') req.hdf['wiki.history_href'] = history_href else: - if not req.perm.has_permission('WIKI_CREATE'): + if not req.perm.has_permission('WIKI_CREATE') and\ + self.authz.has_authorization(req.perm, req.authname, page.name, 'WIKI_CREATE'): raise TracError('Page %s not found' % page.name) req.hdf['wiki.page_html'] = Markup('

Describe "%s" here

', page.name) @@ -375,7 +398,8 @@ for attachment in Attachment.select(self.env, 'wiki', page.name, db): attachments.append(attachment_to_hdf(self.env, db, req, attachment)) req.hdf['wiki.attachments'] = attachments - if req.perm.has_permission('WIKI_MODIFY'): + if req.perm.has_permission('WIKI_MODIFY') and\ + self.authz.has_authorization(req.perm, req.authname, page.name, 'WIKI_MODIFY'): attach_href = self.env.href.attachment('wiki', page.name) req.hdf['wiki.attach_href'] = attach_href @@ -399,7 +423,8 @@ "AND " + sql_query, args) for name, date, author, text in cursor: - yield (self.env.href.wiki(name), - '%s: %s' % (name, shorten_line(text)), - date, author, - shorten_result(text, query.split())) + if self.authz.has_authorization(req.perm, req.authname, name, 'WIKI_VIEW'): + yield (self.env.href.wiki(name), + '%s: %s' % (name, shorten_line(text)), + date, author, + shorten_result(text, query.split())) --- trac/wiki/rbac.py.orig 2006-02-13 15:32:45.000000000 -0800 +++ trac/wiki/rbac.py 2006-02-13 15:29:34.000000000 -0800 @@ -0,0 +1,82 @@ +from trac.core import * +from trac.perm import PermissionError +from trac.versioncontrol.svn_authz import RealSubversionAuthorizer +from trac.versioncontrol import Authorizer, PermissionDenied + +def SubversionAuthorizer(env, authname): + authz_file = env.config.get('wiki', 'authz_file') or\ + env.config.get('trac', 'authz_file') + if not authz_file: + return Authorizer() + + module_name = env.config.get('wiki', 'authz_svn_module_name') + db = env.get_db_cnx() + return ExtendedSubversionAuthorizer(db, authname, module_name, authz_file) + +class IWikiAuthzProvider(Interface): + """Interface for classes that provide some method of checking a + user's access to a portion of the wiki.""" + + def has_authorization(user, path, operation): + """Verify that the given username is authorized to perform + the given operation on the given path. + returns boolean.""" + +class WikiAuthorizer(Component): + providers = ExtensionPoint(IWikiAuthzProvider) + + def _accumulate(self, current, result): + authmode = self.env.config.get('wiki', 'authorization_mode') + + if authmode == 'require_all': + return current & result + elif authmode == 'require_one': + return current | result + else: + return True + + def has_authorization(self, perm, user, path, operation): + if not perm.has_permission('WIKI_VIEW'): + return False + + authzed = True + for provider in self.providers: + authzed = self._accumulate(authzed, provider.has_authorization(user, path, operation)) + return authzed or perm.has_permission('TRAC_ADMIN') + + def assert_authorization(self, perm, user, path, operation): + if not self.has_authorization(perm, user, path, operation): + raise PermissionDenied,\ + '%s authorization on %s is necessary to perform this operation.' % (operation, 'wiki:' + path) + + +class WikiSubversionAuthorizer(Component): + implements(IWikiAuthzProvider) + + def has_authorization(self, user, path, operation): + path = '/' + path + authorizer = SubversionAuthorizer(self.env, user) + self.log.debug("Authorize %s check for: %s on %s:%s", operation, user, authorizer.module_name, path) + return authorizer.has_authorization(path, operation) + +class ExtendedSubversionAuthorizer(RealSubversionAuthorizer): + """Provides extended semantics for the subversion-based authorization""" + + opmap = {'WIKI_CREATE':'c', 'WIKI_DELETE':'d', 'WIKI_MODIFY':'w', 'WIKI_VIEW':'r', 'WIKI_ADMIN':'a'} + + def has_authorization(self, path, op): + self._set_opcheck('WIKI_ADMIN') + if self.has_permission(path): + return True + else: + self._set_opcheck(op) + return self.has_permission(path) + + def _set_opcheck(self, op): + op = self.opmap[op] + self._get_permission = lambda section, subject: self.__get_permission(section, subject, op) + + def __get_permission(self, section, subject, op): + if self.conf_authz.has_option(section, subject): + return op in self.conf_authz.get(section, subject) + return None