id	summary	reporter	owner	description	type	status	priority	component	severity	resolution	keywords	cc	release
221	Patch for 0.9.3	heinlein@madboa.com	puffy	The existing patch doesn't apply cleanly on 0.9.3 or 0.9.4. Here's a diff that works for us. It was written for 0.9.3, but it seems to work on 0.9.4 just as well.\r\n\r\nThe biggest change from 0.9.2 is that 0.9.3 no longer uses the `escape()` routine, opting instead for one called `Markup()`.\r\n\r\nThe only other issue I encountered is that in the very last section of the original (0.9.2) patch of `web_ui.py`, `self.authz.has_authorization()` was passed only three parameters. As far as I can tell, it needs four -- so the second one is now `req.authname`. I'm most definitely not a python hacker, so you might want to double check that for me.\r\n\r\nAnyway, the patch seems to work here, but I don't have a set of regression tests, so my QA is mostly along the lines of, "That page works... And that one... And that one... OK, everything looks right."\r\n\r\nThanks for all your work coding this up. We needed just this sort of access control on our intranet wiki.\r\n\r\n--Paul\r\n\r\n{{{\r\n--- trac/wiki/web_ui.py.orig_2006-02-13 15:32:40.000000000 -0800\r\n+++ trac/wiki/web_ui.py_2006-02-13 15:32:09.000000000 -0800\r\n@@ -32,13 +32,15 @@\r\n from trac.web import IRequestHandler\r\n from trac.wiki.model import WikiPage\r\n from trac.wiki.formatter import wiki_to_html, wiki_to_oneliner\r\n-\r\n+from trac.wiki.rbac import WikiAuthorizer\r\n \r\n class WikiModule(Component):\r\n \r\n     implements(INavigationContributor, IPermissionRequestor, IRequestHandler,\r\n                ITimelineEventProvider, ISearchSource)\r\n \r\n+    authz = property(fget=lambda self: self._get_authz())\r\n+\r\n     # INavigationContributor methods\r\n \r\n     def get_active_navigation_item(self, req):\r\n@@ -74,6 +76,7 @@\r\n         pagename = req.args.get('page', 'WikiStart')\r\n         version = req.args.get('version')\r\n \r\n+        self.authz.assert_authorization(req.perm, req.authname, pagename, 'WIKI_VIEW')\r\n         db = self.env.get_db_cnx()\r\n         page = WikiPage(self.env, pagename, version, db)\r\n \r\n@@ -118,6 +121,13 @@\r\n         req.hdf['wiki.action'] = action\r\n         req.hdf['wiki.page_name'] = page.name\r\n         req.hdf['wiki.current_href'] = self.env.href.wiki(page.name)\r\n+        for permission in self.get_permission_actions():\r\n+            if isinstance(permission, (list, tuple)):\r\n+                               permission = permission[0]\r\n+            self.log.debug("PERMISSION: %s", permission)\r\n+            req.hdf['trac.acl.' + permission] =\\\r\n+                self.authz.has_authorization(req.perm, req.authname, page.name, permission)\r\n+\r\n         return 'wiki.cs', None\r\n \r\n     # ITimelineEventProvider methods\r\n@@ -135,24 +145,29 @@\r\n                            "FROM wiki WHERE time>=%s AND time<=%s",\r\n                            (start, stop))\r\n             for t,name,comment,author in cursor:\r\n-                title = Markup('<em>%s</em> edited by %s', name, author)\r\n-                if format == 'rss':\r\n-                    href = self.env.abs_href.wiki(name)\r\n-                    comment = wiki_to_html(comment or '--', self.env, req, db,\r\n-                                           absurls=True)\r\n-                else:\r\n-                    href = self.env.href.wiki(name)\r\n-                    comment = wiki_to_oneliner(comment, self.env, db,\r\n-                                               shorten=True)\r\n-                yield 'wiki', href, title, t, author, comment\r\n+                if self.authz.has_authorization(req.perm, req.authname, name, 'WIKI_VIEW'):\r\n+                    title = Markup('<em>%s</em> edited by %s', name, author)\r\n+                    if format == 'rss':\r\n+                        href = self.env.abs_href.wiki(name)\r\n+                        comment = wiki_to_html(comment or '--', self.env, req, db,\r\n+                                               absurls=True)\r\n+                    else:\r\n+                        href = self.env.href.wiki(name)\r\n+                        comment = wiki_to_oneliner(comment, self.env, db,\r\n+                                                   shorten=True)\r\n+                    yield 'wiki', href, title, t, author, comment\r\n \r\n     # Internal methods\r\n \r\n+    def _get_authz(self):\r\n+        return WikiAuthorizer(self.env)\r\n+\r\n     def _do_delete(self, req, db, page):\r\n         if page.readonly:\r\n             req.perm.assert_permission('WIKI_ADMIN')\r\n         else:\r\n             req.perm.assert_permission('WIKI_DELETE')\r\n+            self.authz.assert_authorization(req.perm, req.authname, page.name, 'WIKI_DELETE')\r\n \r\n         if req.args.has_key('cancel'):\r\n             req.redirect(self.env.href.wiki(page.name))\r\n@@ -174,8 +189,10 @@\r\n             req.perm.assert_permission('WIKI_ADMIN')\r\n         elif not page.exists:\r\n             req.perm.assert_permission('WIKI_CREATE')\r\n+            self.authz.assert_authorization(req.perm, req.authname, page.name, 'WIKI_CREATE')\r\n         else:\r\n             req.perm.assert_permission('WIKI_MODIFY')\r\n+            self.authz.assert_authorization(req.perm, req.authname, page.name, 'WIKI_MODIFY')\r\n \r\n         page.text = req.args.get('text')\r\n         if req.perm.has_permission('WIKI_ADMIN'):\r\n@@ -192,6 +209,7 @@\r\n             req.perm.assert_permission('WIKI_ADMIN')\r\n         else:\r\n             req.perm.assert_permission('WIKI_DELETE')\r\n+            self.authz.assert_authorization(req.perm, req.authname, page.name, 'WIKI_DELETE')\r\n \r\n         version = None\r\n         if req.args.has_key('delete_version'):\r\n@@ -210,6 +228,7 @@\r\n \r\n     def _render_diff(self, req, db, page):\r\n         req.perm.assert_permission('WIKI_VIEW')\r\n+        self.authz.assert_authorization(req.perm, req.authname, page.name, 'WIKI_VIEW')\r\n \r\n         if not page.exists:\r\n             raise TracError, "Version %s of page %s does not exist" \\\r\n@@ -277,6 +296,7 @@\r\n \r\n     def _render_editor(self, req, db, page, preview=False):\r\n         req.perm.assert_permission('WIKI_MODIFY')\r\n+        self.authz.assert_authorization(req.perm, req.authname, page.name, 'WIKI_MODIFY')\r\n \r\n         if req.args.has_key('text'):\r\n             page.text = req.args.get('text')\r\n@@ -320,6 +340,7 @@\r\n         page.\r\n         """\r\n         req.perm.assert_permission('WIKI_VIEW')\r\n+        self.authz.assert_authorization(req.perm, req.authname, page.name, 'WIKI_VIEW')\r\n \r\n         if not page.exists:\r\n             raise TracError, "Page %s does not exist" % page.name\r\n@@ -344,6 +365,7 @@\r\n \r\n     def _render_view(self, req, db, page):\r\n         req.perm.assert_permission('WIKI_VIEW')\r\n+        self.authz.assert_authorization(req.perm, req.authname, page.name, 'WIKI_VIEW')\r\n \r\n         if page.name == 'WikiStart':\r\n             req.hdf['title'] = ''\r\n@@ -365,7 +387,8 @@\r\n             history_href = self.env.href.wiki(page.name, action='history')\r\n             req.hdf['wiki.history_href'] = history_href\r\n         else:\r\n-            if not req.perm.has_permission('WIKI_CREATE'):\r\n+            if not req.perm.has_permission('WIKI_CREATE') and\\\r\n+                self.authz.has_authorization(req.perm, req.authname, page.name, 'WIKI_CREATE'):\r\n                 raise TracError('Page %s not found' % page.name)\r\n             req.hdf['wiki.page_html'] = Markup('<p>Describe "%s" here</p>',\r\n                                                page.name)\r\n@@ -375,7 +398,8 @@\r\n         for attachment in Attachment.select(self.env, 'wiki', page.name, db):\r\n             attachments.append(attachment_to_hdf(self.env, db, req, attachment))\r\n         req.hdf['wiki.attachments'] = attachments\r\n-        if req.perm.has_permission('WIKI_MODIFY'):\r\n+        if req.perm.has_permission('WIKI_MODIFY') and\\\r\n+            self.authz.has_authorization(req.perm, req.authname, page.name, 'WIKI_MODIFY'):\r\n             attach_href = self.env.href.attachment('wiki', page.name)\r\n             req.hdf['wiki.attach_href'] = attach_href\r\n \r\n@@ -399,7 +423,8 @@\r\n                        "AND " + sql_query, args)\r\n \r\n         for name, date, author, text in cursor:\r\n-            yield (self.env.href.wiki(name),\r\n-                   '%s: %s' % (name, shorten_line(text)),\r\n-                   date, author,\r\n-                   shorten_result(text, query.split()))\r\n+            if self.authz.has_authorization(req.perm, req.authname, name, 'WIKI_VIEW'):\r\n+                yield (self.env.href.wiki(name),\r\n+                    '%s: %s' % (name, shorten_line(text)),\r\n+                    date, author,\r\n+                    shorten_result(text, query.split()))\r\n--- trac/wiki/rbac.py.orig_2006-02-13 15:32:45.000000000 -0800\r\n+++ trac/wiki/rbac.py_2006-02-13 15:29:34.000000000 -0800\r\n@@ -0,0 +1,82 @@\r\n+from trac.core import *\r\n+from trac.perm import PermissionError\r\n+from trac.versioncontrol.svn_authz import RealSubversionAuthorizer\r\n+from trac.versioncontrol import Authorizer, PermissionDenied\r\n+\r\n+def SubversionAuthorizer(env, authname):\r\n+_authz_file = env.config.get('wiki', 'authz_file') or\\\r\n+__env.config.get('trac', 'authz_file')\r\n+_if not authz_file:\r\n+__return Authorizer()\r\n+_\r\n+_module_name = env.config.get('wiki', 'authz_svn_module_name')\r\n+_db = env.get_db_cnx()\r\n+_return ExtendedSubversionAuthorizer(db, authname, module_name, authz_file)\r\n+\r\n+class IWikiAuthzProvider(Interface):\r\n+_"""Interface for classes that provide some method of checking a\r\n+__user's access to a portion of the wiki."""\r\n+\r\n+_def has_authorization(user, path, operation):\r\n+__"""Verify that the given username is authorized to perform\r\n+___the given operation on the given path.\r\n+___returns boolean."""\r\n+\r\n+class WikiAuthorizer(Component):\r\n+_providers = ExtensionPoint(IWikiAuthzProvider)\r\n+\r\n+_def _accumulate(self, current, result):\r\n+__authmode = self.env.config.get('wiki', 'authorization_mode')\r\n+\r\n+__if authmode == 'require_all':\r\n+___return current & result\r\n+__elif authmode == 'require_one':\r\n+___return current | result\r\n+__else:\r\n+___return True\r\n+\r\n+_def has_authorization(self, perm, user, path, operation):\r\n+__if not perm.has_permission('WIKI_VIEW'):\r\n+___return False\r\n+\r\n+__authzed = True\r\n+__for provider in self.providers:\r\n+___authzed = self._accumulate(authzed, provider.has_authorization(user, path, operation))\r\n+__return authzed or perm.has_permission('TRAC_ADMIN')\r\n+\r\n+_def assert_authorization(self, perm, user, path, operation):\r\n+__if not self.has_authorization(perm, user, path, operation):\r\n+___raise PermissionDenied,\\\r\n+____'%s authorization on %s is necessary to perform this operation.' % (operation, 'wiki:' + path)\r\n+\r\n+\r\n+class WikiSubversionAuthorizer(Component):\r\n+_implements(IWikiAuthzProvider)\r\n+\r\n+_def has_authorization(self, user, path, operation):\r\n+__path = '/' + path\r\n+__authorizer = SubversionAuthorizer(self.env, user)\r\n+__self.log.debug("Authorize %s check for: %s on %s:%s", operation, user, authorizer.module_name, path)\r\n+__return authorizer.has_authorization(path, operation)\r\n+\r\n+class ExtendedSubversionAuthorizer(RealSubversionAuthorizer):\r\n+_"""Provides extended semantics for the subversion-based authorization"""\r\n+\r\n+_opmap = {'WIKI_CREATE':'c', 'WIKI_DELETE':'d', 'WIKI_MODIFY':'w', 'WIKI_VIEW':'r', 'WIKI_ADMIN':'a'}\r\n+\r\n+_def has_authorization(self, path, op):\r\n+__self._set_opcheck('WIKI_ADMIN')\r\n+__if self.has_permission(path):\r\n+___return True\r\n+__else:\r\n+___self._set_opcheck(op)\r\n+___return self.has_permission(path)\r\n+\r\n+_def _set_opcheck(self, op):\r\n+__op = self.opmap[op]\r\n+__self._get_permission = lambda section, subject: self.__get_permission(section, subject, op)\r\n+\r\n+_def __get_permission(self, section, subject, op):\r\n+__if self.conf_authz.has_option(section, subject):\r\n+___return op in self.conf_authz.get(section, subject)\r\n+__return None\r\n}}}	defect	closed	normal	WikiRbacPatch	major	fixed			0.9
