Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 49 additions & 49 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,51 +1,51 @@
{
"name": "secretary/php",
"description": "Monorepo for Secretary's PHP implementation",
"type": "library",
"require-dev": {
"php": "^8.2",
"ext-json": "*",
"aws/aws-sdk-php": "^3.91",
"google/cloud-secret-manager": "^2.2",
"guzzlehttp/guzzle": "^7.0",
"mockery/mockery": "^1.6.12",
"phpunit/phpunit": "^10.5 || ^11.0 || ^12.0 || ^13.0",
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0",
"symfony/config": "^5.3 || ^6.0 || ^7.0 || ^8.0",
"symfony/dependency-injection": "^5.0 || ^6.0 || ^7.0 || ^8.0",
"symfony/framework-bundle": "^5.0 || ^6.0 || ^7.0 || ^8.0",
"symfony/http-kernel": "^5.0 || ^6.0 || ^7.0 || ^8.0",
"symfony/options-resolver": "^5.4 || ^6.0 || ^7.0 || ^8.0",
"symfony/yaml": "^5.0 || ^6.0 || ^7.0 || ^8.0",
"symplify/easy-coding-standard": "^12",
"vimeo/psalm": "^5.26 || ^6.14.3"
},
"license": "MIT",
"authors": [
{
"name": "Aaron Scherer",
"email": "aequasi@gmail.com"
}
],
"autoload": {
"psr-4": {
"Secretary\\": "src/Core",
"Secretary\\Adapter\\": "src/Adapter",
"Secretary\\Bundle\\": "src/Bundle"
},
"exclude-from-classmap": [
"**/Tests/"
]
},
"config": {
"preferred-install": {
"*": "dist"
},
"sort-packages": true
},
"scripts": {
"ecs": "ecs check",
"ecs:fix": "ecs check --fix",
"psalm": "psalm --show-info"
}
"name": "secretary/php",
"description": "Monorepo for Secretary's PHP implementation",
"type": "library",
"require-dev": {
"php": "^8.2",
"ext-json": "*",
"aws/aws-sdk-php": "^3.91",
"google/cloud-secret-manager": "^2.2",
"guzzlehttp/guzzle": "^7.0",
"mockery/mockery": "^1.6.12",
"phpunit/phpunit": "^10.5 || ^11.0 || ^12.0 || ^13.0",
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0",
"symfony/config": "^5.3 || ^6.0 || ^7.0 || ^8.0",
"symfony/dependency-injection": "^5.0 || ^6.0 || ^7.0 || ^8.0",
"symfony/framework-bundle": "^5.0 || ^6.0 || ^7.0 || ^8.0",
"symfony/http-kernel": "^5.0 || ^6.0 || ^7.0 || ^8.0",
"symfony/options-resolver": "^5.4 || ^6.0 || ^7.0 || ^8.0",
"symfony/yaml": "^5.0 || ^6.0 || ^7.0 || ^8.0",
"symplify/easy-coding-standard": "^12",
"vimeo/psalm": "^5.26 || ^6.14.3"
},
"license": "MIT",
"authors": [
{
"name": "Aaron Scherer",
"email": "aequasi@gmail.com"
}
],
"autoload": {
"psr-4": {
"Secretary\\": "src/Core",
"Secretary\\Adapter\\": "src/Adapter",
"Secretary\\Bundle\\": "src/Bundle"
},
"exclude-from-classmap": [
"**/Tests/"
]
},
"config": {
"preferred-install": {
"*": "dist"
},
"sort-packages": true
},
"scripts": {
"ecs": "ecs check",
"ecs:fix": "ecs check --fix",
"psalm": "psalm --show-info"
}
}
4 changes: 2 additions & 2 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
<directory name="src/Core/Tests" />
<file name="src/Bundle/SecretaryBundle/Test.php" />
<directory name="**/vendor" />
<directory name="**/Tests" />
</ignoreFiles>
</projectFiles>

Expand Down
1 change: 1 addition & 0 deletions src/Adapter/AWS/SecretsManager/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Tests export-ignore
202 changes: 202 additions & 0 deletions src/Adapter/AWS/SecretsManager/Tests/AWSSecretsManagerAdapterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
<?php

declare(strict_types=1);

/*
* @author Aaron Scherer <aequasi@gmail.com>
* @date 2019
* @license https://opensource.org/licenses/MIT
*/

namespace Secretary\Tests;

use Aws\CommandInterface;
use Aws\Result;
use Aws\SecretsManager\Exception\SecretsManagerException;
use Aws\SecretsManager\SecretsManagerClient;
use Mockery\MockInterface;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;
use Secretary\Adapter\AWS\SecretsManager\AWSSecretsManagerAdapter;
use Secretary\Exception\SecretNotFoundException;
use Secretary\Secret;

