1+ <?php
2+
3+ namespace Neuron \Cli \Commands \Secrets \Key ;
4+
5+ use Neuron \Cli \Commands \Command ;
6+ use Neuron \Data \Settings \SecretManager ;
7+
8+ /**
9+ * Generate encryption key command
10+ *
11+ * Generates a new encryption key for securing credentials.
12+ * Keys are cryptographically secure random values.
13+ *
14+ * Usage:
15+ * neuron secrets:key:generate # Generate master key
16+ * neuron secrets:key:generate --env=production # Generate production key
17+ * neuron secrets:key:generate --force # Overwrite existing key
18+ *
19+ * @package Neuron\Cli\Commands\Secrets\Key
20+ */
21+ class GenerateCommand extends Command
22+ {
23+ private SecretManager $ secretManager ;
24+
25+ /**
26+ * @inheritDoc
27+ */
28+ public function getName (): string
29+ {
30+ return 'secrets:key:generate ' ;
31+ }
32+
33+ /**
34+ * @inheritDoc
35+ */
36+ public function getDescription (): string
37+ {
38+ return 'Generate a new encryption key for secrets ' ;
39+ }
40+
41+ /**
42+ * @inheritDoc
43+ */
44+ public function configure (): void
45+ {
46+ $ this ->addOption ( 'env ' , 'e ' , true , 'Environment for the key (default: master key) ' );
47+ $ this ->addOption ( 'config ' , 'c ' , true , 'Config directory path (default: config) ' );
48+ $ this ->addOption ( 'force ' , 'f ' , false , 'Overwrite existing key file ' );
49+ $ this ->addOption ( 'show ' , 's ' , false , 'Display the generated key ' );
50+ }
51+
52+ /**
53+ * @inheritDoc
54+ */
55+ public function execute (): int
56+ {
57+ $ configPath = $ this ->input ->getOption ( 'config ' , 'config ' );
58+ $ env = $ this ->input ->getOption ( 'env ' );
59+ $ force = $ this ->input ->hasOption ( 'force ' );
60+ $ show = $ this ->input ->hasOption ( 'show ' );
61+
62+ // Determine key path based on environment
63+ if ( $ env )
64+ {
65+ $ keyPath = $ configPath . '/secrets/ ' . $ env . '.key ' ;
66+ $ keyName = $ env . ' environment key ' ;
67+
68+ // Ensure directory exists
69+ $ dir = dirname ( $ keyPath );
70+ if ( !is_dir ( $ dir ) )
71+ {
72+ if ( !mkdir ( $ dir , 0755 , true ) )
73+ {
74+ $ this ->output ->error ( "Failed to create directory: {$ dir }" );
75+ return 1 ;
76+ }
77+ }
78+ }
79+ else
80+ {
81+ $ keyPath = $ configPath . '/master.key ' ;
82+ $ keyName = 'master key ' ;
83+ }
84+
85+ // Check if key already exists
86+ if ( file_exists ( $ keyPath ) && !$ force )
87+ {
88+ $ this ->output ->error ( "Key file already exists: {$ keyPath }" );
89+ $ this ->output ->info ( "Use --force to overwrite the existing key. " );
90+ $ this ->output ->warning ( "WARNING: Overwriting will make existing encrypted files unreadable! " );
91+ return 1 ;
92+ }
93+
94+ // Warn about overwriting
95+ if ( file_exists ( $ keyPath ) && $ force )
96+ {
97+ $ this ->output ->warning ( "You are about to overwrite an existing key! " );
98+ $ this ->output ->warning ( "This will make any files encrypted with the old key unreadable. " );
99+
100+ if ( !$ this ->confirm ( "Are you absolutely sure you want to continue? " ) )
101+ {
102+ $ this ->output ->info ( "Operation cancelled. " );
103+ return 0 ;
104+ }
105+ }
106+
107+ // Create SecretManager and generate key
108+ $ this ->secretManager = new SecretManager ();
109+
110+ try
111+ {
112+ $ key = $ this ->secretManager ->generateKey ( $ keyPath , $ force );
113+
114+ $ this ->output ->success ( "Generated {$ keyName } at: {$ keyPath }" );
115+
116+ // Show the key if requested
117+ if ( $ show )
118+ {
119+ $ this ->output ->newLine ();
120+ $ this ->output ->section ( "Generated Key " );
121+ $ this ->output ->write ( $ key );
122+ $ this ->output ->newLine ();
123+ $ this ->output ->warning ( "This key is shown only once. Store it securely! " );
124+ }
125+
126+ // Display instructions
127+ $ this ->output ->newLine ();
128+ $ this ->output ->info ( "Next steps: " );
129+ $ this ->output ->write ( "1. Add {$ keyPath } to .gitignore (NEVER commit this file) " );
130+ $ this ->output ->write ( "2. Share this key securely with your team " );
131+ $ this ->output ->write ( "3. Use 'neuron secrets:edit " . ($ env ? " --env= {$ env }" : "" ) . "' to add secrets " );
132+
133+ // Environment variable alternative
134+ $ envVar = 'NEURON_ ' . strtoupper (
135+ str_replace ( ['/ ' , '. ' , '- ' ], '_ ' , basename ( $ keyPath , '.key ' ) )
136+ ) . '_KEY ' ;
137+ $ this ->output ->newLine ();
138+ $ this ->output ->info ( "Alternative: Set the key as an environment variable: " );
139+ $ this ->output ->write ( "export {$ envVar }= {$ key }" );
140+ }
141+ catch ( \Exception $ e )
142+ {
143+ $ this ->output ->error ( "Error generating key: " . $ e ->getMessage () );
144+
145+ if ( $ this ->input ->hasOption ( 'verbose ' ) )
146+ {
147+ $ this ->output ->write ( $ e ->getTraceAsString () );
148+ }
149+
150+ return 1 ;
151+ }
152+
153+ return 0 ;
154+ }
155+ }
0 commit comments