func (m *UserManager) UpdateMetadata(ipaddress, useragent, username, csrftoken string) error {
	user, err := m.Get(username)
	if err != nil {
		return fmt.Errorf("error getting user %v", err)
	}
	if err := m.DB.Model(&user).Updates(
		AdminUser{
			LastIPAddress: ipaddress,
			LastUserAgent: useragent,
			CSRFToken:     csrftoken,
			LastAccess:    time.Now(),
		}).Error; err != nil {
		return fmt.Errorf("Update %v", err)
	}
	return nil
}
UpdateMetadata updates IP, User Agent and Last Access for a given user
package users
import (
	"encoding/json"
	"fmt"
	"log"
	"time"
	"github.com/golang-jwt/jwt/v4"
	"github.com/jinzhu/gorm"
	"github.com/jinzhu/gorm/dialects/postgres"
	"github.com/jmpsec/osctrl/types"
	"golang.org/x/crypto/bcrypt"
)
type AdminUser struct {
	gorm.Model
	Username      string `gorm:"index"`
	Email         string
	Fullname      string
	PassHash      string
	APIToken      string
	TokenExpire   time.Time
	Admin         bool
	DefaultEnv    string
	CSRFToken     string
	Permissions   postgres.Jsonb
	LastIPAddress string
	LastUserAgent string
	LastAccess    time.Time
	LastTokenUse  time.Time
}
type TokenClaims struct {
	Username string `json:"username"`
	jwt.StandardClaims
}
type UserManager struct {
	DB        *gorm.DB
	JWTConfig *types.JSONConfigurationJWT
}
func CreateUserManager(backend *gorm.DB, jwtconfig *types.JSONConfigurationJWT) *UserManager {
	var u *UserManager
	u = &UserManager{DB: backend, JWTConfig: jwtconfig}
	if err := backend.AutoMigrate(AdminUser{}).Error; err != nil {
		log.Fatalf("Failed to AutoMigrate table (admin_users): %v", err)
	}
	return u
}
func (m *UserManager) HashTextWithSalt(text string) (string, error) {
	saltedBytes := []byte(text)
	hashedBytes, err := bcrypt.GenerateFromPassword(saltedBytes, bcrypt.DefaultCost)
	if err != nil {
		return "", err
	}
	hash := string(hashedBytes)
	return hash, nil
}
func (m *UserManager) HashPasswordWithSalt(password string) (string, error) {
	return m.HashTextWithSalt(password)
}
func (m *UserManager) CheckLoginCredentials(username, password string) (bool, AdminUser) {
	user, err := m.Get(username)
	if err != nil {
		return false, AdminUser{}
	}
	p := []byte(password)
	existing := []byte(user.PassHash)
	err = bcrypt.CompareHashAndPassword(existing, p)
	if err != nil {
		return false, AdminUser{}
	}
	return true, user
}
func (m *UserManager) CreateToken(username string) (string, time.Time, error) {
	expirationTime := time.Now().Add(time.Hour * time.Duration(m.JWTConfig.HoursToExpire))
	claims := &TokenClaims{
		Username: username,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expirationTime.Unix(),
			Issuer:    "admin",
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, err := token.SignedString([]byte(m.JWTConfig.JWTSecret))
	if err != nil {
		return "", time.Now(), err
	}
	return tokenString, expirationTime, nil
}
func (m *UserManager) CheckToken(jwtSecret, tokenStr string) (TokenClaims, bool) {
	claims := &TokenClaims{}
	tkn, err := jwt.ParseWithClaims(tokenStr, claims, func(token *jwt.Token) (interface{}, error) {
		return []byte(jwtSecret), nil
	})
	if err != nil {
		log.Printf("Error %v", err)
		return *claims, false
	}
	if !tkn.Valid {
		log.Println("Not valid")
		return *claims, false
	}
	return *claims, true
}
func (m *UserManager) Get(username string) (AdminUser, error) {
	var user AdminUser
	if err := m.DB.Where("username = ?", username).First(&user).Error; err != nil {
		return user, err
	}
	return user, nil
}
func (m *UserManager) Create(user AdminUser) error {
	if m.DB.NewRecord(user) {
		if err := m.DB.Create(&user).Error; err != nil {
			return fmt.Errorf("Create AdminUser %v", err)
		}
	} else {
		return fmt.Errorf("db.NewRecord did not return true")
	}
	return nil
}
func (m *UserManager) New(username, password, email, fullname, defaultEnv string, admin bool) (AdminUser, error) {
	if !m.Exists(username) {
		passhash, err := m.HashPasswordWithSalt(password)
		if err != nil {
			return AdminUser{}, err
		}
		permsRaw, err := json.Marshal(m.GenPermissions([]string{}, EnvLevel))
		if err != nil {
			permsRaw = []byte("{}")
		}
		return AdminUser{
			Username:    username,
			PassHash:    passhash,
			Admin:       admin,
			DefaultEnv:  defaultEnv,
			Permissions: postgres.Jsonb{RawMessage: permsRaw},
			Email:       email,
			Fullname:    fullname,
		}, nil
	}
	return AdminUser{}, fmt.Errorf("%s already exists", username)
}
func (m *UserManager) Exists(username string) bool {
	var results int
	m.DB.Model(&AdminUser{}).Where("username = ?", username).Count(&results)
	return (results > 0)
}
func (m *UserManager) ExistsGet(username string) (bool, AdminUser) {
	user, err := m.Get(username)
	if err != nil {
		return false, AdminUser{}
	}
	return true, user
}
func (m *UserManager) IsAdmin(username string) bool {
	var results int
	m.DB.Model(&AdminUser{}).Where("username = ? AND admin = ?", username, true).Count(&results)
	return (results > 0)
}
func (m *UserManager) ChangeAdmin(username string, admin bool) error {
	user, err := m.Get(username)
	if err != nil {
		return fmt.Errorf("error getting user %v", err)
	}
	if admin != user.Admin {
		if err := m.DB.Model(&user).Updates(map[string]interface{}{"admin": admin}).Error; err != nil {
			return err
		}
	}
	return nil
}
func (m *UserManager) ChangePermissions(username string, permissions UserPermissions) error {
	user, err := m.Get(username)
	if err != nil {
		return fmt.Errorf("error getting user %v", err)
	}
	rawPerms, err := json.Marshal(permissions)
	if err != nil {
		return err
	}
	if err := m.DB.Model(&user).Update("permissions", postgres.Jsonb{RawMessage: rawPerms}).Error; err != nil {
		return fmt.Errorf("Update %v", err)
	}
	return nil
}
func (m *UserManager) All() ([]AdminUser, error) {
	var users []AdminUser
	if err := m.DB.Find(&users).Error; err != nil {
		return users, err
	}
	return users, nil
}
func (m *UserManager) Delete(username string) error {
	user, err := m.Get(username)
	if err != nil {
		return fmt.Errorf("error getting user %v", err)
	}
	if err := m.DB.Unscoped().Delete(&user).Error; err != nil {
		return fmt.Errorf("Delete %v", err)
	}
	return nil
}
func (m *UserManager) ChangePassword(username, password string) error {
	user, err := m.Get(username)
	if err != nil {
		return fmt.Errorf("error getting user %v", err)
	}
	passhash, err := m.HashPasswordWithSalt(password)
	if err != nil {
		return err
	}
	if passhash != user.PassHash {
		if err := m.DB.Model(&user).Update("pass_hash", passhash).Error; err != nil {
			return fmt.Errorf("Update %v", err)
		}
	}
	return nil
}
func (m *UserManager) UpdateToken(username, token string, exp time.Time) error {
	user, err := m.Get(username)
	if err != nil {
		return fmt.Errorf("error getting user %v", err)
	}
	if token != user.APIToken {
		if err := m.DB.Model(&user).Updates(
			AdminUser{
				APIToken:    token,
				TokenExpire: exp,
			}).Error; err != nil {
			return fmt.Errorf("Update %v", err)
		}
	}
	return nil
}
func (m *UserManager) ChangeEmail(username, email string) error {
	user, err := m.Get(username)
	if err != nil {
		return fmt.Errorf("error getting user %v", err)
	}
	if email != user.Email {
		if err := m.DB.Model(&user).Update("email", email).Error; err != nil {
			return fmt.Errorf("Update %v", err)
		}
	}
	return nil
}
func (m *UserManager) ChangeDefaultEnv(username, env string) error {
	if err := m.DB.Model(&AdminUser{}).Where("username = ?", username).Update("default_env", env).Error; err != nil {
		return fmt.Errorf("Update default_env %v", err)
	}
	return nil
}
func (m *UserManager) ChangeFullname(username, fullname string) error {
	user, err := m.Get(username)
	if err != nil {
		return fmt.Errorf("error getting user %v", err)
	}
	if fullname != user.Fullname {
		if err := m.DB.Model(&user).Update("fullname", fullname).Error; err != nil {
			return fmt.Errorf("Update %v", err)
		}
	}
	return nil
}