Skip to content

Commit

Permalink
Added exceptions handling
Browse files Browse the repository at this point in the history
  • Loading branch information
cableman committed Oct 7, 2024
1 parent 85c48af commit 97593c3
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 22 deletions.
3 changes: 1 addition & 2 deletions .env
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
COMPOSE_PROJECT_NAME=itkdev-vault
COMPOSE_DOMAIN=php-vault.local.itkdev.dk
COMPOSE_PROJECT_NAME=vault-library
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ See [keep a changelog] for information about writing changes to this log.

## [Unreleased]

* Added version support to secrets.
* Added test cases.
* Added cache of token and possibility to cache secrets.
* Added get secrets.
Expand Down
7 changes: 7 additions & 0 deletions src/Vault/Exception/NotFoundException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace ItkDev\Vault\Exception;

class NotFoundException extends VaultException
{
}
13 changes: 13 additions & 0 deletions src/Vault/Exception/VaultException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace ItkDev\Vault\Exception;

/**
* Main library exception.
*
* All other exceptions extend this exception, making it a catch-all for
* handling errors.
*/
class VaultException extends \Exception
{
}
65 changes: 47 additions & 18 deletions src/Vault/Vault.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@

namespace ItkDev\Vault;

use ItkDev\Vault\Exception\NotFoundException;
use ItkDev\Vault\Exception\VaultException;
use ItkDev\Vault\Model\Secret;
use ItkDev\Vault\Model\Token;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\SimpleCache\CacheInterface;
use Psr\SimpleCache\InvalidArgumentException;

