stillbox/pkg/cmd/admin/admin.go

196 lines
3.9 KiB
Go
Raw Normal View History

2024-07-15 10:12:53 -04:00
package admin
import (
"context"
"errors"
"fmt"
"syscall"
2024-11-03 07:19:03 -05:00
"dynatron.me/x/stillbox/pkg/config"
"dynatron.me/x/stillbox/pkg/database"
2024-11-24 10:38:19 -05:00
"github.com/urfave/cli/v2"
2024-07-15 10:12:53 -04:00
"golang.org/x/crypto/bcrypt"
"golang.org/x/term"
)
const (
PromptPassword = "Password: "
PromptAgain = "Again: "
)
var (
ErrDontMatch = errors.New("passwords do not match")
ErrInvalidArguments = errors.New("invalid arguments")
)
2024-07-29 00:29:16 -04:00
// AddUser adds a new user to the database. It asks for the password on the terminal.
2024-07-24 07:55:36 -04:00
func AddUser(ctx context.Context, username, email string, isAdmin bool) error {
2024-07-15 10:12:53 -04:00
if username == "" || email == "" {
return ErrInvalidArguments
}
2024-07-24 07:55:36 -04:00
db := database.FromCtx(ctx)
2024-07-15 10:12:53 -04:00
pw, err := readPassword(PromptPassword)
if err != nil {
return err
}
pwAgain, err := readPassword(PromptAgain)
if err != nil {
return err
}
if pwAgain != pw {
return ErrDontMatch
}
if pw == "" {
return ErrInvalidArguments
}
hashpw, err := bcrypt.GenerateFromPassword([]byte(pw), bcrypt.DefaultCost)
2024-08-11 14:48:17 -04:00
if err != nil {
return err
}
2024-07-15 10:12:53 -04:00
2024-07-23 21:35:02 -04:00
_, err = db.CreateUser(context.Background(), database.CreateUserParams{
2024-07-15 10:12:53 -04:00
Username: username,
Password: string(hashpw),
Email: email,
2024-07-27 19:25:16 -04:00
IsAdmin: isAdmin,
2024-07-15 10:12:53 -04:00
})
return err
}
2024-07-29 00:29:16 -04:00
// Passwd changes a user's password. It asks for the password on the terminal.
2024-07-24 07:55:36 -04:00
func Passwd(ctx context.Context, username string) error {
if username == "" {
return ErrInvalidArguments
}
2024-07-24 07:55:36 -04:00
db := database.FromCtx(ctx)
2024-07-24 07:55:36 -04:00
_, err := db.GetUserByUsername(ctx, username)
2024-07-23 21:35:02 -04:00
if err != nil && database.IsNoRows(err) {
return fmt.Errorf("no such user %s", username)
}
if err != nil {
return err
}
pw, err := readPassword(PromptPassword)
if err != nil {
return err
}
pwAgain, err := readPassword(PromptAgain)
if err != nil {
return err
}
if pwAgain != pw {
return ErrDontMatch
}
if pw == "" {
return ErrInvalidArguments
}
hashpw, err := bcrypt.GenerateFromPassword([]byte(pw), bcrypt.DefaultCost)
2024-08-11 14:48:17 -04:00
if err != nil {
return err
}
2024-07-24 07:55:36 -04:00
return db.UpdatePassword(context.Background(), username, string(hashpw))
}
2024-07-15 10:12:53 -04:00
func readPassword(prompt string) (string, error) {
fmt.Print(prompt)
pw, err := term.ReadPassword(int(syscall.Stdin))
fmt.Println()
return string(pw), err
}
2024-07-29 00:29:16 -04:00
// Command is the users command.
2024-11-24 10:38:19 -05:00
func Command(cfg *config.Configuration) *cli.Command {
c := &cfg.Config
userCmd := &cli.Command{
Name: "users",
Aliases: []string{"u"},
Usage: "administers users",
Subcommands: []*cli.Command{
addUserCommand(c),
passwdCommand(c),
},
2024-07-15 10:12:53 -04:00
}
2024-11-24 10:38:19 -05:00
return userCmd
2024-07-15 10:12:53 -04:00
}
2024-11-24 10:38:19 -05:00
func addUserCommand(cfg *config.Config) *cli.Command {
c := &cli.Command{
Name: "add",
Description: "adds a user",
UsageText: "stillbox users add [-a] [-m email] [username]",
Args: true,
Action: func(ctx *cli.Context) error {
if ctx.Args().Len() != 1 {
return errors.New(ctx.Command.Usage)
2024-07-24 07:55:36 -04:00
}
2024-11-24 10:38:19 -05:00
db, err := database.NewClient(context.Background(), cfg.DB)
2024-07-15 10:12:53 -04:00
if err != nil {
return err
}
2024-11-24 10:38:19 -05:00
username := ctx.Args().Get(0)
isAdmin := ctx.Bool("admin")
email := ctx.String("email")
2024-07-24 07:55:36 -04:00
return AddUser(database.CtxWithDB(context.Background(), db), username, email, isAdmin)
2024-07-15 10:12:53 -04:00
},
2024-11-24 10:38:19 -05:00
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "admin",
Aliases: []string{"a"},
Value: false,
Usage: "user is an admin",
},
&cli.StringFlag{
Name: "email",
Usage: "email address",
Aliases: []string{"m"},
},
},
2024-07-15 10:12:53 -04:00
}
return c
}
2024-11-24 10:38:19 -05:00
func passwdCommand(cfg *config.Config) *cli.Command {
c := &cli.Command{
Name: "passwd",
Usage: "changes password for a user",
UsageText: "stillbox users passwd [username]",
Args: true,
Action: func(ctx *cli.Context) error {
if ctx.Args().Len() != 1 {
return errors.New(ctx.Command.Usage)
}
2024-10-21 14:11:08 -04:00
db, err := database.NewClient(context.Background(), cfg.DB)
2024-07-24 07:55:36 -04:00
if err != nil {
return err
}
2024-11-24 10:38:19 -05:00
username := ctx.Args().Get(0)
2024-07-24 07:55:36 -04:00
return Passwd(database.CtxWithDB(context.Background(), db), username)
},
}
return c
}