diff --git a/README.md b/README.md index db1d531..bf53634 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Features - Creating new users with roles - Deleting existing users - Extending the validity of existing users -- Resetting passwords of existing users +- Resetting passwords (random or specific) of existing users - Listing existing users and their associated roles - Listing all configured hosts - Listing existing roles diff --git a/cmd/add.go b/cmd/add.go index 5f67685..a26b130 100644 --- a/cmd/add.go +++ b/cmd/add.go @@ -14,7 +14,7 @@ var addCmd = &cobra.Command{ Short: "Add a database user", Long: `Add a database user to a specific database`, Run: func(cmd *cobra.Command, args []string) { - username, conn := givenUserModification(cmd, args, false) + username, conn := givenUserModification(cmd, 1, args, false) roles, err := cmd.Flags().GetStringSlice("roles") if err != nil { diff --git a/cmd/delete.go b/cmd/delete.go index 773ac59..1f95b92 100644 --- a/cmd/delete.go +++ b/cmd/delete.go @@ -13,7 +13,7 @@ var deleteCmd = &cobra.Command{ Short: "Delete a database user", Long: `Delete a database user from a specific database`, Run: func(cmd *cobra.Command, args []string) { - username, conn := givenUserModification(cmd, args, true) + username, conn := givenUserModification(cmd, 1, args, true) err := conn.DeleteUser(username) if err != nil { diff --git a/cmd/extend.go b/cmd/extend.go index a6dc40c..b133fa3 100644 --- a/cmd/extend.go +++ b/cmd/extend.go @@ -14,7 +14,7 @@ var extendCmd = &cobra.Command{ Short: "Extend a database user's valid", Long: `Extend a database user's valid until`, Run: func(cmd *cobra.Command, args []string) { - username, conn := givenUserModification(cmd, args, true) + username, conn := givenUserModification(cmd, 1, args, true) validUntil, err := conn.ExtendUser(username, DefaultValidUntil) if err != nil { diff --git a/cmd/password.go b/cmd/password.go new file mode 100644 index 0000000..a233cb0 --- /dev/null +++ b/cmd/password.go @@ -0,0 +1,63 @@ +package cmd + +import ( + "encoding/json" + "fmt" + "github.com/spf13/cobra" + "os" + "strings" +) + +var passwordCmd = &cobra.Command{ + Use: "password [username] [password]", + Short: "Set a database user's password", + Long: `Set a database user's password`, + Run: func(cmd *cobra.Command, args []string) { + username, conn := givenUserModification(cmd, 2, args, true) + password := args[1] + + password, err := conn.SetPassword(username, password) + if err != nil { + cmd.Println(fmt.Errorf("could not set password for user: %v", err)) + os.Exit(1) + } + + user, err := conn.GetUser(username) + if err != nil { + cmd.Println(fmt.Errorf("could not get user after password set: %v", err)) + os.Exit(1) + } + + output := getOutputType(cmd) + if output == OutputTypeJson { + outputSetPasswordJson(cmd, password) + } else if output == OutputTypeTable { + cmd.Println(fmt.Sprintf("Successfully set user password for %s in %s", username, conn.Config.Host)) + cmd.Println("\ncredentials") + cmd.Println("Valid until:", *user.ValidUntil) + cmd.Println("Username:", username) + cmd.Println("Password:", password) + cmd.Println("Hostname:", conn.Config.Host) + cmd.Println("Port:", conn.Config.Port) + cmd.Println("Database:", conn.Config.Database) + } + }, +} + +func outputSetPasswordJson(cmd *cobra.Command, password string) { + b, err := json.MarshalIndent(struct { + Password string `json:"password"` + }{ + Password: password, + }, "", strings.Repeat(" ", 4)) + if err != nil { + panic(err) + } + + cmd.Println(string(b)) +} + +func init() { + rootCmd.AddCommand(passwordCmd) + addRequiredHostFlag(passwordCmd) +} diff --git a/cmd/reset.go b/cmd/reset.go index 9551bbf..91bf6b6 100644 --- a/cmd/reset.go +++ b/cmd/reset.go @@ -10,10 +10,10 @@ import ( var resetCmd = &cobra.Command{ Use: "reset [username]", - Short: "Reset a database users password", - Long: `Reset a database users password`, + Short: "Reset a database user's password", + Long: `Reset a database user's password`, Run: func(cmd *cobra.Command, args []string) { - username, conn := givenUserModification(cmd, args, true) + username, conn := givenUserModification(cmd, 1, args, true) password, err := conn.ResetPassword(username) if err != nil { diff --git a/cmd/root.go b/cmd/root.go index 97ec7e5..a64b7a0 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -39,8 +39,8 @@ func addRequiredHostFlag(cmd *cobra.Command) { } } -func givenUserModification(cmd *cobra.Command, args []string, userMustExist bool) (string, *database.DBConn) { - if len(args) != 1 { +func givenUserModification(cmd *cobra.Command, argsLen int, args []string, userMustExist bool) (string, *database.DBConn) { + if len(args) != argsLen { cmd.Println(cmd.UsageString()) os.Exit(1) } diff --git a/database/password.go b/database/password.go index f0c6432..1d94cc2 100644 --- a/database/password.go +++ b/database/password.go @@ -19,6 +19,6 @@ func StringWithCharset(length int, charset string) string { return string(b) } -func String(length int) string { +func RandString(length int) string { return StringWithCharset(length, charset) } diff --git a/database/query.go b/database/query.go index 5588932..67d71b4 100644 --- a/database/query.go +++ b/database/query.go @@ -81,7 +81,7 @@ func (conn *DBConn) GetAllRoles() ([]string, error) { func (conn *DBConn) CreateUser(username string, validDuration time.Duration, roles []string) (string, time.Time, error) { tx := conn.db.MustBegin() - password := String(15) + password := RandString(15) validUntil := time.Now().Add(validDuration) @@ -133,9 +133,11 @@ func (conn *DBConn) ExtendUser(username string, validDuration time.Duration) (ti } func (conn *DBConn) ResetPassword(username string) (string, error) { - tx := conn.db.MustBegin() + return conn.SetPassword(username, RandString(15)) +} - password := String(15) +func (conn *DBConn) SetPassword(username string, password string) (string, error) { + tx := conn.db.MustBegin() sql := fmt.Sprintf("ALTER USER \"%s\" WITH PASSWORD '%s'", username, password) tx.MustExec(sql)