readonly class Vault implements VaultInterface
{
Expand All @@ -21,11 +25,10 @@ public function __construct(
}

/**
* @throws \DateMalformedIntervalStringException
* @throws VaultException
* @throws \DateMalformedStringException
* @throws \JsonException
* @throws \Psr\Http\Client\ClientExceptionInterface
* @throws \Psr\SimpleCache\InvalidArgumentException
* @throws InvalidArgumentException
* @throws \DateMalformedIntervalStringException
*/
public function login(string $roleId, string $secretId, string $enginePath = 'approle', bool $refreshCache = false): Token
{
Expand All @@ -43,9 +46,18 @@ public function login(string $roleId, string $secretId, string $enginePath = 'ap
->withHeader('Content-Type', 'application/json')
->withBody($body);

$response = $this->httpClient->sendRequest($request);
try {
$response = $this->httpClient->sendRequest($request);
$data = json_decode($response->getBody(), associative: true, flags: JSON_THROW_ON_ERROR);
} catch (ClientExceptionInterface $e) {
throw new VaultException(sprintf('Vault login failed: %s', $e->getMessage()), previous: $e);
} catch (\JsonException $e) {
throw new VaultException(sprintf('Vault data decode failed: %s', $e->getMessage()), previous: $e);
}

$data = json_decode($response->getBody(), associative: true, flags: JSON_THROW_ON_ERROR);
if (isset($data['errors'])) {
throw new VaultException(sprintf('Vault login failed: %s', reset($data['errors'])));
}

$ttl = (int) $data['auth']['lease_duration'];
$now = new \DateTimeImmutable(timezone: new \DateTimeZone('UTC'));
Expand All @@ -64,18 +76,19 @@ public function login(string $roleId, string $secretId, string $enginePath = 'ap
}

/**
* @throws VaultException
* @throws \DateMalformedStringException
* @throws \JsonException
* @throws \Psr\Http\Client\ClientExceptionInterface
* @throws \Psr\SimpleCache\InvalidArgumentException
* @throws InvalidArgumentException
* @throws NotFoundException
*/
public function getSecret(Token $token, string $path, string $secret, string $id, bool $useCache = false, bool $refreshCache = false, int $expire = 0): Secret
public function getSecret(Token $token, string $path, string $secret, string $id, ?int $version = null, bool $useCache = false, bool $refreshCache = false, int $expire = 0): Secret
{
$secret = $this->getSecrets(
token: $token,
path: $path,
secret: $secret,
ids: [$id],
version: $version,
useCache: $useCache,
refreshCache: $refreshCache,
expire: $expire
Expand All @@ -85,29 +98,45 @@ public function getSecret(Token $token, string $path, string $secret, string $id
}

/**
* @throws VaultException
* @throws NotFoundException
* @throws \DateMalformedStringException
* @throws \JsonException
* @throws \Psr\Http\Client\ClientExceptionInterface
* @throws \Psr\SimpleCache\InvalidArgumentException
* @throws InvalidArgumentException
*/
public function getSecrets(Token $token, string $path, string $secret, array $ids, bool $useCache = false, bool $refreshCache = false, int $expire = 0): array
public function getSecrets(Token $token, string $path, string $secret, array $ids, ?int $version = null, bool $useCache = false, bool $refreshCache = false, int $expire = 0): array
{
$cacheKey = 'itkdev_vault_secret_'.$secret;
$data = $this->cache->get($cacheKey);

if (!$useCache || is_null($data) || $refreshCache) {
$url = sprintf('%s/v1/%s/data/%s', $this->vaultUrl, $path, $secret);
if (!is_null($version)) {
$url .= '?version='.$version;
}

$request = $this->requestFactory->createRequest('GET', $url)
->withHeader('Content-Type', 'application/json')
->withHeader('Authorization', 'Bearer '.$token->token);
$response = $this->httpClient->sendRequest($request);

$res = json_decode($response->getBody(), associative: true, flags: JSON_THROW_ON_ERROR);
try {
$response = $this->httpClient->sendRequest($request);
$res = json_decode($response->getBody(), associative: true, flags: JSON_THROW_ON_ERROR);
} catch (ClientExceptionInterface $e) {
throw new VaultException(sprintf('Vault fetch failed: %s', $e->getMessage()), previous: $e);
} catch (\JsonException $e) {
throw new VaultException(sprintf('Vault data decode failed: %s', $e->getMessage()), previous: $e);
}

if (isset($res['errors'])) {
if (empty($res['errors'])) {
throw new NotFoundException('Secrets not found.');
}
preg_match('/.*:\n\t\* (.+)\n\n$/', reset($res['errors']), $matches);
throw new VaultException(sprintf('Vault failed: %s', $matches[1] ?? ''));
}

$created = new \DateTimeImmutable($res['data']['metadata']['created_time'], new \DateTimeZone('UTC'));
$version = $res['data']['metadata']['version'];

$data = [];
if (!empty($ids)) {
$secrets = $res['data']['data'];
Expand All @@ -120,7 +149,7 @@ public function getSecrets(Token $token, string $path, string $secret, array $id
createdAt: $created
);
} else {
throw new \InvalidArgumentException(sprintf('Secret with ID "%s" not found.', $id));
throw new NotFoundException(sprintf('Secret with ID "%s" not found.', $id));
}
}
}
Expand Down
15 changes: 13 additions & 2 deletions src/Vault/VaultInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace ItkDev\Vault;

use ItkDev\Vault\Exception\VaultException;
use ItkDev\Vault\Model\Secret;
use ItkDev\Vault\Model\Token;

Expand All @@ -21,6 +22,8 @@ interface VaultInterface
*
* @return Token
* Token that can be used to communicate with the vault
*
* @throws VaultException
*/
public function login(string $roleId, string $secretId, string $enginePath = 'approle', bool $refreshCache = false): Token;

Expand All @@ -35,6 +38,8 @@ public function login(string $roleId, string $secretId, string $enginePath = 'ap
* The type of secret being requested
* @param string $id
* An array of identifiers specifying which secrets to retrieve
* @param int|null $version
* The version of the secret
* @param bool $useCache
* Optional parameter to indicate whether to use cached secrets. Defaults to false.
* @param bool $refreshCache
Expand All @@ -44,8 +49,10 @@ public function login(string $roleId, string $secretId, string $enginePath = 'ap
*
* @return Secret
* The secret found
*
* @throws VaultException
*/
public function getSecret(Token $token, string $path, string $secret, string $id, bool $useCache = false, bool $refreshCache = false, int $expire = 0): Secret;
public function getSecret(Token $token, string $path, string $secret, string $id, ?int $version = null, bool $useCache = false, bool $refreshCache = false, int $expire = 0): Secret;

/**
* Retrieves secrets from the specified secret engine path.
Expand All @@ -58,6 +65,8 @@ public function getSecret(Token $token, string $path, string $secret, string $id
* The type of secret being requested
* @param array<string> $ids
* An array of identifiers specifying which secrets to retrieve
* @param int|null $version
* The version of the secrets
* @param bool $useCache
* Optional parameter to indicate whether to use cached secrets. Defaults to false.
* @param bool $refreshCache
Expand All @@ -67,6 +76,8 @@ public function getSecret(Token $token, string $path, string $secret, string $id
*
* @return array
* An array containing the requested secrets
*
* @throws VaultException
*/
public function getSecrets(Token $token, string $path, string $secret, array $ids, bool $useCache = false, bool $refreshCache = false, int $expire = 0): array;
public function getSecrets(Token $token, string $path, string $secret, array $ids, ?int $version = null, bool $useCache = false, bool $refreshCache = false, int $expire = 0): array;
}

0 comments on commit 97593c3

Please sign in to comment.