first commit
This commit is contained in:
159
modules/ps_accounts/classes/Service/PsAccountsService.php
Normal file
159
modules/ps_accounts/classes/Service/PsAccountsService.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?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\Service;
|
||||
|
||||
use PrestaShop\Module\PsAccounts\Adapter\Link;
|
||||
use PrestaShop\Module\PsAccounts\Repository\ConfigurationRepository;
|
||||
|
||||
/**
|
||||
* Class PsAccountsService
|
||||
*/
|
||||
class PsAccountsService
|
||||
{
|
||||
/**
|
||||
* @var Link
|
||||
*/
|
||||
protected $link;
|
||||
|
||||
/**
|
||||
* @var ConfigurationRepository
|
||||
*/
|
||||
private $configuration;
|
||||
|
||||
/**
|
||||
* @var \Ps_accounts
|
||||
*/
|
||||
private $module;
|
||||
|
||||
/**
|
||||
* @var ShopTokenService
|
||||
*/
|
||||
private $shopTokenService;
|
||||
|
||||
/**
|
||||
* PsAccountsService constructor.
|
||||
*
|
||||
* @param \Ps_accounts $module
|
||||
* @param ShopTokenService $shopTokenService
|
||||
* @param ConfigurationRepository $configuration
|
||||
* @param Link $link
|
||||
*/
|
||||
public function __construct(
|
||||
\Ps_accounts $module,
|
||||
ShopTokenService $shopTokenService,
|
||||
ConfigurationRepository $configuration,
|
||||
Link $link
|
||||
) {
|
||||
$this->configuration = $configuration;
|
||||
$this->shopTokenService = $shopTokenService;
|
||||
$this->module = $module;
|
||||
$this->link = $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSuperAdminEmail()
|
||||
{
|
||||
return (new \Employee(1))->email;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string | false
|
||||
*/
|
||||
public function getShopUuidV4()
|
||||
{
|
||||
return $this->configuration->getShopUuid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user firebase token.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getOrRefreshToken()
|
||||
{
|
||||
return $this->shopTokenService->getOrRefreshToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRefreshToken()
|
||||
{
|
||||
return $this->shopTokenService->getRefreshToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getToken()
|
||||
{
|
||||
return $this->shopTokenService->getToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailValidated()
|
||||
{
|
||||
return $this->configuration->firebaseEmailIsVerified();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getEmail()
|
||||
{
|
||||
return $this->configuration->getFirebaseEmail();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function isAccountLinked()
|
||||
{
|
||||
/** @var ShopLinkAccountService $shopLinkAccountService */
|
||||
$shopLinkAccountService = $this->module->getService(ShopLinkAccountService::class);
|
||||
|
||||
return $shopLinkAccountService->isAccountLinked();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ajax admin link with token
|
||||
* available via PsAccountsPresenter into page dom,
|
||||
* ex :
|
||||
* let url = window.contextPsAccounts.adminAjaxLink + '&action=unlinkShop'
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws \PrestaShopException
|
||||
*/
|
||||
public function getAdminAjaxUrl()
|
||||
{
|
||||
// Tools::getAdminTokenLite('AdminAjaxPsAccounts'));
|
||||
return $this->link->getAdminLink('AdminAjaxPsAccounts', true, [], ['ajax' => 1]);
|
||||
}
|
||||
}
|
||||
168
modules/ps_accounts/classes/Service/PsBillingService.php
Normal file
168
modules/ps_accounts/classes/Service/PsBillingService.php
Normal file
@@ -0,0 +1,168 @@
|
||||
<?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\Service;
|
||||
|
||||
use Context;
|
||||
use PrestaShop\Module\PsAccounts\Api\Client\ServicesBillingClient;
|
||||
use PrestaShop\Module\PsAccounts\Exception\BillingException;
|
||||
use PrestaShop\Module\PsAccounts\Repository\ConfigurationRepository;
|
||||
|
||||
/**
|
||||
* Construct the psbilling service.
|
||||
*/
|
||||
class PsBillingService
|
||||
{
|
||||
// /**
|
||||
// * @var \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
// */
|
||||
// protected $container;
|
||||
|
||||
/**
|
||||
* @var ConfigurationRepository
|
||||
*/
|
||||
private $configuration;
|
||||
|
||||
/**
|
||||
* @var ShopTokenService
|
||||
*/
|
||||
private $shopTokenService;
|
||||
|
||||
/**
|
||||
* @var ServicesBillingClient
|
||||
*/
|
||||
private $servicesBillingClient;
|
||||
|
||||
/**
|
||||
* PsBillingService constructor.
|
||||
*
|
||||
* @param ServicesBillingClient $servicesBillingClient
|
||||
* @param ShopTokenService $shopTokenService
|
||||
* @param ConfigurationRepository $configuration
|
||||
*/
|
||||
public function __construct(
|
||||
ServicesBillingClient $servicesBillingClient,
|
||||
ShopTokenService $shopTokenService,
|
||||
ConfigurationRepository $configuration
|
||||
) {
|
||||
$this->servicesBillingClient = $servicesBillingClient;
|
||||
$this->shopTokenService = $shopTokenService;
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Override of native function to always retrieve Symfony container instead of legacy admin container on legacy context.
|
||||
// *
|
||||
// * @param string $serviceName
|
||||
// *
|
||||
// * @return mixed
|
||||
// */
|
||||
// public function get($serviceName)
|
||||
// {
|
||||
// if (null === $this->container) {
|
||||
// $this->container = \PrestaShop\PrestaShop\Adapter\SymfonyContainer::getInstance();
|
||||
// }
|
||||
//
|
||||
// return $this->container->get($serviceName);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Create a Billing customer if needed, and subscribe to $planName.
|
||||
* The $module and $planName must exist in PrestaShop Billing.
|
||||
* The $ip parameter will help PS Billing to preselect a tax rate and a currency
|
||||
* from the geolocalized IP. This IP should be the browser IP displaying the backoffice.
|
||||
*
|
||||
* @param string $module the name of the module
|
||||
* @param string $planName The label of the existing plan for this module
|
||||
* @param mixed $shopId an optional shop ID in multishop context. If left false, the current shop will be selected.
|
||||
* @param mixed $customerIp an optional element to help Billing choosing the currency and tax rate (depending on the IP's country) for later paying plan
|
||||
*
|
||||
* @return mixed An array with subscription identifiers if succeed
|
||||
*
|
||||
* @throws \Exception in case of error
|
||||
*/
|
||||
public function subscribeToFreePlan($module, $planName, $shopId = false, $customerIp = null)
|
||||
{
|
||||
if ($shopId !== false) {
|
||||
$this->configuration->setShopId($shopId);
|
||||
}
|
||||
|
||||
$uuid = $this->configuration->getShopUuid();
|
||||
$toReturn = ['shopAccountId' => $uuid];
|
||||
|
||||
if ($uuid && strlen($uuid) > 0) {
|
||||
$billingClient = $this->servicesBillingClient;
|
||||
|
||||
$response = $billingClient->getBillingCustomer($uuid);
|
||||
|
||||
if (!$response || !array_key_exists('httpCode', $response)) {
|
||||
throw new BillingException('Billing customer request failed.', 50);
|
||||
}
|
||||
if ($response['httpCode'] === 404) {
|
||||
$response = $billingClient->createBillingCustomer(
|
||||
$uuid,
|
||||
$customerIp ? ['created_from_ip' => $customerIp] : []
|
||||
);
|
||||
if (!$response || !array_key_exists('httpCode', $response) || $response['httpCode'] !== 201) {
|
||||
throw new BillingException('Billing customer creation failed.', 60);
|
||||
}
|
||||
}
|
||||
$toReturn['customerId'] = $response['body']['customer']['id'];
|
||||
|
||||
$response = $billingClient->getBillingSubscriptions($uuid, $module);
|
||||
if (!$response || !array_key_exists('httpCode', $response) || $response['httpCode'] >= 500) {
|
||||
throw new BillingException('Billing subscriptions request failed.', 51);
|
||||
}
|
||||
|
||||
if ($response['httpCode'] === 404) {
|
||||
$response = $billingClient->createBillingSubscriptions($uuid, $module, ['plan_id' => $planName, 'module' => $module]);
|
||||
if (!$response || !array_key_exists('httpCode', $response) || $response['httpCode'] >= 400) {
|
||||
if ($response && array_key_exists('body', $response)
|
||||
&& array_key_exists('message', $response['body'])
|
||||
&& array_key_exists(0, $response['body']['message'])
|
||||
) {
|
||||
throw new BillingException($response['body']['message'][0]);
|
||||
}
|
||||
throw new BillingException('Billing subscription creation failed.', 65);
|
||||
}
|
||||
|
||||
$toReturn['subscriptionId'] = $response['body']['subscription']['id'];
|
||||
|
||||
return $toReturn;
|
||||
} else {
|
||||
// There is existing subscription. Testing if planName matches the right one.
|
||||
if (array_key_exists('body', $response) && $response['body']
|
||||
&& array_key_exists('subscription', $response['body'])
|
||||
&& array_key_exists('plan_id', $response['body']['subscription'])
|
||||
&& $response['body']['subscription']['plan_id'] === $planName
|
||||
) {
|
||||
$toReturn['subscriptionId'] = $response['body']['subscription']['id'];
|
||||
$this->shopTokenService->getOrRefreshToken();
|
||||
|
||||
return $toReturn;
|
||||
} else {
|
||||
throw new BillingException('Subscription plan name mismatch.', 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new \Exception('Shop account unknown.', 10);
|
||||
}
|
||||
}
|
||||
155
modules/ps_accounts/classes/Service/ShopKeysService.php
Normal file
155
modules/ps_accounts/classes/Service/ShopKeysService.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?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\Service;
|
||||
|
||||
use phpseclib\Crypt\RSA;
|
||||
use PrestaShop\Module\PsAccounts\Exception\SshKeysNotFoundException;
|
||||
use PrestaShop\Module\PsAccounts\Repository\ConfigurationRepository;
|
||||
|
||||
/**
|
||||
* Manage RSA
|
||||
*/
|
||||
class ShopKeysService
|
||||
{
|
||||
const SIGNATURE_DATA = 'data';
|
||||
|
||||
/**
|
||||
* @var RSA
|
||||
*/
|
||||
private $rsa;
|
||||
|
||||
/**
|
||||
* @var ConfigurationRepository
|
||||
*/
|
||||
private $configuration;
|
||||
|
||||
public function __construct(ConfigurationRepository $configuration)
|
||||
{
|
||||
$this->rsa = new RSA();
|
||||
$this->rsa->setHash('sha256');
|
||||
$this->rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
|
||||
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function createPair()
|
||||
{
|
||||
$this->rsa->setPrivateKeyFormat(RSA::PRIVATE_FORMAT_PKCS1);
|
||||
$this->rsa->setPublicKeyFormat(RSA::PUBLIC_FORMAT_PKCS1);
|
||||
|
||||
return $this->rsa->createKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $privateKey
|
||||
* @param string $data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function signData($privateKey, $data)
|
||||
{
|
||||
$this->rsa->loadKey($privateKey, RSA::PRIVATE_FORMAT_PKCS1);
|
||||
|
||||
return base64_encode($this->rsa->sign($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $publicKey
|
||||
* @param string $signature
|
||||
* @param string $data
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function verifySignature($publicKey, $signature, $data)
|
||||
{
|
||||
$this->rsa->loadKey($publicKey, RSA::PUBLIC_FORMAT_PKCS1);
|
||||
|
||||
return $this->rsa->verify($data, base64_decode($signature));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $refresh
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws SshKeysNotFoundException
|
||||
*/
|
||||
public function generateKeys($refresh = true)
|
||||
{
|
||||
if ($refresh || false === $this->hasKeys()) {
|
||||
$key = $this->createPair();
|
||||
$this->configuration->updateAccountsRsaPrivateKey($key['privatekey']);
|
||||
$this->configuration->updateAccountsRsaPublicKey($key['publickey']);
|
||||
|
||||
$this->configuration->updateAccountsRsaSignData(
|
||||
$this->signData(
|
||||
$this->configuration->getAccountsRsaPrivateKey(),
|
||||
self::SIGNATURE_DATA
|
||||
)
|
||||
);
|
||||
|
||||
if (false === $this->hasKeys()) {
|
||||
throw new SshKeysNotFoundException('No RSA keys found for the shop');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*
|
||||
* @throws SshKeysNotFoundException
|
||||
*/
|
||||
public function regenerateKeys()
|
||||
{
|
||||
$this->generateKeys(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasKeys()
|
||||
{
|
||||
return false === (
|
||||
empty($this->configuration->getAccountsRsaPublicKey())
|
||||
|| empty($this->configuration->getAccountsRsaPrivateKey())
|
||||
|| empty($this->configuration->getAccountsRsaSignData())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPublicKey()
|
||||
{
|
||||
return $this->configuration->getAccountsRsaPublicKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSignature()
|
||||
{
|
||||
return $this->configuration->getAccountsRsaSignData();
|
||||
}
|
||||
}
|
||||
388
modules/ps_accounts/classes/Service/ShopLinkAccountService.php
Normal file
388
modules/ps_accounts/classes/Service/ShopLinkAccountService.php
Normal file
@@ -0,0 +1,388 @@
|
||||
<?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\Service;
|
||||
|
||||
use Module;
|
||||
use PrestaShop\Module\PsAccounts\Adapter\Link;
|
||||
use PrestaShop\Module\PsAccounts\Api\Client\ServicesAccountsClient;
|
||||
use PrestaShop\Module\PsAccounts\Configuration\ConfigOptionsResolver;
|
||||
use PrestaShop\Module\PsAccounts\Configuration\Configurable;
|
||||
use PrestaShop\Module\PsAccounts\Exception\HmacException;
|
||||
use PrestaShop\Module\PsAccounts\Exception\OptionResolutionException;
|
||||
use PrestaShop\Module\PsAccounts\Exception\QueryParamsException;
|
||||
use PrestaShop\Module\PsAccounts\Exception\RsaSignedDataNotFoundException;
|
||||
use PrestaShop\Module\PsAccounts\Exception\SshKeysNotFoundException;
|
||||
use PrestaShop\Module\PsAccounts\Provider\ShopProvider;
|
||||
use PrestaShop\Module\PsAccounts\Repository\ConfigurationRepository;
|
||||
use Ps_accounts;
|
||||
|
||||
class ShopLinkAccountService implements Configurable
|
||||
{
|
||||
/**
|
||||
* @var ShopKeysService
|
||||
*/
|
||||
private $shopKeysService;
|
||||
|
||||
/**
|
||||
* @var ShopProvider
|
||||
*/
|
||||
private $shopProvider;
|
||||
|
||||
/**
|
||||
* @var ShopTokenService
|
||||
*/
|
||||
private $shopTokenService;
|
||||
|
||||
/**
|
||||
* @var ConfigurationRepository
|
||||
*/
|
||||
private $configuration;
|
||||
|
||||
/**
|
||||
* @var Link
|
||||
*/
|
||||
private $link;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $accountsUiUrl;
|
||||
|
||||
/**
|
||||
* ShopLinkAccountService constructor.
|
||||
*
|
||||
* @param array $config
|
||||
* @param ShopProvider $shopProvider
|
||||
* @param ShopKeysService $shopKeysService
|
||||
* @param ShopTokenService $shopTokenService
|
||||
* @param ConfigurationRepository $configuration
|
||||
* @param Link $link
|
||||
*
|
||||
* @throws OptionResolutionException
|
||||
*/
|
||||
public function __construct(
|
||||
array $config,
|
||||
ShopProvider $shopProvider,
|
||||
ShopKeysService $shopKeysService,
|
||||
ShopTokenService $shopTokenService,
|
||||
ConfigurationRepository $configuration,
|
||||
Link $link
|
||||
) {
|
||||
$this->accountsUiUrl = $this->resolveConfig($config)['accounts_ui_url'];
|
||||
$this->shopProvider = $shopProvider;
|
||||
$this->shopKeysService = $shopKeysService;
|
||||
$this->shopTokenService = $shopTokenService;
|
||||
$this->configuration = $configuration;
|
||||
$this->link = $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ServicesAccountsClient
|
||||
*/
|
||||
public function getServicesAccountsClient()
|
||||
{
|
||||
/** @var Ps_accounts $module */
|
||||
$module = Module::getInstanceByName('ps_accounts');
|
||||
|
||||
return $module->getService(ServicesAccountsClient::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $bodyHttp
|
||||
* @param string $trigger
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function updateShopUrl($bodyHttp, $trigger)
|
||||
{
|
||||
if (array_key_exists('shop_id', $bodyHttp)) {
|
||||
// id for multishop
|
||||
$this->configuration->setShopId($bodyHttp['shop_id']);
|
||||
}
|
||||
|
||||
$sslEnabled = $this->shopProvider->getShopContext()->sslEnabled();
|
||||
$protocol = $this->shopProvider->getShopContext()->getProtocol();
|
||||
$domain = $sslEnabled ? $bodyHttp['domain_ssl'] : $bodyHttp['domain'];
|
||||
|
||||
$uuid = $this->configuration->getShopUuid();
|
||||
|
||||
$response = false;
|
||||
$boUrl = $this->replaceScheme(
|
||||
$this->link->getAdminLink('AdminModules', true),
|
||||
$protocol . '://' . $domain
|
||||
);
|
||||
|
||||
if ($uuid && strlen($uuid) > 0) {
|
||||
$response = $this->getServicesAccountsClient()->updateShopUrl(
|
||||
$uuid,
|
||||
[
|
||||
'protocol' => $protocol,
|
||||
'domain' => $domain,
|
||||
'boUrl' => $boUrl,
|
||||
'trigger' => $trigger,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $psxName
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws \PrestaShopException
|
||||
*/
|
||||
public function getLinkAccountUrl($psxName)
|
||||
{
|
||||
$callback = $this->replaceScheme(
|
||||
$this->link->getAdminLink('AdminModules', true) . '&configure=' . $psxName
|
||||
);
|
||||
|
||||
$protocol = $this->shopProvider->getShopContext()->getProtocol();
|
||||
$currentShop = $this->shopProvider->getCurrentShop($psxName);
|
||||
$domainName = $this->shopProvider->getShopContext()->sslEnabled() ? $currentShop['domainSsl'] : $currentShop['domain'];
|
||||
|
||||
$queryParams = [
|
||||
'bo' => $callback,
|
||||
'pubKey' => $this->shopKeysService->getPublicKey(),
|
||||
'next' => $this->replaceScheme(
|
||||
$this->link->getAdminLink('AdminConfigureHmacPsAccounts')
|
||||
),
|
||||
'name' => $currentShop['name'],
|
||||
'lang' => $this->shopProvider->getShopContext()->getContext()->language->iso_code,
|
||||
];
|
||||
|
||||
$queryParamsArray = [];
|
||||
foreach ($queryParams as $key => $value) {
|
||||
$queryParamsArray[] = $key . '=' . urlencode($value);
|
||||
}
|
||||
$strQueryParams = implode('&', $queryParamsArray);
|
||||
|
||||
return $this->accountsUiUrl . '/shop/account/link/' . $protocol . '/' . $domainName
|
||||
. '/' . $protocol . '/' . $domainName . '/' . $psxName . '?' . $strQueryParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $queryParams
|
||||
* @param string $rootDir
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws HmacException
|
||||
* @throws QueryParamsException
|
||||
* @throws RsaSignedDataNotFoundException
|
||||
*/
|
||||
public function getVerifyAccountUrl(array $queryParams, $rootDir)
|
||||
{
|
||||
foreach (
|
||||
[
|
||||
'hmac' => '/[a-zA-Z0-9]{8,64}/',
|
||||
'uid' => '/[a-zA-Z0-9]{8,64}/',
|
||||
'slug' => '/[-_a-zA-Z0-9]{8,255}/',
|
||||
] as $key => $value
|
||||
) {
|
||||
if (!array_key_exists($key, $queryParams)) {
|
||||
throw new QueryParamsException('Missing query params');
|
||||
}
|
||||
|
||||
if (!preg_match($value, $queryParams[$key])) {
|
||||
throw new QueryParamsException('Invalid query params');
|
||||
}
|
||||
}
|
||||
|
||||
$this->writeHmac($queryParams['hmac'], $queryParams['uid'], $rootDir . '/upload/');
|
||||
|
||||
if (empty($this->shopKeysService->getSignature())) {
|
||||
throw new RsaSignedDataNotFoundException('RSA signature not found');
|
||||
}
|
||||
|
||||
$url = $this->accountsUiUrl;
|
||||
|
||||
if ('/' === substr($url, -1)) {
|
||||
$url = substr($url, 0, -1);
|
||||
}
|
||||
|
||||
return $url . '/shop/account/verify/' . $queryParams['uid']
|
||||
. '?shopKey=' . urlencode($this->shopKeysService->getSignature());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*
|
||||
* @throws SshKeysNotFoundException
|
||||
*/
|
||||
public function unlinkShop()
|
||||
{
|
||||
$response = $this->getServicesAccountsClient()->deleteShop((string) $this->configuration->getShopUuid());
|
||||
|
||||
// Réponse: 200: Shop supprimé avec payload contenant un message de confirmation
|
||||
// Réponse: 404: La shop n'existe pas (not found)
|
||||
// Réponse: 401: L'utilisateur n'est pas autorisé à supprimer cette shop
|
||||
|
||||
if ($response['status'] && 200 === $response['httpCode']
|
||||
|| 404 === $response['httpCode']) {
|
||||
$this->resetOnboardingData();
|
||||
|
||||
// FIXME regenerate rsa keys
|
||||
$this->shopKeysService->regenerateKeys();
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty onboarding configuration values
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function resetOnboardingData()
|
||||
{
|
||||
$this->configuration->updateAccountsRsaPrivateKey('');
|
||||
$this->configuration->updateAccountsRsaPublicKey('');
|
||||
$this->configuration->updateAccountsRsaSignData('');
|
||||
|
||||
$this->configuration->updateFirebaseIdAndRefreshTokens('', '');
|
||||
$this->configuration->updateFirebaseEmail('');
|
||||
$this->configuration->updateFirebaseEmailIsVerified(false);
|
||||
|
||||
$this->configuration->updateShopUuid('');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $psxName
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws SshKeysNotFoundException
|
||||
* @throws \PrestaShopException
|
||||
*/
|
||||
public function manageOnboarding($psxName)
|
||||
{
|
||||
$this->shopKeysService->generateKeys();
|
||||
|
||||
$this->updateOnboardingData($psxName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only callable during onboarding
|
||||
*
|
||||
* Prepare onboarding data
|
||||
*
|
||||
* @param string $psxName
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \PrestaShopException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function updateOnboardingData($psxName)
|
||||
{
|
||||
$email = \Tools::getValue('email');
|
||||
$emailVerified = \Tools::getValue('emailVerified');
|
||||
$customToken = \Tools::getValue('adminToken');
|
||||
|
||||
if (is_string($customToken)) {
|
||||
if (false === $this->shopKeysService->hasKeys()) {
|
||||
throw new \Exception('SSH keys were not found');
|
||||
}
|
||||
|
||||
if (!$this->shopTokenService->exchangeCustomTokenForIdAndRefreshToken($customToken)) {
|
||||
throw new \Exception('Unable to get Firebase token');
|
||||
}
|
||||
|
||||
if (!empty($email)) {
|
||||
$this->configuration->updateFirebaseEmail($email);
|
||||
|
||||
if (!empty($emailVerified)) {
|
||||
$this->configuration->updateFirebaseEmailIsVerified('true' === $emailVerified);
|
||||
}
|
||||
|
||||
// FIXME : quick and dirty fix
|
||||
\Tools::redirectAdmin(
|
||||
$this->link->getAdminLink('AdminModules', true, [], [
|
||||
'configure' => $psxName,
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isAccountLinked()
|
||||
{
|
||||
return $this->shopTokenService->getToken()
|
||||
&& $this->configuration->getFirebaseEmail();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $config
|
||||
* @param array $defaults
|
||||
*
|
||||
* @return array|mixed
|
||||
*
|
||||
* @throws OptionResolutionException
|
||||
*/
|
||||
public function resolveConfig(array $config, array $defaults = [])
|
||||
{
|
||||
return (new ConfigOptionsResolver([
|
||||
'accounts_ui_url',
|
||||
]))->resolve($config, $defaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $hmac
|
||||
* @param string $uid
|
||||
* @param string $path
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws HmacException
|
||||
*/
|
||||
private function writeHmac($hmac, $uid, $path)
|
||||
{
|
||||
if (!is_dir($path)) {
|
||||
mkdir($path);
|
||||
}
|
||||
|
||||
if (!is_writable($path)) {
|
||||
throw new HmacException('Directory isn\'t writable');
|
||||
}
|
||||
|
||||
file_put_contents($path . $uid . '.txt', $hmac);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param string $replacement
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function replaceScheme($url, $replacement = '')
|
||||
{
|
||||
return preg_replace('/^https?:\/\/[^\/]+/', $replacement, $url);
|
||||
}
|
||||
}
|
||||
150
modules/ps_accounts/classes/Service/ShopTokenService.php
Normal file
150
modules/ps_accounts/classes/Service/ShopTokenService.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?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\Service;
|
||||
|
||||
use Lcobucci\JWT\Parser;
|
||||
use PrestaShop\Module\PsAccounts\Api\Client\FirebaseClient;
|
||||
use PrestaShop\Module\PsAccounts\Repository\ConfigurationRepository;
|
||||
|
||||
class ShopTokenService
|
||||
{
|
||||
/**
|
||||
* @var FirebaseClient
|
||||
*/
|
||||
private $firebaseClient;
|
||||
|
||||
/**
|
||||
* @var ConfigurationRepository
|
||||
*/
|
||||
private $configuration;
|
||||
|
||||
/**
|
||||
* ShopTokenService constructor.
|
||||
*
|
||||
* @param FirebaseClient $firebaseClient
|
||||
* @param ConfigurationRepository $configuration
|
||||
*/
|
||||
public function __construct(
|
||||
FirebaseClient $firebaseClient,
|
||||
ConfigurationRepository $configuration
|
||||
) {
|
||||
$this->firebaseClient = $firebaseClient;
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://firebase.google.com/docs/reference/rest/auth Firebase documentation
|
||||
*
|
||||
* @param string $customToken
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exchangeCustomTokenForIdAndRefreshToken($customToken)
|
||||
{
|
||||
$response = $this->firebaseClient->signInWithCustomToken($customToken);
|
||||
|
||||
if ($response && true === $response['status']) {
|
||||
$uid = (new Parser())->parse((string) $customToken)->getClaim('uid');
|
||||
|
||||
$this->configuration->updateShopUuid($uid);
|
||||
|
||||
$this->configuration->updateFirebaseIdAndRefreshTokens(
|
||||
$response['body']['idToken'],
|
||||
$response['body']['refreshToken']
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function refreshToken()
|
||||
{
|
||||
$response = $this->firebaseClient->exchangeRefreshTokenForIdToken(
|
||||
$this->configuration->getFirebaseRefreshToken()
|
||||
);
|
||||
|
||||
if ($response && true === $response['status']) {
|
||||
$this->configuration->updateFirebaseIdAndRefreshTokens(
|
||||
$response['body']['id_token'],
|
||||
$response['body']['refresh_token']
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user firebase token.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getOrRefreshToken()
|
||||
{
|
||||
if (
|
||||
$this->configuration->hasFirebaseRefreshToken()
|
||||
&& $this->isTokenExpired()
|
||||
) {
|
||||
$this->refreshToken();
|
||||
}
|
||||
|
||||
return $this->configuration->getFirebaseIdToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRefreshToken()
|
||||
{
|
||||
return $this->configuration->getFirebaseRefreshToken() ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getToken()
|
||||
{
|
||||
return $this->configuration->getFirebaseIdToken() ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function isTokenExpired()
|
||||
{
|
||||
// iat, exp
|
||||
$token = (new Parser())->parse($this->configuration->getFirebaseIdToken());
|
||||
|
||||
return $token->isExpired(new \DateTime());
|
||||
}
|
||||
}
|
||||
91
modules/ps_accounts/classes/Service/SsoService.php
Normal file
91
modules/ps_accounts/classes/Service/SsoService.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?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\Service;
|
||||
|
||||
use Context;
|
||||
use PrestaShop\Module\PsAccounts\Configuration\ConfigOptionsResolver;
|
||||
use PrestaShop\Module\PsAccounts\Configuration\Configurable;
|
||||
use PrestaShop\Module\PsAccounts\Exception\OptionResolutionException;
|
||||
|
||||
/**
|
||||
* Class PsAccountsService
|
||||
*/
|
||||
class SsoService implements Configurable
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $ssoAccountUrl;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $ssoResendVerificationEmailUrl;
|
||||
|
||||
/**
|
||||
* PsAccountsService constructor.
|
||||
*
|
||||
* @param array $config
|
||||
*
|
||||
* @throws OptionResolutionException
|
||||
*/
|
||||
public function __construct(array $config)
|
||||
{
|
||||
$config = $this->resolveConfig($config);
|
||||
$this->ssoAccountUrl = $config['sso_account_url'];
|
||||
$this->ssoResendVerificationEmailUrl = $config['sso_resend_verification_email_url'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSsoAccountUrl()
|
||||
{
|
||||
$url = $this->ssoAccountUrl;
|
||||
$langIsoCode = Context::getContext()->language->iso_code;
|
||||
|
||||
return $url . '?lang=' . substr($langIsoCode, 0, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSsoResendVerificationEmailUrl()
|
||||
{
|
||||
return $this->ssoResendVerificationEmailUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $config
|
||||
* @param array $defaults
|
||||
*
|
||||
* @return array|mixed
|
||||
*
|
||||
* @throws OptionResolutionException
|
||||
*/
|
||||
public function resolveConfig(array $config, array $defaults = [])
|
||||
{
|
||||
return (new ConfigOptionsResolver([
|
||||
'sso_account_url',
|
||||
'sso_resend_verification_email_url',
|
||||
]))->resolve($config, $defaults);
|
||||
}
|
||||
}
|
||||
28
modules/ps_accounts/classes/Service/index.php
Normal file
28
modules/ps_accounts/classes/Service/index.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?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
|
||||
*/
|
||||
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;
|
||||
Reference in New Issue
Block a user