diff --git a/composer.json b/composer.json index fc3057c3e3e0044a591aa4914bffc82420a48959..653e02bb5769f845145b5175665e1cdf12e261bc 100644 --- a/composer.json +++ b/composer.json @@ -9,8 +9,8 @@ "require": { "php": ">=5.5", "psr/log": "^1.0.1", - "wpdesk/wp-cache": "dev-master", - "wpdesk/wp-http-client": "dev-master", + "wpdesk/wp-cache": "dev-feature/first-release", + "wpdesk/wp-http-client": "dev-feature/first-release", "psr/simple-cache": "^1.0" }, "require-dev": { diff --git a/src/Authentication/JWTSaasToken.php b/src/Authentication/JWTSaasToken.php new file mode 100644 index 0000000000000000000000000000000000000000..3465b2149140ea0146d231d3fdc33e565224fe9f --- /dev/null +++ b/src/Authentication/JWTSaasToken.php @@ -0,0 +1,66 @@ +<?php + +namespace WPDesk\ApiClient\Authentication; + +class JWTSaasToken implements Token +{ + const SHOP_ID_PARAM = 'shop'; + + const ROLE_PARAM = 'ROLE_SHOP'; + + + /** @var JWTToken */ + private $token; + + /** + * JWTToken constructor. + * @param string $token + */ + public function __construct(JWTToken $token) + { + $this->token = $token; + } + + public function getAuthString() + { + return $this->token->getAuthString(); + } + + public function isExpired() + { + return $this->token->isExpired(); + } + + public function isSignatureValid() + { + return $this->token->isSignatureValid(); + } + + public function __toString() + { + return $this->token->__toString(); + } + + /** + * If there is shop id in the token + * + * @return bool + */ + public function hasShopId() + { + $info = $this->token->getDecodedPublicTokenInfo(); + return !empty($info[self::SHOP_ID_PARAM]) && in_array(self::ROLE_PARAM, $info['roles']); + } + + /** + * Get shop id from token + * + * @return int + */ + public function getShopId() + { + $info = $this->token->getDecodedPublicTokenInfo(); + return (int)$info[self::SHOP_ID_PARAM]; + } + +} \ No newline at end of file diff --git a/src/Authentication/JWTToken.php b/src/Authentication/JWTToken.php new file mode 100644 index 0000000000000000000000000000000000000000..c6eaccf93e15be47f5be0ff37e1ec27b8383f993 --- /dev/null +++ b/src/Authentication/JWTToken.php @@ -0,0 +1,81 @@ +<?php + +namespace WPDesk\ApiClient\Authentication; + +class JWTToken implements Token +{ + const CONSIDER_EXPIRED_WHEN_LESS = 2; + + const EXPIRED_IN_SECONDS_PARAM = 'exp'; + + /** @var string */ + private $token; + + /** + * JWTToken constructor. + * @param string $token + */ + public function __construct($token) + { + $this->token = $token; + } + + /** + * @return string + */ + public function __toString() + { + return $this->token; + } + + /** + * Get string to perform authentication + * + * @return string + */ + public function getAuthString() + { + return 'Bearer ' . $this->__toString(); + } + + /** + * Returns public data from token + * + * @return array + */ + public function getDecodedPublicTokenInfo() + { + $tokenParts = explode('.', $this->__toString()); + if (!empty($tokenParts[1])) { + $infoPart = base64_decode($tokenParts[1]); + return json_decode($infoPart, true); + } + return []; + } + + /** + * Is token expired or very soon to be expired? + * + * @return bool + */ + public function isExpired() + { + $tokenInfo = $this->getDecodedPublicTokenInfo(); + if (!empty($tokenInfo[self::EXPIRED_IN_SECONDS_PARAM])) { + return $tokenInfo[self::EXPIRED_IN_SECONDS_PARAM] - time() < self::CONSIDER_EXPIRED_WHEN_LESS; + } + return true; + } + + /** + * Validates token signature + * + * @return bool + */ + public function isSignatureValid() + { + // @TODO + return true; + } + +} \ No newline at end of file diff --git a/src/Authentication/NullToken.php b/src/Authentication/NullToken.php new file mode 100644 index 0000000000000000000000000000000000000000..d46c595e2cc99d1b8a6970eede5f04e277350c33 --- /dev/null +++ b/src/Authentication/NullToken.php @@ -0,0 +1,54 @@ +<?php + +namespace WPDesk\ApiClient\Authentication; + +/** + * Null object pattern + * + * @package WPDesk\SaasPlatformClient\Authentication + */ +class NullToken implements Token +{ + public function __construct() + { + } + + /** + * @return string + */ + public function __toString() + { + return ''; + } + + /** + * Get string to perform authentication + * + * @return string + */ + public function getAuthString() + { + return ''; + } + + /** + * Is token expired or very soon to be expired? + * + * @return bool + */ + public function isExpired() + { + return true; + } + + /** + * Validates token signature + * + * @return bool + */ + public function isSignatureValid() + { + return false; + } + +} \ No newline at end of file diff --git a/src/Authentication/Token.php b/src/Authentication/Token.php new file mode 100644 index 0000000000000000000000000000000000000000..bc5a98e6bd53d1a37f068eb051d6d0d0620438bc --- /dev/null +++ b/src/Authentication/Token.php @@ -0,0 +1,32 @@ +<?php + +namespace WPDesk\ApiClient\Authentication; + +interface Token +{ + /** + * Get string to perform authentication + * + * @return string + */ + public function getAuthString(); + + /** + * Is token expired or very soon to be expired? + * + * @return bool + */ + public function isExpired(); + + /** + * Validates token signature + * + * @return bool + */ + public function isSignatureValid(); + + /** + * @return string + */ + public function __toString(); +} \ No newline at end of file diff --git a/src/Client/ApiClientOptions.php b/src/Client/ApiClientOptions.php new file mode 100644 index 0000000000000000000000000000000000000000..808f292cc76300f17879b7ea4d9c7ff62d3b4da1 --- /dev/null +++ b/src/Client/ApiClientOptions.php @@ -0,0 +1,32 @@ +<?php + +namespace WPDesk\ApiClient\Client; + +use Psr\Log\LoggerInterface; +use WPDesk\ApiClient\Serializer\SerializerOptions; +use WPDesk\HttpClient\HttpClientOptions; + +interface ApiClientOptions extends HttpClientOptions, SerializerOptions +{ + + /** + * @return LoggerInterface + */ + public function getLogger(); + + /** + * @return string + */ + public function getApiUrl(); + + /** + * @return array + */ + public function getDefaultRequestHeaders(); + + /** + * @return bool + */ + public function isCachedClient(); + +} \ No newline at end of file diff --git a/src/Client/CachedClient.php b/src/Client/CachedClient.php index 7d23f344f8b06a770de7f4548dfede9e946a641b..3c6ee45eaf5147791ff18ab96cf7957ea2b54497 100644 --- a/src/Client/CachedClient.php +++ b/src/Client/CachedClient.php @@ -1,12 +1,12 @@ <?php -namespace WPDesk\ApiClient\ApiClient; +namespace WPDesk\ApiClient\Client; use Psr\SimpleCache\CacheInterface; -use WPDesk\ApiClient\Cache\CacheDispatcher; -use WPDesk\ApiClient\Cache\CacheItemCreator; -use WPDesk\ApiClient\Cache\CacheItemVerifier; -use WPDesk\ApiClient\HttpClient\HttpClient; +use WPDesk\Cache\CacheDispatcher; +use WPDesk\Cache\CacheItemCreator; +use WPDesk\Cache\CacheItemVerifier; +use WPDesk\HttpClient\HttpClient; use WPDesk\ApiClient\Request\Request; use WPDesk\ApiClient\Response\Response; use WPDesk\ApiClient\Serializer\Serializer; diff --git a/src/Client/Client.php b/src/Client/Client.php index e63e184c6aa9e998cde32ef82cb0be507198e1bf..c3372f9f39e47bf7a115e4915da46282c2de19d9 100644 --- a/src/Client/Client.php +++ b/src/Client/Client.php @@ -1,8 +1,8 @@ <?php -namespace WPDesk\ApiClient\ApiClient; +namespace WPDesk\ApiClient\Client; -use WPDesk\ApiClient\HttpClient\HttpClient; +use WPDesk\HttpClient\HttpClient; use WPDesk\ApiClient\Request\Request; use WPDesk\ApiClient\Response\Response; use WPDesk\ApiClient\Serializer\Serializer; diff --git a/src/Client/ClientFactory.php b/src/Client/ClientFactory.php index b92f4598bd09b69fb2640286e247e66a43f4171a..81ad8aa30ed2b52729e11cd02262e95c84277746 100644 --- a/src/Client/ClientFactory.php +++ b/src/Client/ClientFactory.php @@ -1,19 +1,18 @@ <?php -namespace WPDesk\ApiClient\ApiClient; +namespace WPDesk\ApiClient\Client; -use WPDesk\ApiClient\Cache\WordpressCache; -use WPDesk\ApiClient\HttpClient\HttpClientFactory; -use WPDesk\ApiClient\PlatformFactoryOptions; +use WPDesk\Cache\WordpressCache; +use WPDesk\HttpClient\HttpClientFactory; use WPDesk\ApiClient\Serializer\SerializerFactory; class ClientFactory { /** - * @param PlatformFactoryOptions $options + * @param ApiClientOptions $options * @return Client */ - public function createClient(PlatformFactoryOptions $options) + public function createClient(ApiClientOptions $options) { $httpClientFactory = new HttpClientFactory(); $serializerFactory = new SerializerFactory(); diff --git a/src/Client/ClientImplementation.php b/src/Client/ClientImplementation.php index 02e9c52bfdf4b481f49d203583d703570d5e6829..feaeb75b00947472676c99209b8aab814a9722f8 100644 --- a/src/Client/ClientImplementation.php +++ b/src/Client/ClientImplementation.php @@ -1,17 +1,16 @@ <?php -namespace WPDesk\ApiClient\ApiClient; +namespace WPDesk\ApiClient\Client; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerInterface; use WPDesk\HttpClient\HttpClient; use WPDesk\HttpClient\HttpClientResponse; -use WPDesk\SaasPlatformClient\Platform; -use WPDesk\SaasPlatformClient\Request\Request; -use WPDesk\SaasPlatformClient\Response\RawResponse; -use WPDesk\SaasPlatformClient\Response\Response; -use WPDesk\SaasPlatformClient\Serializer\Serializer; -use WPDesk\SaasPlatformClient\HttpClient\HttpClientRequestException; +use WPDesk\ApiClient\Request\Request; +use WPDesk\ApiClient\Response\RawResponse; +use WPDesk\ApiClient\Response\Response; +use WPDesk\ApiClient\Serializer\Serializer; +use WPDesk\HttpClient\HttpClientRequestException; class ClientImplementation implements Client, LoggerAwareInterface { diff --git a/src/Client/RequestCacheInfoResolver.php b/src/Client/RequestCacheInfoResolver.php index 65e2014b4dd863bc3a1503b16f7fb6b2c4839c20..43a0b7e3d013b754b1bc2149f22c063a19f9a4e1 100644 --- a/src/Client/RequestCacheInfoResolver.php +++ b/src/Client/RequestCacheInfoResolver.php @@ -1,9 +1,9 @@ <?php -namespace WPDesk\SaasPlatformClient\ApiClient; +namespace WPDesk\ApiClient\Client; -use WPDesk\SaasPlatformClient\Cache\CacheInfoResolver; -use WPDesk\SaasPlatformClient\Cache\HowToCache; +use WPDesk\Cache\CacheInfoResolver; +use WPDesk\Cache\HowToCache; use WPDesk\SaasPlatformClient\Request\AuthRequest; use WPDesk\SaasPlatformClient\Request\BasicRequest; use WPDesk\SaasPlatformClient\Request\Request; diff --git a/src/Response/AuthApiResponse.php b/src/Response/AuthApiResponse.php new file mode 100644 index 0000000000000000000000000000000000000000..86f34fa9faf916700eabbed43f0cf3b7aae233ce --- /dev/null +++ b/src/Response/AuthApiResponse.php @@ -0,0 +1,14 @@ +<?php + +namespace WPDesk\ApiClient\Response; + +use WPDesk\ApiClient\Response\Traits\AuthApiResponseDecorator; + +class AuthApiResponse implements ApiResponse +{ + const RESPONSE_CODE_BAD_CREDENTIALS = 401; + + const RESPONSE_CODE_NOT_EXISTS = 404; + + use AuthApiResponseDecorator; +} \ No newline at end of file diff --git a/src/Response/Traits/ApiResponseDecorator.php b/src/Response/Traits/ApiResponseDecorator.php new file mode 100644 index 0000000000000000000000000000000000000000..0960c902925df1af012dfb2daadc2548ddceb700 --- /dev/null +++ b/src/Response/Traits/ApiResponseDecorator.php @@ -0,0 +1,145 @@ +<?php + +namespace WPDesk\ApiClient\Response\Traits; + +use WPDesk\ApiClient\Response\AuthApiResponse; +use WPDesk\ApiClient\Response\RawResponse; +use WPDesk\ApiClient\Response\Response; + +trait ApiResponseDecorator +{ + + /** @var RawResponse */ + private $rawResponse; + + /** + * RawResponseDecorator constructor. + * @param Response $rawResponse + */ + public function __construct(Response $rawResponse) + { + $this->rawResponse = $rawResponse; + } + + /** + * Returns response http code + * + * @return int + */ + public function getResponseCode() + { + return $this->rawResponse->getResponseCode(); + } + + /** + * Returns response body as array + * + * @return array + */ + public function getResponseBody() + { + return $this->rawResponse->getResponseBody(); + } + + /** + * Returns response body as array + * + * @return array + */ + public function getResponseErrorBody() + { + return $this->rawResponse->getResponseErrorBody(); + } + + /** + * Returns response body as array + * + * @return array + */ + public function getResponseHeaders() + { + return $this->rawResponse->getResponseHeaders(); + } + + /** + * Get links structure to the other request + * + * @return array + */ + public function getLinks() + { + $body = $this->getResponseBody(); + return $body['_links']; + } + + /** + * Is it a BAD REQUEST response + * + * @return bool + */ + public function isBadRequest() + { + return $this->getResponseCode() === RawResponse::RESPONSE_CODE_ERROR_BAD_REQUEST; + } + + /** + * Is it a DOMAIN NOT ALLOWED response + * + * @return bool + */ + public function isDomainNotAllowed() + { + return $this->getResponseCode() === RawResponse::RESPONSE_CODE_DOMAIN_NOT_ALLOWED; + } + + /** + * Is it a FATAL ERROR response + * + * @return bool + */ + public function isServerFatalError() + { + return $this->getResponseCode() === RawResponse::RESPONSE_CODE_ERROR_FATAL; + } + + /** + * Is any error occured + * + * @return bool + */ + public function isError() + { + return $this->rawResponse->isError(); + } + + /** + * Is requested resource exists + * + * @return bool + */ + public function isNotExists() + { + return $this->getResponseCode() === AuthApiResponse::RESPONSE_CODE_NOT_EXISTS; + } + + /** + * Is maintenance. + * + * @return bool + */ + public function isMaintenance() + { + return $this->rawResponse->isMaintenance(); + } + + /** + * Get platform version hash string. + * + * @return bool|string + */ + public function getPlatformVersionHash() + { + return $this->rawResponse->getPlatformVersionHash(); + } + + } \ No newline at end of file diff --git a/src/Response/Traits/AuthApiResponseDecorator.php b/src/Response/Traits/AuthApiResponseDecorator.php new file mode 100644 index 0000000000000000000000000000000000000000..a052865b28b0031e961e128f372481266534868a --- /dev/null +++ b/src/Response/Traits/AuthApiResponseDecorator.php @@ -0,0 +1,38 @@ +<?php + +namespace WPDesk\ApiClient\Response\Traits; + +use WPDesk\ApiClient\Response\AuthApiResponse; + +trait AuthApiResponseDecorator +{ + use ApiResponseDecorator; + + /** + * @return bool + */ + public function isBadCredentials() + { + return $this->getResponseCode() === AuthApiResponse::RESPONSE_CODE_BAD_CREDENTIALS; + } + + /** + * Is bad credential because token expires + * + * @return bool + */ + public function isTokenExpired() + { + return $this->isBadCredentials(); + } + + /** + * Is bad credential because token is invalid + * + * @return bool + */ + public function isTokenInvalid() + { + return $this->isBadCredentials(); + } +} \ No newline at end of file diff --git a/src/Response/Traits/PagedListImplementation.php b/src/Response/Traits/PagedListImplementation.php new file mode 100644 index 0000000000000000000000000000000000000000..23286478d3ca85eac7b62a7653000c08f496b502 --- /dev/null +++ b/src/Response/Traits/PagedListImplementation.php @@ -0,0 +1,44 @@ +<?php + +namespace WPDesk\ApiClient\Response\Traits; + +trait PagedListImplementation +{ + /* + * @return array + */ + public function getRawPage() + { + $body = $this->getResponseBody(); + if ($body['_embedded'] !== null && $body['_embedded']['item'] !== null) { + return $body['_embedded']['item']; + } + return []; + } + + /** + * @return int + */ + public function getPageCount() + { + return (int)floor($this->getItemCount() / $this->getItemsPerPage()); + } + + /** + * @return int + */ + public function getItemsPerPage() + { + $body = $this->getResponseBody(); + return (int)$body['itemsPerPage']; + } + + /** + * @return int + */ + public function getItemCount() + { + $body = $this->getResponseBody(); + return (int)$body['totalItems']; + } +} \ No newline at end of file diff --git a/src/Serializer/Exception/CannotUnserializeException.php b/src/Serializer/Exception/CannotUnserializeException.php new file mode 100644 index 0000000000000000000000000000000000000000..0d603480969fdee82a65974bd80ed4799ef15cd3 --- /dev/null +++ b/src/Serializer/Exception/CannotUnserializeException.php @@ -0,0 +1,13 @@ +<?php + +namespace WPDesk\ApiClient\Serializer\Exception; + +/** + * Thrown when serializer cannot unserialize string data + * + * @package WPDesk\ApiClient\Serializer\Exception + */ +class CannotUnserializeException extends \RuntimeException +{ + +} \ No newline at end of file diff --git a/src/Serializer/JsonSerializer.php b/src/Serializer/JsonSerializer.php new file mode 100644 index 0000000000000000000000000000000000000000..55fa230cd7cc2b5c63d0edfadabe86787559cbc6 --- /dev/null +++ b/src/Serializer/JsonSerializer.php @@ -0,0 +1,46 @@ +<?php + +namespace WPDesk\ApiClient\Serializer; + +use WPDesk\ApiClient\Serializer\Exception\CannotUnserializeException; + +class JsonSerializer implements Serializer +{ + + /** + * Convert data to string + * + * @param mixed $data + * @return string + */ + public function serialize($data) + { + return json_encode($data, JSON_FORCE_OBJECT); + } + + /** + * Convert string to php data + * + * @param string $data + * @return mixed + */ + public function unserialize($data) + { + $unserializedResult = json_decode($data, true); + if ($unserializedResult === null) { + throw new CannotUnserializeException("Cannot unserialize data: {$data}"); + } + + return $unserializedResult; + } + + /** + * @return string + */ + public function getMime() + { + return 'application/json'; + } + + +} \ No newline at end of file diff --git a/src/Serializer/Serializer.php b/src/Serializer/Serializer.php new file mode 100644 index 0000000000000000000000000000000000000000..1faa8f943c7d2d844e8790be0bf430bb3ee68727 --- /dev/null +++ b/src/Serializer/Serializer.php @@ -0,0 +1,23 @@ +<?php + +namespace WPDesk\ApiClient\Serializer; + +interface Serializer +{ + /** + * @param mixed $data + * @return string + */ + public function serialize($data); + + /** + * @param string $data + * @return mixed + */ + public function unserialize($data); + + /** + * @return string + */ + public function getMime(); +} \ No newline at end of file diff --git a/src/Serializer/SerializerFactory.php b/src/Serializer/SerializerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..532099ea97383175574556b87f8cd7974223a6b2 --- /dev/null +++ b/src/Serializer/SerializerFactory.php @@ -0,0 +1,16 @@ +<?php + +namespace WPDesk\ApiClient\Serializer; + +class SerializerFactory +{ + /** + * @param SerializerOptions $options + * @return Serializer + */ + public function createSerializer(SerializerOptions $options) + { + $className = $options->getSerializerClass(); + return new $className; + } +} \ No newline at end of file diff --git a/src/Serializer/SerializerOptions.php b/src/Serializer/SerializerOptions.php new file mode 100644 index 0000000000000000000000000000000000000000..d1441bc68ffd14fe5ac8a209f661d3b61823c7ae --- /dev/null +++ b/src/Serializer/SerializerOptions.php @@ -0,0 +1,11 @@ +<?php + +namespace WPDesk\ApiClient\Serializer; + +interface SerializerOptions +{ + /** + * @return string + */ + public function getSerializerClass(); +} \ No newline at end of file diff --git a/tests/unit/Authentication/TestJWTSaasToken.php b/tests/unit/Authentication/TestJWTSaasToken.php new file mode 100644 index 0000000000000000000000000000000000000000..526253d91e94aec5020a0d1381071b34f4bb1952 --- /dev/null +++ b/tests/unit/Authentication/TestJWTSaasToken.php @@ -0,0 +1,64 @@ +<?php + +use WPDesk\ApiClient\Authentication\JWTSaasToken; +use WPDesk\ApiClient\Authentication\JWTToken; + +class TestJWTSaasToken extends \PHPUnit\Framework\TestCase +{ + /** + * Provides tokens with info about expected shop id encoded inside + * + * @return array + */ + public function provideTokensWithShopInfo() + { + return [ + [ + 'token' => 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1MzQ5NDA2NjQsImV4cCI6MTUzNDk0NDI2NCwicm9sZXMiOlsiUk9MRV9VU0VSIiwiUk9MRV9TSE9QIl0sInVzZXJuYW1lIjozLCJpcCI6IjE3Mi4xOS4wLjEiLCJzaG9wIjoxfQ.rG6_U67zKTinkqR4324wOhS0YP9P9_2DH-OMsfijTPt408wNGwh5YKqkA4kglP5wMQQ80UGo7qyYd9R3fc465JMB9DQTbALATz_UMk8fr_-LWl9Gj8cuzQvEMQL2Saya_nggAn0bmWlVG27k6-6ezfBW7Hhb1d1GtoydbiYMA_ntLVPzrRjicvcIoftroadX6rsuCSHy-lLvHd1pj6E6eOEX2IcZXoa9eqd_3-j4dgszNUoPDag8bYXQzmIDIqyseBQ3eaQr69Tj-npTaMDjSyHwFQqtr57leX9aVVo8xPeF2ulrT4u4VgCv6kQt1Fao5_G6WLJ9FpJE19k_e4uebSwmDwiPoSnqU30tmIoVpcqfzxoNDKxfaK6am4HvfiQ6l_A2Cb7Wi9Wh5Px1A2a1NjFNKq-hxI3bVb30cKvacL29tYcjT-315RKv57onnCjVJWaGfzPJ44ZibB2VaVF9HO8Rjci8PJ35lF0NOw7YYb7rTQB0JOsLhHrwe9cjgKBsxwIAOVx1fFt6fq1RRJzUpcHKAx0f7kwGmHzXNwpNIexneBYg1Btz5UfW3yiztxggBPYvDfFAdLc5hfFURFhLtqaQzCklomRYBkbEkBGSt_5iz7HLYiiWuM51TzXgUMa631KwCuyBA675fwEMnwQ01YM51Et7NQgRcja737kSliM', + 'shop' => 1 + ], + [ + 'token' => 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1MzQ5NDEyMDAsImV4cCI6MTUzNDk0NDgwMCwicm9sZXMiOlsiUk9MRV9VU0VSIiwiUk9MRV9TSE9QIl0sInVzZXJuYW1lIjozLCJpcCI6IjE3Mi4xOS4wLjEiLCJzaG9wIjo1fQ.NAh6p40uSgyhcRkXxLSJhsGSn-mRAIYGsbtuMqSrplHk7LxrOQvvLBKfW0cZpl4y683ECGYqykJYmGb3nnWPKOQv5Pl2GExLItvzWuVju6KvkTi-_RD1xVXv6tCxkI6umcrJaI0bUWQhjLogrjNgIAR81DRbfG81lctIRzhP-CtADD9J7uEFrfWIGIbBgVOMdiw4nsAvqB9ECTDbnv5TRsxmOnyhm42mWxfpbrzd7WaRugIMWbysL2_5HXwqLV7jaaPlBHFpGs3cpgpRF0fsZ1VZeM7LAiiwvNuBwVsZbmD_ZKKdXzwL183RbsuSP4C_7gjH4UXEflRWRR6nzom_AyCFhqGrdu1obSNLiIeIt94p_VavOmwicwQKmuhOHGnYHVguTLYW2FcQIS2nMosswnXoZzTJcL3YwQQsMT6Xg-Da43bWZMIWvcK7d-nSvOGHW0OrHzWQRZOcH3RKANzPUEG5L0-KL55yacMbAPq6ykGc-I-_q1IDBxfxE2rSaxGKa72O60uwjX2Mafdhp9YWuESNm2Gvi09hYo3_dBUAHjaSdXOWqJhb8h1ND8c2IJNxltTIukz1fDdLkFvTttZi4g5rjsJoDN94Y3f-19qokY8lzhx8z3Zc4hgbNQ9p8CYrmjB6FDa59Psuxbd-ODwzNm_uLsn1z4U5uo68QHa5g50', + 'shop' => 5 + ], + [ // case with wpdesk and shop role + 'token' => 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1MzQ5NDE0MzYsImV4cCI6MTUzNDk0NTAzNiwicm9sZXMiOlsiUk9MRV9XUERFU0siLCJST0xFX1NIT1AiXSwidXNlcm5hbWUiOjEsImlwIjoiMTcyLjE5LjAuMSIsInNob3AiOjJ9.pD9qQQJB_Mn6hSBcK5unEyrQ2n-3BgnzCRtk1V_u7BL0SKqgqILQN5NGJYpG2dO1Rb54pCNgAVzr6axu9f6ijmGetpHX_V1HSy7HOM4xdI8H7BaPHaK-2KUKTrVFM7lEp_sRc4KBkfSW4Ju2EH0e0Oty42EkIWeTb3J3npTHK_RjXI47xX5e_gZW8inRfJx24EhiSnlkMX77lIpXsqqCyRKqW64niLrQzKMNeSOJ_5HgBvzk9OyR9H5O8_s8EAliD24LvynoxgQOp9r0A78EIBRoqdZKJSS7kSgl_8eRFTGgxZBPGmyBKNWvxrd2XnRp19QPgymMi2kR0HJDWRemtSFHxYsjTrVVnpeiHjZ7WTKNawZPB-7ELPIHm_C_-GuJ4I1fnnM_uUna2rh6mr3KeRQF4LJGmDjesdHIpy8FDmepqsIcAAxpmAi6kJRtDr6QAxoh20zi0jCni1pPmLQyoDsau7U7l3Nev_STFbFiHBk-mLYbhNqV_MCcFY1-GeWynfehehWoVeGTShVoxXrDSWklPb58DaQKuwXJ8-kvmvWVhVqWGqF5l7qUj3D2CIxr4tXsQ6FUPkntvFw3S4GrLqAl-P7XoR5alRxSyC6KypYeY7pHRZBDDeoFWgCOJhEymWtKrcTt-Yzd2EL3vQYnwhYOp0hw1n-eXnE1WkaB4Wg', + 'shop' => 2 + ] + ]; + } + + /** + * Provides tokens when can't get info about shop + * + * @return array + */ + public function provideTokensWithInvalidShopInfo() + { + return [ + ['invalid' => 'siUk9MRV9XUERFU0siLCJST0xFX1NIT1AiXSwidXNlcm5hbWUiOjEsImlwIjoiMTcyLjE5LjAuMSIsInNob3AiOjJ9.qeroBbMHVgQWypDaF6FPmaZadu7D1u1JObCn9rXy44pGL1mWESdVCI6pU_MajeCJsQ-V3emWMOfqHl5AIiH7BZb2iW04ahGJjJTtRGaEfGH5ztyG-00TVzf51V0mtX3i3DuEEJd8F6LDHK8C51oqm6WkHVv__GhsqOlMz_WTDdst-IRUK-kgcUBhbYy-XzVxt4LADQhs4SsJqd-0wYPaobTFRQaHel69oGF_ymwJ4snla4uJxlfIHLtmYRArOIcDNKcPIOH8cMo-UJP9B15IxuOGO0M2hR74LNSmDUpIHmeQ24rEiwp73q2yhXYIHhIBdExDk7GAHFhOILIqa05D48V8fkPLr0TFVVCACvylJHwj3UsYPzI1WvhdrEHcGr1MC8bWB2KAC_RpB5Vvo7kPp0YXcUa7nbszL90v1njKpVrcSrpYG69a4Ym8ZXSdAS7jJmoSH-Xf3JXXi_6vtxtSsm-DG_zwd7VoWupBu1rG1h_ARW-Lambj_Ql2XZEAWAvdR6LqHkL5SyIZ7CnscbNY_VbYoyRHsQIwhzbaQdBSwBa4qu0VsEHtwdV2acbDZhojm-DMb4b7vr3znyw_WnHPSvjOGRXmW2KhD0bwwfrAJcquc0rL5N04FRVYXR7AnJjN-j1j0bfhvDN9a5W-yHidmp9_OZ0HVCqK5c0IIsiv-Ms'], + ['no shop id' => 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1MzQ5NDE5MjMsImV4cCI6MTUzNDk0NTUyMywicm9sZXMiOlsiUk9MRV9VU0VSIiwiUk9MRV9TSE9QIl0sInVzZXJuYW1lIjozLCJpcCI6IjE3Mi4xOS4wLjEifQ.S8Dkz9C7d0zHRn5_uRzjdBHH437JJZ4CdHTRc-Bwn1bnP9rj0SGn3AbCA53TlZjBl2EX0cigRlEMYsKEr0eQdQK8dellP0EjtPSaX5_5nEuU_H0X_6oJT8wYiS9N2jVm6W3UiKHMJBCR9JQfhjHYmMRx-NKhAi5xUgiGIWC7IWhNvLzHu52sRozkG2jTssnHGid087FdnQ1aG9c4dYY9piSw-5xBf8FruP9sDoWhTvFzESHIlwzgL5MNHQx-9HUH6caWoblpas959YqbD29h49YuCHLbifR1sMjSUWnkw69WLGDAFs8aKPCR5FsfKg3Rac0s957Cl3ES60eL7kWJ2mk1kRjxpFa6me1pWvh7Pt6lpDsJPLvTIbcZ_aDX5taOOiucL8YO-PbCB37Ffi2L4Am0-b4EGrqarBprjUWe2CJA9tCfjjFm0McOotDNVjLDmU7wAdHv8hXEOq4Kr2wd1z7suRqZii3VjMotoWFz9Cq4BTu1cs_Z_OLq5aC_uhnWq-SsKx3pdfL5tDXZ_CPqDCW0_3692FT8Z_puk1UUtMI_I_GkY_gp-npH72D8VcYdI-0j4LJoEtviGk6-0Lk47CiygGw79TaFjcEhwxPoYD_7MwMEw7vJiYYO8PVptlSuQy7j2-cPI0XPHlu19tVzA97ilqOlRAxt_SihXMfSoyo'], + ['no shop role' => 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1MzQ5NDIwNjEsImV4cCI6MTUzNDk0NTY2MSwicm9sZXMiOlsiUk9MRV9VU0VSIl0sInVzZXJuYW1lIjozLCJpcCI6IjE3Mi4xOS4wLjEiLCJzaG9wIjo1fQ.PS5SQixw9k9_-Waxho4GKJ1QudCSzee6gy9_cFjWnVcBpMGH-PkHf5iDO5rWwqQSsKpBgMi5m-FFCYoelHa5q1-XAn6FkRlj0aXNcQZCY6YGfqIaDeCP-0DEVoSqfaceFXLZbTyPz4aKrurONjWdZwuw9uull6Ho4Nq1B_E5QTpJBYehmebgWnS5OjHDjJVW0UrknwKHiRSohP-vCA5P2HQGZpZXrUCkLFcL2PqCCchORuHUQr3xjn2OleE4Jt1RsD5Lymo3m8s6WkRw9GepWV7-vJ0WDh3Mu_irkstZlCyujfh0PNnRSEyr9jshW2OXnDX_l0dLzsd5n6pPX1OVa58FpkZNb3s7hAsVxQ2Dd9GUg2o-qMZDCP9LbXn_CGUqiyXo4iIdal4balF0DpUGNEfggwqnL4leTsHqUirsN1bhLLhRmkAYooCfFuEMA9F5G66zF8Vwl5AuZSz8Nrj-T0krM54H5ykhzVttpWyRA9TcVywKAC9E2DFDOMfajtJA_LcMv5xg-m0uhhxlGk25HAY8yVWQdPfBp5KIq2jO3vLPzVmBDPFjtDgwiUXvXVakFS7AmtI8DRWh8dq92F2dGDV67uZ_WK5kqBQEtO1abfM5EF0zcQ9r_E4KqjLJfXyCyLMucw8CUdsE8MEcCfjpE06_mSuRMW1dpoSPJWGXE5Y'], + ['no whole shop info' => 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1MzQ5NDIwMjksImV4cCI6MTUzNDk0NTYyOSwicm9sZXMiOlsiUk9MRV9VU0VSIl0sInVzZXJuYW1lIjozLCJpcCI6IjE3Mi4xOS4wLjEifQ.XTvcWiKWLda4TnHhWnqrOKCBseiHmjnDYgmPCKhX6NrEYMu4mN0puKgnoeOWJVZHBjFQ5X--khMFKNKU4e7Zgc6MWgnSc5niKyZpr73D-PVfUkOG81we2-DSteik2i3F2R2cTSIUbYMJKeqe7fQ0qTZ3-7s6kNgKAkIhGraQrbbxi-FTp4uNbUrDwcDEyBZ_T9zmcR9jXsKLVJszHdmc8K_eVYwj2jOR8bouGOqpCHwlGz0bEpcSPqXJ_DsotVeM91QIBls4JzImTFbYr06N1GdG4oFzMNWqVhO9iJ4-ElN_COPLp1ou0i1Ha0ZedYajI2CJrhailbF5ybZd5x08tiDoMa26aVpH09wOe_P4zYno0ksMnZ66WpPcnBgnPA_dlgcqUUkc93igicD8XCKEBokEyF16IzI3A8Y85FEuwkIj-JadM8ZTJqHErxNbhZyuEGqWvQfwhawade-3CxOUz3krbG8LxhNPBYrm2t8NQpRwwFoQPa0lvEdMiGo4JL0J7hW02shT2iGPOUwJ0_KfpXCU5ensd60Mou0GQhnUPhKvnA0CtvQn5JgX_TM5OKKCSvtcIEaW2pqq96--S8atnQp2GwZU0Ccs8R6HAfH61d8cwL8abwKvr6A5wQoskn28YPwWu8g9Tvt556i2oIvFWEjrFOnfQyhmQj1GCVMafFc'], + ]; + } + + /** + * @dataProvider provideTokensWithShopInfo + */ + public function testValidShopInfoFromToken($token, $shop) + { + $saasToken = new JWTSaasToken(new JWTToken($token)); + $this->assertTrue($saasToken->hasShopId(), 'Token should have shop id info'); + $this->assertEquals($shop, $saasToken->getShopId(), 'Invalid shop id provided in token'); + } + + /** + * @dataProvider provideTokensWithInvalidShopInfo + */ + public function testTokensWithInvalidShopInfo($token) + { + $saasToken = new JWTSaasToken(new JWTToken($token)); + $this->assertFalse($saasToken->hasShopId(), 'Token should NOT have shop id info'); + } +} \ No newline at end of file diff --git a/tests/unit/Authentication/TestJWToken.php b/tests/unit/Authentication/TestJWToken.php new file mode 100644 index 0000000000000000000000000000000000000000..492f1159ff070a0d8ef0f9031118fe82c70dfc60 --- /dev/null +++ b/tests/unit/Authentication/TestJWToken.php @@ -0,0 +1,33 @@ +<?php + +use WPDesk\ApiClient\Authentication\JWTToken; + +class TestJWToken extends \PHPUnit\Framework\TestCase +{ + + public function testExpiredToken() + { + $expiredToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1MzQ4NTc2OTEsImV4cCI6MTUzNDg2MTI5MSwicm9sZXMiOlsiUk9MRV9VU0VSIiwiUk9MRV9TSE9QIl0sInVzZXJuYW1lIjozLCJpcCI6IjE3Mi4xOS4wLjEiLCJzaG9wIjoxfQ.iB9u8f3uBdq1KhkkYPAVpPFSwvkK4CQypfFQIiM8N5acQSFxv-jyzD2guGs4HtLOdMvu4Dt5zkd4ZfQFJNT2b6k7i33FyU4AsEDAVtwHL-TcqsnomXn70CjB5Rhgd2LteFl6wPp3XonE4SZ6Oo3vtBZzSoNNkR6-7T3OseJMwoJ7qzlEFBixNXG6UTlXPJky_b-rbfhFORlInxVzvs4GJgkJM3F3Ugy4bLSSPtBYxWcwKnKkFE8L4Z87Pezp4v35aXqJFpvJ3zll0gJ1F32Z2vx1oCmu0jOHkzzmu3wA2u6gK0iNgp591M7MKH2_3HaLfFY06cLoLDN6TR6wmzSvZYmzSw8C68MUH7uGWyGRU9j4YtdL8Bom3v8D1J-IC4Jx6-QPE665nd1VgzHZb1TFkHseUx3kLF5Jhgq7095NJ79QTC-6XTW2bN-T-dbbFkvjCU-B-9Ti09uMUEn4Rtlt_lThbr_lA9Wyc4qXAecqCz6dAC2UTy-_KxLwvfNrQ4sSS4y3B8bPh8qJrI0EsIbi5nY20sgBT71abUG9DHmIzj4rA5YW_DOu7Cez0WhNoCHjhMEymATrD2cWYxFCPWQfTTBuoD1HZgVfwYI9B-aZPw669orNRGkBtP8Bm5ghmCZoRHpISiY7UoIzgOnVKhB-Qv43g1uRprk7nYnfVEglqXg'; + + $jwtToken = new JWTToken($expiredToken); + $this->assertTrue($jwtToken->isSignatureValid(), 'Signature should be valid'); + $this->assertTrue($jwtToken->isExpired(), 'Token should be expired long ago'); + } + + public function testImmortalToken() + { + $tokenWithExpiredDateInFuture = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1MzQ5NDIyODgsImV4cCI6MzI1MDM2ODAwMDAsInJvbGVzIjpbIlJPTEVfVVNFUiIsIlJPTEVfU0hPUCJdLCJ1c2VybmFtZSI6MywiaXAiOiIxNzIuMTkuMC4xIiwic2hvcCI6NX0.gWsHsCGm8iUc13lGc9PgUtwv-qIYWAl98-jdzOdzuQ_PwreLuEFonP-hutp0_IXZtHeE6e1XLID-Cgn0YaahLrxybVGBTbcVgTJVOMB9Fv-gP9lHYNqvBdfQRcdXiO6PYCBbfpezunhcmiML_ebFfprdoMn8kG3K-XbEkyRB7MOQg0-dZ35tncVCZDfzLh5fucFzteQmCcddIosKpqr-rjYjRRCAB-aTE1vbAulZa1_VmP17l6m64__QBjHW9r07OTq7QviayXpOB_4mBdxI26XjgDXANCzlejcka5Uh7AS03cP3zDP8Mc0VaVZxqUhwotQ93s0VtyMNyUn_nkMebAuwXkDOmgsQzJHhlPD8hs9vzmNbN6ymKwsSZ-E3q1yPTQwXzu-rERdCaNifAIKlUyTETlvE3aoN7M0JjdCIVHBkf81ygppMITYJ0eytjyqBg0wjdRlMCpHEtGVosqLe8sNZo7NeZ9tPexhZxWXQNRA6VyE1NwDYd9yPsKBSPrulFcvkgoGOuL98rfE9iizMDYD60G0eTgafnR_99-IH4yoBMIjf4UNVzAcw8revbSE-3bgmxpdjVTJtVXw9LH13BHdB6GN7KJDwH4NRoz_MQJMSBELU18zY2yjmkNLXYI3YArTE3OQ-qBzD7rgPrcJBmoWTswbNVnTl2csFVHdQZOo'; + + $jwtToken = new JWTToken($tokenWithExpiredDateInFuture); + $this->assertTrue($jwtToken->isSignatureValid(), 'Signature should be valid'); + $this->assertFalse($jwtToken->isExpired(), 'Token should NOT be expired. Have 3000 years of live'); + } + + public function testStringValueAndBearer() { + $someToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1MzQ5NDIyODgsImV4cCI6MzI1MDM2ODAwMDAsInJvbGVzIjpbIlJPTEVfVVNFUiIsIlJPTEVfU0hPUCJdLCJ1c2VybmFtZSI6MywiaXAiOiIxNzIuMTkuMC4xIiwic2hvcCI6NX0.gWsHsCGm8iUc13lGc9PgUtwv-qIYWAl98-jdzOdzuQ_PwreLuEFonP-hutp0_IXZtHeE6e1XLID-Cgn0YaahLrxybVGBTbcVgTJVOMB9Fv-gP9lHYNqvBdfQRcdXiO6PYCBbfpezunhcmiML_ebFfprdoMn8kG3K-XbEkyRB7MOQg0-dZ35tncVCZDfzLh5fucFzteQmCcddIosKpqr-rjYjRRCAB-aTE1vbAulZa1_VmP17l6m64__QBjHW9r07OTq7QviayXpOB_4mBdxI26XjgDXANCzlejcka5Uh7AS03cP3zDP8Mc0VaVZxqUhwotQ93s0VtyMNyUn_nkMebAuwXkDOmgsQzJHhlPD8hs9vzmNbN6ymKwsSZ-E3q1yPTQwXzu-rERdCaNifAIKlUyTETlvE3aoN7M0JjdCIVHBkf81ygppMITYJ0eytjyqBg0wjdRlMCpHEtGVosqLe8sNZo7NeZ9tPexhZxWXQNRA6VyE1NwDYd9yPsKBSPrulFcvkgoGOuL98rfE9iizMDYD60G0eTgafnR_99-IH4yoBMIjf4UNVzAcw8revbSE-3bgmxpdjVTJtVXw9LH13BHdB6GN7KJDwH4NRoz_MQJMSBELU18zY2yjmkNLXYI3YArTE3OQ-qBzD7rgPrcJBmoWTswbNVnTl2csFVHdQZOo'; + + $jwtToken = new JWTToken($someToken); + $this->assertEquals($jwtToken->__toString(), $someToken); + $this->assertEquals($jwtToken->getAuthString(), 'Bearer ' . $someToken); + } +} \ No newline at end of file