1
0
mirror of synced 2025-02-16 20:23:15 +03:00

fix for signer algorithm, test for signer

This commit is contained in:
Pavel 2020-09-28 17:18:21 +03:00
parent f6a0b56c69
commit 1b6e138e64
14 changed files with 373 additions and 65 deletions

104
src/Component/AppData.php Normal file
View File

@ -0,0 +1,104 @@
<?php
/**
* PHP version 7.3
*
* @category AppData
* @package RetailCrm\Component
* @author RetailCRM <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Component;
use RetailCrm\Interfaces\AppDataInterface;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Class AppData
*
* @category AppData
* @package RetailCrm\Component
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
class AppData implements AppDataInterface
{
public const OVERSEAS_ENDPOINT = 'https://api.taobao.com/router/rest';
public const CHINESE_ENDPOINT = 'https://eco.taobao.com/router/rest';
public const AVAILABLE_ENDPOINTS = [self::OVERSEAS_ENDPOINT, self::CHINESE_ENDPOINT];
/**
* @var string $serviceUrl
* @Assert\Url()
* @Assert\Choice(choices=Client::AVAILABLE_ENDPOINTS, message="Invalid endpoint provided.")
*/
protected $serviceUrl;
/**
* @var string $appKey
* @Assert\NotBlank()
*/
private $appKey;
/**
* @var string $appSecret
* @Assert\NotBlank()
*/
private $appSecret;
/**
* AppData constructor.
*
* @param string $serviceUrl
* @param string $appKey
* @param string $appSecret
*/
public function __construct(string $serviceUrl, string $appKey, string $appSecret)
{
$this->serviceUrl = $serviceUrl;
$this->appKey = $appKey;
$this->appSecret = $appSecret;
}
/**
* Constructor shortcut
*
* @param string $serviceUrl
* @param string $appKey
* @param string $appSecret
*
* @return \RetailCrm\Component\AppData
*/
public static function create(string $serviceUrl, string $appKey, string $appSecret): AppDataInterface
{
return new self($serviceUrl, $appKey, $appSecret);
}
/**
* @return string
*/
public function getServiceUrl(): string
{
return $this->serviceUrl;
}
/**
* @return string
*/
public function getAppKey(): string
{
return $this->appKey;
}
/**
* @return string
*/
public function getAppSecret(): string
{
return $this->appSecret;
}
}

View File

@ -57,20 +57,4 @@ class TokenAuthenticator implements AuthenticatorInterface
$request->appKey = $this->appKey;
$request->session = $this->token;
}
/**
* @return string
*/
public function getAppKey(): string
{
return $this->appKey;
}
/**
* @return string
*/
public function getAppSecret(): string
{
return $this->token;
}
}

View File

