Add 'user {create,inspect,ls,rm}' commands

This commit is contained in:
Manfred Touron 2017-11-03 00:15:15 +01:00
parent 9fc9300c34
commit e256ac66cf
2 changed files with 140 additions and 46 deletions

23
db.go
View file

@ -37,6 +37,7 @@ type Host struct {
type User struct { type User struct {
// FIXME: use uuid for ID // FIXME: use uuid for ID
gorm.Model gorm.Model
Email string // FIXME: govalidator: email
Name string // FIXME: govalidator: min length 3, alphanum Name string // FIXME: govalidator: min length 3, alphanum
SSHKeys []SSHKey SSHKeys []SSHKey
Comment string Comment string
@ -48,7 +49,7 @@ func dbInit(db *gorm.DB) error {
db.AutoMigrate(&Host{}) db.AutoMigrate(&Host{})
db.Exec(`CREATE UNIQUE INDEX uix_keys_name ON "ssh_keys"("name") WHERE ("deleted_at" IS NULL)`) db.Exec(`CREATE UNIQUE INDEX uix_keys_name ON "ssh_keys"("name") WHERE ("deleted_at" IS NULL)`)
db.Exec(`CREATE UNIQUE INDEX uix_hosts_name ON "hosts"("name") WHERE ("deleted_at" IS NULL)`) db.Exec(`CREATE UNIQUE INDEX uix_hosts_name ON "hosts"("name") WHERE ("deleted_at" IS NULL)`)
db.Exec(`CREATE UNIQUE INDEX uix_users_name ON "users"("name") WHERE ("deleted_at" IS NULL)`) db.Exec(`CREATE UNIQUE INDEX uix_users_name ON "users"("email") WHERE ("deleted_at" IS NULL)`)
// create default ssh key // create default ssh key
var count uint var count uint
@ -160,3 +161,23 @@ func FindKeysByIdOrName(db *gorm.DB, queries []string) ([]*SSHKey, error) {
} }
return keys, nil return keys, nil
} }
func FindUserByIdOrEmail(db *gorm.DB, query string) (*User, error) {
var user User
if err := db.Where("id = ?", query).Or("email = ?", query).First(&user).Error; err != nil {
return nil, err
}
return &user, nil
}
func FindUsersByIdOrEmail(db *gorm.DB, queries []string) ([]*User, error) {
var users []*User
for _, query := range queries {
user, err := FindUserByIdOrEmail(db, query)
if err != nil {
return nil, err
}
users = append(users, user)
}
return users, nil
}

163
shell.go
View file

@ -52,15 +52,15 @@ GLOBAL OPTIONS:
app.Commands = []cli.Command{ app.Commands = []cli.Command{
{ {
Name: "host", Name: "host",
Usage: "Manage hosts", Usage: "Manages hosts",
Subcommands: []cli.Command{ Subcommands: []cli.Command{
{ {
Name: "create", Name: "create",
Usage: "Create a new host", Usage: "Creates a new host",
ArgsUsage: "<user>[:<password>]@<host>[:<port>]", ArgsUsage: "<user>[:<password>]@<host>[:<port>]",
Description: "$> host create bart@foo.org\n $> host create bob:marley@example.com:2222", Description: "$> host create bart@foo.org\n $> host create bob:marley@example.com:2222",
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{Name: "name", Usage: "Assign a name to the host"}, cli.StringFlag{Name: "name", Usage: "Assigns a name to the host"},
cli.StringFlag{Name: "password", Usage: "If present, sshportal will use password-based authentication"}, cli.StringFlag{Name: "password", Usage: "If present, sshportal will use password-based authentication"},
cli.StringFlag{Name: "fingerprint", Usage: "SSH host key fingerprint"}, cli.StringFlag{Name: "fingerprint", Usage: "SSH host key fingerprint"},
cli.StringFlag{Name: "comment"}, cli.StringFlag{Name: "comment"},
@ -107,10 +107,9 @@ GLOBAL OPTIONS:
fmt.Fprintf(s, "%d\n", host.ID) fmt.Fprintf(s, "%d\n", host.ID)
return nil return nil
}, },
}, }, {
{
Name: "inspect", Name: "inspect",
Usage: "Display detailed information on one or more hosts", Usage: "Shows detailed information on one or more hosts",
ArgsUsage: "<id or name> [<id or name> [<ir or name>...]]", ArgsUsage: "<id or name> [<id or name> [<ir or name>...]]",
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
if c.NArg() < 1 { if c.NArg() < 1 {
@ -126,10 +125,9 @@ GLOBAL OPTIONS:
enc.SetIndent("", " ") enc.SetIndent("", " ")
return enc.Encode(hosts) return enc.Encode(hosts)
}, },
}, }, {
{
Name: "ls", Name: "ls",
Usage: "List hosts", Usage: "Lists hosts",
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
var hosts []Host var hosts []Host
if err := db.Find(&hosts).Error; err != nil { if err := db.Find(&hosts).Error; err != nil {
@ -163,10 +161,9 @@ GLOBAL OPTIONS:
table.Render() table.Render()
return nil return nil
}, },
}, }, {
{
Name: "rm", Name: "rm",
Usage: "Remove one or more hosts", Usage: "Removes one or more hosts",
ArgsUsage: "<id or name> [<id or name> [<ir or name>...]]", ArgsUsage: "<id or name> [<id or name> [<ir or name>...]]",
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
if c.NArg() < 1 { if c.NArg() < 1 {
@ -188,7 +185,7 @@ GLOBAL OPTIONS:
}, },
}, { }, {
Name: "info", Name: "info",
Usage: "Display system-wide information", Usage: "Shows system-wide information",
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
fmt.Fprintf(s, "Debug mode (server): %v\n", globalContext.Bool("debug")) fmt.Fprintf(s, "Debug mode (server): %v\n", globalContext.Bool("debug"))
hostname, _ := os.Hostname() hostname, _ := os.Hostname()
@ -213,14 +210,14 @@ GLOBAL OPTIONS:
}, },
}, { }, {
Name: "key", Name: "key",
Usage: "Manage keys", Usage: "Manages keys",
Subcommands: []cli.Command{ Subcommands: []cli.Command{
{ {
Name: "create", Name: "create",
Usage: "Create a new key", Usage: "Creates a new key",
Description: "$> key create\n $> key create --name=mykey", Description: "$> key create\n $> key create --name=mykey",
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{Name: "name", Usage: "Assign a name to the host"}, cli.StringFlag{Name: "name", Usage: "Assigns a name to the key"},
cli.StringFlag{Name: "type", Value: "rsa"}, cli.StringFlag{Name: "type", Value: "rsa"},
cli.UintFlag{Name: "length", Value: 2048}, cli.UintFlag{Name: "length", Value: 2048},
cli.StringFlag{Name: "comment"}, cli.StringFlag{Name: "comment"},
@ -249,10 +246,9 @@ GLOBAL OPTIONS:
fmt.Fprintf(s, "%d\n", key.ID) fmt.Fprintf(s, "%d\n", key.ID)
return nil return nil
}, },
}, }, {
{
Name: "inspect", Name: "inspect",
Usage: "Display detailed information on one or more keys", Usage: "Shows detailed information on one or more keys",
ArgsUsage: "<id or name> [<id or name> [<ir or name>...]]", ArgsUsage: "<id or name> [<id or name> [<ir or name>...]]",
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
if c.NArg() < 1 { if c.NArg() < 1 {
@ -268,10 +264,9 @@ GLOBAL OPTIONS:
enc.SetIndent("", " ") enc.SetIndent("", " ")
return enc.Encode(keys) return enc.Encode(keys)
}, },
}, }, {
{
Name: "ls", Name: "ls",
Usage: "List keys", Usage: "Lists keys",
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
var keys []SSHKey var keys []SSHKey
if err := db.Find(&keys).Error; err != nil { if err := db.Find(&keys).Error; err != nil {
@ -296,10 +291,9 @@ GLOBAL OPTIONS:
table.Render() table.Render()
return nil return nil
}, },
}, }, {
{
Name: "rm", Name: "rm",
Usage: "Remove one or more keys", Usage: "Removes one or more keys",
ArgsUsage: "<id or name> [<id or name> [<ir or name>...]]", ArgsUsage: "<id or name> [<id or name> [<ir or name>...]]",
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
if c.NArg() < 1 { if c.NArg() < 1 {
@ -321,32 +315,111 @@ GLOBAL OPTIONS:
}, },
}, { }, {
Name: "user", Name: "user",
Usage: "Manage users", Usage: "Manages users",
Subcommands: []cli.Command{ Subcommands: []cli.Command{
{ {
Name: "create", Name: "create",
Usage: "Create a new user", ArgsUsage: "<email>",
Action: func(c *cli.Context) error { return nil }, Usage: "Creates a new user",
}, Description: "$> user create bob\n $> user create --name=mykey",
{ Flags: []cli.Flag{
Name: "inspect", cli.StringFlag{Name: "name", Usage: "Assigns a name to the user"},
Usage: "Display detailed information on one or more users", cli.StringFlag{Name: "comment"},
Action: func(c *cli.Context) error { return nil }, },
}, Action: func(c *cli.Context) error {
{ if c.NArg() != 1 {
Name: "ls", return fmt.Errorf("invalid usage")
Usage: "List users", }
Action: func(c *cli.Context) error { return nil },
}, email := c.Args().First()
{ name := strings.Split(email, "@")[0]
Name: "rm", if c.String("name") != "" {
Usage: "Remove one or more users", name = c.String("name")
Action: func(c *cli.Context) error { return nil }, }
user := User{
Name: name,
Email: email,
Comment: c.String("comment"),
}
// save the user in database
if err := db.Create(&user).Error; err != nil {
return err
}
fmt.Fprintf(s, "%d\n", user.ID)
return nil
},
}, {
Name: "inspect",
Usage: "Shows detailed information on one or more users",
ArgsUsage: "<id or email> [<id or email> [<ir or email>...]]",
Action: func(c *cli.Context) error {
if c.NArg() < 1 {
return fmt.Errorf("invalid usage")
}
hosts, err := FindUsersByIdOrEmail(db, c.Args())
if err != nil {
return nil
}
enc := json.NewEncoder(s)
enc.SetIndent("", " ")
return enc.Encode(hosts)
},
}, {
Name: "ls",
Usage: "Lists users",
Action: func(c *cli.Context) error {
var users []User
if err := db.Find(&users).Error; err != nil {
return err
}
table := tablewriter.NewWriter(s)
table.SetHeader([]string{"ID", "Name", "Email", "Keys", "Comment"})
table.SetBorder(false)
table.SetCaption(true, fmt.Sprintf("Total: %d users.", len(users)))
for _, user := range users {
keys := len(user.SSHKeys)
table.Append([]string{
fmt.Sprintf("%d", user.ID),
user.Name,
user.Email,
fmt.Sprintf("%d", keys),
user.Comment,
//FIXME: add some stats about last access time etc
//FIXME: add creation date
})
}
table.Render()
return nil
},
}, {
Name: "rm",
Usage: "Removes one or more users",
ArgsUsage: "<id or email> [<id or email> [<ir or email>...]]",
Action: func(c *cli.Context) error {
if c.NArg() < 1 {
return fmt.Errorf("invalid usage")
}
users, err := FindUsersByIdOrEmail(db, c.Args())
if err != nil {
return nil
}
for _, user := range users {
db.Where("id = ?", user.ID).Delete(&User{})
fmt.Fprintf(s, "%d\n", user.ID)
}
return nil
},
}, },
}, },
}, { }, {
Name: "version", Name: "version",
Usage: "Show the SSHPortal version information", Usage: "Shows the SSHPortal version information",
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
fmt.Fprintf(s, "%s\n", version) fmt.Fprintf(s, "%s\n", version)
return nil return nil