Subida del módulo y tema de PrestaShop

This commit is contained in:
Kaloyan
2026-04-09 18:31:51 +02:00
parent 12c253296f
commit 16b3ff9424
39262 changed files with 7418797 additions and 0 deletions

View File

@@ -0,0 +1,368 @@
<?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
*/
require_once __DIR__ . '/../../src/Polyfill/Traits/Controller/AjaxRender.php';
use PrestaShop\Module\PsAccounts\Account\Exception\UnknownStatusException;
use PrestaShop\Module\PsAccounts\Account\Session\Firebase\ShopSession;
use PrestaShop\Module\PsAccounts\Account\ShopUrl;
use PrestaShop\Module\PsAccounts\Account\StatusManager;
use PrestaShop\Module\PsAccounts\AccountLogin\OAuth2Session;
use PrestaShop\Module\PsAccounts\Adapter\Link as AccountsLink;
use PrestaShop\Module\PsAccounts\Installer\Installer;
use PrestaShop\Module\PsAccounts\Log\Logger;
use PrestaShop\Module\PsAccounts\Polyfill\Traits\Controller\AjaxRender;
use PrestaShop\Module\PsAccounts\Provider\ShopProvider;
use PrestaShop\Module\PsAccounts\Service\SentryService;
use PrestaShop\Module\PsAccounts\Service\UpgradeService;
/**
* Controller for all ajax calls.
*/
class AdminAjaxPsAccountsController extends \ModuleAdminController
{
use AjaxRender;
/**
* @var Ps_accounts
*/
public $module;
/**
* @var string
*/
private $alertCss = '
<style>
.acc-flex
{
display: flex !important;
}
.acc-btn
{
display: inline-block !important;
text-align: center !important;
vertical-align: middle !important;
user-select: none !important;
border: 1px solid transparent !important;
padding: .5rem 1rem !important;
font-size: .875rem !important;
line-height: 1.5 !important;
font-weight: 600 !important;
border-width: 1px !important;
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out !important;
cursor: pointer !important;
}
.acc-btn-warning
{
width: max-content !important;
color: #1d1d1b !important;
background-color: #FFF5E5 !important;
border-color: #ffb000 !important;
}
.acc-btn-warning:hover
{
background-color: #ffeccc !important;
}
.acc-btn-warning:focus, .acc-btn-warning.focus
{
background-color: #ffeccc !important;
}
.acc-btn-danger
{
width: max-content !important;
color: #1d1d1b !important;
background-color: #ffe4e6 !important;
border-color: #ba151a !important;
}
.acc-btn-danger:hover
{
background-color: #fdbfbf !important;
}
.acc-btn-danger:focus, .acc-btn-danger.focus
{
background-color: #fdbfbf !important;
}
@media(max-width: 768px)
{
.acc-flex {
flex-direction: column !important;
}
.acc-btn-warning,
.acc-btn-danger
{
margin-top: 1em !important;
}
}
.acc-flex-grow-1
{
-webkit-box-flex: 1 !important;
-ms-flex-positive: 1 !important;
flex-grow: 1 !important;
}
.acc-alert-title
{
font-weight: bold !important;
margin-bottom: .9375rem !important;
}
.acc-list
{
list-style-type: none;
padding-left: 0 !important;
}
.acc-alert
{
}
.acc-alert-warning
{
background-color: #FFF5E5 !important;
position: relative !important;
padding: 16px 15px 16px 56px !important;
font-size: 14px !important;
border: solid 1px #ffb000 !important;
color: #1d1d1b !important;
}
.acc-alert-danger
{
background-color: #ffe4e6 !important;
position: relative !important;
padding: 16px 15px 16px 56px !important;
font-size: 14px !important;
border: solid 1px #ba151a !important;
color: #1d1d1b !important;
}
</style>
';
/**
* @var string
*/
private $translationClass;
/**
* AdminAjaxPsAccountsController constructor.
*
* @throws Exception
*/
public function __construct()
{
parent::__construct();
$this->ajax = true;
$this->content_only = true;
$this->translationClass = self::class;
}
/**
* @return void
*
* @throws Exception
*/
public function ajaxProcessGetOrRefreshToken()
{
try {
/** @var ShopSession $shopSession */
$shopSession = $this->module->getService(ShopSession::class);
header('Content-Type: text/json');
$token = $shopSession->getValidToken();
$this->ajaxRender(
(string) json_encode([
'token' => (string) $token->getJwt(),
'refreshToken' => $token->getRefreshToken(),
])
);
} catch (Exception $e) {
SentryService::captureAndRethrow($e);
}
}
/**
* @return void
*
* @throws Exception
*/
public function ajaxProcessGetOrRefreshAccessToken()
{
try {
/** @var OAuth2Session $oauth2Session */
$oauth2Session = $this->module->getService(OAuth2Session::class);
header('Content-Type: text/json');
$this->ajaxRender(
(string) json_encode([
'token' => (string) $oauth2Session->getOrRefreshAccessToken(),
])
);
} catch (Exception $e) {
SentryService::captureAndRethrow($e);
}
}
/**
* @return void
*/
public function ajaxProcessGetNotifications()
{
$notifications = [];
try {
$notifications = array_merge(
$this->getNotificationsUpgradeFailed(),
$this->getNotificationsUrlMismatch()
);
} catch (\Exception $e) {
Logger::getInstance()->error($e->getMessage());
} catch (\Throwable $e) {
Logger::getInstance()->error($e->getMessage());
}
$this->ajaxRender(
(string) json_encode($notifications ?: [])
);
}
/**
* @return array|array[]
*
* @throws UnknownStatusException
*/
protected function getNotificationsUrlMismatch()
{
/** @var StatusManager $statusManager */
$statusManager = $this->module->getService(StatusManager::class);
if (!$statusManager->identityCreated()) {
return [];
}
$status = $statusManager->getStatus();
$shopId = $this->context->shop->id;
$cloudShopUrl = ShopUrl::createFromStatus($status, $shopId)->trimmed();
/** @var ShopProvider $shopProvider */
$shopProvider = $this->module->getService(ShopProvider::class);
$localShopUrl = $shopProvider->getUrl($shopId)->trimmed();
try {
if ($cloudShopUrl->frontendUrlEquals($localShopUrl)) {
return [];
}
} catch (\InvalidArgumentException $e) {
Logger::getInstance()->error($e->getMessage());
return [];
}
/** @var AccountsLink $link */
$link = $this->module->getService(AccountsLink::class);
$moduleLink = $link->getAdminLink('AdminModules', true, [], [
'configure' => 'ps_accounts',
]);
return [[
'html' => $this->alertCss . '
<div class="alert alert-warning acc-alert acc-alert-warning acc-flex">
<div class="acc-flex-grow-1">
<div class="acc-alert-title">
' . $this->module->l('Action required: confirm your store URL', $this->translationClass) . '
</div>
<p>
' . $this->module->l('We\'ve noticed that your store\'s URL no longer matches the one registered in your PrestaShop Account.', $this->translationClass) . '
<br>
' . $this->module->l('For your services to function properly, you must either confirm this change or create a new identity for your store.', $this->translationClass) . '
</p>
<ul class="acc-list">
<li>- ' . $this->module->l('Current store URL', $this->translationClass) . ': <em>' . $localShopUrl->getFrontendUrl() . '</em></li>
<li>- ' . $this->module->l('URL registered in PrestaShop Account', $this->translationClass) . ': <em>' . $cloudShopUrl->getFrontendUrl() . '</em></li>
</ul>
</div>
<div>
<button class="btn warning btn-outline-warning acc-btn btn-warning acc-btn-warning" onclick="document.location=\'' . $moduleLink . '\'">
' . $this->module->l('Review settings', $this->translationClass) . '
</button>
</div>
</div>
',
]];
}
/**
* @return array|array[]
*/
protected function getNotificationsUpgradeFailed()
{
/** @var StatusManager $statusManager */
$statusManager = $this->module->getService(StatusManager::class);
/** @var UpgradeService $upgradeService */
$upgradeService = $this->module->getService(UpgradeService::class);
if ($upgradeService->getCoreRegisteredVersion() === \Ps_accounts::VERSION &&
(!$statusManager->identityCreated() || $upgradeService->getRegisteredVersion() === \Ps_accounts::VERSION)) {
return [];
}
/** @var AccountsLink $link */
$link = $this->module->getService(AccountsLink::class);
$resetLink = $link->getAdminLink('AdminAjaxPsAccounts', true, [], ['ajax' => 1, 'action' => 'resetModule']);
return [[
'html' => $this->alertCss . '
<div class="alert alert-danger acc-alert acc-alert-danger acc-flex">
<div class="acc-flex-grow-1">
<div class="acc-alert-title">
' . $this->module->l('Action required: reset your PS Account module', $this->translationClass) . '
</div>
<p>' . $this->module->l('A simple reset is needed to finish the update and ensure all your modules are working correctly.', $this->translationClass) . '</p>
</div>
<div>
<button class="btn danger btn-outline-danger acc-btn btn-danger acc-btn-danger"
onclick="this.disabled = true; this.innerHTML = \'' . $this->module->l('Resetting module...', $this->translationClass) . '\'; fetch(\'' . $resetLink . '\').then(response => {document.location.reload();})">
' . $this->module->l('Reset module', $this->translationClass) . '
</button>
</div>
</div>
',
]];
}
/**
* @return void
*/
public function ajaxProcessResetModule()
{
$status = false;
try {
/** @var Installer $installer */
$installer = $this->module->getService(Installer::class);
$status = $installer->resetModule('ps_accounts');
} catch (\Exception $e) {
Logger::getInstance()->error($e->getMessage());
} catch (\Throwable $e) {
Logger::getInstance()->error($e->getMessage());
}
$this->ajaxRender(
(string) json_encode([
'status' => $status,
])
);
}
}

