Mercurial > moin-2.0
changeset 2080:3488b0e24ee8
forward port moin 1.9 password reset notification code, templates, docs
* add sphinx docs about password resetting
* fix RECOVERPASS_KEY KeyError if users tries to use same token twice
* add moin account-password options for mail subject and text, skipping
of accounts with already invalidated passwords
* change order of input fields in password recovery form, so the browser
will be able to find the right username/password fields to remember
| author | Thomas Waldmann <tw AT waldmann-edv DOT de> |
|---|---|
| date | Mon, 08 Apr 2013 00:20:44 +0200 |
| parents | 1501cd140d03 |
| children | 88660eb0ca43 |
| files | MoinMoin/config/default.py MoinMoin/script/account/resetpw.py MoinMoin/templates/recoverpass.html MoinMoin/user.py docs/admin/password-reset.rst docs/examples/password-reset/mailtemplate-de.txt docs/examples/password-reset/mailtemplate.txt docs/examples/password-reset/wikitemplate-de.txt docs/examples/password-reset/wikitemplate.txt docs/index.rst |
| diffstat | 10 files changed, 343 insertions(+), 15 deletions(-) [+] |
line wrap: on
line diff
--- a/MoinMoin/config/default.py Sun Apr 07 19:11:37 2013 +0200 +++ b/MoinMoin/config/default.py Mon Apr 08 00:20:44 2013 +0200 @@ -484,6 +484,7 @@ SHOW_COMMENTS: False, WANT_TRIVIAL: False, ENC_PASSWORD: u'', # empty value == invalid hash + RECOVERPASS_KEY: u'', # empty value == invalid key SESSION_KEY: u'', # empty value == invalid key DISABLED: False, BOOKMARKS: {},
--- a/MoinMoin/script/account/resetpw.py Sun Apr 07 19:11:37 2013 +0200 +++ b/MoinMoin/script/account/resetpw.py Mon Apr 08 00:20:44 2013 +0200 @@ -8,6 +8,9 @@ """ +import sys + +from flask import current_app as app from flask.ext.script import Command, Option from MoinMoin.constants.keys import ITEMID, NAME, NAME_EXACT, EMAIL @@ -23,17 +26,25 @@ """raised if no such user exists""" +class UserHasNoEMail(Fault): + """raised if user has no e-mail address in his profile""" + + class MailFailed(Fault): """raised if e-mail sending failed""" -def set_password(uid, password, notify=False): +def set_password(uid, password, notify=False, skip_invalid=False, subject=None, text=None): u = user.User(uid) if u and u.exists(): + if skip_invalid and u.has_invalidated_password(): + return u.set_password(password) u.save() - if notify and not u.disabled and u.email: - mailok, msg = u.mail_password_recovery() + if not u.email: + raise UserHasNoEMail('User profile does not have an E-Mail address (name: %r id: %r)!' % (u.name, u.id)) + if notify and not u.disabled: + mailok, msg = u.mail_password_recovery(subject=subject, text=text) if not mailok: raise MailFailed(msg) else: @@ -55,14 +66,29 @@ help='Notify user(s), send them an E-Mail with a password reset link.'), Option('--verbose', '-v', required=False, dest='verbose', action='store_true', default=False, help='Verbose operation'), + Option('--subject', required=False, dest='subject', type=unicode, + help='Subject text for the password reset notification E-Mail.'), + Option('--text', required=False, dest='text', type=unicode, + help='Template text for the password reset notification E-Mail. Default: use the builtin standard template'), + Option('--text-from-file', required=False, dest='text_file', type=unicode, + help='Read full template for the password reset notification E-Mail from the given file, overrides --text. Default: None'), + Option('--skip-invalid', required=False, dest='skip_invalid', action='store_true', + help='If a user\'s password hash is already invalid (pw is already reset), skip this user.'), ) - def run(self, name, uid, password, all_users, notify, verbose): + def run(self, name, uid, password, all_users, notify, verbose, subject, text, text_file, skip_invalid): flags_given = name or uid or all_users if not flags_given: print 'incorrect number of arguments' - import sys - sys.exit() + sys.exit(1) + + if notify and not app.cfg.mail_enabled: + print "This wiki is not enabled for mail processing. The --notify option requires this. Aborting..." + sys.exit(1) + + if text_file: + with open(text_file) as f: + text = f.read().decode('utf-8') before_wiki() if uid: @@ -79,8 +105,9 @@ name = meta[NAME] email = meta[EMAIL] try: - set_password(uid, password, notify=notify) - except Fault, err: + set_password(uid, password, notify=notify, skip_invalid=skip_invalid, + subject=subject, text=text) + except Fault as err: status = "FAILURE: [%s]" % str(err) else: status = "SUCCESS"
--- a/MoinMoin/templates/recoverpass.html Sun Apr 07 19:11:37 2013 +0200 +++ b/MoinMoin/templates/recoverpass.html Mon Apr 08 00:20:44 2013 +0200 @@ -6,8 +6,8 @@ {{ gen.form.open(form, method="post", action=url_for('frontend.recoverpass')) }} {{ forms.render_errors(form) }} <dl> + {{ forms.render(form['token']) }} {{ forms.render(form['username']) }} - {{ forms.render(form['token']) }} {{ forms.render(form['password1']) }} {{ forms.render(form['password2']) }} </dl>
--- a/MoinMoin/user.py Sun Apr 07 19:11:37 2013 +0200 +++ b/MoinMoin/user.py Mon Apr 08 00:20:44 2013 +0200 @@ -473,6 +473,12 @@ # Invalidate all other browser sessions except this one. session['user.session_token'] = self.generate_session_token(False) + def has_invalidated_password(self): + """ + Check if the password hash of this user is invalid. + """ + return self.profile[ENC_PASSWORD] == '' + def save(self, force=False): """ Save user account data to user account file on disk. @@ -752,13 +758,21 @@ self.save() return True - def mail_password_recovery(self, cleartext_passwd=None): + def mail_password_recovery(self, cleartext_passwd=None, subject=None, text=None): """ Mail a user who forgot his password a message enabling him to login again. """ + if not self.email: + return False, "user has no E-Mail address in his profile." + token = self.generate_recovery_token() - text = _("""\ + if subject is None: + subject = _('[%(sitename)s] Your wiki password recovery link', sitename='%(sitename)s') + subject = subject % dict(sitename=self._cfg.sitename or "Wiki") + + if text is None: + text = _("""\ Somebody has requested to email you a password recovery link. Please use the link below to change your password to a known value: @@ -766,11 +780,9 @@ %(link)s If you didn't forget your password, please ignore this email. - -""", link=url_for('frontend.recoverpass', username=self.name0, token=token, _external=True)) +""", link='%(link)s') + text = text % dict(link=url_for('frontend.recoverpass', username=self.name0, token=token, _external=True)) - subject = _('[%(sitename)s] Your wiki password recovery link', - sitename=self._cfg.sitename or "Wiki") mailok, msg = sendmail.sendmail(subject, text, to=[self.email], mail_from=self._cfg.mail_from) return mailok, msg
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/admin/password-reset.rst Mon Apr 08 00:20:44 2013 +0200 @@ -0,0 +1,134 @@ +=============================== +Password Resetting/Invalidation +=============================== +There might be circumstances when the wiki admin wants or needs to reset one +user's or all users' password (hash). + +For example: + +* you had a security breach on your wiki server (or somewhere else) and the + old password hashes (or passwords) were exposed +* you want to make sure some user or all users set a new password, e.g. if: + + - your password policy has changed (requiring longer passwords for example) + - you changed your passlib configuration and want to immediately have all + hashes upgraded + +Note: if we say "reset a password" (to use a commonly used term), we mean to +"invalidate the password hash" (so that no password exists that validates +against that hash). MoinMoin does not keep user passwords in cleartext. + +The files we refer to below are located in docs/examples/password-reset/... + + +Resetting one or few password(s) +================================ +If you somehow interact with the users corresponding to the user accounts in +question (by phone or directly), you don't need the extensive procedure as +described below, just use:: + + moin account-password --name JoeDoe + +That will reset JoeDoe's password. Tell him to visit the login URL and use +the "forgot my password" functionality to define a new password. + +If that doesn't work (e.g. if e-mail is not enabled for your wiki or he has +a non-working e-mail address in his profile), you can also set a password for +him:: + + moin account-password --name JoeDoe --password uIkV9.-a3 + +Choose a rather complicated password to make sure they change it a minute +afterwards (to another, hopefully safe password). + + +Resetting many or all password(s) +================================= +If you have a lot of passwords to reset, you need a better procedure that +avoids having to deal with too many users individually. + + +Preparing your users +-------------------- +Tell your users beforehands that you will be doing a password reset, otherwise +they might find the automatically generated E-Mail they'll get suspicious and +you'll have to explain it to them individually that the E-Mail is legitimate. + +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 an active user does somehow not get such a mail, you likely will have to +manually define a valid E-Mail address (or even password) for that user. + + +Make sure E-Mail functionality works +------------------------------------ +If you know you have working E-Mail functionality, skip this section. + +Password recovery and password reset notification work via E-Mail, so you +should have it configured:: + + # 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_username = 'wiki@example.org' + mail_password = 'SuperSecretSMTPPassword' + +You can try whether it works by using the "forgot my password" functionality +on the login page. + + +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. + +Some places you likely should edit are marked with XXX. + +Do not use any % character in your text (except for the placeholders). +If you need a verbatim % character, you need to write %%. + +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. + +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. + +We have added a wikitemplate.txt you can use to create such a wiki page. + +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. + + +Editing wikitemplate.txt +------------------------ +Just copy & paste it to some public page in your wiki, e.g. "PasswordReset". + +Some places you likely should edit are marked with XXX. + + +Doing the password reset +------------------------ +Maybe first try it with a single user account:: + + moin account-password --name JoeDoe --notify --subject 'Wiki password reset' --text-from-file 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-password --verbose --all-users --notify --subject 'Wiki password reset' --text-from-file 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' +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/examples/password-reset/mailtemplate-de.txt Mon Apr 08 00:20:44 2013 +0200 @@ -0,0 +1,14 @@ +Der Wiki-Administrator hat Ihr Passwort invalidiert und angefordert, +Ihnen diese E-Mail zu senden, damit Sie ein neues Passwort setzen koennen. + +Bitte lesen Sie wichtige Informationen hierzu (sowie Hinweise zur +Problembeseitigung und Wiki-Administrator-Kontaktinformationen) dort: + +(XXX hier die URL angeben XXX) + + +Bitte besuchen Sie nun die unten angezeigte Passwort-Ruecksetz-URL und +setzen Sie ein neues Passwort. + +%(link)s +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/examples/password-reset/mailtemplate.txt Mon Apr 08 00:20:44 2013 +0200 @@ -0,0 +1,13 @@ +The wiki administrator has invalidated your wiki password and requested +to send this E-Mail to you, so you can set a new one. + +Important information about this (including troubleshooting information and +wiki administrator contact information) is available there, please read it: + +(XXX give URL here XXX) + + +Now, please go to the password reset URL below and set a new password. + +%(link)s +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/examples/password-reset/wikitemplate-de.txt Mon Apr 08 00:20:44 2013 +0200 @@ -0,0 +1,67 @@ +Hinweis: einige Stellen, die man editieren muss, sind mit XXX markiert. + += (XXX Datum angeben) YYYY-MM-DD Passworte zurückgesetzt = + +Der Wiki-Administrator hat für alle Benutzerkonten dieses Wikis die Passwörter invalidiert (auf ungültig gesetzt). + +Sie müssen eine Passwort-Rücksetzung ausführen, um ein ''neues und anderes'' Passwort für Ihr Konto zu setzen. + +== Gründe für die Passwort-Invalidierung == + + * (XXX Grund angeben) + * (XXX Grund angeben) + +== Was Sie tun müssen == +=== Lesen Sie wichtige Informationen === +Bitte besuchen Sie diese URLs für weitere Informationen: + * http://moinmo.in/HowToHandleSecurityBreach/de (allgemeine Hinweise, unbedingt lesen!) + * http://moinmo.in/SecurityFixes/CVE-YYYY-NNNN (über den spezifischen Vorfall, wenn Sie mehr Informationen möchten) (XXX URL korrigieren) + +=== Ein neues Passwort setzen === +Sie sollten eine E-Mail (inklusive eines Links, um Ihr Passwort zurückzusetzen) erhalten haben. Lesen und befolgen Sie sie. + +=== Problembehandlung === +==== Ungültiges Token? ==== +Das Token hat eine begrenzte Gültigkeitsdauer. + +Wenn es "Token ist ungültig" sagt, müssen Sie einfach ein neues Token anfordern, siehe unten. + +==== Sie haben eine unerwartete E-Mail über ein unbekanntes Benutzerkonto erhalten? ==== +Wenn Sie eine E-Mail erhalten haben, die Sie auffordert, das Passwort ''für ein Ihnen unbekanntes Benutzerkonto'' zurückzusetzen: +ignorieren Sie sie einfach, setzen Sie das Passwort ''nicht'' zurück + +Wahrscheinlich hat jemand (z.B. ein Spammer) Ihre E-Mail-Adresse missbraucht, als er ein Benutzerkonto angelegt hat. +Das passiert oft, Sie können das auch in Ihrem E-Mail-Spam-Ordner sehen. + +==== Sie haben die E-Mail nicht erhalten? ==== +Wenn Sie die E-Mail noch nicht gesehen haben, prüfen Sie bitte Ihren Spam-Ordner (oder auch andere Ordner, wo E-Mails vom Wiki sein könnten). + +Wenn Sie die von uns an Sie gesandte E-Mail nicht finden können, können Sie alternativ auch die normale Passwort-Rücksetz-Funktion des Wikis benutzen, siehe unten. + +==== Ein neues Passwort-Rücksetz-Token erhalten ==== + * gehen Sie zur Anmelde-Seite des Wiki + * klicken Sie auf den "Password vergessen?"-Link + * Dann: + * ''entweder'' Ihren Wiki-Benutzernamen eingeben (empfehlenswert, wenn Sie ihn wissen oder einfach herausfinden können) + * ''oder'' Ihre E-Mail-Addresse (die gleiche wie in Ihrem Wiki-Benutzerprofil - möglicherweise brauchen Sie mehrere Versuche, wenn Sie sich hier nicht sicher sind) + * prüfen Sie Ihren E-Mail-Eingang, Sie sollten nun eine E-Mail mit einem Passwort-Rücksetz-Link haben + * klicken Sie auf den Link, definieren Sie ein neues Passwort + +==== Brauchen Sie Hilfe? ==== +Wenn das nicht funktioniert hat, brauchen Sie vermutlich Hilfe durch einen Wiki-Administrator, bitte kontaktieren Sie: + +(XXX hier Name und E-Mail des Wiki-Administrators angeben XXX) + +In Ihrer E-Mail, geben Sie bitte Folgendes an: + * die Wiki-Adresse (URL) + * Ihren Wiki-Benutzernamen + * die E-Mail-Adresse, die Sie in Ihrem Wiki-Benutzerprofil verwendet haben + * falls diese E-Mail-Adresse nicht mehr funktioniert, teilen Sie uns das bitte mit + +== Fragen? == +Wenn Sie Fragen haben, die nicht bereits auf dieser oder den verlinkten Seiten beantwortet werden, fragen Sie den Wiki-Administrator. + +E-Mails mit Fragen, die dort bereits beantwortet sind, werden ignoriert. + +Anstelle Fragen individuell über E-Mail zu beantworten, können oft gestellte Fragen auch auf dieser Seite oder den verlinkten Seiten beantwortet werden. +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/examples/password-reset/wikitemplate.txt Mon Apr 08 00:20:44 2013 +0200 @@ -0,0 +1,59 @@ +Note: some stuff that needs editing is marked with XXX. + += (XXX give date) YYYY-MM-DD Password Reset = + +The wiki administrator has invalidated the passwords for all user accounts on this wiki. + +You need to do a password recovery to set a ''new and changed'' password for your account. + +== Reason(s) for the password invalidation == + + * (XXX give reason) + * (XXX give reason) + +== What you need to do == +=== Read important information === +Please visit these URLs for more information: + * http://moinmo.in/HowToHandleSecurityBreach ('''generic advice, MUST READ!''') + * http://moinmo.in/SecurityFixes/CVE-YYYY-NNNN (about the specific incident, if you want more information) (XXX fix url) + +=== Enter a new password === +You should have received an E-Mail (including a link to reset your password). Just read and follow it. + +==== Invalid token? ==== +The token has a limited lifetime. + +If it says "Token is invalid", you just need to get a new password recovery token, see below. + +==== You didn't get the E-Mail? ==== +If you didn't see that E-Mail yet, please check your spam folder (or other folders where mails from the wiki could be). + +If you can't find the E-Mail we sent to you, you can alternatively just use the normal password recovery function of the wiki, see below. + +==== Getting a new password recovery token ==== + * go to the login page + * click the "Forgot your password?" link + * now: + * ''either'' enter your wiki username (recommended, if you still remember it or you can find out easily) + * ''or'' your E-Mail address (same as in your wiki user profile - you might need multiple tries if you are not sure about this) + * check your E-Mail, you should have one now with a password recovery link + * click on the link, define a new password + +==== Need help? ==== +If that didn't work, you will need help by a wiki administrator, please contact: + +(XXX give name and E-Mail address of wiki administrator here XXX) + +In your E-Mail, please give: + * the wiki address (URL) + * your wiki user name + * the E-Mail address that you used in your wiki userprofile + * if that E-Mail address is not working any more, please tell so + +== Questions? == +If you have any questions that are not answered by reading this page or the pages linked from here, ask the wiki administrator. + +E-Mails asking questions that ''are'' answered on these pages will be ignored. + +Instead of answering individually via E-Mail, frequently asked questions might might also get answered on this page or the pages linked from here. +