@ -14,6 +14,7 @@ namespace RetailCrm\Factory;
use Psr\Container\ContainerInterface;
use RetailCrm\Component\Constants;
use RetailCrm\Interfaces\AppDataInterface;
use RetailCrm\Interfaces\AuthenticatorInterface;
use RetailCrm\Interfaces\ContainerAwareInterface;
use RetailCrm\Interfaces\FactoryInterface;
@ -34,8 +35,8 @@ class ClientFactory implements ContainerAwareInterface, FactoryInterface
{
use ContainerAwareTrait;
/** @var string $serviceUrl */
private $serviceUrl;
/** @var \RetailCrm\Interfaces\AppDataInterface $appData */
private $appData;
/*** @var AuthenticatorInterface $authenticator */
protected $authenticator;
@ -54,13 +55,13 @@ class ClientFactory implements ContainerAwareInterface, FactoryInterface
}
/**
* @param string $serviceUrl
* @param \RetailCrm\Interfaces\AppDataInterface $appData
*
* @return $this
* @return ClientFactory
*/
public function setServiceUrl(string $serviceUrl): self
public function setAppData(AppDataInterface $appData): ClientFactory
{
$this->serviceUrl = $serviceUrl;
$this->appData = $appData;
return $this;
}
@ -81,7 +82,7 @@ class ClientFactory implements ContainerAwareInterface, FactoryInterface
*/
public function create(): Client
{
$client = new Client($this->serviceUrl, $this->authenticator);
$client = new Client($this->appData, $this->authenticator);
$client->setHttpClient($this->container->get(Constants::HTTP_CLIENT));
$client->setSerializer($this->container->get(Constants::SERIALIZER));
$client->setValidator($this->container->get(Constants::VALIDATOR));

View File

@ -15,6 +15,7 @@ namespace RetailCrm\Factory;
use JMS\Serializer\GraphNavigatorInterface;
use JMS\Serializer\Serializer;
use RetailCrm\Component\Constants;
use RetailCrm\Service\RequestSigner;
use Shieldon\Psr17\StreamFactory;
use Shieldon\Psr17\UploadedFileFactory as BaseFactory;
use JMS\Serializer\SerializerBuilder;
@ -109,6 +110,9 @@ class ContainerFactory implements FactoryInterface
);
$container->set(Constants::SERIALIZER, $this->getSerializer());
$container->set(UploadedFileFactory::class, new UploadedFileFactory(new BaseFactory(), new StreamFactory()));
$container->set(RequestSigner::class, function (ContainerInterface $container) {
return new RequestSigner($container->get(Constants::SERIALIZER));
});
}
/**

View File

@ -0,0 +1,42 @@
<?php
/**
* PHP version 7.3
*
* @category AppDataInterface
* @package RetailCrm\Interfaces
* @author RetailCRM <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Interfaces;
/**
* Interface AppDataInterface
*
* @category AppDataInterface
* @package RetailCrm\Interfaces
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
interface AppDataInterface
{
/**
* @return string
*/
public function getServiceUrl(): string;
/**
* @return string
*/
public function getAppKey(): string;
/**
* @return string
*/
public function getAppSecret(): string;
}

View File

@ -31,14 +31,4 @@ interface AuthenticatorInterface
* @param \RetailCrm\Model\Request\BaseRequest $request
*/
public function authenticate(BaseRequest $request): void;
/**
* @return string
*/
public function getAppKey(): string;
/**
* @return string
*/
public function getAppSecret(): string;
}

View File

@ -30,8 +30,8 @@ interface RequestSignerInterface
/**
* Signs provided request.
*
* @param \RetailCrm\Model\Request\BaseRequest $request
* @param \RetailCrm\Interfaces\AuthenticatorInterface $authenticator
* @param \RetailCrm\Model\Request\BaseRequest $request
* @param \RetailCrm\Interfaces\AppDataInterface $authenticator
*/
public function sign(BaseRequest $request, AuthenticatorInterface $authenticator): void;
public function sign(BaseRequest $request, AppDataInterface $authenticator): void;
}

View File

@ -58,7 +58,7 @@ abstract class BaseRequest
/**
* @var \DateTime $timestamp
*
* @JMS\Type("DateTime<'yyyy-MM-dd H:i:s'>")
* @JMS\Type("DateTime<'Y-m-d H:i:s'>")
* @JMS\SerializedName("timestamp")
* @Assert\NotBlank()
*/

View File

@ -14,7 +14,7 @@ namespace RetailCrm\Service;
use JMS\Serializer\SerializerInterface;
use RetailCrm\Component\Constants;
use RetailCrm\Interfaces\AuthenticatorInterface;
use RetailCrm\Interfaces\AppDataInterface;
use RetailCrm\Interfaces\RequestSignerInterface;
use RetailCrm\Model\Request\BaseRequest;
@ -46,10 +46,10 @@ class RequestSigner implements RequestSignerInterface
}
/**
* @param BaseRequest $request
* @param \RetailCrm\Interfaces\AuthenticatorInterface $authenticator
* @param BaseRequest $request
* @param \RetailCrm\Interfaces\AppDataInterface $appData
*/
public function sign(BaseRequest $request, AuthenticatorInterface $authenticator): void
public function sign(BaseRequest $request, AppDataInterface $appData): void
{
$stringToBeSigned = '';
$params = $this->getRequestData($request);
@ -60,10 +60,12 @@ class RequestSigner implements RequestSignerInterface
switch ($request->signMethod) {
case Constants::SIGN_TYPE_MD5:
$request->sign = strtoupper(md5($authenticator->getAppSecret() . $stringToBeSigned));
$request->sign = strtoupper(md5(
$appData->getAppSecret() . $stringToBeSigned . $appData->getAppSecret()
));
break;
case Constants::SIGN_TYPE_HMAC:
$request->sign = strtoupper(hash_hmac('md5', $stringToBeSigned, $authenticator->getAppSecret()));
$request->sign = strtoupper(hash_hmac('md5', $stringToBeSigned, $appData->getAppSecret()));
break;
}
}

