HEX
Server: Apache
System: Linux srv13.cpanelhost.cl 3.10.0-962.3.2.lve1.5.38.el7.x86_64 #1 SMP Thu Jun 18 05:28:41 EDT 2020 x86_64
User: cca63905 (4205)
PHP: 7.3.20
Disabled: NONE
Upload Files
File: /home4/cca63905/public_html/nueva/modules/ps_accounts/src/AccountLogin/OAuth2LoginTrait.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\AccountLogin;

use Employee;
use PrestaShop\Module\PsAccounts\AccountLogin\Exception\AccountLoginException;
use PrestaShop\Module\PsAccounts\AccountLogin\Exception\EmailNotVerifiedException;
use PrestaShop\Module\PsAccounts\AccountLogin\Exception\EmployeeNotFoundException;
use PrestaShop\Module\PsAccounts\AccountLogin\Exception\Oauth2LoginException;
use PrestaShop\Module\PsAccounts\Context\ShopContext;
use PrestaShop\Module\PsAccounts\Entity\EmployeeAccount;
use PrestaShop\Module\PsAccounts\Log\Logger;
use PrestaShop\Module\PsAccounts\Repository\EmployeeAccountRepository;
use PrestaShop\Module\PsAccounts\Service\AnalyticsService;
use PrestaShop\Module\PsAccounts\Service\OAuth2\OAuth2Exception;
use PrestaShop\Module\PsAccounts\Service\OAuth2\OAuth2Service;
use PrestaShop\Module\PsAccounts\Service\OAuth2\Resource\AccessToken;
use PrestaShop\Module\PsAccounts\Service\OAuth2\Resource\UserInfo;
use PrestaShop\Module\PsAccounts\Service\PsAccountsService;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Tools;

trait OAuth2LoginTrait
{
    /**
     * @return OAuth2Service
     */
    abstract protected function getOAuth2Service();

    /**
     * @param AccessToken $accessToken
     *
     * @return bool
     */
    abstract protected function initUserSession(AccessToken $accessToken);

    /**
     * @return mixed
     */
    abstract protected function redirectAfterLogin();

    /**
     * @return mixed
     */
    abstract protected function logout();

    /**
     * @return mixed
     */
    abstract protected function onLoginFailedRedirect();

    /**
     * @return SessionInterface
     */
    abstract protected function getSession();

    /**
     * @return OAuth2Session
     */
    abstract protected function getOauth2Session();

    /**
     * @return AnalyticsService
     */
    abstract protected function getAnalyticsService();

    /**
     * @return PsAccountsService
     */
    abstract protected function getPsAccountsService();

    /**
     * @return string
     */
    abstract protected function getSignupUrl();

    /**
     * @return mixed
     *
     * @throws EmailNotVerifiedException
     * @throws EmployeeNotFoundException
     * @throws Oauth2LoginException
     * @throws \Exception
     */
    public function oauth2Login()
    {
        $shopId = Tools::getValue('shop_id', $this->getShopId() ?: \Context::getContext()->shop->id);

        /** @var ShopContext $shopContext */
        $shopContext = $this->module->getService(ShopContext::class);

        return $shopContext->execInShopContext($shopId, function () use ($shopId) {
            // FIXME: rework multishop context management
            //\Shop::setContext(\Shop::CONTEXT_SHOP, $shopId);

            $apiClient = $this->getOAuth2Service();

            //$this->getSession()->start();
            $session = $this->getSession();
            $oauth2Session = $this->getOauth2Session();

            $returnTo = Tools::getValue($this->getReturnToParam());
            $error = Tools::getValue('error', '');
            $state = Tools::getValue('state', '');
            $code = Tools::getValue('code', '');
            $action = Tools::getValue('action', 'login');
            $source = Tools::getValue('source', 'ps_accounts');
            $forceSignup = Tools::getValue('forceSignup', false);

            if (!empty($error)) {
                // Got an error, probably user denied access
                throw new \Exception('Got error: ' . $error);
            // If we don't have an authorization code then get one
            } elseif (empty($code)) {
                // cleanup existing accessToken
                $oauth2Session->clear();

                $this->setReturnTo($returnTo);
                $this->setOAuthAction($action);
                $this->setSource($source);
                $this->setShopId($shopId);
                $this->setForceSignup($forceSignup);

                $this->oauth2Redirect(Tools::getValue('locale', 'en'), $shopId);

            // Check given state against previously stored one to mitigate CSRF attack
            } elseif (empty($state) || ($session->has('oauth2state') && $state !== $session->get('oauth2state'))) {
                $session->remove('oauth2state');

                throw new \Exception('Invalid state');
            } else {
                $this->assertValidCode($code);

                try {
                    $accessToken = $apiClient->getAccessTokenByAuthorizationCode(
                        $code,
                        $this->getSession()->get('oauth2pkceCode'),
                        [],
                        [],
                        $shopId
                    );
                } catch (OAuth2Exception $e) {
                    throw new Oauth2LoginException($e->getMessage(), null, $e);
                }

                if ($this->initUserSession($accessToken)) {
                    return $this->redirectAfterLogin();
                }
            }
        });
    }

