mirror of
https://github.com/moul/sshportal.git
synced 2025-01-27 18:08:51 +08:00
commit
31a8cef59f
6 changed files with 308 additions and 42 deletions
|
@ -6,6 +6,8 @@
|
|||
* Add 'host update' command (fix [#2](https://github.com/moul/sshportal/issues/2))
|
||||
* Add 'user update' command (fix [#3](https://github.com/moul/sshportal/issues/3))
|
||||
* Add 'acl update' command (fix [#4](https://github.com/moul/sshportal/issues/4))
|
||||
* Allow connecting to the shell mode with the registered username or email (fix [#5](https://github.com/moul/sshportal/issues/5))
|
||||
* Add 'listhosts' role (fix [#5](https://github.com/moul/sshportal/issues/5))
|
||||
|
||||
## v1.2.0 (2017-11-22)
|
||||
|
||||
|
|
2
Makefile
2
Makefile
|
@ -24,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 --port=$(PORT)" .
|
||||
CompileDaemon -exclude-dir=.git -exclude=".#*" -color=true -command="./sshportal --demo --debug --bind-address=:$(PORT)" .
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
|
|
41
db.go
41
db.go
|
@ -65,10 +65,16 @@ type UserKey struct {
|
|||
Comment string `valid:"optional"`
|
||||
}
|
||||
|
||||
type UserRole struct {
|
||||
gorm.Model
|
||||
Name string `valid:"required,length(1|32),unix_user"`
|
||||
Users []*User `gorm:"many2many:user_user_roles"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
// FIXME: use uuid for ID
|
||||
gorm.Model
|
||||
IsAdmin bool
|
||||
Roles []*UserRole `gorm:"many2many:user_user_roles"`
|
||||
Email string `valid:"required,email"`
|
||||
Name string `valid:"required,length(1|32),unix_user"`
|
||||
Keys []*UserKey `gorm:"ForeignKey:UserID"`
|
||||
|
@ -188,10 +194,31 @@ func UserGroupsByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
|
|||
|
||||
// User helpers
|
||||
func UsersPreload(db *gorm.DB) *gorm.DB {
|
||||
return db.Preload("Groups").Preload("Keys")
|
||||
return db.Preload("Groups").Preload("Keys").Preload("Roles")
|
||||
}
|
||||
func UsersByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
|
||||
return db.Where("id IN (?)", identifiers).Or("email IN (?)", identifiers)
|
||||
return db.Where("id IN (?)", identifiers).Or("email IN (?)", identifiers).Or("name IN (?)", identifiers)
|
||||
}
|
||||
func UserHasRole(user User, name string) bool {
|
||||
for _, role := range user.Roles {
|
||||
if role.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
func UserCheckRoles(user User, names []string) error {
|
||||
ok := false
|
||||
for _, name := range names {
|
||||
if UserHasRole(user, name) {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("you don't have permission to access this feature (requires any of these roles: '%s')", strings.Join(names, "', '"))
|
||||
}
|
||||
|
||||
// ACL helpers
|
||||
|
@ -209,3 +236,11 @@ func UserKeysPreload(db *gorm.DB) *gorm.DB {
|
|||
func UserKeysByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
|
||||
return db.Where("id IN (?)", identifiers)
|
||||
}
|
||||
|
||||
// UserRole helpers
|
||||
func UserRolesPreload(db *gorm.DB) *gorm.DB {
|
||||
return db.Preload("Users")
|
||||
}
|
||||
func UserRolesByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
|
||||
return db.Where("id IN (?)", identifiers).Or("name IN (?)", identifiers)
|
||||
}
|
||||
|
|
101
dbinit.go
101
dbinit.go
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
|
@ -201,6 +202,96 @@ func dbInit(db *gorm.DB) error {
|
|||
Rollback: func(tx *gorm.DB) error {
|
||||
return db.Model(&HostGroup{}).RemoveIndex("uix_hostgroups_name").Error
|
||||
},
|
||||
}, {
|
||||
ID: "15",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
type UserRole struct {
|
||||
gorm.Model
|
||||
Name string `valid:"required,length(1|32),unix_user"`
|
||||
Users []*User `gorm:"many2many:user_user_roles"`
|
||||
}
|
||||
return tx.AutoMigrate(&UserRole{}).Error
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return tx.DropTable("user_roles").Error
|
||||
},
|
||||
}, {
|
||||
ID: "16",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
type User struct {
|
||||
gorm.Model
|
||||
IsAdmin bool
|
||||
Roles []*UserRole `gorm:"many2many:user_user_roles"`
|
||||
Email string `valid:"required,email"`
|
||||
Name string `valid:"required,length(1|32),unix_user"`
|
||||
Keys []*UserKey `gorm:"ForeignKey:UserID"`
|
||||
Groups []*UserGroup `gorm:"many2many:user_user_groups;"`
|
||||
Comment string `valid:"optional"`
|
||||
InviteToken string `valid:"optional,length(10|60)"`
|
||||
}
|
||||
return tx.AutoMigrate(&User{}).Error
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
},
|
||||
}, {
|
||||
ID: "17",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.Create(&UserRole{Name: "admin"}).Error
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return tx.Where("name = ?", "admin").Delete(&UserRole{}).Error
|
||||
},
|
||||
}, {
|
||||
ID: "18",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
var adminRole UserRole
|
||||
if err := db.Where("name = ?", "admin").First(&adminRole).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var users []User
|
||||
if err := db.Preload("Roles").Where("is_admin = ?", true).Find(&users).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
user.Roles = append(user.Roles, &adminRole)
|
||||
if err := tx.Save(&user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
},
|
||||
}, {
|
||||
ID: "19",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
type User struct {
|
||||
gorm.Model
|
||||
Roles []*UserRole `gorm:"many2many:user_user_roles"`
|
||||
Email string `valid:"required,email"`
|
||||
Name string `valid:"required,length(1|32),unix_user"`
|
||||
Keys []*UserKey `gorm:"ForeignKey:UserID"`
|
||||
Groups []*UserGroup `gorm:"many2many:user_user_groups;"`
|
||||
Comment string `valid:"optional"`
|
||||
InviteToken string `valid:"optional,length(10|60)"`
|
||||
}
|
||||
return tx.AutoMigrate(&User{}).Error
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
},
|
||||
}, {
|
||||
ID: "20",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.Create(&UserRole{Name: "listhosts"}).Error
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return tx.Where("name = ?", "listhosts").Delete(&UserRole{}).Error
|
||||
},
|
||||
},
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
|
@ -284,15 +375,21 @@ func dbInit(db *gorm.DB) error {
|
|||
if os.Getenv("SSHPORTAL_DEFAULT_ADMIN_INVITE_TOKEN") != "" {
|
||||
inviteToken = os.Getenv("SSHPORTAL_DEFAULT_ADMIN_INVITE_TOKEN")
|
||||
}
|
||||
var adminRole UserRole
|
||||
if err := db.Where("name = ?", "admin").First(&adminRole).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
user := User{
|
||||
Name: "Administrator",
|
||||
Email: "admin@sshportal",
|
||||
Comment: "created by sshportal",
|
||||
IsAdmin: true,
|
||||
Roles: []*UserRole{&adminRole},
|
||||
InviteToken: inviteToken,
|
||||
Groups: []*UserGroup{&defaultUserGroup},
|
||||
}
|
||||
db.Create(&user)
|
||||
if err := db.Create(&user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("Admin user created, use the user 'invite:%s' to associate a public key with this account", user.InviteToken)
|
||||
}
|
||||
|
||||
|
|
8
main.go
8
main.go
|
@ -119,11 +119,7 @@ func server(c *cli.Context) error {
|
|||
}
|
||||
|
||||
switch username := s.User(); {
|
||||
case username == c.String("config-user"):
|
||||
if !currentUser.IsAdmin {
|
||||
fmt.Fprintf(s, "You are not an administrator, permission denied.\n")
|
||||
return
|
||||
}
|
||||
case username == currentUser.Name || username == currentUser.Email || username == c.String("config-user"):
|
||||
if err := shell(c, s, s.Command(), db); err != nil {
|
||||
fmt.Fprintf(s, "error: %v\n", err)
|
||||
}
|
||||
|
@ -181,7 +177,7 @@ func server(c *cli.Context) error {
|
|||
// lookup user by key
|
||||
db.Where("key = ?", key.Marshal()).First(&userKey)
|
||||
if userKey.UserID > 0 {
|
||||
db.Where("id = ?", userKey.UserID).First(&user)
|
||||
db.Preload("Roles").Where("id = ?", userKey.UserID).First(&user)
|
||||
if strings.HasPrefix(username, "invite:") {
|
||||
ctx.SetValue(errorContextKey, fmt.Errorf("invites are only supported for ney SSH keys; your ssh key is already associated with the user %q.", user.Email))
|
||||
}
|
||||
|
|
196
shell.go
196
shell.go
|
@ -53,6 +53,8 @@ GLOBAL OPTIONS:
|
|||
app := cli.NewApp()
|
||||
app.Writer = s
|
||||
app.HideVersion = true
|
||||
|
||||
myself := s.Context().Value(userContextKey).(User)
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "acl",
|
||||
|
@ -71,6 +73,9 @@ GLOBAL OPTIONS:
|
|||
cli.UintFlag{Name: "weight, w", Usage: "Assigns the ACL weight (priority)"},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
acl := ACL{
|
||||
Comment: c.String("comment"),
|
||||
HostPattern: c.String("pattern"),
|
||||
|
@ -118,6 +123,9 @@ GLOBAL OPTIONS:
|
|||
if c.NArg() < 1 {
|
||||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var acls []ACL
|
||||
if err := ACLsPreload(ACLsByIdentifiers(db, c.Args())).Find(&acls).Error; err != nil {
|
||||
|
@ -132,6 +140,9 @@ GLOBAL OPTIONS:
|
|||
Name: "ls",
|
||||
Usage: "Lists acls",
|
||||
Action: func(c *cli.Context) error {
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
var acls []ACL
|
||||
if err := db.Preload("UserGroups").Preload("HostGroups").Find(&acls).Error; err != nil {
|
||||
return err
|
||||
|
@ -171,6 +182,9 @@ GLOBAL OPTIONS:
|
|||
if c.NArg() < 1 {
|
||||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ACLsByIdentifiers(db, c.Args()).Delete(&ACL{}).Error
|
||||
},
|
||||
|
@ -192,6 +206,9 @@ GLOBAL OPTIONS:
|
|||
if c.NArg() < 1 {
|
||||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var acls []ACL
|
||||
if err := ACLsByIdentifiers(db, c.Args()).Find(&acls).Error; err != nil {
|
||||
|
@ -260,6 +277,10 @@ GLOBAL OPTIONS:
|
|||
},
|
||||
Description: "ssh admin@portal config backup > sshportal.bkp",
|
||||
Action: func(c *cli.Context) error {
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config := Config{}
|
||||
if err := db.Find(&config.Hosts).Error; err != nil {
|
||||
return err
|
||||
|
@ -303,6 +324,10 @@ GLOBAL OPTIONS:
|
|||
cli.BoolFlag{Name: "confirm", Usage: "yes, I want to replace everything with this backup!"},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config := Config{}
|
||||
|
||||
dec := json.NewDecoder(s)
|
||||
|
@ -413,6 +438,11 @@ GLOBAL OPTIONS:
|
|||
if c.NArg() != 1 {
|
||||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
host, err := NewHostFromURL(c.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -469,8 +499,16 @@ GLOBAL OPTIONS:
|
|||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
|
||||
if err := UserCheckRoles(myself, []string{"admin", "listhosts"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var hosts []Host
|
||||
if err := HostsPreload(HostsByIdentifiers(db, c.Args())).Find(&hosts).Error; err != nil {
|
||||
db = db.Preload("Groups")
|
||||
if UserHasRole(myself, "admin") {
|
||||
db = db.Preload("SSHKey")
|
||||
}
|
||||
if err := HostsByIdentifiers(db, c.Args()).Find(&hosts).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -482,6 +520,10 @@ GLOBAL OPTIONS:
|
|||
Name: "ls",
|
||||
Usage: "Lists hosts",
|
||||
Action: func(c *cli.Context) error {
|
||||
if err := UserCheckRoles(myself, []string{"admin", "listhosts"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var hosts []*Host
|
||||
if err := db.Preload("Groups").Find(&hosts).Error; err != nil {
|
||||
return err
|
||||
|
@ -528,6 +570,10 @@ GLOBAL OPTIONS:
|
|||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return HostsByIdentifiers(db, c.Args()).Delete(&Host{}).Error
|
||||
},
|
||||
}, {
|
||||
|
@ -548,6 +594,10 @@ GLOBAL OPTIONS:
|
|||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var hosts []Host
|
||||
if err := HostsByIdentifiers(db, c.Args()).Find(&hosts).Error; err != nil {
|
||||
return err
|
||||
|
@ -615,6 +665,10 @@ GLOBAL OPTIONS:
|
|||
cli.StringFlag{Name: "comment", Usage: "Adds a comment"},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hostGroup := HostGroup{
|
||||
Name: c.String("name"),
|
||||
Comment: c.String("comment"),
|
||||
|
@ -642,6 +696,10 @@ GLOBAL OPTIONS:
|
|||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var hostGroups []HostGroup
|
||||
if err := HostGroupsPreload(HostGroupsByIdentifiers(db, c.Args())).Find(&hostGroups).Error; err != nil {
|
||||
return err
|
||||
|
@ -655,6 +713,10 @@ GLOBAL OPTIONS:
|
|||
Name: "ls",
|
||||
Usage: "Lists host groups",
|
||||
Action: func(c *cli.Context) error {
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var hostGroups []*HostGroup
|
||||
if err := db.Preload("ACLs").Preload("Hosts").Find(&hostGroups).Error; err != nil {
|
||||
return err
|
||||
|
@ -685,6 +747,10 @@ GLOBAL OPTIONS:
|
|||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return HostGroupsByIdentifiers(db, c.Args()).Delete(&HostGroup{}).Error
|
||||
},
|
||||
},
|
||||
|
@ -693,6 +759,10 @@ GLOBAL OPTIONS:
|
|||
Name: "info",
|
||||
Usage: "Shows system-wide information",
|
||||
Action: func(c *cli.Context) error {
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(s, "Debug mode (server): %v\n", globalContext.Bool("debug"))
|
||||
hostname, _ := os.Hostname()
|
||||
fmt.Fprintf(s, "Hostname: %s\n", hostname)
|
||||
|
@ -708,7 +778,6 @@ GLOBAL OPTIONS:
|
|||
fmt.Fprintf(s, "Go version (build): %v\n", runtime.Version())
|
||||
fmt.Fprintf(s, "Uptime: %v\n", time.Since(startTime))
|
||||
|
||||
myself := s.Context().Value(userContextKey).(User)
|
||||
fmt.Fprintf(s, "User email: %v\n", myself.ID)
|
||||
fmt.Fprintf(s, "User email: %s\n", myself.Email)
|
||||
fmt.Fprintf(s, "Version: %s\n", VERSION)
|
||||
|
@ -737,6 +806,10 @@ GLOBAL OPTIONS:
|
|||
cli.StringFlag{Name: "comment", Usage: "Adds a comment"},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name := namesgenerator.GetRandomName(0)
|
||||
if c.String("name") != "" {
|
||||
name = c.String("name")
|
||||
|
@ -770,6 +843,10 @@ GLOBAL OPTIONS:
|
|||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var keys []SSHKey
|
||||
if err := SSHKeysByIdentifiers(db, c.Args()).Find(&keys).Error; err != nil {
|
||||
return err
|
||||
|
@ -783,6 +860,10 @@ GLOBAL OPTIONS:
|
|||
Name: "ls",
|
||||
Usage: "Lists keys",
|
||||
Action: func(c *cli.Context) error {
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var keys []SSHKey
|
||||
if err := db.Preload("Hosts").Find(&keys).Error; err != nil {
|
||||
return err
|
||||
|
@ -816,6 +897,10 @@ GLOBAL OPTIONS:
|
|||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return SSHKeysByIdentifiers(db, c.Args()).Delete(&SSHKey{}).Error
|
||||
},
|
||||
},
|
||||
|
@ -833,6 +918,10 @@ GLOBAL OPTIONS:
|
|||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var users []User
|
||||
if err := UsersPreload(UsersByIdentifiers(db, c.Args())).Find(&users).Error; err != nil {
|
||||
return err
|
||||
|
@ -857,6 +946,10 @@ GLOBAL OPTIONS:
|
|||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// FIXME: validate email
|
||||
|
||||
email := c.Args().First()
|
||||
|
@ -896,12 +989,16 @@ GLOBAL OPTIONS:
|
|||
Name: "ls",
|
||||
Usage: "Lists users",
|
||||
Action: func(c *cli.Context) error {
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var users []User
|
||||
if err := db.Preload("Groups").Preload("Keys").Find(&users).Error; err != nil {
|
||||
if err := db.Preload("Groups").Preload("Roles").Preload("Keys").Find(&users).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
table := tablewriter.NewWriter(s)
|
||||
table.SetHeader([]string{"ID", "Name", "Email", "Admin", "Keys", "Groups", "Comment"})
|
||||
table.SetHeader([]string{"ID", "Name", "Email", "Roles", "Keys", "Groups", "Comment"})
|
||||
table.SetBorder(false)
|
||||
table.SetCaption(true, fmt.Sprintf("Total: %d users.", len(users)))
|
||||
for _, user := range users {
|
||||
|
@ -909,15 +1006,15 @@ GLOBAL OPTIONS:
|
|||
for _, userGroup := range user.Groups {
|
||||
groupNames = append(groupNames, userGroup.Name)
|
||||
}
|
||||
isAdmin := ""
|
||||
if user.IsAdmin {
|
||||
isAdmin = "yes"
|
||||
roleNames := []string{}
|
||||
for _, role := range user.Roles {
|
||||
roleNames = append(roleNames, role.Name)
|
||||
}
|
||||
table.Append([]string{
|
||||
fmt.Sprintf("%d", user.ID),
|
||||
user.Name,
|
||||
user.Email,
|
||||
isAdmin,
|
||||
strings.Join(roleNames, ", "),
|
||||
fmt.Sprintf("%d", len(user.Keys)),
|
||||
strings.Join(groupNames, ", "),
|
||||
user.Comment,
|
||||
|
@ -937,6 +1034,10 @@ GLOBAL OPTIONS:
|
|||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return UsersByIdentifiers(db, c.Args()).Delete(&User{}).Error
|
||||
},
|
||||
}, {
|
||||
|
@ -946,16 +1047,20 @@ GLOBAL OPTIONS:
|
|||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "name, n", Usage: "Renames the user"},
|
||||
cli.StringFlag{Name: "email, e", Usage: "Updates the email"},
|
||||
cli.BoolFlag{Name: "set-admin", Usage: "Sets admin flag"},
|
||||
cli.BoolFlag{Name: "unset-admin", Usage: "Unsets admin flag"},
|
||||
cli.StringSliceFlag{Name: "assign-group, g", Usage: "Assign the user to a new `USERGROUPS`"},
|
||||
cli.StringSliceFlag{Name: "unassign-group", Usage: "Unassign the user from a `USERGROUPS`"},
|
||||
cli.StringSliceFlag{Name: "assign-role, r", Usage: "Assign the user to new `USERROLES`"},
|
||||
cli.StringSliceFlag{Name: "unassign-role", Usage: "Unassign the user from `USERROLES`"},
|
||||
cli.StringSliceFlag{Name: "assign-group, g", Usage: "Assign the user to new `USERGROUPS`"},
|
||||
cli.StringSliceFlag{Name: "unassign-group", Usage: "Unassign the user from `USERGROUPS`"},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.NArg() < 1 {
|
||||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// FIXME: check if unset-admin + user == myself
|
||||
var users []User
|
||||
if err := UsersByIdentifiers(db, c.Args()).Find(&users).Error; err != nil {
|
||||
|
@ -983,27 +1088,13 @@ GLOBAL OPTIONS:
|
|||
}
|
||||
}
|
||||
|
||||
// special fields
|
||||
if c.Bool("set-admin") {
|
||||
if err := model.Updates(User{IsAdmin: true}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.Bool("unset-admin") {
|
||||
if err := model.Updates(map[string]interface{}{"is_admin": false}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// associations
|
||||
var appendGroups []UserGroup
|
||||
var deleteGroups []UserGroup
|
||||
if err := UserGroupsByIdentifiers(db, c.StringSlice("assign-group")).Find(&appendGroups).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
var deleteGroups []UserGroup
|
||||
if err := UserGroupsByIdentifiers(db, c.StringSlice("unassign-group")).Find(&deleteGroups).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
|
@ -1012,6 +1103,21 @@ GLOBAL OPTIONS:
|
|||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
var appendRoles []UserRole
|
||||
if err := UserRolesByIdentifiers(db, c.StringSlice("assign-role")).Find(&appendRoles).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
var deleteRoles []UserRole
|
||||
if err := UserRolesByIdentifiers(db, c.StringSlice("unassign-role")).Find(&deleteRoles).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if err := model.Association("Roles").Append(&appendRoles).Delete(deleteRoles).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Commit().Error
|
||||
|
@ -1031,6 +1137,10 @@ GLOBAL OPTIONS:
|
|||
cli.StringFlag{Name: "comment", Usage: "Adds a comment"},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userGroup := UserGroup{
|
||||
Name: c.String("name"),
|
||||
Comment: c.String("comment"),
|
||||
|
@ -1043,10 +1153,8 @@ GLOBAL OPTIONS:
|
|||
return err
|
||||
}
|
||||
// FIXME: check if name already exists
|
||||
// FIXME: add myself to the new group
|
||||
|
||||
// add myself to the new group
|
||||
myself := s.Context().Value(userContextKey).(User)
|
||||
// FIXME: use foreign key with ID to avoid updating the user with the context
|
||||
userGroup.Users = []*User{&myself}
|
||||
|
||||
if err := db.Create(&userGroup).Error; err != nil {
|
||||
|
@ -1064,6 +1172,10 @@ GLOBAL OPTIONS:
|
|||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var userGroups []UserGroup
|
||||
if err := UserGroupsPreload(UserGroupsByIdentifiers(db, c.Args())).Find(&userGroups).Error; err != nil {
|
||||
return err
|
||||
|
@ -1077,6 +1189,10 @@ GLOBAL OPTIONS:
|
|||
Name: "ls",
|
||||
Usage: "Lists user groups",
|
||||
Action: func(c *cli.Context) error {
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var userGroups []*UserGroup
|
||||
if err := db.Preload("ACLs").Preload("Users").Find(&userGroups).Error; err != nil {
|
||||
return err
|
||||
|
@ -1107,6 +1223,10 @@ GLOBAL OPTIONS:
|
|||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return UserGroupsByIdentifiers(db, c.Args()).Delete(&UserGroup{}).Error
|
||||
},
|
||||
},
|
||||
|
@ -1128,6 +1248,10 @@ GLOBAL OPTIONS:
|
|||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var user User
|
||||
if err := UsersByIdentifiers(db, c.Args()).First(&user).Error; err != nil {
|
||||
return err
|
||||
|
@ -1171,6 +1295,10 @@ GLOBAL OPTIONS:
|
|||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var userKeys []UserKey
|
||||
if err := UserKeysPreload(UserKeysByIdentifiers(db, c.Args())).Find(&userKeys).Error; err != nil {
|
||||
return err
|
||||
|
@ -1184,6 +1312,10 @@ GLOBAL OPTIONS:
|
|||
Name: "ls",
|
||||
Usage: "Lists userkeys",
|
||||
Action: func(c *cli.Context) error {
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var userkeys []UserKey
|
||||
if err := db.Preload("User").Find(&userkeys).Error; err != nil {
|
||||
return err
|
||||
|
@ -1212,6 +1344,10 @@ GLOBAL OPTIONS:
|
|||
return cli.ShowSubcommandHelp(c)
|
||||
}
|
||||
|
||||
if err := UserCheckRoles(myself, []string{"admin"}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return UserKeysByIdentifiers(db, c.Args()).Delete(&UserKey{}).Error
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue