Applying access control lists using wiki hierarchy
How does AccessControlList class work currently?
ACL objects are made from ACL lines in config and page. First acl_rights_before line is copied, then page lines are copied from page, or acl_rights_default if page does not have ACL lines. Last, acl_rights_after line is copied. Then, ACL object is created from the lines. This object contains a list of rights dictionaries and is created for each page and cached.
When accessing a page, user.may method is called, which retrieves the acl object from the page and checks for rights.
Patch Index
Patch |
Author/Email |
ACL Type |
Applicable MoinMoin releases |
Comments/Issues |
?? |
Traditional |
?? |
|
|
Traditional |
patch level 666 (Debian) |
|
||
?? |
1.3 patch 935 |
|
||
pc2-206? |
Traditional |
1.5.1, 1.5.2, 1.5.3 |
port of hierachical-acl-moin--main--1.3-935.diff to 1.5.1 |
|
inherit at page creation? |
1.3 patch 556 |
|
Problems
acl_rights_before, acl_rights_default and acl_rights_after are duplicated for every page
Worst case example: in a 1000 pages wiki with no acls, we copy acl_before, acl_default and acl_after to any page acl, then parse the same lines 1000 times, and finally cache 1000 equal acl instances.
With hierarchical acl, we would parse acl_rights_before, acl_rights_after and acl_rights_default once and keep them in memory, then cache None for every other page in the acl cache.
- This is independent from hierachical ACLs, isn't it? So we should think about implementing it as fast as possible in order to gain speed.
Using ACLs is too much work
You must add acl lines to any page you want to protect with different acl than the default acls. For example, if you would like to create some few pages for one project, you will have to add acl lines to each new page. If you want to change the project acl rights, you have to do it again for all the pages of the project.
One example is the acl rights of the help and system pages. To change these we use a script to edit all those pages text outside the wiki.
Testcases
Please add some test cases and write how your suggestion would catch these.
- There are project groups that need their own page hierarchies. Every project group wants to protect some pages to be read just by the particular project group. Others should just be open to other projects. The remaining ones should stay public.
How hierarchial ACLs should work
before -> default -> parent page -> sub page -> after -> No!
Changes:
- acl_rights_xx are separated from the page acl object. Each acl_rights_xx is parsed and saved in the wiki config, then it can be checked separately.
- acl_rights_default is treated as the wiki virtual root page acl, which all pages inherit from. If a page does not have acl, it inherits from its parent, if the parent does not have acl, it inherits from acl_rights_default.
- replace that lambda in security module with a real function
- make that fuction avoid loading page acl if not needed
Flow of acl checking:
- acl_rights_before checked, so the admin can define rights nobody can override.
- acl_rights_default, pages and sub pages are checked - the order depending on the implementation
- acl_rights_after is checked, so we can add default rights to all pages.
- Last, if no acl matches, we deny access.
Benefits
- Less work to define ACLs, define only some few ACLs for the whole wiki using sub pages.
Needs very small cache which can be loaded from disk very fast - see MoinCaching.
Performance Issues
- In the best case when acl_rights_before matches, we don't have to get the ACL from the page/s, which means preventing expensive disk access.
- This is rather rarely the case. before is often used for the wiki admin only.
In the worst case we have to check more than one page acl, which means multiple expensive disk accesses. We can solve this by better acl caching, keeping acl dict in memory in long running process, or loading it from a pickle for cgi - see MoinCaching.
Changes to current code
The current AccessControlList.may checks a composite acl object, made from all acl_rights* and page rights, and returns True or False. The new .may function will check a single acl object, and return 3 answers: True, False or None, which means "don't know". The basic acl check looks like this:
The complete acl checking will move from AccessControlList.may to the SecurityPolicy.xxx, e.g. SecurityPolicy.read
Checking the page hierarchy can be done in two ways: Posix file system like, or traditional acls.
Posix file system like hierarchy
Moved this content under Reference section, below.
Traditional ACL hierarchy
Moved this content under Reference section, below.
Reference
NTFS ACLs
In NTFS' ACLs, it works like this:
- Each object has an ACL.
- Each object has a virtual flag "inherited_acl".
- Each object can contain its own ACL.
- Each entry of the ACL might be allow or revoke.
- If it is inherited, every change of the parent ACL is inherited.
- The ACL of the object may enhance or narrow down the inherited ACL. This is possible because there is allow and revoke.
Posix ACLs
(This was already above, moved here...)
This should work like file system permissions. Say there are the following pages, TopSecret and !TopSecret/!PasswordFiles . If the user "protects" TopSecret by removing read right to everyone but himself, he would naturally expect everything below TopSecret to be protected.
For example, for the path a/b/c:
- check acl_rights_before
- check acl_rights_default
- check a
- check a/b
- check a/b/c
- check acl_rights_after
- return False
Pro:
- Familiar and simple concept, less error prone.
Contra:
- You can't make e.g. a subpage less restrictive than its upper pages.
Traditional ACL hierarchy
(Moved from above)
Check each page acl, starting with the longest path, so each page can override the parent.
For example, for the path a/b/c:
- check acl_rights_before
- check a/b/c
- check a/b
- check a
- check acl_rights_default
- check acl_rights_after
- return False
Pro:
- More power, you can define any acl to any page, ignoring the parent acl.
Here is a patch for this solution: hierachical-acl.diff
Notes:
- We don't need to check for some_acl, simply check acl_rights_default after the page hierarchy, in case no page acl matched.
- ??? what does this mean ???
Does every "Patch" section below refer to the same patch? And is this the patch that implements "Traditional ACL Hierarchy"? This would be my best guess, but it's not very clear. Suggest placing everything related to a patch on a subpage, with a link on this page clearly explaining what the patch is. Otherwise, the topic nesting on a single page becomes unmanageable. -- SteveDavison 2007-02-19 10:38:19
Patch update
I was searching for solution to allow my users creating new pages and added this patch. The current version for Debian uses patch level 666 of MoinMoinWiki. So here the corresponding patch: hierachical-acl-666.diff
But after applying this patch i found a bug in wikiacl.py (addDefault is not available anymore):
154 if entries == ['Default']: 155 self.addDefault(cfg) 156 continue
My simple fix at the moment is to disable these lines. Maybe somebody can help here. I have no overview over the system classes yet. -- StephanZehrer 2005-06-12 13:11:59
The original patch is wrong, addDefault is needed. Without it, acl lines like
#acl UserName:read,write Default
Patch update against moin--main--1.3 patch-935
I diffed the hierachical-acl patch one more time against current stable repository after fixing the addDefault problem pointed out above. I believe hierachical-acl is a much better model than the default one so I recommend inclusion in mainline (at least in the development tree). For example this automatically prevent spam blogging with the example in the Include macro. It works fine for me. hierachical-acl-moin--main--1.3-935.diff
Patch update against 1.5.1
Check wiki:MoinMoinPatch/HierarchicalACL.
Inherit parent pages acl lines
Here when a page acl is created, it copies all lines from parent pages, so the total acl lines per page are acl_before + parents lines + page lines + acl_after
Updated patch to moin--main--1.3--patch-556
But I think the solution above which splits the acls and and does not need all this cache-mangeling here would be the better way of doing it. -- OliverGraf
Before potential inclusion ...
... tne:
- Performance testing
- We currently have performance problems for some operations like pageList, because they check ACL for every case before showing it to the user. It has to be checked if this does not get even worse with this patch.
- Same for some other usual operations.
- Migration strategy
- Should we enable it by default and in turn migrate existing wikis which already use ACLs? By default the hierarchial patch leaves the feature disabled, so there is no risk of breakages, it has to be explicitly enabled in the config file with acl_hierarchic = 1.
- Determine right time to include this patch. It doesn't look like 1.5 is the right time as it is short before release and in feature freeze. After 1.5 we will work on 2.0 and have lots of time (and work).
- 2.0 will move ACLs to metadata and generalize their usage over all items, so there will be changes in this area anyway.
I would merge it into 1.5 now, we are still in the beta phase. 2.0 is too far away and it is a highly demanded feature. -- AlexanderSchremmer 2005-11-22 12:43:47
- I agree with Alexander having an option to get something better sounds quite urgent. I would like to have a acl = 'posix' too, but the current acl_hierarchic = 1 is good enough for most usages and I guess it performs better than posix avoiding to walk more than one parent. So after all it might be more pratical. Having all three modes would be the best but we can live with two.
- You are too late, see the 1.5 release time table.
Come on, this is an open source project isn't it? Deadlines don't need to be perfectly respected if there's a good enough technical reason for it. To me the patch looks small enough, we're not asking for posix acl to go in, or to enable it by default of course.
Furthermore, the dead lines will be held active even if the tests still do not pass. I do not like that. -- AlexanderSchremmer 2005-11-29 16:01:44
- Are we going to include this in the 1.6 release? --Counterpoke
- Is someone still working on this feature?
Yeah, It is one of the new features of the upcoming 1.6 release. -- ReimarBauer 2007-06-09 23:10:49
Are details of the planned implementation written up somewhere? This page contains so many ideas, but I don't see any clear indication of what came out the winner. I'd like to be able to understand what's planned and be able to comment. All the other ideas should be moved to a subpage as "other ideas" or "rejected ideas" so this page is not so cluttered and confusing. -- SteveDavison 2007-06-10 01:31:26
- Thanks for the link. After reading it, I'd like to say...
- The section on hierarchical does not do an adequate job of explaining how it functions. Just enough to get a ponderous look, then a nod from anyone who already knows how it works (or has thought about it alot), and still leaves out some important information. I'll try to do something about that.
- But, first I want to be sure I really do understand the implementation. Here's my take:
With acl_hierarchic disabled, the sequence acl_before + (#acl or acl_default) + acl_after (where #acl means whatever is contained in the page's #acl directive)
with acl_hierarchic enabled, the (#acl or acl_default) is then replaced by (#acl + parent ACL). The absence of #acl on a page does not cause acl_default to be used instead. It simply means that processing moves directly to the parent's ACL
The "parent ACL" term is recursive. It is defined as the part of the parent page's ACL that lies between the acl_before and the acl_after; namely, (#acl + parent ACL) from the perspective of the parent page.
An example: There is a page A/B/C/D. Page A/B and A/B/C contain a #acl line, but the others don't. The ACL sequence for page A/B/C/D is as follows: acl_before, C:#acl, B:#acl, (acl_default?), acl_after.
An important detail: in order for hierarchical ACL to fulfill its most basic purpose, the application of the acl_default settings must be altered. This is not covered in the new page. If child pages with no #acl were to apply Default before processing the parent ACL, then there would be no way for the parent to override Default for its children; this would negate the most obvious purpose of the feature. Therefore, Default must be applied not on a per-page basis, but within the overall hierarchic scheme. The question is... When and where exactly should Default be applied? There are several options:
Only apply Default if none of the pages in the hierarchic chain contained any #acl lines, then it would be the only thing between acl_before and acl_after
- Follow the rules exactly as they were for non-hierarchical ACL (apply Defaults on a page-by-page basis, for any page in the chain that has no #acl)
- Only substitute Default for the top-level page if it contains no #acl line
- Always apply the Default once, directly before acl_after
- Only option (b) is completely unreasonable. (a) means that any #acl in the chain throws out Default, which is workable, and most compatible with non-hierarchical behavior. (c) is a bit quirky, making an exception of the top page that doesn't make much sense. (d) probably has the simplest implementation, and is the most logically pure. It would be as if there were a single (imaginary) root page holding all the non-sub-pages, with an #acl line containing an exact copy of the acl_default setting. However, it does break the original spirit of Default, in that it would interject Default before acl_after, even though there were page ACLs.
I think there's a need for a new directive, #subacl. The idea here is that you may want to specify an ACL that applies to all subpages, but this is not necessarily the same as what you want for the parent! I can offer two example use cases, but I'm sure there are plenty of others:
- You want to provide a top-level page that gives some instructions, and may contain a listing of all subpages (as links), and allow the user to create subpages. However, you don't want users editing the parent page.
- You might have a section that can only be read by a certain group of users, but you still want the "gateway" (parent) page that anyone can access, to explain what the subsection is about and who to contact if you need access.
While working the last days with 30 different acl groups I got some ideas how I would prefer that feature solved.
The most difference of this idea is to have the rights of the page visible written on each subpage.
To get that idea working we have to answer the following questions:
Why didnt we just write if hacl in wikiconfig is enabled the rights of the upper page to the new lower page?
I think that could be solved by:
- getting from acl_rights_before the user who is able to create the acl line of the page and
- splitting the new page creation internally in two actions if hacl is enabled.
- first we check if the one who like to create a lower page has write rights on the upper one.
- with saving we create the page with the acl line from the upper page changing temporary to the first user in acl_rights_before with admin rights
- the content of the page is saved as the user who entered the text
so hacl enabled requires at least one user set in acl_rights_before with admin rights.
What should we do on an existing wiki where one wants to enter now hierarchical acls and has some pages with rights already defined?
if one changes to hierarical acls on an exising wiki I think he needs to call a currently non existing moin maint subcommand to setup the acl rights of the hierarchy on the pages. A lower page with no rights defined gets the rights of the upper page added
I tried to find answers to the following parts related to moving pages.
- What should happen to a lower page if one with admin rights changes the rights of an upper page?
the acl line needs to be changed on every sub page with the same old rights too (by exchanging it to the new one)
- What should happen if a page is moved to start a new hierarchy to main level?
its just moved
- What should happen if a page with no rights is moved into a hierarchy?
it gets by saving the rights of the upper page added
- What should happen if a page with acls is moved into the hierarchy?
it needs to be checked if the rights of the page are included in the rights of the upper page:
If no then moving is rejected with a hint, not to have compatible acl rights and rights needs to be changed
if yes it is moved. if the acl rights are different to the upper one it starts a new hierarchy. If it has subpages which have had old rights they have to get the new rights
- What should happen if a page with acls is moved on the last level of the hierarchy?
it needs to be checked for write rights on the upper page:
If no then moving is rejected with a hint, not to have compatible acl rights and rights needs to be changed
if yes it is moved. if the acl rights are different to the upper one it starts a new hierarchy.
If later on someone disables in wikiconfig hacl the acl rights are on the pages stay defined. Only new lower pages dont get acl rights added. And if one spreads pages into other wikis the acls on the pages are defined too.
I do prefer this solution because you always see rights written on pages and this makes it in my eyes more comfortable to work with acls.
-- ReimarBauer 2007-10-15 07:05:09