Skip to content

Commit 2ffe1f0

Browse files
committed
create first vault as part of configure
1 parent 84913af commit 2ffe1f0

4 files changed

Lines changed: 187 additions & 141 deletions

File tree

README.md

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,46 +17,50 @@
1717

1818
The package provides a secure, organized way to manage application secrets without storing them in version control or sharing them insecurely.
1919

20-
## Installation
20+
## Quick Start
2121

22-
You can install the package via composer:
22+
### Install and configure Keep
23+
24+
Install the package via composer:
2325

2426
```bash
2527
composer require stechstudio/laravel-keep
2628
```
2729

28-
## Quick Example
30+
This will install a command in your `vendor/bin` directory called `keep`. Run `keep configure` to configure Keep and your first vault.
2931

30-
Let's say you have three environments (local, staging, production) and you want to store secrets in AWS SSM Parameter Store with the default KMS encryption key, in the `us-east-1` region. (You can also use AWS Secrets Manager - see configuration docs for details.)
32+
```bash
33+
./vendor/bin/keep configure
34+
```
3135

32-
### Setup
36+
You should now have Keep configured with a default vault. Run `keep verify` to check your setup and ensure you have necessary permissions.
3337

34-
1. Install the package via composer (as shown above).
35-
2. Ensure you have AWS credentials configured in your environment, with permissions to access SSM Parameter Store (see docs for full example).
36-
3. Run `php artisan keep:verify` to check your setup, verify your vault configuration, and ensure you have necessary permissions.
38+
```bash
39+
./vendor/bin/keep verify
40+
```
3741

3842
### Manage secrets
3943

40-
You can add secrets using the artisan command:
44+
You can add secrets using `keep set`:
4145

4246
```bash
4347
# You will be prompted for the stage and secret value
44-
php artisan keep:set DB_PASSWORD
48+
./vendor/bin/keep set DB_PASSWORD
4549

4650
# Or specify the stage and value directly
47-
php artisan keep:set DB_PASSWORD --stage=production --value="supersecretpassword"
51+
./vendor/bin/keep set DB_PASSWORD --stage=production --value="supersecretpassword"
4852
```
4953

50-
This will store the `DB_PASSWORD` secret in AWS SSM under the path `/[app-name-slug]/production/DB_PASSWORD`.
54+
This will store the `DB_PASSWORD` secret in AWS SSM under the path `/[namespace]/production/DB_PASSWORD`.
5155

5256
Check that the secret was added:
5357

5458
```bash
5559
# Retrieve a single secret
56-
php artisan keep:get DB_PASSWORD --stage=production
60+
./vendor/bin/keep get DB_PASSWORD --stage=production
5761

5862
# List all secrets for production
59-
php artisan keep:list --stage=production
63+
./vendor/bin/keep list --stage=production
6064
```
6165

6266
### Using secrets in your application
@@ -66,7 +70,7 @@ php artisan keep:list --stage=production
6670
If 100% of your .env variables are managed via Keep, you can export them all to a .env file as part of your deployment process:
6771

6872
```bash
69-
php artisan keep:export --stage=production --output=.env
73+
./vendor/bin/keep export --stage=production --output=.env
7074
```
7175

7276
#### Merge secrets into a base template `.env` file
@@ -85,7 +89,7 @@ DB_PASSWORD={ssm:DB_PASSWORD} # or just {ssm} since the key matches the variable
8589
Then run the merge command:
8690

8791
```bash
88-
php artisan keep:merge --template=.env.base --output=.env --stage=production
92+
./vendor/bin/keep merge --template=.env.base --output=.env --stage=production
8993
```
9094