View File

@@ -0,0 +1,241 @@
<?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
*/
require_once __DIR__ . '/../../src/Http/Controller/AbstractAdminAjaxCorsController.php';
use PrestaShop\Module\PsAccounts\Account\Command\CreateIdentityCommand;
use PrestaShop\Module\PsAccounts\Account\Command\MigrateOrCreateIdentityV8Command;
use PrestaShop\Module\PsAccounts\Account\Command\VerifyIdentityCommand;
use PrestaShop\Module\PsAccounts\Account\Exception\RefreshTokenException;
use PrestaShop\Module\PsAccounts\Account\Query\GetContextQuery;
use PrestaShop\Module\PsAccounts\Account\StatusManager;
use PrestaShop\Module\PsAccounts\Context\ShopContext;
use PrestaShop\Module\PsAccounts\Cqrs\CommandBus;
use PrestaShop\Module\PsAccounts\Cqrs\QueryBus;
use PrestaShop\Module\PsAccounts\Http\Controller\AbstractAdminAjaxCorsController;
use PrestaShop\Module\PsAccounts\Log\Logger;
use PrestaShop\Module\PsAccounts\Service\Accounts;
use PrestaShop\Module\PsAccounts\Service\Accounts\AccountsException;
use PrestaShop\Module\PsAccounts\Service\Accounts\AccountsService;
use PrestaShop\Module\PsAccounts\Service\OAuth2;
/**
* Controller for all ajax calls.
*/
class AdminAjaxV2PsAccountsController extends AbstractAdminAjaxCorsController
{
/**
* @var CommandBus
*/
private $commandBus;
/**
* @var QueryBus
*/
private $queryBus;
/**
* @var ShopContext
*/
private $shopContext;
/**
* AdminAjaxV2PsAccountsController constructor.
*
* @throws Exception
*/
public function __construct()
{
parent::__construct();
$this->commandBus = $this->module->getService(CommandBus::class);
$this->queryBus = $this->module->getService(QueryBus::class);
$this->shopContext = $this->module->getService(ShopContext::class);
}
/**
* @return void
*
* @throws Exception
*/
public function ajaxProcessGetContext()
{
$command = new GetContextQuery(
Tools::getValue('source', 'ps_accounts'),
Tools::getValue('context_type', null),
Tools::getValue('context_id', null),
filter_var(Tools::getValue('refresh', false), FILTER_VALIDATE_BOOLEAN)
);
$this->ajaxRender(
(string) json_encode($this->queryBus->handle($command))
);
}
/**
* @return void
*
* @throws Exception
*/
public function ajaxProcessFallbackCreateIdentity()
{
$shopId = Tools::getValue('shop_id', null);
$source = Tools::getValue('source', 'ps_accounts');
if (!$shopId) {
throw new Exception('Shop ID is required for migration or creation.');
}
$this->shopContext->execInShopContext($shopId, function () use ($shopId, $source) {
/** @var StatusManager $statusManager */
$statusManager = $this->module->getService(StatusManager::class);
$statusManager->withThrowException(true);
$command = (new MigrateOrCreateIdentityV8Command($shopId))
->withOrigin(AccountsService::ORIGIN_FALLBACK)
->withSource($source);
$this->commandBus->handle($command);
$statusManager->resetThrowException();
});
$this->ajaxRender(
(string) json_encode([
'success' => true,
])
);
}
/**
* @return void
*
* @throws Exception
*/
public function ajaxProcessRenewIdentity()
{
$shopId = Tools::getValue('shop_id', null);
$source = Tools::getValue('source', 'ps_accounts');
if (!$shopId) {
throw new Exception('Shop ID is required for renew.');
}
$this->shopContext->execInShopContext($shopId, function () use ($shopId, $source) {
$command = (new CreateIdentityCommand($shopId, true))
->withOrigin(AccountsService::ORIGIN_MISMATCH_CREATE)
->withSource($source);
$this->commandBus->handle($command);
});
$this->ajaxRender(
(string) json_encode([
'success' => true,
])
);
}
/**
* @return void
*
* @throws Exception
*/
public function ajaxProcessUpdateIdentity()
{
$shopId = Tools::getValue('shop_id', null);
$source = Tools::getValue('source', 'ps_accounts');
if (!$shopId) {
throw new Exception('Shop ID is required for update.');
}
$this->shopContext->execInShopContext($shopId, function () use ($shopId, $source) {
$command = (new VerifyIdentityCommand($shopId, true))
->withOrigin(AccountsService::ORIGIN_MISMATCH_UPDATE)
->withSource($source);
$this->commandBus->handle($command);
});
$this->ajaxRender(
(string) json_encode([
'success' => true,
])
);
}
/**
* @param \Throwable|\Exception $e
*
* @return void
*/
protected function handleError($e)
{
Logger::getInstance()->error($e);
if ($e instanceof RefreshTokenException) {
$e = $e->getPrevious();
}
if ($e instanceof OAuth2\Exception\ConnectException) {
http_response_code(400);
$this->ajaxRender(
(string) json_encode([
'message' => $e->getMessage(),
'code' => 'oauth-server/connect-error',
//'details' => $e->getDetails(),
])
);
return;
}
if ($e instanceof Accounts\Exception\ConnectException) {
http_response_code(400);
$this->ajaxRender(
(string) json_encode([
'message' => $e->getMessage(),
'code' => 'accounts-api/connect-error',
//'details' => $e->getDetails(),
])
);
return;
}
if ($e instanceof AccountsException) {
http_response_code(400);
$this->ajaxRender(
(string) json_encode([
'message' => $e->getMessage(),
'code' => $e->getErrorCode(),
'details' => $e->getDetails(),
])
);
return;
}
parent::handleError($e);
}
}

