mirror of
https://github.com/moul/sshportal.git
synced 2025-01-29 02:50:15 +08:00
211 lines
5.9 KiB
Go
211 lines
5.9 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/asaskevich/govalidator"
|
|
"github.com/gliderlabs/ssh"
|
|
"github.com/jinzhu/gorm"
|
|
)
|
|
|
|
type Config struct {
|
|
SSHKeys []*SSHKey `json:"keys"`
|
|
Hosts []*Host `json:"hosts"`
|
|
UserKeys []*UserKey `json:"user_keys"`
|
|
Users []*User `json:"users"`
|
|
UserGroups []*UserGroup `json:"user_groups"`
|
|
HostGroups []*HostGroup `json:"host_groups"`
|
|
ACLs []*ACL `json:"acls"`
|
|
Settings []*Setting `json:"settings"`
|
|
Date time.Time `json:"date"`
|
|
}
|
|
|
|
type Setting struct {
|
|
gorm.Model
|
|
Name string `valid:"required"`
|
|
Value string `valid:"required"`
|
|
}
|
|
|
|
type SSHKey struct {
|
|
// FIXME: use uuid for ID
|
|
gorm.Model
|
|
Name string `valid:"required,length(1|32),unix_user"`
|
|
Type string `valid:"required"`
|
|
Length uint `valid:"required"`
|
|
Fingerprint string `valid:"optional"`
|
|
PrivKey string `sql:"size:10000" valid:"required"`
|
|
PubKey string `sql:"size:10000" valid:"optional"`
|
|
Hosts []*Host `gorm:"ForeignKey:SSHKeyID"`
|
|
Comment string `valid:"optional"`
|
|
}
|
|
|
|
type Host struct {
|
|
// FIXME: use uuid for ID
|
|
gorm.Model
|
|
Name string `gorm:"size:32" valid:"required,length(1|32),unix_user"`
|
|
Addr string `valid:"required"`
|
|
User string `valid:"optional"`
|
|
Password string `valid:"optional"`
|
|
SSHKey *SSHKey `gorm:"ForeignKey:SSHKeyID"`
|
|
SSHKeyID uint `gorm:"index"`
|
|
Groups []*HostGroup `gorm:"many2many:host_host_groups;"`
|
|
Fingerprint string `valid:"optional"` // FIXME: replace with hostKey ?
|
|
Comment string `valid:"optional"`
|
|
}
|
|
|
|
type UserKey struct {
|
|
gorm.Model
|
|
Key []byte `sql:"size:10000" valid:"required,length(1|10000)"`
|
|
UserID uint ``
|
|
User *User `gorm:"ForeignKey:UserID"`
|
|
Comment string `valid:"optional"`
|
|
}
|
|
|
|
type User struct {
|
|
// FIXME: use uuid for ID
|
|
gorm.Model
|
|
IsAdmin bool
|
|
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)"`
|
|
}
|
|
|
|
type UserGroup struct {
|
|
gorm.Model
|
|
Name string `valid:"required,length(1|32),unix_user"`
|
|
Users []*User `gorm:"many2many:user_user_groups;"`
|
|
ACLs []*ACL `gorm:"many2many:user_group_acls;"`
|
|
Comment string `valid:"optional"`
|
|
}
|
|
|
|
type HostGroup struct {
|
|
gorm.Model
|
|
Name string `valid:"required,length(1|32),unix_user"`
|
|
Hosts []*Host `gorm:"many2many:host_host_groups;"`
|
|
ACLs []*ACL `gorm:"many2many:host_group_acls;"`
|
|
Comment string `valid:"optional"`
|
|
}
|
|
|
|
type ACL struct {
|
|
gorm.Model
|
|
HostGroups []*HostGroup `gorm:"many2many:host_group_acls;"`
|
|
UserGroups []*UserGroup `gorm:"many2many:user_group_acls;"`
|
|
HostPattern string `valid:"optional"`
|
|
Action string `valid:"required"`
|
|
Weight uint ``
|
|
Comment string `valid:"optional"`
|
|
}
|
|
|
|
func init() {
|
|
unixUserRegexp := regexp.MustCompile("[a-z_][a-z0-9_-]*")
|
|
|
|
govalidator.CustomTypeTagMap.Set("unix_user", govalidator.CustomTypeValidator(func(i interface{}, context interface{}) bool {
|
|
name, ok := i.(string)
|
|
if !ok {
|
|
return false
|
|
}
|
|
return unixUserRegexp.MatchString(name)
|
|
}))
|
|
}
|
|
|
|
func RemoteHostFromSession(s ssh.Session, db *gorm.DB) (*Host, error) {
|
|
var host Host
|
|
db.Preload("SSHKey").Where("name = ?", s.User()).Find(&host)
|
|
if host.Name == "" {
|
|
// FIXME: add available hosts
|
|
return nil, fmt.Errorf("No such target: %q", s.User())
|
|
}
|
|
return &host, nil
|
|
}
|
|
|
|
func (host *Host) URL() string {
|
|
return fmt.Sprintf("%s@%s", host.User, host.Addr)
|
|
}
|
|
|
|
func NewHostFromURL(rawurl string) (*Host, error) {
|
|
if !strings.Contains(rawurl, "://") {
|
|
rawurl = "ssh://" + rawurl
|
|
}
|
|
u, err := url.Parse(rawurl)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
host := Host{Addr: u.Host}
|
|
if !strings.Contains(host.Addr, ":") {
|
|
host.Addr += ":22" // add port if not present
|
|
}
|
|
host.User = "root" // default username
|
|
if u.User != nil {
|
|
password, _ := u.User.Password()
|
|
host.Password = password
|
|
host.User = u.User.Username()
|
|
}
|
|
return &host, nil
|
|
}
|
|
|
|
func (host *Host) Hostname() string {
|
|
return strings.Split(host.Addr, ":")[0]
|
|
}
|
|
|
|
// Host helpers
|
|
func HostsPreload(db *gorm.DB) *gorm.DB {
|
|
return db.Preload("Groups").Preload("SSHKey")
|
|
}
|
|
func HostsByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
|
|
return db.Where("id IN (?)", identifiers).Or("name IN (?)", identifiers)
|
|
}
|
|
|
|
// SSHKey helpers
|
|
func SSHKeysPreload(db *gorm.DB) *gorm.DB {
|
|
return db.Preload("Hosts")
|
|
}
|
|
func SSHKeysByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
|
|
return db.Where("id IN (?)", identifiers).Or("name IN (?)", identifiers)
|
|
}
|
|
|
|
// HostGroup helpers
|
|
func HostGroupsPreload(db *gorm.DB) *gorm.DB {
|
|
return db.Preload("ACLs").Preload("Hosts")
|
|
}
|
|
func HostGroupsByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
|
|
return db.Where("id IN (?)", identifiers).Or("name IN (?)", identifiers)
|
|
}
|
|
|
|
// UserGroup heleprs
|
|
func UserGroupsPreload(db *gorm.DB) *gorm.DB {
|
|
return db.Preload("ACLs").Preload("Users")
|
|
}
|
|
func UserGroupsByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
|
|
return db.Where("id IN (?)", identifiers).Or("name IN (?)", identifiers)
|
|
}
|
|
|
|
// User helpers
|
|
func UsersPreload(db *gorm.DB) *gorm.DB {
|
|
return db.Preload("Groups").Preload("Keys")
|
|
}
|
|
func UsersByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
|
|
return db.Where("id IN (?)", identifiers).Or("email IN (?)", identifiers)
|
|
}
|
|
|
|
// ACL helpers
|
|
func ACLsPreload(db *gorm.DB) *gorm.DB {
|
|
return db.Preload("UserGroups").Preload("HostGroups")
|
|
}
|
|
func ACLsByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
|
|
return db.Where("id IN (?)", identifiers)
|
|
}
|
|
|
|
// UserKey helpers
|
|
func UserKeysPreload(db *gorm.DB) *gorm.DB {
|
|
return db.Preload("User")
|
|
}
|
|
func UserKeysByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
|
|
return db.Where("id IN (?)", identifiers)
|
|
}
|