Hi folks,
I decided to do an analysis of how exactly how permissions are checked so we might be able
to brainstorm about where there is low hanging fruit in the optimization field.
0. Check that the user is authenticated
1. If user is attempting write operation on read-only wiki, return false.
2. If user is a guest, get XWikiPreferences for the wiki and do 4 checks on the
XWikiPreferences object to see if the action requires authentication.
3. If action is "delete", get current document and check if current user
created that document.
4. Determine if user is superadmin
5. Load DocumentReferenceResolver from ComponentManager and normalize username then
compare to superadmin name.
6. Check for admin status on main wiki
7. Get XWikiPreferences document for main wiki
8. scan for rights objects which allow the user "admin" permission, return
if found
9. get list of all groups which user is a member of, repeat check for each, returning
if found
10. If checking permission for "programming" then repeat from step 7 with
"programming" as permission.
11. If the permission being checked is programming, return the result even if false.
12. If in a subwiki, get the XWikiServer<subwiki> document and return if the current
user is the owner.
13. If checking for register permission then get the XWikiPreferences document for the
wiki.
14. scan for rights objects which allow the user, return if found
15. get list of all groups which user is a member of, repeat check for each, returning
if found
16. if no match is found, check for deny rights on the user, return if found
17. get list of all groups which user is a member of, repeat check for each,
returning if found
18. Get XWikiPreferences from wiki
19. scan for rights objects which allow the user "admin" permission, return if
found.
20. get list of all groups which user is a member of, repeat check for each, returning
if found
21. Get WebPreferences for space where document to check permissions resides.
22. scan for rights objects which allow the user "admin" permission, return if
found.
23. get list of all groups which user is a member of, repeat check for each, returning
if found
24. if the WebPreferences document specifies another Space as it's
"parent" then change to that space and return to step 21.
25. Get the document to check permission on
26. scan for rights objects which deny the user, return if found
27. get list of all groups which user is a member of, repeat check for each, returning
if found
28. scan for rights objects which allow the user, return if found
29. get list of all groups which user is a member of, repeat check for each, returning
if found
30. Get the WebPreferences document for the space where document to check permission on
resides.
31. scan for rights objects which deny the user, return if found
32. get list of all groups which user is a member of, repeat check for each, returning
if found.
33. scan for rights objects which allow the user, return if found
34. get list of all groups which user is a member of, repeat check for each, returning
if found.
35. Check if the WebPreferences document specifies a "parent space" if so,
change to that space and go to step 30.
36. Get the XWikiPreferences document for the wiki
37. scan for rights objects which deny the user, return if found
38. get list of all groups which user is a member of, repeat check for each, returning
if found
39. scan for rights objects which allow the user, return if found
40. get list of all groups which user is a member of, repeat check for each, returning
if found
Getting the list of groups which a user is a member of is relatively quick after the first
run since it is cached.
After being loaded it is stored in the context but the implementation doesn't trust
the version in the context and gets it again. It could be a little bit faster by just
assuming that anything in the context is still valid.
If a group is a member of another group then the checks will be repeated twice each time.
One thought that comes to mind is that we could check permissions on a user and all of the
groups of which he is a member, simultaneously. This would reduce the number of scans over
the list of objects in a given document and since this is such a hotspot, I think
optimizations like this might pay off.
Documents which need to be loaded include:
The XWikiPreferences for the main wiki
XWikiPreferences for the subwiki (if applicable)
WebPreferences for the space (and any space listed as a parent)
The document which we are checking
If in a subwiki, the XWikiServer<subwiki> document
This means that for any level of performance, all documents other than the document we are
checking need to remain in the cache perpetually.
Since the vast majority of checks are for things like view and edit with users who
don't have programming or admin permission, it makes sense to rearrange the checks so
that the most common path executes the fastest. This would be a major project though.
Any other thoughts and ideas about it?
Caleb