View File

@@ -0,0 +1,26 @@
<?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
*/
/**
* @deprecated removed starting ps_accounts 8.0.0
*/
class AdminDebugPsAccountsController extends \ModuleAdminController
{
}

View File

@@ -0,0 +1,26 @@
<?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
*/
/**
* @deprecated removed starting ps_accounts 7.0.0
*/
class AdminLoginController extends \AdminLoginControllerCore
{
}

View File

@@ -0,0 +1,181 @@
<?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
*/
use PrestaShop\Module\PsAccounts\Api\Client\ExternalAssetsClient;
use PrestaShop\Module\PsAccounts\Polyfill\Traits\AdminController\IsAnonymousAllowed;
use PrestaShop\Module\PsAccounts\Service\OAuth2\OAuth2Client;
class AdminLoginPsAccountsController extends \AdminController
{
use IsAnonymousAllowed;
const PARAM_MODE_LOCAL = 'local';
/**
* @var string
*/
public $template = 'login.tpl';
/**
* @var Ps_accounts
*/
public $module;
/**
* @var ExternalAssetsClient
*/
private $externalAssetsClient;
/**
* @throws Exception
*/
public function __construct()
{
$this->bootstrap = true;
parent::__construct();
$this->errors = [];
$this->display_header = false;
/* @phpstan-ignore-next-line */
$this->display_footer = false;
/** @var Ps_accounts $module */
$module = Module::getInstanceByName('ps_accounts');
$this->module = $module;
$this->externalAssetsClient = $this->module->getService(ExternalAssetsClient::class);
if (!headers_sent()) {
header('Login: true');
}
}
/**
* @return void
*/
public function initContent()
{
if ($nb_errors = count($this->errors)) {
$this->context->smarty->assign([
'errors' => $this->errors,
'nbErrors' => $nb_errors,
'shop_name' => Tools::safeOutput((string) Configuration::get('PS_SHOP_NAME')),
'disableDefaultErrorOutPut' => true,
]);
}
$this->setMedia($isNewTheme = false);
$this->initHeader();
parent::initContent();
$this->initFooter();
//force to disable modals
$this->context->smarty->assign('modals', null);
}
/**
* @return bool
*/
public function checkToken()
{
return true;
}
/**
* All BO users can access the login page
*
* @param bool $disable
*
* @return bool
*/
public function viewAccess($disable = false)
{
return true;
}
/**
* @param bool $isNewTheme
*
* @return void
*/
public function setMedia($isNewTheme = false)
{
$this->addCss($this->module->getLocalPath() . '/views/css/login.css' .
'?v=' . urlencode($this->module->version)
);
$this->addJS($this->module->getLocalPath() . '/views/js/login.js' .
'?v=' . urlencode($this->module->version)
);
}
/**
* @param string $tpl_name
*
* @phpstan-ignore-next-line
*
* @return Smarty_Internal_Template
*/
public function createTemplate($tpl_name)
{
/** @var Oauth2Client $oAuth2Client */
$oAuth2Client = $this->module->getService(Oauth2Client::class);
$session = $this->module->getSession();
/* @phpstan-ignore-next-line */
$isoCode = $this->context->currentLocale->getCode();
$this->context->smarty->assign([
/* @phpstan-ignore-next-line */
'shopUrl' => $this->context->shop->getBaseUrl(true),
'oauthRedirectUri' => $oAuth2Client->getRedirectUri(),
'legacyLoginUri' => $this->context->link->getAdminLink(
'AdminLogin', true, [], [
'mode' => self::PARAM_MODE_LOCAL,
]),
'isoCode' => substr($isoCode, 0, 2),
'defaultIsoCode' => 'en',
'testimonials' => $this->getTestimonials(),
'loginError' => $session->remove('loginError'),
'meta_title' => '',
'ssoResendVerificationEmail' => $this->module->getParameter(
'ps_accounts.sso_resend_verification_email_url'
),
]);
/* @phpstan-ignore-next-line */
return $this->context->smarty->createTemplate(
$this->module->getLocalPath() . '/views/templates/admin/' . $this->template,
$this->context->smarty
);
}
/**
* @return array
*/
private function getTestimonials()
{
$res = $this->externalAssetsClient->getTestimonials();
return $res->isSuccessful ? $res->body : [];
}
}

