Modify

Opened 9 years ago

Closed 9 years ago

#221 closed defect (fixed)

Patch for 0.9.3

Reported by: heinlein@… Owned by: puffy
Priority: normal Component: WikiRbacPatch
Severity: major Keywords:
Cc: Trac Release: 0.9

Description

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.

The biggest change from 0.9.2 is that 0.9.3 no longer uses the escape() routine, opting instead for one called Markup().

The 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.

Anyway, 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."

Thanks for all your work coding this up. We needed just this sort of access control on our intranet wiki.

--Paul

--- 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('<em>%s</em> 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('<em>%s</em> 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('<p>Describe "%s" here</p>',
                                                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

Attachments (1)

rbac-0.9.3.patch.txt (10.1 KB) - added by anonymous 9 years ago.
Patch for 0.9.3 or 0.9.4

Download all attachments as: .zip

Change History (6)

comment:1 Changed 9 years ago by mark@…

  • Trac Release set to 0.9
  • Type changed from enhancement to defect

Hi Paul,

please attach your patch to this ticket. The inline one has its indentation broken.

Thanks,

Mark

Changed 9 years ago by anonymous

Patch for 0.9.3 or 0.9.4

comment:2 Changed 9 years ago by heinlein@…

The patch added a couple minutes ago by "anonymous" is mine. I forgot to enter my address before attaching the file.

Paul

comment:3 Changed 9 years ago by mark@…

  • Resolution set to duplicate
  • Status changed from new to closed

I have just noticed there is no need for newer patches, as this functionality is already implemented: http://projects.edgewall.com/trac/wiki/FineGrainedPermissions

I believe puffy should mention that on WikiRbacPatch's-site.

Anyway, thanks for th updated patch!

comment:4 Changed 9 years ago by coderanger

  • Resolution duplicate deleted
  • Status changed from closed to reopened

Trac's fine-grained permissions system has nothing to do with the wiki, it is for the repository browser only.

comment:5 Changed 9 years ago by kempf@…

  • Resolution set to fixed
  • Status changed from reopened to closed

fixed in [634]

Add Comment

Modify Ticket

Action
as closed The owner will remain puffy.
The resolution will be deleted. Next status will be 'reopened'.
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.