Merge pull request #8 from moul/dev/moul/wip-update

Add 'host update'
This commit is contained in:
Manfred Touron 2017-11-23 10:36:34 +01:00 committed by GitHub
commit 8ba418308e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 206 additions and 287 deletions

View file

@ -2,7 +2,8 @@
## master (unreleased)
* No entry
* More details in 'ls' commands
* Add 'host update' command (fix [#2](https://github.com/moul/sshportal/pull/2))
## v1.2.0 (2017-11-22)

View file

@ -3,6 +3,7 @@ GIT_TAG ?= $(shell git describe --tags --always)
GIT_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD)
LDFLAGS ?= -X main.GIT_SHA=$(GIT_SHA) -X main.GIT_TAG=$(GIT_TAG) -X main.GIT_BRANCH=$(GIT_BRANCH)
VERSION ?= $(shell grep 'VERSION =' main.go | cut -d'"' -f2)
PORT ?= 2222
.PHONY: install
install:
@ -14,7 +15,7 @@ docker.build:
.PHONY: integration
integration:
bash ./examples/integration/test.sh
PORT="$(PORT)" bash ./examples/integration/test.sh
.PHONY: _docker_install
_docker_install:
@ -23,7 +24,7 @@ _docker_install:
.PHONY: dev
dev:
-go get github.com/githubnemo/CompileDaemon
CompileDaemon -exclude-dir=.git -exclude=".#*" -color=true -command="./sshportal --demo --debug" .
CompileDaemon -exclude-dir=.git -exclude=".#*" -color=true -command="./sshportal --demo --debug --port=$(PORT)" .
.PHONY: test
test:

View file

@ -139,10 +139,10 @@ You can enter in interactive mode using this syntax: `ssh admin@portal.example.o
```sh
# acl management
acl help
acl create [-h] [--hostgroup=<value>...] [--usergroup=<value>...] [--pattern=<value>] [--comment=<value>] [--action=<value>] [--weight=value]
acl inspect [-h] <id>...
acl create [-h] [--hostgroup=HOSTGROUP...] [--usergroup=USERGROUP...] [--pattern=<value>] [--comment=<value>] [--action=<value>] [--weight=value]
acl inspect [-h] ACL...
acl ls [-h]
acl rm [-h] <id>...
acl rm [-h] ACL...
# config management
config help
@ -151,38 +151,39 @@ config restore [-h] [--confirm]
# host management
host help
host create [-h] [--name=<value>] [--password=<value>] [--fingerprint=<value>] [--comment=<value>] [--key=<value>] [--group=<value>...] <user>[:<password>]@<host>[:<port>]
host inspect [-h] <id or name>...
host create [-h] [--name=<value>] [--password=<value>] [--fingerprint=<value>] [--comment=<value>] [--key=KEY] [--group=HOSTGROUP...] <username>[:<password>]@<host>[:<port>]
host inspect [-h] HOST...
host ls [-h]
host rm [-h] <id or name>...
host rm [-h] HOST...
host update [-h] USER [--name=<value>] [--comment=<value>] [--fingerprint=<value>] [--key=KEY] [--assign-group=HOSTGROUP...] [--unassign-group=HOSTGROUP...]
# hostgroup management
hostgroup help
hostgroup create [-h] [--name=<value>] [--comment=<value>]
hostgroup inspect [-h] <id or name>...
hostgroup inspect [-h] HOSTGROUP...
hostgroup ls [-h]
hostgroup rm [-h] <id or name>...
hostgroup rm [-h] HOSTGROUP...
# key management
key help
key create [-h] [--name=<value>] [--type=<value>] [--length=<value>] [--comment=<value>]
key inspect [-h] <id or name>...
key inspect [-h] KEY...
key ls [-h]
key rm [-h] <id or name>...
key rm [-h] KEY...
# user management
user help
user invite [-h] [--name=<value>] [--comment=<value>] [--group=<value>...] <email>
user inspect [-h] <id or email>...
user invite [-h] [--name=<value>] [--comment=<value>] [--group=USERGROUP...] <email>
user inspect [-h] USER...
user ls [-h]
user rm [-h] <id or email>...
user rm [-h] USER...
# usergroup management
usergroup help
hostgroup create [-h] [--name=<value>] [--comment=<value>]
usergroup inspect [-h] <id or name>...
usergroup inspect [-h] USERGROUP...
usergroup ls [-h]
usergroup rm [-h] <id or name>...
usergroup rm [-h] USERGROUP...
# other
exit [-h]

147
db.go
View file

@ -155,148 +155,57 @@ func (host *Host) Hostname() string {
}
// Host helpers
func FindHostByIdOrName(db *gorm.DB, query string) (*Host, error) {
var host Host
if err := db.Preload("Groups").Preload("SSHKey").Where("id = ?", query).Or("name = ?", query).First(&host).Error; err != nil {
return nil, err
}
return &host, nil
func HostsPreload(db *gorm.DB) *gorm.DB {
return db.Preload("Groups").Preload("SSHKey")
}
func FindHostsByIdOrName(db *gorm.DB, queries []string) ([]*Host, error) {
var hosts []*Host
for _, query := range queries {
host, err := FindHostByIdOrName(db, query)
if err != nil {
return nil, err
}
hosts = append(hosts, host)
}
return hosts, nil
func HostsByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
return db.Where("id IN (?)", identifiers).Or("name IN (?)", identifiers)
}
// SSHKey helpers
func FindKeyByIdOrName(db *gorm.DB, query string) (*SSHKey, error) {
var key SSHKey
if err := db.Preload("Hosts").Where("id = ?", query).Or("name = ?", query).First(&key).Error; err != nil {
return nil, err
}
return &key, nil
func SSHKeysPreload(db *gorm.DB) *gorm.DB {
return db.Preload("Hosts")
}
func FindKeysByIdOrName(db *gorm.DB, queries []string) ([]*SSHKey, error) {
var keys []*SSHKey
for _, query := range queries {
key, err := FindKeyByIdOrName(db, query)
if err != nil {
return nil, err
}
keys = append(keys, key)
}
return keys, nil
func SSHKeysByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
return db.Where("id IN (?)", identifiers).Or("name IN (?)", identifiers)
}
// HostGroup helpers
func FindHostGroupByIdOrName(db *gorm.DB, query string) (*HostGroup, error) {
var hostGroup HostGroup
if err := db.Preload("ACLs").Preload("Hosts").Where("id = ?", query).Or("name = ?", query).First(&hostGroup).Error; err != nil {
return nil, err
}
return &hostGroup, nil
func HostGroupsPreload(db *gorm.DB) *gorm.DB {
return db.Preload("ACLs").Preload("Hosts")
}
func FindHostGroupsByIdOrName(db *gorm.DB, queries []string) ([]*HostGroup, error) {
var hostGroups []*HostGroup
for _, query := range queries {
hostGroup, err := FindHostGroupByIdOrName(db, query)
if err != nil {
return nil, err
}
hostGroups = append(hostGroups, hostGroup)
}
return hostGroups, nil
func HostGroupsByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
return db.Where("id IN (?)", identifiers).Or("name IN (?)", identifiers)
}
// UserGroup heleprs
func FindUserGroupByIdOrName(db *gorm.DB, query string) (*UserGroup, error) {
var userGroup UserGroup
if err := db.Preload("ACLs").Preload("Users").Where("id = ?", query).Or("name = ?", query).First(&userGroup).Error; err != nil {
return nil, err
}
return &userGroup, nil
func UserGroupsPreload(db *gorm.DB) *gorm.DB {
return db.Preload("ACLs").Preload("Users")
}
func FindUserGroupsByIdOrName(db *gorm.DB, queries []string) ([]*UserGroup, error) {
var userGroups []*UserGroup
for _, query := range queries {
userGroup, err := FindUserGroupByIdOrName(db, query)
if err != nil {
return nil, err
}
userGroups = append(userGroups, userGroup)
}
return userGroups, nil
func UserGroupsByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
return db.Where("id IN (?)", identifiers).Or("name IN (?)", identifiers)
}
// User helpers
func FindUserByIdOrEmail(db *gorm.DB, query string) (*User, error) {
var user User
if err := db.Preload("Groups").Preload("Keys").Where("id = ?", query).Or("email = ?", query).First(&user).Error; err != nil {
return nil, err
}
return &user, nil
func UsersPreload(db *gorm.DB) *gorm.DB {
return db.Preload("Groups").Preload("Keys")
}
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
func UsersByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
return db.Where("id IN (?)", identifiers).Or("email IN (?)", identifiers)
}
// ACL helpers
func FindACLById(db *gorm.DB, query string) (*ACL, error) {
var acl ACL
if err := db.Preload("UserGroups").Preload("HostGroups").Where("id = ?", query).First(&acl).Error; err != nil {
return nil, err
}
return &acl, nil
func ACLsPreload(db *gorm.DB) *gorm.DB {
return db.Preload("UserGroups").Preload("HostGroups")
}
func FindACLsById(db *gorm.DB, queries []string) ([]*ACL, error) {
var acls []*ACL
for _, query := range queries {
acl, err := FindACLById(db, query)
if err != nil {
return nil, err
}
acls = append(acls, acl)
}
return acls, nil
func ACLsByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
return db.Where("id IN (?)", identifiers)
}
// UserKey helpers
func FindUserkeyById(db *gorm.DB, query string) (*UserKey, error) {
var userkey UserKey
if err := db.Preload("User").Where("id = ?", query).First(&userkey).Error; err != nil {
return nil, err
}
return &userkey, nil
func UserKeysPreload(db *gorm.DB) *gorm.DB {
return db.Preload("User")
}
func FindUserkeysById(db *gorm.DB, queries []string) ([]*UserKey, error) {
var userkeys []*UserKey
for _, query := range queries {
userkey, err := FindUserkeyById(db, query)
if err != nil {
return nil, err
}
userkeys = append(userkeys, userkey)
}
return userkeys, nil
func UserKeysByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
return db.Where("id IN (?)", identifiers)
}

View file

@ -315,20 +315,20 @@ func dbInit(db *gorm.DB) error {
}
func dbDemo(db *gorm.DB) error {
hostGroup, err := FindHostGroupByIdOrName(db, "default")
if err != nil {
var hostGroup HostGroup
if err := HostGroupsByIdentifiers(db, []string{"default"}).First(&hostGroup).Error; err != nil {
return err
}
key, err := FindKeyByIdOrName(db, "default")
if err != nil {
var key SSHKey
if err := SSHKeysByIdentifiers(db, []string{"default"}).First(&key).Error; err != nil {
return err
}
var (
host1 = Host{Name: "sdf", Addr: "sdf.org:22", User: "new", SSHKeyID: key.ID, Groups: []*HostGroup{hostGroup}}
host2 = Host{Name: "whoami", Addr: "whoami.filippo.io:22", User: "test", SSHKeyID: key.ID, Groups: []*HostGroup{hostGroup}}
host3 = Host{Name: "ssh-chat", Addr: "chat.shazow.net:22", User: "test", SSHKeyID: key.ID, Fingerprint: "MD5:e5:d5:d1:75:90:38:42:f6:c7:03:d7:d0:56:7d:6a:db", Groups: []*HostGroup{hostGroup}}
host1 = Host{Name: "sdf", Addr: "sdf.org:22", User: "new", SSHKeyID: key.ID, Groups: []*HostGroup{&hostGroup}}
host2 = Host{Name: "whoami", Addr: "whoami.filippo.io:22", User: "test", SSHKeyID: key.ID, Groups: []*HostGroup{&hostGroup}}
host3 = Host{Name: "ssh-chat", Addr: "chat.shazow.net:22", User: "test", SSHKeyID: key.ID, Fingerprint: "MD5:e5:d5:d1:75:90:38:42:f6:c7:03:d7:d0:56:7d:6a:db", Groups: []*HostGroup{&hostGroup}}
)
// FIXME: check if hosts exist to avoid `UNIQUE constraint` error

View file

@ -223,8 +223,8 @@ func server(c *cli.Context) error {
}))
opts = append(opts, func(srv *ssh.Server) error {
key, err := FindKeyByIdOrName(db, "host")
if err != nil {
var key SSHKey
if err := SSHKeysByIdentifiers(db, []string{"host"}).First(&key).Error; err != nil {
return err
}
signer, err := gossh.ParsePrivateKey([]byte(key.PrivKey))

289
shell.go
View file

@ -63,10 +63,10 @@ GLOBAL OPTIONS:
Usage: "Creates a new ACL",
Description: "$> acl create -",
Flags: []cli.Flag{
cli.StringSliceFlag{Name: "hostgroup, hg", Usage: "Assigns host groups to the acl"},
cli.StringSliceFlag{Name: "usergroup, ug", Usage: "Assigns host groups to the acl"},
cli.StringSliceFlag{Name: "hostgroup, hg", Usage: "Assigns `HOSTGROUPS` to the acl"},
cli.StringSliceFlag{Name: "usergroup, ug", Usage: "Assigns `HOSTGROUPS` to the acl"},
cli.StringFlag{Name: "pattern", Usage: "Assigns a host pattern to the acl"},
cli.StringFlag{Name: "comment"},
cli.StringFlag{Name: "comment", Usage: "Adds a comment"},
cli.StringFlag{Name: "action", Usage: "Assigns the ACL action (allow,deny)", Value: "allow"},
cli.UintFlag{Name: "weight, w", Usage: "Assigns the ACL weight (priority)"},
},
@ -86,20 +86,16 @@ GLOBAL OPTIONS:
return err
}
for _, name := range c.StringSlice("usergroup") {
userGroup, err := FindUserGroupByIdOrName(db, name)
if err != nil {
return fmt.Errorf("unknown user group %q: %v", name, err)
}
acl.UserGroups = append(acl.UserGroups, userGroup)
var userGroups []*UserGroup
if err := UserGroupsPreload(UserGroupsByIdentifiers(db, c.StringSlice("usergroup"))).Find(&userGroups).Error; err != nil {
return err
}
for _, name := range c.StringSlice("hostgroup") {
hostGroup, err := FindHostGroupByIdOrName(db, name)
if err != nil {
return fmt.Errorf("unknown host group %q: %v", name, err)
}
acl.HostGroups = append(acl.HostGroups, hostGroup)
acl.UserGroups = append(acl.UserGroups, userGroups...)
var hostGroups []*HostGroup
if err := HostGroupsPreload(HostGroupsByIdentifiers(db, c.StringSlice("hostgroup"))).Find(&hostGroups).Error; err != nil {
return err
}
acl.HostGroups = append(acl.HostGroups, hostGroups...)
if len(acl.UserGroups) == 0 {
return fmt.Errorf("an ACL must have at least one user group")
@ -117,15 +113,15 @@ GLOBAL OPTIONS:
}, {
Name: "inspect",
Usage: "Shows detailed information on one or more acls",
ArgsUsage: "<id> [<id> [<id>...]]",
ArgsUsage: "ACL...",
Action: func(c *cli.Context) error {
if c.NArg() < 1 {
return cli.ShowSubcommandHelp(c)
}
acls, err := FindACLsById(db, c.Args())
if err != nil {
return nil
var acls []ACL
if err := ACLsPreload(ACLsByIdentifiers(db, c.Args())).Find(&acls).Error; err != nil {
return err
}
enc := json.NewEncoder(s)
@ -169,22 +165,13 @@ GLOBAL OPTIONS:
}, {
Name: "rm",
Usage: "Removes one or more acls",
ArgsUsage: "<id or name> [<id or name> [<id or name>...]]",
ArgsUsage: "ACL...",
Action: func(c *cli.Context) error {
if c.NArg() < 1 {
return cli.ShowSubcommandHelp(c)
}
acls, err := FindACLsById(db, c.Args())
if err != nil {
return nil
}
for _, acl := range acls {
db.Where("id = ?", acl.ID).Delete(&ACL{})
fmt.Fprintf(s, "%d\n", acl.ID)
}
return nil
return ACLsByIdentifiers(db, c.Args()).Delete(&ACL{}).Error
},
},
},
@ -240,7 +227,7 @@ GLOBAL OPTIONS:
Usage: "Restores a backup",
Description: "ssh admin@portal config restore < sshportal.bkp",
Flags: []cli.Flag{
cli.BoolFlag{Name: "confirm", Usage: "automatically confirms"},
cli.BoolFlag{Name: "confirm", Usage: "yes, I want to replace everything with this backup!"},
},
Action: func(c *cli.Context) error {
config := Config{}
@ -346,8 +333,8 @@ GLOBAL OPTIONS:
cli.StringFlag{Name: "password, p", Usage: "If present, sshportal will use password-based authentication"},
cli.StringFlag{Name: "fingerprint, f", Usage: "SSH host key fingerprint"},
cli.StringFlag{Name: "comment, c"},
cli.StringFlag{Name: "key, k", Usage: "ID or name of the key to use for authentication"},
cli.StringSliceFlag{Name: "group, g", Usage: "Names or IDs of host groups (default: \"default\")"},
cli.StringFlag{Name: "key, k", Usage: "`KEY` to use for authentication"},
cli.StringSliceFlag{Name: "group, g", Usage: "Assigns the host to `HOSTGROUPS` (default: \"default\")"},
},
Action: func(c *cli.Context) error {
if c.NArg() != 1 {
@ -378,8 +365,8 @@ GLOBAL OPTIONS:
inputKey = "default"
}
if inputKey != "" {
key, err := FindKeyByIdOrName(db, inputKey)
if err != nil {
var key SSHKey
if err := SSHKeysByIdentifiers(db, []string{inputKey}).First(&key).Error; err != nil {
return err
}
host.SSHKeyID = key.ID
@ -390,11 +377,9 @@ GLOBAL OPTIONS:
if len(inputGroups) == 0 {
inputGroups = []string{"default"}
}
hostGroups, err := FindHostGroupsByIdOrName(db, inputGroups)
if err != nil {
if err := HostGroupsByIdentifiers(db, inputGroups).Find(&host.Groups).Error; err != nil {
return err
}
host.Groups = hostGroups
if err := db.Create(&host).Error; err != nil {
return err
@ -405,15 +390,15 @@ GLOBAL OPTIONS:
}, {
Name: "inspect",
Usage: "Shows detailed information on one or more hosts",
ArgsUsage: "<id or name> [<id or name> [<id or name>...]]",
ArgsUsage: "HOST...",
Action: func(c *cli.Context) error {
if c.NArg() < 1 {
return cli.ShowSubcommandHelp(c)
}
hosts, err := FindHostsByIdOrName(db, c.Args())
if err != nil {
return nil
var hosts []Host
if err := HostsPreload(HostsByIdentifiers(db, c.Args())).Find(&hosts).Error; err != nil {
return err
}
enc := json.NewEncoder(s)
@ -435,20 +420,24 @@ GLOBAL OPTIONS:
for _, host := range hosts {
authKey, authPass := "", ""
if host.Password != "" {
authPass = "X"
authPass = "yes"
}
if host.SSHKeyID > 0 {
var key SSHKey
db.Model(&host).Related(&key)
authKey = key.Name
}
groupNames := []string{}
for _, hostGroup := range host.Groups {
groupNames = append(groupNames, hostGroup.Name)
}
table.Append([]string{
fmt.Sprintf("%d", host.ID),
host.Name,
host.URL(),
authKey,
authPass,
fmt.Sprintf("%d", len(host.Groups)),
strings.Join(groupNames, ", "),
host.Comment,
//FIXME: add some stats about last access time etc
//FIXME: add creation date
@ -460,22 +449,83 @@ GLOBAL OPTIONS:
}, {
Name: "rm",
Usage: "Removes one or more hosts",
ArgsUsage: "<id or name> [<id or name> [<id or name>...]]",
ArgsUsage: "HOST...",
Action: func(c *cli.Context) error {
if c.NArg() < 1 {
return cli.ShowSubcommandHelp(c)
}
hosts, err := FindHostsByIdOrName(db, c.Args())
if err != nil {
return nil
return HostsByIdentifiers(db, c.Args()).Delete(&Host{}).Error
},
}, {
Name: "update",
Usage: "Updates an existing host",
ArgsUsage: "HOST",
Flags: []cli.Flag{
cli.StringFlag{Name: "name, n", Usage: "Rename the host"},
cli.StringFlag{Name: "password, p", Usage: "Update/set a password, use \"none\" to unset"},
cli.StringFlag{Name: "fingerprint, f", Usage: "Update/set a host fingerprint, use \"none\" to unset"},
cli.StringFlag{Name: "comment, c", Usage: "Update/set a host comment"},
cli.StringFlag{Name: "key, k", Usage: "Link a `KEY` to use for authentication"},
cli.StringSliceFlag{Name: "assign-group, g", Usage: "Assign the host to a new `HOSTGROUPS`"},
cli.StringSliceFlag{Name: "unassign-group", Usage: "Unassign the host from a `HOSTGROUPS`"},
},
Action: func(c *cli.Context) error {
if c.NArg() < 1 {
return cli.ShowSubcommandHelp(c)
}
for _, host := range hosts {
db.Where("id = ?", host.ID).Delete(&Host{})
fmt.Fprintf(s, "%d\n", host.ID)
var hosts []Host
if err := HostsByIdentifiers(db, c.Args()).Find(&hosts).Error; err != nil {
return err
}
return nil
if len(hosts) > 1 && c.String("name") != "" {
return fmt.Errorf("cannot set --name when editing multiple hosts at once")
}
tx := db.Begin()
for _, host := range hosts {
model := tx.Model(&host)
// simple fields
for _, fieldname := range []string{"name", "comment", "password", "fingerprint"} {
if c.String(fieldname) != "" {
if err := model.Update(fieldname, c.String(fieldname)).Error; err != nil {
tx.Rollback()
return err
}
}
}
// associations
if c.String("key") != "" {
var key SSHKey
if err := SSHKeysByIdentifiers(db, []string{c.String("key")}).First(&key).Error; err != nil {
tx.Rollback()
return err
}
if err := model.Association("SSHKey").Replace(&key).Error; err != nil {
tx.Rollback()
return err
}
}
var appendGroups []HostGroup
var deleteGroups []HostGroup
if err := HostGroupsByIdentifiers(db, c.StringSlice("assign-group")).Find(&appendGroups).Error; err != nil {
tx.Rollback()
return err
}
if err := HostGroupsByIdentifiers(db, c.StringSlice("unassign-group")).Find(&deleteGroups).Error; err != nil {
tx.Rollback()
return err
}
if err := model.Association("Groups").Append(&appendGroups).Delete(deleteGroups).Error; err != nil {
tx.Rollback()
return err
}
}
return tx.Commit().Error
},
},
},
@ -489,7 +539,7 @@ GLOBAL OPTIONS:
Description: "$> hostgroup create --name=prod",
Flags: []cli.Flag{
cli.StringFlag{Name: "name", Usage: "Assigns a name to the host group"},
cli.StringFlag{Name: "comment"},
cli.StringFlag{Name: "comment", Usage: "Adds a comment"},
},
Action: func(c *cli.Context) error {
hostGroup := HostGroup{
@ -513,15 +563,15 @@ GLOBAL OPTIONS:
}, {
Name: "inspect",
Usage: "Shows detailed information on one or more host groups",
ArgsUsage: "<id or name> [<id or name> [<id or name>...]]",
ArgsUsage: "HOSTGROUP...",
Action: func(c *cli.Context) error {
if c.NArg() < 1 {
return cli.ShowSubcommandHelp(c)
}
hostGroups, err := FindHostGroupsByIdOrName(db, c.Args())
if err != nil {
return nil
var hostGroups []HostGroup
if err := HostGroupsPreload(HostGroupsByIdentifiers(db, c.Args())).Find(&hostGroups).Error; err != nil {
return err
}
enc := json.NewEncoder(s)
@ -556,22 +606,13 @@ GLOBAL OPTIONS:
}, {
Name: "rm",
Usage: "Removes one or more host groups",
ArgsUsage: "<id or name> [<id or name> [<id or name>...]]",
ArgsUsage: "HOSTGROUP...",
Action: func(c *cli.Context) error {
if c.NArg() < 1 {
return cli.ShowSubcommandHelp(c)
}
hostGroups, err := FindHostGroupsByIdOrName(db, c.Args())
if err != nil {
return nil
}
for _, hostGroup := range hostGroups {
db.Where("id = ?", hostGroup.ID).Delete(&HostGroup{})
fmt.Fprintf(s, "%d\n", hostGroup.ID)
}
return nil
return HostGroupsByIdentifiers(db, c.Args()).Delete(&HostGroup{}).Error
},
},
},
@ -620,7 +661,7 @@ GLOBAL OPTIONS:
cli.StringFlag{Name: "name", Usage: "Assigns a name to the key"},
cli.StringFlag{Name: "type", Value: "rsa"},
cli.UintFlag{Name: "length", Value: 2048},
cli.StringFlag{Name: "comment"},
cli.StringFlag{Name: "comment", Usage: "Adds a comment"},
},
Action: func(c *cli.Context) error {
name := namesgenerator.GetRandomName(0)
@ -650,15 +691,15 @@ GLOBAL OPTIONS:
}, {
Name: "inspect",
Usage: "Shows detailed information on one or more keys",
ArgsUsage: "<id or name> [<id or name> [<id or name>...]]",
ArgsUsage: "KEY...",
Action: func(c *cli.Context) error {
if c.NArg() < 1 {
return cli.ShowSubcommandHelp(c)
}
keys, err := FindKeysByIdOrName(db, c.Args())
if err != nil {
return nil
var keys []SSHKey
if err := SSHKeysByIdentifiers(db, c.Args()).Find(&keys).Error; err != nil {
return err
}
enc := json.NewEncoder(s)
@ -696,22 +737,13 @@ GLOBAL OPTIONS:
}, {
Name: "rm",
Usage: "Removes one or more keys",
ArgsUsage: "<id or name> [<id or name> [<id or name>...]]",
ArgsUsage: "KEY...",
Action: func(c *cli.Context) error {
if c.NArg() < 1 {
return cli.ShowSubcommandHelp(c)
}
keys, err := FindKeysByIdOrName(db, c.Args())
if err != nil {
return nil
}
for _, key := range keys {
db.Where("id = ?", key.ID).Delete(&SSHKey{})
fmt.Fprintf(s, "%d\n", key.ID)
}
return nil
return SSHKeysByIdentifiers(db, c.Args()).Delete(&SSHKey{}).Error
},
},
},
@ -722,15 +754,15 @@ GLOBAL OPTIONS:
{
Name: "inspect",
Usage: "Shows detailed information on one or more users",
ArgsUsage: "<id or email> [<id or email> [<id or email>...]]",
ArgsUsage: "USER...",
Action: func(c *cli.Context) error {
if c.NArg() < 1 {
return cli.ShowSubcommandHelp(c)
}
users, err := FindUsersByIdOrEmail(db, c.Args())
if err != nil {
return nil
var users []User
if err := UsersPreload(UsersByIdentifiers(db, c.Args())).Find(&users).Error; err != nil {
return err
}
enc := json.NewEncoder(s)
@ -744,8 +776,8 @@ GLOBAL OPTIONS:
Description: "$> user invite bob@example.com\n $> user invite --name=Robert bob@example.com",
Flags: []cli.Flag{
cli.StringFlag{Name: "name", Usage: "Assigns a name to the user"},
cli.StringFlag{Name: "comment"},
cli.StringSliceFlag{Name: "group, g", Usage: "Names or IDs of user groups (default: \"default\")"},
cli.StringFlag{Name: "comment", Usage: "Adds a comment"},
cli.StringSliceFlag{Name: "group, g", Usage: "Names or IDs of `USERGROUPS` (default: \"default\")"},
},
Action: func(c *cli.Context) error {
if c.NArg() != 1 {
@ -776,11 +808,9 @@ GLOBAL OPTIONS:
if len(inputGroups) == 0 {
inputGroups = []string{"default"}
}
userGroups, err := FindUserGroupsByIdOrName(db, inputGroups)
if err != nil {
if err := UserGroupsByIdentifiers(db, inputGroups).Find(&user.Groups).Error; err != nil {
return err
}
user.Groups = userGroups
// save the user in database
if err := db.Create(&user).Error; err != nil {
@ -802,12 +832,16 @@ GLOBAL OPTIONS:
table.SetBorder(false)
table.SetCaption(true, fmt.Sprintf("Total: %d users.", len(users)))
for _, user := range users {
groupNames := []string{}
for _, userGroup := range user.Groups {
groupNames = append(groupNames, userGroup.Name)
}
table.Append([]string{
fmt.Sprintf("%d", user.ID),
user.Name,
user.Email,
fmt.Sprintf("%d", len(user.Keys)),
fmt.Sprintf("%d", len(user.Groups)),
strings.Join(groupNames, ", "),
user.Comment,
//FIXME: add some stats about last access time etc
//FIXME: add creation date
@ -819,22 +853,13 @@ GLOBAL OPTIONS:
}, {
Name: "rm",
Usage: "Removes one or more users",
ArgsUsage: "<id or email> [<id or email> [<id or email>...]]",
ArgsUsage: "USER...",
Action: func(c *cli.Context) error {
if c.NArg() < 1 {
return cli.ShowSubcommandHelp(c)
}
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
return UsersByIdentifiers(db, c.Args()).Delete(&User{}).Error
},
},
},
@ -848,7 +873,7 @@ GLOBAL OPTIONS:
Description: "$> usergroup create --name=prod",
Flags: []cli.Flag{
cli.StringFlag{Name: "name", Usage: "Assigns a name to the user group"},
cli.StringFlag{Name: "comment"},
cli.StringFlag{Name: "comment", Usage: "Adds a comment"},
},
Action: func(c *cli.Context) error {
userGroup := UserGroup{
@ -878,15 +903,15 @@ GLOBAL OPTIONS:
}, {
Name: "inspect",
Usage: "Shows detailed information on one or more user groups",
ArgsUsage: "<id or name> [<id or name> [<id or name>...]]",
ArgsUsage: "USERGROUP...",
Action: func(c *cli.Context) error {
if c.NArg() < 1 {
return cli.ShowSubcommandHelp(c)
}
userGroups, err := FindUserGroupsByIdOrName(db, c.Args())
if err != nil {
return nil
var userGroups []UserGroup
if err := UserGroupsPreload(UserGroupsByIdentifiers(db, c.Args())).Find(&userGroups).Error; err != nil {
return err
}
enc := json.NewEncoder(s)
@ -921,22 +946,13 @@ GLOBAL OPTIONS:
}, {
Name: "rm",
Usage: "Removes one or more user groups",
ArgsUsage: "<id or name> [<id or name> [<id or name>...]]",
ArgsUsage: "USERGROUP...",
Action: func(c *cli.Context) error {
if c.NArg() < 1 {
return cli.ShowSubcommandHelp(c)
}
userGroups, err := FindUserGroupsByIdOrName(db, c.Args())
if err != nil {
return nil
}
for _, userGroup := range userGroups {
db.Where("id = ?", userGroup.ID).Delete(&UserGroup{})
fmt.Fprintf(s, "%d\n", userGroup.ID)
}
return nil
return UserGroupsByIdentifiers(db, c.Args()).Delete(&UserGroup{}).Error
},
},
},
@ -950,15 +966,15 @@ GLOBAL OPTIONS:
Usage: "Creates a new userkey",
Description: "$> userkey create bob\n $> user create --name=mykey bob",
Flags: []cli.Flag{
cli.StringFlag{Name: "comment"},
cli.StringFlag{Name: "comment", Usage: "Adds a comment"},
},
Action: func(c *cli.Context) error {
if c.NArg() != 1 {
return cli.ShowSubcommandHelp(c)
}
user, err := FindUserByIdOrEmail(db, c.Args().First())
if err != nil {
var user User
if err := UsersByIdentifiers(db, c.Args()).First(&user).Error; err != nil {
return err
}
@ -972,7 +988,7 @@ GLOBAL OPTIONS:
}
userkey := UserKey{
UserID: user.ID,
User: &user,
Key: key.Marshal(),
Comment: comment,
}
@ -994,20 +1010,20 @@ GLOBAL OPTIONS:
}, {
Name: "inspect",
Usage: "Shows detailed information on one or more userkeys",
ArgsUsage: "<id> [<id> [<id>...]]",
ArgsUsage: "USERKEY...",
Action: func(c *cli.Context) error {
if c.NArg() < 1 {
return cli.ShowSubcommandHelp(c)
}
userkeys, err := FindUserkeysById(db, c.Args())
if err != nil {
return nil
var userKeys []UserKey
if err := UserKeysPreload(UserKeysByIdentifiers(db, c.Args())).Find(&userKeys).Error; err != nil {
return err
}
enc := json.NewEncoder(s)
enc.SetIndent("", " ")
return enc.Encode(userkeys)
return enc.Encode(userKeys)
},
}, {
Name: "ls",
@ -1035,22 +1051,13 @@ GLOBAL OPTIONS:
}, {
Name: "rm",
Usage: "Removes one or more userkeys",
ArgsUsage: "<id> [<id> [<id>...]]",
ArgsUsage: "USERKEY...",
Action: func(c *cli.Context) error {
if c.NArg() < 1 {
return cli.ShowSubcommandHelp(c)
}
userkeys, err := FindUserkeysById(db, c.Args())
if err != nil {
return nil
}
for _, userkey := range userkeys {
db.Where("id = ?", userkey.ID).Delete(&UserKey{})
fmt.Fprintf(s, "%d\n", userkey.ID)
}
return nil
return UserKeysByIdentifiers(db, c.Args()).Delete(&UserKey{}).Error
},
},
},