Back to index

moin  1.9.0~rc2
check.py
Go to the documentation of this file.
00001 # -*- coding: iso-8859-1 -*-
00002 """
00003 MoinMoin - check / process user accounts
00004 
00005 @copyright: 2003-2006 MoinMoin:ThomasWaldmann
00006 @license: GNU GPL, see COPYING for details.
00007 """
00008 
00009 # ----------------------------------------------------------------------------
00010 # if a user subscribes to magicpages, it means that he wants to keep
00011 # exactly THIS account - this will avoid deleting it.
00012 magicpages = [
00013     "ThisAccountIsCorrect",
00014     "DieserAccountIstRichtig",
00015 ]
00016 
00017 # ----------------------------------------------------------------------------
00018 
00019 import os, sys
00020 
00021 from MoinMoin.script import MoinScript
00022 from MoinMoin import user, wikiutil
00023 
00024 class PluginScript(MoinScript):
00025     """\
00026 Purpose:
00027 ========
00028 When using ACLs, a wiki user name has to be unique, there must not be
00029 multiple accounts having the same username. The problem is, that this
00030 was possible before the introduction of ACLs and many users, who forgot
00031 their ID, simply created a new ID using the same user name.
00032 
00033 Because access rights (when using ACLs) depend on the NAME (not the ID),
00034 this must be cleaned up before using ACLs or users will have difficulties
00035 changing settings and saving their account data (system won't accept the
00036 save, if the user name and email is not unique).
00037 
00038 Detailed Instructions:
00039 ======================
00040 
00041 General syntax: moin [options] account check [check-options]
00042 
00043 [options] usually should be:
00044     --config-dir=/path/to/my/cfg/ --wiki-url=wiki.example.org/
00045 
00046 [check-options] see below:
00047 
00048     0. Check the settings at top of the code!
00049        Making a backup of your wiki might be also a great idea.
00050 
00051     1. Best is to first look at duplicate user names:
00052        moin ... account check --usersunique
00053 
00054        If everything looks OK there, you may save that to disk:
00055        moin ... account check --usersunique --save
00056 
00057     2. Now, check also for duplicate email addresses:
00058        moin ... account check --emailsunique
00059 
00060        If everything looks OK, you may save that to disk:
00061        moin ... account check --emailsunique --save
00062 
00063     3. If the announced action is incorrect, you may choose to better manually
00064        disable some accounts: moin ... account disable --uid 1234567.8.90
00065 
00066     4. After cleaning up, do 1. and 2. again. There should be no output now, if
00067        everything is OK.
00068 
00069     5. Optionally you may want to make wikinames out of the user names
00070        moin ... account check --wikinames
00071        moin ... account check --wikinames --save
00072 """
00073 
00074     def __init__(self, argv, def_values):
00075         MoinScript.__init__(self, argv, def_values)
00076         self._addFlag("usersunique",
00077             "Makes user names unique (by appending the ID to"
00078             " name and email, disabling subscribed pages and"
00079             " disabling all, but the latest saved user account);"
00080             " default is to SHOW what will be happening, you"
00081             " need to give the --save option to really do it."
00082         )
00083         self._addFlag("emailsunique",
00084             "Makes user emails unique;"
00085             " default is to show, use --save to save it."
00086         )
00087         self._addFlag("wikinames",
00088             "Convert user account names to wikinames (camel-case)."
00089         )
00090         self._addFlag("save",
00091             "If specified as LAST option, will allow the other"
00092             " options to save user accounts back to disk."
00093             " If not specified, no settings will be changed permanently."
00094         )
00095         self._addFlag("removepasswords",
00096             "Remove pre-1.1 cleartext passwords from accounts."
00097         )
00098 
00099     def _addFlag(self, name, help):
00100         self.parser.add_option("--" + name,
00101             action="store_true", dest=name, default=0, help=help)
00102 
00103     def collect_data(self):
00104         import re
00105         request = self.request
00106         for uid in user.getUserList(request):
00107             u = user.User(request, uid)
00108             self.users[uid] = u
00109 
00110             # collect name duplicates:
00111             if u.name in self.names:
00112                 self.names[u.name].append(uid)
00113             else:
00114                 self.names[u.name] = [uid]
00115 
00116             # collect email duplicates:
00117             if u.email:
00118                 if u.email in self.emails:
00119                     self.emails[u.email].append(uid)
00120                 else:
00121                     self.emails[u.email] = [uid]
00122 
00123             # collect account with no or invalid email address set:
00124             if not u.email or not re.match(".*@.*\..*", u.email):
00125                 self.uids_noemail[uid] = u.name
00126 
00127     def hasmagicpage(self, uid):
00128         u = self.users[uid]
00129         return u.isSubscribedTo(magicpages)
00130 
00131     def disableUser(self, uid):
00132         u = self.users[uid]
00133         print " %-20s %-30r %-35r" % (uid, u.name, u.email),
00134         keepthis = self.hasmagicpage(uid)
00135         if keepthis:
00136             print "- keeping (magicpage)!"
00137             u.save() # update timestamp, so this will be latest next run
00138         elif not u.disabled: # only disable once
00139             u.disabled = 1
00140             u.name = "%s-%s" % (u.name, uid)
00141             if u.email:
00142                 u.email = "%s-%s" % (u.email, uid)
00143             u.subscribed_pages = "" # avoid using email
00144             if self.options.save:
00145                 u.save()
00146                 print "- disabled."
00147             else:
00148                 print "- would be disabled."
00149 
00150     def getsortvalue(self, uid, user):
00151         return float(user.last_saved) # when user did last SAVE of his account data
00152 
00153     def process(self, uidlist):
00154         sortlist = []
00155         for uid in uidlist:
00156             u = self.users[uid]
00157             sortlist.append((self.getsortvalue(uid, u), uid))
00158         sortlist.sort()
00159         #print sortlist
00160         # disable all, but the last/latest one
00161         for dummy, uid in sortlist[:-1]:
00162             self.disableUser(uid)
00163         # show what will be kept
00164         uid = sortlist[-1][1]
00165         u = self.users[uid]
00166         print " %-20s %-30r %-35r - keeping%s!" % (uid, u.name, u.email, self.hasmagicpage(uid) and " (magicpage)" or "")
00167 
00168     def make_users_unique(self):
00169         for name, uids in self.names.items():
00170             if len(uids) > 1:
00171                 self.process(uids)
00172 
00173     def make_emails_unique(self):
00174         for email, uids in self.emails.items():
00175             if len(uids) > 1:
00176                 self.process(uids)
00177 
00178     def make_WikiNames(self):
00179         for uid, u in self.users.items():
00180             if u.disabled:
00181                 continue
00182             if not wikiutil.isStrictWikiname(u.name):
00183                 newname = u.name.capwords().replace(" ", "").replace("-", "")
00184                 if not wikiutil.isStrictWikiname(newname):
00185                     print " %-20s %-30r - no WikiName, giving up" % (uid, u.name)
00186                 else:
00187                     print " %-20s %-30r - no WikiName -> %r" % (uid, u.name, newname)
00188                     if self.options.save:
00189                         u.name = newname
00190                         u.save()
00191 
00192     def remove_passwords(self):
00193         for uid, u in self.users.items():
00194             # user.User already clears the old cleartext passwords on loading,
00195             # so nothing to do here!
00196             if self.options.save:
00197                 # we can't encrypt the cleartext password as it is cleared
00198                 # already. and we would not trust it anyway, so we don't WANT
00199                 # to do that either!
00200                 # Just save the account data without cleartext password:
00201                 print " %-20s %-25s - saving" % (uid, u.name)
00202                 u.save()
00203 
00204     def mainloop(self):
00205         # we don't expect non-option arguments
00206         if len(self.args) != 0:
00207             self.parser.error("incorrect number of arguments")
00208 
00209         # check for correct option combination
00210         flags_given = (self.options.usersunique
00211                     or self.options.emailsunique
00212                     or self.options.wikinames
00213                     or self.options.removepasswords)
00214 
00215         # no option given ==> show usage
00216         if not flags_given:
00217             self.parser.print_help()
00218             sys.exit(1)
00219 
00220         self.init_request()
00221 
00222         self.users = {} # uid : UserObject
00223         self.names = {} # name : [uid, uid, uid]
00224         self.emails = {} # email : [uid, uid, uid]
00225         self.uids_noemail = {} # uid : name
00226 
00227         self.collect_data()
00228         if self.options.usersunique:
00229             self.make_users_unique()
00230         if self.options.emailsunique:
00231             self.make_emails_unique()
00232         if self.options.wikinames:
00233             self.make_WikiNames()
00234         if self.options.removepasswords:
00235             self.remove_passwords()
00236