changeset 5936:2d3352c547bc

moin account resetpw - misc. improvements add some options to define email subject and text, either by commandline arguments or by reading an email template from a text file. added instructions about how to deal with a password reset and a email template, see docs/resetpw/ if notificiation is requested and the wiki is not configured for email sending, abort.
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Thu, 24 Jan 2013 22:34:41 +0100
parents 6f201a3b1b24
children 9a9af7912a44
files MoinMoin/script/account/resetpw.py MoinMoin/user.py docs/CHANGES docs/resetpw/README docs/resetpw/mailtemplate.txt
diffstat 5 files changed, 183 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/script/account/resetpw.py	Thu Jan 24 14:25:48 2013 +0100
+++ b/MoinMoin/script/account/resetpw.py	Thu Jan 24 22:34:41 2013 +0100
@@ -7,6 +7,8 @@
 @license: GNU GPL, see COPYING for details.
 """
 
+import sys
+
 from MoinMoin.script import MoinScript, log
 from MoinMoin.user import getUserList, set_password, Fault
 
@@ -67,6 +69,26 @@
             help="Notify user(s), send them an E-Mail with a password reset link."
         )
         self.parser.add_option(
+            "--subject", metavar="SUBJECT", dest="subject",
+            help="Subject text for the password reset notification E-Mail."
+        )
+        self.parser.add_option(
+            "--text-intro", metavar="TEXT_INTRO", dest="text_intro",
+            help="Intro text for the password reset notification E-Mail. Default: empty."
+        )
+        self.parser.add_option(
+            "--text-msg", metavar="TEXT_MSG", dest="text_msg",
+            help="Main text for the password reset notification E-Mail. Default: use the builtin standard message"
+        )
+        self.parser.add_option(
+            "--text-data", metavar="TEXT_DATA", dest="text_data",
+            help="Data template text for the password reset notification E-Mail. Default: use the builtin standard data template"
+        )
+        self.parser.add_option(
+            "--text-from-file", metavar="TEXT_DATA", dest="text_file",
+            help="Read full template for the password reset notification E-Mail from the given file, overrides --text-intro/msg/data. Default: None"
+        )
+        self.parser.add_option(
             "-v", "--verbose", dest="verbose", action="store_true",
             help="Verbose operation."
         )
@@ -83,22 +105,40 @@
         flags_given = self.options.uid or self.options.uname or self.options.all_users
         if not flags_given:
             self.parser.print_help()
-            import sys
             sys.exit(1)
 
         self.init_request()
         request = self.request
 
+        notify = self.options.notify
+        if notify and not request.cfg.mail_enabled:
+            print "This wiki is not enabled for mail processing. The --notify option requires this. Aborting..."
+            sys,exit(1)
+
+        subject = self.options.subject
+        text_intro = self.options.text_intro
+        text_msg = self.options.text_msg
+        text_data = self.options.text_data
+        text_file = self.options.text_file
+
+        if text_file:
+            text_intro = ''
+            text_msg = ''
+            with open(text_file) as f:
+                text_data = f.read().decode('utf-8')
+
         if self.options.uid:
             try:
                 set_password(request, newpass, uid=self.options.uid,
-                             notify=self.options.notify)
+                             notify=notify, subject=subject,
+                             text_intro=text_intro, text_msg=text_msg, text_data=text_data)
             except Fault, err:
                 print str(err)
         elif self.options.uname:
             try:
                 set_password(request, newpass, uname=self.options.uname,
-                             notify=self.options.notify)
+                             notify=notify, subject=subject,
+                             text_intro=text_intro, text_msg=text_msg, text_data=text_data)
             except Fault, err:
                 print str(err)
         elif self.options.all_users:
@@ -108,6 +148,7 @@
                 log("%05d / %05d - processing uid %s" % (nr, total, uid))
                 try:
                     set_password(request, newpass, uid=uid,
-                                 notify=self.options.notify)
+                                 notify=notify, subject=subject,
+                                 text_intro=text_intro, text_msg=text_msg, text_data=text_data)
                 except Fault, err:
                     print str(err)
--- a/MoinMoin/user.py	Thu Jan 24 14:25:48 2013 +0100
+++ b/MoinMoin/user.py	Thu Jan 24 22:34:41 2013 +0100
@@ -191,7 +191,9 @@
     """raised if e-mail sending failed"""
 
 
-def set_password(request, newpass, u=None, uid=None, uname=None, notify=False):
+def set_password(request, newpass, u=None, uid=None, uname=None,
+                 notify=False, subject=None,
+                 text_intro=None, text_msg=None, text_data=None):
     if uid:
         u = User(request, uid)
     elif uname:
@@ -204,7 +206,8 @@
             u.enc_password = encodePassword(request.cfg, newpass)
         u.save()
         if notify and not u.disabled:
-            mailok, msg = u.mailAccountData()
+            mailok, msg = u.mailAccountData(subject=subject,
+                                            text_intro=text_intro, text_msg=text_msg, text_data=text_data)
             if not mailok:
                 raise MailFailed(msg)
     else:
@@ -1111,12 +1114,13 @@
         self.save()
         return True
 
-    def mailAccountData(self, cleartext_passwd=None):
+    def mailAccountData(self, cleartext_passwd=None,
+                        subject=None,
+                        text_intro=None, text_msg=None, text_data=None):
         """ Mail a user who forgot his password a message enabling
             him to login again.
         """
         from MoinMoin.mail import sendmail
-        from MoinMoin.wikiutil import getLocalizedPage
         _ = self._request.getText
 
         if not self.email:
@@ -1124,30 +1128,35 @@
 
         tok = self.generate_recovery_token()
 
-        text = '\n' + _("""\
+        if subject is None:
+            subject = _('[%(sitename)s] Your wiki account data')
+        subject = subject % dict(sitename=self._cfg.sitename or "Wiki")
+        if text_intro is None:
+            text_intro = ''
+        if text_msg is None:
+            text_msg = """\
+Somebody has requested to email you a password recovery token.
+
+If you lost your password, please go to the password reset URL below or
+go to the password recovery page again and enter your username and the
+recovery token.
+"""
+        if text_data is None:
+            text_data = """\
 Login Name: %s
 
 Password recovery token: %s
 
 Password reset URL: %s?action=recoverpass&name=%s&token=%s
-""") % (
+"""
+        # note: text_intro is for custom stuff, we do not have i18n for it anyway
+        text = text_intro + '\n' + _(text_msg) + '\n' + _(text_data) % (
                         self.name,
                         tok,
                         self._request.url, # use full url, including current page
                         url_quote_plus(self.name),
                         tok, )
 
-        text = _("""\
-Somebody has requested to email you a password recovery token.
-
-If you lost your password, please go to the password reset URL below or
-go to the password recovery page again and enter your username and the
-recovery token.
-""") + text
-
-
-        subject = _('[%(sitename)s] Your wiki account data',
-                ) % {'sitename': self._cfg.sitename or "Wiki"}
         mailok, msg = sendmail.sendmail(self._request, [self.email], subject,
                                     text, mail_from=self._cfg.mail_from)
         return mailok, msg
--- a/docs/CHANGES	Thu Jan 24 14:25:48 2013 +0100
+++ b/docs/CHANGES	Thu Jan 24 22:34:41 2013 +0100
@@ -87,10 +87,7 @@
     http://packages.python.org/passlib/new_app_quickstart.html#choosing-a-hash
     http://packages.python.org/passlib/password_hash_api.html#choosing-the-right-rounds-value
 
-  * Password mass reset support:
-    Resetting the passwords of all wiki users can be done using:
-    moin ... --verbose account resetpw --all-users --notify
-
+  * Password mass reset/invalidation support, see docs/resetpw/.
     This is useful to make sure everybody sets a new password and moin computes
     the password hash using the current configuration.
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/resetpw/README	Thu Jan 24 22:34:41 2013 +0100
@@ -0,0 +1,92 @@
+Resetting (invalidating) password(s)
+====================================
+
+First make sure E-Mail functionality for your wiki is enabled, something like:
+
+    # the E-Mail address used for From: (consider using an address that
+    # can be directly replied to, at least while doing the pw reset):
+    mail_from = 'wiki@example.org'
+    # your smtp mail server hostname:port (default is 25)
+    mail_smarthost = 'mail.example.org:587'
+    # the login there, if authentication is needed
+    mail_login = 'wiki@example.org SuperSecretSMTPPassword'
+
+Optionally, you can also remind your users that having a valid E-Mail address
+in their user settings is essential for getting a password recovery E-Mail.
+If a active user does somehow not get such a mail, you likely will have to
+manually define a password for that user.
+
+If you had a security breach on your wiki (password hashes stolen) or you want
+to make sure all password hashes use strong algorithms (which is strongly
+recommended), you need to reset ALL users' passwords:
+
+Editing mailtemplate.txt
+========================
+
+If you edit mailtemplate.txt, please be very careful and follow these rules
+(otherwise you might just see the script command crashing):
+
+The contents must be utf-8 (or ascii, which is a subset of utf-8).
+In case of doubt, just use plain English.
+
+Never ever change the AMOUNT or ORDER of the %s placeholders in the template.
+They will get replaced by MoinMoin with specific values in a specific order.
+We know this can be done better in Python, but this restriction is for
+compatibility with existing translations of the builtin texts, thus we
+could not do better without requiring lots of translation updates.
+
+Do not use any % character in your text (except for the placeholders).
+
+That said, feel free to change or add any other text.
+
+It is a very good idea to give some URL (e.g. of a web or wiki page) in
+the text where users can read more information about why you reset the
+password(s), who exactly resetted the password(s) and who (E-Mail) to contact
+if there are any questions or issues.
+
+Of course the information at that URL should be readable without requiring
+a wiki login (you just have invalidated his/her password!), so the user can
+get informed before clicking links he got from someone via E-Mail.
+
+Instead of creating a web or wiki page with the information, you could
+also write all the stuff into the mail template directly, but please consider
+that E-Mail delivery to some users might fail for misc. reasons, so having
+some information on the web/wiki is usually better.
+
+Doing the password reset
+========================
+
+Maybe first try it with a single user account:
+
+moin ... account resetpw --name JoeDoe --notify --subject 'Wiki password reset' --text-from-file contrib/resetpw/mailtemplate.txt
+
+Use some valid name, maybe a testing account of yourself. You should now have
+mail. If that worked ok, you can now do a global password reset for your wiki:
+
+moin ... account resetpw --all-users --notify --subject 'Wiki password reset' --text-from-file contrib/resetpw/mailtemplate.txt
+
+The subject may contain a placeholder for the sitename, which is useful for
+wiki farms (showing the builtin default here):
+
+    '[%(sitename)s] Your wiki account data'
+
+Please note:
+- Instead of ... you need to give the --config-dir and --wiki-url options with
+  correct values for the wiki you want to operate on.
+- If you have a farm wiki, you need to invoke the command with correct params
+  for each of your farm member wikis.
+
+Helping users who can't do the password recovery
+================================================
+Either they did not get the E-Mail, or deleted it, or ...
+
+You can just set an arbitrary password for them (and tell it to them).
+But make sure they change it a minute afterwards:
+
+Alternatively, if you are in the superuser list, you can go to YOUR user
+settings, switch to the user having troubles and just fix / add a valid
+E-Mail address for that user (if that was the problem). Logout to get back
+to your user account.
+
+Then do the password reset again, but just for that user.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/resetpw/mailtemplate.txt	Thu Jan 24 22:34:41 2013 +0100
@@ -0,0 +1,19 @@
+The wiki administrator has invalidated your wiki password and requested
+to send this E-Mail to you, so you can set a wiki password.
+
+Reason for the password invalidation:
+
+...
+
+Please go to the password reset URL below and set a password.
+
+Alternatively, you can go to the password recovery page and manually
+(use copy&paste) enter the recovery token, your login name and the
+password.
+
+Login Name: %s
+
+Password recovery token: %s
+
+Password reset URL: %s?action=recoverpass&name=%s&token=%s
+