aws-mgmt-go/internal/cmd/secrets.go
2024-12-29 16:38:20 -05:00

169 lines
3.8 KiB
Go

package cmd
import (
"fmt"
"os"
"strings"
"time"
"github.com/charmbracelet/huh"
"github.com/pquerna/otp/totp"
cli "github.com/urfave/cli/v2"
"aws-mgmt/pkg/client"
)
const secretsDesc = `View secrets from AWS Secrets Manager.`
var SecretsApp = &cli.App{
Name: "secrets",
Usage: "view secrets",
UsageText: "secrets [options]",
HideHelpCommand: true,
Args: true,
ArgsUsage: "[secret]",
Action: secretsFunc,
Description: secretsDesc,
Flags: flags,
}
var SecretsCommand = &cli.Command{
Name: "secrets",
Usage: "view secrets",
UsageText: "secrets [options]",
HideHelpCommand: true,
Args: true,
ArgsUsage: "[secret]",
Action: secretsFunc,
Description: secretsDesc,
Flags: flags,
}
var flags = []cli.Flag{
&cli.BoolFlag{
Name: "sh",
Usage: "Print the equivalent AWS CLI / shell command(s) to stderr",
},
&cli.StringSliceFlag{
Name: "tags",
Usage: "Specify tags to filter secrets, like --tags type=totp",
},
&cli.BoolFlag{
Name: "totp",
Usage: "Generate a MFA token based on the secret value",
},
}
func secretsFunc(c *cli.Context) error {
// create aws config
cfg, err := client.CreateAWSConfig()
if err != nil {
return err
}
awsClient := client.NewAWSClient(
client.SetConfig(cfg),
)
// get hostname & domain
secret := c.Args().Get(0)
tags := parseTags(c.StringSlice("tags"))
if secret == "" {
secrets, err := awsClient.ListSecrets(tags)
if err != nil {
return err
}
if c.Bool("sh") {
tags_str := ""
for k, v := range tags {
tags_str += fmt.Sprintf("--filters Name=tag:%s,Values=%s ", k, v)
}
// remove trailing space
tags_str = strings.TrimRight(tags_str, " ")
str := fmt.Sprintf(
"aws secretsmanager list-secrets --query 'SecretList[].[Name,Description]' %s --output text | sort",
tags_str,
)
fmt.Fprintln(os.Stderr, str)
}
if len(secrets) == 0 {
return fmt.Errorf("no secrets found")
}
secret, err = selectSecretDialog(secrets)
if err != nil {
return err
}
}
// get secret value for secretName
secretValue, err := awsClient.GetSecretValue(secret)
if err != nil {
return err
}
// if the totp flag is set, generate a token
if c.Bool("totp") {
token, err := totp.GenerateCode(secretValue, time.Now())
if err != nil {
return err
}
fmt.Println(token)
} else {
// print the aws cli equivalent command to output this secret to stderr
if c.Bool("sh") {
fmt.Fprintln(os.Stderr, fmt.Sprintf("aws secretsmanager get-secret-value --secret-id %s --query SecretString --output text", secret))
}
fmt.Println(secretValue)
}
return nil
}
func parseTags(rawTags []string) map[string]string {
tags := make(map[string]string)
for _, tag := range rawTags {
parts := strings.SplitN(tag, "=", 2)
if len(parts) == 2 {
tags[parts[0]] = parts[1]
}
}
return tags
}
// selectSecretDialog prompts the user to select a secret from a list of SecretListEntry
func selectSecretDialog(secretList []client.SecretListEntry) (string, error) {
// create a select prompt for the user to choose a secret
var secretName string
options := make([]huh.Option[string], len(secretList))
// options := make([]huh.Option, len(secretList))
for i, secret := range secretList {
// create a new option for each secret
options[i] = huh.NewOption[string](strings.TrimSuffix(fmt.Sprintf("%s: %s", secret.Name, secret.Description), ": "), secret.Name)
}
form := huh.NewForm(
huh.NewGroup(
huh.NewSelect[string]().
Title("Select a secret").
Options(options...).
Value(&secretName),
),
)
err := form.Run()
if err != nil {
return "", err
}
/*
huh.NewSelect[string]().
Title("Select a secret").
Options(options...).
Value(&secretName).
Run()
*/
return secretName, nil
}