Size: 6095
Comment:
|
Size: 5159
Comment: refactor
|
Deletions are marked like this. | Additions are marked like this. |
Line 5: | Line 5: |
Acl objects are made from acl lines in config and page. First acl_before line is copied, then page lines are copied from page, or acl_default if page has not acl lines. Last, acl_after line is copied. Then, acl object is created from the lines. This object contain a list of right dictionaries. This object is created for each page and cached. | Acl objects are made from acl lines in config and page. First acl_before line is copied, then page lines are copied from page, or acl_default if page does not have acl lines. Last, acl_after line is copied. Then, acl object is created from the lines. This object contain a list of right dictionaries. This object is created for each page and cached. |
Line 11: | Line 11: |
* acl_before and acl_after are duplicated for every page | === acl_before, acl_default and acl_after are duplicated for every page === |
Line 13: | Line 13: |
= How should hierarchial acl work = | Worst case example: in a 1000 pags 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 once acl_before, acl_after and acl_default, and keep them in memory, then cache None for every other page in the acl cache. === Too much work to use === You must add acl lines to any page you want to protect with different acl then the default acls. For example, if you would like to create 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 on all the pages of the projects. An example is the acl rights of the help and system pages. To change these we use a script the edit all those pages text outside the wiki. = How hierarchial acl should work = {{{ before -> default -> parent page -> sub page -> after -> No! }}} Changes: * acl_rights_xx are splited from the page acl. Each acl_right_xx is parsed and saved in the wiki config, then can be checked separately. * replace that lambda in security with real function * make that fuction avoid loading page acl if that not needed Flow of acl checking: 1. acl_rights_before checked, so the admin can define rights nobody can override. 1. All pages inherit the wiki acl rights, whats called now acl_rights_default. We can see this as the acl of the virtual parent page of all the wiki pages. 1. Each page inherit its parent acl 1. Each page may override its parent acl - depending on the implementation 1. acl_rights_after is checked, so we can add default rights to all pages. 1. Last, if no acl match, we deny the access. == Benefits == * Less work to define acl, define only few acls for 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 match, we don't have to get acl from the page/s, which means preventing expensive disk access. * 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. == Changes to current code == The current `AccessControlList.may` check a composite acl object, made from all acl_rights and page rights, and return 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 look like this: {{{#!python allowed = acl.may(request, name, what) if allowed is not None: 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 == |
Line 17: | Line 71: |
For example, for the path a/b/c: 1. check acl_rights_before 1. check acl_rights_default 1. check a 1. check a/b 1. check a/b/c 1. check acl_rights_after 1. return False |
|
Line 18: | Line 82: |
* Define only one acl line for whole tree of pages | |
Line 21: | Line 84: |
* Cheap to implement - if a user is not allowed by outer parts of the tree, there is not need to check inner parts. * Need very small cache, keep in memory for faster access, faster to same/load from disk in cgi. |
|
Line 24: | Line 85: |
Problems: * You can't add more right in the hierarchy, either move page to higher less protected area, or give use more rights in higher parts of the tree. |
== Traditional ACL hierarchy == |
Line 27: | Line 87: |
= Implementations = | Check each page acl, starting with the longest path, so each page can override the parent. |
Line 29: | Line 89: |
== Check parent then children == | For example, for the path a/b/c: |
Line 31: | Line 91: |
user.may is patched to to hierarchial check instead of single page check. Each page in the hierarchy is checked, if one of them does not give the user the requested right, it stops. If all page agree to give requested right, you have the right. [http://nirs.dyndns.org/nirs@freeshell.org--2004/moin/moin--fix/moin--fix--1.3/patch-124/ patch-124] Test: http://nirs.dyndns.org/fix/ParentPage === Problems === This will is not entirely the effect I suspect a inherited acl to work. Goal is that a user that has no rights in a wiki, but for one page, can create/edit/delete subpages of that page. Checking all pages in the path until none says no does not work, cause the non-existing last page will always say no... but its the right way, I think. acl_rights_after is likely to block all access in a restricted wiki and so it will be impossible to add more rights on a lower tier or checking. It does not make much sense to check acl_before and acl_after multiple times in a hierarchial check, but they are currently embedded in the page acl. The solution is to remove them from page acl, and use only page lines in page acl. acl_before and acl_after may be global acl objects we can query once before and after an hierarchial check. acl_before can be renamed to wiki_acl, this make more sense when we talk about hierarchy. I don't see why we need acl_after. Is there a configuration that can not be done without it? If a page has acl lines, acl_default does not come into play. So if there is no All: rule in the page acl, but you want one for all your pages, acl_after is needed. -- OliverGraf [[DateTime(2005-01-18T08:15:05Z)]] This splits and precompiles before, after and default acls: attachment:split-before-after.diff Now the may routine has to get recursive over the page parents... -- OliverGraf [[DateTime(2005-01-18T08:58:32Z)]] So, these are the fruits of my work: attachment:hierachical-acl.diff It splits before, after and default as before and checks the parent if acl_inherit_parent is true. Still needs some tests. -- OliverGraf [[DateTime(2005-01-18T09:51:36Z)]] Ahhh, stop. The diff has still a problem with edits and acls. Better version follows soon, I hope... -- OliverGraf [[DateTime(2005-01-18T09:59:03Z)]] Cornerpoints: * separate standard acls from page acl (see split-before-after patch above) * replace that lambda in security with real function (like in nirs fix branch) * make that fuction avoid loading page acl if that not needed * if no page in path has an acl, check acl_rights_default Flow of checking in security should be: 1. check acl_before, return if not None 1. load page acl for page path, beginning with the longest path 1. if there is a page acl, remember this 1. check page acl, return if not None * How many acl lines are checked for /a/b/c if c has one? 1. if there where no page acls at all, check acl_default. return if not None 1. check acl_after. return if not None |
1. check acl_rights_before 1. check a/b/c 1. check a/b 1. check a 1. check acl_rights_default 1. check acl_rights_after |
Line 76: | Line 99: |
A patch that implements this is ready (in my working copy), but I still need to write some tests for it. -- OliverGraf [[DateTime(2005-01-18T15:13:06Z)]] | Benefits: * More power, you can define any acl to any page, ignoring the parent acl = Implementation = |
Line 78: | Line 104: |
The patch was updated. !PageEditor has to check admin rights for changed acl by calling user.may, as any other thing must do. /!\ There is still debug output (opens + writes) inside to debug the thing! -- OliverGraf [[DateTime(2005-01-19T19:06:44Z)]] | attachment:hierachical-acl.diff |
Line 80: | Line 106: |
== Inherit parent pages acl lines == | = Reference = |
Line 82: | Line 108: |
Here when page acl is created, it copies all lines from parent pages, so the total lines page lines 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 [[DateTime(2005-01-18T08:15:05Z)]] === Problems === This will increase even more the acl cache, when parent lines are duplicated into all children in a tree. Kind of "flattening" acl hierarchy. == Concepts == |
== NTFS ACL == |
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_before line is copied, then page lines are copied from page, or acl_default if page does not have acl lines. Last, acl_after line is copied. Then, acl object is created from the lines. This object contain a list of right dictionaries. This object is created for each page and cached.
When accessing pages, user may mehtod is called, which retrive the acl object from the page and check for rights.
problems
=== acl_before, acl_default and acl_after are duplicated for every page ===
Worst case example: in a 1000 pags 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 once acl_before, acl_after and acl_default, and keep them in memory, then cache None for every other page in the acl cache.
Too much work to use
You must add acl lines to any page you want to protect with different acl then the default acls. For example, if you would like to create 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 on all the pages of the projects.
An example is the acl rights of the help and system pages. To change these we use a script the edit all those pages text outside the wiki.
How hierarchial acl should work
before -> default -> parent page -> sub page -> after -> No!
Changes:
- acl_rights_xx are splited from the page acl. Each acl_right_xx is parsed and saved in the wiki config, then can be checked separately.
- replace that lambda in security with real function
- make that fuction avoid loading page acl if that not needed
Flow of acl checking:
- acl_rights_before checked, so the admin can define rights nobody can override.
- All pages inherit the wiki acl rights, whats called now acl_rights_default. We can see this as the acl of the virtual parent page of all the wiki pages.
- Each page inherit its parent acl
- Each page may override its parent acl - depending on the implementation
- acl_rights_after is checked, so we can add default rights to all pages.
- Last, if no acl match, we deny the access.
Benefits
- Less work to define acl, define only few acls for 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 match, we don't have to get acl from the page/s, which means preventing expensive disk access.
- 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.
Changes to current code
The current AccessControlList.may check a composite acl object, made from all acl_rights and page rights, and return 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 look 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
The 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, it would naturally expect everything bellow TopSecret to be procected.
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
Benefits:
- Can't give more rights to a part of a tree, you can only add protection
- Familiar and simple concept, less error prone
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
Benefits:
- More power, you can define any acl to any page, ignoring the parent acl
Implementation
attachment:hierachical-acl.diff
Reference
NTFS ACL
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 done the inherited ACL. This is possible because there is allow and revoke.