Initial commit
This commit is contained in:
commit
04c84ca83b
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
.gitattributes export-ignore
|
||||||
|
.gitignore export-ignore
|
||||||
|
.drone.yml export-ignore
|
||||||
|
test/ export-ignore
|
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
composer.lock
|
||||||
|
composer.phar
|
||||||
|
.idea/
|
||||||
|
build/
|
||||||
|
test/temp/
|
||||||
|
vendor/
|
||||||
|
test/.phpunit.result.cache
|
21
LICENSE
Executable file
21
LICENSE
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
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.
|
33
composer.json
Executable file
33
composer.json
Executable file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "fuzeworks/authentication",
|
||||||
|
"license": ["MIT"],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Abel Hoogeveen",
|
||||||
|
"email": "abel@i15.nl"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.1.0",
|
||||||
|
"fuzeworks/core": "~1.3.0",
|
||||||
|
"fuzeworks/mvcr": "~1.3.0",
|
||||||
|
"fuzeworks/database": "~1.3.0",
|
||||||
|
"fuzeworks/objectstorage": "~1.3.0",
|
||||||
|
"fuzeworks/layout": "~1.3.0",
|
||||||
|
"fuzeworks/forms": "~1.3.0",
|
||||||
|
"fuzeworks/mailer-wrapper": "~1.3.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"fuzeworks/webcomponent": "Provides a web-based frontend for authentication"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^9",
|
||||||
|
"fuzeworks/webcomponent": "~1.3.0",
|
||||||
|
"fuzeworks/tracycomponent": "~1.3.0"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"FuzeWorks\\Authentication\\": "src/FuzeWorks/Authentication/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
config.authentication.php
Executable file
58
config.authentication.php
Executable file
@ -0,0 +1,58 @@
|
|||||||
|
<?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
|
||||||
|
*/
|
||||||
|
|
||||||
|
return [
|
||||||
|
// Whether the plugin is enabled
|
||||||
|
'auth_enabled' => true,
|
||||||
|
'auth_url' => "auth",
|
||||||
|
|
||||||
|
// Whether registration is enabled on this website
|
||||||
|
'register_enabled' => true,
|
||||||
|
|
||||||
|
// Whether the user should be able to manually change their passwords
|
||||||
|
'forgot_password_enabled' => true,
|
||||||
|
|
||||||
|
// The database group to use for authentication
|
||||||
|
'database_group' => 'default',
|
||||||
|
|
||||||
|
// Password settings
|
||||||
|
'password_min_length' => 8,
|
||||||
|
'password_min_score' => 3,
|
||||||
|
'password_algorithm' => PASSWORD_DEFAULT,
|
||||||
|
'password_options' => [],
|
||||||
|
|
||||||
|
// Email settings
|
||||||
|
'verifyEmailWithin' => 3600*24*3
|
||||||
|
];
|
485
controller/controller.authentication.php
Executable file
485
controller/controller.authentication.php
Executable file
@ -0,0 +1,485 @@
|
|||||||
|
<?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\Controller;
|
||||||
|
use FuzeWorks\Authentication\Events\LoginEvent;
|
||||||
|
use FuzeWorks\Authentication\Events\RegisterEvent;
|
||||||
|
use FuzeWorks\Authentication\Exceptions\AuthenticationException;
|
||||||
|
use FuzeWorks\Authentication\Exceptions\InputException;
|
||||||
|
use FuzeWorks\Authentication\Exceptions\LoginErrorException;
|
||||||
|
use FuzeWorks\Authentication\Exceptions\LoginWarningException;
|
||||||
|
use FuzeWorks\Authentication\Exceptions\RegisterErrorException;
|
||||||
|
use FuzeWorks\Authentication\Exceptions\RegisterWarningException;
|
||||||
|
use FuzeWorks\Authentication\Model\Session;
|
||||||
|
use FuzeWorks\Authentication\Model\User;
|
||||||
|
use FuzeWorks\ConfigORM\ConfigORM;
|
||||||
|
use FuzeWorks\Controller;
|
||||||
|
use FuzeWorks\Authentication\AuthenticationPlugin;
|
||||||
|
use FuzeWorks\Events;
|
||||||
|
use FuzeWorks\Exception\FactoryException;
|
||||||
|
use FuzeWorks\Exception\LayoutException;
|
||||||
|
use FuzeWorks\Factory;
|
||||||
|
use FuzeWorks\Forms\Form;
|
||||||
|
use FuzeWorks\Layout;
|
||||||
|
use FuzeWorks\Logger;
|
||||||
|
use FuzeWorks\Mailer\PHPMailerWrapper;
|
||||||
|
use FuzeWorks\ObjectStorage\ObjectStorageComponent;
|
||||||
|
use PHPMailer\PHPMailer\Exception;
|
||||||
|
use Psr\SimpleCache\CacheInterface;
|
||||||
|
|
||||||
|
class AuthenticationController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
protected AuthenticationPlugin $plugin;
|
||||||
|
public Session $session;
|
||||||
|
public ConfigORM $authConfig;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->plugin = $this->plugins->get('auth');
|
||||||
|
$this->session = $this->plugin->sessions->start();
|
||||||
|
$this->authConfig = $this->plugin->config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt a login using a designated identifier and password.
|
||||||
|
*
|
||||||
|
* Sessions are automatically extended as they approach their end. Sessions with remember = false will last an hour.
|
||||||
|
* Sessions with remember = true will last 3 months.
|
||||||
|
*
|
||||||
|
* Context data will be saved into the database for security purposes.
|
||||||
|
*
|
||||||
|
* Login may be prevented by the following causes:
|
||||||
|
* - Username and password mismatch, or password is not set.
|
||||||
|
* - The user is already currently logged in the current session.
|
||||||
|
* - The user doesn't exist.
|
||||||
|
* - User is set to inactive.
|
||||||
|
* - Email verification threshold has expired and the user must verify before being allowed to log in.
|
||||||
|
* - The event system cancels login for other reasons.
|
||||||
|
*
|
||||||
|
* @param string $identifier
|
||||||
|
* @param string $password
|
||||||
|
* @param bool $remember
|
||||||
|
* @param array $context
|
||||||
|
* @return Session
|
||||||
|
* @throws LoginErrorException
|
||||||
|
* @throws LoginWarningException
|
||||||
|
*/
|
||||||
|
public function login(string $identifier, string $password, bool $remember = false, array $context = []): Session
|
||||||
|
{
|
||||||
|
/** @var LoginEvent $event */
|
||||||
|
$event = Events::fireEvent(new LoginEvent($identifier, $password, $remember, $context));
|
||||||
|
if ($event->isCancelled())
|
||||||
|
throw new LoginErrorException("Login cancelled by system.");
|
||||||
|
|
||||||
|
// Fetch the user
|
||||||
|
$user = $this->plugin->users->getUserByEmail($event->identifier);
|
||||||
|
foreach ($event->getIdentifierFields() as $field)
|
||||||
|
{
|
||||||
|
if (empty($user))
|
||||||
|
$user = $this->plugin->users->getUsersByProperty($field, $event->identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If user not found, notify
|
||||||
|
if (empty($user))
|
||||||
|
throw new LoginWarningException("The provided username and/or password is invalid.");
|
||||||
|
|
||||||
|
// Verify that the current session is not the same as the supposed login session
|
||||||
|
if ($this->session->user->id === $user->id)
|
||||||
|
throw new LoginWarningException("User is already logged in.");
|
||||||
|
|
||||||
|
// Check if the password is correct
|
||||||
|
if (!password_verify($event->password, $user->password))
|
||||||
|
throw new LoginWarningException("The provided username and/or password is invalid.");
|
||||||
|
|
||||||
|
// Check if the password needs to be updated
|
||||||
|
if (password_needs_rehash($user->password,
|
||||||
|
$this->plugin->config->get("password_algorithm"),
|
||||||
|
$this->plugin->config->get("password_options")))
|
||||||
|
{
|
||||||
|
$this->plugin->users->changePassword($user, $event->password);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the user is still active
|
||||||
|
if (!$user->active)
|
||||||
|
throw new LoginErrorException("User is inactive. Login blocked.");
|
||||||
|
|
||||||
|
// Check if email has been verified on time
|
||||||
|
if (!is_null($user->emailVerifyToken) && date('U') > $user->emailVerifyExpiry)
|
||||||
|
{
|
||||||
|
$url = $this->plugin->getAuthenticationURL() . "/resend_verify_email?t=" . base64_encode($user->primaryEmail);
|
||||||
|
throw new LoginWarningException("User must verify email before logging in. Click <a href='$url'>here</a> to resend the verification email.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a session, log and return it
|
||||||
|
$this->session = $this->plugin->sessions->createSession($user, $event->context, $event->remember);
|
||||||
|
Logger::log("Logged in user '" . $user->primaryEmail . "'");
|
||||||
|
return $this->session;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a user out of the current session.
|
||||||
|
*
|
||||||
|
* Will set the session active to false and remove the session cookie.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function logout(): bool
|
||||||
|
{
|
||||||
|
if ($this->session->user->id == "0")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return $this->plugin->sessions->endSession($this->session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a registration of a new user using the provided data.
|
||||||
|
*
|
||||||
|
* The only absolutely required parameter is email. No user can exist without a valid email address.
|
||||||
|
*
|
||||||
|
* If password is null (for instance by creating the user from a panel instead of registration), the user shall be asked to set a password after verifying their email.
|
||||||
|
*
|
||||||
|
* Parameters should be an empty array, unless the developer wants to explicitly set internal values like emailExpiryTime.
|
||||||
|
*
|
||||||
|
* Properties can be any extra key => value data the developer wants to store about their users, such as usernames or social data.
|
||||||
|
*
|
||||||
|
* Use $bypassRestrictions in case registration is disabled by config, but a new user must be added anyway.
|
||||||
|
*
|
||||||
|
* Registration can be denied for the following reasons:
|
||||||
|
* - Registration is disabled by config and $bypassRestrictions is not set to true.
|
||||||
|
* - User already exists.
|
||||||
|
* - The event system cancels registration of another reason.
|
||||||
|
*
|
||||||
|
* @param string $email
|
||||||
|
* @param string|null $password
|
||||||
|
* @param array $parameters
|
||||||
|
* @param array $properties
|
||||||
|
* @param bool $bypassRestrictions
|
||||||
|
* @return bool
|
||||||
|
* @throws RegisterErrorException
|
||||||
|
* @throws RegisterWarningException
|
||||||
|
*/
|
||||||
|
public function register(string $email, ?string $password, array $parameters, array $properties, bool $bypassRestrictions = false): bool
|
||||||
|
{
|
||||||
|
// First check if registration is enabled
|
||||||
|
if (!$this->authConfig->get("register_enabled") && !$bypassRestrictions)
|
||||||
|
throw new RegisterErrorException("Registration is disabled.");
|
||||||
|
|
||||||
|
/** @var RegisterEvent $event */
|
||||||
|
$event = Events::fireEvent(new RegisterEvent($email, $password, $parameters, $properties));
|
||||||
|
if ($event->isCancelled())
|
||||||
|
{
|
||||||
|
$error = is_null($event->getError()) ? "Register cancelled by system." : $event->getError();
|
||||||
|
throw new RegisterErrorException($error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the user
|
||||||
|
try {
|
||||||
|
if (!$this->plugin->users->addUser($event->email, $event->password, true, $event->properties))
|
||||||
|
throw new RegisterErrorException("Could not create user. System error.");
|
||||||
|
|
||||||
|
// Fetch the user
|
||||||
|
$user = $this->plugin->users->getUserByEmail($event->email);
|
||||||
|
$this->sendVerifyEmail($user);
|
||||||
|
} catch (InputException|AuthenticationException $e) {
|
||||||
|
throw new RegisterErrorException($e->getMessage());
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new RegisterErrorException("Could not add user. Failed to send mail to user: " . $e->getMessage());
|
||||||
|
} catch (FactoryException|LayoutException $e) {
|
||||||
|
throw new RegisterErrorException("Could not add user. System failure.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a new email verification message to the user, in case the existing message is lost or expired.
|
||||||
|
*
|
||||||
|
* @param string $email
|
||||||
|
* @return bool
|
||||||
|
* @throws LoginErrorException
|
||||||
|
* @throws RegisterErrorException
|
||||||
|
*/
|
||||||
|
public function resendVerifyEmail(string $email): bool
|
||||||
|
{
|
||||||
|
$user = $this->plugin->users->getUserByEmail($email);
|
||||||
|
if (is_null($user))
|
||||||
|
throw new LoginErrorException("User not found.");
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->sendVerifyEmail($user);
|
||||||
|
} catch (FactoryException|LayoutException $e) {
|
||||||
|
throw new LoginErrorException("Could not resend verify email. System failure.");
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new RegisterErrorException("Could not resend verify email. Failed to send mail to user: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a email verification message to the user.
|
||||||
|
*
|
||||||
|
* @param User $user
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function sendVerifyEmail(User $user): void
|
||||||
|
{
|
||||||
|
// Load template engine
|
||||||
|
/** @var Layout $layout */
|
||||||
|
$layout = Factory::getInstance("layouts");
|
||||||
|
$layout->assign("user", $user);
|
||||||
|
$layout->assign("verifyURL", $this->plugin->getAuthenticationURL() . "/verify");
|
||||||
|
|
||||||
|
$html = $layout->get("mail/register");
|
||||||
|
$alt = $layout->get("mail/register_alt");
|
||||||
|
|
||||||
|
// Prepare mailer
|
||||||
|
/** @var PHPMailerWrapper $mailer */
|
||||||
|
$mailer = $this->libraries->get("mailer");
|
||||||
|
$mailer->addAddress($user->primaryEmail);
|
||||||
|
$serverName = $this->config->getConfig("web")->get("serverName");
|
||||||
|
$mailer->Subject = "Welcome to $serverName !";
|
||||||
|
$mailer->isHTML();
|
||||||
|
$mailer->Body = $html;
|
||||||
|
$mailer->AltBody = $alt;
|
||||||
|
|
||||||
|
// And send mail
|
||||||
|
$mailer->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies a user by their email token.
|
||||||
|
*
|
||||||
|
* @param string $token
|
||||||
|
* @return User|null
|
||||||
|
*/
|
||||||
|
public function verifyEmail(string $token): ?User
|
||||||
|
{
|
||||||
|
// Fetch user
|
||||||
|
$users = $this->plugin->users->getUsersByVariable("emailVerifyToken", $token);
|
||||||
|
if (count($users) === 1)
|
||||||
|
{
|
||||||
|
// Select user
|
||||||
|
/** @var User $user */
|
||||||
|
$user = $users[0];
|
||||||
|
$user->emailVerifyToken = null;
|
||||||
|
$user->emailVerifyExpiry = null;
|
||||||
|
|
||||||
|
// Update the user
|
||||||
|
$this->plugin->users->updateUser($user);
|
||||||
|
|
||||||
|
// And return true
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a forgot password email to the user, based on the email address of the user.
|
||||||
|
*
|
||||||
|
* Will be denied if:
|
||||||
|
* - Password resets are disabled by config.
|
||||||
|
* - The user doesn't exist.
|
||||||
|
* - The user is marked as inactive.
|
||||||
|
*
|
||||||
|
* @param string $email
|
||||||
|
* @return bool
|
||||||
|
* @throws FactoryException
|
||||||
|
* @throws LoginErrorException
|
||||||
|
* @throws LoginWarningException
|
||||||
|
*/
|
||||||
|
public function forgotPassword(string $email): bool
|
||||||
|
{
|
||||||
|
// Check if forgot password is enabled
|
||||||
|
if (!$this->authConfig->get("forgot_password_enabled"))
|
||||||
|
throw new LoginErrorException("Forgot password is disabled.");
|
||||||
|
|
||||||
|
// Fetch the user
|
||||||
|
$user = $this->plugin->users->getUserByEmail($email);
|
||||||
|
|
||||||
|
// If user not found, notify
|
||||||
|
if (empty($user))
|
||||||
|
throw new LoginWarningException("The provided email is unknown.");
|
||||||
|
|
||||||
|
// Check if the user is still active
|
||||||
|
if (!$user->active)
|
||||||
|
throw new LoginErrorException("The provided email is unknown");
|
||||||
|
|
||||||
|
// Generate reset token
|
||||||
|
$token = bin2hex(random_bytes(32));
|
||||||
|
|
||||||
|
// Save the token and user ID to ObjectStorage
|
||||||
|
/** @var CacheInterface $cache */
|
||||||
|
$cache = Factory::getInstance("storage")->getCache();
|
||||||
|
$cache->set("forgot_password_" . $token, $user->id, 3600);
|
||||||
|
|
||||||
|
// And send mail
|
||||||
|
try {
|
||||||
|
// Data is verified. Now send the email.
|
||||||
|
/** @var Layout $layout */
|
||||||
|
$layout = Factory::getInstance("layouts");
|
||||||
|
$layout->assign("resetURL", $this->plugin->getAuthenticationURL() . "/reset_password");
|
||||||
|
$layout->assign("token", $token);
|
||||||
|
$html = $layout->get("mail/forgot_password");
|
||||||
|
$alt = $layout->get("mail/forgot_password_alt");
|
||||||
|
|
||||||
|
// Prepare mailer
|
||||||
|
/** @var PHPMailerWrapper $mailer */
|
||||||
|
$mailer = $this->libraries->get("mailer");
|
||||||
|
$mailer->addAddress($email);
|
||||||
|
$mailer->Subject = "Password reset";
|
||||||
|
$mailer->isHTML();
|
||||||
|
$mailer->Body = $html;
|
||||||
|
$mailer->AltBody = $alt;
|
||||||
|
|
||||||
|
$mailer->send();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new LoginErrorException("Could not send mail to user: " . $e->getMessage());
|
||||||
|
} catch (FactoryException|LayoutException $e) {
|
||||||
|
throw new LoginErrorException("Could not send mail to user. System failure.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method used by AdminView to generate a token to reset password with, after verifying email.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @param User $user
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function createResetToken(User $user): string
|
||||||
|
{
|
||||||
|
// Generate reset token
|
||||||
|
$token = bin2hex(random_bytes(32));
|
||||||
|
|
||||||
|
// Save the token and user ID to ObjectStorage
|
||||||
|
/** @var CacheInterface $cache */
|
||||||
|
$cache = Factory::getInstance("storage")->getCache();
|
||||||
|
$cache->set("forgot_password_" . $token, $user->id, 3600);
|
||||||
|
|
||||||
|
return $token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the password of the user with the selected token
|
||||||
|
*
|
||||||
|
* @param string $token
|
||||||
|
* @param string $password
|
||||||
|
* @return bool
|
||||||
|
* @throws LoginErrorException
|
||||||
|
*/
|
||||||
|
public function resetPassword(string $token, string $password): bool
|
||||||
|
{
|
||||||
|
// Check token in cache
|
||||||
|
/** @var CacheInterface $cache */
|
||||||
|
$cache = Factory::getInstance("storage")->getCache();
|
||||||
|
if (!$cache->has("forgot_password_" . $token))
|
||||||
|
throw new LoginErrorException("User was not found.");
|
||||||
|
|
||||||
|
// Fetch user
|
||||||
|
$user = $this->plugin->users->getUserByUid($cache->get("forgot_password_" . $token));
|
||||||
|
|
||||||
|
// Modify the user
|
||||||
|
try {
|
||||||
|
if ($this->plugin->users->changePassword($user, $password))
|
||||||
|
$cache->delete("forgot_password_" . $token);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (InputException $e) {
|
||||||
|
throw new LoginErrorException($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of detailed user information.
|
||||||
|
*
|
||||||
|
* Returns ['user' => User, 'sessions' => Session[]] , or null if not found.
|
||||||
|
*
|
||||||
|
* @param string $userId
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
public function getDetailedUserInformation(string $userId): ?array
|
||||||
|
{
|
||||||
|
$user = $this->plugin->users->getUserByUid($userId);
|
||||||
|
if (is_null($user))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
$sessions = $this->plugin->sessions->getSessions(['user' => $userId]);
|
||||||
|
return ['user' => $user, 'sessions' => $sessions];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives a list of users that match the filter.
|
||||||
|
*
|
||||||
|
* @param array $filter
|
||||||
|
* @param int $index
|
||||||
|
* @param int $limit
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function listUsers(array $filter = [], int $index = 0, int $limit = 25): array
|
||||||
|
{
|
||||||
|
return $this->plugin->users->getUsers($index, $limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Condition callable for register form, to check if password 1 and 2 are equivalent.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* @param Form $form
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function registerFormCondition(Form $form): bool
|
||||||
|
{
|
||||||
|
// Verify password1 and password2 equivalency
|
||||||
|
$pass1 = $form->getField("password1");
|
||||||
|
$pass2 = $form->getField("password2");
|
||||||
|
if ($pass1->getValue() !== $pass2->getValue())
|
||||||
|
{
|
||||||
|
$pass1->invalidate();
|
||||||
|
$pass2->invalidate();
|
||||||
|
$pass1->addError("Passwords do not match");
|
||||||
|
$pass2->addError("Passwords do not match");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
5
layout/admin/layout.users_create.latte
Normal file
5
layout/admin/layout.users_create.latte
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
{$form|noescape}
|
||||||
|
</div>
|
||||||
|
</div>
|
65
layout/admin/layout.users_list.latte
Normal file
65
layout/admin/layout.users_list.latte
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
{varType FuzeWorks\Authentication\Model\User[] $users}
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3 class="card-title">List of registered users on this system</h3>
|
||||||
|
<a href="/{$adminKey}/authentication/create" class="btn btn-sm btn-primary float-right">Create User</a>
|
||||||
|
</div>
|
||||||
|
<!-- /.card-header -->
|
||||||
|
<div class="card-body">
|
||||||
|
<table id="example2" class="table table-bordered table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Permissions</th>
|
||||||
|
<th>Email verified</th>
|
||||||
|
<th>2FA</th>
|
||||||
|
<th>Active</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{foreach $users as $user}
|
||||||
|
<tr>
|
||||||
|
<td><a href="/{$adminKey}/authentication/view/{$user->id}">{$user->primaryEmail}</a></td>
|
||||||
|
<td>{$user->permissions|implode:", "}</td>
|
||||||
|
<td>{is_null($user->emailVerifyToken) ? "Yes" : "No"}</td>
|
||||||
|
<td>{is_null($user->mfaSecret) ? "No" : "Yes"}</td>
|
||||||
|
<td>{$user->active ? "Yes" : "No"}</td>
|
||||||
|
</tr>
|
||||||
|
{/foreach}
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Permissions</th>
|
||||||
|
<th>Email verified</th>
|
||||||
|
<th>2FA</th>
|
||||||
|
<th>Active</th>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<!-- /.card-body -->
|
||||||
|
</div>
|
||||||
|
<!-- /.card -->
|
||||||
|
</div>
|
||||||
|
<!-- /.col -->
|
||||||
|
</div>
|
||||||
|
<!-- /.row -->
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(function () {
|
||||||
|
$('#example2').DataTable({
|
||||||
|
"paging": true,
|
||||||
|
"lengthChange": false,
|
||||||
|
"searching": false,
|
||||||
|
"ordering": true,
|
||||||
|
"info": true,
|
||||||
|
"autoWidth": false,
|
||||||
|
"responsive": true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
26
layout/bridge/layout.authpanel.php
Normal file
26
layout/bridge/layout.authpanel.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
/** @var \FuzeWorks\Authentication\Model\Session $session */
|
||||||
|
?>
|
||||||
|
<h1 title="Authentication">Session info:</h1>
|
||||||
|
<div class="tracy-inner">
|
||||||
|
<table>
|
||||||
|
<?php
|
||||||
|
if (!is_null($session->user->id)) { ?>
|
||||||
|
<tr class='fuzeworks-authTable'>
|
||||||
|
<th>Key</th>
|
||||||
|
<th>Value</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr><td>UserID</td><td><?= $session->user->id ?></td></tr>
|
||||||
|
<tr><td>Email</td><td><?= htmlspecialchars($session->user->primaryEmail, ENT_QUOTES, 'UTF-8') ?></td></tr>
|
||||||
|
<tr><td>SessionKey</td><td><?= htmlspecialchars($session->sessionKey, ENT_QUOTES, 'UTF-8') ?></td></tr>
|
||||||
|
<tr><td>Persistent</td><td><?= ($session->persistent ? 'true' : 'false') ?></td></tr>
|
||||||
|
<tr><td>Expire</td><td><?= date('d M Y H:i', $session->expiryDate) ?></td></tr>
|
||||||
|
<tr><td>Update</td><td><?= date('d M Y H:i', $session->expiryThreshold) ?></td></tr>
|
||||||
|
<tr><td>Permissions</td><td><?= implode(', ', $session->user->permissions) ?></td></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
13
layout/bridge/layout.authtab.php
Normal file
13
layout/bridge/layout.authtab.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
/** @var \FuzeWorks\Authentication\Model\Session $session */
|
||||||
|
$color = $session->user->id !== "0" ? '#3ba9e6' : '#666666';
|
||||||
|
$color = $session->user->hasPermission("SUPER") ? "#aa0000" : $color;
|
||||||
|
$user = $session->user->id !== "0" ? $session->user->primaryEmail : 'Guest';
|
||||||
|
?>
|
||||||
|
|
||||||
|
<span title="Auth">
|
||||||
|
<svg viewBox="0 0 1000 1000">
|
||||||
|
<path d="M500,10C229.4,10,10,229.4,10,500c0,270.7,219.4,490,490,490c270.7,0,490-219.4,490-490C990,229.4,770.7,10,500,10z M381.2,220.6c31.7-31.7,73.8-49.1,118.9-49.1c44.9,0,87.3,17.4,118.7,49.1c32,31.8,49.5,73.9,49.5,118.9c0,44.9-17.5,86.9-49.5,118.7c-31.5,31.7-73.8,49.3-118.7,49.3c-45,0-87.1-17.5-118.9-49.3c-31.7-31.7-49.3-73.9-49.3-118.7C331.9,294.5,349.5,252.4,381.2,220.6z M500,895c-125.9,0-239.7-52-320.8-135.8c36.9-105.4,135.6-185.3,245.5-185.3h150.5c109.7,0,208.6,79.9,245.6,185.2C739.6,843,625.9,895,500,895z" fill-opacity=".9" fill="<?= $color ?>"/>
|
||||||
|
</svg>
|
||||||
|
<span class="tracy-label"><?= $user ?></span>
|
||||||
|
</span>
|
215
layout/mail/layout.forgot_password.latte
Normal file
215
layout/mail/layout.forgot_password.latte
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
{varType string $serverName}
|
||||||
|
{varType string $resetURL}
|
||||||
|
{varType string $token}
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Compiled with Bootstrap Email version: 1.3.1 --><meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||||
|
<meta name="x-apple-disable-message-reformatting">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<style type="text/css" n:syntax="off">
|
||||||
|
body,table,td{font-family:Helvetica,Arial,sans-serif !important}.ExternalClass{width:100%}.ExternalClass,.ExternalClass p,.ExternalClass span,.ExternalClass font,.ExternalClass td,.ExternalClass div{line-height:150%}a{text-decoration:none}*{color:inherit}a[x-apple-data-detectors],u+#body a,#MessageViewBody a{color:inherit;text-decoration:none;font-size:inherit;font-family:inherit;font-weight:inherit;line-height:inherit}img{-ms-interpolation-mode:bicubic}table:not([class^=s-]){font-family:Helvetica,Arial,sans-serif;mso-table-lspace:0pt;mso-table-rspace:0pt;border-spacing:0px;border-collapse:collapse}table:not([class^=s-]) td{border-spacing:0px;border-collapse:collapse}@media screen and (max-width: 600px){.w-full,.w-full>tbody>tr>td{width:100% !important}*[class*=s-lg-]>tbody>tr>td{font-size:0 !important;line-height:0 !important;height:0 !important}.s-2>tbody>tr>td{font-size:8px !important;line-height:8px !important;height:8px !important}.s-3>tbody>tr>td{font-size:12px !important;line-height:12px !important;height:12px !important}.s-5>tbody>tr>td{font-size:20px !important;line-height:20px !important;height:20px !important}.s-10>tbody>tr>td{font-size:40px !important;line-height:40px !important;height:40px !important}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="bg-light" style="outline: 0; width: 100%; min-width: 100%; height: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-family: Helvetica, Arial, sans-serif; line-height: 24px; font-weight: normal; font-size: 16px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; color: #000000; margin: 0; padding: 0; border-width: 0;" bgcolor="#f7fafc">
|
||||||
|
<table class="bg-light body" valign="top" role="presentation" border="0" cellpadding="0" cellspacing="0" style="outline: 0; width: 100%; min-width: 100%; height: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-family: Helvetica, Arial, sans-serif; line-height: 24px; font-weight: normal; font-size: 16px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; color: #000000; margin: 0; padding: 0; border-width: 0;" bgcolor="#f7fafc">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td valign="top" style="line-height: 24px; font-size: 16px; margin: 0;" align="left" bgcolor="#f7fafc">
|
||||||
|
<table class="container" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="line-height: 24px; font-size: 16px; margin: 0; padding: 0 16px;">
|
||||||
|
<!--[if (gte mso 9)|(IE)]>
|
||||||
|
<table align="center" role="presentation">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td width="600">
|
||||||
|
<![endif]-->
|
||||||
|
<table align="center" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%; max-width: 600px; margin: 0 auto;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
|
||||||
|
<table class="s-10 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 40px; font-size: 40px; width: 100%; height: 40px; margin: 0;" align="left" width="100%" height="40">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table class="card" role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-radius: 6px; border-collapse: separate !important; width: 100%; overflow: hidden; border: 1px solid #e2e8f0;" bgcolor="#ffffff">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 24px; font-size: 16px; width: 100%; margin: 0;" align="left" bgcolor="#ffffff">
|
||||||
|
<table class="card-body" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 24px; font-size: 16px; width: 100%; margin: 0; padding: 20px;" align="left">
|
||||||
|
<h1 class="h3" style="padding-top: 0; padding-bottom: 0; font-weight: 500; vertical-align: baseline; font-size: 28px; line-height: 33.6px; margin: 0;" align="left">Password reset request for {$serverName}</h1>
|
||||||
|
<table class="s-2 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 8px; font-size: 8px; width: 100%; height: 8px; margin: 0;" align="left" width="100%" height="8">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table class="s-5 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 20px; font-size: 20px; width: 100%; height: 20px; margin: 0;" align="left" width="100%" height="20">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table class="hr" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 24px; font-size: 16px; border-top-width: 1px; border-top-color: #e2e8f0; border-top-style: solid; height: 1px; width: 100%; margin: 0;" align="left">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table class="s-5 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 20px; font-size: 20px; width: 100%; height: 20px; margin: 0;" align="left" width="100%" height="20">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="space-y-3">
|
||||||
|
<p class="text-gray-700" style="line-height: 24px; font-size: 16px; color: #4a5568; width: 100%; margin: 0;" align="left">If you've reset your password or wish to reset it, use the link below to get started.</p>
|
||||||
|
<table class="s-3 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 12px; font-size: 12px; width: 100%; height: 12px; margin: 0;" align="left" width="100%" height="12">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p class="text-gray-700" style="line-height: 24px; font-size: 16px; color: #4a5568; width: 100%; margin: 0;" align="left">
|
||||||
|
If you did not request a password reset, please ignore this email. Only a person with access to your email account can reset your password.
|
||||||
|
</p>
|
||||||
|
<table class="s-3 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 12px; font-size: 12px; width: 100%; height: 12px; margin: 0;" align="left" width="100%" height="12">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p class="text-gray-700" style="line-height: 24px; font-size: 16px; color: #4a5568; width: 100%; margin: 0;" align="left">
|
||||||
|
If you have any questions, feel free to contact us using the contact form on the website.
|
||||||
|
</p>
|
||||||
|
<table class="s-3 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 12px; font-size: 12px; width: 100%; height: 12px; margin: 0;" align="left" width="100%" height="12">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 12px; font-size: 12px; width: 100%; height: 12px; margin: 0;" align="left" width="100%" height="12">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p class="text-gray-700" style="line-height: 24px; font-size: 16px; color: #4a5568; width: 100%; margin: 0;" align="left">
|
||||||
|
With kind regards,
|
||||||
|
</p>
|
||||||
|
<table class="s-3 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 12px; font-size: 12px; width: 100%; height: 12px; margin: 0;" align="left" width="100%" height="12">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p class="text-gray-700" style="line-height: 24px; font-size: 16px; color: #4a5568; width: 100%; margin: 0;" align="left">
|
||||||
|
<b>The {$serverName} team.</b>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<table class="s-5 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 20px; font-size: 20px; width: 100%; height: 20px; margin: 0;" align="left" width="100%" height="20">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table class="hr" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 24px; font-size: 16px; border-top-width: 1px; border-top-color: #e2e8f0; border-top-style: solid; height: 1px; width: 100%; margin: 0;" align="left">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table class="s-5 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 20px; font-size: 20px; width: 100%; height: 20px; margin: 0;" align="left" width="100%" height="20">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table class="btn btn-primary" role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-radius: 6px; border-collapse: separate !important;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 24px; font-size: 16px; border-radius: 6px; margin: 0;" align="center" bgcolor="#0d6efd">
|
||||||
|
<a href="{$resetURL}?t={$token}" target="_blank" style="color: #ffffff; font-size: 16px; font-family: Helvetica, Arial, sans-serif; text-decoration: none; border-radius: 6px; line-height: 20px; display: block; font-weight: normal; white-space: nowrap; background-color: #0d6efd; padding: 8px 12px; border: 1px solid #0d6efd;">Click here to reset your password!</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table class="s-10 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 40px; font-size: 40px; width: 100%; height: 40px; margin: 0;" align="left" width="100%" height="40">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<!--[if (gte mso 9)|(IE)]>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<![endif]-->
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
13
layout/mail/layout.forgot_password_alt.latte
Normal file
13
layout/mail/layout.forgot_password_alt.latte
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{varType string $serverName}
|
||||||
|
{varType string $resetURL}
|
||||||
|
{varType string $token}
|
||||||
|
Password reset request for {$serverName}!
|
||||||
|
|
||||||
|
If you've reset your password or wish to reset it, use the link below to get started.
|
||||||
|
|
||||||
|
{$resetURL}?t={$token} Click here to reset your password.
|
||||||
|
|
||||||
|
If you did not request a password reset, please ignore this email. Only a person with access to your email account can reset your password.
|
||||||
|
|
||||||
|
With kind regards,
|
||||||
|
The {$serverName} team.
|
214
layout/mail/layout.register.latte
Normal file
214
layout/mail/layout.register.latte
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
{varType FuzeWorks\Authentication\Model\User $user}
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Compiled with Bootstrap Email version: 1.3.1 --><meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||||
|
<meta name="x-apple-disable-message-reformatting">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<style type="text/css" n:syntax="off">
|
||||||
|
body,table,td{font-family:Helvetica,Arial,sans-serif !important}.ExternalClass{width:100%}.ExternalClass,.ExternalClass p,.ExternalClass span,.ExternalClass font,.ExternalClass td,.ExternalClass div{line-height:150%}a{text-decoration:none}*{color:inherit}a[x-apple-data-detectors],u+#body a,#MessageViewBody a{color:inherit;text-decoration:none;font-size:inherit;font-family:inherit;font-weight:inherit;line-height:inherit}img{-ms-interpolation-mode:bicubic}table:not([class^=s-]){font-family:Helvetica,Arial,sans-serif;mso-table-lspace:0pt;mso-table-rspace:0pt;border-spacing:0px;border-collapse:collapse}table:not([class^=s-]) td{border-spacing:0px;border-collapse:collapse}@media screen and (max-width: 600px){.w-full,.w-full>tbody>tr>td{width:100% !important}*[class*=s-lg-]>tbody>tr>td{font-size:0 !important;line-height:0 !important;height:0 !important}.s-2>tbody>tr>td{font-size:8px !important;line-height:8px !important;height:8px !important}.s-3>tbody>tr>td{font-size:12px !important;line-height:12px !important;height:12px !important}.s-5>tbody>tr>td{font-size:20px !important;line-height:20px !important;height:20px !important}.s-10>tbody>tr>td{font-size:40px !important;line-height:40px !important;height:40px !important}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="bg-light" style="outline: 0; width: 100%; min-width: 100%; height: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-family: Helvetica, Arial, sans-serif; line-height: 24px; font-weight: normal; font-size: 16px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; color: #000000; margin: 0; padding: 0; border-width: 0;" bgcolor="#f7fafc">
|
||||||
|
<table class="bg-light body" valign="top" role="presentation" border="0" cellpadding="0" cellspacing="0" style="outline: 0; width: 100%; min-width: 100%; height: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; font-family: Helvetica, Arial, sans-serif; line-height: 24px; font-weight: normal; font-size: 16px; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; color: #000000; margin: 0; padding: 0; border-width: 0;" bgcolor="#f7fafc">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td valign="top" style="line-height: 24px; font-size: 16px; margin: 0;" align="left" bgcolor="#f7fafc">
|
||||||
|
<table class="container" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="line-height: 24px; font-size: 16px; margin: 0; padding: 0 16px;">
|
||||||
|
<!--[if (gte mso 9)|(IE)]>
|
||||||
|
<table align="center" role="presentation">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td width="600">
|
||||||
|
<![endif]-->
|
||||||
|
<table align="center" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%; max-width: 600px; margin: 0 auto;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
|
||||||
|
<table class="s-10 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 40px; font-size: 40px; width: 100%; height: 40px; margin: 0;" align="left" width="100%" height="40">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table class="card" role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-radius: 6px; border-collapse: separate !important; width: 100%; overflow: hidden; border: 1px solid #e2e8f0;" bgcolor="#ffffff">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 24px; font-size: 16px; width: 100%; margin: 0;" align="left" bgcolor="#ffffff">
|
||||||
|
<table class="card-body" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 24px; font-size: 16px; width: 100%; margin: 0; padding: 20px;" align="left">
|
||||||
|
<h1 class="h3" style="padding-top: 0; padding-bottom: 0; font-weight: 500; vertical-align: baseline; font-size: 28px; line-height: 33.6px; margin: 0;" align="left">Welcome to {$serverName}!</h1>
|
||||||
|
<table class="s-2 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 8px; font-size: 8px; width: 100%; height: 8px; margin: 0;" align="left" width="100%" height="8">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<h5 class="text-teal-700" style="color: #13795b; padding-top: 0; padding-bottom: 0; font-weight: 500; vertical-align: baseline; font-size: 20px; line-height: 24px; margin: 0;" align="left">Just one more step needed.</h5>
|
||||||
|
<table class="s-5 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 20px; font-size: 20px; width: 100%; height: 20px; margin: 0;" align="left" width="100%" height="20">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table class="hr" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 24px; font-size: 16px; border-top-width: 1px; border-top-color: #e2e8f0; border-top-style: solid; height: 1px; width: 100%; margin: 0;" align="left">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table class="s-5 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 20px; font-size: 20px; width: 100%; height: 20px; margin: 0;" align="left" width="100%" height="20">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="space-y-3">
|
||||||
|
<p class="text-gray-700" style="line-height: 24px; font-size: 16px; color: #4a5568; width: 100%; margin: 0;" align="left">You registered an account on {$serverName}.</p>
|
||||||
|
<table class="s-3 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 12px; font-size: 12px; width: 100%; height: 12px; margin: 0;" align="left" width="100%" height="12">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p class="text-gray-700" style="line-height: 24px; font-size: 16px; color: #4a5568; width: 100%; margin: 0;" align="left">
|
||||||
|
Before being able to use your account, you need to verify that this is your email address by clicking the button at the bottom.
|
||||||
|
</p>
|
||||||
|
<table class="s-3 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 12px; font-size: 12px; width: 100%; height: 12px; margin: 0;" align="left" width="100%" height="12">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p class="text-gray-700" style="line-height: 24px; font-size: 16px; color: #4a5568; width: 100%; margin: 0;" align="left">
|
||||||
|
If you have any questions, feel free to contact us using the contact form on the website.
|
||||||
|
</p>
|
||||||
|
<table class="s-3 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 12px; font-size: 12px; width: 100%; height: 12px; margin: 0;" align="left" width="100%" height="12">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 12px; font-size: 12px; width: 100%; height: 12px; margin: 0;" align="left" width="100%" height="12">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p class="text-gray-700" style="line-height: 24px; font-size: 16px; color: #4a5568; width: 100%; margin: 0;" align="left">
|
||||||
|
With kind regards,
|
||||||
|
</p>
|
||||||
|
<table class="s-3 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 12px; font-size: 12px; width: 100%; height: 12px; margin: 0;" align="left" width="100%" height="12">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p class="text-gray-700" style="line-height: 24px; font-size: 16px; color: #4a5568; width: 100%; margin: 0;" align="left">
|
||||||
|
<b>The {$serverName} team.</b>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<table class="s-5 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 20px; font-size: 20px; width: 100%; height: 20px; margin: 0;" align="left" width="100%" height="20">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table class="hr" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 24px; font-size: 16px; border-top-width: 1px; border-top-color: #e2e8f0; border-top-style: solid; height: 1px; width: 100%; margin: 0;" align="left">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table class="s-5 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 20px; font-size: 20px; width: 100%; height: 20px; margin: 0;" align="left" width="100%" height="20">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table class="btn btn-primary" role="presentation" border="0" cellpadding="0" cellspacing="0" style="border-radius: 6px; border-collapse: separate !important;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 24px; font-size: 16px; border-radius: 6px; margin: 0;" align="center" bgcolor="#0d6efd">
|
||||||
|
<a href="{$verifyURL}?t={$user->emailVerifyToken}" target="_blank" style="color: #ffffff; font-size: 16px; font-family: Helvetica, Arial, sans-serif; text-decoration: none; border-radius: 6px; line-height: 20px; display: block; font-weight: normal; white-space: nowrap; background-color: #0d6efd; padding: 8px 12px; border: 1px solid #0d6efd;">Click here to verify email!</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table class="s-10 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td style="line-height: 40px; font-size: 40px; width: 100%; height: 40px; margin: 0;" align="left" width="100%" height="40">
|
||||||
|
 
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<!--[if (gte mso 9)|(IE)]>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<![endif]-->
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
11
layout/mail/layout.register_alt.latte
Normal file
11
layout/mail/layout.register_alt.latte
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{varType FuzeWorks\Authentication\Model\User $user}
|
||||||
|
Welcome to {$serverName}!
|
||||||
|
|
||||||
|
You registered an account on {$serverName}. Before being able to use your account, you need to verify that this is your email address by clicking the link below.
|
||||||
|
|
||||||
|
If you have any questions, feel free to contact us using the contact form on the website.
|
||||||
|
|
||||||
|
With kind regards,
|
||||||
|
The {$serverName} team.
|
||||||
|
|
||||||
|
{$verifyURL}?t={$user->emailVerifyToken} Click here to verify email!
|
34
layout/pages/layout.auth_form_page.latte
Normal file
34
layout/pages/layout.auth_form_page.latte
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{varType bool $register_enabled}
|
||||||
|
{varType bool $forgot_password_enabled}
|
||||||
|
{varType string $auth_url}
|
||||||
|
{varType FuzeWorks\Forms\Form $form}
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<title>{$serverName} > {$form->getLabel()}</title>
|
||||||
|
|
||||||
|
<!-- Bootstrap core CSS -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
|
||||||
|
|
||||||
|
<!-- Custom styles for this template -->
|
||||||
|
<link href="signin.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body class="text-center">
|
||||||
|
<h3>{$form->getLabel()}</h3>
|
||||||
|
{$form|noescape}
|
||||||
|
<br/>
|
||||||
|
<div class="btn-group" role="group">
|
||||||
|
{if $form->getName() == 'AuthLoginForm'}
|
||||||
|
{if $register_enabled}
|
||||||
|
<br/><a class="btn btn-outline-primary" href="{$auth_url}/register" role="button">Sign up</a>
|
||||||
|
{/if}
|
||||||
|
{elseif $form->getName() == 'AuthRegisterForm' OR $form->getName() == "AuthForgotPasswordForm"}
|
||||||
|
<br/><a class="btn btn-outline-primary" href="{$auth_url}/login" role="button">Sign in</a>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="mt-5 mb-3 text-muted">© 2013 - {date('Y')}</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
184
src/FuzeWorks/Authentication/AuthenticationPlugin.php
Executable file
184
src/FuzeWorks/Authentication/AuthenticationPlugin.php
Executable file
@ -0,0 +1,184 @@
|
|||||||
|
<?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 FuzeWorks\Authentication;
|
||||||
|
use Application\Controller\AuthenticationController;
|
||||||
|
use FuzeWorks\Authentication\Model\Sessions;
|
||||||
|
use FuzeWorks\Authentication\Model\Users;
|
||||||
|
use FuzeWorks\Config;
|
||||||
|
use FuzeWorks\ConfigORM\ConfigORM;
|
||||||
|
use FuzeWorks\Database;
|
||||||
|
use FuzeWorks\DatabaseEngine\MongoEngine;
|
||||||
|
use FuzeWorks\DatabaseEngine\PDOEngine;
|
||||||
|
use FuzeWorks\Factory;
|
||||||
|
use FuzeWorks\iPluginHeader;
|
||||||
|
use FuzeWorks\Priority;
|
||||||
|
use FuzeWorks\Router;
|
||||||
|
|
||||||
|
class AuthenticationPlugin implements iPluginHeader
|
||||||
|
{
|
||||||
|
|
||||||
|
public Users $users;
|
||||||
|
public Sessions $sessions;
|
||||||
|
public ConfigORM $config;
|
||||||
|
protected string $authURL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the plugin and integrates it into the other systems of FuzeWorks.
|
||||||
|
*
|
||||||
|
* Particularly, AuthenticationPlugin first loads the MVC-components, and adds its internal directories to them.
|
||||||
|
* It then registers the /auth URL with Router and loads all the sub-components such as Users management, Sessions management and more.
|
||||||
|
*/
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
// Load the basic requirements for every request
|
||||||
|
/** @var Config $config */
|
||||||
|
$config = Factory::getInstance('config');
|
||||||
|
$views = Factory::getInstance('views');
|
||||||
|
$controllers = Factory::getInstance('controllers');
|
||||||
|
$layout = Factory::getInstance("layouts");
|
||||||
|
|
||||||
|
// Determine path and add to components
|
||||||
|
$pluginPath = dirname(__DIR__, 3);
|
||||||
|
$config->addComponentPath($pluginPath, Priority::LOWEST);
|
||||||
|
$views->addComponentPath($pluginPath . DS . "view", Priority::LOWEST);
|
||||||
|
$controllers->addComponentPath($pluginPath . DS . "controller", Priority::LOWEST);
|
||||||
|
$layout->addComponentPath($pluginPath . DS . "layout", Priority::LOWEST);
|
||||||
|
|
||||||
|
// Open the configuration
|
||||||
|
$this->config = $config->getConfig('authentication');
|
||||||
|
|
||||||
|
// Settle auth URL
|
||||||
|
$webURL = $config->getConfig("web")->get("base_url");
|
||||||
|
$auth_selector = $this->config->get("auth_url");
|
||||||
|
$this->authURL = $webURL . "/" . $auth_selector;
|
||||||
|
|
||||||
|
// If the plugin is disabled, stop here
|
||||||
|
if ($this->config->get('auth_enabled') !== true)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Fetch Driver type and determine type
|
||||||
|
/** @var Database $databases */
|
||||||
|
$databases = Factory::getInstance("databases");
|
||||||
|
$databaseConnection = $this->config->get("database_group");
|
||||||
|
$engine = $databases->get($databaseConnection);
|
||||||
|
if ($engine instanceof PDOEngine)
|
||||||
|
$driver = Driver::PDO;
|
||||||
|
elseif ($engine instanceof MongoEngine)
|
||||||
|
$driver = Driver::MONGO;
|
||||||
|
else
|
||||||
|
$driver = Driver::UNKNOWN;
|
||||||
|
|
||||||
|
// Load submodules
|
||||||
|
$this->users = new Users($engine, $driver, $this);
|
||||||
|
$this->sessions = new Sessions($engine, $driver, $this->config, $this->users);
|
||||||
|
|
||||||
|
// Load dependencies which are needed upon every request with the plugin enabled
|
||||||
|
/** @var Router $router */
|
||||||
|
$router = Factory::getInstance('router');
|
||||||
|
|
||||||
|
// Register route and related views and controllers
|
||||||
|
$webString = $auth_selector . '(|\/(?P<viewMethod>.*?)(|\/(?P<viewParameters>.*?)))';
|
||||||
|
$router->addRoute($webString, ['viewType' => 'html', 'viewName' => 'authentication'], Priority::HIGH);
|
||||||
|
|
||||||
|
// And register the tracy bridge
|
||||||
|
if (class_exists('Tracy\Debugger', true))
|
||||||
|
new AuthenticationTracyBridge($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method that returns on which URL authentication is set to listen to.
|
||||||
|
*
|
||||||
|
* By default, this is /auth , but this can be configured in the authentication configuration file.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getAuthenticationURL(): string
|
||||||
|
{
|
||||||
|
return $this->authURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the authentication configuration file.
|
||||||
|
*
|
||||||
|
* @return ConfigORM
|
||||||
|
*/
|
||||||
|
public function getConfig(): ConfigORM
|
||||||
|
{
|
||||||
|
return $this->config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return 'auth';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getClassesPrefix(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getSourceDirectory(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getPluginClass(): ?string
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this plugin in accordance with FuzeWorks\Plugins
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function getPlugin(): AuthenticationPlugin
|
||||||
|
{
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
84
src/FuzeWorks/Authentication/AuthenticationTracyBridge.php
Normal file
84
src/FuzeWorks/Authentication/AuthenticationTracyBridge.php
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?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 FuzeWorks\Authentication;
|
||||||
|
|
||||||
|
use Tracy\Debugger;
|
||||||
|
use Tracy\IBarPanel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AuthenticationTracyBridge Class.
|
||||||
|
*
|
||||||
|
* Integrates the AuthenticationPlugin with the bottom development panel provided by Nette Tracy.
|
||||||
|
*
|
||||||
|
* It creates a small icon on the development bar, with info on the current session.
|
||||||
|
*/
|
||||||
|
class AuthenticationTracyBridge implements IBarPanel
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var AuthenticationPlugin $plugin
|
||||||
|
*/
|
||||||
|
protected AuthenticationPlugin $plugin;
|
||||||
|
|
||||||
|
public function __construct(AuthenticationPlugin $plugin)
|
||||||
|
{
|
||||||
|
$this->plugin = $plugin;
|
||||||
|
$bar = Debugger::getBar();
|
||||||
|
$bar->addPanel($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
function getTab(): string
|
||||||
|
{
|
||||||
|
$session = $this->plugin->sessions->getCurrentSession();
|
||||||
|
ob_start();
|
||||||
|
require(dirname(__DIR__, 3) . DS . "layout" . DS . "bridge" . DS . "layout.authtab.php");
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
function getPanel(): string
|
||||||
|
{
|
||||||
|
$session = $this->plugin->sessions->getCurrentSession();
|
||||||
|
ob_start();
|
||||||
|
require(dirname(__DIR__, 3) . DS . "layout" . DS . "bridge" . DS . "layout.authpanel.php");
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
}
|
46
src/FuzeWorks/Authentication/Driver.php
Executable file
46
src/FuzeWorks/Authentication/Driver.php
Executable file
@ -0,0 +1,46 @@
|
|||||||
|
<?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 FuzeWorks\Authentication;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Driver Enum.
|
||||||
|
*/
|
||||||
|
enum Driver
|
||||||
|
{
|
||||||
|
case PDO;
|
||||||
|
case MONGO;
|
||||||
|
case UNKNOWN;
|
||||||
|
}
|
@ -0,0 +1,147 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Drivers;
|
||||||
|
|
||||||
|
use FuzeWorks\Authentication\Model\Session;
|
||||||
|
use FuzeWorks\Authentication\Model\Users;
|
||||||
|
use FuzeWorks\Authentication\SessionsModelDriverInterface;
|
||||||
|
use FuzeWorks\DatabaseEngine\iDatabaseEngine;
|
||||||
|
use FuzeWorks\DatabaseEngine\MongoEngine;
|
||||||
|
use MongoDB\Collection;
|
||||||
|
|
||||||
|
class MongoSessionsModelDriver implements SessionsModelDriverInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var MongoEngine */
|
||||||
|
protected iDatabaseEngine $engine;
|
||||||
|
protected Collection $collection;
|
||||||
|
protected Users $usersModel;
|
||||||
|
|
||||||
|
public function __construct(iDatabaseEngine $engine, Users $usersModel)
|
||||||
|
{
|
||||||
|
$this->engine = $engine;
|
||||||
|
$this->collection = $this->engine->selectDatabase("auth")->selectCollection("sessions");
|
||||||
|
$this->usersModel = $usersModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Session $session
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function createSession(Session $session): bool
|
||||||
|
{
|
||||||
|
$insert = [
|
||||||
|
"sessionKey" => $session->sessionKey,
|
||||||
|
"user" => $session->user->id,
|
||||||
|
"expiryDate" => $session->expiryDate,
|
||||||
|
"expiryThreshold" => $session->expiryThreshold,
|
||||||
|
"persistent" => $session->persistent,
|
||||||
|
"active" => $session->active,
|
||||||
|
"context" => $session->context
|
||||||
|
];
|
||||||
|
|
||||||
|
$res = $this->collection->insertOne($insert);
|
||||||
|
if ($res->getInsertedCount() !== 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function readSessions(array $filter): array
|
||||||
|
{
|
||||||
|
// Fetch from database
|
||||||
|
$results = $this->collection->find($filter);
|
||||||
|
$out = [];
|
||||||
|
foreach ($results as $result)
|
||||||
|
{
|
||||||
|
// Load variables
|
||||||
|
$sessionKey = $result->sessionKey;
|
||||||
|
$user = $this->usersModel->getUserByUid($result->user);
|
||||||
|
$expiryDate = $result->expiryDate;
|
||||||
|
$expiryThreshold = $result->expiryThreshold;
|
||||||
|
$persistent = $result->persistent;
|
||||||
|
$active = $result->active;
|
||||||
|
$context = (array) $result->context;
|
||||||
|
|
||||||
|
$out[] = new Session($user, $sessionKey, $expiryDate, $expiryThreshold, $context, $persistent, $active);
|
||||||
|
}
|
||||||
|
|
||||||
|
// And output cumulative results
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateSessions(array $sessions): bool
|
||||||
|
{
|
||||||
|
$modified = 0;
|
||||||
|
foreach ($sessions as $session)
|
||||||
|
{
|
||||||
|
$update = [
|
||||||
|
"user" => $session->user->id,
|
||||||
|
"expiryDate" => $session->expiryDate,
|
||||||
|
"expiryThreshold" => $session->expiryThreshold,
|
||||||
|
"persistent" => $session->persistent,
|
||||||
|
"active" => $session->active,
|
||||||
|
"context" => $session->context
|
||||||
|
];
|
||||||
|
|
||||||
|
$res = $this->collection->updateOne(
|
||||||
|
["sessionKey" => $session->sessionKey],
|
||||||
|
['$set' => $update]
|
||||||
|
);
|
||||||
|
$modified += $res->getModifiedCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($modified === count($sessions))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteSessions(array $sessions): bool
|
||||||
|
{
|
||||||
|
$deleted = 0;
|
||||||
|
foreach ($sessions as $session)
|
||||||
|
{
|
||||||
|
$res = $this->collection->deleteOne(["sessionKey" => $session->sessionKey]);
|
||||||
|
$deleted += $res->getDeletedCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($deleted === count($sessions))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
142
src/FuzeWorks/Authentication/Drivers/MongoUsersModelDriver.php
Executable file
142
src/FuzeWorks/Authentication/Drivers/MongoUsersModelDriver.php
Executable file
@ -0,0 +1,142 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Drivers;
|
||||||
|
|
||||||
|
use FuzeWorks\Authentication\Model\User;
|
||||||
|
use FuzeWorks\Authentication\UsersModelDriverInterface;
|
||||||
|
use FuzeWorks\DatabaseEngine\iDatabaseEngine;
|
||||||
|
use FuzeWorks\DatabaseEngine\MongoEngine;
|
||||||
|
use MongoDB\Collection;
|
||||||
|
|
||||||
|
class MongoUsersModelDriver implements UsersModelDriverInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var MongoEngine */
|
||||||
|
protected iDatabaseEngine $engine;
|
||||||
|
protected Collection $collection;
|
||||||
|
|
||||||
|
public function __construct(iDatabaseEngine $engine)
|
||||||
|
{
|
||||||
|
$this->engine = $engine;
|
||||||
|
$this->collection = $this->engine->selectDatabase("auth")->selectCollection("users");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User[] $users
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function createUsers(array $users): bool
|
||||||
|
{
|
||||||
|
$res = $this->collection->insertMany($users);
|
||||||
|
if ($res->getInsertedCount() == count($users))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function readUsers(array $filter, array $options = []): array
|
||||||
|
{
|
||||||
|
// Process options
|
||||||
|
$opt = [];
|
||||||
|
if (isset($options['limit']))
|
||||||
|
$opt['limit'] = $options['limit'];
|
||||||
|
if (isset($options['index']))
|
||||||
|
$opt['skip'] = $options['index'];
|
||||||
|
|
||||||
|
$results = $this->collection->find($filter, $opt);
|
||||||
|
$out = [];
|
||||||
|
foreach ($results as $result)
|
||||||
|
{
|
||||||
|
$user = new User($result->id);
|
||||||
|
if (isset($result->primaryEmail))
|
||||||
|
$user->primaryEmail = $result->primaryEmail;
|
||||||
|
if (isset($result->password))
|
||||||
|
$user->password = $result->password;
|
||||||
|
if (isset($result->permissions))
|
||||||
|
$user->permissions = (array) $result->permissions;
|
||||||
|
if (isset($result->active))
|
||||||
|
$user->active = (bool) $result->active;
|
||||||
|
if (isset($result->emailVerifyToken))
|
||||||
|
$user->emailVerifyToken = $result->emailVerifyToken;
|
||||||
|
if (isset($result->emailVerifyExpiry))
|
||||||
|
$user->emailVerifyExpiry = $result->emailVerifyExpiry;
|
||||||
|
if (isset($result->properties))
|
||||||
|
$user->properties = (array) $result->properties;
|
||||||
|
|
||||||
|
$out[] = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User[] $users
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function updateUsers(array $users): bool
|
||||||
|
{
|
||||||
|
$modified = 0;
|
||||||
|
foreach ($users as $user)
|
||||||
|
{
|
||||||
|
$res = $this->collection->updateOne(["id" => $user->id], ['$set' => $user]);
|
||||||
|
$modified += $res->getModifiedCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($modified === count($users))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User[] $users
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteUsers(array $users): bool
|
||||||
|
{
|
||||||
|
$deleted = 0;
|
||||||
|
foreach ($users as $user)
|
||||||
|
{
|
||||||
|
$res = $this->collection->deleteOne(["id" => $user->id]);
|
||||||
|
$deleted += $res->getDeletedCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($deleted === count($users))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
205
src/FuzeWorks/Authentication/Drivers/PdoSessionsModelDriver.php
Normal file
205
src/FuzeWorks/Authentication/Drivers/PdoSessionsModelDriver.php
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Drivers;
|
||||||
|
|
||||||
|
use FuzeWorks\Authentication\Exceptions\AuthenticationException;
|
||||||
|
use FuzeWorks\Authentication\Model\Session;
|
||||||
|
use FuzeWorks\Authentication\Model\Users;
|
||||||
|
use FuzeWorks\Authentication\SessionsModelDriverInterface;
|
||||||
|
use FuzeWorks\DatabaseEngine\iDatabaseEngine;
|
||||||
|
use FuzeWorks\DatabaseEngine\PDOEngine;
|
||||||
|
use FuzeWorks\Factory;
|
||||||
|
use FuzeWorks\ObjectStorage\ObjectStorageComponent;
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
class PdoSessionsModelDriver implements SessionsModelDriverInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var PDOEngine $engine */
|
||||||
|
protected iDatabaseEngine $engine;
|
||||||
|
protected Users $usersModel;
|
||||||
|
protected bool $schema;
|
||||||
|
|
||||||
|
public function __construct(iDatabaseEngine $engine, Users $usersModel)
|
||||||
|
{
|
||||||
|
$this->engine = $engine;
|
||||||
|
$this->usersModel = $usersModel;
|
||||||
|
|
||||||
|
// Load object storage
|
||||||
|
/** @var ObjectStorageComponent $storage */
|
||||||
|
$storage = Factory::getInstance("storage");
|
||||||
|
$cache = $storage->getCache();
|
||||||
|
|
||||||
|
// Lookup schema
|
||||||
|
$schema = $cache->get("authPdoSessionsSchema");
|
||||||
|
if (is_null($schema))
|
||||||
|
{
|
||||||
|
$this->loadSchema();
|
||||||
|
$cache->set("authPdoSessionsSchema", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->schema = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadSchema(): bool
|
||||||
|
{
|
||||||
|
// Check if the sessions table exists
|
||||||
|
$statement = $this->engine->prepare("SELECT * FROM information_schema.tables WHERE `table_name` = :table_name LIMIT 1");
|
||||||
|
$statement->execute([':table_name' => 'sessions']);
|
||||||
|
if ($statement->rowCount() !== 1 && !$this->populateSchema())
|
||||||
|
throw new AuthenticationException("Could not initiate Authentication. Database schema is missing.");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function populateSchema(): bool
|
||||||
|
{
|
||||||
|
// @todo Implement schema populator
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createSession(Session $session): bool
|
||||||
|
{
|
||||||
|
$insert = [
|
||||||
|
"sessionKey" => $session->sessionKey,
|
||||||
|
"user" => $session->user->id,
|
||||||
|
"expiryDate" => $session->expiryDate,
|
||||||
|
"expiryThreshold" => $session->expiryThreshold,
|
||||||
|
"persistent" => (int) $session->persistent,
|
||||||
|
"active" => (int) $session->active,
|
||||||
|
"context" => json_encode($session->context)
|
||||||
|
];
|
||||||
|
|
||||||
|
// Perform the insert
|
||||||
|
$statement = $this->engine->prepare("INSERT INTO sessions (sessionKey, user, expiryDate, expiryThreshold, persistent, active, context) VALUES (:sessionKey, :user, :expiryDate, :expiryThreshold, :persistent, :active, :context)");
|
||||||
|
$statement->execute($insert);
|
||||||
|
|
||||||
|
return $statement->rowCount() === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function readSessions(array $filter): array
|
||||||
|
{
|
||||||
|
// Prepare the where statement
|
||||||
|
if (empty($filter))
|
||||||
|
$where = '';
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$whereKeys = [];
|
||||||
|
foreach ($filter as $filterKey => $filterVal)
|
||||||
|
$whereKeys[] = $filterKey . '=:' . $filterKey;
|
||||||
|
|
||||||
|
$where = 'WHERE ' . implode(' AND ', $whereKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the statement
|
||||||
|
$sql = "SELECT * FROM sessions $where";
|
||||||
|
$statement = $this->engine->prepare($sql);
|
||||||
|
|
||||||
|
// And execute the query
|
||||||
|
foreach ($filter as $key => $val)
|
||||||
|
{
|
||||||
|
if (is_null($val))
|
||||||
|
$statement->bindValue(':' . $key, $val, PDO::PARAM_NULL);
|
||||||
|
else
|
||||||
|
$statement->bindValue(':' . $key, $val);
|
||||||
|
}
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
// And return the results
|
||||||
|
$sessions = [];
|
||||||
|
while ($result = $statement->fetch(PDO::FETCH_OBJ))
|
||||||
|
{
|
||||||
|
// Load variables
|
||||||
|
$sessionKey = $result->sessionKey;
|
||||||
|
$user = $this->usersModel->getUserByUid($result->user);
|
||||||
|
$expiryDate = $result->expiryDate;
|
||||||
|
$expiryThreshold = $result->expiryThreshold;
|
||||||
|
$persistent = $result->persistent;
|
||||||
|
$active = $result->active;
|
||||||
|
$context = json_decode($result->context, true);
|
||||||
|
|
||||||
|
$sessions[] = new Session($user, $sessionKey, $expiryDate, $expiryThreshold, $context, $persistent, $active);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sessions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateSessions(array $sessions): bool
|
||||||
|
{
|
||||||
|
// Prepare statement
|
||||||
|
$statement = $this->engine->prepare("UPDATE sessions SET user=:user, expiryDate=:expiryDate, expiryThreshold=:expiryThreshold, persistent=:persistent, active=:active, context=:context WHERE sessionKey=:sessionKey");
|
||||||
|
|
||||||
|
// Loop over sessions
|
||||||
|
foreach ($sessions as $session)
|
||||||
|
{
|
||||||
|
$update = [
|
||||||
|
"user" => $session->user->id,
|
||||||
|
"expiryDate" => $session->expiryDate,
|
||||||
|
"expiryThreshold" => $session->expiryThreshold,
|
||||||
|
"persistent" => (int) $session->persistent,
|
||||||
|
"active" => (int) $session->active,
|
||||||
|
"context" => json_encode($session->context),
|
||||||
|
"sessionKey" => $session->sessionKey
|
||||||
|
];
|
||||||
|
|
||||||
|
// Perform the update
|
||||||
|
$statement->execute($update);
|
||||||
|
|
||||||
|
if ($statement->rowCount() !== 1)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteSessions(array $sessions): bool
|
||||||
|
{
|
||||||
|
// Prepare statement
|
||||||
|
$statement = $this->engine->prepare("DELETE FROM sessions WHERE sessionKey=:sessionKey");
|
||||||
|
|
||||||
|
// Loop over sessions
|
||||||
|
foreach ($sessions as $session)
|
||||||
|
{
|
||||||
|
// Perform the delete
|
||||||
|
$statement->execute([':sessionKey' => $session->sessionKey]);
|
||||||
|
|
||||||
|
if ($statement->rowCount() !== 1)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
449
src/FuzeWorks/Authentication/Drivers/PdoUsersModelDriver.php
Normal file
449
src/FuzeWorks/Authentication/Drivers/PdoUsersModelDriver.php
Normal file
@ -0,0 +1,449 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Drivers;
|
||||||
|
|
||||||
|
use FuzeWorks\Authentication\Exceptions\AuthenticationException;
|
||||||
|
use FuzeWorks\Authentication\Model\User;
|
||||||
|
use FuzeWorks\Authentication\UsersModelDriverInterface;
|
||||||
|
use FuzeWorks\DatabaseEngine\iDatabaseEngine;
|
||||||
|
use FuzeWorks\DatabaseEngine\PDOEngine;
|
||||||
|
use FuzeWorks\Exception\DatabaseException;
|
||||||
|
use FuzeWorks\Exception\TransactionException;
|
||||||
|
use FuzeWorks\Factory;
|
||||||
|
use FuzeWorks\Logger;
|
||||||
|
use FuzeWorks\ObjectStorage\ObjectStorageComponent;
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
class PdoUsersModelDriver implements UsersModelDriverInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var PDOEngine $engine */
|
||||||
|
protected iDatabaseEngine $engine;
|
||||||
|
protected array $schema;
|
||||||
|
|
||||||
|
public function __construct(iDatabaseEngine $engine)
|
||||||
|
{
|
||||||
|
$this->engine = $engine;
|
||||||
|
|
||||||
|
// Load object storage
|
||||||
|
/** @var ObjectStorageComponent $storage */
|
||||||
|
$storage = Factory::getInstance("storage");
|
||||||
|
$cache = $storage->getCache();
|
||||||
|
|
||||||
|
// Lookup the current schema
|
||||||
|
$schema = $cache->get('authPdoUsersSchema');
|
||||||
|
if (is_null($schema))
|
||||||
|
{
|
||||||
|
$schema = $this->loadSchema();
|
||||||
|
$cache->set('authPdoUsersSchema', $schema, 3600);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->schema = $schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads out the database for the current schema. Important to know which columns are available
|
||||||
|
* in the properties.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @throws AuthenticationException
|
||||||
|
*/
|
||||||
|
private function loadSchema(): array
|
||||||
|
{
|
||||||
|
// Check if the users table exist
|
||||||
|
$statement = $this->engine->prepare("SELECT * FROM information_schema.tables WHERE `table_name` = :table_name LIMIT 1");
|
||||||
|
$statement->execute(['table_name' => "users"]);
|
||||||
|
if ($statement->rowCount() !== 1 && !$this->populateSchema())
|
||||||
|
throw new AuthenticationException("Could not initiate Authentication. Database schema is missing.");
|
||||||
|
|
||||||
|
// Read the properties table and find all existing columns
|
||||||
|
$statement = $this->engine->prepare("SELECT `column_name` FROM information_schema.columns WHERE `table_name` = :table_name");
|
||||||
|
$statement->execute(['table_name' => "users_properties"]);
|
||||||
|
if ($statement->rowCount() < 1)
|
||||||
|
throw new AuthenticationException("Could not initiate Authentication. Database error.");
|
||||||
|
|
||||||
|
// Fetch columns and remove standard columns
|
||||||
|
$columns = $statement->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
$columns = array_splice($columns, 1);
|
||||||
|
|
||||||
|
return ['properties' => $columns];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function populateSchema(): bool
|
||||||
|
{
|
||||||
|
// @todo Implement schema populator
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the property to the schema. This will add the column to the properties table and save the
|
||||||
|
* new schema to the cache.
|
||||||
|
*
|
||||||
|
* @param string $propertyName
|
||||||
|
* @param mixed $propertyExample
|
||||||
|
* @return bool
|
||||||
|
* @throws DatabaseException
|
||||||
|
*/
|
||||||
|
private function addSchemaProperty(string $propertyName, mixed $propertyExample): bool
|
||||||
|
{
|
||||||
|
// First check if the property is not already in the schema
|
||||||
|
$illegal = ['id', 'primaryEmail', 'password', 'mfaSecret', 'permissions', 'active', 'emailVerifyToken', 'emailVerifyExpiry', 'properties', 'user_id'];
|
||||||
|
if (in_array($propertyName, $illegal))
|
||||||
|
{
|
||||||
|
Logger::logError("Could not add property to schema. Illegal property name: " . $propertyName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the type of the variable
|
||||||
|
$type = gettype($propertyExample);
|
||||||
|
switch ($type)
|
||||||
|
{
|
||||||
|
case "integer":
|
||||||
|
$type = "INT";
|
||||||
|
break;
|
||||||
|
case "double":
|
||||||
|
$type = "DOUBLE";
|
||||||
|
break;
|
||||||
|
case "string":
|
||||||
|
$type = "VARCHAR(255)";
|
||||||
|
break;
|
||||||
|
case "boolean":
|
||||||
|
$type = "TINYINT(1)";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Logger::logError("Could not add property to schema. Unknown type: " . $type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the column to the properties table
|
||||||
|
$this->engine->query("ALTER TABLE `users_properties` ADD $propertyName $type NULL");
|
||||||
|
|
||||||
|
// Save the new schema
|
||||||
|
$this->schema['properties'][] = $propertyName;
|
||||||
|
|
||||||
|
/** @var ObjectStorageComponent $storage */
|
||||||
|
$storage = Factory::getInstance("storage");
|
||||||
|
$cache = $storage->getCache();
|
||||||
|
$cache->set('authPdoUsersSchema', $this->schema, 3600);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function createUsers(array $users): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Prepare statements
|
||||||
|
$users_sql = "INSERT INTO `users` (id, primaryEmail, password, mfaSecret, permissions, active, emailVerifyToken, emailVerifyExpiry) VALUES (:id, :primaryEmail, :password, :mfaSecret, :permissions, :active, :emailVerifyToken, :emailVerifyExpiry)";
|
||||||
|
$properties_sql = "INSERT INTO `users_properties` (user_id) VALUES (:id)";
|
||||||
|
$users_statement = $this->engine->prepare($users_sql);
|
||||||
|
$properties_statement = $this->engine->prepare($properties_sql);
|
||||||
|
|
||||||
|
// Loop over each property
|
||||||
|
foreach ($users as $user)
|
||||||
|
foreach ($user->properties as $property => $value)
|
||||||
|
// Check if the property is already in the schema
|
||||||
|
if (!in_array($property, $this->schema['properties']))
|
||||||
|
// Add the property to the schema
|
||||||
|
if (!$this->addSchemaProperty($property, $value))
|
||||||
|
throw new DatabaseException("Could not add property to schema.");
|
||||||
|
|
||||||
|
// Start transaction
|
||||||
|
$this->engine->transactionStart();
|
||||||
|
|
||||||
|
// Loop over each user
|
||||||
|
foreach ($users as $user)
|
||||||
|
{
|
||||||
|
// First insert the user into the general users table
|
||||||
|
$users_statement->execute([
|
||||||
|
"id" => $user->id,
|
||||||
|
"primaryEmail" => $user->primaryEmail,
|
||||||
|
"password" => $user->password,
|
||||||
|
"mfaSecret" => $user->mfaSecret,
|
||||||
|
"permissions" => implode(";", $user->permissions),
|
||||||
|
"active" => (int) $user->active,
|
||||||
|
"emailVerifyToken" => $user->emailVerifyToken,
|
||||||
|
"emailVerifyExpiry" => $user->emailVerifyExpiry
|
||||||
|
]);
|
||||||
|
if ($users_statement->rowCount() !== 1)
|
||||||
|
{
|
||||||
|
$this->engine->transactionRollback();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then append the user id to the properties table
|
||||||
|
$properties_statement->execute(['id' => $user->id]);
|
||||||
|
|
||||||
|
// Insert all properties
|
||||||
|
foreach ($user->properties as $propertyKey => $propertyValue)
|
||||||
|
{
|
||||||
|
// Insert the property
|
||||||
|
$statement = $this->engine->prepare("UPDATE `users_properties` SET $propertyKey = :propertyValue WHERE `user_id` = :id");
|
||||||
|
$statement->execute([
|
||||||
|
"propertyValue" => $propertyValue,
|
||||||
|
"id" => $user->id
|
||||||
|
]);
|
||||||
|
if ($statement->rowCount() !== 1)
|
||||||
|
{
|
||||||
|
$this->engine->transactionRollback();
|
||||||
|
Logger::logError("Could not create users. Database error.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit the transaction
|
||||||
|
return $this->engine->transactionCommit();
|
||||||
|
} catch (TransactionException $e) {
|
||||||
|
Logger::logError("Could not create users. Transaction failure: " . $e->getMessage());
|
||||||
|
} catch (DatabaseException $e) {
|
||||||
|
Logger::logError("Could not create users. Database error: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function readUsers(array $filter, array $options = []): array
|
||||||
|
{
|
||||||
|
// Prepare the where statement
|
||||||
|
if (empty($filter))
|
||||||
|
$where = '';
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$whereKeys = [];
|
||||||
|
foreach ($filter as $filterKey => $filterVal)
|
||||||
|
$whereKeys[] = $filterKey . '=:' . $filterKey;
|
||||||
|
|
||||||
|
$where = 'WHERE ' . implode(' AND ', $whereKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare statement
|
||||||
|
$sql = "SELECT * FROM `users` LEFT JOIN `users_properties` ON users.`id`=users_properties.`user_id` $where";
|
||||||
|
$statement = $this->engine->prepare($sql);
|
||||||
|
|
||||||
|
// And execute the query
|
||||||
|
foreach ($filter as $key => $val)
|
||||||
|
{
|
||||||
|
if (is_null($val))
|
||||||
|
$statement->bindValue(':' . $key, $val, PDO::PARAM_NULL);
|
||||||
|
else
|
||||||
|
$statement->bindValue(':' . $key, $val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// And at last, execute
|
||||||
|
$statement->execute();
|
||||||
|
$results = $statement->fetchAll(PDO::FETCH_OBJ);
|
||||||
|
$out = [];
|
||||||
|
foreach ($results as $result)
|
||||||
|
{
|
||||||
|
$user = new User($result->id);
|
||||||
|
if (isset($result->primaryEmail))
|
||||||
|
$user->primaryEmail = $result->primaryEmail;
|
||||||
|
if (isset($result->password))
|
||||||
|
$user->password = $result->password;
|
||||||
|
if (isset($result->mfaSecret))
|
||||||
|
$user->mfaSecret = $result->mfaSecret;
|
||||||
|
if (isset($result->permissions))
|
||||||
|
$user->permissions = explode(";", $result->permissions);
|
||||||
|
if (isset($result->active))
|
||||||
|
$user->active = (bool) $result->active;
|
||||||
|
if (isset($result->emailVerifyToken))
|
||||||
|
$user->emailVerifyToken = $result->emailVerifyToken;
|
||||||
|
if (isset($result->emailVerifyExpiry))
|
||||||
|
$user->emailVerifyExpiry = $result->emailVerifyExpiry;
|
||||||
|
|
||||||
|
// And map properties
|
||||||
|
foreach ($this->schema['properties'] as $property)
|
||||||
|
if (isset($result->$property))
|
||||||
|
$user->properties[$property] = $result->$property;
|
||||||
|
|
||||||
|
$out[] = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function updateUsers(array $users): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Loop over each property
|
||||||
|
foreach ($users as $user)
|
||||||
|
foreach ($user->properties as $property => $value)
|
||||||
|
// Check if the property is already in the schema
|
||||||
|
if (!in_array($property, $this->schema['properties']))
|
||||||
|
// Add the property to the schema
|
||||||
|
if (!$this->addSchemaProperty($property, $value))
|
||||||
|
throw new DatabaseException("Could not add property to schema.");
|
||||||
|
|
||||||
|
// Start transaction
|
||||||
|
$this->engine->transactionStart();
|
||||||
|
|
||||||
|
foreach ($users as $user)
|
||||||
|
{
|
||||||
|
// Get the unaltered user
|
||||||
|
$oldUser = $this->readUsers(['id' => $user->id])[0];
|
||||||
|
|
||||||
|
// Check if there is a difference
|
||||||
|
$vars = [];
|
||||||
|
if ($user->primaryEmail != $oldUser->primaryEmail)
|
||||||
|
$vars['primaryEmail'] = $user->primaryEmail;
|
||||||
|
if ($user->password != $oldUser->password)
|
||||||
|
$vars['password'] = $user->password;
|
||||||
|
if ($user->mfaSecret != $oldUser->mfaSecret)
|
||||||
|
$vars['mfaSecret'] = $user->mfaSecret;
|
||||||
|
if ($user->permissions != $oldUser->permissions)
|
||||||
|
$vars['permissions'] = implode(";", $user->permissions);
|
||||||
|
if ($user->active != $oldUser->active)
|
||||||
|
$vars['active'] = (int) $user->active;
|
||||||
|
if ($user->emailVerifyToken != $oldUser->emailVerifyToken)
|
||||||
|
$vars['emailVerifyToken'] = $user->emailVerifyToken;
|
||||||
|
if ($user->emailVerifyExpiry != $oldUser->emailVerifyExpiry)
|
||||||
|
$vars['emailVerifyExpiry'] = $user->emailVerifyExpiry;
|
||||||
|
|
||||||
|
// Prepare statement
|
||||||
|
$sql = "UPDATE `users` SET " . implode(', ', array_map(function ($key) {
|
||||||
|
return $key . '=:' . $key;
|
||||||
|
}, array_keys($vars))) . " WHERE `id`=:id";
|
||||||
|
$statement = $this->engine->prepare($sql);
|
||||||
|
|
||||||
|
// Bind values
|
||||||
|
foreach ($vars as $key => $val)
|
||||||
|
$statement->bindValue(':' . $key, $val);
|
||||||
|
|
||||||
|
// Bind id
|
||||||
|
$statement->bindValue(':id', $user->id);
|
||||||
|
|
||||||
|
// And execute
|
||||||
|
if (!empty($vars))
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
// Update all properties
|
||||||
|
$vars = [];
|
||||||
|
|
||||||
|
// First check if any properties of oldUser are removed
|
||||||
|
foreach ($oldUser->properties as $property => $value)
|
||||||
|
if (!isset($user->properties[$property]))
|
||||||
|
$vars[$property] = null;
|
||||||
|
|
||||||
|
// Then check if any properties of newUser are added
|
||||||
|
foreach ($user->properties as $property => $value)
|
||||||
|
if (!isset($oldUser->properties[$property]) || $oldUser->properties[$property] !== $value)
|
||||||
|
$vars[$property] = $value;
|
||||||
|
|
||||||
|
// If there are no changes, continue
|
||||||
|
if (empty($vars))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Prepare statement
|
||||||
|
$sql = "UPDATE `users_properties` SET " . implode(', ', array_map(function ($key) {
|
||||||
|
return $key . '=:' . $key;
|
||||||
|
}, array_keys($vars))) . " WHERE `user_id`=:id";
|
||||||
|
$statement = $this->engine->prepare($sql);
|
||||||
|
|
||||||
|
// Bind values
|
||||||
|
foreach ($vars as $key => $val)
|
||||||
|
$statement->bindValue(':' . $key, $val);
|
||||||
|
|
||||||
|
// Bind id
|
||||||
|
$statement->bindValue(':id', $user->id);
|
||||||
|
|
||||||
|
// And execute
|
||||||
|
$statement->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit the transaction
|
||||||
|
return $this->engine->transactionCommit();
|
||||||
|
} catch (TransactionException $e) {
|
||||||
|
Logger::logError("Could not create users. Transaction failure: " . $e->getMessage());
|
||||||
|
} catch (DatabaseException $e) {
|
||||||
|
Logger::logError("Could not create users. Database error: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function deleteUsers(array $users): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Start transaction
|
||||||
|
$this->engine->transactionStart();
|
||||||
|
|
||||||
|
// Prepare statements
|
||||||
|
$statement = $this->engine->prepare("DELETE FROM `users` WHERE `id`=:id");
|
||||||
|
$statement2 = $this->engine->prepare("DELETE FROM `users_properties` WHERE `user_id`=:id");
|
||||||
|
|
||||||
|
// Loop over each user
|
||||||
|
foreach ($users as $user)
|
||||||
|
{
|
||||||
|
// Bind id
|
||||||
|
$statement->bindValue(':id', $user->id);
|
||||||
|
$statement2->bindValue(':id', $user->id);
|
||||||
|
|
||||||
|
// And execute
|
||||||
|
$statement2->execute();
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
// Verify that the user was deleted with rowCount
|
||||||
|
if ($statement->rowCount() !== 1 || $statement2->rowCount() !== 1)
|
||||||
|
{
|
||||||
|
$this->engine->transactionRollback();
|
||||||
|
Logger::logError("Could not delete users. Database error.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit the transaction
|
||||||
|
return $this->engine->transactionCommit();
|
||||||
|
} catch (TransactionException $e) {
|
||||||
|
Logger::logError("Could not delete users. Transaction failure: " . $e->getMessage());
|
||||||
|
} catch (DatabaseException $e) {
|
||||||
|
Logger::logError("Could not delete users. Database error: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
91
src/FuzeWorks/Authentication/Events/AddUserEvent.php
Executable file
91
src/FuzeWorks/Authentication/Events/AddUserEvent.php
Executable file
@ -0,0 +1,91 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Events;
|
||||||
|
use FuzeWorks\Event;
|
||||||
|
|
||||||
|
class AddUserEvent extends Event
|
||||||
|
{
|
||||||
|
|
||||||
|
public string $email;
|
||||||
|
public ?string $password;
|
||||||
|
public bool $active;
|
||||||
|
protected array $properties;
|
||||||
|
protected ?string $cancelError = null;
|
||||||
|
|
||||||
|
public function __construct(string $email, ?string $password, bool $active, array $parameters)
|
||||||
|
{
|
||||||
|
$this->email = $email;
|
||||||
|
$this->password = $password;
|
||||||
|
$this->active = $active;
|
||||||
|
$this->properties = $parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProperties(): array
|
||||||
|
{
|
||||||
|
return $this->properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setProperty(string $key, mixed $value): void
|
||||||
|
{
|
||||||
|
$this->properties[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasProperty(string $key): bool
|
||||||
|
{
|
||||||
|
return isset($this->properties[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setError(string $errorText)
|
||||||
|
{
|
||||||
|
$this->cancelError = $errorText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getError(): ?string
|
||||||
|
{
|
||||||
|
return $this->cancelError;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get(string $key)
|
||||||
|
{
|
||||||
|
return $this->properties[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set(string $key, mixed $value): void
|
||||||
|
{
|
||||||
|
$this->properties[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Events;
|
||||||
|
|
||||||
|
use FuzeWorks\Event;
|
||||||
|
use FuzeWorks\Forms\Field;
|
||||||
|
|
||||||
|
class GetRegisterFieldsEvent extends Event
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Field[] $fields
|
||||||
|
*/
|
||||||
|
protected array $fields = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Field[]
|
||||||
|
*/
|
||||||
|
public function getFields(): array
|
||||||
|
{
|
||||||
|
return $this->fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a field to be used on the register page
|
||||||
|
*
|
||||||
|
* @param Field $field
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function addField(Field $field): void
|
||||||
|
{
|
||||||
|
$this->fields[] = $field;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
66
src/FuzeWorks/Authentication/Events/LoginEvent.php
Normal file
66
src/FuzeWorks/Authentication/Events/LoginEvent.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Events;
|
||||||
|
|
||||||
|
use FuzeWorks\Event;
|
||||||
|
|
||||||
|
class LoginEvent extends Event
|
||||||
|
{
|
||||||
|
public string $identifier;
|
||||||
|
public string $password;
|
||||||
|
public bool $remember;
|
||||||
|
public array $context;
|
||||||
|
|
||||||
|
protected array $identifierFields = [];
|
||||||
|
|
||||||
|
public function __construct(string $identifier, string $password, bool $remember = false, array $context = [])
|
||||||
|
{
|
||||||
|
$this->identifier = $identifier;
|
||||||
|
$this->password = $password;
|
||||||
|
$this->remember = $remember;
|
||||||
|
$this->context = $context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addIdentifierField(string $identifierField)
|
||||||
|
{
|
||||||
|
$this->identifierFields[] = $identifierField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIdentifierFields(): array
|
||||||
|
{
|
||||||
|
return $this->identifierFields;
|
||||||
|
}
|
||||||
|
}
|
80
src/FuzeWorks/Authentication/Events/RegisterEvent.php
Normal file
80
src/FuzeWorks/Authentication/Events/RegisterEvent.php
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Events;
|
||||||
|
use FuzeWorks\Event;
|
||||||
|
|
||||||
|
class RegisterEvent extends Event
|
||||||
|
{
|
||||||
|
|
||||||
|
public string $email;
|
||||||
|
public ?string $password;
|
||||||
|
public array $parameters;
|
||||||
|
public array $properties;
|
||||||
|
protected ?string $cancelError = null;
|
||||||
|
|
||||||
|
public function __construct(string $email, ?string $password, array $parameters, array $properties)
|
||||||
|
{
|
||||||
|
$this->email = $email;
|
||||||
|
$this->password = $password;
|
||||||
|
$this->parameters = $parameters;
|
||||||
|
$this->properties = $properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProperties(): array
|
||||||
|
{
|
||||||
|
return $this->properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setProperty(string $key, mixed $value): void
|
||||||
|
{
|
||||||
|
$this->properties[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasProperty(string $key): bool
|
||||||
|
{
|
||||||
|
return isset($this->properties[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setError(string $errorText)
|
||||||
|
{
|
||||||
|
$this->cancelError = $errorText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getError(): ?string
|
||||||
|
{
|
||||||
|
return $this->cancelError;
|
||||||
|
}
|
||||||
|
}
|
41
src/FuzeWorks/Authentication/Exceptions/AuthenticationException.php
Executable file
41
src/FuzeWorks/Authentication/Exceptions/AuthenticationException.php
Executable file
@ -0,0 +1,41 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Exceptions;
|
||||||
|
use FuzeWorks\Exception\PluginException;
|
||||||
|
|
||||||
|
class AuthenticationException extends PluginException
|
||||||
|
{
|
||||||
|
}
|
40
src/FuzeWorks/Authentication/Exceptions/InputException.php
Executable file
40
src/FuzeWorks/Authentication/Exceptions/InputException.php
Executable file
@ -0,0 +1,40 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Exceptions;
|
||||||
|
|
||||||
|
class InputException extends AuthenticationException
|
||||||
|
{
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Exceptions;
|
||||||
|
|
||||||
|
class LoginErrorException extends LoginException
|
||||||
|
{
|
||||||
|
}
|
54
src/FuzeWorks/Authentication/Exceptions/LoginException.php
Normal file
54
src/FuzeWorks/Authentication/Exceptions/LoginException.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Exceptions;
|
||||||
|
|
||||||
|
use FuzeWorks\Authentication\AuthenticationPlugin;
|
||||||
|
use FuzeWorks\Factory;
|
||||||
|
|
||||||
|
class LoginException extends AuthenticationException
|
||||||
|
{
|
||||||
|
public function __construct(string $message = "", int $code = 0, \Throwable $previous = null)
|
||||||
|
{
|
||||||
|
parent::__construct($message, $code, $previous);
|
||||||
|
|
||||||
|
// Log the exception
|
||||||
|
/** @var AuthenticationPlugin $plugin */
|
||||||
|
$plugin = Factory::getInstance('plugins')->get('auth');
|
||||||
|
|
||||||
|
// @todo Log all exceptions of this kind to a log somewhere
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Exceptions;
|
||||||
|
|
||||||
|
class LoginWarningException extends LoginException
|
||||||
|
{
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Exceptions;
|
||||||
|
|
||||||
|
class RegisterErrorException extends AuthenticationException
|
||||||
|
{
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Exceptions;
|
||||||
|
|
||||||
|
class RegisterWarningException extends AuthenticationException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
51
src/FuzeWorks/Authentication/Model/GuestSession.php
Normal file
51
src/FuzeWorks/Authentication/Model/GuestSession.php
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GuestSession Class.
|
||||||
|
*
|
||||||
|
* A special class with the info of guest users, or the data available if sessions are not verified.
|
||||||
|
*/
|
||||||
|
class GuestSession extends Session
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$user = new User("0", "guest@local", null, ["GUEST"], true, null, null, []);
|
||||||
|
parent::__construct($user, "0", 0, 0, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
121
src/FuzeWorks/Authentication/Model/Session.php
Executable file
121
src/FuzeWorks/Authentication/Model/Session.php
Executable file
@ -0,0 +1,121 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Model;
|
||||||
|
|
||||||
|
class Session
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The User this Session pertains to.
|
||||||
|
*
|
||||||
|
* @var User
|
||||||
|
*/
|
||||||
|
public User $user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A 16 character random string identifying this Session.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public string $sessionKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A unix int timestamp when the session will expire.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public int $expiryDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A unix int timestamp after which, but before the $expiryDate, a session will be extended.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public int $expiryThreshold;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How long a session should last. If false, sessions should only last an hour. If true, sessions should last 3 months.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public bool $persistent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Other data describing context of the Session, such as User-Agent and IP.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public array $context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this session is active or not.
|
||||||
|
*
|
||||||
|
* If false, the user shall be logged out automatically.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public bool $active;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data about this session stored in ObjectStorage
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected array $sessionData;
|
||||||
|
|
||||||
|
public function __construct(User $user, string $sessionKey, int $expiryDate, int $expiryThreshold, array $context, bool $persistent = false, bool $active = true)
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
$this->sessionKey = $sessionKey;
|
||||||
|
$this->expiryDate = $expiryDate;
|
||||||
|
$this->expiryThreshold = $expiryThreshold;
|
||||||
|
$this->context = $context;
|
||||||
|
$this->persistent = $persistent;
|
||||||
|
$this->active = $active;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the user of this session has access to a specific permission node.
|
||||||
|
*
|
||||||
|
* @param string $permissionString
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasPermission(string $permissionString): bool
|
||||||
|
{
|
||||||
|
return $this->user->hasPermission($permissionString);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
272
src/FuzeWorks/Authentication/Model/Sessions.php
Executable file
272
src/FuzeWorks/Authentication/Model/Sessions.php
Executable file
@ -0,0 +1,272 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Model;
|
||||||
|
use FuzeWorks\Authentication\Driver;
|
||||||
|
use FuzeWorks\Authentication\Drivers\MongoSessionsModelDriver;
|
||||||
|
use FuzeWorks\Authentication\Drivers\PdoSessionsModelDriver;
|
||||||
|
use FuzeWorks\Authentication\SessionsModelDriverInterface;
|
||||||
|
use FuzeWorks\Config;
|
||||||
|
use FuzeWorks\ConfigORM\ConfigORM;
|
||||||
|
use FuzeWorks\DatabaseEngine\iDatabaseEngine;
|
||||||
|
use FuzeWorks\Exception\FactoryException;
|
||||||
|
use FuzeWorks\Factory;
|
||||||
|
use FuzeWorks\Input;
|
||||||
|
use FuzeWorks\Logger;
|
||||||
|
use FuzeWorks\Model;
|
||||||
|
|
||||||
|
class Sessions extends Model
|
||||||
|
{
|
||||||
|
|
||||||
|
protected ?SessionsModelDriverInterface $driver;
|
||||||
|
protected ConfigORM $authCFG;
|
||||||
|
protected ConfigORM $webCFG;
|
||||||
|
protected Users $usersModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current session
|
||||||
|
*
|
||||||
|
* @var Session
|
||||||
|
*/
|
||||||
|
protected Session $session;
|
||||||
|
|
||||||
|
public function __construct(iDatabaseEngine $engine, Driver $driver, ConfigORM $config, Users $usersModel)
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
// Load engine
|
||||||
|
if ($driver == Driver::PDO)
|
||||||
|
$this->driver = new PdoSessionsModelDriver($engine, $usersModel);
|
||||||
|
elseif ($driver == Driver::MONGO)
|
||||||
|
$this->driver = new MongoSessionsModelDriver($engine, $usersModel);
|
||||||
|
|
||||||
|
// Load config
|
||||||
|
$this->authCFG = $config;
|
||||||
|
$this->webCFG = Factory::getInstance("config")->getConfig("web");
|
||||||
|
|
||||||
|
// Save Users
|
||||||
|
$this->usersModel = $usersModel;
|
||||||
|
|
||||||
|
// And load the guest session
|
||||||
|
$this->session = new GuestSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $sessionKey
|
||||||
|
* @return Session
|
||||||
|
*/
|
||||||
|
public function start(string $sessionKey = null): Session
|
||||||
|
{
|
||||||
|
// Fetch the sessionKey
|
||||||
|
if (is_null($sessionKey))
|
||||||
|
{
|
||||||
|
/** @var Input $input */
|
||||||
|
$input = Factory::getInstance("input");
|
||||||
|
$cookieName = $this->webCFG->get("cookie_prefix") . "fw_auth_token";
|
||||||
|
$sessionKey = $input->cookie($cookieName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the sessionKey is still null, return the current GuestSession
|
||||||
|
if (is_null($sessionKey))
|
||||||
|
return $this->session;
|
||||||
|
|
||||||
|
// Otherwise, fetch the current session from the database
|
||||||
|
$session = $this->getSessionByHash($sessionKey);
|
||||||
|
if (is_null($session))
|
||||||
|
{
|
||||||
|
// Remove the session cookie
|
||||||
|
$this->_setSession("", 0);
|
||||||
|
return $this->session;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the date has expired
|
||||||
|
if (date('U') > $session->expiryDate)
|
||||||
|
{
|
||||||
|
Logger::log("Current session has expired. Ending session.");
|
||||||
|
$session->active = false;
|
||||||
|
if (!$this->updateSession($session))
|
||||||
|
Logger::logError("Failed to update session. Setting guest session for now.");
|
||||||
|
|
||||||
|
// And remove the session cookie
|
||||||
|
$this->_setSession("", 0);
|
||||||
|
return $this->session;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If session has been deactivated, remove cookie and return guest session
|
||||||
|
if (!$session->active)
|
||||||
|
{
|
||||||
|
$this->_setSession("", 0);
|
||||||
|
return $this->session;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If user has been deactivated, update session and remove cookie
|
||||||
|
if (!$session->user->active)
|
||||||
|
{
|
||||||
|
Logger::log("Current user has been deactivated. Ending session.");
|
||||||
|
$session->active = false;
|
||||||
|
if (!$this->updateSession($session))
|
||||||
|
Logger::logError("Failed to update session. Setting guest session for now.");
|
||||||
|
|
||||||
|
// And remove the session cookie
|
||||||
|
$this->_setSession("", 0);
|
||||||
|
return $this->session;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that email has been verified on time
|
||||||
|
if (!is_null($session->user->emailVerifyToken) && date('U') > $session->user->emailVerifyExpiry)
|
||||||
|
{
|
||||||
|
// Deactivate the user and write to database
|
||||||
|
Logger::log("User must verify email before continuing.");
|
||||||
|
$this->_setSession("", 0);
|
||||||
|
return $this->session;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend the session if threshold has expired
|
||||||
|
if (date('U') > $session->expiryThreshold)
|
||||||
|
{
|
||||||
|
$session->expiryDate = ($session->persistent ? (int) date('U') + (3600*24*31*3) : (int) date('U') + 3600);
|
||||||
|
$session->expiryThreshold = ($session->persistent ? (int) date('U') + (3600*24) : (int) date('U') + 900);
|
||||||
|
Logger::log("Current session needs to be extended. Extending session.");
|
||||||
|
if (!$this->updateSession($session))
|
||||||
|
Logger::logError("Failed to update session. Maintaining current session.");
|
||||||
|
|
||||||
|
// Extend the session
|
||||||
|
$this->_setSession($session->sessionKey, $session->expiryDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log the session
|
||||||
|
$id = !is_null($session->user->primaryEmail) ? $session->user->primaryEmail : $session->user->id;
|
||||||
|
Logger::log("Current session is: '" . $id . "'");
|
||||||
|
|
||||||
|
// Set the values and return the session
|
||||||
|
$this->session = $session;
|
||||||
|
return $this->session;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sessionKey
|
||||||
|
* @return Session|null
|
||||||
|
*/
|
||||||
|
public function getSessionByHash(string $sessionKey): ?Session
|
||||||
|
{
|
||||||
|
$sessions = $this->driver->readSessions(["sessionKey" => $sessionKey]);
|
||||||
|
if (empty($sessions))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return $sessions[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $contextKey
|
||||||
|
* @param string $contextValue
|
||||||
|
* @return Session[]
|
||||||
|
*/
|
||||||
|
public function getSessionsByContext(string $contextKey, string $contextValue): array
|
||||||
|
{
|
||||||
|
return $this->driver->readSessions(["context." . $contextKey => $contextValue]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $filter
|
||||||
|
* @return Session[]
|
||||||
|
*/
|
||||||
|
public function getSessions(array $filter): array
|
||||||
|
{
|
||||||
|
return $this->driver->readSessions($filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createSession(User $user, array $context, bool $persistent = false, bool $active = true): ?Session
|
||||||
|
{
|
||||||
|
// Generate variables
|
||||||
|
$hash = substr(base64_encode(sha1(mt_rand())), 0, 16);
|
||||||
|
$expire = ($persistent ? (int) date('U') + (3600*24*31*3) : (int) date('U') + 3600);
|
||||||
|
$threshold = ($persistent ? (int) date('U') + (3600*24) : (int) date('U') + 900);
|
||||||
|
|
||||||
|
// Create Model
|
||||||
|
$session = new Session($user, $hash, $expire, $threshold, $context, $persistent, $active);
|
||||||
|
|
||||||
|
// Write to driver and set cookie
|
||||||
|
if ($this->driver->createSession($session))
|
||||||
|
{
|
||||||
|
$this->_setSession($hash, $expire);
|
||||||
|
return $session;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateSession(Session $session): bool
|
||||||
|
{
|
||||||
|
return $this->driver->updateSessions([$session]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function endSession(Session $session): bool
|
||||||
|
{
|
||||||
|
$session->active = false;
|
||||||
|
if ($this->updateSession($session))
|
||||||
|
{
|
||||||
|
$this->_setSession("", 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteSession(Session $session): bool
|
||||||
|
{
|
||||||
|
return $this->driver->deleteSessions([$session]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCurrentSession(): Session
|
||||||
|
{
|
||||||
|
return $this->session;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _setSession(string $sessionKey, int $expire)
|
||||||
|
{
|
||||||
|
// @todo Update to use FuzeWorks\Output
|
||||||
|
setcookie(
|
||||||
|
$this->webCFG->get("cookie_prefix") . "fw_auth_token",
|
||||||
|
$sessionKey,
|
||||||
|
$expire,
|
||||||
|
$this->webCFG->get("cookie_path"),
|
||||||
|
$this->webCFG->get("cookie_domain"),
|
||||||
|
$this->webCFG->get("cookie_secure"),
|
||||||
|
$this->webCFG->get("cookie_httponly")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
146
src/FuzeWorks/Authentication/Model/User.php
Executable file
146
src/FuzeWorks/Authentication/Model/User.php
Executable file
@ -0,0 +1,146 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Model;
|
||||||
|
|
||||||
|
class User
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A 16 character random id of the user.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public string $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The primary identifier for the user.
|
||||||
|
*
|
||||||
|
* Emails will get sent to this address.
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
public ?string $primaryEmail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The password of the user, as created by the password_hash() function.
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
public ?string $password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A secret key used to generate TOTP tokens.
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
public ?string $mfaSecret = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Separated permission nodes this user has access to.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public array $permissions = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this user is active or not.
|
||||||
|
*
|
||||||
|
* If false, user should be blocked from logging in or continuing a session.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public bool $active;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A 16 character random token used to verify the email belongs to the user.
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
public ?string $emailVerifyToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A unix int timestamp before which time the user needs to have verified their email.
|
||||||
|
*
|
||||||
|
* @var int|null
|
||||||
|
*/
|
||||||
|
public ?int $emailVerifyExpiry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A key => value list of other properties describing the user, such as username or social info.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public array $properties = [];
|
||||||
|
|
||||||
|
public function __construct(string $id, string $primaryEmail = null, string $password = null, array $permissions = [], bool $active = true, string $emailToken = null, int $emailExpire = null, array $properties = [])
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
$this->primaryEmail = $primaryEmail;
|
||||||
|
$this->password = $password;
|
||||||
|
$this->permissions = $permissions;
|
||||||
|
$this->active = $active;
|
||||||
|
$this->emailVerifyToken = $emailToken;
|
||||||
|
$this->emailVerifyExpiry = $emailExpire;
|
||||||
|
$this->properties = $properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the user has access to a specific permission node.
|
||||||
|
*
|
||||||
|
* @param string $permissionTag
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasPermission(string $permissionTag): bool
|
||||||
|
{
|
||||||
|
return in_array(strtoupper($permissionTag), $this->permissions) || in_array("SUPER", $this->permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __isset(string $name)
|
||||||
|
{
|
||||||
|
return isset($this->properties[$name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get(string $name)
|
||||||
|
{
|
||||||
|
return $this->properties[$name];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set(string $name, mixed $value)
|
||||||
|
{
|
||||||
|
$this->properties[$name] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
279
src/FuzeWorks/Authentication/Model/Users.php
Executable file
279
src/FuzeWorks/Authentication/Model/Users.php
Executable file
@ -0,0 +1,279 @@
|
|||||||
|
<?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 FuzeWorks\Authentication\Model;
|
||||||
|
use FuzeWorks\Authentication\AuthenticationPlugin;
|
||||||
|
use FuzeWorks\Authentication\Driver;
|
||||||
|
use FuzeWorks\Authentication\Drivers\MongoUsersModelDriver;
|
||||||
|
use FuzeWorks\Authentication\Drivers\PdoUsersModelDriver;
|
||||||
|
use FuzeWorks\Authentication\Events\AddUserEvent;
|
||||||
|
use FuzeWorks\Authentication\Exceptions\AuthenticationException;
|
||||||
|
use FuzeWorks\Authentication\Exceptions\InputException;
|
||||||
|
use FuzeWorks\Authentication\UsersModelDriverInterface;
|
||||||
|
use FuzeWorks\ConfigORM\ConfigORM;
|
||||||
|
use FuzeWorks\DatabaseEngine\iDatabaseEngine;
|
||||||
|
use FuzeWorks\Events;
|
||||||
|
use FuzeWorks\Exception\FactoryException;
|
||||||
|
use FuzeWorks\Exception\LayoutException;
|
||||||
|
use FuzeWorks\Factory;
|
||||||
|
use FuzeWorks\Layout;
|
||||||
|
use FuzeWorks\Mailer\PHPMailerWrapper;
|
||||||
|
use FuzeWorks\Model;
|
||||||
|
use PHPMailer\PHPMailer\Exception;
|
||||||
|
|
||||||
|
class Users extends Model
|
||||||
|
{
|
||||||
|
|
||||||
|
protected ?UsersModelDriverInterface $driver;
|
||||||
|
protected ConfigORM $authCFG;
|
||||||
|
protected AuthenticationPlugin $plugin;
|
||||||
|
|
||||||
|
public function __construct(iDatabaseEngine $engine, Driver $driver, AuthenticationPlugin $plugin)
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
// Determine driver
|
||||||
|
// @todo Implement PDO
|
||||||
|
if ($driver == Driver::PDO)
|
||||||
|
$this->driver = new PdoUsersModelDriver($engine);
|
||||||
|
elseif ($driver == Driver::MONGO)
|
||||||
|
$this->driver = new MongoUsersModelDriver($engine);
|
||||||
|
|
||||||
|
// Set plugin
|
||||||
|
$this->plugin = $plugin;
|
||||||
|
|
||||||
|
// Load config
|
||||||
|
$this->authCFG = $plugin->config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $email
|
||||||
|
* @return User|null
|
||||||
|
*/
|
||||||
|
public function getUserByEmail(string $email): ?User
|
||||||
|
{
|
||||||
|
$users = $this->driver->readUsers(["primaryEmail" => $email]);
|
||||||
|
if (empty($users))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return $users[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $uid
|
||||||
|
* @return User|null
|
||||||
|
*/
|
||||||
|
public function getUserByUid(string $uid): ?User
|
||||||
|
{
|
||||||
|
$users = $this->driver->readUsers(["id" => $uid]);
|
||||||
|
if (empty($users))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return $users[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $ids
|
||||||
|
* @return User[]
|
||||||
|
*/
|
||||||
|
public function getUsersByUids(array $ids): array
|
||||||
|
{
|
||||||
|
$out = [];
|
||||||
|
foreach ($ids as $id)
|
||||||
|
{
|
||||||
|
$user = $this->getUserByUid($id);
|
||||||
|
if (!is_null($user))
|
||||||
|
$out[] = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all the users which have a specific permission node.
|
||||||
|
*
|
||||||
|
* @note Currently non-functional on PDO backends
|
||||||
|
* @todo Fix^
|
||||||
|
*
|
||||||
|
* @param string $permissionString
|
||||||
|
* @return User[]
|
||||||
|
*/
|
||||||
|
public function getUsersByPermissionString(string $permissionString): array
|
||||||
|
{
|
||||||
|
return $this->driver->readUsers(["permissions" => $permissionString]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $variableKey
|
||||||
|
* @param string $variableValue
|
||||||
|
* @return User[]
|
||||||
|
*/
|
||||||
|
public function getUsersByProperty(string $variableKey, string $variableValue): array
|
||||||
|
{
|
||||||
|
return $this->driver->readUsers(["properties.".$variableKey => $variableValue]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $variableKey
|
||||||
|
* @param string $variableValue
|
||||||
|
* @return User[]
|
||||||
|
*/
|
||||||
|
public function getUsersByVariable(string $variableKey, string $variableValue): array
|
||||||
|
{
|
||||||
|
return $this->driver->readUsers([$variableKey => $variableValue]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all users
|
||||||
|
*
|
||||||
|
* @return Users[]
|
||||||
|
*/
|
||||||
|
public function getUsers(int $index = 0, int $limit = 999): array
|
||||||
|
{
|
||||||
|
return $this->driver->readUsers([], ['index' => $index, 'limit' => $limit]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $email
|
||||||
|
* @param string|null $password
|
||||||
|
* @param bool $active
|
||||||
|
* @param array $properties
|
||||||
|
* @return bool
|
||||||
|
* @throws InputException
|
||||||
|
* @throws AuthenticationException
|
||||||
|
*/
|
||||||
|
public function addUser(string $email, ?string $password, bool $active = true, array $properties = []): bool
|
||||||
|
{
|
||||||
|
// First verify the variables
|
||||||
|
if (!is_null($password))
|
||||||
|
{
|
||||||
|
$this->validatePassword($password);
|
||||||
|
|
||||||
|
// Hash the password
|
||||||
|
$password = password_hash($password, $this->authCFG->get("password_algorithm"), $this->authCFG->get("password_options"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate uid
|
||||||
|
$uid = substr(base64_encode(sha1(mt_rand())), 0, 16);
|
||||||
|
|
||||||
|
// Verify if a user with this identifier already exists
|
||||||
|
$this->validateEmail($email);
|
||||||
|
if ($this->getUserByEmail($email) !== null)
|
||||||
|
throw new InputException("This user already exists.");
|
||||||
|
|
||||||
|
// Fire AddUserEvent
|
||||||
|
/** @var AddUserEvent $event */
|
||||||
|
$event = Events::fireEvent(new AddUserEvent($email, $password, $active, $properties));
|
||||||
|
if ($event->isCancelled())
|
||||||
|
{
|
||||||
|
$error = is_null($event->getError()) ? "Add user cancelled by system." : $event->getError();
|
||||||
|
throw new InputException($error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare user verification code
|
||||||
|
$emailExpire = (int) date('U') + (int) $this->authCFG->get("verifyEmailWithin");
|
||||||
|
$emailToken = substr(base64_encode(sha1(mt_rand())), 0, 16);
|
||||||
|
|
||||||
|
// Prepare user object
|
||||||
|
$user = new User($uid, $event->email, $event->password, ["USER"], $event->active, $emailToken, $emailExpire, $event->getProperties());
|
||||||
|
|
||||||
|
// Send the data to the driver
|
||||||
|
return $this->driver->createUsers([$user]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
* @param string $newPassword
|
||||||
|
* @return bool
|
||||||
|
* @throws InputException
|
||||||
|
*/
|
||||||
|
public function changePassword(User $user, string $newPassword): bool
|
||||||
|
{
|
||||||
|
// Validate the password
|
||||||
|
$this->validatePassword($newPassword);
|
||||||
|
|
||||||
|
// Calculate password
|
||||||
|
$user->password = password_hash($newPassword, $this->authCFG->get("password_algorithm"), $this->authCFG->get("password_options"));
|
||||||
|
|
||||||
|
// And commit
|
||||||
|
return $this->updateUser($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateUser(User $user): bool
|
||||||
|
{
|
||||||
|
return $this->driver->updateUsers([$user]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteUser(User $user): bool
|
||||||
|
{
|
||||||
|
return $this->driver->deleteUsers([$user]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate if the password is strong enough
|
||||||
|
*
|
||||||
|
* @param string $password
|
||||||
|
* @return bool
|
||||||
|
* @throws InputException
|
||||||
|
*/
|
||||||
|
protected function validatePassword(string $password): bool
|
||||||
|
{
|
||||||
|
// First check for length
|
||||||
|
$min_length = $this->authCFG->get('password_min_length');
|
||||||
|
if (strlen($password) < $min_length)
|
||||||
|
throw new InputException("Password is too short. Must contain at least $min_length characters.");
|
||||||
|
|
||||||
|
// @todo Implement more tests
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate if the provided string is a valid email.
|
||||||
|
*
|
||||||
|
* @param string $email
|
||||||
|
* @return bool
|
||||||
|
* @throws InputException
|
||||||
|
*/
|
||||||
|
protected function validateEmail(string $email): bool
|
||||||
|
{
|
||||||
|
if (!filter_var($email, FILTER_VALIDATE_EMAIL))
|
||||||
|
throw new InputException("Provided email is not valid.");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
86
src/FuzeWorks/Authentication/SessionsModelDriverInterface.php
Executable file
86
src/FuzeWorks/Authentication/SessionsModelDriverInterface.php
Executable file
@ -0,0 +1,86 @@
|
|||||||
|
<?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 FuzeWorks\Authentication;
|
||||||
|
|
||||||
|
use FuzeWorks\Authentication\Model\Session;
|
||||||
|
use FuzeWorks\Authentication\Model\Users;
|
||||||
|
use FuzeWorks\DatabaseEngine\iDatabaseEngine;
|
||||||
|
|
||||||
|
interface SessionsModelDriverInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Initializes the Model.
|
||||||
|
*
|
||||||
|
* Should accept the Engine and UsersModel.
|
||||||
|
*
|
||||||
|
* @param iDatabaseEngine $engine
|
||||||
|
* @param Users $usersModel
|
||||||
|
*/
|
||||||
|
public function __construct(iDatabaseEngine $engine, Users $usersModel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new Session to the database using the provided Session object.
|
||||||
|
*
|
||||||
|
* @param Session $session
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function createSession(Session $session): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of Session[] from the database on the basis of the provided search filter.
|
||||||
|
*
|
||||||
|
* @param array $filter
|
||||||
|
* @return Session[]
|
||||||
|
*/
|
||||||
|
public function readSessions(array $filter): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a list of Session[] in the database on the basis of the provided Session[] objects.
|
||||||
|
*
|
||||||
|
* @param Session[] $sessions
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function updateSessions(array $sessions): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the provided Session[] objects from the database.
|
||||||
|
*
|
||||||
|
* @param Session[] $sessions
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteSessions(array $sessions): bool;
|
||||||
|
|
||||||
|
}
|
85
src/FuzeWorks/Authentication/UsersModelDriverInterface.php
Executable file
85
src/FuzeWorks/Authentication/UsersModelDriverInterface.php
Executable file
@ -0,0 +1,85 @@
|
|||||||
|
<?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 FuzeWorks\Authentication;
|
||||||
|
|
||||||
|
use FuzeWorks\Authentication\Exceptions\InputException;
|
||||||
|
use FuzeWorks\Authentication\Model\User;
|
||||||
|
use FuzeWorks\DatabaseEngine\iDatabaseEngine;
|
||||||
|
|
||||||
|
interface UsersModelDriverInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the model.
|
||||||
|
*
|
||||||
|
* Should only require the provided Database Engine.
|
||||||
|
*
|
||||||
|
* @param iDatabaseEngine $engine
|
||||||
|
*/
|
||||||
|
public function __construct(iDatabaseEngine $engine);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new Session to the database using the provided User object.
|
||||||
|
*
|
||||||
|
* @param User[] $users
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function createUsers(array $users): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of User[] from the database on the basis of the provided search filter.
|
||||||
|
*
|
||||||
|
* @return User[]
|
||||||
|
*/
|
||||||
|
public function readUsers(array $filter, array $options = []): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a list of User[] in the database on the basis of the provided User[] objects.
|
||||||
|
*
|
||||||
|
* @param User[] $users
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function updateUsers(array $users): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the provided User[] objects from the database.
|
||||||
|
*
|
||||||
|
* @param User[] $users
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function deleteUsers(array $users): bool;
|
||||||
|
|
||||||
|
}
|
56
test/base/PluginTest.php
Normal file
56
test/base/PluginTest.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?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. (http://i15.nl)
|
||||||
|
* @license https://opensource.org/licenses/MIT MIT License
|
||||||
|
*
|
||||||
|
* @link https://i15.nl
|
||||||
|
* @since Version 1.3.0
|
||||||
|
*
|
||||||
|
* @version Version 1.3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
use FuzeWorks\Authentication\AuthenticationPlugin;
|
||||||
|
use FuzeWorks\Factory;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class PluginTest extends TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
private AuthenticationPlugin $plugin;
|
||||||
|
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->plugin = Factory::getInstance('plugins')->get('authentication');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFoundation()
|
||||||
|
{
|
||||||
|
$this->assertInstanceOf(AuthenticationPlugin::class, $this->plugin);
|
||||||
|
}
|
||||||
|
}
|
65
test/bootstrap.php
Normal file
65
test/bootstrap.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?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
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once(dirname(__DIR__) . '/vendor/autoload.php');
|
||||||
|
|
||||||
|
use FuzeWorks\Configurator;
|
||||||
|
|
||||||
|
$configurator = new Configurator();
|
||||||
|
|
||||||
|
// Set directories
|
||||||
|
$configurator->setTempDirectory(__DIR__ . '/temp');
|
||||||
|
$configurator->setLogDirectory(__DIR__ . '/temp');
|
||||||
|
|
||||||
|
// Other values
|
||||||
|
$configurator->setTimeZone('Europe/Amsterdam');
|
||||||
|
|
||||||
|
// Add config folder
|
||||||
|
$configurator->addDirectory(dirname(__FILE__), 'config');
|
||||||
|
|
||||||
|
// Add components
|
||||||
|
$configurator->addComponent(new \FuzeWorks\WebComponent());
|
||||||
|
$configurator->addComponent(new \FuzeWorks\ObjectStorage\ObjectStorageComponent());
|
||||||
|
$configurator->addComponent(new \FuzeWorks\DatabaseComponent());
|
||||||
|
$configurator->addComponent(new \FuzeWorks\LayoutComponent());
|
||||||
|
$configurator->addComponent(new \FuzeWorks\TracyComponent());
|
||||||
|
|
||||||
|
// Build the container
|
||||||
|
$container = $configurator->createContainer();
|
||||||
|
|
||||||
|
// And add the library
|
||||||
|
$container->libraries->addLibraryClass("forms", \FuzeWorks\Forms\Forms::class);
|
||||||
|
$container->plugins->addPlugin(new \FuzeWorks\Authentication\AuthenticationPlugin());
|
||||||
|
return $container;
|
56
test/config.database.php
Normal file
56
test/config.database.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?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
|
||||||
|
*/
|
||||||
|
|
||||||
|
use FuzeWorks\Core;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'active_group' => Core::getEnv("DATABASE_DRIVER", "pdo"),
|
||||||
|
'connections' => [
|
||||||
|
'pdo' => [
|
||||||
|
'engineName' => 'pdo',
|
||||||
|
'dsn' => Core::getEnv("PDO_DSN", Core::getEnv("PDO_DRIVER", "mysql") . ":host=" . Core::getEnv("PDO_HOST", "127.0.0.1") . ";dbname=" . Core::getEnv("PDO_DATABASE", "") . ";charset=utf8"),
|
||||||
|
'username' => Core::getEnv("PDO_USERNAME", ""),
|
||||||
|
'password' => Core::getEnv("PDO_PASSWORD", "")
|
||||||
|
],
|
||||||
|
'mongo' => [
|
||||||
|
'engineName' => 'mongo',
|
||||||
|
'uri' => Core::getEnv("MONGO_URI", "mongodb://" . Core::getEnv("MONGO_HOST", "127.0.0.1") . ":" . Core::getEnv("MONGO_PORT", "27017")),
|
||||||
|
'uriOptions' => [],
|
||||||
|
'driverOptions' => [],
|
||||||
|
'username' => Core::getEnv("MONGO_USERNAME"),
|
||||||
|
'password' => Core::getEnv("MONGO_PASSWORD")
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
60
test/config.objectstorage.php
Executable file
60
test/config.objectstorage.php
Executable file
@ -0,0 +1,60 @@
|
|||||||
|
<?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
|
||||||
|
*/
|
||||||
|
|
||||||
|
use FuzeWorks\Core;
|
||||||
|
|
||||||
|
return [
|
||||||
|
// Which provider shall be used
|
||||||
|
// Options: DummyProvider, RedisProvider, FileProvider
|
||||||
|
'ObjectStorageProvider' => 'DummyProvider',
|
||||||
|
'DummyProvider' => [],
|
||||||
|
'RedisProvider' => [
|
||||||
|
// Type can be 'tcp' or 'unix'
|
||||||
|
'socket_type' => Core::getEnv('OBJECTSTORAGE_REDIS_SOCKET_TYPE', 'tcp'),
|
||||||
|
// If socket_type == 'unix', set the socket here
|
||||||
|
'socket' => Core::getEnv('OBJECTSTORAGE_REDIS_SOCKET', null),
|
||||||
|
// If socket_type == 'tcp', set the host here
|
||||||
|
'host' => Core::getEnv('OBJECTSTORAGE_REDIS_HOST', '127.0.0.1'),
|
||||||
|
// And some standard settings
|
||||||
|
'port' => Core::getEnv('OBJECTSTORAGE_REDIS_PORT', 6379),
|
||||||
|
'password' => Core::getEnv('OBJECTSTORAGE_REDIS_PASSWORD', null),
|
||||||
|
'timeout' => Core::getEnv('OBJECTSTORAGE_REDIS_TIMEOUT', 0),
|
||||||
|
'db_index' => Core::getEnv('OBJECTSTORAGE_REDIS_DBINDEX', 0),
|
||||||
|
],
|
||||||
|
'FileProvider' => [
|
||||||
|
// The directory where objects get stored by the FileProvider
|
||||||
|
'storage_directory' => Core::getEnv('OBJECTSTORAGE_FILE_DIRECTORY', Core::$tempDir)
|
||||||
|
]
|
||||||
|
];
|
17
test/phpunit.xml
Normal file
17
test/phpunit.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" bootstrap="bootstrap.php" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" stopOnError="false" stopOnFailure="false" stopOnIncomplete="false" stopOnSkipped="false" colors="false">
|
||||||
|
<coverage processUncoveredFiles="false">
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">../</directory>
|
||||||
|
</include>
|
||||||
|
<exclude>
|
||||||
|
<directory suffix=".php">../vendor/</directory>
|
||||||
|
<directory suffix=".php">../test/</directory>
|
||||||
|
</exclude>
|
||||||
|
</coverage>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Authentication Suite">
|
||||||
|
<directory>./</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
</phpunit>
|
2
test/temp/.gitignore
vendored
Normal file
2
test/temp/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
144
view/view.admin.authentication.php
Normal file
144
view/view.admin.authentication.php
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
<?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\Administration\AdminView;
|
||||||
|
use FuzeWorks\Administration\Attributes\DisplayAttribute;
|
||||||
|
use FuzeWorks\Administration\Attributes\HiddenAttribute;
|
||||||
|
use FuzeWorks\Administration\Attributes\IconAttribute;
|
||||||
|
use FuzeWorks\Administration\Attributes\PermissionAttribute;
|
||||||
|
use FuzeWorks\Administration\Attributes\PriorityAttribute;
|
||||||
|
use FuzeWorks\Authentication\Events\GetRegisterFieldsEvent;
|
||||||
|
use FuzeWorks\Authentication\Exceptions\RegisterErrorException;
|
||||||
|
use FuzeWorks\Authentication\Exceptions\RegisterWarningException;
|
||||||
|
use FuzeWorks\Controller;
|
||||||
|
use FuzeWorks\Events;
|
||||||
|
use FuzeWorks\Forms\Fields\CheckboxField;
|
||||||
|
use FuzeWorks\Forms\Fields\EmailField;
|
||||||
|
use FuzeWorks\Forms\Fields\PasswordField;
|
||||||
|
use FuzeWorks\Forms\Fields\SubmitField;
|
||||||
|
use FuzeWorks\Forms\Form;
|
||||||
|
use FuzeWorks\Forms\Forms;
|
||||||
|
use FuzeWorks\Priority;
|
||||||
|
|
||||||
|
class AuthenticationAdminView extends AdminView
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var AuthenticationController $controller */
|
||||||
|
protected Controller $controller;
|
||||||
|
|
||||||
|
#[DisplayAttribute("Users"), IconAttribute("users"), PriorityAttribute(Priority::LOW)]
|
||||||
|
#[PermissionAttribute(["ADMIN"])]
|
||||||
|
public function users()
|
||||||
|
{
|
||||||
|
// List all users
|
||||||
|
$users = $this->controller->listUsers();
|
||||||
|
$this->layouts->assign("users", $users);
|
||||||
|
return $this->layouts->get("admin/users_list");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[HiddenAttribute]
|
||||||
|
#[PermissionAttribute(["ADMIN"])]
|
||||||
|
public function view(string $userId)
|
||||||
|
{
|
||||||
|
$info = $this->controller->getDetailedUserInformation($userId);
|
||||||
|
dump($info);
|
||||||
|
exit;
|
||||||
|
|
||||||
|
|
||||||
|
return $userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[HiddenAttribute]
|
||||||
|
#[DisplayAttribute("Create User Account")]
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
// Prepare form
|
||||||
|
/** @var Forms $forms */
|
||||||
|
$forms = $this->libraries->get("forms");
|
||||||
|
$form = $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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Terms and conditions field
|
||||||
|
$field4 = new CheckboxField("sendemail");
|
||||||
|
$field4->setLabel("Send User Registration Notification");
|
||||||
|
$field4->checked();
|
||||||
|
$form->field($field4);
|
||||||
|
|
||||||
|
// Submit field
|
||||||
|
$field5 = new SubmitField("submit");
|
||||||
|
$field5->setButtonText("Create User Account");
|
||||||
|
$form->field($field5);
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}, "AuthCreateUserForm", "Register");
|
||||||
|
|
||||||
|
// After validation, send data to the controller
|
||||||
|
if ($form->validate())
|
||||||
|
{
|
||||||
|
// Prepare variables
|
||||||
|
$identifier = $form->getField("identifier")->getValue();
|
||||||
|
|
||||||
|
// Then forward the form to the controller
|
||||||
|
try {
|
||||||
|
$this->controller->register($identifier, null, [], [], true);
|
||||||
|
} 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);
|
||||||
|
return $this->layouts->get("admin/users_create");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
414
view/view.html.authentication.php
Executable file
414
view/view.html.authentication.php
Executable file
@ -0,0 +1,414 @@
|
|||||||
|
<?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")
|
||||||
|
$this->output->setHeader("Location: " . $redirect);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
$this->output->setHeader("Location: " . $redirect);
|
||||||
|
} 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;
|
||||||
|
$this->output->setHeader("Location: " . $redirect);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function register()
|
||||||
|
{
|
||||||
|
// First check if the registration is enabled
|
||||||
|
if (!$this->plugin->getConfig()->get("register_enabled"))
|
||||||
|
$this->output->setHeader("Location: /");
|
||||||
|
|
||||||
|
if ($this->controller->session->user->id !== "0")
|
||||||
|
$this->output->setHeader("Location: /");
|
||||||
|
|
||||||
|
// 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))
|
||||||
|
$this->output->setHeader("Location: /");
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
$this->output->setHeader("Location: " . $this->plugin->getAuthenticationURL() . "/reset_password?t=" . $token);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$this->output->setHeader("Location: " . $this->plugin->getAuthenticationURL());
|
||||||
|
|
||||||
|
return "Token verified";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function forgot_password()
|
||||||
|
{
|
||||||
|
// If user is already logged in, redirect to homepage
|
||||||
|
if ($this->controller->session->user->id !== "0")
|
||||||
|
$this->output->setHeader("Location: /");
|
||||||
|
|
||||||
|
// Check if forgot password is disabled
|
||||||
|
if (!$this->plugin->getConfig()->get("forgot_password_enabled"))
|
||||||
|
$this->output->setHeader("Location: /");
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
$this->output->setHeader("Location: " . $this->plugin->getAuthenticationURL() . "/login");
|
||||||
|
} 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user