<?php
/**
* Pimcore
*
* This source file is available under two different licenses:
* - GNU General Public License version 3 (GPLv3)
* - Pimcore Commercial License (PCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
* @license http://www.pimcore.org/license GPLv3 and PCL
*/
namespace Pimcore\Tool;
use Defuse\Crypto\Crypto;
use Defuse\Crypto\Exception\CryptoException;
use Pimcore\Logger;
use Pimcore\Model\User;
use Pimcore\Tool;
use Symfony\Component\HttpFoundation\Request;
class Authentication
{
/**
* @deprecated
*
* @param string $username
* @param string $password
*
* @return null|User
*/
public static function authenticatePlaintext($username, $password)
{
trigger_deprecation(
'pimcore/pimcore',
'10.6',
sprintf('%s is deprecated and will be removed in Pimcore 11', __METHOD__),
);
/** @var User $user */
$user = User::getByName($username);
// user needs to be active, needs a password and an ID (do not allow system user to login, ...)
if (self::isValidUser($user)) {
if (self::verifyPassword($user, $password)) {
$user->setLastLoginDate(); //set user current login date
return $user;
}
}
return null;
}
/**
* @param Request|null $request
*
* @return User|null
*/
public static function authenticateSession(Request $request = null)
{
if (null === $request) {
$request = \Pimcore::getContainer()->get('request_stack')->getCurrentRequest();
if (null === $request) {
return null;
}
}
if (!Session::requestHasSessionId($request, true)) {
// if no session cookie / ID no authentication possible, we don't need to start a session
return null;
}
$session = Session::getReadOnly();
$user = $session->get('user');
if ($user instanceof User) {
// renew user
$user = User::getById($user->getId());
if (self::isValidUser($user)) {
return $user;
}
}
return null;
}
/**
* @deprecated
*
* @throws \Exception
*
* @return User
*/
public static function authenticateHttpBasic()
{
trigger_deprecation(
'pimcore/pimcore',
'10.6',
sprintf('%s is deprecated and will be removed in Pimcore 11', __METHOD__),
);
// we're using Sabre\HTTP for basic auth
$request = \Sabre\HTTP\Sapi::getRequest();
$response = new \Sabre\HTTP\Response();
$auth = new \Sabre\HTTP\Auth\Basic(Tool::getHostname(), $request, $response);
$result = $auth->getCredentials();
if (is_array($result)) {
list($username, $password) = $result;
$user = self::authenticatePlaintext($username, $password);
if ($user) {
return $user;
}
}
$auth->requireLogin();
$response->setBody('Authentication required');
Logger::error('Authentication Basic (WebDAV) required');
\Sabre\HTTP\Sapi::sendResponse($response);
die();
}
/**
* @param string $token
* @param bool $adminRequired
*
* @return null|User
*/
public static function authenticateToken($token, $adminRequired = false)
{
$username = null;
$timestamp = null;
try {
$decrypted = self::tokenDecrypt($token);
list($timestamp, $username) = $decrypted;
} catch (CryptoException $e) {
return null;
}
$user = User::getByName($username);
if (self::isValidUser($user)) {
if ($adminRequired && !$user->isAdmin()) {
return null;
}
try {
$timeZone = date_default_timezone_get();
date_default_timezone_set('UTC');
if ($timestamp > time() || $timestamp < (time() - (60 * 60 * 24))) {
return null;
}
} finally {
date_default_timezone_set($timeZone);
}
return $user;
}
return null;
}
/**
* @param User $user
* @param string $password
*
* @return bool
*/
public static function verifyPassword($user, $password)
{
if (!$user->getPassword()) {
// do not allow logins for users without a password
return false;
}
$password = self::preparePlainTextPassword($user->getName(), $password);
if (!password_verify($password, $user->getPassword())) {
return false;
}
$config = \Pimcore::getContainer()->getParameter('pimcore.config')['security']['password'];
if (password_needs_rehash($user->getPassword(), $config['algorithm'], $config['options'])) {
$user->setPassword(self::getPasswordHash($user->getName(), $password));
$user->save();
}
return true;
}
/**
* @param User|null $user
*
* @return bool
*/
public static function isValidUser($user)
{
if ($user instanceof User && $user->isActive() && $user->getId() && $user->getPassword()) {
return true;
}
return false;
}
/**
* @internal
*
* @param string $username
* @param string $plainTextPassword
*
* @return string
*
* @throws \Exception
*/
public static function getPasswordHash($username, $plainTextPassword)
{
$password = self::preparePlainTextPassword($username, $plainTextPassword);
$config = \Pimcore::getContainer()->getParameter('pimcore.config')['security']['password'];
if ($hash = password_hash($password, $config['algorithm'], $config['options'])) {
return $hash;
}
throw new \Exception('Unable to create password hash for user: ' . $username);
}
/**
* @param string $username
* @param string $plainTextPassword
*
* @return string
*/
private static function preparePlainTextPassword($username, $plainTextPassword)
{
// plaintext password is prepared as digest A1 hash, this is to be backward compatible because this was
// the former hashing algorithm in pimcore (< version 2.1.1)
return md5($username . ':pimcore:' . $plainTextPassword);
}
/**
* @internal
*
* @param string $username
*
* @return string
*/
public static function generateToken($username)
{
$secret = \Pimcore::getContainer()->getParameter('secret');
$data = time() - 1 . '|' . $username;
$token = Crypto::encryptWithPassword($data, $secret);
return $token;
}
/**
* @param string $token
*
* @return array
*/
private static function tokenDecrypt($token)
{
$secret = \Pimcore::getContainer()->getParameter('secret');
$decrypted = Crypto::decryptWithPassword($token, $secret);
return explode('|', $decrypted);
}
}