Auth: Refactor user management commands #98
This commit is contained in:
parent
5cec098524
commit
fa8e02b430
7 changed files with 92 additions and 67 deletions
|
@ -70,10 +70,10 @@ func main() {
|
||||||
commands.RestoreCommand,
|
commands.RestoreCommand,
|
||||||
commands.ResetCommand,
|
commands.ResetCommand,
|
||||||
commands.ConfigCommand,
|
commands.ConfigCommand,
|
||||||
|
commands.UsersCommand,
|
||||||
commands.PasswdCommand,
|
commands.PasswdCommand,
|
||||||
commands.VersionCommand,
|
commands.VersionCommand,
|
||||||
commands.StatusCommand,
|
commands.StatusCommand,
|
||||||
commands.UserCommand,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := app.Run(os.Args); err != nil {
|
if err := app.Run(os.Args); err != nil {
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
// FacesCommand registers the faces cli command.
|
// FacesCommand registers the faces cli command.
|
||||||
var FacesCommand = cli.Command{
|
var FacesCommand = cli.Command{
|
||||||
Name: "faces",
|
Name: "faces",
|
||||||
Usage: "Runs facial recognition commands",
|
Usage: "Facial recognition sub-commands",
|
||||||
Subcommands: []cli.Command{
|
Subcommands: []cli.Command{
|
||||||
{
|
{
|
||||||
Name: "stats",
|
Name: "stats",
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/manifoldco/promptui"
|
||||||
"github.com/photoprism/photoprism/internal/config"
|
"github.com/photoprism/photoprism/internal/config"
|
||||||
"github.com/photoprism/photoprism/internal/entity"
|
"github.com/photoprism/photoprism/internal/entity"
|
||||||
"github.com/photoprism/photoprism/internal/form"
|
"github.com/photoprism/photoprism/internal/form"
|
||||||
|
@ -16,15 +17,20 @@ import (
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserCommand Create, List, Update and Delete Users.
|
// UsersCommand registers user management commands.
|
||||||
var UserCommand = cli.Command{
|
var UsersCommand = cli.Command{
|
||||||
Name: "users",
|
Name: "users",
|
||||||
Usage: "Manage Users from CLI",
|
Usage: "User management sub-commands",
|
||||||
Subcommands: []cli.Command{
|
Subcommands: []cli.Command{
|
||||||
|
{
|
||||||
|
Name: "list",
|
||||||
|
Usage: "lists registered users",
|
||||||
|
Action: usersListAction,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "add",
|
Name: "add",
|
||||||
Usage: "creates a new user. Provide at least username and password",
|
Usage: "adds a new user",
|
||||||
Action: userAdd,
|
Action: usersAddAction,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "fullname, n",
|
Name: "fullname, n",
|
||||||
|
@ -45,9 +51,9 @@ var UserCommand = cli.Command{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "modify",
|
Name: "update",
|
||||||
Usage: "modify a users information.",
|
Usage: "updates user information",
|
||||||
Action: userModify,
|
Action: usersModifyAction,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "fullname, n",
|
Name: "fullname, n",
|
||||||
|
@ -69,26 +75,15 @@ var UserCommand = cli.Command{
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "delete",
|
Name: "delete",
|
||||||
Usage: "deletes user by username",
|
Usage: "deletes an existing user",
|
||||||
Action: userDelete,
|
Action: usersDeleteAction,
|
||||||
ArgsUsage: "takes username as argument",
|
ArgsUsage: "[username]",
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "force, f",
|
|
||||||
Usage: "execute deletion",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "list",
|
|
||||||
Usage: "prints a list of all users",
|
|
||||||
Action: userList,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func userAdd(ctx *cli.Context) error {
|
func usersAddAction(ctx *cli.Context) error {
|
||||||
return withDependencies(ctx, func(conf *config.Config) error {
|
return callWithDependencies(ctx, func(conf *config.Config) error {
|
||||||
|
|
||||||
uc := form.UserCreate{
|
uc := form.UserCreate{
|
||||||
UserName: strings.TrimSpace(ctx.String("username")),
|
UserName: strings.TrimSpace(ctx.String("username")),
|
||||||
|
@ -150,47 +145,58 @@ func userAdd(ctx *cli.Context) error {
|
||||||
if err := entity.CreateWithPassword(uc); err != nil {
|
if err := entity.CreateWithPassword(uc); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func userDelete(ctx *cli.Context) error {
|
func usersDeleteAction(ctx *cli.Context) error {
|
||||||
return withDependencies(ctx, func(conf *config.Config) error {
|
return callWithDependencies(ctx, func(conf *config.Config) error {
|
||||||
username := ctx.Args()[0]
|
userName := strings.TrimSpace(ctx.Args().First())
|
||||||
if !ctx.Bool("force") {
|
|
||||||
user := entity.FindUserByName(username)
|
if userName == "" {
|
||||||
if user != nil {
|
return errors.New("please provide a username")
|
||||||
log.Infof("found user %s with uid: %s. Use -f to perform actual deletion\n", user.UserName, user.UserUID)
|
}
|
||||||
return nil
|
|
||||||
|
actionPrompt := promptui.Prompt{
|
||||||
|
Label: fmt.Sprintf("Delete %s?", txt.Quote(userName)),
|
||||||
|
IsConfirm: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := actionPrompt.Run(); err == nil {
|
||||||
|
if m := entity.FindUserByName(userName); m == nil {
|
||||||
|
return errors.New("user not found")
|
||||||
|
} else if err := m.Delete(); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
log.Infof("%s deleted", txt.Quote(userName))
|
||||||
}
|
}
|
||||||
return errors.New("user not found")
|
} else {
|
||||||
|
log.Infof("keeping user")
|
||||||
}
|
}
|
||||||
err := query.DeleteUserByName(username)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("%s\n", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
log.Infof("sucessfully deleted %s\n", username)
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func userList(ctx *cli.Context) error {
|
func usersListAction(ctx *cli.Context) error {
|
||||||
return withDependencies(ctx, func(conf *config.Config) error {
|
return callWithDependencies(ctx, func(conf *config.Config) error {
|
||||||
users := query.AllUsers()
|
users := query.RegisteredUsers()
|
||||||
fmt.Printf("%-16s %-16s %-16s\n", "Username", "Full Name", "Email")
|
log.Infof("found %d users", len(users))
|
||||||
fmt.Printf("%-16s %-16s %-16s\n", "--------", "---------", "-----")
|
|
||||||
|
fmt.Printf("%-4s %-16s %-16s %-16s\n", "ID", "LOGIN", "NAME", "EMAIL")
|
||||||
|
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
fmt.Printf("%-16s %-16s %-16s", user.UserName, user.FullName, user.PrimaryEmail)
|
fmt.Printf("%-4d %-16s %-16s %-16s", user.ID, user.UserName, user.FullName, user.PrimaryEmail)
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
}
|
}
|
||||||
fmt.Printf("total users found: %v\n", len(users))
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func userModify(ctx *cli.Context) error {
|
func usersModifyAction(ctx *cli.Context) error {
|
||||||
return withDependencies(ctx, func(conf *config.Config) error {
|
return callWithDependencies(ctx, func(conf *config.Config) error {
|
||||||
username := ctx.Args().First()
|
username := ctx.Args().First()
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return errors.New("pass username as argument")
|
return errors.New("pass username as argument")
|
||||||
|
@ -231,16 +237,18 @@ func userModify(ctx *cli.Context) error {
|
||||||
if err := u.Validate(); err != nil {
|
if err := u.Validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := u.Save(); err != nil {
|
if err := u.Save(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("user successfully updated: %v\n", u.UserName)
|
fmt.Printf("user successfully updated: %v\n", u.UserName)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func withDependencies(ctx *cli.Context, f func(conf *config.Config) error) error {
|
func callWithDependencies(ctx *cli.Context, f func(conf *config.Config) error) error {
|
||||||
conf := config.NewConfig(ctx)
|
conf := config.NewConfig(ctx)
|
||||||
|
|
||||||
_, cancel := context.WithCancel(context.Background())
|
_, cancel := context.WithCancel(context.Background())
|
||||||
|
@ -253,9 +261,10 @@ func withDependencies(ctx *cli.Context, f func(conf *config.Config) error) error
|
||||||
conf.InitDb()
|
conf.InitDb()
|
||||||
defer conf.Shutdown()
|
defer conf.Shutdown()
|
||||||
|
|
||||||
// command is executed here
|
// Run command.
|
||||||
if err := f(conf); err != nil {
|
if err := f(conf); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
|
@ -371,12 +371,12 @@ var GlobalFlags = []cli.Flag{
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "download-token",
|
Name: "download-token",
|
||||||
Usage: "optional static `SECRET` url token for file downloads",
|
Usage: "static url `TOKEN` for original file downloads",
|
||||||
EnvVar: "PHOTOPRISM_DOWNLOAD_TOKEN",
|
EnvVar: "PHOTOPRISM_DOWNLOAD_TOKEN",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "preview-token",
|
Name: "preview-token",
|
||||||
Usage: "optional static `SECRET` url token for preview images and video streaming",
|
Usage: "static url `TOKEN` for thumbnails and video streaming",
|
||||||
EnvVar: "PHOTOPRISM_PREVIEW_TOKEN",
|
EnvVar: "PHOTOPRISM_PREVIEW_TOKEN",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
|
|
|
@ -195,6 +195,20 @@ func FindUserByUID(uid string) *User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete marks the entity as deleted.
|
||||||
|
func (m *User) Delete() error {
|
||||||
|
if m.ID <= 1 {
|
||||||
|
return fmt.Errorf("can't delete system user")
|
||||||
|
}
|
||||||
|
|
||||||
|
return Db().Delete(m).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deleted tests if the entity is marked as deleted.
|
||||||
|
func (m *User) Deleted() bool {
|
||||||
|
return m.DeletedAt != nil
|
||||||
|
}
|
||||||
|
|
||||||
// String returns an identifier that can be used in logs.
|
// String returns an identifier that can be used in logs.
|
||||||
func (m *User) String() string {
|
func (m *User) String() string {
|
||||||
if m.UserName != "" {
|
if m.UserName != "" {
|
||||||
|
|
|
@ -22,12 +22,11 @@ func DeleteUserByName(userName string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllUsers Returns a list of all registered Users.
|
// RegisteredUsers finds all registered users.
|
||||||
func AllUsers() []entity.User {
|
func RegisteredUsers() (result entity.Users) {
|
||||||
var users []entity.User
|
if err := Db().Where("id > 0").Find(&result).Error; err != nil {
|
||||||
if err := Db().Find(&users).Error; err != nil {
|
log.Errorf("users: %s", err)
|
||||||
log.Error(err)
|
|
||||||
return []entity.User{}
|
|
||||||
}
|
}
|
||||||
return users
|
|
||||||
|
return result
|
||||||
}
|
}
|
|
@ -30,13 +30,16 @@ func TestDeleteUserByName(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAllUsers(t *testing.T) {
|
func TestRegisteredUsers(t *testing.T) {
|
||||||
t.Run("list all", func(t *testing.T) {
|
t.Run("success", func(t *testing.T) {
|
||||||
users := AllUsers()
|
users := RegisteredUsers()
|
||||||
|
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
log.Infof("user: %v, %s, %s, %s", user.ID, user.UserUID, user.UserName, user.FullName)
|
t.Logf("user: %v, %s, %s, %s", user.ID, user.UserUID, user.UserName, user.FullName)
|
||||||
}
|
}
|
||||||
log.Infof("user count: %v", len(users))
|
|
||||||
assert.Greater(t, len(users), 3)
|
t.Logf("user count: %v", len(users))
|
||||||
|
|
||||||
|
assert.GreaterOrEqual(t, len(users), 3)
|
||||||
})
|
})
|
||||||
}
|
}
|
Loading…
Reference in a new issue