    /**
     * @param string $locale
     * @param int|null $shopId
     *
     * @return void
     *
     * @throws \Exception
     */
    private function oauth2Redirect($locale, $shopId)
    {
        $apiClient = $this->getOAuth2Service();

        $state = $apiClient->getRandomState();
        $pkceCode = $apiClient->getRandomPkceCode();

        $this->getSession()->set('oauth2state', $state);
        $this->getSession()->set('oauth2pkceCode', $pkceCode);

        $authorizationUrl = $apiClient->getAuthorizationUri(
            $state,
            $pkceCode,
            'S256',
            $locale,
            '',
            'login',
            $shopId
        );

        // Redirect the user to the authorization URL.
        header('Location: ' . $authorizationUrl);
        exit;
    }

    /**
     * @param string $code
     *
     * @return void
     */
    private function assertValidCode($code)
    {
        if (!preg_match('/^[^\s\"\';\(\)]+$/', $code)) {
            throw new \InvalidArgumentException('Invalid code');
        }
    }

    /**
     * @param string $msg
     *
     * @return void
     *
     * @throws \Exception
     */
    private function oauth2ErrorLog($msg)
    {
        Logger::getInstance()->error('[OAuth2] ' . $msg);
    }

    /**
     * @return string
     *
     * @throws \Exception
     */
    private function getReturnTo()
    {
        return $this->getSession()->get($this->getReturnToParam(), '');
    }

    /**
     * @param string $returnTo
     *
     * @return void
     *
     * @throws \Exception
     */
    private function setReturnTo($returnTo)
    {
        $this->getSession()->set($this->getReturnToParam(), $returnTo);
    }

    /**
     * @return string
     */
    private function getReturnToParam()
    {
        return 'return_to';
    }

    /**
     * @return string
     */
    private function getOAuthAction()
    {
        return $this->getSession()->get('oauth2action');
    }

    /**
     * @param string $action
     *
     * @return void
     */
    private function setOAuthAction($action)
    {
        $this->getSession()->set('oauth2action', $action);
    }

    /**
     * @return string
     */
    private function getSource()
    {
        return $this->getSession()->get('source');
    }

    /**
     * @param string $source
     *
     * @return void
     */
    private function setSource($source)
    {
        $this->getSession()->set('source', $source);
    }

    /**
     * @return string
     */
    private function getShopId()
    {
        return $this->getSession()->get('shopId');
    }

    /**
     * @param string $shopId
     *
     * @return void
     */
    private function setShopId($shopId)
    {
        $this->getSession()->set('shopId', $shopId);
    }

    /**
     * @return bool
     */
    private function getForceSignup()
    {
        return (bool) $this->getSession()->get('forceSignup', false);
    }

    /**
     * @param bool $forceSignup
     *
     * @return void
     */
    private function setForceSignup($forceSignup)
    {
        $this->getSession()->set('forceSignup', $forceSignup);
    }

    /**
     * @param string $uid
     * @param string $email
     *
     * @return Employee
     */
    protected function getEmployeeByUidOrEmail($uid, $email)
    {
        $repository = new EmployeeAccountRepository();

        try {
            $employeeAccount = $repository->findByUid($uid);

            /* @phpstan-ignore-next-line */
            if ($employeeAccount) {
                $employee = new Employee($employeeAccount->getEmployeeId());
            } else {
                $employeeAccount = new EmployeeAccount();
                $employee = new Employee();
                if (Employee::employeeExists($email)) {
                    $employee->getByEmail($email);
                }
            }

            // Update account
            if ($employee->id) {
                $repository->upsert(
                    $employeeAccount
                        ->setEmployeeId($employee->id)
                        ->setUid($uid)
                        ->setEmail($email)
                );
            }
        } catch (\Exception $e) {
            $employee = new Employee();
            $employee->getByEmail($email);
        }

        return $employee;
    }

    /**
     * @param AccountLoginException $e
     *
     * @return mixed
     */
    protected function onLoginFailed(AccountLoginException $e)
    {
        if ($this->module->isShopEdition() && (
                $e instanceof EmployeeNotFoundException ||
                $e instanceof EmailNotVerifiedException
            )) {
            $this->trackEditionLoginFailedEvent($e);
        }

        $this->oauth2ErrorLog($e->getMessage());
        $this->setLoginError($e->getType());

        return $this->onLoginFailedRedirect();
    }

    /**
     * @param mixed $error
     *
     * @return void
     */
    protected function setLoginError($error)
    {
        $this->getSession()->set('loginError', $error);
    }

    /**
     * @param UserInfo $user
     *
     * @return void
     */
    protected function trackEditionLoginEvent(UserInfo $user)
    {
        if ($this->module->isShopEdition()) {
            $this->getAnalyticsService()->identify(
                $user->sub,
                $user->name,
                $user->email
            );
            $this->getAnalyticsService()->group(
                $user->sub,
                (string) $this->getPsAccountsService()->getShopUuid()
            );
            $this->getAnalyticsService()->trackUserSignedIntoApp(
                $user->sub,
                'smb-edition'
            );
        }
    }

    /**
     * @param EmployeeNotFoundException|EmailNotVerifiedException $e
     *
     * @return void
     */
    protected function trackEditionLoginFailedEvent($e)
    {
        $user = $e->getUser();
        $this->getAnalyticsService()->identify(
            $user->sub,
            $user->name,
            $user->email
        );
        $this->getAnalyticsService()->group(
            $user->sub,
            (string) $this->getPsAccountsService()->getShopUuid()
        );
        $this->getAnalyticsService()->trackBackOfficeSSOSignInFailed(
            $user->sub,
            $e->getType(),
            $e->getMessage()
        );
    }
}