Current File : /var/www/vinorea/modules/ps_accounts/src/Http/Controller/AbstractV2RestController.php
<?php
/**
 * Copyright since 2007 PrestaShop SA and Contributors
 * PrestaShop is an International Registered Trademark & Property of PrestaShop SA
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License version 3.0
 * that is bundled with this package in the file LICENSE.md.
 * It is also available through the world-wide-web at this URL:
 * https://opensource.org/licenses/AFL-3.0
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@prestashop.com so we can send you a copy immediately.
 *
 * @author    PrestaShop SA and Contributors <contact@prestashop.com>
 * @copyright Since 2007 PrestaShop SA and Contributors
 * @license   https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
 */

namespace PrestaShop\Module\PsAccounts\Http\Controller;

use Context;
use PrestaShop\Module\PsAccounts\Http\Exception\HttpException;
use PrestaShop\Module\PsAccounts\Http\Exception\UnauthorizedException;
use PrestaShop\Module\PsAccounts\Service\OAuth2\OAuth2Service;
use PrestaShop\Module\PsAccounts\Service\OAuth2\Token\Validator\Exception\AudienceInvalidException;
use PrestaShop\Module\PsAccounts\Service\OAuth2\Token\Validator\Exception\ScopeInvalidException;
use PrestaShop\Module\PsAccounts\Service\OAuth2\Token\Validator\Exception\SignatureInvalidException;
use PrestaShop\Module\PsAccounts\Service\OAuth2\Token\Validator\Exception\TokenExpiredException;
use PrestaShop\Module\PsAccounts\Service\OAuth2\Token\Validator\Exception\TokenInvalidException;
use PrestaShop\Module\PsAccounts\Service\OAuth2\Token\Validator\Validator;
use PrestaShop\Module\PsAccounts\Service\SentryService;

abstract class AbstractV2RestController extends AbstractRestController
{
    /**
     * @var object
     */
    protected $token;

    /**
     * @var Validator
     */
    protected $validator;

    public function __construct()
    {
        parent::__construct();

        $this->validator = new Validator(
            $this->module->getService(OAuth2Service::class)
        );
    }

    /**
     * Controller level scopes
     *
     * @return array
     */
    protected function getScope()
    {
        return [];
    }

    /**
     * Controller level audiences
     *
     * @return array
     */
    protected function getAudience()
    {
        return [];
    }

    /**
     * Controller's entry point
     *
     * @return void
     *
     * @throws \PrestaShopException
     */
    // public function init()
    // public function displayAjax()
    public function postProcess()
    {
        try {
            if ($this->authenticated) {
                $this->checkAuthorization();
            }

            $payload = $this->decodePayload();
            $httpMethod = $this->extractMethod($payload);

            $this->dispatchVerb($httpMethod, $payload);
        } catch (\Throwable $e) {
            $this->handleException($e);
            /* @phpstan-ignore-next-line */
        } catch (\Exception $e) {
            $this->handleException($e);
        }
    }

    /**
     * @param int|null $defaultShopId
     *
     * @return array
     */
    protected function decodePayload($defaultShopId = null)
    {
        if ($defaultShopId === null) {
            $defaultShopId = Context::getContext()->shop->id;
        }

        if (in_array($_SERVER['REQUEST_METHOD'], ['POST', 'PUT', 'PATCH'], true)) {
            $decoded = json_decode(file_get_contents('php://input'), true);
            $payload = json_last_error() === JSON_ERROR_NONE && is_array($decoded) ? $decoded : [];
        } else {
            $payload = $_GET;
        }

        if (!isset($payload['shop_id'])) {
            $payload['shop_id'] = $defaultShopId;
        }

        return $this->initShopContext($payload);
    }

    /**
     * @param array $payload
     *
     * @return array
     */
    protected function initShopContext(array $payload)
    {
        $shop = new \Shop((int) $payload['shop_id']);
        if ($shop->id) {
            $this->setContextShop($shop);
        }

        return $payload;
    }

    /**
     * @param array $payload
     *
     * @return mixed
     */
    protected function extractMethod(array & $payload)
    {
        $method = $_SERVER['REQUEST_METHOD'];
        // detect method from payload (hack with some shop server configuration)
        if (isset($payload['method'])) {
            $method = $payload['method'];
            unset($payload['method']);
        }

        return $method;
    }

//    /**
//     * @return bool
//     */
//    protected function displayMaintenancePage()
//    {
//        return false;
//    }

    /**
     * @return true
     *
     * @throws UnauthorizedException
     */
    protected function checkAuthorization()
    {
        $authorizationHeader = $this->getRequestHeader('Authorization');
        if (!isset($authorizationHeader)) {
            throw new UnauthorizedException('Authorization header is required.');
        }

        $jwtString = trim(str_replace('Bearer', '', $authorizationHeader));

        $errorMsg = 'Invalid token';
        try {
            $this->token = $this->validator->validateToken($jwtString, $this->getScope(), $this->getAudience());

            return true;
        } catch (SignatureInvalidException $e) {
        } catch (AudienceInvalidException $e) {
            $errorMsg = 'Invalid audience';
        } catch (ScopeInvalidException $e) {
            $errorMsg = 'Invalid scope';
        } catch (TokenExpiredException $e) {
        } catch (TokenInvalidException $e) {
        }
        $this->module->getLogger()->error($e);

        throw new UnauthorizedException($errorMsg, 0, $e);
    }

    /**
     * Method level scope checking
     *
     * @param array $scope
     *
     * @return void
     *
     * @throws UnauthorizedException
     */
    protected function assertScope(array $scope)
    {
        if (!$this->authenticated) {
            return;
        }

        try {
            $this->validator->validateScope($this->token, $scope);
        } catch (ScopeInvalidException $e) {
            $this->module->getLogger()->error($e);

            throw new UnauthorizedException('Invalid scope', 0, $e);
        }
    }

    /**
     * Method level audience checking
     *
     * @param array $audience
     *
     * @return void
     *
     * @throw UnauthorizedException
     */
    protected function assertAudience(array $audience)
    {
        if (!$this->authenticated) {
            return;
        }

        try {
            $this->validator->validateAudience($this->token, $audience);
        } catch (AudienceInvalidException $e) {
            $this->module->getLogger()->error($e);

            throw new UnauthorizedException('Invalid audience', 0, $e);
        }
    }

    /**
     * @param \Throwable|\Exception $e
     * @param string|null $message
     *
     * @return void
     */
    protected function handleException($e, $message = null)
    {
        if ($e instanceof HttpException) {
            $this->module->getLogger()->error($e);

            $this->dieWithResponseJson([
                'error' => true,
                'message' => $message ?: $e->getMessage(),
            ], $e->getStatusCode());
        } else {
            SentryService::capture($e);

            $this->dieWithResponseJson([
                'error' => true,
                'message' => $message ?: 'Failed processing your request',
            ], 500);
        }
    }
}