From f19d42d4d6348e93c483ad7ad56498964c33052a Mon Sep 17 00:00:00 2001 From: Tobias Gross-Vogt <tgros@tugraz.at> Date: Mon, 7 Nov 2022 11:27:39 +0100 Subject: [PATCH] new helper class Http\ApiConnection communicating with an OIDC server and API server --- src/Http/ApiConnection.php | 157 +++++++++++++++++++++++++++++++ src/Http/ConnectionException.php | 1 + 2 files changed, 158 insertions(+) create mode 100644 src/Http/ApiConnection.php diff --git a/src/Http/ApiConnection.php b/src/Http/ApiConnection.php new file mode 100644 index 0000000..1b22c4f --- /dev/null +++ b/src/Http/ApiConnection.php @@ -0,0 +1,157 @@ +<?php + +declare(strict_types=1); + +namespace Dbp\Relay\CoreBundle\Http; + +use Dbp\Relay\CoreBundle\Helpers\Tools; +use Psr\Cache\CacheItemPoolInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Log\LoggerInterface; + +class ApiConnection +{ + public const API_URL_CONFIG_PARAMETER = 'api_url'; + + public const KEYCLOAK_SERVER_URL_CONFIG_PARAMETER = 'keycloak_server_url'; + public const KEYCLOAK_REALM_CONFIG_PARAMETER = 'keycloak_realm'; + public const CLIENT_ID_CONFIG_PARAMETER = 'client_id'; + public const CLIENT_SECRET_CONFIG_PARAMETER = 'client_secret'; + + /** @var Connection */ + private $connection; + + /** @var array */ + private $config; + + /** @var object|null */ + private $clientHandler; + + /** @var CacheItemPoolInterface|null */ + private $cachePool; + + /** @var int */ + private $cacheTTL; + + /** @var LoggerInterface|null */ + private $logger; + + /** @var string */ + private $accessToken; + + public function __construct() + { + $this->config = []; + } + + public function setCache(?CacheItemPoolInterface $cachePool, int $ttl): void + { + $this->cachePool = $cachePool; + $this->cacheTTL = $ttl; + if ($this->connection !== null) { + $this->connection->setCache($cachePool, $ttl); + } + } + + public function setConfig(array $config): void + { + $this->config = $config; + } + + /* + * Used to mock up server for unit testing. + */ + public function setClientHandler(?object $handler): void + { + $this->clientHandler = $handler; + if ($this->connection !== null) { + $this->connection->setClientHandler($handler); + } + } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + if ($this->connection !== null) { + $this->connection->setLogger($logger); + } + } + + /** + * @throws ConnectionException + */ + public function get(string $uri, array $options): ResponseInterface + { + $requestOptions = [ + Connection::REQUEST_OPTION_HEADERS => [ + 'Authorization' => 'Bearer '.$this->getAccessToken(), + ], + ]; + + return $this->getApiConnection()->get($uri, $options, $requestOptions); + } + + /** + * @throws ConnectionException + */ + private function getAccessToken(): string + { + if ($this->accessToken === null) { + $idServerUrl = $this->config[self::KEYCLOAK_SERVER_URL_CONFIG_PARAMETER]; + $idServerRealm = $this->config[self::KEYCLOAK_REALM_CONFIG_PARAMETER]; + $clientId = $this->config[self::CLIENT_ID_CONFIG_PARAMETER]; + $clientSecret = $this->config[self::CLIENT_SECRET_CONFIG_PARAMETER]; + + $tokenUrl = $idServerUrl.'/realms/'.$idServerRealm.'/protocol/openid-connect/token'; + $response = $this->getIdServerConnection()->postForm($tokenUrl, [ + 'client_id' => $clientId, + 'client_secret' => $clientSecret, + 'grant_type' => 'client_credentials', + ]); + + $responseData = $response->getBody()->getContents(); + + try { + $token = Tools::decodeJSON($responseData, true); + } catch (\JsonException $exception) { + throw new ConnectionException(sprintf('Failed to JSON decode access token: '.$exception->getMessage()), ConnectionException::JSON_EXCEPTION); + } + + $this->accessToken = $token['access_token']; + } + + return $this->accessToken; + } + + private function getApiConnection(): Connection + { + if ($this->connection === null) { + $connection = new Connection($this->config[self::API_URL_CONFIG_PARAMETER]); + + if ($this->cachePool !== null) { + $connection->setCache($this->cachePool, $this->cacheTTL); + } + if ($this->clientHandler !== null) { + $connection->setClientHandler($this->clientHandler); + } + if ($this->logger !== null) { + $connection->setLogger($this->logger); + } + + $this->connection = $connection; + } + + return $this->connection; + } + + private function getIdServerConnection(): Connection + { + $idServerConnection = new Connection(); + + if ($this->clientHandler !== null) { + $idServerConnection->setClientHandler($this->clientHandler); + } + + return $idServerConnection; + } +} diff --git a/src/Http/ConnectionException.php b/src/Http/ConnectionException.php index e890139..3c3ee6f 100644 --- a/src/Http/ConnectionException.php +++ b/src/Http/ConnectionException.php @@ -10,6 +10,7 @@ use Psr\Http\Message\ResponseInterface; class ConnectionException extends \RuntimeException { public const REQUEST_EXCEPTION = 1; + public const JSON_EXCEPTION = 2; /** @var RequestInterface|null */ private $request; -- GitLab