View File

@@ -0,0 +1,310 @@
<?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
*/
require_once __DIR__ . '/../../src/AccountLogin/OAuth2LoginTrait.php';
require_once __DIR__ . '/../../src/Polyfill/Traits/AdminController/IsAnonymousAllowed.php';
use PrestaShop\Module\PsAccounts\Account\Command\IdentifyContactCommand;
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\OAuth2LoginTrait;
use PrestaShop\Module\PsAccounts\AccountLogin\OAuth2Session;
use PrestaShop\Module\PsAccounts\Cqrs\CommandBus;
use PrestaShop\Module\PsAccounts\Log\Logger;
use PrestaShop\Module\PsAccounts\Polyfill\ConfigurationStorageSession;
use PrestaShop\Module\PsAccounts\Polyfill\Traits\AdminController\IsAnonymousAllowed;
use PrestaShop\Module\PsAccounts\Service\AnalyticsService;
use PrestaShop\Module\PsAccounts\Service\OAuth2\OAuth2Service;
use PrestaShop\Module\PsAccounts\Service\OAuth2\Resource\AccessToken;
use PrestaShop\Module\PsAccounts\Service\PsAccountsService;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class AdminOAuth2PsAccountsController extends \ModuleAdminController
{
use OAuth2LoginTrait;
use IsAnonymousAllowed;
/**
* @var Ps_accounts
*/
public $module;
/**
* @var AnalyticsService
*/
private $analyticsService;
/**
* @var PsAccountsService
*/
private $psAccountsService;
/**
* @var CommandBus
*/
private $commandBus;
/**
* @throws PrestaShopException
* @throws Exception
*/
public function __construct()
{
parent::__construct();
$this->analyticsService = $this->module->getService(AnalyticsService::class);
$this->psAccountsService = $this->module->getService(PsAccountsService::class);
$this->commandBus = $this->module->getService(CommandBus::class);
$this->ajax = true;
$this->content_only = true;
}
/**
* @return bool
*/
public function checkToken()
{
return true;
}
/**
* All BO users can access the login page
*
* @param bool $disable
*
* @return bool
*/
public function viewAccess($disable = false)
{
return true;
}
/**
* @return void
*
* @throws PrestaShopException
*/
//public function display()
public function init()
{
try {
$this->oauth2Login();
} catch (AccountLoginException $e) {
$this->onLoginFailed($e);
} catch (Exception $e) {
$this->onLoginFailed(new AccountLoginException($e->getMessage(), null, $e));
}
// why do this at the end of the method ?
parent::init();
}
/**
* @param AccessToken $accessToken
*
* @return bool
*
* @throws EmailNotVerifiedException
* @throws EmployeeNotFoundException
*/
protected function initUserSession(AccessToken $accessToken)
{
$user = $this->getOAuth2Service()->getUserInfo($accessToken->access_token);
Logger::getInstance()->info(
'[OAuth2] ' . (string) print_r($user, true)
);
if ($this->getOAuthAction() === 'identifyPointOfContact') {
$this->commandBus->handle(
(new IdentifyContactCommand($accessToken, $user))
->withSource($this->getSource())
);
return true;
}
$this->getOauth2Session()->setTokenProvider($accessToken);
//$user = $oauth2Session->getUserInfo();
Logger::getInstance()->info(
'[OAuth2] ' . (string) print_r($user, true)
);
$context = $this->context;
$emailVerified = $user->email_verified;
$context->employee = $this->getEmployeeByUidOrEmail($user->sub, $user->email);
if (!$context->employee->id || empty($emailVerified)) {
$context->employee->logout();
if (empty($emailVerified)) {
throw new EmailNotVerifiedException('Your account email is not verified', $user);
}
throw new EmployeeNotFoundException('The email address is not associated to a PrestaShop backoffice account.', $user);
}
$context->employee->remote_addr = (int) ip2long(Tools::getRemoteAddr());
$cookie = $context->cookie;
/* @phpstan-ignore-next-line */
$cookie->id_employee = $context->employee->id;
/* @phpstan-ignore-next-line */
$cookie->email = $context->employee->email;
/* @phpstan-ignore-next-line */
$cookie->profile = $context->employee->id_profile;
/* @phpstan-ignore-next-line */
$cookie->passwd = $context->employee->passwd;
/* @phpstan-ignore-next-line */
$cookie->remote_addr = $context->employee->remote_addr;
if (class_exists('EmployeeSession') && method_exists($cookie, 'registerSession')) {
$cookie->registerSession(new EmployeeSession());
}
if (!Tools::getValue('stay_logged_in')) {
/* @phpstan-ignore-next-line */
$cookie->last_activity = time();
}
$cookie->write();
$this->trackEditionLoginEvent($user);
return true;
}
/**
* @return OAuth2Service
*
* @throws Exception
*/
protected function getOAuth2Service()
{
return $this->module->getService(OAuth2Service::class);
}
/**
* @return mixed
*/
protected function redirectAfterLogin()
{
if ($this->getOAuthAction() === 'identifyPointOfContact') {
$forceSignup = $this->getForceSignup();
$this->getSession()->clear();
$this->closePopup($forceSignup);
}
$returnTo = $this->getReturnTo() ?: 'AdminDashboard';
if (preg_match('/^([A-Z][a-z0-9]+)+$/', $returnTo)) {
$returnTo = $this->context->link->getAdminLink($returnTo);
}
Tools::redirectAdmin($returnTo);
}
/**
* @return mixed
*/
protected function logout()
{
Tools::redirectAdmin(
$this->context->link->getAdminLink('AdminLogin', true, [], [
'logout' => 1,
])
);
}
/**
* @return mixed
*/
protected function onLoginFailedRedirect()
{
if ($this->getOAuthAction() === 'identifyPointOfContact') {
$this->closePopup();
}
$this->logout();
}
/**
* @return SessionInterface
*/
protected function getSession()
{
if (\Context::getContext()->employee->id) {
// FIXME: fallback only for setPointOfContact
return $this->module->getService(ConfigurationStorageSession::class);
}
return $this->module->getSession();
}
/**
* @return OAuth2Session
*/
protected function getOauth2Session()
{
return $this->module->getService(OAuth2Session::class);
}
/**
* @return AnalyticsService
*/
protected function getAnalyticsService()
{
return $this->analyticsService;
}
/**
* @return PsAccountsService
*/
protected function getPsAccountsService()
{
return $this->psAccountsService;
}
/**
* @param bool $forceSignup
*
* @return void
*/
protected function closePopup($forceSignup = false)
{
if ($forceSignup) {
Tools::redirect($this->getSignupUrl());
} else {
echo '
<script type="text/javascript">
window.close();
</script>
';
exit;
}
}
/**
* @return string
*/
protected function getSignupUrl()
{
return $this->module->getParameter('ps_accounts.accounts_ui_url') .
'?signupContext=popup';
}
}