View File

@ -14,6 +14,7 @@ namespace RetailCrm\TopClient;
use JMS\Serializer\SerializerInterface;
use Psr\Http\Client\ClientInterface;
use RetailCrm\Interfaces\AppDataInterface;
use RetailCrm\Interfaces\AuthenticatorInterface;
use RetailCrm\Traits\ValidatorAwareTrait;
use Symfony\Component\Validator\Constraints as Assert;
@ -32,16 +33,10 @@ class Client
{
use ValidatorAwareTrait;
public const OVERSEAS_ENDPOINT = 'https://api.taobao.com/router/rest';
public const CHINESE_ENDPOINT = 'https://eco.taobao.com/router/rest';
public const AVAILABLE_ENDPOINTS = [self::OVERSEAS_ENDPOINT, self::CHINESE_ENDPOINT];
/**
* @var string $serviceUrl
* @Assert\Url()
* @Assert\Choice(choices=Client::AVAILABLE_ENDPOINTS, message="Invalid endpoint provided.")
* @var \RetailCrm\Interfaces\AppDataInterface $appData
*/
protected $serviceUrl;
protected $appData;
/**
* @var AuthenticatorInterface $authenticator
@ -64,12 +59,12 @@ class Client
/**
* Client constructor.
*
* @param string $serviceUrl
* @param \RetailCrm\Interfaces\AppDataInterface $appData
* @param \RetailCrm\Interfaces\AuthenticatorInterface $authenticator
*/
public function __construct(string $serviceUrl, AuthenticatorInterface $authenticator)
public function __construct(AppDataInterface $appData, AuthenticatorInterface $authenticator)
{
$this->serviceUrl = $serviceUrl;
$this->appData = $appData;
$this->authenticator = $authenticator;
}

View File

@ -0,0 +1,33 @@
<?php
namespace RetailCrm\Test;
use Psr\Container\ContainerInterface;
use RetailCrm\Component\Environment;
use RetailCrm\Factory\ContainerFactory;
/**
* Class TestCase
*
* @category TestCase
* @package ${NAMESPACE}
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
class TestCase extends \PHPUnit\Framework\TestCase
{
private $container;
public function getContainer($recreate = false): ContainerInterface
{
if (null === $this->container || $recreate) {
$this->container = ContainerFactory::withEnv(Environment::TEST)
->withClient(new \GuzzleHttp\Client())
->create();
}
return $this->container;
}
}

View File

@ -0,0 +1,66 @@
<?php
/**
* PHP version 7.3
*
* @category TestRequest
* @package RetailCrm\Test
* @author RetailCRM <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Test;
use JMS\Serializer\Annotation as JMS;
use RetailCrm\Model\Request\BaseRequest;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Class TestRequest
*
* @category TestRequest
* @package RetailCrm\Test
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
class TestSignerRequest extends BaseRequest
{
/**
* @var string $serviceName
*
* @JMS\Type("string")
* @JMS\SerializedName("service_name")
* @Assert\NotBlank()
*/
public $serviceName;
/**
* @var string $outRef
*
* @JMS\Type("string")
* @JMS\SerializedName("out_ref")
* @Assert\NotBlank()
*/
public $outRef;
/**
* @var string $sendType
*
* @JMS\Type("string")
* @JMS\SerializedName("send_type")
* @Assert\NotBlank()
*/
public $sendType;
/**
* @var string $logisitics_no
*
* @JMS\Type("string")
* @JMS\SerializedName("logisitics_no")
* @Assert\NotBlank()
*/
public $logisticsNo;
}