#[CoversClass(AWSSecretsManagerAdapter::class)]
class AWSSecretsManagerAdapterTest extends TestCase
{
private AWSSecretsManagerAdapter $adapter;

private SecretsManagerClient|MockInterface $client;

protected function setUp(): void
{
parent::setUp();

$this->client = \Mockery::mock(SecretsManagerClient::class);
$this->adapter = new AWSSecretsManagerAdapter([]);

$reflection = new \ReflectionProperty(AWSSecretsManagerAdapter::class, 'client');
$reflection->setValue($this->adapter, $this->client);
}

protected function tearDown(): void
{
\Mockery::close();
parent::tearDown();
}

public function testGetSecretWithStringValue(): void
{
$result = new Result(['SecretString' => 'my-secret-value']);

$this->client
->shouldReceive('getSecretValue')
->with(\Mockery::on(fn (array $opts) => $opts['SecretId'] === 'my/key'))
->once()
->andReturn($result);

$secret = $this->adapter->getSecret('my/key');

$this->assertInstanceOf(Secret::class, $secret);
$this->assertEquals('my/key', $secret->getKey());
$this->assertEquals('my-secret-value', $secret->getValue());
}

public function testGetSecretWithJsonValue(): void
{
$jsonData = ['username' => 'admin', 'password' => 'secret123'];
$result = new Result(['SecretString' => json_encode($jsonData)]);

$this->client
->shouldReceive('getSecretValue')
->with(\Mockery::on(fn (array $opts) => $opts['SecretId'] === 'db/credentials'))
->once()
->andReturn($result);

$secret = $this->adapter->getSecret('db/credentials');

$this->assertInstanceOf(Secret::class, $secret);
$this->assertEquals('db/credentials', $secret->getKey());
$this->assertEquals($jsonData, $secret->getValue());
}

public function testGetSecretThrowsSecretNotFoundException(): void
{
$this->expectException(SecretNotFoundException::class);

$command = \Mockery::mock(CommandInterface::class);
$exception = new SecretsManagerException(
'Error',
$command,
['message' => "Secrets Manager can\u{2019}t find the specified secret"]
);

$this->client
->shouldReceive('getSecretValue')
->once()
->andThrow($exception);

$this->adapter->getSecret('nonexistent/key');
}

public function testGetSecretRethrowsOtherExceptions(): void
{
$this->expectException(SecretsManagerException::class);

$command = \Mockery::mock(CommandInterface::class);
$exception = new SecretsManagerException(
'Access denied',
$command,
['message' => 'User is not authorized']
);

$this->client
->shouldReceive('getSecretValue')
->once()
->andThrow($exception);

$this->adapter->getSecret('forbidden/key');
}

public function testPutSecretUpdatesExisting(): void
{
$secret = new Secret('my/key', 'my-value');

$this->client
->shouldReceive('updateSecret')
->with(\Mockery::on(function (array $opts) {
return $opts['SecretId'] === 'my/key'
&& $opts['SecretString'] === 'my-value';
}))
->once();

$result = $this->adapter->putSecret($secret);

$this->assertSame($secret, $result);
}

public function testPutSecretCreatesWhenUpdateFails(): void
{
$secret = new Secret('new/key', 'new-value');

$this->client
->shouldReceive('updateSecret')
->once()
->andThrow(new \Exception('Secret not found'));

$this->client
->shouldReceive('createSecret')
->with(\Mockery::on(function (array $opts) {
return $opts['Name'] === 'new/key'
&& $opts['SecretString'] === 'new-value';
}))
->once();

$result = $this->adapter->putSecret($secret);

$this->assertSame($secret, $result);
}

public function testPutSecretWithArrayValue(): void
{
$value = ['user' => 'admin', 'pass' => 'secret'];
$secret = new Secret('my/key', $value);

$this->client
->shouldReceive('updateSecret')
->with(\Mockery::on(function (array $opts) use ($value) {
return $opts['SecretString'] === json_encode($value);
}))
->once();

$result = $this->adapter->putSecret($secret);

$this->assertSame($secret, $result);
}

public function testDeleteSecretByKey(): void
{
$this->client
->shouldReceive('deleteSecret')
->with(\Mockery::on(fn (array $opts) => $opts['SecretId'] === 'my/key'))
->once();

$this->adapter->deleteSecretByKey('my/key');

$this->assertTrue(true);
}

public function testDeleteSecret(): void
{
$secret = new Secret('my/key', 'value');

$this->client
->shouldReceive('deleteSecret')
->with(\Mockery::on(fn (array $opts) => $opts['SecretId'] === 'my/key'))
->once();

$this->adapter->deleteSecret($secret);

$this->assertTrue(true);
}
}
36 changes: 15 additions & 21 deletions src/Adapter/AWS/SecretsManager/composer.json
Original file line number Diff line number Diff line change
@@ -1,39 +1,33 @@
{
"name": "secretary/aws-secrets-manager-adapter",
"description": "AWS Secrets Manager Adapter for Secretary",
"type": "library",
"license": "MIT",
"keywords": [
"name": "secretary/aws-secrets-manager-adapter",
"description": "AWS Secrets Manager Adapter for Secretary",
"type": "library",
"license": "MIT",
"keywords": [
"secrets",
"aws",
"aws secrets manager",
"secretary"
],
"authors": [
"authors": [
{
"name": "Aaron Scherer",
"name": "Aaron Scherer",
"email": "aequasi@gmail.com"
}
],
"minimum-stability": "stable",
"require": {
"require": {
"php": "^8.2",
"ext-json": "*",
"ext-json": "*",
"aws/aws-sdk-php": "^3.0",
"secretary/core": "self.version"
"secretary/core": "self.version"
},
"require-dev": {
"phpunit/phpunit": "^10.5 || ^11.0",
"mockery/mockery": "^1.6.12"
},
"autoload": {
"autoload": {
"psr-4": {
"Secretary\\Adapter\\AWS\\SecretsManager\\": ""
}
},
"autoload-dev": {
"psr-4": {
"Secretary\\Adapter\\AWS\\SecretsManager\\Tests\\": "Tests/"
}
},
"exclude-from-classmap": [
"/Tests/"
]
}
}
9 changes: 0 additions & 9 deletions src/Adapter/Cache/PSR16Cache/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,9 @@
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0",
"secretary/core": "self.version"
},
"require-dev": {
"phpunit/phpunit": "^10.5 || ^11.0",
"mockery/mockery": "^1.6.12"
},
"autoload": {
"psr-4": {
"Secretary\\Adapter\\Cache\\PSR16Cache\\": ""
}
},
"autoload-dev": {
"psr-4": {
"Secretary\\Adapter\\Cache\\PSR16Cache\\Tests\\": "tests/"
}
}
}
Loading