9195
You will now have a `.env` file with all the values from the template and the secrets filled in.
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<?php
2+
3+
namespace STS\Keep\Commands\Concerns;
4+
5+
use function Laravel\Prompts\text;
6+
use function Laravel\Prompts\select;
7+
use function Laravel\Prompts\info;
8+
use function Laravel\Prompts\error;
9+
10+
trait ConfiguresVaults
11+
{
12+
protected function configureNewVault(): ?array
13+
{
14+
// Get available vault classes and build options
15+
$availableVaults = $this->manager->getAvailableVaults();
16+
$driverOptions = [];
17+
$vaultClassMap = [];
18+
19+
foreach ($availableVaults as $vaultClass) {
20+
$driver = $vaultClass::DRIVER;
21+
$name = $vaultClass::NAME;
22+
$driverOptions[$driver] = $name;
23+
$vaultClassMap[$driver] = $vaultClass;
24+
}
25+
26+
$selectedDriver = select(
27+
label: 'Which vault driver would you like to use?',
28+
options: $driverOptions
29+
);
30+
31+
$selectedVaultClass = $vaultClassMap[$selectedDriver];
32+
33+
// Generate default slug and check for uniqueness
34+
$defaultSlug = $this->generateUniqueSlug($selectedDriver);
35+
36+
$slug = text(
37+
label: 'Driver slug (used in template placeholders)',
38+
default: $defaultSlug,
39+
hint: 'Short identifier used in secret templates like {vault:DB_PASSWORD}'
40+
);
41+
42+
// Validate slug uniqueness
43+
$existingVaults = $this->manager->getConfiguredVaults();
44+
if (isset($existingVaults[$slug])) {
45+
error("A vault with slug '{$slug}' already exists!");
46+
return null;
47+
}
48+
49+
// Get friendly name
50+
$friendlyName = text(
51+
label: 'Friendly name for this vault',
52+
default: $selectedVaultClass::NAME,
53+
hint: 'A descriptive name to identify this vault configuration'
54+
);
55+
56+
// Configure the specific vault using dynamic prompts
57+
$vaultConfig = $this->configureVaultSettings($selectedVaultClass, $friendlyName);
58+
59+
if (!$vaultConfig) {
60+
error('Failed to configure vault');
61+
return null;
62+
}
63+
64+
// Save the vault configuration
65+
$this->saveVaultConfig($slug, $vaultConfig);
66+
67+
// Set as default vault if none exists
68+
if (empty($this->manager->getSetting('default_vault'))) {
69+
info("Setting '{$slug}' as your default vault since you don't have one yet.");
70+
$this->setDefaultVault($slug);
71+
}
72+
73+
info("{$friendlyName} vault '{$slug}' configured successfully");
74+
75+
return ['slug' => $slug, 'config' => $vaultConfig];
76+
}
77+
78+
private function generateUniqueSlug(string $driver): string
79+
{
80+
$existingVaults = $this->manager->getConfiguredVaults();
81+
82+
// If the driver doesn't exist, use it as-is
83+
if (!isset($existingVaults[$driver])) {
84+
return $driver;
85+
}
86+
87+
// Find a unique numbered suffix
88+
$counter = 2;
89+
while (isset($existingVaults["{$driver}{$counter}"])) {
90+
$counter++;
91+
}
92+
93+
return "{$driver}{$counter}";
94+
}
95+
96+
private function configureVaultSettings(string $vaultClass, string $friendlyName): ?array
97+
{
98+
info("Configuring {$friendlyName}...");
99+
100+
// Get dynamic prompts from the vault class
101+
$prompts = $vaultClass::configure();
102+
$config = [
103+
'driver' => $vaultClass::DRIVER,
104+
'name' => $friendlyName
105+
];
106+
107+
// Process each prompt
108+
foreach ($prompts as $key => $prompt) {
109+
$value = $prompt->prompt();
110+
111+
// Only store non-empty values
112+
if ($value !== '') {
113+
$config[$key] = $value;
114+
}
115+
}
116+
117+
return $config;
118+
}
119+
120+
private function saveVaultConfig(string $name, array $config): void
121+
{
122+
$vaultPath = getcwd() . "/.keep/vaults/{$name}.json";
123+
file_put_contents($vaultPath, json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
124+
}
125+
126+
private function setDefaultVault(string $vaultName): void
127+
{
128+
$settingsPath = getcwd() . '/.keep/settings.json';
129+
$settings = json_decode(file_get_contents($settingsPath), true);
130+
$settings['default_vault'] = $vaultName;
131+
file_put_contents($settingsPath, json_encode($settings, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
132+
}
133+
}

src/Commands/ConfigureCommand.php

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@
33
namespace STS\Keep\Commands;
44

55
use Illuminate\Support\Str;
6+
use STS\Keep\Commands\Concerns\ConfiguresVaults;
67
use function Laravel\Prompts\text;
78
use function Laravel\Prompts\multiselect;
89
use function Laravel\Prompts\info;
910
use function Laravel\Prompts\note;
11+
use function Laravel\Prompts\confirm;
1012

1113
class ConfigureCommand extends BaseCommand
1214
{
15+
use ConfiguresVaults;
16+
1317
protected $signature = 'configure';
1418
protected $description = 'Configure Keep settings for your project';
1519

@@ -41,14 +45,14 @@ protected function process()
4145
$stages = multiselect(
4246
label: 'Which environments/stages do you want to manage secrets for?',
4347
options: [
44-
'development' => 'Development (local dev)',
48+
'local' => 'Local (development)',
4549
'qa' => 'QA (test team validation)',
4650
'uat' => 'UAT (stakeholder testing)',
4751
'staging' => 'Staging (pre-production)',
4852
'sandbox' => 'Sandbox (demos / experiments)',
4953
'production' => 'Production (live)'
5054
],
51-
default: $existingSettings['stages'] ?? ['development', 'staging', 'production'],
55+
default: $existingSettings['stages'] ?? ['local', 'staging', 'production'],
5256
scroll: 6,
5357
hint: 'You can add more later. Toggle with space bar, confirm with enter.',
5458
);
@@ -59,13 +63,29 @@ protected function process()
5963

6064
info('✅ Configuration updated successfully!');
6165

62-
// Show next steps
63-
if (empty($this->manager->getConfiguredVaults())) {
64-
note('Next steps:');
65-
note('• Add your first vault: keep vault:add');
66-
note('• Set your first secret: keep set MY_SECRET');
66+
// Offer to create first vault if none exist (but not in non-interactive mode)
67+
if (empty($this->manager->getConfiguredVaults()) && !$this->option('no-interaction')) {
68+
info('🗄️ Vault Setup');
69+
note('You\'ll need at least one vault to store your secrets.');
70+
71+
if (confirm('Would you like to set up your first vault now?', true)) {
72+
$result = $this->configureNewVault();
73+
74+
if ($result) {
75+
note('🎉 All set! Your Keep configuration is ready to use.');
76+
note('Next step: Set your first secret with: keep set MY_SECRET');
77+
} else {
78+
note('No worries! You can add a vault later with: keep vault:add');
79+
}
80+
} else {
81+
note('No worries! You can add a vault later with: keep vault:add');
82+
}
6783
} else {
68-
note('Your configuration has been updated.');
84+
if (empty($this->manager->getConfiguredVaults())) {
85+
note('Next steps:');
86+
note('• Add your first vault: keep vault:add');
87+
note('• Set your first secret: keep set MY_SECRET');
88+
}
6989
}
7090
}
7191

0 commit comments

Comments
 (0)