View File

@ -4,26 +4,25 @@
* PHP version 7.4
*
* @category ClientFactoryTest
* @package Component
* @package RetailCrm\Tests\Component
* @author RetailCRM <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace Component;
namespace RetailCrm\Tests\Component;
use PHPUnit\Framework\TestCase;
use RetailCrm\Component\AppData;
use RetailCrm\Component\Authenticator\TokenAuthenticator;
use RetailCrm\Component\Environment;
use RetailCrm\Factory\ClientFactory;
use RetailCrm\Factory\ContainerFactory;
use RetailCrm\Test\TestCase;
use RetailCrm\TopClient\Client;
/**
* Class ClientFactoryTest
*
* @category ClientFactoryTest
* @package Component
* @package RetailCrm\Tests\Component
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
@ -33,11 +32,8 @@ class ClientFactoryTest extends TestCase
{
public function testCreateClient()
{
$client = ClientFactory::withContainer(
ContainerFactory::withEnv(Environment::DEV)
->withClient(new \GuzzleHttp\Client())
->create()
)->setServiceUrl(Client::OVERSEAS_ENDPOINT)
$client = ClientFactory::withContainer($this->getContainer())
->setAppData(AppData::create(AppData::OVERSEAS_ENDPOINT, 'appKey', 'helloworld'))
->setAuthenticator(new TokenAuthenticator('appKey', 'token'))
->create();

View File

@ -0,0 +1,91 @@
<?php
/**
* PHP version 7.3
*
* @category RequestSigner
* @package RetailCrm\Tests\Service
* @author RetailCRM <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Tests\Service;
use DateTime;
use RetailCrm\Component\AppData;
use RetailCrm\Component\Authenticator\TokenAuthenticator;
use RetailCrm\Component\Constants;
use RetailCrm\Interfaces\AppDataInterface;
use RetailCrm\Interfaces\RequestSignerInterface;
use RetailCrm\Service\RequestSigner;
use RetailCrm\Test\TestCase;
use RetailCrm\Test\TestSignerRequest;
/**
* Class RequestSigner
*
* @category RequestSigner
* @package RetailCrm\Tests\Service
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
class RequestSignerTest extends TestCase
{
/**
* @dataProvider signDataProvider
*
* @param \RetailCrm\Test\TestSignerRequest $request
* @param \RetailCrm\Interfaces\AppDataInterface $appData
* @param string $expectedHash
*/
public function testSign(TestSignerRequest $request, AppDataInterface $appData, string $expectedHash): void
{
/** @var RequestSignerInterface $signer */
$signer = $this->getContainer()->get(RequestSigner::class);
$signer->sign($request, $appData);
self::assertEquals($expectedHash, $request->sign);
}
public function signDataProvider(): array
{
$appData = AppData::create(AppData::OVERSEAS_ENDPOINT, 'appKey', 'helloworld');
return [
[
$this->getRequestWithSignMethod(Constants::SIGN_TYPE_MD5),
$appData,
'4BC79C5FAA1B5E254E95A97E65BACEAB'
],
[
$this->getRequestWithSignMethod(Constants::SIGN_TYPE_HMAC),
$appData,
'497FA7FCAD98F4F335EFAE2451F8291D'
]
];
}
/**
* @param string $signMethod
*
* @return \RetailCrm\Test\TestSignerRequest
*/
private function getRequestWithSignMethod(string $signMethod): TestSignerRequest
{
$request = new TestSignerRequest();
$request->method = 'aliexpress.solution.order.fulfill';
$request->appKey = '12345678';
$request->session = 'test';
$request->timestamp = DateTime::createFromFormat('Y-m-d H:i:s', '2016-01-01 12:00:00');
$request->signMethod = $signMethod;
$request->serviceName = 'SPAIN_LOCAL_CORREOS';
$request->outRef = '1000006270175804';
$request->sendType = 'all';
$request->logisticsNo = 'ES2019COM0000123456';
return $request;
}
}