├── src
├── Bundle
│ └── SecretaryBundle
│ │ ├── .gitignore
│ │ ├── SecretaryBundle.php
│ │ ├── services.yaml
│ │ ├── LICENSE
│ │ ├── composer.json
│ │ ├── README.md
│ │ ├── Test.php
│ │ ├── EnvVar
│ │ └── EnvVarProcessor.php
│ │ └── DependencyInjection
│ │ ├── Configuration.php
│ │ └── SecretaryExtension.php
├── Core
│ ├── Exception
│ │ ├── ValueNotSupportedException.php
│ │ └── SecretNotFoundException.php
│ ├── Adapter
│ │ ├── AbstractAdapter.php
│ │ └── AdapterInterface.php
│ ├── LICENSE
│ ├── Helper
│ │ └── ArrayHelper.php
│ ├── composer.json
│ ├── Manager.php
│ ├── Secret.php
│ ├── Tests
│ │ └── ManagerTest.php
│ └── README.md
└── Adapter
│ ├── AWS
│ └── SecretsManager
│ │ ├── README.md
│ │ ├── LICENSE
│ │ ├── composer.json
│ │ └── AWSSecretsManagerAdapter.php
│ ├── Local
│ └── JSONFile
│ │ ├── README.md
│ │ ├── composer.json
│ │ ├── LICENSE
│ │ └── LocalJSONFileAdapter.php
│ ├── Chain
│ ├── composer.json
│ ├── LICENSE
│ └── ChainAdapter.php
│ ├── Cache
│ ├── PSR16Cache
│ │ ├── LICENSE
│ │ ├── composer.json
│ │ └── PSR16CacheAdapter.php
│ └── PSR6Cache
│ │ ├── LICENSE
│ │ ├── composer.json
│ │ └── PSR6CacheAdapter.php
│ └── Hashicorp
│ └── Vault
│ ├── LICENSE
│ ├── composer.json
│ ├── Client
│ ├── Middleware
│ │ └── AppRoleAuthenticator.php
│ └── Client.php
│ └── HashicorpVaultAdapter.php
├── .gitignore
├── .releaserc.json
├── README.md
├── psalm.xml
├── .github
└── workflows
│ ├── release.yml
│ └── main.yml
├── examples
├── HashicorpVaultAdapter.php
├── PSR6CacheAdapter.php
└── AWSSecretsManagerAdapter.php
├── LICENSE
├── phpunit.xml.dist
├── composer.json
├── ecs.php
├── CHANGELOG.md
└── .editorconfig
/src/Bundle/SecretaryBundle/.gitignore:
--------------------------------------------------------------------------------
1 | composer.lock
2 | vendor
3 | .idea
4 | .vscode
5 | var
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | composer.lock
2 | vendor
3 | .idea
4 | .vscode
5 | .phpunit.result.cache
6 | phpunit.xml
7 | .ecs_cache
8 |
--------------------------------------------------------------------------------
/.releaserc.json:
--------------------------------------------------------------------------------
1 | {
2 | "tagFormat": "${version}",
3 | "plugins": [
4 | "@semantic-release/commit-analyzer",
5 | "@semantic-release/release-notes-generator",
6 | "@semantic-release/github",
7 | "@semantic-release/changelog",
8 | "@semantic-release/git"
9 | ]
10 | }
--------------------------------------------------------------------------------
/src/Bundle/SecretaryBundle/SecretaryBundle.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary\Bundle\SecretaryBundle;
12 |
13 | use Symfony\Component\HttpKernel\Bundle\Bundle;
14 |
15 | /**
16 | * Class SecretaryBundle.
17 | *
18 | * @package Secretary\Bundle\SecretaryBundle
19 | */
20 | class SecretaryBundle extends Bundle
21 | {
22 | }
23 |
--------------------------------------------------------------------------------
/src/Core/Exception/ValueNotSupportedException.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary\Exception;
12 |
13 | class ValueNotSupportedException extends \Exception
14 | {
15 | public function __construct(string $key)
16 | {
17 | parent::__construct('This adapter doesn\'t support storing the value passed for: "'.$key.'"');
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Core/Exception/SecretNotFoundException.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary\Exception;
12 |
13 | class SecretNotFoundException extends \Exception
14 | {
15 | public function __construct(string $key, ?\Exception $childException = null)
16 | {
17 | parent::__construct('No secret was found with the key: "'.$key.'"', 404, $childException);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Secretary - Secrets Manager for PHP
2 | [](https://github.com/secretary/php-core/blob/master/LICENSE)
3 | ---
4 |
5 | Secrets are an important aspect of most applications you can build. How you store them, and keep them "secret" is a challenge.
6 | Luckily, there are tools you can use to keep them all safe.
7 |
8 | Secretary is a tool to integrate your PHP application with these tools.
9 |
10 | To get started, view the [documentation](https://github.com/secretary/php/tree/master/src/Core)
11 |
--------------------------------------------------------------------------------
/psalm.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/Adapter/AWS/SecretsManager/README.md:
--------------------------------------------------------------------------------
1 | # Secretary - AWS Secrets Manager Adapter
2 |
3 | AWS Secrets Manager Adapter for [Secretary](https://github.com/secretary/php)
4 |
5 | ## Table of Contents
6 |
7 | 1. [Installation](#installation)
8 | 2. [Options](#options)
9 |
10 | ### Installation
11 |
12 | ```bash
13 | $ composer require secretary/core secretary/aws-secrets-manager-adapter
14 | ```
15 |
16 | ### Options
17 |
18 | Options passed to this adapter can be found [on the AWS PHP SDK docs](https://docs.aws.amazon.com/aws-sdk-php/v3/api/class-Aws.AwsClient.html#___construct).
19 |
20 |
--------------------------------------------------------------------------------
/src/Adapter/Local/JSONFile/README.md:
--------------------------------------------------------------------------------
1 | # Secretary - JSON File Adapter
2 |
3 | JSON File Adapter for [Secretary](https://github.com/secretary/php)
4 |
5 | ## Table of Contents
6 |
7 | 1. [Installation](#installation)
8 | 2. [Secret Structure](#secrets-file-structure)
9 |
10 | ### Installation
11 |
12 | ```bash
13 | $ composer require secretary/core secretary/local-json-file-adapter
14 | ```
15 |
16 | ### Secrets File Structure
17 |
18 | ```json
19 | [
20 | {
21 | "key": "my-secret-key",
22 | "value": "some secret"
23 | },
24 | {
25 | "key": "some-other-secret",
26 | "value": {
27 | "a": "b"
28 | },
29 | "metadata": {"foo": "bar"}
30 | }
31 | ]
32 | ```
33 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches: [master]
6 |
7 | jobs:
8 | release:
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - name: 'Checkout'
13 | uses: 'actions/checkout@v4'
14 |
15 | - name: 'Setup Node.js'
16 | uses: 'actions/setup-node@v4'
17 | with:
18 | node-version: 20.x
19 |
20 | - name: 'Install dependencies'
21 | run: npm i -g semantic-release@22 @semantic-release/changelog @semantic-release/git @semantic-release/changelog @semantic-release/commit-analyzer @semantic-release/release-notes-generator
22 |
23 | - name: 'Release'
24 | env:
25 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
26 | run: npx semantic-release
27 |
--------------------------------------------------------------------------------
/examples/HashicorpVaultAdapter.php:
--------------------------------------------------------------------------------
1 |
6 | * @date 2019
7 | * @license http://opensource.org/licenses/MIT
8 | */
9 |
10 |
11 | require_once __DIR__.'/vendor/autoload.php';
12 | use Secretary\Adapter\Hashicorp\Vault\HashicorpVaultAdapter;
13 |
14 | $fooSecret = new \Secretary\Secret('foo', 'bar');
15 | $bazSecret = new \Secretary\Secret('baz', ['foo' => 'foobar']);
16 |
17 | $manager = new \Secretary\Manager(new HashicorpVaultAdapter());
18 |
19 | try {
20 | $manager->putSecret($fooSecret);
21 | } catch (\Exception $e) {
22 | // Throws an exception because hashicopr vault requires key/value secrets
23 | }
24 | $manager->putSecret($bazSecret);
25 |
26 | var_dump(
27 | $manager->getSecret('baz'),
28 | $manager->getSecret('baz')['foo']
29 | );
30 |
31 | $manager->deleteSecret($bazSecret);
--------------------------------------------------------------------------------
/src/Core/Adapter/AbstractAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary\Adapter;
12 |
13 | use Symfony\Component\OptionsResolver\OptionsResolver;
14 |
15 | /**
16 | * Class AbstractAdapter.
17 | *
18 | * @package Secretary\Adapter
19 | */
20 | abstract class AbstractAdapter implements AdapterInterface
21 | {
22 | public function configureSharedOptions(OptionsResolver $resolver): void
23 | {
24 | }
25 |
26 | public function configureGetSecretOptions(OptionsResolver $resolver): void
27 | {
28 | }
29 |
30 | public function configurePutSecretOptions(OptionsResolver $resolver): void
31 | {
32 | }
33 |
34 | public function configureDeleteSecretOptions(OptionsResolver $resolver): void
35 | {
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/examples/PSR6CacheAdapter.php:
--------------------------------------------------------------------------------
1 |
6 | * @date 2019
7 | * @license http://opensource.org/licenses/MIT
8 | */
9 |
10 | use Cache\Adapter\Apc\ApcCachePool;
11 | use Secretary\Adapter\Cache\PSR6Cache\ChainAdapter;
12 | use Secretary\Adapter\Hashicorp\Vault\HashicorpVaultAdapter;
13 |
14 | require_once __DIR__.'/vendor/autoload.php';
15 |
16 | $manager = new \Secretary\Manager(new ChainAdapter(new HashicorpVaultAdapter(), new ApcCachePool()));
17 |
18 | $bazSecret = new \Secretary\Secret('baz', ['foo' => 'foobar']);
19 |
20 | $manager->putSecret($bazSecret);
21 |
22 | var_dump(
23 | $manager->getSecret('baz', ['ttl' => 1000 * 60]),
24 | $manager->getSecret('baz')['foo'] // Pulls from cache!
25 | );
26 |
27 | $manager->deleteSecret($bazSecret);
28 |
29 |
30 | var_dump($manager->getSecret('baz')); // 404, delete cleared the cache
--------------------------------------------------------------------------------
/src/Bundle/SecretaryBundle/services.yaml:
--------------------------------------------------------------------------------
1 | parameters:
2 | 'kernel.secret': 'foo'
3 | foo: '%env(secret:default:SECRET_PATH:bar)%'
4 |
5 | #services:
6 | # Symfony\Component\Cache\Adapter\ApcuAdapter: ~
7 |
8 | secretary:
9 | adapters:
10 | aws:
11 | adapter: Secretary\Adapter\AWS\SecretsManager\AWSSecretsManagerAdapter
12 | config:
13 | region: 'us-east-1'
14 | version: '2017-10-17'
15 | cache:
16 | enabled: false
17 | type: psr6
18 | service_id: Symfony\Component\Cache\Adapter\ApcuAdapter
19 | default:
20 | adapter: Secretary\Adapter\Chain\ChainAdapter
21 | config:
22 | - '@secretary.adapter.aws'
23 | cache:
24 | enabled: false
25 | type: psr6
26 | service_id: Symfony\Component\Cache\Adapter\ApcuAdapter
--------------------------------------------------------------------------------
/src/Adapter/Chain/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "secretary/chain-adapter",
3 | "description": "Chain Adapter for Secretary",
4 | "type": "library",
5 | "license": "MIT",
6 | "keywords": [
7 | "secrets",
8 | "chain",
9 | "secretary"
10 | ],
11 | "authors": [
12 | {
13 | "name": "Aaron Scherer",
14 | "email": "aequasi@gmail.com"
15 | }
16 | ],
17 | "minimum-stability": "stable",
18 | "require": {
19 | "php": "^8.0",
20 | "secretary/core": "self.version"
21 | },
22 | "require-dev": {
23 | "phpunit/phpunit": "^9.0 || ^10.0",
24 | "mockery/mockery": "^1.4"
25 | },
26 | "autoload": {
27 | "psr-4": {
28 | "Secretary\\Adapter\\Chain\\": ""
29 | }
30 | },
31 | "autoload-dev": {
32 | "psr-4": {
33 | "Secretary\\Adapter\\Chain\\Tests\\": "Tests/"
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/examples/AWSSecretsManagerAdapter.php:
--------------------------------------------------------------------------------
1 |
6 | * @date 2019
7 | * @license http://opensource.org/licenses/MIT
8 | */
9 |
10 |
11 | require_once __DIR__.'/../vendor/autoload.php';
12 |
13 | use Secretary\Adapter\AWS\SecretsManager\LocalJSONFileAdapter;
14 |
15 | $manager = new \Secretary\Manager(
16 | new LocalJSONFileAdapter(
17 | [
18 | 'region' => 'us-east-1',
19 | 'version' => '2017-10-17',
20 | ]
21 | )
22 | );
23 |
24 | $fooSecret = new \Secretary\Secret('foo', 'bar');
25 | $bazSecret = new \Secretary\Secret('baz', ['foo' => 'foobar']);
26 |
27 | $manager->putSecret($fooSecret);
28 | $manager->putSecret($bazSecret);
29 |
30 | var_dump(
31 | $manager->getSecret('foo'),
32 | $manager->getSecret('baz'),
33 | $manager->getSecret('baz')['foo']
34 | );
35 |
36 | $manager->deleteSecret($fooSecret, ['ForceDeleteWithoutRecovery' => true]);
37 | $manager->deleteSecret($bazSecret, ['ForceDeleteWithoutRecovery' => true]);
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Aaron Scherer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/src/Adapter/Local/JSONFile/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "secretary/local-json-file-adapter",
3 | "description": "JSON File Adapter for Secretary",
4 | "type": "library",
5 | "license": "MIT",
6 | "keywords": [
7 | "secrets",
8 | "json",
9 | "secretary"
10 | ],
11 | "authors": [
12 | {
13 | "name": "Aaron Scherer",
14 | "email": "aequasi@gmail.com"
15 | }
16 | ],
17 | "minimum-stability": "stable",
18 | "require": {
19 | "php": "^8.0",
20 | "ext-json": "*",
21 | "secretary/core": "self.version"
22 | },
23 | "require-dev": {
24 | "mockery/mockery": "^1.4",
25 | "phpunit/phpunit": "^9.0 || ^10.0"
26 | },
27 | "autoload": {
28 | "psr-4": {
29 | "Secretary\\Adapter\\Local\\JSONFile\\": ""
30 | }
31 | },
32 | "autoload-dev": {
33 | "psr-4": {
34 | "Secretary\\Adapter\\Local\\JSONFile\\Tests\\": "Tests/"
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Core/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Aaron Scherer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/src/Adapter/Chain/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Aaron Scherer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/src/Adapter/AWS/SecretsManager/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Aaron Scherer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/src/Adapter/Cache/PSR16Cache/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Aaron Scherer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/src/Adapter/Cache/PSR6Cache/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Aaron Scherer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/src/Adapter/Hashicorp/Vault/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Aaron Scherer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/src/Adapter/Local/JSONFile/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Aaron Scherer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/src/Bundle/SecretaryBundle/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Aaron Scherer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/src/Adapter/Cache/PSR6Cache/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "secretary/psr-6-cache-adapter",
3 | "description": "PSR-6 Cache Adapter for Secretary",
4 | "type": "library",
5 | "license": "MIT",
6 | "keywords": [
7 | "secrets",
8 | "cache",
9 | "psr-6",
10 | "secretary"
11 | ],
12 | "authors": [
13 | {
14 | "name": "Aaron Scherer",
15 | "email": "aequasi@gmail.com"
16 | }
17 | ],
18 | "minimum-stability": "stable",
19 | "require": {
20 | "php": "^8.0",
21 | "psr/cache": "^1.0 || ^2.0 || ^3.0",
22 | "secretary/core": "self.version"
23 | },
24 | "require-dev": {
25 | "phpunit/phpunit": "^9.0 || ^10.0",
26 | "mockery/mockery": "^1.4"
27 | },
28 | "autoload": {
29 | "psr-4": {
30 | "Secretary\\Adapter\\Cache\\PSR6Cache\\": ""
31 | }
32 | },
33 | "autoload-dev": {
34 | "psr-4": {
35 | "Secretary\\Adapter\\Cache\\PSR6Cache\\Tests\\": "Tests/"
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Adapter/Cache/PSR16Cache/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "secretary/psr-16-cache-adapter",
3 | "description": "PSR-16 Cache Adapter for Secretary",
4 | "type": "library",
5 | "license": "MIT",
6 | "keywords": [
7 | "secrets",
8 | "cache",
9 | "psr-16",
10 | "secretary"
11 | ],
12 | "authors": [
13 | {
14 | "name": "Aaron Scherer",
15 | "email": "aequasi@gmail.com"
16 | }
17 | ],
18 | "minimum-stability": "stable",
19 | "require": {
20 | "php": "^8.0",
21 | "psr/simple-cache": "^1.0 || ^2.0 || ^3.0",
22 | "secretary/core": "self.version"
23 | },
24 | "require-dev": {
25 | "phpunit/phpunit": "^9.0 || ^10.0",
26 | "mockery/mockery": "^1.4"
27 | },
28 | "autoload": {
29 | "psr-4": {
30 | "Secretary\\Adapter\\Cache\\PSR16Cache\\": ""
31 | }
32 | },
33 | "autoload-dev": {
34 | "psr-4": {
35 | "Secretary\\Adapter\\Cache\\PSR16Cache\\Tests\\": "tests/"
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Adapter/Hashicorp/Vault/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "secretary/hashicorp-vault-adapter",
3 | "description": "Hashicorp Vault adapter for Secretary",
4 | "type": "library",
5 | "license": "MIT",
6 | "keywords": [
7 | "secrets",
8 | "vault",
9 | "secretary"
10 | ],
11 | "authors": [
12 | {
13 | "name": "Aaron Scherer",
14 | "email": "aequasi@gmail.com"
15 | }
16 | ],
17 | "minimum-stability": "stable",
18 | "require": {
19 | "php": "^8.0",
20 | "ext-json": "*",
21 | "guzzlehttp/guzzle": "^7.0",
22 | "symfony/options-resolver": "^5.4 || ^6.0 || ^7.0"
23 | },
24 | "require-dev": {
25 | "phpunit/phpunit": "^9.0 || ^10.0",
26 | "mockery/mockery": "^1.4"
27 | },
28 | "autoload": {
29 | "psr-4": {
30 | "Secretary\\Adapter\\Hashicorp\\Vault\\": ""
31 | }
32 | },
33 | "autoload-dev": {
34 | "psr-4": {
35 | "Secretary\\Adapter\\Hashicorp\\Vault\\Tests\\": "Tests/"
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Adapter/AWS/SecretsManager/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "secretary/aws-secrets-manager-adapter",
3 | "description": "AWS Secrets Manager Adapter for Secretary",
4 | "type": "library",
5 | "license": "MIT",
6 | "keywords": [
7 | "secrets",
8 | "aws",
9 | "aws secrets manager",
10 | "secretary"
11 | ],
12 | "authors": [
13 | {
14 | "name": "Aaron Scherer",
15 | "email": "aequasi@gmail.com"
16 | }
17 | ],
18 | "minimum-stability": "stable",
19 | "require": {
20 | "php": "^8.0",
21 | "ext-json": "*",
22 | "aws/aws-sdk-php": "^3.0",
23 | "secretary/core": "self.version"
24 | },
25 | "require-dev": {
26 | "phpunit/phpunit": "^9.0 || ^10.0",
27 | "mockery/mockery": "^1.4"
28 | },
29 | "autoload": {
30 | "psr-4": {
31 | "Secretary\\Adapter\\AWS\\SecretsManager\\": ""
32 | }
33 | },
34 | "autoload-dev": {
35 | "psr-4": {
36 | "Secretary\\Adapter\\AWS\\SecretsManager\\Tests\\": "Tests/"
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | ./src/Core/Tests
17 |
18 |
19 | ./src/Adapter/**/Tests
20 |
21 |
22 | ./src/Bundle/**/Tests
23 |
24 |
25 |
26 |
27 |
28 | src
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/Core/Helper/ArrayHelper.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary\Helper;
12 |
13 | /**
14 | * @package Secretary\Helper
15 | */
16 | abstract class ArrayHelper
17 | {
18 | /**
19 | * @param string ...$keys
20 | */
21 | public static function without(array $array, ...$keys): array
22 | {
23 | $newArray = $array;
24 | foreach ($keys as $key) {
25 | if (!empty($newArray[$key])) {
26 | unset($newArray[$key]);
27 | }
28 | }
29 |
30 | return $newArray;
31 | }
32 |
33 | /**
34 | * Remove elements from an array based on a list of keys and return a new array with the removed elements.
35 | *
36 | * @param string ...$keys
37 | */
38 | public static function remove(array $array, ...$keys): array
39 | {
40 | $newArray = [];
41 |
42 | foreach ($keys as $key) {
43 | $newArray[$key] = $array[$key] ?? null;
44 |
45 | if (!empty($array[$key])) {
46 | unset($array[$key]);
47 | }
48 | }
49 |
50 | return $newArray;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Core/Adapter/AdapterInterface.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary\Adapter;
12 |
13 | use Secretary\Exception\SecretNotFoundException;
14 | use Secretary\Secret;
15 | use Symfony\Component\OptionsResolver\OptionsResolver;
16 |
17 | /**
18 | * Interface AdapterInterface.
19 | *
20 | * @package Secretary\Adapter
21 | */
22 | interface AdapterInterface
23 | {
24 | /**
25 | * Get a secret by a key.
26 | *
27 | * @throws SecretNotFoundException
28 | */
29 | public function getSecret(string $key, ?array $options = []): Secret;
30 |
31 | /**
32 | * Add \ Update a secret by a key.
33 | */
34 | public function putSecret(Secret $secret, ?array $options = []): Secret;
35 |
36 | /**
37 | * Delete a secret by a key.
38 | */
39 | public function deleteSecret(Secret $secret, ?array $options = []): void;
40 |
41 | public function configureSharedOptions(OptionsResolver $resolver): void;
42 |
43 | public function configureGetSecretOptions(OptionsResolver $resolver): void;
44 |
45 | public function configurePutSecretOptions(OptionsResolver $resolver): void;
46 |
47 | public function configureDeleteSecretOptions(OptionsResolver $resolver): void;
48 | }
49 |
--------------------------------------------------------------------------------
/src/Bundle/SecretaryBundle/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "secretary/secretary-bundle",
3 | "description": "Secrets Manager Bundle for Symfony",
4 | "type": "symfony-bundle",
5 | "license": "MIT",
6 | "keywords": [
7 | "secrets",
8 | "vault",
9 | "secretsmanager",
10 | "keyvault",
11 | "secretary"
12 | ],
13 | "authors": [
14 | {
15 | "name": "Aaron Scherer",
16 | "email": "aequasi@gmail.com"
17 | }
18 | ],
19 | "prefer-stable": true,
20 | "minimum-stability": "dev",
21 | "require": {
22 | "php": "^8.0",
23 | "secretary/core": "self.version"
24 | },
25 | "require-dev": {
26 | "symfony/config": "^5.3 || ^6.0 || ^7.0",
27 | "symfony/dependency-injection": "^5.0 || ^6.0 || ^7.0",
28 | "symfony/http-kernel": "^5.0 || ^6.0 || ^7.0",
29 | "symfony/framework-bundle": "^5.0 || ^6.0 || ^7.0",
30 | "symfony/yaml": "^5.0 || ^6.0 || ^7.0",
31 | "aws/aws-sdk-php": "^3.91"
32 | },
33 | "autoload": {
34 | "psr-4": {
35 | "Secretary\\Bundle\\SecretaryBundle\\": ""
36 | },
37 | "exclude-from-classmap": [
38 | "/Tests/"
39 | ]
40 | },
41 | "autoload-dev": {
42 | "psr-4": {
43 | "Secretary\\Bundle\\SecretaryBundle\\Tests\\": "tests/"
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "secretary/php",
3 | "description": "Monorepo for Secretary's PHP implementation",
4 | "type": "library",
5 | "require-dev": {
6 | "php": "^8.0",
7 | "ext-json": "*",
8 | "aws/aws-sdk-php": "^3.91",
9 | "guzzlehttp/guzzle": "^7.0",
10 | "mockery/mockery": "^1.4",
11 | "phpunit/phpunit": "^9.0 || ^10.0",
12 | "psr/simple-cache": "^1.0 || ^2.0 || ^3.0",
13 | "symfony/config": "^5.3 || ^6.0 || ^7.0",
14 | "symfony/dependency-injection": "^5.0 || ^6.0 || ^7.0",
15 | "symfony/framework-bundle": "^5.0 || ^6.0 || ^7.0",
16 | "symfony/http-kernel": "^5.0 || ^6.0 || ^7.0",
17 | "symfony/options-resolver": "^5.4 || ^6.0 || ^7.0",
18 | "symfony/yaml": "^5.0 || ^6.0 || ^7.0",
19 | "symplify/easy-coding-standard": "^12",
20 | "vimeo/psalm": "^5.0 || ^6.0"
21 | },
22 | "license": "MIT",
23 | "authors": [
24 | {
25 | "name": "Aaron Scherer",
26 | "email": "aequasi@gmail.com"
27 | }
28 | ],
29 | "autoload": {
30 | "psr-4": {
31 | "Secretary\\": "src/Core",
32 | "Secretary\\Adapter\\": "src/Adapter",
33 | "Secretary\\Bundle\\": "src/Bundle"
34 | },
35 | "exclude-from-classmap": [
36 | "**/Tests/"
37 | ]
38 | },
39 | "config": {
40 | "preferred-install": {
41 | "*": "dist"
42 | },
43 | "sort-packages": true
44 | },
45 | "scripts": {
46 | "ecs": "ecs check",
47 | "ecs:fix": "ecs check --fix",
48 | "psalm": "psalm --show-info"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Core/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "secretary/core",
3 | "description": "Secrets Manager for PHP",
4 | "type": "library",
5 | "license": "MIT",
6 | "keywords": [
7 | "secrets",
8 | "vault",
9 | "secretsmanager",
10 | "keyvault",
11 | "secretary"
12 | ],
13 | "authors": [
14 | {
15 | "name": "Aaron Scherer",
16 | "email": "aequasi@gmail.com"
17 | }
18 | ],
19 | "minimum-stability": "stable",
20 | "require": {
21 | "php": "^8.0",
22 | "symfony/options-resolver": "^5.4 || ^6.0 || ^7.0"
23 | },
24 | "require-dev": {
25 | "phpunit/phpunit": "^9.0",
26 | "mockery/mockery": "^1.4"
27 | },
28 | "suggest": {
29 | "secretary/aws-secrets-manager-adapter": "For reading secrets from AWS Secrets Manager",
30 | "secretary/hashicorp-vault-adapter": "For reading secrets from Hashicorp Vault",
31 | "secretary/psr6-cache-adapter": "For caching secrets using a PSR-6 Cache Interface",
32 | "secretary/psr16-cache-adapter": "For caching secrets using a PSR-16 SimpleCache Interface",
33 | "secretary/secretary-bundle": "For integrating Secretary with the Symfony Framework"
34 | },
35 | "autoload": {
36 | "psr-4": {
37 | "Secretary\\": ""
38 | },
39 | "exclude-from-classmap": [
40 | "/Tests/"
41 | ]
42 | },
43 | "autoload-dev": {
44 | "psr-4": {
45 | "Secretary\\Tests\\": "tests/"
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Bundle/SecretaryBundle/README.md:
--------------------------------------------------------------------------------
1 | # Secretary Bundle - Secrets Manager for Symfony
2 |
3 | ## This Bundle Experimental!
4 |
5 | Secrets are an important aspect of most applications you can build. How you store them, and keep them "secret" is a challenge.
6 | Luckily, there are tools you can use to keep them all safe.
7 |
8 | Secretary is a tool to integrate your PHP application with these tools.
9 |
10 | You can find more information about the underlying library over at [the main docs](https://github.com/secretary/php).
11 |
12 | ### Installation
13 |
14 | ```bash
15 | $ composer require secretary/symfony
16 | ```
17 |
18 | ### Configuration
19 |
20 | ```yaml
21 | # config/packages/secretary.yamlg
22 | services:
23 | Symfony\Component\Cache\Adapter\ApcuAdapter:
24 | arguments: ['secrets', 300000]
25 |
26 | secretary:
27 | adapters:
28 | json:
29 | adapter: Secretary\Adapter\Local\JSONFile\LocalJSONFileAdapter
30 | config:
31 | file: '%kernel.root_dir%/config/secrets.json'
32 | aws:
33 | adapter: Secretary\Adapter\AWS\SecretsManager\AWSSecretsManagerAdapter
34 | config:
35 | region: 'us-east-1'
36 | version: 'latest'
37 | credentials:
38 | key: "%env(API_AWS_ACCESS_KEY_ID)%"
39 | secret: "%env(API_AWS_SECRET_ACCESS_KEY)%"
40 | default: # chain adapter
41 | adapter: Secretary\Adapter\Chain\ChainAdapter
42 | config:
43 | - @secretary.adapter.json
44 | - @secretary.adapter.aws
45 | cache:
46 | enabled: true
47 | type: psr6
48 | service_id: cache.secrets
49 | ```
--------------------------------------------------------------------------------
/src/Bundle/SecretaryBundle/Test.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | require_once __DIR__.'/vendor/autoload.php';
12 |
13 | use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
14 | use Symfony\Component\Config\Loader\LoaderInterface;
15 | use Symfony\Component\DependencyInjection\ContainerBuilder;
16 |
17 | /**
18 | * @author Aaron Scherer
19 | * @date 2019
20 | *
21 | * @license http://opensource.org/licenses/MIT
22 | *
23 | * @internal
24 | * @coversNothing
25 | */
26 | class Test extends \Symfony\Component\HttpKernel\Kernel
27 | {
28 | use MicroKernelTrait;
29 | public const CONFIG_EXTS = '.{php,xml,yaml,yml}';
30 |
31 | /**
32 | * Returns an array of bundles to register.
33 | *
34 | * @return iterable|\Symfony\Component\HttpKernel\Bundle\BundleInterface An iterable of bundle instances
35 | */
36 | public function registerBundles()
37 | {
38 | yield new \Symfony\Bundle\FrameworkBundle\FrameworkBundle();
39 | yield new \Secretary\Bundle\SecretaryBundle\SecretaryBundle();
40 | }
41 |
42 | /**
43 | * Add or import routes into your application.
44 | *
45 | * $routes->import('config/routing.yml');
46 | * $routes->add('/admin', 'App\Controller\AdminController::dashboard', 'admin_dashboard');
47 | *
48 | */
49 | protected function configureRoutes(Symfony\Component\Routing\RouteCollectionBuilder $routes)
50 | {
51 | }
52 |
53 | /**
54 | * {@inheritDc}.
55 | *
56 | * @throws Exception
57 | */
58 | protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader)
59 | {
60 | $loader->load(__DIR__.'/services'.self::CONFIG_EXTS, 'glob');
61 | }
62 | }
63 |
64 | $k = new Test('dev', true);
65 | $k->boot();
66 | var_dump($k->getContainer()->getParameter('foo'));
67 |
--------------------------------------------------------------------------------
/src/Adapter/Hashicorp/Vault/Client/Middleware/AppRoleAuthenticator.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary\Adapter\Hashicorp\Vault\Client\Middleware;
12 |
13 | use GuzzleHttp\Client;
14 | use GuzzleHttp\Utils;
15 | use Psr\Http\Message\RequestInterface;
16 |
17 | /**
18 | * Class AppRoleAuthenticator.
19 | *
20 | * @package Secretary\Adapter\Hashicorp\Vault\Client\Middleware
21 | */
22 | class AppRoleAuthenticator
23 | {
24 | public const authRoute = 'v1/auth/approle/login';
25 |
26 | private Client $client;
27 |
28 | private string $roleId;
29 |
30 | private string $secretId;
31 |
32 | private ?string $token = null;
33 |
34 | private ?int $tokenExpiration = null;
35 |
36 | public function __construct(Client $client, string $roleId, string $secretId)
37 | {
38 | $this->client = $client;
39 | $this->roleId = $roleId;
40 | $this->secretId = $secretId;
41 | }
42 |
43 | public function __invoke(callable $handler): callable
44 | {
45 | return function (RequestInterface $request, array $options) use ($handler) {
46 | if (empty($this->token) || time() > $this->tokenExpiration) {
47 | $this->authenticate();
48 | }
49 |
50 | $request = $request->withHeader('X-Vault-Token', $this->token);
51 |
52 | return $handler($request, $options);
53 | };
54 | }
55 |
56 | private function authenticate(): void
57 | {
58 | $response = $this->client->post(
59 | static::authRoute,
60 | [
61 | 'json' => [
62 | 'role_id' => $this->roleId,
63 | 'secret-id' => $this->secretId,
64 | ],
65 | 'handler' => Utils::chooseHandler(),
66 | ]
67 | );
68 |
69 | $response = json_decode($response->getBody()->getContents(), true);
70 | $this->token = (string) $response['auth']['client_token'];
71 | $this->tokenExpiration = time() + (int) $response['auth']['lease_duration'];
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Adapter/Hashicorp/Vault/HashicorpVaultAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary\Adapter\Hashicorp\Vault;
12 |
13 | use GuzzleHttp\Client as GuzzleClient;
14 | use Secretary\Adapter\AbstractAdapter;
15 | use Secretary\Adapter\Hashicorp\Vault\Client\Client;
16 | use Secretary\Secret;
17 |
18 | /**
19 | * Class HashicorpVaultAdapter.
20 | *
21 | * @package Secretary\Adapter\Hashicorp\Vault
22 | */
23 | class HashicorpVaultAdapter extends AbstractAdapter
24 | {
25 | private Client $client;
26 |
27 | /**
28 | * @throws \Exception
29 | */
30 | public function __construct(array $config = [])
31 | {
32 | if (!class_exists(GuzzleClient::class)) {
33 | throw new \Exception('guzzlehttp/guzzle is required to use the HashicorpVaultAdapter');
34 | }
35 |
36 | $this->client = new Client($config);
37 | }
38 |
39 | /**
40 | * {@inheritdoc}
41 | */
42 | public function getSecret(string $key, ?array $options = []): Secret
43 | {
44 | $response = $this->client->getClient()->get('/v1/secret/'.$key);
45 | $json = json_decode($response->getBody()->getContents(), true);
46 |
47 | return new Secret($key, $json['data']);
48 | }
49 |
50 | /**
51 | * {@inheritdoc}
52 | */
53 | public function putSecret(Secret $secret, ?array $options = []): Secret
54 | {
55 | if (!is_array($secret->getValue())) {
56 | throw new \Exception('Value for this adapter must be a key/value array');
57 | }
58 |
59 | $this->client->getClient()->post('/v1/secret/'.$secret->getKey(), ['json' => $secret->getValue()]);
60 |
61 | return $secret;
62 | }
63 |
64 | /**
65 | * {@inheritdoc}
66 | */
67 | public function deleteSecret(Secret $secret, ?array $options = []): void
68 | {
69 | $this->deleteSecretByKey($secret->getKey(), $options);
70 | }
71 |
72 | public function deleteSecretByKey(string $key, ?array $options = []): void
73 | {
74 | $this->client->getClient()->delete('/v1/secret/'.$key);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Bundle/SecretaryBundle/EnvVar/EnvVarProcessor.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary\Bundle\SecretaryBundle\EnvVar;
12 |
13 | use Secretary\Manager;
14 | use Symfony\Component\DependencyInjection\EnvVarProcessorInterface;
15 |
16 | class EnvVarProcessor implements EnvVarProcessorInterface
17 | {
18 | /**
19 | * @var list
20 | */
21 | private array $managers;
22 |
23 | public function __construct(\Traversable $managers)
24 | {
25 | $this->managers = iterator_to_array($managers);
26 | }
27 |
28 | /**
29 | * @psalm-suppress InvalidArrayOffset
30 | */
31 | public function getEnv(string $prefix, string $name, \Closure $getEnv): mixed
32 | {
33 | $parts = explode(':', $name);
34 |
35 | if (!array_key_exists($parts[0], $this->managers)) {
36 | throw new \InvalidArgumentException(sprintf('%s is not a valid manager name. Available managers: %s', $parts[0], implode(', ', array_keys($this->managers))));
37 | }
38 |
39 | $manager = $this->managers[$parts[0]];
40 | $key = $getEnv($parts[1]);
41 | $value = $manager->getSecret($key)->getValue();
42 |
43 | if (array_key_exists(2, $parts)) {
44 | if (!is_array($value)) {
45 | throw new \InvalidArgumentException(sprintf('Index isn\'t available n %s. Value is a string.', $key));
46 | }
47 |
48 | if (!array_key_exists($parts[2], $value)) {
49 | throw new \InvalidArgumentException(sprintf('%s is not a valid index in that secret. Available indexes: %s', $parts[2], implode(', ', array_keys($value))));
50 | }
51 | $value = $value[$parts[2]];
52 | }
53 |
54 | return $value;
55 | }
56 |
57 | public static function getProvidedTypes(): array
58 | {
59 | return [
60 | 'secretary' => 'bool|int|float|string',
61 | 'secret' => 'bool|int|float|string',
62 | 'secretArray' => 'array',
63 | 'secretaryArray' => 'array',
64 | ];
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Core/Manager.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary;
12 |
13 | use Secretary\Adapter\AdapterInterface;
14 | use Symfony\Component\OptionsResolver\OptionsResolver;
15 |
16 | /**
17 | * Class Manager.
18 | *
19 | * @package Secretary
20 | */
21 | class Manager
22 | {
23 | private AdapterInterface $adapter;
24 |
25 | public function __construct(AdapterInterface $adapter)
26 | {
27 | $this->adapter = $adapter;
28 | }
29 |
30 | /**
31 | * @throws Exception\SecretNotFoundException
32 | */
33 | public function getSecret(string $key, ?array $options = []): Secret
34 | {
35 | $resolver = new OptionsResolver();
36 | $this->adapter->configureSharedOptions($resolver);
37 | $this->adapter->configureGetSecretOptions($resolver);
38 |
39 | return $this->adapter->getSecret($key, $resolver->resolve($options));
40 | }
41 |
42 | public function putSecret(Secret $secret, ?array $options = []): Secret
43 | {
44 | $resolver = new OptionsResolver();
45 | $this->adapter->configureSharedOptions($resolver);
46 | $this->adapter->configurePutSecretOptions($resolver);
47 |
48 | return $this->adapter->putSecret($secret, $resolver->resolve($options));
49 | }
50 |
51 | /**
52 | * @throws Exception\SecretNotFoundException
53 | */
54 | public function deleteSecretByKey(string $key, ?array $options = []): void
55 | {
56 | $secret = $this->getSecret($key, $options);
57 |
58 | $resolver = new OptionsResolver();
59 | $this->adapter->configureSharedOptions($resolver);
60 | $this->adapter->configureDeleteSecretOptions($resolver);
61 |
62 | $this->adapter->deleteSecret($secret, $resolver->resolve($options));
63 | }
64 |
65 | public function deleteSecret(Secret $secret, ?array $options = []): void
66 | {
67 | $resolver = new OptionsResolver();
68 | $this->adapter->configureSharedOptions($resolver);
69 | $this->adapter->configureDeleteSecretOptions($resolver);
70 |
71 | $this->adapter->deleteSecret($secret, $resolver->resolve($options));
72 | }
73 |
74 | public function getAdapter(): AdapterInterface
75 | {
76 | return $this->adapter;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/Bundle/SecretaryBundle/DependencyInjection/Configuration.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary\Bundle\SecretaryBundle\DependencyInjection;
12 |
13 | use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
14 | use Symfony\Component\Config\Definition\Builder\TreeBuilder;
15 | use Symfony\Component\Config\Definition\ConfigurationInterface;
16 |
17 | /**
18 | * @package Secretary\Bundle\SecretaryBundle\DependencyInjection
19 | */
20 | class Configuration implements ConfigurationInterface
21 | {
22 | public function getConfigTreeBuilder(): TreeBuilder
23 | {
24 | $treeBuilder = new TreeBuilder('secretary', 'array');
25 |
26 | /** @var ArrayNodeDefinition $rootNode */
27 | $rootNode = $treeBuilder->getRootNode();
28 |
29 | $rootNode
30 | ->children()
31 | ->append($this->addAdaptersSection())
32 | ->end();
33 |
34 | return $treeBuilder;
35 | }
36 |
37 | /**
38 | * @psalm-suppress UndefinedInterfaceMethod
39 | */
40 | private function addAdaptersSection(): ArrayNodeDefinition
41 | {
42 | $treeBuilder = new TreeBuilder('adapters');
43 |
44 | /** @var ArrayNodeDefinition $node */
45 | $node = $treeBuilder->getRootNode();
46 |
47 | $node
48 | ->useAttributeAsKey('name')
49 | ->arrayPrototype()
50 | ->addDefaultsIfNotSet()
51 | ->children()
52 | ->scalarNode('adapter')
53 | ->isRequired()
54 | ->info('Class name, or service ID of adapter')
55 | ->end()
56 | ->arrayNode('config')
57 | ->ignoreExtraKeys(false)
58 | ->end()
59 | ->arrayNode('cache')
60 | ->canBeEnabled()
61 | ->addDefaultsIfNotSet()
62 | ->children()
63 | ->enumNode('type')
64 | ->values(['psr6', 'psr16'])
65 | ->end()
66 | ->scalarNode('service_id')->end()
67 | ->end()
68 | ->end()
69 | ->end()
70 | ->end();
71 |
72 | return $node;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Core/Secret.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary;
12 |
13 | use Secretary\Exception\ValueNotSupportedException;
14 |
15 | /**
16 | * @implements \ArrayAccess
17 | */
18 | class Secret implements \ArrayAccess
19 | {
20 | private string $key;
21 |
22 | private array|string $value;
23 |
24 | private ?array $metadata;
25 |
26 | public function __construct(string $key, array|string $value, ?array $metadata = null)
27 | {
28 | $this->key = $key;
29 | $this->value = $value;
30 | $this->metadata = $metadata;
31 | }
32 |
33 | public function getKey(): string
34 | {
35 | return $this->key;
36 | }
37 |
38 | /**
39 | * @return array|string
40 | */
41 | public function getValue()
42 | {
43 | return $this->value;
44 | }
45 |
46 | public function getMetadata(): array
47 | {
48 | return $this->metadata ?? [];
49 | }
50 |
51 | /**
52 | */
53 | public function offsetExists($offset): bool
54 | {
55 | return is_array($this->value) && array_key_exists($offset, $this->value);
56 | }
57 |
58 | /**
59 | *
60 | * @throws ValueNotSupportedException
61 | */
62 | public function offsetGet($offset): mixed
63 | {
64 | if (!is_array($this->value)) {
65 | throw new ValueNotSupportedException($this->key);
66 | }
67 |
68 | return $this->value[$offset];
69 | }
70 |
71 | /**
72 | * @throws \Exception
73 | */
74 | public function offsetSet(mixed $offset, mixed $value): void
75 | {
76 | throw new \Exception('Secrets are immutable');
77 | }
78 |
79 | /**
80 | * @throws \Exception
81 | */
82 | public function offsetUnset(mixed $offset): void
83 | {
84 | throw new \Exception('Secrets are immutable');
85 | }
86 |
87 | /**
88 | * Returns a new instance of this secret with the value changed.
89 | *
90 | * @param array|string $value
91 | */
92 | public function withValue($value): self
93 | {
94 | return new self($this->key, $value, $this->metadata);
95 | }
96 |
97 | /**
98 | * Returns a new instance of this secret with the metadata changed.
99 | */
100 | public function withMetadata(array $metadata): self
101 | {
102 | return new self($this->key, $this->value, $metadata);
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/Adapter/Chain/ChainAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary\Adapter\Chain;
12 |
13 | use Secretary\Adapter\AbstractAdapter;
14 | use Secretary\Adapter\AdapterInterface;
15 | use Secretary\Exception\SecretNotFoundException;
16 | use Secretary\Secret;
17 |
18 | /**
19 | * Class ChainAdapter.
20 | *
21 | * @package Secretary\Adapter\Chain
22 | */
23 | final class ChainAdapter extends AbstractAdapter
24 | {
25 | /**
26 | * @var list
27 | */
28 | private array $adapters;
29 |
30 | /**
31 | * @param AdapterInterface[] $adapters
32 | */
33 | public function __construct(array $adapters)
34 | {
35 | $this->adapters = $adapters;
36 | }
37 |
38 | /**
39 | * Note: $options is a 0-indexed array of options. Each index corresponds to the index of the adapters
40 | * {@inheritdoc}
41 | */
42 | public function getSecret(string $key, ?array $options = []): Secret
43 | {
44 | foreach ($this->adapters as $index => $adapter) {
45 | try {
46 | return $adapter->getSecret($key, $options[$index] ?? []);
47 | } catch (SecretNotFoundException $ignored) {
48 | }
49 | }
50 |
51 | throw new SecretNotFoundException($key);
52 | }
53 |
54 | /**
55 | * {@inheritdoc}
56 | */
57 | public function putSecret(Secret $secret, ?array $options = []): Secret
58 | {
59 | foreach ($this->adapters as $index => $adapter) {
60 | $adapter->putSecret($secret, $options[$index] ?? []);
61 | }
62 |
63 | return $secret;
64 | }
65 |
66 | /**
67 | * {@inheritdoc}
68 | */
69 | public function deleteSecret(Secret $secret, ?array $options = []): void
70 | {
71 | foreach ($this->adapters as $index => $adapter) {
72 | $adapter->deleteSecret($secret, $options[$index] ?? []);
73 | }
74 | }
75 |
76 | /**
77 | * {@inheritdoc}
78 | */
79 | public function deleteSecretByKey(string $key, ?array $options = []): void
80 | {
81 | $success = false;
82 | foreach ($this->adapters as $index => $adapter) {
83 | try {
84 | $adapter->deleteSecret($adapter->getSecret($key), $options[$index] ?? []);
85 | $success = true;
86 | } catch (SecretNotFoundException $ignored) {
87 | }
88 | }
89 |
90 | if (!$success) {
91 | throw new SecretNotFoundException($key);
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/Adapter/Cache/PSR16Cache/PSR16CacheAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary\Adapter\Cache\PSR16Cache;
12 |
13 | use Psr\SimpleCache\CacheInterface;
14 | use Secretary\Adapter\AbstractAdapter;
15 | use Secretary\Adapter\AdapterInterface;
16 | use Secretary\Helper\ArrayHelper;
17 | use Secretary\Secret;
18 |
19 | /**
20 | * @package Secretary\Adapter\Cache
21 | */
22 | final class PSR16CacheAdapter extends AbstractAdapter
23 | {
24 | private AdapterInterface $adapter;
25 |
26 | private CacheInterface $cache;
27 |
28 | public function __construct(AdapterInterface $adapter, CacheInterface $cache)
29 | {
30 | $this->adapter = $adapter;
31 | $this->cache = $cache;
32 | }
33 |
34 | /**
35 | * {@inheritdoc}
36 | */
37 | public function getSecret(string $key, ?array $options = []): Secret
38 | {
39 | ['ttl' => $ttl] = ArrayHelper::remove($options, 'ttl');
40 |
41 | if ($this->cache->has(sha1($key))) {
42 | [$value, $metadata] = json_decode($this->cache->get(sha1($key)), true);
43 |
44 | return new Secret($key, $value, $metadata);
45 | }
46 |
47 | $secret = $this->adapter->getSecret($key, $options);
48 | $this->cache->set(sha1($key), json_encode([$secret->getValue(), $secret->getMetadata()]), $ttl);
49 |
50 | return $secret;
51 | }
52 |
53 | /**
54 | * {@inheritdoc}
55 | */
56 | public function putSecret(Secret $secret, ?array $options = []): Secret
57 | {
58 | ['ttl' => $ttl] = ArrayHelper::remove($options, 'ttl');
59 |
60 | $this->adapter->putSecret($secret, $options);
61 |
62 | if ($this->cache->has(sha1($secret->getKey())) || $ttl === 0) {
63 | $this->cache->delete(sha1($secret->getKey()));
64 |
65 | return $secret;
66 | }
67 |
68 | $this->cache->set(sha1($secret->getKey()), json_encode([$secret->getValue(), $secret->getMetadata()]), $ttl);
69 |
70 | return $secret;
71 | }
72 |
73 | /**
74 | * {@inheritdoc}
75 | */
76 | public function deleteSecret(Secret $secret, ?array $options = []): void
77 | {
78 | $this->deleteSecretByKey($secret->getKey(), $options);
79 | }
80 |
81 | /**
82 | * {@inheritdoc}
83 | */
84 | public function deleteSecretByKey(string $key, ?array $options = []): void
85 | {
86 | $this->adapter->deleteSecret($this->adapter->getSecret($key, $options), $options);
87 | $this->cache->delete(sha1($key));
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/Adapter/Cache/PSR6Cache/PSR6CacheAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary\Adapter\Cache\PSR6Cache;
12 |
13 | use Psr\Cache\CacheItemPoolInterface;
14 | use Secretary\Adapter\AbstractAdapter;
15 | use Secretary\Adapter\AdapterInterface;
16 | use Secretary\Helper\ArrayHelper;
17 | use Secretary\Secret;
18 |
19 | /**
20 | * Class PSR6CacheAdapter.
21 | *
22 | * @package Secretary\Adapter\Cache
23 | */
24 | final class PSR6CacheAdapter extends AbstractAdapter
25 | {
26 | private AdapterInterface $adapter;
27 |
28 | private CacheItemPoolInterface $cache;
29 |
30 | public function __construct(AdapterInterface $adapter, CacheItemPoolInterface $cache)
31 | {
32 | $this->adapter = $adapter;
33 | $this->cache = $cache;
34 | }
35 |
36 | /**
37 | * {@inheritdoc}
38 | */
39 | public function getSecret(string $key, ?array $options = []): Secret
40 | {
41 | ['ttl' => $ttl] = ArrayHelper::remove($options, 'ttl');
42 |
43 | $item = $this->cache->getItem(sha1($key));
44 |
45 | if ($item->isHit()) {
46 | [$value, $metadata] = json_decode($item->get(), true);
47 |
48 | return new Secret($key, $value, $metadata);
49 | }
50 |
51 | $secret = $this->adapter->getSecret($key, $options);
52 | $item->set(json_encode([$secret->getValue(), $secret->getMetadata()]));
53 |
54 | if ($ttl !== null) {
55 | $item->expiresAfter($ttl);
56 | }
57 | $this->cache->save($item);
58 |
59 | return $secret;
60 | }
61 |
62 | /**
63 | * {@inheritdoc}
64 | */
65 | public function putSecret(Secret $secret, ?array $options = []): Secret
66 | {
67 | ['ttl' => $ttl] = ArrayHelper::remove($options, 'ttl');
68 |
69 | $this->adapter->putSecret($secret, $options);
70 |
71 | if ($this->cache->hasItem(sha1($secret->getKey())) || $ttl === 0) {
72 | $this->cache->deleteItem(sha1($secret->getKey()));
73 |
74 | return $secret;
75 | }
76 |
77 | $item = $this->cache->getItem(sha1($secret->getKey()));
78 | $item->set(json_encode([$secret->getValue(), $secret->getMetadata()]));
79 |
80 | if (!empty($ttl)) {
81 | $item->expiresAfter($ttl);
82 | }
83 | $this->cache->save($item);
84 |
85 | return $secret;
86 | }
87 |
88 | /**
89 | * {@inheritdoc}
90 | */
91 | public function deleteSecret(Secret $secret, ?array $options = []): void
92 | {
93 | $this->deleteSecretByKey($secret->getKey(), $options);
94 | }
95 |
96 | public function deleteSecretByKey(string $key, ?array $options = []): void
97 | {
98 | $this->adapter->deleteSecret($this->adapter->getSecret($key, $options), $options);
99 |
100 | if ($this->cache->hasItem(sha1($key))) {
101 | $this->cache->deleteItem(sha1($key));
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/Adapter/Hashicorp/Vault/Client/Client.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary\Adapter\Hashicorp\Vault\Client;
12 |
13 | use GuzzleHttp\HandlerStack;
14 | use Secretary\Adapter\Hashicorp\Vault\Client\Middleware\AppRoleAuthenticator;
15 | use Symfony\Component\OptionsResolver\OptionsResolver;
16 |
17 | class Client
18 | {
19 | private \GuzzleHttp\Client $client;
20 |
21 | public function __construct(array $config)
22 | {
23 | $config = $this->validateOptions($config);
24 |
25 | $baseUri = rtrim($config['address'], '/');
26 |
27 | $stack = HandlerStack::create();
28 | $options = ['base_uri' => $baseUri, 'stack' => $stack];
29 |
30 | if (!empty($config['credentials']['token'])) {
31 | $options['headers'] = ['X-Vault-Token' => $config['credentials']['token']];
32 | }
33 |
34 | if (!empty($config['credentials']['appRole'])) {
35 | ['roleId' => $roleId, 'secretId' => $secretId] = $config['credentials']['appRole'];
36 |
37 | if (!empty($roleId) && !empty($secretId)) {
38 | $stack->push(new AppRoleAuthenticator($this->client, $roleId, $secretId));
39 | }
40 | }
41 |
42 | $this->client = new \GuzzleHttp\Client($options);
43 | }
44 |
45 | public function getClient(): \GuzzleHttp\Client
46 | {
47 | return $this->client;
48 | }
49 |
50 | /**
51 | * @todo Add options for SSL cert
52 | */
53 | private function validateOptions(array $config): array
54 | {
55 | $addr = (string) getenv('VAULT_ADDR');
56 | $token = (string) getenv('VAULT_TOKEN');
57 |
58 | $resolver = new OptionsResolver();
59 | $resolver
60 | ->setDefined('address')
61 | ->setAllowedTypes('address', 'string');
62 |
63 | if (!empty($addr)) {
64 | $resolver->setDefault('address', $addr);
65 | }
66 |
67 | $resolver->setDefault(
68 | 'credentials',
69 | function (OptionsResolver $credentials) use ($token) {
70 | $credentials
71 | ->setRequired('token')
72 | ->setAllowedTypes('token', 'string');
73 |
74 | if (!empty($token)) {
75 | $credentials->setDefault('token', $token);
76 | }
77 |
78 | $credentials->setDefault(
79 | 'appRole',
80 | function (OptionsResolver $appRole) {
81 | $appRole
82 | ->setDefault('roleId', '')
83 | ->setRequired('roleId')
84 | ->setAllowedTypes('roleId', 'string');
85 |
86 | $appRole
87 | ->setDefault('secretId', '')
88 | ->setRequired('secretId')
89 | ->setAllowedTypes('secretId', 'string');
90 | }
91 | );
92 | }
93 | );
94 |
95 | return $resolver->resolve($config);
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/ecs.php:
--------------------------------------------------------------------------------
1 | cacheDirectory(__DIR__.'/.ecs_cache');
30 | $ecsConfig->parallel();
31 |
32 | $ecsConfig->paths([
33 | __DIR__.'/src',
34 | ]);
35 |
36 | $ecsConfig->skip([
37 | IsNullFixer::class,
38 | MultilineWhitespaceBeforeSemicolonsFixer::class,
39 | NativeFunctionInvocationFixer::class,
40 | ReturnAssignmentFixer::class,
41 | SingleLineCommentStyleFixer::class,
42 | YodaStyleFixer::class,
43 | PhpdocNoPackageFixer::class,
44 | ]);
45 |
46 | $ecsConfig->sets([
47 | SetList::PSR_12,
48 | ]);
49 |
50 | $ecsConfig->rules([
51 | NoUselessReturnFixer::class,
52 | NoUselessElseFixer::class,
53 | ModernizeStrposFixer::class,
54 | NoUnusedImportsFixer::class,
55 | NoSuperfluousPhpdocTagsFixer::class,
56 | ReturnTypeDeclarationFixer::class,
57 | ]);
58 |
59 | $ecsConfig->ruleWithConfiguration(ConcatSpaceFixer::class, [
60 | 'spacing' => 'none',
61 | ]);
62 |
63 | $ecsConfig->ruleWithConfiguration(IncrementStyleFixer::class, [
64 | 'style' => 'post'
65 | ]);
66 |
67 | $ecsConfig->ruleWithConfiguration(ClassAttributesSeparationFixer::class, [
68 | 'elements' => ['const' => 'only_if_meta', 'method' => 'one', 'property' => 'one', 'trait_import' => 'only_if_meta'],
69 | ]);
70 |
71 |
72 | $ecsConfig->ruleWithConfiguration(BlankLineBeforeStatementFixer::class, [
73 | 'statements' => ['if', 'break', 'continue', 'declare', 'return', 'throw', 'try', 'switch'],
74 | ]);
75 |
76 | $ecsConfig->ruleWithConfiguration(BinaryOperatorSpacesFixer::class, [
77 | 'default' => 'align_single_space_minimal',
78 | 'operators' => [
79 | '|' => 'no_space',
80 | '/' => null,
81 | '*' => null,
82 | '||' => null,
83 | '&&' => null,
84 | ],
85 | ]);
86 |
87 | $header = <<<'EOF'
88 | @author Aaron Scherer
89 | @date 2019
90 | @license https://opensource.org/licenses/MIT
91 | EOF;
92 |
93 | $ecsConfig->ruleWithConfiguration(HeaderCommentFixer::class, [
94 | 'header' => $header
95 | ]);
96 | };
97 |
--------------------------------------------------------------------------------
/src/Bundle/SecretaryBundle/DependencyInjection/SecretaryExtension.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary\Bundle\SecretaryBundle\DependencyInjection;
12 |
13 | use Secretary\Adapter\Cache\PSR16Cache\PSR16CacheAdapter;
14 | use Secretary\Adapter\Cache\PSR6Cache\PSR6CacheAdapter;
15 | use Secretary\Bundle\SecretaryBundle\EnvVar\EnvVarProcessor;
16 | use Secretary\Manager;
17 | use Symfony\Component\DependencyInjection\Alias;
18 | use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
19 | use Symfony\Component\DependencyInjection\ContainerBuilder;
20 | use Symfony\Component\DependencyInjection\Extension\Extension;
21 | use Symfony\Component\DependencyInjection\Reference;
22 |
23 | class SecretaryExtension extends Extension
24 | {
25 | public function load(array $configs, ContainerBuilder $container): void
26 | {
27 | $config = $this->processConfiguration(new Configuration(), $configs);
28 |
29 | $services = [];
30 | $default = isset($config['adapters']['default']) ? 'default' : null;
31 | foreach ($config['adapters'] as $name => $arguments) {
32 | if ($default === null) {
33 | $default = $name;
34 | }
35 |
36 | $arguments['config'] = $this->replaceReferences($arguments['config']);
37 |
38 | if ($container->has($arguments['adapter'])) {
39 | $ref = new Reference($arguments['adapter']);
40 | } else {
41 | $adapter = $container->register('secretary.adapter.'.$name, $arguments['adapter']);
42 | $adapter->addArgument($arguments['config']);
43 | $adapter->setPublic(true);
44 |
45 | $ref = new Reference('secretary.adapter.'.$name);
46 | }
47 |
48 | if ($arguments['cache']['enabled']) {
49 | $adapter = $container->register(
50 | 'secretary.adapter.'.$name.'.cache',
51 | $arguments['cache']['type'] === 'psr6' ? PSR6CacheAdapter::class : PSR16CacheAdapter::class
52 | );
53 | $adapter->addArgument($ref);
54 | $adapter->addArgument(new Reference($arguments['cache']['service_id']));
55 | $adapter->setPublic(true);
56 |
57 | $ref = new Reference('secretary.adapter.'.$name.'.cache');
58 | }
59 |
60 | $def = $container->register('secretary.manager.'.$name, Manager::class);
61 | $def->addArgument($ref);
62 | $def->addTag('secretary.manager');
63 | $def->setPublic(true);
64 |
65 | $services[$name] = new Reference('secretary.manager.'.$name);
66 | }
67 |
68 | if ($default !== null) {
69 | $alias = new Alias('secretary.manager.'.$default, true);
70 |
71 | $container->setAlias('secretary', $alias);
72 | $container->setAlias(Manager::class, $alias);
73 | }
74 |
75 | $container->register('secretary.env_var_processor', EnvVarProcessor::class)
76 | ->addArgument(new IteratorArgument($services))
77 | ->addTag('container.env_var_processor')
78 | ->setPublic(false);
79 | }
80 |
81 | private function replaceReferences($value)
82 | {
83 | if (is_array($value)) {
84 | foreach ($value as $k => $v) {
85 | $value[$k] = $this->replaceReferences($v);
86 | }
87 |
88 | return $value;
89 | }
90 |
91 | if (!is_string($value)) {
92 | return $value;
93 | }
94 |
95 | if (str_starts_with($value, '@')) {
96 | return new Reference(substr($value, 1));
97 | }
98 |
99 | return $value;
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | phpunit:
7 | runs-on: ubuntu-latest
8 |
9 | strategy:
10 | matrix:
11 | php-version: [8.0, 8.1, 8.2, 8.3]
12 | dependency-version: [prefer-lowest, prefer-stable]
13 | steps:
14 | - name: 'Checkout'
15 | uses: 'actions/checkout@v4'
16 |
17 | - name: 'Install PHP'
18 | uses: 'shivammathur/setup-php@v2'
19 | with:
20 | coverage: 'none'
21 | php-version: '${{ matrix.php-version }}'
22 |
23 | - name: 'Get composer cache directory'
24 | run: echo "composer_cache=$(composer config cache-files-dir)" >> $GITHUB_ENV
25 |
26 | - name: 'Cache dependencies'
27 | uses: actions/cache@v3
28 | with:
29 | path: ${{ env.composer_cache }}
30 | key: deps-php-${{ matrix.php-version }}-composer-${{ hashFiles('composer.json') }}-dep-${{ matrix.dependency-version }}
31 |
32 | - name: 'Install dependencies'
33 | run: composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest
34 |
35 | - name: 'PHPUnit Tests'
36 | run: vendor/bin/phpunit
37 |
38 | static-analysis:
39 | runs-on: ubuntu-latest
40 |
41 | strategy:
42 | matrix:
43 | php-version: [8.0, 8.1, 8.2, 8.3]
44 | dependency-version: [ prefer-lowest, prefer-stable ]
45 |
46 | steps:
47 | - name: 'Checkout'
48 | uses: 'actions/checkout@v4'
49 |
50 | - name: 'Install PHP'
51 | uses: 'shivammathur/setup-php@v2'
52 | with:
53 | coverage: 'none'
54 | php-version: '${{ matrix.php-version }}'
55 | tools: cs2pr
56 |
57 | - name: 'Get composer cache directory'
58 | run: echo "composer_cache=$(composer config cache-files-dir)" >> $GITHUB_ENV
59 |
60 | - name: 'Cache dependencies'
61 | uses: actions/cache@v3
62 | with:
63 | path: ${{ env.composer_cache }}
64 | key: deps-php-${{ matrix.php-version }}-composer-${{ hashFiles('composer.json') }}-dep-${{ matrix.dependency-version }}
65 |
66 | - name: 'Install dependencies'
67 | run: composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest
68 |
69 | - name: 'Psalm'
70 | run: ./vendor/bin/psalm
71 |
72 | code-standards:
73 | runs-on: ubuntu-latest
74 |
75 | strategy:
76 | matrix:
77 | php-version: [8.3]
78 | dependency-version: [ prefer-lowest, prefer-stable ]
79 |
80 | steps:
81 | - name: 'Checkout'
82 | uses: 'actions/checkout@v4'
83 |
84 | - name: 'Install PHP'
85 | uses: 'shivammathur/setup-php@v2'
86 | with:
87 | coverage: 'none'
88 | php-version: '${{ matrix.php-version }}'
89 | tools: cs2pr
90 |
91 | - name: 'Get composer cache directory'
92 | run: echo "composer_cache=$(composer config cache-files-dir)" >> $GITHUB_ENV
93 |
94 | - name: 'Cache dependencies'
95 | uses: actions/cache@v3
96 | with:
97 | path: ${{ env.composer_cache }}
98 | key: deps-php-${{ matrix.php-version }}-composer-${{ hashFiles('composer.json') }}-dep-${{ matrix.dependency-version }}
99 |
100 | - name: 'Install dependencies'
101 | run: composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest
102 |
103 | - name: 'Check code standards'
104 | run: ./vendor/bin/ecs check
105 |
--------------------------------------------------------------------------------
/src/Adapter/Local/JSONFile/LocalJSONFileAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary\Adapter\Local\JSONFile;
12 |
13 | use Secretary\Adapter\AbstractAdapter;
14 | use Secretary\Exception\SecretNotFoundException;
15 | use Secretary\Secret;
16 |
17 | /**
18 | * Class LocalJSONFileAdapter.
19 | *
20 | * @package Secretary\Adapter\Local\JSONFile
21 | */
22 | class LocalJSONFileAdapter extends AbstractAdapter
23 | {
24 | private string $secretsFile;
25 |
26 | private int $jsonOptions;
27 |
28 | /**
29 | * @throws \Exception
30 | */
31 | public function __construct(array $config)
32 | {
33 | if ($config === []) {
34 | throw new \Exception('Configuration is required.');
35 | }
36 |
37 | if (!isset($config['file'])) {
38 | throw new \Exception('`file` is a required config.');
39 | }
40 |
41 | if (!isset($config['jsonOptions'])) {
42 | $config['jsonOptions'] = JSON_PRETTY_PRINT;
43 | }
44 |
45 | $this->secretsFile = $config['file'];
46 | $this->jsonOptions = $config['jsonOptions'];
47 | }
48 |
49 | /**
50 | * {@inheritdoc}
51 | *
52 | * @throws SecretNotFoundException
53 | */
54 | public function getSecret(string $key, ?array $options = []): Secret
55 | {
56 | $secrets = $this->loadSecrets();
57 | $keys = array_column($secrets, 'key');
58 | $index = array_search($key, $keys, true);
59 |
60 | if ($index === false || $index === null) {
61 | throw new SecretNotFoundException($key);
62 | }
63 |
64 | return new Secret(
65 | $secrets[$index]['key'],
66 | $secrets[$index]['value'],
67 | $secrets[$index]['metadata'] ?? null
68 | );
69 | }
70 |
71 | /**
72 | * {@inheritdoc}
73 | */
74 | public function putSecret(Secret $secret, ?array $options = []): Secret
75 | {
76 | $secrets = self::updateValue($secret, $this->loadSecrets());
77 | $this->saveSecrets($secrets);
78 |
79 | return $secret;
80 | }
81 |
82 | /**
83 | * {@inheritdoc}
84 | */
85 | public function deleteSecretByKey(string $key, ?array $options = []): void
86 | {
87 | $secrets = $this->loadSecrets();
88 | $keys = array_column($secrets, 'key');
89 | $index = array_search($key, $keys, true);
90 |
91 | if ($index === false || $index === null) {
92 | throw new SecretNotFoundException($key);
93 | }
94 |
95 | array_splice($secrets, $index, 1);
96 | $this->saveSecrets($secrets);
97 | }
98 |
99 | /**
100 | * {@inheritdoc}
101 | *
102 | * @throws SecretNotFoundException
103 | */
104 | public function deleteSecret(Secret $secret, ?array $options = []): void
105 | {
106 | $this->deleteSecretByKey($secret->getKey(), $options);
107 | }
108 |
109 | /**
110 | * @param Secret[] $secrets
111 | */
112 | private static function updateValue(Secret $secret, array $secrets): array
113 | {
114 | $keys = array_column($secrets, 'key');
115 | $index = array_search($secret->getKey(), $keys, true);
116 |
117 | if ($index === false || $index === null) {
118 | $secrets[] = $secret;
119 | } else {
120 | $secrets[$index]['value'] = $secret->getValue();
121 | }
122 |
123 | return $secrets;
124 | }
125 |
126 | /**
127 | * @throws \Exception
128 | *
129 | * @return list
130 | */
131 | private function loadSecrets(): array
132 | {
133 | return json_decode(file_get_contents($this->secretsFile), true, 512, JSON_THROW_ON_ERROR);
134 | }
135 |
136 | private function saveSecrets(array $secrets): void
137 | {
138 | file_put_contents($this->secretsFile, json_encode($secrets, $this->jsonOptions));
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/src/Core/Tests/ManagerTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary\Tests;
12 |
13 | use Mockery\MockInterface;
14 | use PHPUnit\Framework\TestCase;
15 | use Secretary\Adapter\AdapterInterface;
16 | use Secretary\Exception\SecretNotFoundException;
17 | use Secretary\Manager;
18 | use Secretary\Secret;
19 |
20 | /**
21 | * @internal
22 | * @coversNothing
23 | */
24 | class ManagerTest extends TestCase
25 | {
26 | /**
27 | * @var AdapterInterface|MockInterface
28 | */
29 | private $adapter;
30 |
31 | protected function setUp(): void
32 | {
33 | parent::setUp();
34 |
35 | $this->adapter = \Mockery::mock(AdapterInterface::class);
36 | }
37 |
38 | /**
39 | * @psalm-suppress RedundantCondition
40 | */
41 | public function testConstruct(): void
42 | {
43 | $manager = new Manager($this->adapter);
44 |
45 | $this->assertInstanceOf(Manager::class, $manager);
46 | }
47 |
48 | /**
49 | * @psalm-suppress RedundantCondition
50 | */
51 | public function testGetSecret(): void
52 | {
53 | $secret = new Secret('foo', 'bar');
54 | $manager = new Manager($this->adapter);
55 |
56 | $this->adapter->shouldReceive('configureSharedOptions')->withAnyArgs()->once();
57 | $this->adapter->shouldReceive('configureGetSecretOptions')->withAnyArgs()->once();
58 | $this->adapter->shouldReceive('getSecret')->with('foo', [])->andReturn($secret)->once();
59 |
60 | $result = $manager->getSecret('foo');
61 | $this->assertInstanceOf(Secret::class, $result);
62 | $this->assertEquals($secret, $result);
63 | }
64 |
65 | public function testGetBadSecret(): void
66 | {
67 | $this->expectException(SecretNotFoundException::class);
68 | $this->expectExceptionMessage('No secret was found with the key: "foo"');
69 |
70 | $manager = new Manager($this->adapter);
71 |
72 | $this->adapter->shouldReceive('configureSharedOptions')->withAnyArgs()->once();
73 | $this->adapter->shouldReceive('configureGetSecretOptions')->withAnyArgs()->once();
74 | $this->adapter->shouldReceive('getSecret')
75 | ->with('foo', [])->andThrow(new SecretNotFoundException('foo'))->once();
76 |
77 | $manager->getSecret('foo');
78 | }
79 |
80 | public function testPutSecret(): void
81 | {
82 | $manager = new Manager($this->adapter);
83 | $secret = new Secret('foo', 'bar');
84 |
85 | $this->adapter->shouldReceive('configureSharedOptions')->withAnyArgs()->once();
86 | $this->adapter->shouldReceive('configurePutSecretOptions')->withAnyArgs()->once();
87 | $this->adapter->shouldReceive('putSecret')->with($secret, [])->andReturn($secret)->once();
88 |
89 | $response = $manager->putSecret($secret);
90 | $this->assertEquals($secret, $response);
91 | }
92 |
93 | public function testDeleteSecretByKey(): void
94 | {
95 | $manager = new Manager($this->adapter);
96 | $secret = new Secret('foo', '');
97 |
98 | $this->adapter->shouldReceive('configureSharedOptions')->withAnyArgs()->twice();
99 |
100 | $this->adapter->shouldReceive('configureGetSecretOptions')->withAnyArgs()->once();
101 | $this->adapter->shouldReceive('getSecret')->with('foo', [])->andReturn($secret)->once();
102 |
103 | $this->adapter->shouldReceive('configureDeleteSecretOptions')->withAnyArgs()->once();
104 | $this->adapter->shouldReceive('deleteSecret')->with($secret, [])->once();
105 |
106 | $manager->deleteSecretByKey('foo');
107 | $this->assertTrue(true);
108 | }
109 |
110 | public function testDeleteSecret(): void
111 | {
112 | $manager = new Manager($this->adapter);
113 | $secret = new Secret('foo', 'bar');
114 |
115 | $this->adapter->shouldReceive('configureSharedOptions')->withAnyArgs()->once();
116 | $this->adapter->shouldReceive('configureDeleteSecretOptions')->withAnyArgs()->once();
117 | $this->adapter->shouldReceive('deleteSecret')->with($secret, [])->once();
118 |
119 | $manager->deleteSecret($secret);
120 | $this->assertTrue(true);
121 | }
122 |
123 | public function testGetAdapter(): void
124 | {
125 | $manager = new Manager($this->adapter);
126 |
127 | $this->assertEquals($this->adapter, $manager->getAdapter());
128 | $this->assertInstanceOf(AdapterInterface::class, $manager->getAdapter());
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/Adapter/AWS/SecretsManager/AWSSecretsManagerAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | * @date 2019
8 | * @license https://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace Secretary\Adapter\AWS\SecretsManager;
12 |
13 | use Aws\SecretsManager\Exception\SecretsManagerException;
14 | use Aws\SecretsManager\SecretsManagerClient;
15 | use Secretary\Adapter\AbstractAdapter;
16 | use Secretary\Exception\SecretNotFoundException;
17 | use Secretary\Helper\ArrayHelper;
18 | use Secretary\Secret;
19 | use Symfony\Component\OptionsResolver\OptionsResolver;
20 |
21 | /**
22 | * Class AWSSecretsManagerAdapter.
23 | *
24 | * @package Secretary\Adapter\AWS\SecretsManager
25 | */
26 | class AWSSecretsManagerAdapter extends AbstractAdapter
27 | {
28 | private ?SecretsManagerClient $client = null;
29 |
30 | private array $config;
31 |
32 | /**
33 | * @throws \Exception
34 | */
35 | public function __construct(array $config)
36 | {
37 | if (!class_exists(SecretsManagerClient::class)) {
38 | throw new \Exception('aws/aws-sdk-php is required to use the AWSSecretsManagerAdapter');
39 | }
40 |
41 | $this->config = $config;
42 | }
43 |
44 | /**
45 | * {@inheritdoc}
46 | */
47 | public function getSecret(string $key, ?array $options = []): Secret
48 | {
49 | $options['SecretId'] = $key;
50 |
51 | try {
52 | $data = $this->getClient()->getSecretValue($options);
53 |
54 | /** @var string $secretString */
55 | $secretString = $data->get('SecretString');
56 |
57 | return new Secret(
58 | $key,
59 | static::isJson($secretString) ? json_decode($data->get('SecretString'), true) : $secretString
60 | );
61 | } catch (SecretsManagerException $exception) {
62 | $awsMsg = $exception->getAwsErrorMessage() ?? '';
63 |
64 | if (str_contains($awsMsg, 'Secrets Manager can’t find the specified secret')) {
65 | throw new SecretNotFoundException($key, $exception);
66 | }
67 |
68 | throw $exception;
69 | }
70 | }
71 |
72 | /**
73 | * {@inheritdoc}
74 | */
75 | public function putSecret(Secret $secret, ?array $options = []): Secret
76 | {
77 | $options['SecretString'] = is_array($secret->getValue())
78 | ? json_encode($secret->getValue()) : $secret->getValue();
79 |
80 | try {
81 | $options = ArrayHelper::without($options, 'Tags');
82 | $options['SecretId'] = $secret->getKey();
83 |
84 | $this->getClient()->updateSecret($options);
85 | } catch (\Exception $e) {
86 | $options['Name'] = $secret->getKey();
87 |
88 | $this->getClient()->createSecret($options);
89 | }
90 |
91 | return $secret;
92 | }
93 |
94 | /**
95 | * {@inheritdoc}
96 | */
97 | public function deleteSecretByKey(string $key, ?array $options = []): void
98 | {
99 | $options['SecretId'] = $key;
100 |
101 | $this->getClient()->deleteSecret($options);
102 | }
103 |
104 | /**
105 | * {@inheritdoc}
106 | */
107 | public function deleteSecret(Secret $secret, ?array $options = []): void
108 | {
109 | $this->deleteSecretByKey($secret->getKey(), $options);
110 | }
111 |
112 | public function configureGetSecretOptions(OptionsResolver $resolver): void
113 | {
114 | parent::configureSharedOptions($resolver);
115 | $resolver->setDefined(['VersionId', 'VersionStage'])
116 | ->setAllowedTypes('VersionId', 'string')
117 | ->setAllowedTypes('VersionStage', 'string');
118 | }
119 |
120 | public function configurePutSecretOptions(OptionsResolver $resolver): void
121 | {
122 | parent::configureSharedOptions($resolver);
123 | $resolver->setDefined(['KmsKeyId', 'Tags', 'Description'])
124 | ->setAllowedTypes('KmsKeyId', 'string')
125 | ->setAllowedTypes('Description', 'string');
126 | }
127 |
128 | public function configureDeleteSecretOptions(OptionsResolver $resolver): void
129 | {
130 | parent::configureDeleteSecretOptions($resolver);
131 | $resolver
132 | ->setDefined(['ForceDeleteWithoutRecovery', 'RecoveryWindowInDays'])
133 | ->setAllowedTypes('ForceDeleteWithoutRecovery', 'bool')
134 | ->setAllowedTypes('RecoveryWindowInDays', 'int');
135 | }
136 |
137 | private function getClient(): SecretsManagerClient
138 | {
139 | if (!$this->client instanceof SecretsManagerClient) {
140 | $this->client = new SecretsManagerClient($this->config);
141 | }
142 |
143 | return $this->client;
144 | }
145 |
146 | private static function isJson(string $str)
147 | {
148 | $json = json_decode($str);
149 |
150 | return $json && $str != $json;
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [3.0.6](https://github.com/secretary/php/compare/3.0.5...3.0.6) (2025-02-05)
2 |
3 |
4 | ### Bug Fixes
5 |
6 | * Fixed Symfony deprecation ([03f1da5](https://github.com/secretary/php/commit/03f1da511717ba3dc8cbb70faf75772218ed6373))
7 |
8 | ## [3.0.5](https://github.com/secretary/php/compare/3.0.4...3.0.5) (2025-02-05)
9 |
10 |
11 | ### Bug Fixes
12 |
13 | * Fixed PHP 8.4 deprecation ([19e99e7](https://github.com/secretary/php/commit/19e99e7a7c6f37cdbf33fef887c7d11ba44cfef9))
14 |
15 | ## [3.0.4](https://github.com/secretary/php/compare/3.0.3...3.0.4) (2024-01-29)
16 |
17 |
18 | ### Bug Fixes
19 |
20 | * ArrayHelper remove method ([61d1477](https://github.com/secretary/php/commit/61d147755c1091bbf9b08b6792debd041a0286fd))
21 |
22 | ## [3.0.3](https://github.com/secretary/php/compare/3.0.2...3.0.3) (2024-01-15)
23 |
24 |
25 | ### Bug Fixes
26 |
27 | * Optimized array helper method ([f996d0b](https://github.com/secretary/php/commit/f996d0b6fd9e524787535bb3683fb884450d5d3e))
28 |
29 | ## [3.0.2](https://github.com/secretary/php/compare/3.0.1...3.0.2) (2024-01-11)
30 |
31 |
32 | ### Bug Fixes
33 |
34 | * Array helper ([4098f7e](https://github.com/secretary/php/commit/4098f7e4d578fefb71a70739976783d72e4a238f))
35 |
36 | ## [3.0.1](https://github.com/secretary/php/compare/3.0.0...3.0.1) (2024-01-10)
37 |
38 |
39 | ### Bug Fixes
40 |
41 | * **CI:** Updated node and semantic-release ([096e283](https://github.com/secretary/php/commit/096e283ce38a77ba0901746612f2be1320867900))
42 |
43 | ## [2.1.1](https://github.com/secretary/php/compare/2.1.0...2.1.1) (2024-01-09)
44 |
45 |
46 | ### Bug Fixes
47 |
48 | * Allow PHP Unit 10 ([f20907e](https://github.com/secretary/php/commit/f20907efcf2b84c3c07b1d2e01747e54ecfb42e5))
49 |
50 | ## [2.0.1](https://github.com/secretary/php/compare/2.0.0...2.0.1) (2024-01-09)
51 |
52 |
53 | ### Bug Fixes
54 |
55 | * **global:** Allow Symfony 7 ([#17](https://github.com/secretary/php/issues/17)) ([86ef910](https://github.com/secretary/php/commit/86ef9100d7f233f0c57df148a0ee24886e6eefbd))
56 |
57 |
58 | ### Performance Improvements
59 |
60 | * **Symfony:** Allow SF 7 ([ac9146c](https://github.com/secretary/php/commit/ac9146c583a93a8706aaea7b770ab83ee510536c))
61 |
62 | # [2.0.0](https://github.com/secretary/php/compare/1.4.0...2.0.0) (2022-02-12)
63 |
64 |
65 | ### Performance Improvements
66 |
67 | * **PHP:** Dropped support for PHP 7.4 ([b14daf1](https://github.com/secretary/php/commit/b14daf1822875fa2c3f7cb2f8840737df7fed1bc))
68 |
69 |
70 | ### BREAKING CHANGES
71 |
72 | * **PHP:** Removed support for PHP 7.4
73 |
74 | # [1.4.0](https://github.com/secretary/php/compare/1.3.2...1.4.0) (2022-02-11)
75 |
76 |
77 | ### Bug Fixes
78 |
79 | * **CI:** Replaced Travis CI with GH workflow ([0c9fdb3](https://github.com/secretary/php/commit/0c9fdb3b3110b8060be96a06d0766bff4549bca6))
80 | * **CI:** Replaced Travis CI with GH workflow ([a88154d](https://github.com/secretary/php/commit/a88154d88c8976d2990f6c62d1cbd8d0394c62ca))
81 |
82 |
83 | ### Features
84 |
85 | * **global:** PHP 7.4 as minimum PHP version ([eb2ff91](https://github.com/secretary/php/commit/eb2ff91db84c3924eab57f43d59aa5c4eac61e4d))
86 |
87 | ## [1.3.2](https://github.com/secretary/php/compare/1.3.1...1.3.2) (2021-02-28)
88 |
89 |
90 | ### Bug Fixes
91 |
92 | * **chore:** Triggering release ([7d43834](https://github.com/secretary/php/commit/7d4383441f058377bca4258408d38e05eb81853a))
93 |
94 | ## [1.3.1](https://github.com/secretary/php/compare/1.3.0...1.3.1) (2021-02-23)
95 |
96 |
97 | ### Bug Fixes
98 |
99 | * **chore:** Triggering release ([74123e6](https://github.com/secretary/php/commit/74123e632d1c182a4d03ebf6852ca2e7a6dade1a))
100 |
101 | # [1.3.0](https://github.com/secretary/php/compare/1.2.18...1.3.0) (2021-02-14)
102 |
103 |
104 | ### Bug Fixes
105 |
106 | * **travis:** tests for php 8 ([9889dbf](https://github.com/secretary/php/commit/9889dbfd9fa639d6a942c2b2577323beccadb923))
107 | * **travis:** tests for php 8 ([4ddfeaa](https://github.com/secretary/php/commit/4ddfeaaf27b8558a49d1a9381c2df335dbc0a96f))
108 | * **travis:** tests for php 8 ([425918e](https://github.com/secretary/php/commit/425918e173f99ce623ab96ac2993675788d18d51))
109 |
110 |
111 | ### Features
112 |
113 | * **global:** PHP 8 Support ([27be33d](https://github.com/secretary/php/commit/27be33d2771b56e938d38e335c442a19c3427c74))
114 |
115 | ## [1.2.18](https://github.com/secretary/php/compare/1.2.17...1.2.18) (2019-08-15)
116 |
117 |
118 | ### Bug Fixes
119 |
120 | * **bundle:** Fixing cache adapters ([e6fd44d](https://github.com/secretary/php/commit/e6fd44d))
121 | * **bundle:** Fixing cache adapters ([97504a2](https://github.com/secretary/php/commit/97504a2))
122 |
123 | ## [1.2.17](https://github.com/secretary/php/compare/1.2.16...1.2.17) (2019-08-15)
124 |
125 |
126 | ### Bug Fixes
127 |
128 | * **aws:** Throwing error when the value for a secret can't be found ([78baf8c](https://github.com/secretary/php/commit/78baf8c))
129 |
130 | ## [1.2.16](https://github.com/secretary/php/compare/1.2.15...1.2.16) (2019-08-15)
131 |
132 |
133 | ### Bug Fixes
134 |
135 | * **aws:** Throwing error when the value for a secret can't be found ([9202f85](https://github.com/secretary/php/commit/9202f85))
136 |
137 | ## [1.2.15](https://github.com/secretary/php/compare/1.2.14...1.2.15) (2019-08-14)
138 |
139 |
140 | ### Bug Fixes
141 |
142 | * **aws:** Throwing error when the value for a secret can't be found ([38c7818](https://github.com/secretary/php/commit/38c7818))
143 |
144 | ## [1.2.14](https://github.com/secretary/php/compare/1.2.13...1.2.14) (2019-08-14)
145 |
146 |
147 | ### Bug Fixes
148 |
149 | * **aws:** Throwing error when the value for a secret can't be found ([e99e036](https://github.com/secretary/php/commit/e99e036))
150 |
151 | ## [1.2.13](https://github.com/secretary/php/compare/1.2.12...1.2.13) (2019-08-14)
152 |
153 |
154 | ### Bug Fixes
155 |
156 | * **bundle:** Fixing runtime error in key-not-found scenario ([92b1694](https://github.com/secretary/php/commit/92b1694))
157 | * **bundle:** Fixing runtime error in key-not-found scenario ([d698cf5](https://github.com/secretary/php/commit/d698cf5))
158 |
159 | ## [1.2.12](https://github.com/secretary/php/compare/1.2.11...1.2.12) (2019-08-14)
160 |
161 |
162 | ### Bug Fixes
163 |
164 | * **chain:** Fixing chain adapter ([6449bb4](https://github.com/secretary/php/commit/6449bb4))
165 |
166 | ## [1.2.11](https://github.com/secretary/php/compare/1.2.10...1.2.11) (2019-08-13)
167 |
168 |
169 | ### Bug Fixes
170 |
171 | * **bundle:** Making adapters public ([3b5f4ef](https://github.com/secretary/php/commit/3b5f4ef))
172 |
173 | ## [1.2.10](https://github.com/secretary/php/compare/1.2.9...1.2.10) (2019-08-13)
174 |
175 |
176 | ### Bug Fixes
177 |
178 | * **chain:** Fixing bad package name ([074eb9c](https://github.com/secretary/php/commit/074eb9c))
179 |
180 | ## [1.2.9](https://github.com/secretary/php/compare/1.2.8...1.2.9) (2019-08-13)
181 |
182 |
183 | ### Bug Fixes
184 |
185 | * **json:** Check metadata ([#4](https://github.com/secretary/php/issues/4)) ([80ef20e](https://github.com/secretary/php/commit/80ef20e))
186 |
187 | ## [1.2.8](https://github.com/secretary/php/compare/1.2.7...1.2.8) (2019-08-13)
188 |
189 |
190 | ### Bug Fixes
191 |
192 | * **travis:** Fixing flow ([39284e8](https://github.com/secretary/php/commit/39284e8))
193 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | # charset = utf-8
5 | # end_of_line = lf
6 | # indent_size = 4
7 | # indent_style = space
8 | insert_final_newline = true
9 | # max_line_length = 120
10 | # tab_width = 4
11 | # ij_continuation_indent_size = 8
12 | # ij_formatter_off_tag = @formatter:off
13 | # ij_formatter_on_tag = @formatter:on
14 | # ij_formatter_tags_enabled = false
15 | # ij_smart_tabs = false
16 | # ij_wrap_on_typing = false
17 |
18 | [{*.yml,*.yaml}]
19 | ij_continuation_indent_size = 2
20 | # ij_yaml_keep_indents_on_empty_lines = false
21 | # ij_yaml_keep_line_breaks = true
22 |
23 | [{console,*.module,*.hphp,*.phtml,*.php5,*.php4,*.php,*.ctp,*.inc}]
24 | indent_style = space
25 | indent_size = 4
26 | # ij_continuation_indent_size = 4
27 | ij_php_align_assignments = true
28 | ij_php_align_class_constants = true
29 | ij_php_align_group_field_declarations = true
30 | # ij_php_align_inline_comments = false
31 | ij_php_align_key_value_pairs = true
32 | # ij_php_align_multiline_array_initializer_expression = false
33 | # ij_php_align_multiline_binary_operation = false
34 | # ij_php_align_multiline_chained_methods = false
35 | # ij_php_align_multiline_extends_list = false
36 | # ij_php_align_multiline_for = true
37 | # ij_php_align_multiline_parameters = true
38 | # ij_php_align_multiline_parameters_in_calls = false
39 | # ij_php_align_multiline_ternary_operation = false
40 | ij_php_align_phpdoc_comments = false
41 | ij_php_align_phpdoc_param_names = true
42 | # ij_php_api_weight = 28
43 | # ij_php_array_initializer_new_line_after_left_brace = false
44 | # ij_php_array_initializer_right_brace_on_new_line = false
45 | # ij_php_array_initializer_wrap = off
46 | # ij_php_assignment_wrap = off
47 | # ij_php_author_weight = 28
48 | # ij_php_binary_operation_sign_on_next_line = false
49 | # ij_php_binary_operation_wrap = off
50 | # ij_php_blank_lines_after_class_header = 0
51 | # ij_php_blank_lines_after_function = 1
52 | # ij_php_blank_lines_after_imports = 1
53 | # ij_php_blank_lines_after_opening_tag = 0
54 | # ij_php_blank_lines_after_package = 0
55 | # ij_php_blank_lines_around_class = 1
56 | # ij_php_blank_lines_around_constants = 0
57 | # ij_php_blank_lines_around_field = 0
58 | # ij_php_blank_lines_around_method = 1
59 | # ij_php_blank_lines_before_class_end = 0
60 | # ij_php_blank_lines_before_imports = 1
61 | # ij_php_blank_lines_before_method_body = 0
62 | # ij_php_blank_lines_before_package = 1
63 | ij_php_blank_lines_before_return_statement = 1
64 | # ij_php_block_brace_style = end_of_line
65 | # ij_php_call_parameters_new_line_after_left_paren = false
66 | # ij_php_call_parameters_right_paren_on_new_line = false
67 | # ij_php_call_parameters_wrap = off
68 | # ij_php_catch_on_new_line = false
69 | # ij_php_category_weight = 28
70 | # ij_php_class_brace_style = next_line
71 | ij_php_comma_after_last_array_element = true
72 | ij_php_concat_spaces = false
73 | # ij_php_copyright_weight = 28
74 | # ij_php_deprecated_weight = 28
75 | # ij_php_do_while_brace_force = never
76 | # ij_php_else_if_style = as_is
77 | # ij_php_else_on_new_line = false
78 | # ij_php_example_weight = 28
79 | # ij_php_extends_keyword_wrap = off
80 | # ij_php_extends_list_wrap = off
81 | # ij_php_fields_default_visibility = private
82 | # ij_php_filesource_weight = 28
83 | # ij_php_finally_on_new_line = false
84 | # ij_php_for_brace_force = never
85 | # ij_php_for_statement_new_line_after_left_paren = false
86 | # ij_php_for_statement_right_paren_on_new_line = false
87 | # ij_php_for_statement_wrap = off
88 | # ij_php_force_short_declaration_array_style = false
89 | # ij_php_global_weight = 28
90 | # ij_php_group_use_wrap = on_every_item
91 | # ij_php_if_brace_force = never
92 | # ij_php_if_lparen_on_next_line = false
93 | # ij_php_if_rparen_on_next_line = false
94 | # ij_php_ignore_weight = 28
95 | # ij_php_import_sorting = alphabetic
96 | # ij_php_indent_break_from_case = true
97 | # ij_php_indent_case_from_switch = true
98 | # ij_php_indent_code_in_php_tags = false
99 | # ij_php_internal_weight = 28
100 | # ij_php_keep_blank_lines_after_lbrace = 2
101 | # ij_php_keep_blank_lines_before_right_brace = 2
102 | # ij_php_keep_blank_lines_in_code = 2
103 | # ij_php_keep_blank_lines_in_declarations = 2
104 | # ij_php_keep_control_statement_in_one_line = true
105 | # ij_php_keep_first_column_comment = true
106 | # ij_php_keep_indents_on_empty_lines = false
107 | # ij_php_keep_line_breaks = true
108 | # ij_php_keep_rparen_and_lbrace_on_one_line = false
109 | # ij_php_keep_simple_methods_in_one_line = false
110 | # ij_php_lambda_brace_style = end_of_line
111 | # ij_php_license_weight = 28
112 | # ij_php_line_comment_add_space = false
113 | # ij_php_line_comment_at_first_column = true
114 | # ij_php_link_weight = 28
115 | # ij_php_lower_case_boolean_const = false
116 | # ij_php_lower_case_null_const = false
117 | # ij_php_method_brace_style = next_line
118 | # ij_php_method_call_chain_wrap = off
119 | # ij_php_method_parameters_new_line_after_left_paren = false
120 | # ij_php_method_parameters_right_paren_on_new_line = false
121 | # ij_php_method_parameters_wrap = off
122 | # ij_php_method_weight = 28
123 | # ij_php_modifier_list_wrap = false
124 | # ij_php_multiline_chained_calls_semicolon_on_new_line = false
125 | # ij_php_namespace_brace_style = 1
126 | # ij_php_null_type_position = in_the_end
127 | # ij_php_package_weight = 28
128 | # ij_php_param_weight = 0
129 | # ij_php_parentheses_expression_new_line_after_left_paren = false
130 | # ij_php_parentheses_expression_right_paren_on_new_line = false
131 | # ij_php_phpdoc_blank_line_before_tags = false
132 | ij_php_phpdoc_blank_lines_around_parameters = true
133 | # ij_php_phpdoc_keep_blank_lines = true
134 | # ij_php_phpdoc_param_spaces_between_name_and_description = 1
135 | # ij_php_phpdoc_param_spaces_between_tag_and_type = 1
136 | # ij_php_phpdoc_param_spaces_between_type_and_name = 1
137 | # ij_php_phpdoc_use_fqcn = false
138 | # ij_php_phpdoc_wrap_long_lines = false
139 | # ij_php_place_assignment_sign_on_next_line = false
140 | # ij_php_place_parens_for_constructor = 0
141 | # ij_php_property_read_weight = 28
142 | # ij_php_property_weight = 28
143 | # ij_php_property_write_weight = 28
144 | # ij_php_return_type_on_new_line = false
145 | # ij_php_return_weight = 1
146 | # ij_php_see_weight = 28
147 | # ij_php_since_weight = 28
148 | ij_php_sort_phpdoc_elements = false
149 | # ij_php_space_after_colon = true
150 | ij_php_space_after_colon_in_return_type = true
151 | # ij_php_space_after_comma = true
152 | # ij_php_space_after_for_semicolon = true
153 | # ij_php_space_after_quest = true
154 | ij_php_space_after_type_cast = true
155 | # ij_php_space_after_unary_not = false
156 | # ij_php_space_before_array_initializer_left_brace = false
157 | # ij_php_space_before_catch_keyword = true
158 | # ij_php_space_before_catch_left_brace = true
159 | # ij_php_space_before_catch_parentheses = true
160 | # ij_php_space_before_class_left_brace = true
161 | # ij_php_space_before_closure_left_parenthesis = true
162 | # ij_php_space_before_colon = true
163 | # ij_php_space_before_colon_in_return_type = false
164 | # ij_php_space_before_comma = false
165 | # ij_php_space_before_do_left_brace = true
166 | # ij_php_space_before_else_keyword = true
167 | # ij_php_space_before_else_left_brace = true
168 | # ij_php_space_before_finally_keyword = true
169 | # ij_php_space_before_finally_left_brace = true
170 | # ij_php_space_before_for_left_brace = true
171 | # ij_php_space_before_for_parentheses = true
172 | # ij_php_space_before_for_semicolon = false
173 | # ij_php_space_before_if_left_brace = true
174 | # ij_php_space_before_if_parentheses = true
175 | # ij_php_space_before_method_call_parentheses = false
176 | # ij_php_space_before_method_left_brace = true
177 | # ij_php_space_before_method_parentheses = false
178 | # ij_php_space_before_quest = true
179 | # ij_php_space_before_switch_left_brace = true
180 | # ij_php_space_before_switch_parentheses = true
181 | # ij_php_space_before_try_left_brace = true
182 | # ij_php_space_before_unary_not = false
183 | # ij_php_space_before_while_keyword = true
184 | # ij_php_space_before_while_left_brace = true
185 | # ij_php_space_before_while_parentheses = true
186 | # ij_php_space_between_ternary_quest_and_colon = false
187 | # ij_php_spaces_around_additive_operators = true
188 | # ij_php_spaces_around_arrow = false
189 | # ij_php_spaces_around_assignment_in_declare = false
190 | # ij_php_spaces_around_assignment_operators = true
191 | # ij_php_spaces_around_bitwise_operators = true
192 | # ij_php_spaces_around_equality_operators = true
193 | # ij_php_spaces_around_logical_operators = true
194 | # ij_php_spaces_around_multiplicative_operators = true
195 | # ij_php_spaces_around_null_coalesce_operator = true
196 | # ij_php_spaces_around_relational_operators = true
197 | # ij_php_spaces_around_shift_operators = true
198 | # ij_php_spaces_around_unary_operator = false
199 | # ij_php_spaces_around_var_within_brackets = false
200 | # ij_php_spaces_within_array_initializer_braces = false
201 | # ij_php_spaces_within_brackets = false
202 | # ij_php_spaces_within_catch_parentheses = false
203 | # ij_php_spaces_within_for_parentheses = false
204 | # ij_php_spaces_within_if_parentheses = false
205 | # ij_php_spaces_within_method_call_parentheses = false
206 | # ij_php_spaces_within_method_parentheses = false
207 | # ij_php_spaces_within_parentheses = false
208 | # ij_php_spaces_within_short_echo_tags = true
209 | # ij_php_spaces_within_switch_parentheses = false
210 | # ij_php_spaces_within_while_parentheses = false
211 | # ij_php_special_else_if_treatment = false
212 | # ij_php_subpackage_weight = 28
213 | # ij_php_ternary_operation_signs_on_next_line = false
214 | # ij_php_ternary_operation_wrap = off
215 | # ij_php_throws_weight = 2
216 | # ij_php_todo_weight = 28
217 | # ij_php_unknown_tag_weight = 28
218 | # ij_php_upper_case_boolean_const = false
219 | # ij_php_upper_case_null_const = false
220 | # ij_php_uses_weight = 28
221 | # ij_php_var_weight = 28
222 | # ij_php_variable_naming_style = mixed
223 | # ij_php_version_weight = 28
224 | # ij_php_while_brace_force = never
225 | # ij_php_while_on_new_line = false
226 |
--------------------------------------------------------------------------------
/src/Core/README.md:
--------------------------------------------------------------------------------
1 | # Secretary - Secrets Manager for PHP
2 | [](https://packagist.org/packages/secretary/core) [](https://packagist.org/packages/secretary/core)
3 |
4 | Secrets are an important aspect of most applications you can build. How you store them, and keep them "secret" is a challenge.
5 | Luckily, there are tools you can use to keep them all safe.
6 |
7 | Secretary is a tool to integrate your PHP application with these tools.
8 |
9 | ## Table of Contents
10 |
11 | 1. [Installation](#installation)
12 | 2. [Api Documentation](#api-documentation)
13 | 1. [Secretary\Manager](#manager-class)
14 | 1. [Initializing](#manager-constructor)
15 | 2. [getSecret](#manager-getSecret)
16 | 3. [putSecret](#manager-putSecret)
17 | 4. [deleteSecret](#manager-deleteSecret)
18 | 5. [getAdapter](#manager-getAdapter)
19 | 2. [Secretary\Secret](#secret-class)
20 | 1. [getKey](#secret-getKey)
21 | 1. [getValue](#secret-getValue)
22 |
23 | ## Installation
24 |
25 | ```bash
26 | $ composer req secretary/core
27 | ```
28 |
29 | ##### Choose the version you need
30 |
31 | | Version (X.Y.Z) | PHP | Symfony | Comment |
32 | |:---------------:|:----------:|:----------:|:--------------------|
33 | | `3.*` | `>= 8.1.0` | `7.0` | **Current version** |
34 | | `2.*` | `>= 8.1.0` | `5.4, 6.0` | Previous version |
35 | | `1.*` | `>= 7.4.0` | `5.3` | Previous version |
36 |
37 | By itself, the core is useless. You will also need to add at least one adapter:
38 |
39 | | Storage Engine | Badges |
40 | | -------------- | -------- |
41 | | [AWS Secrets Manager][aws-secrets-manager-adapter] | [](https://packagist.org/packages/secretary/aws-secrets-manager-adapter) [](https://packagist.org/packages/secretary/aws-secrets-manager-adapter) |
42 | | [HashiCorp Vault][hashicorp-vault-adapter] | [](https://packagist.org/packages/secretary/hashicorp-vault-adapter) [](https://packagist.org/packages/secretary/hashicorp-vault-adapter) |
43 | | [JSON File][json-file-adapter] | [](https://packagist.org/packages/secretary/local-json-file-adapter) [](https://packagist.org/packages/secretary/local-json-file-adapter) |
44 |
45 | There are also miscellaneous packages that add on to Secretary
46 |
47 | | Package | Purpose | Badges |
48 | | ------- | ------- | ------ |
49 | | [PSR-6 Cache Adapter][psr-6-cache-adapter] | Allows for caching secrets using a PSR-6 Cache Interface | [](https://packagist.org/packages/secretary/psr-6-cache-adapter) [](https://packagist.org/packages/secretary/psr-6-cache-adapter) |
50 | | [PSR-16 Cache Adapter][psr-16-cache-adapter] | Allows for caching secrets using a PSR-16 Cache Interface | [](https://packagist.org/packages/secretary/psr-16-cache-adapter) [](https://packagist.org/packages/secretary/psr-16-cache-adapter) |
51 | | [Secretary Bundle][secretary-bundle] | Allows for integrating with the Symfony Framework | [](https://packagist.org/packages/secretary/secretary-bundle) [](https://packagist.org/packages/secretary/secretary-bundle) |
52 |
53 | ## Api Documentation
54 |
55 | There's two classes you interface with in Secretary:
56 |
57 | * [`Secretary\Manager`][Secretary\Manager::class]
58 | * [`Secretary\Secret`][Secretary\Secret::class]
59 |
60 |
61 |
62 | ### Secretary\Manager
63 |
64 |
65 |
66 | #### Secretary\Manager->__construct(AdapterInterface $adapter)
67 |
68 | Pass in your desired adapter.
69 |
70 | ```php
71 | 'us-east-1',
78 | 'credentials' => [
79 | 'accessKeyId' => 'myAccessKeyId',
80 | 'secretAccessKey' => 'mySecretAccessKey'
81 | ]
82 | ])
83 | );
84 | ```
85 |
86 | Optionally, you may wrap your adapter, with one of the two cache adapters.
87 |
88 | ```php
89 | 'us-east-1',
100 | 'credentials' => [
101 | 'accessKeyId' => 'myAccessKeyId',
102 | 'secretAccessKey' => 'mySecretAccessKey'
103 | ]
104 | ]),
105 | new ApcCachePool()
106 | )
107 | );
108 | ```
109 |
110 | For mor information on the arguments and options for the adapters, view their respective documentation.
111 |
112 |
113 |
114 | #### Secretary\Manager->getSecret(string $key, ?array $options): Secret
115 |
116 | Fetches a secret from the configured adapter. `$key` is the name of the secret (or path) you are trying to get.
117 |
118 | Certain adapters will take custom options as well, like VersionId and VersionStage for the AWS SecretsManager Adapter
119 |
120 | This will throw a `Secretary\SecretNotFoundException` if the secret cannot be found
121 |
122 | ```php
123 | $secret = $manager->getSecret('databases/redis/dsn');
124 | /*
125 | Secret {
126 | "path" = "databases/redis/dsn",
127 | "value" = "redis://localhost:6379"
128 | }
129 | */
130 | ```
131 |
132 | Some adapters also support storing a key/value map as a secret's value.
133 |
134 | ```php
135 | $secret = $manager->getSecret('databases/redis');
136 | /*
137 | Secret {
138 | "path" = "databases/redis",
139 | "value" = [
140 | "dsn" => "redis://localhost:6379",
141 | "password" => "my_super_strong_password"
142 | ]
143 | }
144 | */
145 | ```
146 |
147 |
148 |
149 | #### Secretary\Manager->putSecret(string $key, string|array $value, ?array $options): void
150 |
151 | Puts a secret with the given `$value`, into the storage engine, under the given `$key`.
152 |
153 | If the current adapter doesn't support arrays, and you pass one it, it will throw a `Secretary\ValueNotSupportedException`.
154 |
155 | Again, some adapters allow passing in custom options to send along with the request.
156 |
157 | ```php
158 | $manager->putSecret('database/redis', 'postgres://localhost:5432');
159 | ```
160 |
161 | And for adapters that support a key/value map as a value:
162 |
163 | ```php
164 | $manager->putSecret('database/redis', ['dsn' => 'redis://localhost:6379', 'password' => 'my_super_strong_password']);
165 | ```
166 |
167 |
168 |
169 | #### Secretary\Manager->deleteSecret(string $key, ?array $options): void
170 |
171 | Deletes a secret from the storage engine using the given `$key`.
172 |
173 | Again, some adapters allow passing in custom options to send along with the request.
174 |
175 | ```php
176 | $manager->deleteSecret('database/redis');
177 | ```
178 |
179 |
180 |
181 | #### Secretary\Manager->getAdapter(): AdapterInterface
182 |
183 | Will return the adapter that was passed to this manager during construction.
184 |
185 |
186 |
187 | ### Secretary\Secret
188 |
189 | This class implements ArrayAccess, so if your secret supports passing a key/value map, you can grab straight from the map:
190 |
191 | Secrets are immutable, so attempting to change a value will throw an Exception.
192 |
193 | ```php
194 | $secret = $manager->getSecret('database/redis');
195 |
196 | $dsn = $secret['dsn'];
197 | ```
198 |
199 |
200 |
201 | #### Secretary\Secret->getKey(): string
202 |
203 | Returns the key for the secret
204 |
205 | ```php
206 | $secret = $manager->getSecret('dabase/redis');
207 |
208 | $secret->getKey() === 'database/redis'; // true
209 | ```
210 |
211 |
212 |
213 | #### Secretary\Secret->getValue(): string | array
214 |
215 | Returns the value for the secret. If the secret is a key/value map, its an array
216 |
217 | ```php
218 | $secret = $manager->getSecret('dabase/redis/dsn');
219 |
220 | $secret->getValue() === 'redis://localhost:6379'; // true
221 |
222 | // Or
223 |
224 | $secret = $manager->getSecret('dabase/redis');
225 |
226 | print_r($secret->getValue());
227 | /*
228 | [
229 | "dsn" => "redis://localhost:6379",
230 | "password" => "my_super_strong_password"
231 | ]
232 | */
233 | ```
234 |
235 |
236 | [aws-secrets-manager-adapter]: https://github.com/secretary/php-aws-secrets-manager-adapter
237 | [hashicorp-vault-adapter]: https://github.com/secretary/php-hashicorp-vault-adapter
238 | [json-file-adapter]: https://github.com/secretary/php-json-file-adapter
239 | [psr-6-cache-adapter]: https://github.com/secretary/php-psr-6-cache-adapter
240 | [psr-16-cache-adapter]: https://github.com/secretary/php-psr-16-cache-adapter
241 | [secretary-bundle]: https://github.com/secretary/php-secretary-bundle
242 | [Secretary\Manager::class]: https://github.com/secretary/php/blob/master/src/Core/src/Manager.php
243 | [Secretary\Secret::class]: https://github.com/secretary/php/blob/master/src/Core/src/Secret.php
244 |
--------------------------------------------------------------------------------