Details

Applies to
1.5.1, 1.5.2, 1.5.3, 1.5.6, 1.5.7
Purpose

Adds hierarchical ACL support to MoinMoin

Description

Check HierarchicalAccessControlList for the orginal patch "hierachical-acl-moin--main--1.3-935.diff". This is just the same patch made appliaple for 1.5.1. Not extensively tested with 1.5.1 but seems to work. Add "acl_hierarchic = 1" to wikiconfig.py to activate.

Patch

   1 diff -ru moin-1.5.1/MoinMoin/PageEditor.py moin-1.5.1+acl/MoinMoin/PageEditor.py
   2 --- moin-1.5.1/MoinMoin/PageEditor.py	2006-01-17 11:00:22.000000000 +0200
   3 +++ moin-1.5.1+acl/MoinMoin/PageEditor.py	2006-01-27 14:34:21.000000000 +0200
   4 @@ -890,7 +890,7 @@
   5              # of wating for next request.
   6              acl = self.getACL(self.request)
   7              if (not self.request.user.may.admin(self.page_name) and
   8 -                parseACL(self.request, newtext) != acl and
   9 +                parseACL(self.request, newtext).acl != acl.acl and
  10                  action != "SAVE/REVERT"):
  11                  msg = _("You can't change ACLs on this page since you have no admin rights on it!")
  12                  raise self.NoAdmin, msg
  13 diff -ru moin-1.5.1/MoinMoin/multiconfig.py moin-1.5.1+acl/MoinMoin/multiconfig.py
  14 --- moin-1.5.1/MoinMoin/multiconfig.py	2006-01-22 12:27:04.000000000 +0200
  15 +++ moin-1.5.1+acl/MoinMoin/multiconfig.py	2006-01-27 15:04:33.000000000 +0200
  16 @@ -162,7 +162,8 @@
  17  
  18  class DefaultConfig:
  19      """ default config values """
  20 -    
  21 +
  22 +    acl_hierarchic = 0    
  23      # All acl_right lines must use unicode!
  24      acl_rights_default = u"Trusted:read,write,delete,revert Known:read,write,delete,revert All:read,write"
  25      acl_rights_before = u""
  26 @@ -461,6 +462,12 @@
  27          # check if mail is possible and set flag:
  28          self.mail_enabled = (self.mail_smarthost is not None or self.mail_sendmail is not None) and self.mail_from
  29  
  30 +        # precompile acl strings
  31 +        from wikiacl import AccessControlList
  32 +        self.acl_default = AccessControlList(None, [self.acl_rights_default], cfg=self)
  33 +        self.acl_before = AccessControlList(None, [self.acl_rights_before], cfg=self)
  34 +        self.acl_after = AccessControlList(None, [self.acl_rights_after], cfg=self)
  35 +
  36      def _config_check(self):
  37          """ Check namespace and warn about unknown names
  38          
  39 diff -ru moin-1.5.1/MoinMoin/security.py moin-1.5.1+acl/MoinMoin/security.py
  40 --- moin-1.5.1/MoinMoin/security.py	2005-09-22 19:22:09.000000000 +0300
  41 +++ moin-1.5.1+acl/MoinMoin/security.py	2006-01-27 14:34:21.000000000 +0200
  42 @@ -22,8 +22,21 @@
  43  class Permissions:
  44      """ Basic interface for user permissions and system policy.
  45  
  46 -        Note that you still need to allow some of the related actions, this
  47 -        just controls their behaviour, not their activation.
  48 +    Note that you still need to allow some of the related actions, this
  49 +    just controls their behavior, not their activation.
  50 +
  51 +    When sub classing this class, you must extend the class methods, not
  52 +    replace them, or you might break the acl in the wiki. Correct sub
  53 +    classing look like this:
  54 +
  55 +    def read(self, pagename):
  56 +        # Your special security rule
  57 +        if somehting:
  58 +            return false
  59 +
  60 +        # Do not return True or you break acl!
  61 +        # This call will use the default acl rules
  62 +        return Permissions.read(pagename)
  63      """
  64  
  65      def __init__(self, user):
  66 @@ -32,27 +45,105 @@
  67          from MoinMoin.Page import Page
  68          self.Page = Page
  69          self.name = user.name
  70 +        self.user = user
  71          self.request = user._request
  72  
  73      def save(self, editor, newtext, rev, **kw):
  74          """ Check whether user may save a page.
  75  
  76 -            `editor` is the PageEditor instance, the other arguments are
  77 -            those of the `PageEditor.saveText` method.
  78 +        Default implementation ignore newtext, rev and kw, and just try
  79 +        to get permission for write.
  80 +
  81 +        @param editor: PageEditor instance.
  82 +        @param newtext: new page text, you can enable of disable saving according
  83 +            to the content of the text, e.g. prevent link spam.
  84 +        @param rev: new revision number? XXX
  85 +        @param kw: XXX
  86 +        @rtype: bool
  87 +        @return: True if you can save or False
  88          """
  89          return self.write(editor.page_name)
  90  
  91      def __getattr__(self, attr):
  92 -        """ if attr is one of the rights in acl_rights_valid, then return a
  93 -            checking function for it. Else raise an error.
  94 +        """ Shortcut to export getPermission function for all known acl rights
  95 +        
  96 +        if attr is one of the rights in acl_rights_valid, then return a
  97 +        checking function for it. Else use normal getattr().
  98 +
  99 +        @param attr: one of acl known rights as defined in acl_rights_valid
 100 +        @rtype: function
 101 +        @return: checking function for that right, accepting a pagename
 102          """
 103 -        request = self.request
 104 -        Page = self.Page
 105 -        if attr in request.cfg.acl_rights_valid:
 106 -            return lambda pagename, Page=Page, request=request, attr=attr: Page(request, pagename).getACL(request).may(request, self.name, attr)
 107 +        if attr in self.request.cfg.acl_rights_valid:
 108 +            self._right = attr
 109 +            return self._getPermission
 110          else:
 111              raise AttributeError, attr
 112          
 113 +    def _getPermission(self, pagename):
 114 +        """ Get permission by transversing page hierarchy
 115 + 
 116 +        If cfg.acl_hierarchic, we check each page in the hierarchy. If all
 117 +        pages agree, we have permission. If one page in the hierarchy
 118 +        does not agree, we don't. Otherwise, only pagename is checked.
 119 +
 120 +        This method should not be called by users, use __getattr__
 121 +        instead. If you want to get permission for delete, try:
 122 +        instance.delete(pagename).
 123 +
 124 +        @param pagename: pagename to get permission from
 125 +        @rtype: bool
 126 +        @return: True if you have permission or False
 127 +        """
 128 +        from MoinMoin.Page import Page
 129 +
 130 +        # Use page hierarchy or only pagename, according to wiki config.
 131 +        if self.request.cfg.acl_hierarchic:
 132 +            pages = pagename.split('/')
 133 +            #open('/tmp/acl.log','a').write('acl_hierarchic enabled\n')
 134 +        else:
 135 +            #open('/tmp/acl.log','a').write('acl_hierarchic disabled\n')
 136 +            pages = [pagename]
 137 +
 138 +        # disable logging seems not bugfree
 139 +        #open('/tmp/acl.log','a').write('may(%s): pages %s\n'%(self._right, pages,))
 140 +        # check before
 141 +        allowed = self.request.cfg.acl_before.may(self.request, self.user.name, self._right)
 142 +        if allowed is not None:
 143 +            #open('/tmp/acl.log','a').write('may: before returns %s\n'%(allowed,))
 144 +            return allowed
 145 +        
 146 +        # Get permission
 147 +        some_acl = False
 148 +        for i in range(len(pages), 0, -1):
 149 +            # Create the next pagename in the hierarchy
 150 +            # starting with the leave, going to the root
 151 +            name = '/'.join(pages[:i])
 152 +            # Get page acl and ask for permission
 153 +            acl = Page(self.request, name).getACL(self.request)
 154 +            #open('/tmp/acl.log','a').write('may: checking acl of %s (%s)\n'%(name, `acl.acl`))
 155 +            if acl.acl:
 156 +                some_acl = True
 157 +                allowed = acl.may(self.request, self.user.name, self._right)
 158 +                if allowed is not None:
 159 +                    #open('/tmp/acl.log','a').write('may: acl of %s (%s) returns %s\n'%(name, `acl.acl`, allowed,))
 160 +                    return allowed
 161 +        if not some_acl:
 162 +            allowed = self.request.cfg.acl_default.may(self.request, self.user.name, self._right)
 163 +            if allowed is not None:
 164 +                #open('/tmp/acl.log','a').write('may: default returns %s\n'%(allowed,))
 165 +                return allowed
 166 +
 167 +        # check after
 168 +        allowed = self.request.cfg.acl_after.may(self.request, self.user.name, self._right)
 169 +        if allowed is not None:
 170 +            #open('/tmp/acl.log','a').write('may: after returns %s\n'%(allowed,))
 171 +            return allowed
 172 +
 173 +        # sane default permission, if you want True,
 174 +        # just put 'All:read' (or more) into your acl_rights_after
 175 +        return False
 176 +
 177  
 178  # make an alias for the default policy
 179  Default = Permissions
 180 diff -ru moin-1.5.1/MoinMoin/wikiacl.py moin-1.5.1+acl/MoinMoin/wikiacl.py
 181 --- moin-1.5.1/MoinMoin/wikiacl.py	2005-09-25 15:47:06.000000000 +0300
 182 +++ moin-1.5.1+acl/MoinMoin/wikiacl.py	2006-01-27 14:34:21.000000000 +0200
 183 @@ -109,32 +109,26 @@
 184  
 185      special_users = ["All", "Known", "Trusted"] # order is important
 186  
 187 -    def __init__(self, request, lines=[]):
 188 +    def __init__(self, request, lines=[], cfg=None):
 189          """Initialize an ACL, starting from <nothing>.
 190          """
 191 -        self.setLines(request.cfg, lines)
 192 +        if cfg is not None:
 193 +            self.setLines(cfg, lines)
 194 +        else:
 195 +            self.setLines(request.cfg, lines)
 196  
 197      def setLines(self, cfg, lines=[]):
 198          self.clean()
 199 -        self.addBefore(cfg)
 200 -        if not lines:
 201 -            self.addDefault(cfg)
 202 -        else:
 203 -            for line in lines:
 204 -                self.addLine(cfg, line)
 205 -        self.addAfter(cfg)
 206 +        for line in lines:
 207 +            self.addLine(cfg, line)
 208  
 209      def clean(self):
 210          self.acl = [] # [ ('User', {"read": 0, ...}), ... ]
 211          self.acl_lines = []
 212          self._is_group = {}
 213  
 214 -    def addBefore(self, cfg):
 215 -        self.addLine(cfg, cfg.acl_rights_before, remember=0)
 216      def addDefault(self, cfg):
 217          self.addLine(cfg, cfg.acl_rights_default, remember=0)
 218 -    def addAfter(self, cfg):
 219 -        self.addLine(cfg, cfg.acl_rights_after, remember=0)
 220  
 221      def addLine(self, cfg, aclstring, remember=1):
 222          """ Add another ACL line
 223 @@ -177,7 +171,7 @@
 224                          rightsdict[right] = (right in rights)
 225                  self.acl.append((entry, rightsdict))
 226  
 227 -    def may(self, request, name, dowhat):
 228 +    def may(self, request, name, dowhat, recurse=False):
 229          """May <name> <dowhat>?
 230             Returns boolean answer.
 231          """
 232 @@ -202,7 +196,7 @@
 233                  allowed = rightsdict.get(dowhat)
 234              if allowed is not None:
 235                  return allowed
 236 -        return 0
 237 +        return allowed
 238  
 239      def getString(self, b='#acl ', e='\n'):
 240          """print the acl strings we were fed with"""
hierachical_acl_modified_for_moin_1.5.1.patch

Discussion

Plan


CategoryMoinMoinPatch

MoinMoin: MoinMoinPatch/HierarchicalACL (last edited 2007-10-29 19:13:49 by localhost)