392 lines
9.2 KiB
PHP
392 lines
9.2 KiB
PHP
<?php
|
|
/**
|
|
* @package awf
|
|
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
|
|
* @license GNU GPL version 3 or later
|
|
*/
|
|
|
|
namespace Awf\User;
|
|
|
|
use Awf\Application\Application;
|
|
use Awf\Container\Container;
|
|
use Awf\Database\Driver;
|
|
use Awf\Text\Text;
|
|
|
|
/**
|
|
* The User Manager class allows you to load, save, log in and log out users
|
|
*/
|
|
class Manager implements ManagerInterface
|
|
{
|
|
/**
|
|
* An array of the instances we have already created
|
|
*
|
|
* @var array[ManagerInterface]
|
|
*/
|
|
protected static $instances = array();
|
|
|
|
/**
|
|
* The container this instance of User Manager is attached to
|
|
*
|
|
* @var Container
|
|
*/
|
|
protected $container;
|
|
|
|
/**
|
|
* The name of the table where user accounts are stored
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $user_table = '#__users';
|
|
|
|
/**
|
|
* The name of the class we'll use to create new user objects
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $user_class = '\\Awf\\User\\User';
|
|
|
|
/**
|
|
* The current user's object
|
|
*
|
|
* @var UserInterface
|
|
*/
|
|
protected $currentUser = null;
|
|
|
|
/**
|
|
* The list of privilege plugins to load on each user object
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $privileges = array();
|
|
|
|
/**
|
|
* The list of authentication plugins to load on each user object
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $authentications = array();
|
|
|
|
/**
|
|
* Public constructor. Creates a new User Manager. Do not call this directly. It's best to call getInstance()
|
|
* instead.
|
|
*
|
|
* @param Container $container
|
|
*/
|
|
public function __construct(Container $container = null)
|
|
{
|
|
if (!is_object($container))
|
|
{
|
|
$container = Application::getInstance()->getContainer();
|
|
}
|
|
|
|
$this->user_table = $container->appConfig->get('user_table', '#__users');
|
|
$this->user_class = $container->appConfig->get('user_class', '\\Awf\\User\\User');
|
|
|
|
$this->container = $container;
|
|
}
|
|
|
|
/**
|
|
* Get user by numeric ID. Skip the ID (or use null) to get the currently logged in user. Use the ID=0 to get a new,
|
|
* empty user instance.
|
|
*
|
|
* @param integer $id The numeric ID of the user to load
|
|
*
|
|
* @return UserInterface|null A user object if it exists, null if it doesn't
|
|
*/
|
|
public function getUser($id = null)
|
|
{
|
|
// If we're not given an ID get the current user
|
|
if (is_null($id))
|
|
{
|
|
// We don't have a current user yet? Let's load it!
|
|
if (!is_object($this->currentUser))
|
|
{
|
|
// Get the ID from the session. If nobody is logged in we get 0 (create a new, not logged in user)
|
|
$id = $this->container->segment->get('user_id', 0);
|
|
// Load the current user
|
|
$this->currentUser = $this->getUser($id);
|
|
|
|
/**
|
|
* If the current user doesn't exist in the database we get null back. In this case we need to revert to a
|
|
* guest user.
|
|
*/
|
|
if (is_null($this->currentUser))
|
|
{
|
|
$this->currentUser = $this->getUser(0);
|
|
}
|
|
}
|
|
|
|
$user = $this->currentUser;
|
|
}
|
|
else
|
|
{
|
|
// Create a new user
|
|
/** @var UserInterface $user */
|
|
$user = new $this->user_class;
|
|
|
|
// Create and attach the privilege objects
|
|
if (!empty($this->privileges))
|
|
{
|
|
foreach ($this->privileges as $name => $privilegeClass)
|
|
{
|
|
$privilegeObject = new $privilegeClass();
|
|
$user->attachPrivilegePlugin($name, $privilegeObject);
|
|
}
|
|
}
|
|
|
|
// Create and attach the authentication objects
|
|
if (!empty($this->authentications))
|
|
{
|
|
foreach ($this->authentications as $name => $authenticationClass)
|
|
{
|
|
$authenticationObject = new $authenticationClass();
|
|
$user->attachAuthenticationPlugin($name, $authenticationObject);
|
|
}
|
|
}
|
|
|
|
$data = null;
|
|
|
|
if (!empty($id))
|
|
{
|
|
try
|
|
{
|
|
// Load the data from the database
|
|
$db = $this->container->db;
|
|
$query = $db->getQuery(true)
|
|
->select('*')
|
|
->from($db->qn($this->user_table))
|
|
->where($db->qn('id') . ' = ' . $db->q($id));
|
|
$db->setQuery($query);
|
|
$data = $db->loadObject();
|
|
}
|
|
catch (\Exception $e)
|
|
{
|
|
$data = new \stdClass();
|
|
}
|
|
|
|
if (!is_object($data))
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Bind the data to the user object
|
|
if (is_object($data))
|
|
{
|
|
$user->bind($data);
|
|
}
|
|
}
|
|
|
|
// Finally, return the user object
|
|
return $user;
|
|
}
|
|
|
|
/**
|
|
* Get user by username
|
|
*
|
|
* @param string $username The username of the user to load
|
|
*
|
|
* @return UserInterface|null A user object if it exists, null if it doesn't
|
|
*/
|
|
public function getUserByUsername($username)
|
|
{
|
|
try
|
|
{
|
|
$db = $this->container->db;
|
|
$query = $db->getQuery(true)
|
|
->select($db->qn('id'))
|
|
->from($db->qn($this->user_table))
|
|
->where($db->qn('username') . ' = ' . $db->q($username));
|
|
$db->setQuery($query);
|
|
$id = $db->loadResult();
|
|
}
|
|
catch (\Exception $e)
|
|
{
|
|
$id = null;
|
|
}
|
|
|
|
if (is_null($id))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return $this->getUser($id);
|
|
}
|
|
|
|
/**
|
|
* Try to log in a user given the username, password and any additional parameters which may be required by the
|
|
* user class
|
|
*
|
|
* @param string $username The username of the user to log in
|
|
* @param string $password The (unhashed) password of the user to log in
|
|
* @param array $params [optional] Any additional parameters you may want to pass to the user object, e.g. 2FA
|
|
*
|
|
* @return boolean True on success
|
|
*
|
|
* @throws \Exception When the login fails
|
|
*/
|
|
public function loginUser($username, $password, $params = array())
|
|
{
|
|
$user = $this->getUserByUsername($username);
|
|
|
|
if (is_null($user))
|
|
{
|
|
throw new \RuntimeException(Text::_('AWF_USER_ERROR_AUTHERROR'), 403);
|
|
}
|
|
|
|
if (!$user->verifyPassword($password, $params))
|
|
{
|
|
throw new \RuntimeException(Text::_('AWF_USER_ERROR_AUTHERROR'), 403);
|
|
}
|
|
|
|
$this->container->segment->set('user_id', $user->getId());
|
|
$this->currentUser = $user;
|
|
}
|
|
|
|
/**
|
|
* Log out the current user. Logging out a user immediately clears the session storage.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function logoutUser()
|
|
{
|
|
$this->currentUser = null;
|
|
$this->container->segment->clear();
|
|
}
|
|
|
|
/**
|
|
* Save the provided user record
|
|
*
|
|
* @param UserInterface $user The user to save
|
|
*
|
|
* @return boolean True on success
|
|
*
|
|
* @throws \RuntimeException If an error occurs when saving the user
|
|
*/
|
|
public function saveUser(UserInterface $user)
|
|
{
|
|
$user->triggerEvent('onBeforeSave');
|
|
|
|
$db = $this->container->db;
|
|
|
|
if ($user->getId())
|
|
{
|
|
$query = $db->getQuery(true)
|
|
->update($db->qn($this->user_table))
|
|
->set($db->qn('username') . ' = ' . $db->q($user->getUsername()))
|
|
->set($db->qn('name') . ' = ' . $db->q($user->getName()))
|
|
->set($db->qn('email') . ' = ' . $db->q($user->getEmail()))
|
|
->set($db->qn('password') . ' = ' . $db->q($user->getPassword()))
|
|
->set($db->qn('parameters') . ' = ' . $db->q($user->getParameters()->toString('JSON')))
|
|
->where($db->qn('id') . ' = ' . $db->q($user->getId()));
|
|
}
|
|
else
|
|
{
|
|
$query = $db->getQuery(true)
|
|
->insert($db->qn($this->user_table))
|
|
->columns(array(
|
|
$db->qn('username'),
|
|
$db->qn('name'),
|
|
$db->qn('email'),
|
|
$db->qn('password'),
|
|
$db->qn('parameters'),
|
|
))->values(
|
|
$db->q($user->getUsername()) . ', ' .
|
|
$db->q($user->getName()) . ', ' .
|
|
$db->q($user->getEmail()) . ', ' .
|
|
$db->q($user->getPassword()) . ', ' .
|
|
$db->q($user->getParameters()->toString('JSON'))
|
|
);
|
|
}
|
|
|
|
$db->setQuery($query);
|
|
$db->execute();
|
|
|
|
$user->triggerEvent('onAfterSave');
|
|
}
|
|
|
|
/**
|
|
* Delete the user given their ID
|
|
*
|
|
* @param integer $id The numeric ID of the user record to delete
|
|
*
|
|
* @return boolean True on success
|
|
*
|
|
* @throws \RuntimeException If an error occurs when saving the user
|
|
*/
|
|
public function deleteUser($id)
|
|
{
|
|
if (empty($id))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
$db = $this->container->db;
|
|
|
|
$query = $db->getQuery(true)
|
|
->delete($db->qn($this->user_table))
|
|
->where($db->qn('id') . ' = ' . $db->q($id));
|
|
|
|
$db->setQuery($query);
|
|
$db->execute();
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Register a privilege plugin class with this user manager
|
|
*
|
|
* @param string $name The name of the privilege management object
|
|
* @param string $privilege The privilege management class name we will be attaching to user objects
|
|
*
|
|
* @return void
|
|
*/
|
|
public function registerPrivilegePlugin($name, $privilege)
|
|
{
|
|
$this->privileges[$name] = $privilege;
|
|
}
|
|
|
|
/**
|
|
* Unregister a privilege plugin class from this user manager
|
|
*
|
|
* @param string $name The name of the privilege management object to unregister
|
|
*
|
|
* @return void
|
|
*/
|
|
public function unregisterPrivilegePlugin($name)
|
|
{
|
|
if (isset($this->privileges[$name]))
|
|
{
|
|
unset($this->privileges[$name]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register a user authentication class with this user manager
|
|
*
|
|
* @param string $name The name of the user authentication object
|
|
* @param string $authentication The user authentication class name we will be attaching to user objects
|
|
*
|
|
* @return void
|
|
*/
|
|
public function registerAuthenticationPlugin($name, $authentication)
|
|
{
|
|
$this->authentications[$name] = $authentication;
|
|
}
|
|
|
|
/**
|
|
* Unregister a user authentication class from this user manager
|
|
*
|
|
* @param string $name The name of the user authentication object to unregister
|
|
*
|
|
* @return mixed
|
|
*/
|
|
public function unregisterAuthenticationPlugin($name)
|
|
{
|
|
if (isset($this->authentications[$name]))
|
|
{
|
|
unset($this->authentications[$name]);
|
|
}
|
|
}
|
|
}
|