Skip to content

Commit 5364139

Browse files
cevianclaude
andcommitted
Implement db save-password command with role support
Add new command to save database passwords with support for different database roles. Passwords can be provided via three methods: 1. --password=value flag (highest precedence) 2. TIGER_NEW_PASSWORD environment variable 3. Interactive prompt (when neither provided) The command stores passwords according to the --password-storage setting (keyring, pgpass, or none) and supports saving passwords for different database roles independently. Features: - Save passwords for any database role (default: tsdbadmin) - Three input methods with clear precedence - Integration with existing password storage system - Comprehensive test coverage for all input methods Example usage: tiger db save-password svc-12345 --password=mypass tiger db save-password svc-12345 --role readonly tiger db save-password svc-12345 # prompts interactively 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 4bf4c2b commit 5364139

File tree

3 files changed

+582
-10
lines changed

3 files changed

+582
-10
lines changed

internal/tiger/cmd/db.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cmd
22

33
import (
4+
"bufio"
45
"context"
56
"errors"
67
"fmt"
@@ -20,6 +21,9 @@ import (
2021
var (
2122
// getAPIKeyForDB can be overridden for testing
2223
getAPIKeyForDB = config.GetAPIKey
24+
25+
// getServiceDetailsFunc can be overridden for testing
26+
getServiceDetailsFunc = getServiceDetails
2327
)
2428

2529
func buildDbConnectionStringCmd() *cobra.Command {
@@ -229,6 +233,98 @@ Examples:
229233
return cmd
230234
}
231235

236+
func buildDbSavePasswordCmd() *cobra.Command {
237+
var dbSavePasswordRole string
238+
var dbSavePasswordValue string
239+
240+
cmd := &cobra.Command{
241+
Use: "save-password [service-id]",
242+
Short: "Save password for a database service",
243+
Long: `Save a password for a database service to configured password storage.
244+
245+
The service ID can be provided as an argument or will use the default service
246+
from your configuration. The password can be provided via:
247+
1. --password flag with explicit value (highest precedence)
248+
2. TIGER_NEW_PASSWORD environment variable
249+
3. Interactive prompt (if neither provided)
250+
251+
The password will be saved according to your --password-storage setting
252+
(keyring, pgpass, or none).
253+
254+
Examples:
255+
# Save password with explicit value (highest precedence)
256+
tiger db save-password svc-12345 --password=your-password
257+
258+
# Using environment variable
259+
export TIGER_NEW_PASSWORD=your-password
260+
tiger db save-password svc-12345
261+
262+
# Interactive password prompt (when neither flag nor env var provided)
263+
tiger db save-password svc-12345
264+
265+
# Save password for custom role
266+
tiger db save-password svc-12345 --password=your-password --role readonly
267+
268+
# Save to specific storage location
269+
tiger db save-password svc-12345 --password=your-password --password-storage pgpass`,
270+
RunE: func(cmd *cobra.Command, args []string) error {
271+
service, err := getServiceDetailsFunc(cmd, args)
272+
if err != nil {
273+
return err
274+
}
275+
276+
// Determine password based on precedence:
277+
// 1. --password flag with value
278+
// 2. TIGER_NEW_PASSWORD environment variable
279+
// 3. Interactive prompt
280+
var passwordToSave string
281+
282+
if cmd.Flags().Changed("password") {
283+
// --password flag was provided
284+
passwordToSave = dbSavePasswordValue
285+
if passwordToSave == "" {
286+
return fmt.Errorf("password cannot be empty when provided via --password flag")
287+
}
288+
} else if envPassword := os.Getenv("TIGER_NEW_PASSWORD"); envPassword != "" {
289+
// Use environment variable
290+
passwordToSave = envPassword
291+
} else {
292+
// Interactive prompt
293+
fmt.Fprint(cmd.OutOrStdout(), "Enter password: ")
294+
scanner := bufio.NewScanner(cmd.InOrStdin())
295+
if scanner.Scan() {
296+
passwordToSave = scanner.Text()
297+
} else {
298+
if err := scanner.Err(); err != nil {
299+
return fmt.Errorf("failed to read password: %w", err)
300+
}
301+
return fmt.Errorf("failed to read password: EOF")
302+
}
303+
if passwordToSave == "" {
304+
return fmt.Errorf("password cannot be empty")
305+
}
306+
}
307+
308+
// Save password using configured storage
309+
storage := password.GetPasswordStorage()
310+
err = storage.Save(service, passwordToSave, dbSavePasswordRole)
311+
if err != nil {
312+
return fmt.Errorf("failed to save password: %w", err)
313+
}
314+
315+
fmt.Fprintf(cmd.OutOrStdout(), "Password saved successfully for service %s (role: %s)\n",
316+
*service.ServiceId, dbSavePasswordRole)
317+
return nil
318+
},
319+
}
320+
321+
// Add flags for db save-password command
322+
cmd.Flags().StringVar(&dbSavePasswordValue, "password", "", "Password to save")
323+
cmd.Flags().StringVar(&dbSavePasswordRole, "role", "tsdbadmin", "Database role/username")
324+
325+
return cmd
326+
}
327+
232328
func buildDbCmd() *cobra.Command {
233329
cmd := &cobra.Command{
234330
Use: "db",
@@ -239,6 +335,7 @@ func buildDbCmd() *cobra.Command {
239335
cmd.AddCommand(buildDbConnectionStringCmd())
240336
cmd.AddCommand(buildDbConnectCmd())
241337
cmd.AddCommand(buildDbTestConnectionCmd())
338+
cmd.AddCommand(buildDbSavePasswordCmd())
242339

243340
return cmd
244341
}

0 commit comments

Comments
 (0)