package models import ( "encoding/json" "errors" "time" "github.com/GehirnInc/crypt" _ "github.com/GehirnInc/crypt/sha256_crypt" _ "github.com/GehirnInc/crypt/sha512_crypt" "github.com/google/uuid" ) type Disposition string const ( DispositionIntrinsic Disposition = "intrinsic" DispositionSystem Disposition = "system" DispositionDynamic Disposition = "dynamic" DispositionRegular Disposition = "regular" DispositionContainer Disposition = "container" DispositionReserved Disposition = "reserved" ) type ResourceLimit struct { Cur uint64 `mapstructure:"cur"` Max uint64 `mapstructure:"max"` } type UserRecord struct { UUID uuid.UUID `mapstructure:"uuid"` UserName string `mapstructure:"userName"` Realm string `mapstructure:"realm,omitempty"` RealName string `mapstructure:"realName,omitempty"` EmailAddress string `mapstructure:"emailAddress,omitempty"` IconName string `mapstructure:"iconName,omitempty"` Location string `mapstructure:"location,omitempty"` Disposition Disposition `mapstructure:"disposition,omitempty"` Shell string `mapstructure:"shell,omitempty"` Ummask *int32 `mapstructure:"umask,omitempty"` Environment []string `mapstructure:"environment,omitempty"` TimeZone string `mapstructure:"timeZone,omitempty"` PreferredLanguage string `mapstructure:"preferredLanguage,omitempty"` NiceLevel int32 `mapstructure:"niceLevel,omitempty"` ResourceLimits map[string]ResourceLimit `mapstructure:"resourceLimits,omitempty"` Locked bool `mapstructure:"locked,omitempty"` NotBeforeUSec *int64 `mapstructure:"notBeforeUSec,omitempty"` NotAfterUSec *int64 `mapstructure:"notAfterUSec,omitempty"` HomeDirectory string `mapstructure:"homeDirectory,omitempty"` UID uint32 `mapstructure:"uid,omitempty"` GID uint32 `mapstructure:"gid,omitempty"` Service string `mapstructure:"service,omitempty"` Privileged *UserPrivileged `mapstructure:"privileged,omitempty"` Other map[string]interface{} `mapstructure:"-,remain"` } func (ur *UserRecord) ID() uuid.UUID { return ur.UUID } func (ur *UserRecord) SetID(id uuid.UUID) { ur.UUID = id } func (ur *UserRecord) Type() EntityType { return TypeUser } func (ur *UserRecord) Name() string { return ur.UserName } func (ur *UserRecord) Email() string { return ur.EmailAddress } func (ur *UserRecord) StripPrivileged() { ur.Privileged = nil } func (ur *UserRecord) MarshalJSON() ([]byte, error) { m := make(map[string]interface{}) for k, v := range ur.Other { m[k] = v } if err := decode(ur, &m); err != nil { return nil, err } m["@type"] = string(TypeUser) return json.Marshal(m) } func (ur *UserRecord) UnmarshalJSON(data []byte) error { m := make(map[string]interface{}) if err := json.Unmarshal(data, m); err != nil { return err } delete(m, "@type") return decode(m, ur) } func (ur *UserRecord) SynthesizeGroup() *GroupRecord { return &GroupRecord{ UUID: ur.UUID, GroupName: ur.UserName, Realm: ur.Realm, Description: ur.RealName, Disposition: ur.Disposition, GID: ur.GID, Service: ur.Service, } } func (ur *UserRecord) IsUserAllowedToLogin() bool { if ur.Locked { return false } nowMicros := time.Now().UnixMicro() if ur.NotBeforeUSec != nil && *ur.NotBeforeUSec > nowMicros { return false } if ur.NotAfterUSec != nil && *ur.NotAfterUSec < nowMicros { return false } return true } func (ur *UserRecord) EnsurePrivileged() *UserPrivileged { if ur.Privileged == nil { ur.Privileged = new(UserPrivileged) } return ur.Privileged } type UserPrivileged struct { HashedPassword []string `mapstructure:"hashedPassword,omitempty"` SSHAuthorizedKeys []string `mapstructure:"sshAuthorizedKeys,omitempty"` PublicKeyCredentials []PublicKeyCredential `mapstructure:"publicKeyCredentials,omitempty"` Other map[string]interface{} `mapstructure:"-,remain"` } func (priv *UserPrivileged) CheckPassword(pw string) bool { if priv == nil { return false } k := []byte(pw) for _, possibleHash := range priv.HashedPassword { if !crypt.IsHashSupported(possibleHash) { continue } cr := crypt.NewFromHash(possibleHash) err := cr.Verify(possibleHash, k) if err == nil { return true } else if errors.Is(err, crypt.ErrKeyMismatch) { return false } else { continue } } return false }