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"""
Discussion
- Fix typos in comments / src
- either remove debug logging or clean it up
Plan
- Priority:
- Assigned to:
- Status: