]> git.ipfire.org Git - ipfire.org.git/commitdiff
people: Implement initiation of password reset
authorMichael Tremer <michael.tremer@ipfire.org>
Thu, 31 Oct 2019 16:17:47 +0000 (16:17 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Thu, 31 Oct 2019 16:18:12 +0000 (16:18 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
src/backend/accounts.py
src/templates/auth/login.html
src/templates/auth/messages/password-reset.txt [new file with mode: 0644]
src/templates/auth/password-reset-initiation.html [new file with mode: 0644]
src/templates/auth/password-reset-successful.html [new file with mode: 0644]
src/web/__init__.py
src/web/auth.py

index 7fbf75d048b34569c9d6c9ffa4b4739a8d65a94f..6d4c8e5f0bdff4b45e116cabdab49ac87c66c4a5 100644 (file)
@@ -113,6 +113,8 @@ templates_auth_DATA = \
        src/templates/auth/activate.html \
        src/templates/auth/activated.html \
        src/templates/auth/login.html \
+       src/templates/auth/password-reset-initiation.html \
+       src/templates/auth/password-reset-successful.html \
        src/templates/auth/register.html \
        src/templates/auth/register-spam.html \
        src/templates/auth/register-success.html
@@ -120,6 +122,7 @@ templates_auth_DATA = \
 templates_authdir = $(templatesdir)/auth
 
 templates_auth_messages_DATA = \
+       src/templates/auth/messages/password-reset.txt \
        src/templates/auth/messages/register.txt
 
 templates_auth_messagesdir = $(templates_authdir)/messages
index 7cd4d726fc6c1a57e9bdde5103c6fd591fd352b0..341709e2125e02cdbcd6174c6548fdb5dac02bc3 100644 (file)
@@ -603,6 +603,16 @@ class Account(Object):
                        self.first_name, self.last_name,
                ))
 
+       def reset_password(self, address=None):
+               reset_code = util.random_string(64)
+
+               self.db.execute("INSERT INTO account_password_resets(uid, reset_code, address) \
+                       VALUES(%s, %s, %s)", self.uid, reset_code, address)
+
+               # Send a password reset email
+               self.backend.messages.send_template("auth/messages/password-reset",
+                       recipients=[self.email], priority=100, account=self, reset_code=reset_code)
+
        def is_admin(self):
                return self.is_member_of_group("sudo")
 
index 23bdb025f0a62c0061e005df0a045cff2ebace61..897a1955d9194583ccadc1fefd0b1878ca93b3e0 100644 (file)
@@ -37,7 +37,7 @@
                                        </form>
 
                                        <p class="card-text text-center small mt-3">
-                                               <a class="text-muted" href="//people.ipfire.org/password-reset{% if incorrect %}?uid={{ username }}{% end %}">
+                                               <a class="text-muted" href="//people.ipfire.org/password-reset{% if incorrect %}?username={{ username }}{% end %}">
                                                        {{ _("Did you forget your password?") }}
                                                </a>
                                        </p>
diff --git a/src/templates/auth/messages/password-reset.txt b/src/templates/auth/messages/password-reset.txt
new file mode 100644 (file)
index 0000000..26c9119
--- /dev/null
@@ -0,0 +1,11 @@
+From: IPFire Project <no-reply@ipfire.org>
+To: {{ account }} <{{ account.email }}>
+Subject: {{ _("Please Reset Your Password") }}
+
+{{ _("Hello %s!") % account.first_name }}
+
+{{ _("You, or somebody else on your behalf, has requested to change your password.") }} {{ _("If this was not you, please notify a team member.") }}
+
+{{ _("To reset your password, please click on this link:") }}
+
+  https://people.ipfire.org/password-reset/{{ account.uid }}/{{ reset_code }}
diff --git a/src/templates/auth/password-reset-initiation.html b/src/templates/auth/password-reset-initiation.html
new file mode 100644 (file)
index 0000000..c1306d7
--- /dev/null
@@ -0,0 +1,29 @@
+{% extends "../base.html" %}
+
+{% block title %}{{ _("Password Reset") }}{% end block %}
+
+{% block content %}
+       <div class="row justify-content-center my-5">
+               <div class="col col-md-4">
+                       <div class="card">
+                               <div class="card-body">
+                                       <h5 class="card-title text-center mb-4">{{ _("Password Reset") }}</h5>
+
+                                       <form action="" method="POST">
+                                               {% raw xsrf_form_html() %}
+
+                                               <div class="form-group">
+                                                       <input type="text" class="form-control form-control-lg"
+                                                               name="username" placeholder="{{ _("Username") }}"
+                                                               value="{{ username or "" }}" required autofocus>
+                                               </div>
+
+                                               <button type="submit" class="btn btn-primary btn-block">
+                                                       {{ _("Reset Password") }}
+                                               </button>
+                                       </form>
+                               </div>
+                       </div>
+               </div>
+       </div>
+{% end block %}
diff --git a/src/templates/auth/password-reset-successful.html b/src/templates/auth/password-reset-successful.html
new file mode 100644 (file)
index 0000000..ff45d61
--- /dev/null
@@ -0,0 +1,19 @@
+{% extends "../base.html" %}
+
+{% block title %}{{ _("Password Reset") }}{% end block %}
+
+{% block content %}
+       <div class="row justify-content-center my-5">
+               <div class="col-12 col-md-6">
+                       <div class="card bg-success text-white p-md-5">
+                               <div class="card-body text-center">
+                                       <span class="fas fa-lock fa-5x my-4"></span>
+
+                                       <p class="lead">
+                                               {{ _("You will shortly receive an email with instructions on how to reset your password.") }}
+                                       </p>
+                               </div>
+                       </div>
+               </div>
+       </div>
+{% end block %}
index d1712816083253cc511b216465d2505e7570a868..4acb0dfc325e797d04a0a51932d3a82436448a5d 100644 (file)
@@ -287,6 +287,9 @@ class Application(tornado.web.Application):
                        # Single-Sign-On for Discourse
                        (r"/sso/discourse", people.SSODiscourse),
 
+                       # Password Reset
+                       (r"/password-reset", auth.PasswordResetInitiationHandler),
+
                        # API
                        (r"/api/check/uid", auth.APICheckUID),
                ]  + authentication_handlers)
index cb980666341ebd6e71b940c6a6d8fcdbdcf507ab..e3fb5f13fe3e3de7e4509b913e26bdae74973c4f 100644 (file)
@@ -147,6 +147,25 @@ class ActivateHandler(AuthenticationMixin, base.BaseHandler):
                self.render("auth/activated.html", account=account)
 
 
+class PasswordResetInitiationHandler(AuthenticationMixin, base.BaseHandler):
+       def get(self):
+               username = self.get_argument("username", None)
+
+               self.render("auth/password-reset-initiation.html", username=username)
+
+       @base.ratelimit(minutes=60, requests=5)
+       def post(self):
+               username = self.get_argument("username")
+
+               # Fetch account and submit password reset
+               account = self.backend.accounts.get_by_uid(username)
+               if account:
+                       with self.db.transaction():
+                               account.reset_password()
+
+               self.render("auth/password-reset-successful.html")
+
+
 class APICheckUID(base.APIHandler):
        @base.ratelimit(minutes=10, requests=100)
        def get(self):