From 53608219dd060eef378ec81e79eb703d7fd009e2 Mon Sep 17 00:00:00 2001 From: Chris Tankersley Date: Fri, 16 Apr 2021 14:57:08 -0400 Subject: [PATCH] Moved authentication logic to individual handlers (#283) --- src/Client.php | 98 ++++++------------- src/Client/APIResource.php | 63 ++++++++++-- .../Credentials/Handler/AbstractHandler.php | 28 ++++++ .../Credentials/Handler/BasicHandler.php | 22 +++++ .../Credentials/Handler/HandlerInterface.php | 14 +++ .../Credentials/Handler/KeypairHandler.php | 19 ++++ .../Handler/SignatureBodyFormHandler.php | 29 ++++++ .../Handler/SignatureBodyHandler.php | 27 +++++ .../Handler/SignatureQueryHandler.php | 26 +++++ .../Handler/TokenBodyFormHandler.php | 25 +++++ .../Credentials/Handler/TokenBodyHandler.php | 29 ++++++ .../Credentials/Handler/TokenQueryHandler.php | 22 +++++ 12 files changed, 324 insertions(+), 78 deletions(-) create mode 100644 src/Client/Credentials/Handler/AbstractHandler.php create mode 100644 src/Client/Credentials/Handler/BasicHandler.php create mode 100644 src/Client/Credentials/Handler/HandlerInterface.php create mode 100644 src/Client/Credentials/Handler/KeypairHandler.php create mode 100644 src/Client/Credentials/Handler/SignatureBodyFormHandler.php create mode 100644 src/Client/Credentials/Handler/SignatureBodyHandler.php create mode 100644 src/Client/Credentials/Handler/SignatureQueryHandler.php create mode 100644 src/Client/Credentials/Handler/TokenBodyFormHandler.php create mode 100644 src/Client/Credentials/Handler/TokenBodyHandler.php create mode 100644 src/Client/Credentials/Handler/TokenQueryHandler.php diff --git a/src/Client.php b/src/Client.php index be28d613..6a78a821 100644 --- a/src/Client.php +++ b/src/Client.php @@ -32,6 +32,14 @@ use Vonage\Client\Credentials\Basic; use Vonage\Client\Credentials\Container; use Vonage\Client\Credentials\CredentialsInterface; +use Vonage\Client\Credentials\Handler\BasicHandler; +use Vonage\Client\Credentials\Handler\KeypairHandler; +use Vonage\Client\Credentials\Handler\SignatureBodyFormHandler; +use Vonage\Client\Credentials\Handler\SignatureBodyHandler; +use Vonage\Client\Credentials\Handler\SignatureQueryHandler; +use Vonage\Client\Credentials\Handler\TokenBodyFormHandler; +use Vonage\Client\Credentials\Handler\TokenBodyHandler; +use Vonage\Client\Credentials\Handler\TokenQueryHandler; use Vonage\Client\Credentials\Keypair; use Vonage\Client\Credentials\OAuth; use Vonage\Client\Credentials\SignatureSecret; @@ -297,39 +305,17 @@ public static function signRequest(RequestInterface $request, SignatureSecret $c { switch ($request->getHeaderLine('content-type')) { case 'application/json': - $body = $request->getBody(); - $body->rewind(); - $content = $body->getContents(); - $params = json_decode($content, true); - $params['api_key'] = $credentials['api_key']; - $signature = new Signature($params, $credentials['signature_secret'], $credentials['signature_method']); - $body->rewind(); - $body->write(json_encode($signature->getSignedParams())); + $handler = new SignatureBodyHandler(); break; case 'application/x-www-form-urlencoded': - $body = $request->getBody(); - $body->rewind(); - $content = $body->getContents(); - $params = []; - parse_str($content, $params); - $params['api_key'] = $credentials['api_key']; - $signature = new Signature($params, $credentials['signature_secret'], $credentials['signature_method']); - $params = $signature->getSignedParams(); - $body->rewind(); - $body->write(http_build_query($params, '', '&')); + $handler = new SignatureBodyFormHandler(); break; default: - $query = []; - parse_str($request->getUri()->getQuery(), $query); - $query['api_key'] = $credentials['api_key']; - $signature = new Signature($query, $credentials['signature_secret'], $credentials['signature_method']); - $request = $request->withUri( - $request->getUri()->withQuery(http_build_query($signature->getSignedParams())) - ); + $handler = new SignatureQueryHandler(); break; } - return $request; + return $handler($request, $credentials); } public static function authRequest(RequestInterface $request, Basic $credentials): RequestInterface @@ -337,57 +323,26 @@ public static function authRequest(RequestInterface $request, Basic $credentials switch ($request->getHeaderLine('content-type')) { case 'application/json': if (static::requiresBasicAuth($request)) { - $c = $credentials->asArray(); - $cx = base64_encode($c['api_key'] . ':' . $c['api_secret']); - - $request = $request->withHeader('Authorization', 'Basic ' . $cx); + $handler = new BasicHandler(); } elseif (static::requiresAuthInUrlNotBody($request)) { - $query = []; - parse_str($request->getUri()->getQuery(), $query); - $query = array_merge($query, $credentials->asArray()); - - $request = $request->withUri($request->getUri()->withQuery(http_build_query($query))); + $handler = new TokenQueryHandler(); } else { - $body = $request->getBody(); - $body->rewind(); - $content = $body->getContents(); - $params = json_decode($content, true); - - if (!$params) { - $params = []; - } - - $params = array_merge($params, $credentials->asArray()); - $body->rewind(); - $body->write(json_encode($params)); + $handler = new TokenBodyHandler(); } break; case 'application/x-www-form-urlencoded': - $body = $request->getBody(); - $body->rewind(); - $content = $body->getContents(); - $params = []; - parse_str($content, $params); - $params = array_merge($params, $credentials->asArray()); - $body->rewind(); - $body->write(http_build_query($params, '', '&')); + $handler = new TokenBodyFormHandler(); break; default: if (static::requiresBasicAuth($request)) { - $c = $credentials->asArray(); - $cx = base64_encode($c['api_key'] . ':' . $c['api_secret']); - - $request = $request->withHeader('Authorization', 'Basic ' . $cx); + $handler = new BasicHandler(); } else { - $query = []; - parse_str($request->getUri()->getQuery(), $query); - $query = array_merge($query, $credentials->asArray()); - $request = $request->withUri($request->getUri()->withQuery(http_build_query($query))); + $handler = new TokenQueryHandler(); } break; } - return $request; + return $handler($request, $credentials); } /** @@ -504,16 +459,14 @@ public function send(RequestInterface $request): ResponseInterface { if ($this->credentials instanceof Container) { if ($this->needsKeypairAuthentication($request)) { - $token = $this->credentials->get(Keypair::class)->generateJwt(); - - $request = $request->withHeader('Authorization', 'Bearer ' . $token->toString()); + $handler = new KeypairHandler(); + $request = $handler($request, $this->getCredentials()); } else { $request = self::authRequest($request, $this->credentials->get(Basic::class)); } } elseif ($this->credentials instanceof Keypair) { - $token = $this->credentials->generateJwt(); - - $request = $request->withHeader('Authorization', 'Bearer ' . $token->toString()); + $handler = new KeypairHandler(); + $request = $handler($request, $this->getCredentials()); } elseif ($this->credentials instanceof SignatureSecret) { $request = self::signRequest($request, $this->credentials); } elseif ($this->credentials instanceof Basic) { @@ -696,4 +649,9 @@ public function getLogger(): ?LoggerInterface return $this->logger; } + + public function getCredentials(): CredentialsInterface + { + return $this->credentials; + } } diff --git a/src/Client/APIResource.php b/src/Client/APIResource.php index f24e8ee6..31aa9ba8 100644 --- a/src/Client/APIResource.php +++ b/src/Client/APIResource.php @@ -11,23 +11,28 @@ namespace Vonage\Client; +use function is_null; +use function json_decode; +use function json_encode; +use function http_build_query; use Laminas\Diactoros\Request; -use Psr\Http\Client\ClientExceptionInterface; +use Vonage\Entity\Filter\EmptyFilter; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use Vonage\Entity\Filter\EmptyFilter; -use Vonage\Entity\Filter\FilterInterface; use Vonage\Entity\IterableAPICollection; - -use function http_build_query; -use function is_null; -use function json_decode; -use function json_encode; +use Vonage\Entity\Filter\FilterInterface; +use Psr\Http\Client\ClientExceptionInterface; +use Vonage\Client\Credentials\Handler\HandlerInterface; class APIResource implements ClientAwareInterface { use ClientAwareTrait; + /** + * @var HandlerInterface + */ + protected $authHandler; + /** * Base URL that we will hit. This can be overridden from the underlying * client or directly on this class. @@ -80,6 +85,16 @@ class APIResource implements ClientAwareInterface */ protected $lastResponse; + /** + * Adds authentication to a request + * @todo Use this method when Vonage\Client no longer adds authentication + */ + public function addAuth(RequestInterface $request) + { + $creds = $this->getClient()->getCredentials(); + return $this->getAuthHandler()($request, $creds); + } + /** * @throws ClientExceptionInterface * @throws Exception\Exception @@ -97,6 +112,10 @@ public function create(array $body, string $uri = '', array $headers = []): ?arr $headers ); + if ($this->getAuthHandler()) { + $request = $this->addAuth($request); + } + $request->getBody()->write(json_encode($body)); $this->lastRequest = $request; @@ -142,6 +161,10 @@ public function delete(string $id, array $headers = []): ?array $headers ); + if ($this->getAuthHandler()) { + $request = $this->addAuth($request); + } + $response = $this->getClient()->send($request); $status = (int)$response->getStatusCode(); @@ -186,6 +209,10 @@ public function get($id, array $query = [], array $headers = []) $headers ); + if ($this->getAuthHandler()) { + $request = $this->addAuth($request); + } + $response = $this->getClient()->send($request); $status = (int)$response->getStatusCode(); @@ -202,6 +229,11 @@ public function get($id, array $query = [], array $headers = []) return json_decode($response->getBody()->getContents(), true); } + public function getAuthHandler(): ?HandlerInterface + { + return $this->authHandler; + } + public function getBaseUrl(): ?string { if (!$this->baseUrl && $this->client) { @@ -294,6 +326,13 @@ public function search(?FilterInterface $filter = null, string $uri = ''): Itera return $collection; } + public function setAuthHandler($handler): self + { + $this->authHandler = $handler; + + return $this; + } + public function setBaseUrl(string $url): self { $this->baseUrl = $url; @@ -362,6 +401,10 @@ public function submit(array $formData = [], string $uri = '', array $headers = $headers ); + if ($this->getAuthHandler()) { + $request = $this->addAuth($request); + } + $request->getBody()->write(http_build_query($formData)); $response = $this->getClient()->send($request); $status = $response->getStatusCode(); @@ -396,6 +439,10 @@ public function update(string $id, array $body, array $headers = []): ?array $headers ); + if ($this->getAuthHandler()) { + $request = $this->addAuth($request); + } + $request->getBody()->write(json_encode($body)); $response = $this->getClient()->send($request); diff --git a/src/Client/Credentials/Handler/AbstractHandler.php b/src/Client/Credentials/Handler/AbstractHandler.php new file mode 100644 index 00000000..0e0c8a6f --- /dev/null +++ b/src/Client/Credentials/Handler/AbstractHandler.php @@ -0,0 +1,28 @@ +get($class); + if (!is_null($creds)) { + return $creds; + } + } + + throw new \RuntimeException('Requested auth type not found'); + } +} diff --git a/src/Client/Credentials/Handler/BasicHandler.php b/src/Client/Credentials/Handler/BasicHandler.php new file mode 100644 index 00000000..5e54a824 --- /dev/null +++ b/src/Client/Credentials/Handler/BasicHandler.php @@ -0,0 +1,22 @@ +extract(Basic::class, $credentials); + + $c = $credentials->asArray(); + $cx = base64_encode($c['api_key'] . ':' . $c['api_secret']); + + $request = $request->withHeader('Authorization', 'Basic ' . $cx); + + return $request; + } +} \ No newline at end of file diff --git a/src/Client/Credentials/Handler/HandlerInterface.php b/src/Client/Credentials/Handler/HandlerInterface.php new file mode 100644 index 00000000..f7a81bb9 --- /dev/null +++ b/src/Client/Credentials/Handler/HandlerInterface.php @@ -0,0 +1,14 @@ +extract(Keypair::class, $credentials); + $token = $credentials->generateJwt(); + + return $request->withHeader('Authorization', 'Bearer ' . $token->toString()); + } +} \ No newline at end of file diff --git a/src/Client/Credentials/Handler/SignatureBodyFormHandler.php b/src/Client/Credentials/Handler/SignatureBodyFormHandler.php new file mode 100644 index 00000000..4bcc8618 --- /dev/null +++ b/src/Client/Credentials/Handler/SignatureBodyFormHandler.php @@ -0,0 +1,29 @@ +extract(SignatureSecret::class, $credentials); + + $body = $request->getBody(); + $body->rewind(); + $content = $body->getContents(); + $params = []; + parse_str($content, $params); + $params['api_key'] = $credentials['api_key']; + $signature = new Signature($params, $credentials['signature_secret'], $credentials['signature_method']); + $params = $signature->getSignedParams(); + $body->rewind(); + $body->write(http_build_query($params, '', '&')); + + return $request; + } +} \ No newline at end of file diff --git a/src/Client/Credentials/Handler/SignatureBodyHandler.php b/src/Client/Credentials/Handler/SignatureBodyHandler.php new file mode 100644 index 00000000..38e0533d --- /dev/null +++ b/src/Client/Credentials/Handler/SignatureBodyHandler.php @@ -0,0 +1,27 @@ +extract(SignatureSecret::class, $credentials); + + $body = $request->getBody(); + $body->rewind(); + $content = $body->getContents(); + $params = json_decode($content, true); + $params['api_key'] = $credentials['api_key']; + $signature = new Signature($params, $credentials['signature_secret'], $credentials['signature_method']); + $body->rewind(); + $body->write(json_encode($signature->getSignedParams())); + + return $request; + } +} \ No newline at end of file diff --git a/src/Client/Credentials/Handler/SignatureQueryHandler.php b/src/Client/Credentials/Handler/SignatureQueryHandler.php new file mode 100644 index 00000000..170fdf4e --- /dev/null +++ b/src/Client/Credentials/Handler/SignatureQueryHandler.php @@ -0,0 +1,26 @@ +extract(SignatureSecret::class, $credentials); + + $query = []; + parse_str($request->getUri()->getQuery(), $query); + $query['api_key'] = $credentials['api_key']; + $signature = new Signature($query, $credentials['signature_secret'], $credentials['signature_method']); + $request = $request->withUri( + $request->getUri()->withQuery(http_build_query($signature->getSignedParams())) + ); + + return $request; + } +} \ No newline at end of file diff --git a/src/Client/Credentials/Handler/TokenBodyFormHandler.php b/src/Client/Credentials/Handler/TokenBodyFormHandler.php new file mode 100644 index 00000000..20ee3d8d --- /dev/null +++ b/src/Client/Credentials/Handler/TokenBodyFormHandler.php @@ -0,0 +1,25 @@ +extract(Basic::class, $credentials); + $body = $request->getBody(); + $body->rewind(); + $content = $body->getContents(); + $params = []; + parse_str($content, $params); + $params = array_merge($params, $credentials->asArray()); + $body->rewind(); + $body->write(http_build_query($params, '', '&')); + + return $request; + } +} \ No newline at end of file diff --git a/src/Client/Credentials/Handler/TokenBodyHandler.php b/src/Client/Credentials/Handler/TokenBodyHandler.php new file mode 100644 index 00000000..8783d537 --- /dev/null +++ b/src/Client/Credentials/Handler/TokenBodyHandler.php @@ -0,0 +1,29 @@ +extract(Basic::class, $credentials); + $body = $request->getBody(); + $body->rewind(); + $content = $body->getContents(); + $params = json_decode($content, true); + + if (!$params) { + $params = []; + } + + $params = array_merge($params, $credentials->asArray()); + $body->rewind(); + $body->write(json_encode($params)); + + return $request; + } +} diff --git a/src/Client/Credentials/Handler/TokenQueryHandler.php b/src/Client/Credentials/Handler/TokenQueryHandler.php new file mode 100644 index 00000000..ddbc4e28 --- /dev/null +++ b/src/Client/Credentials/Handler/TokenQueryHandler.php @@ -0,0 +1,22 @@ +extract(Basic::class, $credentials); + $query = []; + parse_str($request->getUri()->getQuery(), $query); + $query = array_merge($query, $credentials->asArray()); + + $request = $request->withUri($request->getUri()->withQuery(http_build_query($query))); + + return $request; + } +} \ No newline at end of file