View File

@@ -0,0 +1,11 @@
<?php
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;

View File

@@ -0,0 +1,278 @@
<?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
*/
use PrestaShop\Module\PsAccounts\Account\Exception\RefreshTokenException;
use PrestaShop\Module\PsAccounts\Account\Session\Firebase;
use PrestaShop\Module\PsAccounts\Account\Session\ShopSession;
use PrestaShop\Module\PsAccounts\Account\StatusManager;
use PrestaShop\Module\PsAccounts\Account\Token\NullToken;
use PrestaShop\Module\PsAccounts\Account\Token\Token;
use PrestaShop\Module\PsAccounts\Http\Controller\AbstractV2ShopRestController;
use PrestaShop\Module\PsAccounts\Http\Request\ShopHealthCheckRequest;
use PrestaShop\Module\PsAccounts\Service\Accounts\AccountsService;
use PrestaShop\Module\PsAccounts\Service\OAuth2\OAuth2Client;
use PrestaShop\Module\PsAccounts\Service\OAuth2\OAuth2Exception;
use PrestaShop\Module\PsAccounts\Service\OAuth2\OAuth2Service;
use PrestaShop\Module\PsAccounts\Service\PsAccountsService;
class ps_AccountsApiV2ShopHealthCheckModuleFrontController extends AbstractV2ShopRestController
{
/**
* @var StatusManager
*/
private $statusManager;
/**
* @var OAuth2Client
*/
private $oauth2Client;
/**
* @var ShopSession
*/
private $shopSession;
/**
* @var Firebase\ShopSession
*/
private $firebaseShopSession;
/**
* @var Firebase\OwnerSession
*/
private $firebaseOwnerSession;
/**
* @var PsAccountsService
*/
private $psAccountsService;
/**
* @var AccountsService
*/
private $accountsService;
/**
* @var OAuth2Service
*/
private $oauth2Service;
/**
* @return array
*/
public function getScope()
{
return [
'shop.health',
];
}
/**
* @return array
*/
public function getAudience()
{
return [
'ps_accounts/' . $this->statusManager->getCloudShopId(),
];
}
public function __construct()
{
parent::__construct();
// public healthcheck
$this->authenticated = false;
if ($this->getRequestHeader(self::HEADER_AUTHORIZATION) !== null) {
$this->authenticated = true;
}
$this->statusManager = $this->module->getService(StatusManager::class);
$this->oauth2Client = $this->module->getService(OAuth2Client::class);
$this->shopSession = $this->module->getService(ShopSession::class);
$this->firebaseShopSession = $this->module->getService(Firebase\ShopSession::class);
$this->firebaseOwnerSession = $this->module->getService(Firebase\OwnerSession::class);
$this->accountsService = $this->module->getService(AccountsService::class);
$this->psAccountsService = $this->module->getService(PsAccountsService::class);
$this->oauth2Service = $this->module->getService(OAuth2Service::class);
}
/**
* ?fc=module&module=ps_accounts&controller=apiV1ShopHealthCheck&shop_id=1&autoheal
*
* @param Shop $shop
* @param ShopHealthCheckRequest $request
*
* @return array
*/
public function show(Shop $shop, ShopHealthCheckRequest $request)
{
// $this->assertAudience([
// 'ps_accounts/' . $this->shopIdentity->getShopUuid(),
// ]);
// $this->assertScope([
// 'shop.health',
// ]);
if ($request->autoheal) {
try {
$this->firebaseShopSession->getValidToken();
$this->firebaseOwnerSession->getValidToken();
} catch (RefreshTokenException $e) {
}
}
$firebaseShopToken = $this->firebaseShopSession->getToken();
$firebaseOwnerToken = $this->firebaseOwnerSession->getToken();
$shopToken = $this->shopSession->getToken();
$healthCheckMessage = [
'oauth2Client' => $this->oauth2Client->exists(),
'shopLinked' => (bool) $this->statusManager->getCloudShopId(),
'isSsoEnabled' => $this->psAccountsService->getLoginActivated(),
'oauthToken' => $this->tokenInfos($shopToken),
'firebaseOwnerToken' => $this->tokenInfos($firebaseOwnerToken),
'firebaseShopToken' => $this->tokenInfos($firebaseShopToken),
'fopenActive' => (bool) ini_get('allow_url_fopen'),
'curlActive' => extension_loaded('curl'), //function_exists('curl_version'),
'oauthApiConnectivity' => $this->getOauthApiStatus(),
'accountsApiConnectivity' => $this->getAccountsApiStatus(),
'serverUTC' => time(),
'mysqlUTC' => $this->getDatabaseTimestamp(),
'env' => [
'oauth2Url' => $this->module->getParameter('ps_accounts.oauth2_url'),
'accountsApiUrl' => $this->module->getParameter('ps_accounts.accounts_api_url'),
'accountsUiUrl' => $this->module->getParameter('ps_accounts.accounts_ui_url'),
'accountsCdnUrl' => $this->module->getParameter('ps_accounts.accounts_cdn_url'),
'testimonialsUrl' => $this->module->getParameter('ps_accounts.testimonials_url'),
'checkApiSslCert' => $this->module->getParameter('ps_accounts.check_api_ssl_cert'),
],
];
if ($this->authenticated) {
$healthCheckMessage = array_merge($healthCheckMessage, [
// 'shopId' => $shop->id,
// 'shopBoUri' => '',
'psVersion' => _PS_VERSION_,
'moduleVersion' => Ps_accounts::VERSION,
'phpVersion' => phpversion(),
'cloudShopId' => (string) $this->statusManager->getCloudShopId(),
'shopName' => $shop->name,
'ownerEmail' => (string) $this->statusManager->getPointOfContactEmail(),
]);
}
return $healthCheckMessage;
}
/**
* {
* "aud": ["shop_58d..."],
* "client_id": "374c21dd-8b34-...",
* "exp": {
* "date": "2024-06-11 17:53:26.000000",
* "timezone_type": 1,
* "timezone": "+00:00"
* },
* "ext": {
* },
* "iat": {
* "date": "2024-06-11 16:53:26.000000",
* "timezone_type": 1,
* "timezone": "+00:00"
* },
* "iss": "https://oauth.prestashop.com",
* "jti": "bd21ec1d-bc08-458a-bc2b-c29b7cc5abcd",
* "nbf": {
* "date": "2024-06-11 16:53:26.000000",
* "timezone_type": 1,
* "timezone": "+00:00"
* },
* "scp": [],
* "sub": "374c21dd-8b34-..."
* }
*
* @param Token $token
*
* @return array
*/
private function tokenInfos(Token $token)
{
$jwt = $token->getJwt();
if ($jwt instanceof NullToken) {
return [];
}
$claims = $jwt->claims();
/** @var DateTimeImmutable $iat */
$iat = $claims->get('iat');
/** @var DateTimeImmutable $exp */
$exp = $claims->get('exp');
return [
'issuer' => $claims->get('iss'),
'issuedAt' => $iat->getTimestamp(),
'expireAt' => $exp->getTimestamp(),
'isExpired' => $token->isExpired(),
];
}
/**
* @return bool
*/
private function getAccountsApiStatus()
{
$response = $this->accountsService->healthCheck();
return $response->isSuccessful;
}
/**
* @return bool
*/
public function getOauthApiStatus()
{
try {
$this->oauth2Service->getWellKnown();
return true;
} catch (OAuth2Exception $e) {
return false;
}
}
/**
* @return int
*/
private function getDatabaseTimestamp()
{
try {
$row = \Db::getInstance()->getRow('SELECT NOW() AS utc');
if (is_array($row) && isset($row['utc'])) {
return (new DateTime($row['utc']))->getTimestamp();
}
} catch (Exception $e) {
}
return 0;
}
}

