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
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
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")
</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>
--- /dev/null
+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 }}
--- /dev/null
+{% 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 %}
--- /dev/null
+{% 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 %}
# 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)
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):