2023-02-06 15:44:28 +01:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* FuzeWorks Authentication Plugin.
|
|
|
|
*
|
|
|
|
* The FuzeWorks PHP FrameWork
|
|
|
|
*
|
|
|
|
* Copyright (C) 2013 - 2023 i15
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
|
|
* copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
* SOFTWARE.
|
|
|
|
*
|
|
|
|
* @author i15
|
|
|
|
* @copyright Copyright (C) 2013 - 2023, i15. (https://i15.nl)
|
|
|
|
* @license https://opensource.org/licenses/MIT MIT License
|
|
|
|
*
|
|
|
|
* @since Version 1.3.0
|
|
|
|
*
|
|
|
|
* @version Version 1.3.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace Application\View;
|
|
|
|
use Application\Controller\AuthenticationController;
|
|
|
|
use FuzeWorks\Authentication\AuthenticationPlugin;
|
|
|
|
use FuzeWorks\Authentication\Events\GetRegisterFieldsEvent;
|
|
|
|
use FuzeWorks\Authentication\Exceptions\LoginErrorException;
|
|
|
|
use FuzeWorks\Authentication\Exceptions\LoginWarningException;
|
|
|
|
use FuzeWorks\Authentication\Exceptions\RegisterErrorException;
|
|
|
|
use FuzeWorks\Authentication\Exceptions\RegisterWarningException;
|
|
|
|
use FuzeWorks\ConfigORM\ConfigORM;
|
|
|
|
use FuzeWorks\Controller;
|
|
|
|
use FuzeWorks\Event\LayoutLoadEvent;
|
|
|
|
use FuzeWorks\Events;
|
|
|
|
use FuzeWorks\Factory;
|
|
|
|
use FuzeWorks\Forms\Fields\CheckboxField;
|
|
|
|
use FuzeWorks\Forms\Fields\DecoratorField;
|
|
|
|
use FuzeWorks\Forms\Fields\EmailField;
|
|
|
|
use FuzeWorks\Forms\Fields\PasswordField;
|
|
|
|
use FuzeWorks\Forms\Fields\SubmitField;
|
|
|
|
use FuzeWorks\Forms\Fields\TextField;
|
|
|
|
use FuzeWorks\Forms\Form;
|
|
|
|
use FuzeWorks\Forms\Forms;
|
|
|
|
use FuzeWorks\Forms\Method;
|
|
|
|
use FuzeWorks\Layout;
|
|
|
|
use FuzeWorks\WebView;
|
|
|
|
use OTPHP\TOTP;
|
|
|
|
|
|
|
|
class AuthenticationHtmlView extends WebView
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var AuthenticationController
|
|
|
|
*/
|
|
|
|
protected Controller $controller;
|
|
|
|
protected Layout $layouts;
|
|
|
|
protected AuthenticationPlugin $plugin;
|
|
|
|
protected Forms $forms;
|
|
|
|
|
|
|
|
public function __construct()
|
|
|
|
{
|
|
|
|
parent::__construct();
|
|
|
|
$this->layouts = Factory::getInstance("layouts");
|
|
|
|
|
|
|
|
// Get the authentication config
|
|
|
|
$this->plugin = $this->plugins->get('auth');
|
|
|
|
$this->forms = $this->libraries->get("forms");
|
|
|
|
|
|
|
|
// And assign all related layout variables
|
|
|
|
$this->assignLayoutVariables();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function login()
|
|
|
|
{
|
|
|
|
// If the current user is already logged in, redirect
|
|
|
|
$location = $this->input->get("location");
|
|
|
|
$redirect = is_null($location) ? "/" : $location;
|
|
|
|
if ($this->controller->session->user->id !== "0")
|
2023-02-14 15:08:20 +01:00
|
|
|
$this->output->setLocation($redirect);
|
2023-02-06 15:44:28 +01:00
|
|
|
|
|
|
|
// Prepare form
|
|
|
|
$form = $this->forms->getCachedForm(function (Form $form) {
|
|
|
|
// First prepare the identifier field
|
|
|
|
$field1 = new TextField("identifier");
|
|
|
|
$field1->setLabel("Username")->placeholder("Username or email");
|
|
|
|
$form->field($field1);
|
|
|
|
|
|
|
|
// Then prepare the password field
|
|
|
|
$field2 = new PasswordField("password");
|
|
|
|
$field2->setLabel("Password")->placeholder("Password");
|
|
|
|
$form->field($field2);
|
|
|
|
|
|
|
|
// Forgot password link
|
|
|
|
if ($this->plugin->config->forgot_password_enabled)
|
|
|
|
{
|
|
|
|
$url = $this->plugin->getAuthenticationURL();
|
|
|
|
$forgotHtml = "<a href='$url/forgot_password'>Forgot password?</a>";
|
|
|
|
$forgot = new DecoratorField("forgot", $forgotHtml);
|
|
|
|
$form->field($forgot);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Then prepare remember me field
|
|
|
|
$field3 = new CheckboxField("remember");
|
|
|
|
$field3->setLabel("Remember me");
|
|
|
|
$form->field($field3);
|
|
|
|
|
|
|
|
// Submit field
|
|
|
|
$field4 = new SubmitField("submit");
|
|
|
|
$field4->setButtonText("Login")->addClass("btn-success")->addClass("btn-block");
|
|
|
|
$form->field($field4);
|
|
|
|
|
|
|
|
return $form;
|
|
|
|
}, "AuthLoginForm", "Login");
|
|
|
|
|
|
|
|
// Validate the form
|
|
|
|
if ($form->validate()) {
|
|
|
|
// Send data to the controller
|
|
|
|
try {
|
|
|
|
// Collect context
|
|
|
|
$context = [
|
|
|
|
'userAgent' => $this->input->userAgent(),
|
|
|
|
'remoteAddr' => $this->input->server("REMOTE_ADDR")
|
|
|
|
];
|
|
|
|
|
|
|
|
// Attempt to login. If it fails, will be handled by exception handlers.
|
|
|
|
$this->controller->login($form->identifier->getValue(), $form->password->getValue(), $form->remember->getValue(), $context);
|
|
|
|
|
|
|
|
// And redirect the user
|
2023-02-14 15:08:20 +01:00
|
|
|
$this->output->setLocation($redirect);
|
2023-02-06 15:44:28 +01:00
|
|
|
} catch (LoginErrorException $e) {
|
|
|
|
$form->invalidate(true);
|
|
|
|
$form->addFormError($e->getMessage());
|
|
|
|
} catch (LoginWarningException $e) {
|
|
|
|
$form->invalidate(true);
|
|
|
|
$form->addFormWarning($e->getMessage());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Assign the form and display the page
|
|
|
|
$this->layouts->assign("form", $form);
|
|
|
|
$this->layouts->display("pages/auth_form_page");
|
|
|
|
}
|
|
|
|
|
|
|
|
public function logout()
|
|
|
|
{
|
|
|
|
$this->controller->logout();
|
|
|
|
$location = $this->input->get("location");
|
|
|
|
$redirect = is_null($location) ? "/" : $location;
|
2023-02-14 15:08:20 +01:00
|
|
|
$this->output->setLocation($redirect);
|
2023-02-06 15:44:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function register()
|
|
|
|
{
|
|
|
|
// First check if the registration is enabled
|
|
|
|
if (!$this->plugin->getConfig()->get("register_enabled"))
|
2023-02-14 15:08:20 +01:00
|
|
|
$this->output->setLocation("/");
|
2023-02-06 15:44:28 +01:00
|
|
|
|
|
|
|
if ($this->controller->session->user->id !== "0")
|
2023-02-14 15:08:20 +01:00
|
|
|
$this->output->setLocation("/");
|
2023-02-06 15:44:28 +01:00
|
|
|
|
|
|
|
// Prepare form
|
|
|
|
$form = $this->forms->getCachedForm(function (Form $form) {
|
|
|
|
// First prepare the identifier field
|
|
|
|
$field1 = new EmailField("identifier");
|
|
|
|
$field1->setLabel("Email address")
|
|
|
|
->placeholder("Email");
|
|
|
|
$form->field($field1);
|
|
|
|
|
|
|
|
// Add additional fields through GetRegisterFieldsEvent
|
|
|
|
/** @var GetRegisterFieldsEvent $event */
|
|
|
|
$event = Events::fireEvent(new GetRegisterFieldsEvent());
|
|
|
|
foreach ($event->getFields() as $field) {
|
|
|
|
$field->setNote("eventField");
|
|
|
|
$form->field($field);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add password fields
|
|
|
|
$field2 = new PasswordField("password1");
|
|
|
|
$field3 = new PasswordField("password2");
|
|
|
|
$field2->setLabel("Password")
|
|
|
|
->placeholder("Password")
|
|
|
|
->minLength($this->controller->authConfig->get("password_min_length"));
|
|
|
|
$field3->setLabel("Repeat password")
|
|
|
|
->placeholder("Password")
|
|
|
|
->minLength($this->controller->authConfig->get("password_min_length"));
|
|
|
|
$form->field($field2);
|
|
|
|
$form->field($field3);
|
|
|
|
|
|
|
|
// Terms and conditions field
|
|
|
|
$field4 = new CheckboxField("terms");
|
|
|
|
$field4->setLabel("Accept Terms and Conditions");
|
|
|
|
$field4->required("You must accept the terms and conditions.");
|
|
|
|
$form->field($field4);
|
|
|
|
|
|
|
|
// Submit field
|
|
|
|
$field5 = new SubmitField("submit");
|
|
|
|
$field5->setButtonText("Register")->addClass("btn-success")->addClass("btn-block");
|
|
|
|
$form->field($field5);
|
|
|
|
|
|
|
|
// And add a password equivalency check
|
|
|
|
$form->condition([AuthenticationController::class, "registerFormCondition"]);
|
|
|
|
|
|
|
|
return $form;
|
|
|
|
}, "AuthRegisterForm", "Register");
|
|
|
|
|
|
|
|
// After validation, send data to controller
|
|
|
|
if ($form->validate()) {
|
|
|
|
// Prepare variables
|
|
|
|
$identifier = $form->getField("identifier")->getValue();
|
|
|
|
$password = $form->getField("password1")->getValue();
|
|
|
|
$properties = [];
|
|
|
|
foreach ($form->getFields() as $field)
|
|
|
|
if ($field->getNote() === "eventField")
|
|
|
|
$properties[$field->getName()] = $field->getValue();
|
|
|
|
|
|
|
|
// Then forward the form to the controller
|
|
|
|
try {
|
|
|
|
$this->controller->register($identifier, $password, [], $properties);
|
|
|
|
} catch (RegisterErrorException $e) {
|
|
|
|
$form->invalidate(true);
|
|
|
|
$form->addFormError($e->getMessage());
|
|
|
|
} catch (RegisterWarningException $e) {
|
|
|
|
$form->invalidate(true);
|
|
|
|
$form->addFormWarning($e->getMessage());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->layouts->assign("form", $form);
|
|
|
|
$this->layouts->display("pages/auth_form_page");
|
|
|
|
}
|
|
|
|
|
|
|
|
public function resend_verify_email()
|
|
|
|
{
|
|
|
|
// Get token
|
|
|
|
$token = $this->input->get("t");
|
|
|
|
|
|
|
|
// If the token is empty, report so
|
|
|
|
if (empty($token))
|
2023-02-14 15:08:20 +01:00
|
|
|
$this->output->setLocation("/");
|
2023-02-06 15:44:28 +01:00
|
|
|
|
|
|
|
// Generate basic form
|
|
|
|
$form = new Form("ResendVerifyEmailForm", "Resend verification email");
|
|
|
|
$form->method(Method::GET);
|
|
|
|
$form->validate();
|
|
|
|
|
|
|
|
// Extract email
|
|
|
|
$email = base64_decode($token);
|
|
|
|
|
|
|
|
// Resend verification email using the controller
|
|
|
|
try {
|
|
|
|
$this->controller->resendVerifyEmail($email);
|
|
|
|
|
|
|
|
return "A new verification email has been sent to your email address.";
|
|
|
|
} catch (LoginErrorException $e) {
|
|
|
|
$form->invalidate(true);
|
|
|
|
$form->addFormError($e->getMessage());
|
|
|
|
} catch (LoginWarningException $e) {
|
|
|
|
$form->invalidate(true);
|
|
|
|
$form->addFormWarning($e->getMessage());
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->layouts->assign("form", $form);
|
|
|
|
$this->layouts->display("pages/auth_form_page");
|
|
|
|
}
|
|
|
|
|
|
|
|
public function verify(): string
|
|
|
|
{
|
|
|
|
// Fetch token
|
|
|
|
$token = $this->input->get("t");
|
|
|
|
|
|
|
|
// If the token is empty, report so
|
|
|
|
if (is_null($token))
|
|
|
|
return "No token provided.";
|
|
|
|
|
|
|
|
// Verify with controller
|
|
|
|
$user = $this->controller->verifyEmail($token);
|
|
|
|
if (is_null($user))
|
|
|
|
return "Token invalid";
|
|
|
|
|
|
|
|
// If no password has been set, redirect the user to set a password
|
|
|
|
if (is_null($user->password))
|
|
|
|
{
|
|
|
|
$token = $this->controller->createResetToken($user);
|
2023-02-14 15:08:20 +01:00
|
|
|
$this->output->setLocation($this->plugin->getAuthenticationURL() . "/reset_password?t=" . $token);
|
2023-02-06 15:44:28 +01:00
|
|
|
}
|
|
|
|
else
|
2023-02-14 15:08:20 +01:00
|
|
|
$this->output->setLocation($this->plugin->getAuthenticationURL());
|
2023-02-06 15:44:28 +01:00
|
|
|
|
|
|
|
return "Token verified";
|
|
|
|
}
|
|
|
|
|
|
|
|
public function forgot_password()
|
|
|
|
{
|
|
|
|
// If user is already logged in, redirect to homepage
|
|
|
|
if ($this->controller->session->user->id !== "0")
|
2023-02-14 15:08:20 +01:00
|
|
|
$this->output->setLocation("/");
|
2023-02-06 15:44:28 +01:00
|
|
|
|
|
|
|
// Check if forgot password is disabled
|
|
|
|
if (!$this->plugin->getConfig()->get("forgot_password_enabled"))
|
2023-02-14 15:08:20 +01:00
|
|
|
$this->output->setLocation("/");
|
2023-02-06 15:44:28 +01:00
|
|
|
|
|
|
|
// Prepare form
|
|
|
|
$form = $this->forms->getCachedForm(function (Form $form){
|
|
|
|
// First prepare the identifier field
|
|
|
|
$field1 = new EmailField("identifier");
|
|
|
|
$field1->setLabel("Email address")
|
|
|
|
->placeholder("Email");
|
|
|
|
$form->field($field1);
|
|
|
|
|
|
|
|
// Submit field
|
|
|
|
$field4 = new SubmitField("submit");
|
|
|
|
$field4->setButtonText("Send Account Recovery Email");
|
|
|
|
$form->field($field4);
|
|
|
|
|
|
|
|
return $form;
|
|
|
|
}, "AuthForgotPasswordForm", "Forgot password");
|
|
|
|
|
|
|
|
if ($form->validate())
|
|
|
|
{
|
|
|
|
// Fetch identifier
|
|
|
|
$identifier = $form->getField("identifier")->getValue();
|
|
|
|
|
|
|
|
// Request a password reset
|
|
|
|
try {
|
|
|
|
$this->controller->forgotPassword($identifier);
|
|
|
|
} catch (LoginErrorException $e) {
|
|
|
|
$form->invalidate(true);
|
|
|
|
$form->addFormError($e->getMessage());
|
|
|
|
} catch (LoginWarningException $e) {
|
|
|
|
$form->invalidate(true);
|
|
|
|
$form->addFormWarning($e->getMessage());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->layouts->assign("form", $form);
|
|
|
|
$this->layouts->display("pages/auth_form_page");
|
|
|
|
}
|
|
|
|
|
|
|
|
public function reset_password()
|
|
|
|
{
|
|
|
|
$form = $this->forms->getCachedForm(function (Form $form){
|
|
|
|
// Add password fields
|
|
|
|
$field2 = new PasswordField("password1");
|
|
|
|
$field3 = new PasswordField("password2");
|
|
|
|
$field2->setLabel("Password")
|
|
|
|
->placeholder("Password")
|
|
|
|
->minLength($this->controller->authConfig->get("password_min_length"));
|
|
|
|
$field3->setLabel("Repeat password")
|
|
|
|
->placeholder("Password")
|
|
|
|
->minLength($this->controller->authConfig->get("password_min_length"));
|
|
|
|
$form->field($field2);
|
|
|
|
$form->field($field3);
|
|
|
|
|
|
|
|
// Submit field
|
|
|
|
$field4 = new SubmitField("submit");
|
|
|
|
$field4->setButtonText("Reset Password");
|
|
|
|
$form->field($field4);
|
|
|
|
|
|
|
|
// And add a password equivalency check
|
|
|
|
$form->condition([AuthenticationController::class, "registerFormCondition"]);
|
|
|
|
|
|
|
|
return $form;
|
|
|
|
}, "AuthResetPasswordForm", "Reset Password");
|
|
|
|
|
|
|
|
if ($form->validate())
|
|
|
|
{
|
|
|
|
// Fetch token
|
|
|
|
$token = $this->input->get("t");
|
|
|
|
$password = $form->getField("password1")->getValue();
|
|
|
|
|
|
|
|
// Reset by controller
|
|
|
|
try {
|
|
|
|
$this->controller->resetPassword($token, $password);
|
2023-02-14 15:08:20 +01:00
|
|
|
$this->output->setLocation($this->plugin->getAuthenticationURL() . "/login");
|
2023-02-06 15:44:28 +01:00
|
|
|
} catch (LoginErrorException $e) {
|
|
|
|
$form->invalidate(true);
|
|
|
|
$form->addFormError($e->getMessage());
|
|
|
|
} catch (LoginWarningException $e) {
|
|
|
|
$form->invalidate(true);
|
|
|
|
$form->addFormWarning($e->getMessage());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->layouts->assign("form", $form);
|
|
|
|
$this->layouts->display("pages/auth_form_page");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Assign all view related variables to the layouts
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
private function assignLayoutVariables(): void
|
|
|
|
{
|
|
|
|
$config = $this->plugin->getConfig();
|
|
|
|
$this->layouts->assign("register_enabled", $config->get("register_enabled"));
|
|
|
|
$this->layouts->assign("forgot_password_enabled", $config->get("forgot_password_enabled"));
|
|
|
|
$this->layouts->assign("auth_url", $this->plugin->getAuthenticationURL());
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|