changeset 91:5c1c7f2d6469

Implement user password reset. Use the same mechanism used for account activation for password reset.
author Daniele Nicolodi <daniele@grinta.net>
date Sun, 21 Aug 2011 18:17:27 +0200
parents c55432c9600b
children 6e9ba2b64d1f
files src/ltpdarepo/templates/mail/reset.txt src/ltpdarepo/templates/users/reset.html src/ltpdarepo/templates/users/view.html src/ltpdarepo/user.py src/ltpdarepo/views/profile.py src/ltpdarepo/views/users.py
diffstat 6 files changed, 92 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ltpdarepo/templates/mail/reset.txt	Sun Aug 21 18:17:27 2011 +0200
@@ -0,0 +1,8 @@
+Dear {{ user.name or user.username }},
+
+your password has ben reset. To choose a new one visit
+
+{{ url }}
+
+Regards,
+The LTPDA Repository Admin at {{ request.host }}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ltpdarepo/templates/users/reset.html	Sun Aug 21 18:17:27 2011 +0200
@@ -0,0 +1,16 @@
+{% import 'forms.html' as forms %}
+{% extends "layout.html" %}
+{% block title %}User {{ user.username }}{% endblock%}
+{% block body %}
+<h2>Reset password for user &#x00AB;{{ user.username }}&#x00BB;</h2>
+<p class="discrete">A password reset token will be sent to the user email address.</p>
+<form action="" method="post" enctype="multipart/form-data" >
+  <fieldset>
+    {% for field in form %}
+    {{ forms.render_form_field(field) }}
+    {% endfor %}
+    <input id="ok" name="ok" type="submit" value="ok"></input>
+    <input id="cancel" name="cancel" type="submit" value="cancel"></input>
+  </fieldset>
+</form>
+{% endblock %}
--- a/src/ltpdarepo/templates/users/view.html	Sun Aug 21 18:17:27 2011 +0200
+++ b/src/ltpdarepo/templates/users/view.html	Sun Aug 21 18:17:27 2011 +0200
@@ -36,5 +36,6 @@
 <ul class="actions">
   <li><a href="{{ url_for('manage.users.edit', username=username) }}">Edit</a></li>
   <li><a href="{{ url_for('manage.users.drop', username=username) }}">Drop</a></li>
+  <li><a href="{{ url_for('manage.users.reset', username=username) }}">Reset password</a></li>
 </ul>
 {% endblock %}
--- a/src/ltpdarepo/user.py	Sun Aug 21 18:17:27 2011 +0200
+++ b/src/ltpdarepo/user.py	Sun Aug 21 18:17:27 2011 +0200
@@ -111,6 +111,17 @@
 
         g.db.commit()
 
+    def reset(self):
+        curs = g.db.cursor()
+
+        curs.execute("""SELECT Host FROM mysql.user WHERE User=%s""", self.username)
+        hosts = [row[0] for row in curs.fetchall()]
+        for host in hosts:
+            curs.execute("""SET PASSWORD FOR %s@%s = %s""",
+                         (self.username, host, INVALIDPASSWORD))
+
+        g.db.commit()
+
     def save(self):
         curs = g.db.cursor()
 
--- a/src/ltpdarepo/views/profile.py	Sun Aug 21 18:17:27 2011 +0200
+++ b/src/ltpdarepo/views/profile.py	Sun Aug 21 18:17:27 2011 +0200
@@ -79,6 +79,29 @@
     return render_template('users/activate.html', username=username, form=form)
 
 
+@app.route('/<username>/reset', methods=['GET', 'POST'])
+def reset(username):
+    user = User.load(username)
+    if user is None:
+        # not found
+        abort(404)
+
+    # validate token
+    _validate_request(request, username)
+
+    form = IPassword()
+    if request.method == 'POST' and form.validate():
+        # set password
+        user.passwd(form.password.data)
+        flash('Password set.')
+        # login if not already logged in
+        if 'username' not in session:
+            session['username'] = username
+        return redirect(url_for('index'))
+
+    return render_template('users/password.html', username=username, form=form)
+
+
 @app.route('/<username>/password', methods=('GET', 'POST'))
 @require('user')
 def password(username):
--- a/src/ltpdarepo/views/users.py	Sun Aug 21 18:17:27 2011 +0200
+++ b/src/ltpdarepo/views/users.py	Sun Aug 21 18:17:27 2011 +0200
@@ -91,6 +91,39 @@
     return render_template('users/create.html', form=form)
 
 
+@app.route('/<username>/reset', methods=['GET', 'POST'])
+@require('admin')
+def reset(username):
+    user = User.load(username)
+    if user is None:
+        # not found
+        abort(404)
+    # use an empty form for CSRF protection
+    form = Form()
+    if request.method == 'POST' and form.validate():
+        if request.form.get('ok'):
+            user.reset()
+
+            # generate password reset token
+            token = Signer().dumps(user.username)
+
+            # activation url
+            url = url_for('user.reset', username=user.username, token=token, _external=True)
+
+            # send activation token
+            mailer = Mailer()
+            message = MIMEText(render_template('mail/reset.txt', user=user, url=url))
+            message['Subject'] = 'LTPDA Repository password reset'
+            message['From'] = mailer.admin_email_addr
+            message['To'] = user.emailaddr
+            mailer.send(message)
+
+            flash('Password reset token: <a href="%s"><tt>%s</tt></a>' % (url, token))
+
+        return redirect(url_for('manage.users.index'))
+    return render_template('users/reset.html', form=form, user=user)
+
+
 @app.route('/<username>/drop', methods=('GET', 'POST'))
 @require('admin')
 def drop(username):