Source of file Authenticator.php

Size: 5,837 Bytes - Last Modified: 2020-10-24T02:46:31+00:00

/home/travis/build/NextDom/nextdom-core/src/Rest/Authenticator.php

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
<?php

/*
* This file is part of the NextDom software (https://github.com/NextDom or http://nextdom.github.io).
* Copyright (c) 2018 NextDom.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

namespace NextDom\Rest;

use NextDom\Helpers\Api;
use NextDom\Managers\UserManager;
use NextDom\Model\Entity\User;
use ReallySimpleJWT\Exception\TokenValidatorException;
use ReallySimpleJWT\Token;
use Symfony\Component\HttpFoundation\Request;

// user access 10 hours
define('TOKEN_EXPIRATION_TIME', 3600 * 10);

class Authenticator
{
    /**
     * @var Authenticator Instance
     */
    private static $instance;

    /**
     * @var Request Content of the request
     */
    private $request;

    /**
     * @var bool If query is authenticated
     */
    private $authenticated = false;

    /**
     * @var User Connected user
     */
    private $connectedUser = null;

    /**
     * @var string Secret key for token
     */
    private $secret;

    /**
     * Private authenticator constructor with request for singleton
     *
     * @param Request $request
     */
    private function __construct(Request $request)
    {
        global $CONFIG;
        $this->request = $request;
        $this->secret = $CONFIG['secretKey'];
    }

    /**
     * Init authenticator instance
     *
     * @param Request $request
     *
     * @return Authenticator
     */
    public static function init(Request $request): Authenticator
    {
        self::$instance = new self($request);
        return self::$instance;
    }

    /**
     * Get instance of the singleton
     *
     * @return Authenticator Instance of the singleton
     */
    public static function getInstance()
    {
        return self::$instance;
    }

    /**
     * Test if query use authentication
     *
     * @return bool True if query use authentication
     */
    public function supportAuthentication(): bool
    {
        if ($this->request->headers->has('X-AUTH-TOKEN')) {
            return true;
        }
        return false;
    }

    /**
     * Authentication state of the request
     *
     * @return bool True if user send token
     */
    public function isAuthenticated(): bool
    {
        return $this->authenticated;
    }

    /**
     * Check user credentials
     *
     * @param string $login User login
     * @param string $password User passowrd
     *
     * @return User|false User object or false
     *
     * @throws \Exception
     */
    public function checkCredentials(string $login, string $password)
    {
        $user = UserManager::connect($login, $password);
        return $user;
    }

    /**
     * Create user token for next request
     *
     * @param User $user
     *
     * @return string User token
     *
     * @throws \NextDom\Exceptions\CoreException
     * @throws \ReflectionException
     */
    public function createTokenForUser(User $user): string
    {
        $token = Token::getToken($user->getId(), $this->secret, time() + TOKEN_EXPIRATION_TIME, 'localhost');
        // Save token in database
        $user->setOptions('token', $token);
        $user->save();

        return $token;
    }

    /**
     * Check if sended token is valid
     *
     * @return bool True if the token is valid
     *
     * @throws \Exception
     */
    public function checkSendedToken(): bool
    {
        $this->authenticated = false;

        if ($this->secret !== null) {
            try {
                // Try JWT token first
                if (Token::validate($this->request->headers->get('X-AUTH-TOKEN'), $this->secret)) {
                    $this->connectedUser = $this->getUserFromToken();
                    if (is_object($this->connectedUser)) {
                        $this->authenticated = true;
                    }
                }
            } catch (TokenValidatorException $e) {

            }
        }

        return $this->authenticated;
    }

    /**
     * Get the user from the token
     *
     * @return User|null User
     * @throws \Exception
     */
    private function getUserFromToken()
    {
        $user = null;
        $payload = Token::getPayload($this->request->headers->get('X-AUTH-TOKEN'));
        if (!empty($payload)) {
            $payloadData = json_decode($payload, true);
            if (isset($payloadData['user_id'])) {
                $user = UserManager::byId($payloadData['user_id']);
            }
        }
        return $user;
    }

    /**
     * Check API Key sended in URL (?apikey=MY_KEY)\n
     * Consider connected like the first admin in database
     *
     * @return bool True if API Key works
     *
     * @throws \Exception
     */
    public function checkApiKey(): bool
    {
        $apiKey = $this->request->query->get('apikey');
        if ($apiKey !== null) {
            if ($apiKey == Api::getApiKey('core')) {
                $adminUser = UserManager::byProfils('admin', true);
                if (count($adminUser) > 0) {
                    $this->connectedUser = $adminUser[0];
                }
                $this->authenticated = true;
            }
        }
        return $this->authenticated;
    }

    /**
     * Get current connected user
     *
     * @return User Connected user
     */
    public function getConnectedUser()
    {
        return $this->connectedUser;
    }
}