Introduction / Context
At work, I administer a small Moin instance for tech-savvy people. It's only accessible on the intranet and the Wiki is closed to only let authorized people to access the information.
As this wiki instance also contains documentation which can be interesting for other people, the idea was to open the wiki to accept other users, but still controlling to which information they can access, as some needs to be only accessible by the original group of users.
As the script used and the information about the methodology can be useful for other people, I share it here.
If you want to correct some mistakes, ask questions or simply discuss about this, please use the /Discussion page.
Details
The initial configuration is similar to:
all normal users are part of the AdminsGroup
some people only need read access to the Wiki and are members of ReadersGroup (mostly hierarchy)
- all other people have no rights on any page
The ACL specified in wikiconfig.py are like
acl_rights_before = u"AdminsGroup:read,write,revert,delete,admin" acl_rights_default = u"ReadersGroup:read All:"
Security is also enforced by using LDAP authentication, restricting access to a specific usergroup in the LDAP directory.
Problems
The normal use of a Wiki is to be open to all users, even anonymous one. This way content can be 'protected' with this behaviour in mind, using ACL on pages or some hierarchy of pages.
As our decision was to use our wiki in a non-common way, ie. closing it completely, opening it later giving different access to some content is non-natural and complicated.
After a lot of reflexions and tests, one solution was choosen and will be presented below, but FYI here is a short list of some of the other solutions that were imagined and finally not adopted (with the problems associated):
- opening only the needed pages
using ACL it's possible to give access to specific pages but access to system/underlay pages (RecentChanges, HelpContents...) is forbidden so the user experience is poor
- opening only the needed pages + run a script to change ACL on the system/underlay pages
- problem: the script must be adapted and run at each moin migration
- open another wiki instance
this does not solve our problem as content needs to be added/updated on the two wikis or is only available in one of the two... (maybe a wikifarm is the solution but i don't know it)
Solution
The solution that we finally choose and implement is the following:
- move all pages that need protection under another page: in reality it's a rename not a move
ex: rename PageOne, PageTwo, PageThree to SomeHierarchy/PageOne, SomeHierarchy/PageTwo, SomeHierarchy/PageThree
- check all pages to update links to the pages that were renamed
a UsersGroup is created and users are added to it
- wikiconfig.py is updated
acl_rights_default = u"ReadersGroup:read UsersGroup:read All:"
acl_hierarchic = True
on SomeHierarchy page ACL is put to forbid access to all
#acl -All:read
- if necessary, this is also done for other sensitive pages
Script
Steps 1 (pages rename) and 2 (update links) were handled by a script to avoid excessive repetition of human tasks and errors. This script is presented here and commented below. I hope it can be useful for other people.
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 import os, re, shutil, sys
5 from MoinMoin.Page import Page
6 from MoinMoin.PageEditor import PageEditor
7 from MoinMoin.web.contexts import ScriptContext
8
9
10 ## variables
11 wiki_dir = "/var/www/wikiinstance"
12 wiki_url = "http://wiki.domain.tld"
13
14 renaming_prefix = "SomeHierarchy/"
15
16 immutables_pages_regex = ur"""
17 ^(\S+)Category$
18 |
19 ^(\S+)Dict$
20 |
21 ^(\S+)Group$
22 |
23 ^(\S+)Template$
24 |
25 ^
26 (
27 [a-z-]+
28 \.
29 [a-z-]+
30 (\/.+)*
31 )
32 |^(\S*)Hierarchy$
33 |^ToDo$
34 |^SomeSpecificPage$
35 """
36
37
38 ##
39 def classify_pages():
40 """
41 function to classify pages depending of their type
42 - immutables pages of if they are underlay or match the immutables_pages_regex
43 - invalid pages if they don't exists
44 - valid pages for others
45 """
46 regex = re.compile(immutables_pages_regex, re.VERBOSE)
47 request = ScriptContext(wiki_url)
48 pagelist = request.rootpage.getPageList(include_underlay=False)
49 for pagename in pagelist:
50 try:
51 page = Page(request, pagename)
52 except:
53 print 'exception'
54 continue
55 if not page.exists:
56 invalid_pages.append(pagename)
57 if page.isUnderlayPage() or regex.search(pagename):
58 immutables_pages.append(pagename)
59 else:
60 valid_pages.append(pagename)
61
62
63 def renaming_pages(pages, prefix):
64 """
65 Renaming pages with the moin API
66
67 @param pages: list of pages to rename
68 @param prefix: prefix used to rename pages
69 """
70 request = ScriptContext(wiki_url)
71 for pagename in pages:
72 try:
73 page = PageEditor(request, pagename)
74 except:
75 print "exception on %s" % pagename
76 continue
77 page.renamePage(prefix+pagename)
78
79
80 def update_links(changed_pages, prefix):
81 """
82 Verify all pages to change any links in the changed_pages list
83 to add the defined prefix
84
85 @param pages: list of pages renamed
86 @param prefix: prefix used to update links
87 """
88 request = ScriptContext(wiki_url)
89 pagelist = request.rootpage.getPageList(include_underlay=False)
90 for pagename in pagelist:
91 try:
92 page = PageEditor(request, pagename)
93 except:
94 print "exception on %s" % pagename
95 continue
96 links = page.getPageLinks(request)
97 to_be_changed_links = []
98 not_changed_links = []
99 text = page.get_raw_body()
100 for link in links:
101 if link in changed_pages:
102 to_be_changed_links.append(link)
103 text = text.replace(link, prefix+link)
104 else:
105 not_changed_links.append(link)
106 try:
107 page.saveText(text, 0, comments="updated links")
108 except (PageEditor.Unchanged, PageEditor.EmptyPage):
109 continue
110
111
112 if __name__ == "__main__":
113 sys.path.append(wiki_dir)
114 import wikiconfig
115 wikiconfig.Config.acl_rights_before = wikiconfig.Config.acl_rights_before + " All:read,write,delete,revert,admin"
116
117 invalid_pages = []
118 immutables_pages = []
119 valid_pages = []
120
121 classify_pages()
122
123 renaming_pages(valid_pages, renaming_prefix)
124
125 update_links(valid_pages, renaming_prefix)
The variables (lines 11 to 35) needs to be edited to suit to your configuration.
The immutables_pages_regex lets you define which pages will not be renamed. In the example there are:
- category, template, dict and group special pages (adapt to your own configuration, check wikiconfig.py)
- the [a-z-]+\.[a-z-]+(\/.+)* regex define our users homepage in the form name.surname
the others are for specific pages especially the SomeHierarchy which will the base for the renamed pages
Changes are handled with the moin api so they appears in the RecentChanges page, but as 'anonymous'.
Links
http://docs.moinmo.in/moin/1.9/ Moin API
http://moinmo.in/MoinAPI/Beispiele in German