From 90965374ff90453926f8c9096e103e91af4f90df Mon Sep 17 00:00:00 2001 From: Melvin Date: Thu, 14 Sep 2023 15:32:59 +0200 Subject: [PATCH] Initial commit --- .gitignore | 1 + .../InvalidBasicAuthPasswordException.php | 18 ++ .../InvalidBasicAuthUsernameException.php | 18 ++ Exceptions/InvalidBearerTokenException.php | 16 ++ Http/Client.php | 109 ++++++++++ Http/Plugins/Auth/ApiKeyPluginWrapper.php | 46 ++++ Http/Plugins/Auth/AuthHttpPluginInterface.php | 12 ++ Http/Plugins/Auth/BasicAuthPluginWrapper.php | 63 ++++++ Http/Plugins/Auth/BearerPluginWrapper.php | 34 +++ Http/Plugins/HostPlugin.php | 32 +++ Http/Plugins/HttpPluginInterface.php | 12 ++ Http/Plugins/LoggerPlugin.php | 35 +++ Http/Plugins/PathPlugin.php | 32 +++ README.md | 199 ++++++++++++++++++ composer.json | 19 ++ etc/acl.xml | 15 ++ etc/adminhtml/system.xml | 23 ++ etc/config.xml | 11 + etc/di.xml | 13 ++ etc/module.xml | 5 + registration.php | 9 + 21 files changed, 722 insertions(+) create mode 100644 .gitignore create mode 100644 Exceptions/InvalidBasicAuthPasswordException.php create mode 100644 Exceptions/InvalidBasicAuthUsernameException.php create mode 100644 Exceptions/InvalidBearerTokenException.php create mode 100644 Http/Client.php create mode 100644 Http/Plugins/Auth/ApiKeyPluginWrapper.php create mode 100644 Http/Plugins/Auth/AuthHttpPluginInterface.php create mode 100644 Http/Plugins/Auth/BasicAuthPluginWrapper.php create mode 100644 Http/Plugins/Auth/BearerPluginWrapper.php create mode 100644 Http/Plugins/HostPlugin.php create mode 100644 Http/Plugins/HttpPluginInterface.php create mode 100644 Http/Plugins/LoggerPlugin.php create mode 100644 Http/Plugins/PathPlugin.php create mode 100644 README.md create mode 100644 composer.json create mode 100644 etc/acl.xml create mode 100644 etc/adminhtml/system.xml create mode 100644 etc/config.xml create mode 100644 etc/di.xml create mode 100644 etc/module.xml create mode 100644 registration.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62c8935 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea/ \ No newline at end of file diff --git a/Exceptions/InvalidBasicAuthPasswordException.php b/Exceptions/InvalidBasicAuthPasswordException.php new file mode 100644 index 0000000..eeddc06 --- /dev/null +++ b/Exceptions/InvalidBasicAuthPasswordException.php @@ -0,0 +1,18 @@ +plugins = $plugins; + $this->clientName = $clientName; + $this->additionalPlugins = $additionalPlugins; + $this->apiKeyPlugin = $apiKeyPlugin; + $this->bearerPlugin = $bearerPlugin; + $this->basicAuthPlugin = $basicAuthPlugin; + } + + public function getClient(array $authenticationOptions = []) + { + $cacheKey = sha1(json_encode($authenticationOptions)); + if (array_key_exists($cacheKey, $this->clientRegistry)) { + return $this->clientRegistry[$cacheKey]; + } + + $plugins = $this->getPlugins(); + + if ($authenticationRegistry = $this->getAuthenticationRegistry($authenticationOptions)) { + $plugins[] = $authenticationRegistry; + } + + $httpClient = new PluginClient(HttpClientDiscovery::find(), $plugins); + + $this->clientRegistry[$cacheKey] = $this->clientName::create($httpClient); + + return $this->clientRegistry[$cacheKey]; + } + + /** + * @return Plugin[] + */ + private function getPlugins(): array + { + $result = array_map(static function(HttpPluginInterface $plugin): ?Plugin { + return $plugin->create(); + }, array_merge($this->plugins, $this->additionalPlugins)); + + // remove null plugins that are not enabled yet + return array_filter($result); + } + + private function getAuthenticationRegistry(array $options = []): ?AuthenticationRegistry + { + $authenticationPlugins = []; + + if ($this->apiKeyPlugin !== null) { + $authenticationPlugins[] = $this->apiKeyPlugin->create(); + } + + if ($this->bearerPlugin !== null) { + $authenticationPlugins[] = $this->bearerPlugin->create($options); + } + + if ($this->basicAuthPlugin !== null) { + $authenticationPlugins[] = $this->basicAuthPlugin->create($options); + } + + if (empty($authenticationPlugins)) { + return null; + } + + return new AuthenticationRegistry($authenticationPlugins); + } +} diff --git a/Http/Plugins/Auth/ApiKeyPluginWrapper.php b/Http/Plugins/Auth/ApiKeyPluginWrapper.php new file mode 100644 index 0000000..1678ea7 --- /dev/null +++ b/Http/Plugins/Auth/ApiKeyPluginWrapper.php @@ -0,0 +1,46 @@ +scopeConfig = $scopeConfig; + $this->encryptor = $encryptor; + $this->authenticationPluginClass = $authenticationPluginClass; + $this->configPath = $configPath; + } + + /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function create(array $options = []): AuthenticationPlugin + { + $apiKey = $this->encryptor->decrypt( + $this->scopeConfig->getValue($this->configPath) + ); + + return new $this->authenticationPluginClass($apiKey); + } +} diff --git a/Http/Plugins/Auth/AuthHttpPluginInterface.php b/Http/Plugins/Auth/AuthHttpPluginInterface.php new file mode 100644 index 0000000..859f8c1 --- /dev/null +++ b/Http/Plugins/Auth/AuthHttpPluginInterface.php @@ -0,0 +1,12 @@ +basicAuthPluginClass = $basicAuthPluginClass; + $this->scopeConfig = $scopeConfig; + $this->encryptor = $encryptor; + $this->configPathUsername = $configPathUsername; + $this->configPathPassword = $configPathPassword; + } + + /** + * @param array $options + * + * @return AuthenticationPlugin + * @throws InvalidBasicAuthPasswordException + * @throws InvalidBasicAuthUsernameException + */ + public function create(array $options = []): AuthenticationPlugin + { + if (!($username = $this->scopeConfig->getValue($this->configPathUsername))) { + throw new InvalidBasicAuthUsernameException(); + } + if (!($passwordEncrypt = $this->scopeConfig->getValue($this->configPathPassword))) { + throw new InvalidBasicAuthPasswordException(); + } + + $password = $this->encryptor->decrypt( + $passwordEncrypt + ); + + return new $this->basicAuthPluginClass($username, $password); + } +} diff --git a/Http/Plugins/Auth/BearerPluginWrapper.php b/Http/Plugins/Auth/BearerPluginWrapper.php new file mode 100644 index 0000000..bbf984c --- /dev/null +++ b/Http/Plugins/Auth/BearerPluginWrapper.php @@ -0,0 +1,34 @@ +bearerPluginClass = $bearerPluginClass; + } + + /** + * @throws InvalidBearerTokenException + */ + public function create(array $options = []): AuthenticationPlugin + { + if (!isset($options['Bearer'])) { + throw new InvalidBearerTokenException(); + } + + return new $this->bearerPluginClass($options['Bearer']); + } +} diff --git a/Http/Plugins/HostPlugin.php b/Http/Plugins/HostPlugin.php new file mode 100644 index 0000000..bde29b2 --- /dev/null +++ b/Http/Plugins/HostPlugin.php @@ -0,0 +1,32 @@ +scopeConfig = $scopeConfig; + $this->configPath = $configPath; + } + + public function create(): ?Plugin + { + $uri = Psr17FactoryDiscovery::findUriFactory()->createUri( + $this->scopeConfig->getValue($this->configPath) + ); + + return new AddHostPlugin($uri); + } +} diff --git a/Http/Plugins/HttpPluginInterface.php b/Http/Plugins/HttpPluginInterface.php new file mode 100644 index 0000000..b70a4dd --- /dev/null +++ b/Http/Plugins/HttpPluginInterface.php @@ -0,0 +1,12 @@ +scopeConfig = $scopeConfig; + $this->logger = $logger; + } + + public function create(): ?Plugin + { + if (!$this->scopeConfig->isSetFlag(self::JANE_LOGGER_ACTIVE_PATH)) { + return null; + } + + return new Plugin\LoggerPlugin($this->logger); + } +} + diff --git a/Http/Plugins/PathPlugin.php b/Http/Plugins/PathPlugin.php new file mode 100644 index 0000000..2c7afb4 --- /dev/null +++ b/Http/Plugins/PathPlugin.php @@ -0,0 +1,32 @@ +scopeConfig = $scopeConfig; + $this->configPath = $configPath; + } + + public function create(): ?Plugin + { + $uri = Psr17FactoryDiscovery::findUriFactory()->createUri( + $this->scopeConfig->getValue($this->configPath) + ); + + return new AddPathPlugin($uri); + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..2c06906 --- /dev/null +++ b/README.md @@ -0,0 +1,199 @@ +# SDK Bridge from JanePHP to Magento 2 + +# How to install + +You can install it by typing : `composer require adexos/m2-jane-sdk-bridge:^1.0` + +# How to use + +This SDK Bridge is a tool to easier Jane SDK generated client your implementation through your Magento 2 application. + +In your `di.xml`, you have to declare : + +```xml + + + + MyHttpClient + + + + + + + MyClientHostPlugin + MyClientPathPlugin + + + PathTo\Generated\Jane\Client + + + + + + + Vendor\MySDKBridge\Model\Config::PATH_TO_ENDPOINT_URL_YOU_MUST_DEFINE + + + + + + + + Vendor\MySDKBridge\Model\Config::XPATH_TO_ENDPOINT_URL_YOU_MUST_DEFINE + + + + +``` + +Where your `Vendor\MySDKBridge\Http\MyClient` is + +```php +client = $client; + } + + public function getClient(): \PathTo\Generated\JaneClient + { + //This class allows you to have an attachment point to the HTTP client + //and to have hint types because of the generic SDKs + //you can do whatever you want here such as doe somes checks or passing Bearer token : + // $this->tokenStrategy->getToken(); + // return $this->client->getClient(['Bearer' => $token]); + + return $this->client->getClient(); + } +} +``` + +You must extends this specific class to have autocompletion for your injected client. + +Then, in a class you can inject : + +```php +client->getClient()->updateCustomer(['firstname' => $customer->getFirstname()]); + } +} +``` + +## Authentication + +You may want to use authentication provided by JanePHP in a generic way (API key or Bearer for example). + +JanePHP generates (if needed) an authentication class in the `Authentication` folder of your SDK : + +`PathTo\Generated\Authentication\ApiKeyAuthentication` + +To do so, use the `ApiKeyPluginWrapper` provided in this module and add it to your plugins : + +You can use following auth methods : +- Bearer authorization +- Basic auth authorization + +```xml + + + MyHttpClient + + + + + + + + + MyApiKeyPlugin + MyBearerPlugin + MyBasicAuthPlugin + + + + + + + PathTo\Generated\Authentication\ApiKeyAuthentication + + + + Vendor\MySDKBridge\Model\Config::XML_PATH_ZDFR_CUSTOMER_SDK_API_KEY + + + + + + + + + PathTo\Generated\Authentication\Bearer + + + + + + + + + PathTo\Generated\Authentication\BasicAuth + + + Vendor\MySDKBridge\Model\Config::XML_PATH_API_BASIC_AUTH_USERNAME + + + Vendor\MySDKBridge\Model\Config::XML_PATH_API_BASIC_AUTH_PASSWORD + + + +``` + +> /!\ Be aware that your API key **MUST** be [encrypted](https://www.magevision.com/blog/post/decrypt-an-encrypted-config-value-magento-2). +> This behavior won't be modified for security reasons. + +## Disclaimer + +Your PHP application must implement its own `Client` or have some packages installed to handle : + +- `Psr\Http\Client\ClientInterface` PSR-18 +- `Psr\Http\Message\RequestFactoryInterface` PSR-17 +- `Psr\Http\Message\StreamFactoryInterface` PSR-17 + +In this package : +- PSR-18 is handled by `symfony/http-client >= 4.4` +- PSR-17 is handled by `nyholm/psr7 ^1.4` + +You can use the package you want as long as it respects the PSR standards. + +## Logger + +- The bridge implement the http logger plugin and if you active this feature you can log all request and response to a loggerInterface. You can admin this in Configuration section of your magento. \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..52bb84f --- /dev/null +++ b/composer.json @@ -0,0 +1,19 @@ +{ + "name": "adexos/m2-jane-sdk-bridge", + "description": "Adexos Jane SDK Bridge for Magento 2", + "require": { + "php": "^7.4 || ^8.0", + "nyholm/psr7": "^1.4", + "symfony/http-client": ">= 4.4", + "php-http/logger-plugin": "^1.3" + }, + "type": "magento2-module", + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Adexos\\JaneSDKBridge\\": "" + } + } +} diff --git a/etc/acl.xml b/etc/acl.xml new file mode 100644 index 0000000..039e3f4 --- /dev/null +++ b/etc/acl.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml new file mode 100644 index 0000000..a4f25c6 --- /dev/null +++ b/etc/adminhtml/system.xml @@ -0,0 +1,23 @@ + + + + + + +
+ separator-top + + adexos + Adexos_JaneSDKBridge::configuration + + + + + Magento\Config\Model\Config\Source\Yesno + + +
+
+
diff --git a/etc/config.xml b/etc/config.xml new file mode 100644 index 0000000..d0419c8 --- /dev/null +++ b/etc/config.xml @@ -0,0 +1,11 @@ + + + + + + 1 + + + + diff --git a/etc/di.xml b/etc/di.xml new file mode 100644 index 0000000..500e766 --- /dev/null +++ b/etc/di.xml @@ -0,0 +1,13 @@ + + + + + + Adexos\JaneSDKBridge\Http\Plugins\HostPlugin + Adexos\JaneSDKBridge\Http\Plugins\PathPlugin + Adexos\JaneSDKBridge\Http\Plugins\LoggerPlugin + + + + diff --git a/etc/module.xml b/etc/module.xml new file mode 100644 index 0000000..6281e3e --- /dev/null +++ b/etc/module.xml @@ -0,0 +1,5 @@ + + + + diff --git a/registration.php b/registration.php new file mode 100644 index 0000000..9975eb9 --- /dev/null +++ b/registration.php @@ -0,0 +1,9 @@ +