From f085f8438b7408ee67be9d779da551f2cf07b734 Mon Sep 17 00:00:00 2001 From: Gert Date: Mon, 20 Apr 2015 16:01:20 +0200 Subject: [PATCH] forgot password [fixes #28] --- classes/controller.php | 114 ++++++++++++++++++++ pages/admin/forgot.md | 11 ++ pages/admin/reset.md | 17 +++ themes/grav/templates/email/base.html.twig | 11 ++ themes/grav/templates/email/reset.html.twig | 12 +++ themes/grav/templates/forgot.html.twig | 25 +++++ themes/grav/templates/login.html.twig | 2 +- themes/grav/templates/reset.html.twig | 29 +++++ 8 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 pages/admin/forgot.md create mode 100644 pages/admin/reset.md create mode 100644 themes/grav/templates/email/base.html.twig create mode 100644 themes/grav/templates/email/reset.html.twig create mode 100644 themes/grav/templates/forgot.html.twig create mode 100644 themes/grav/templates/reset.html.twig diff --git a/classes/controller.php b/classes/controller.php index 8502d46b..2e70bc1f 100644 --- a/classes/controller.php +++ b/classes/controller.php @@ -139,6 +139,120 @@ class AdminController return true; } + protected function taskForgot() + { + $data = $this->post; + + $username = isset($data['username']) ? $data['username'] : ''; + $user = !empty($username) ? User::load($username) : null; + + if (!isset($this->grav['Email'])) { + $this->admin->setMessage('Cannot reset password. This site is not configured to send emails.'); + $this->setRedirect('/'); + return true; + } + + if (!$user || !$user->exists()) { + $this->admin->setMessage('User with username \'' . $username . '\' does not exist.'); + $this->setRedirect('/forgot'); + return true; + } + + if (empty($user->email)) { + $this->admin->setMessage('Cannot reset password for \'' . $username . '\', no email address is set.'); + $this->setRedirect('/forgot'); + return true; + } + + $token = md5(uniqid(mt_rand(), true)); + $expire = time() + 604800; // next week + + $user->reset = $token . '::' . $expire; + $user->save(); + + $author = $this->grav['config']->get('site.author.name', ''); + $fullname = $user->fullname ?: $username; + $reset_link = rtrim($this->grav['uri']->rootUrl(true), '/') . '/' . trim($this->admin->base, '/') . '/reset/task:reset/user:' . $username . '/token:' . $token; + + $from = $this->grav['config']->get('site.author.email', 'noreply@getgrav.org'); + $to = $user->email; + $subject = $this->grav['config']->get('site.title', 'Website') . ' password reset'; + $body = $this->grav['twig']->processString('{% include "email/reset.html.twig" %}', [ + 'name' => $fullname, + 'author' => $author, + 'reset_link' =>$reset_link + ]); + + $message = $this->grav['Email']->message($subject, $body, 'text/html') + ->setFrom($from) + ->setTo($to); + + $sent = $this->grav['Email']->send($message); + + if ($sent < 1) { + $this->admin->setMessage('Failed to email instructions, please try again later.'); + } else { + $this->admin->setMessage('Instructions to reset your password have been sent by email.'); + } + + $this->setRedirect('/'); + return true; + } + + public function taskReset() + { + $data = $this->post; + + if (isset($data['password'])) { + + $username = isset($data['username']) ? $data['username'] : null; + $user = !empty($username) ? User::load($username) : null; + $password = isset($data['password']) ? $data['password'] : null; + $token = isset($data['token']) ? $data['token'] : null; + + if (!empty($user) && $user->exists() && !empty($user->reset)) { + list($good_token, $expire) = explode('::', $user->reset); + + if ($good_token === $token) { + + if (time() > $expire) { + $this->admin->setMessage('Reset link has expired, please try again.'); + $this->setRedirect('/forgot'); + return true; + } + + + unset($user->hashed_password); + unset($user->reset); + $user->password = $password; + $user->save(); + + $this->admin->setMessage('Password has been reset.'); + $this->setRedirect('/'); + return true; + } + } + + $this->admin->setMessage('Invalid reset link used, please try again.'); + $this->setRedirect('/forgot'); + return true; + + } else { + $user = $this->grav['uri']->param('user'); + $token = $this->grav['uri']->param('token'); + + if (empty($user) || empty($token)) { + $this->admin->setMessage('Invalid reset link used, please try again.'); + $this->setRedirect('/forgot'); + return true; + } + + $this->admin->forgot = [ 'username' => $user, 'token' => $token ]; + } + + return true; + } + protected function taskClearCache() { $results = Cache::clearCache('standard'); diff --git a/pages/admin/forgot.md b/pages/admin/forgot.md new file mode 100644 index 00000000..85a816e8 --- /dev/null +++ b/pages/admin/forgot.md @@ -0,0 +1,11 @@ +--- +title: Forgot password + +form: + fields: + - name: username + type: text + placeholder: Username + autofocus: true +--- + diff --git a/pages/admin/reset.md b/pages/admin/reset.md new file mode 100644 index 00000000..092b4e52 --- /dev/null +++ b/pages/admin/reset.md @@ -0,0 +1,17 @@ +--- +title: Reset password + +form: + fields: + - name: username + type: text + placeholder: Username + readonly: true + - name: password + type: password + placeholder: Password + autofocus: true + - name: token + type: hidden +--- + diff --git a/themes/grav/templates/email/base.html.twig b/themes/grav/templates/email/base.html.twig new file mode 100644 index 00000000..31c27b13 --- /dev/null +++ b/themes/grav/templates/email/base.html.twig @@ -0,0 +1,11 @@ + + + + {% block head %} + + {% endblock %} + + + {% block content %}{% endblock %} + + diff --git a/themes/grav/templates/email/reset.html.twig b/themes/grav/templates/email/reset.html.twig new file mode 100644 index 00000000..42852102 --- /dev/null +++ b/themes/grav/templates/email/reset.html.twig @@ -0,0 +1,12 @@ +{% extends 'email/base.html.twig' %} + +{% block content %} +

Dear {{ name }},

+ +

To reset your password, follow this link or copy the following URL into your browser's address bar: {{ reset_link }}.

+ +

+ Kind regards
+ {{ author }} +

+{% endblock %} diff --git a/themes/grav/templates/forgot.html.twig b/themes/grav/templates/forgot.html.twig new file mode 100644 index 00000000..eae4dbcc --- /dev/null +++ b/themes/grav/templates/forgot.html.twig @@ -0,0 +1,25 @@ +{% extends 'partials/base.html.twig' %} + +{% block page %} +
+

+ Grav forgot password +

+ + {% include 'partials/messages.html.twig' %} + +
+ {% for field in page.header.form.fields %} + {% if field.type %} +
+ {% include ["forms/fields/#{field.type}/#{field.type}.html.twig", 'forms/fields/text/text.html.twig'] %} +
+ {% endif %} + {% endfor %} +
+ +
+
+
+{% endblock %} + diff --git a/themes/grav/templates/login.html.twig b/themes/grav/templates/login.html.twig index 5da794ba..d5e2be6f 100644 --- a/themes/grav/templates/login.html.twig +++ b/themes/grav/templates/login.html.twig @@ -17,7 +17,7 @@ {% endif %} {% endfor %}
- + Forgot
diff --git a/themes/grav/templates/reset.html.twig b/themes/grav/templates/reset.html.twig new file mode 100644 index 00000000..096ab0bc --- /dev/null +++ b/themes/grav/templates/reset.html.twig @@ -0,0 +1,29 @@ +{% extends 'partials/base.html.twig' %} + +{% block page %} +
+

+ Reset Grav password +

+ + {% include 'partials/messages.html.twig' %} + +
+ + {% for field in page.header.form.fields %} + {% set value = attribute(admin.forgot, field.name) is defined ? attribute(admin.forgot, field.name) : null %} + + {% if field.type %} +
+ {% include ["forms/fields/#{field.type}/#{field.type}.html.twig", 'forms/fields/text/text.html.twig'] %} +
+ {% endif %} + {% endfor %} + +
+ +
+
+
+{% endblock %} +