View File

@@ -0,0 +1,84 @@
<?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
*/
use PrestaShop\Module\PsAccounts\Account\ProofManager;
use PrestaShop\Module\PsAccounts\Account\StatusManager;
use PrestaShop\Module\PsAccounts\Http\Controller\AbstractV2ShopRestController;
class ps_AccountsApiV2ShopProofModuleFrontController extends AbstractV2ShopRestController
{
/**
* @var bool
*/
protected $authenticated = true;
/**
* @var ProofManager
*/
private $proofManager;
/**
* @var StatusManager
*/
private $statusManager;
/**
* @return array
*/
public function getScope()
{
return [
'shop.proof.read',
];
}
/**
* @return array
*/
public function getAudience()
{
return [
'ps_accounts/' . $this->statusManager->getCloudShopId(),
];
}
public function __construct()
{
parent::__construct();
$this->proofManager = $this->module->getService(ProofManager::class);
$this->statusManager = $this->module->getService(StatusManager::class);
}
/**
* @param Shop $shop
* @param array $payload
*
* @return array
*/
public function show(Shop $shop, array $payload)
{
$this->statusManager->invalidateCache();
return [
'proof' => $this->proofManager->getProof(),
];
}
}

View File

@@ -0,0 +1,11 @@
<?php
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;

View File

@@ -0,0 +1,11 @@
<?php
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;