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.
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 hierachies. 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.
- How can it be better cached than now?
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:
1 allowed = acl.may(request, name, what)
2 if allowed is not None:
3 return allowed
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
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
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: attachment: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 ???
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: attachment: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 DateTime(2005-06-12T13:11:59Z)
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. attachment:hierachical-acl-moin--main--1.3-935.diff
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
attachment:inherit-parent-acl.diff
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
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
Maybe someone familiar with them can add how they work?