272 lines
9.1 KiB
PHP
Executable File
272 lines
9.1 KiB
PHP
Executable File
<?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")
|
|
);
|
|
}
|
|
|
|
|
|
} |