nightingale/src/models/user.go

488 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package models
import (
"fmt"
"os"
"strings"
"time"
"github.com/pkg/errors"
"github.com/toolkits/pkg/slice"
"github.com/toolkits/pkg/str"
"gorm.io/gorm"
"github.com/didi/nightingale/v5/src/pkg/ldapx"
"github.com/didi/nightingale/v5/src/pkg/ormx"
"github.com/didi/nightingale/v5/src/webapi/config"
)
type User struct {
Id int64 `json:"id" gorm:"primaryKey"`
Username string `json:"username"`
Nickname string `json:"nickname"`
Password string `json:"-"`
Phone string `json:"phone"`
Email string `json:"email"`
Portrait string `json:"portrait"`
Roles string `json:"-"` // 这个字段写入数据库
RolesLst []string `json:"roles" gorm:"-"` // 这个字段和前端交互
Contacts ormx.JSONObj `json:"contacts"` // 内容为 map[string]string 结构
CreateAt int64 `json:"create_at"`
CreateBy string `json:"create_by"`
UpdateAt int64 `json:"update_at"`
UpdateBy string `json:"update_by"`
Admin bool `json:"admin" gorm:"-"` // 方便前端使用
}
func (u *User) TableName() string {
return "user"
}
func (u *User) IsAdmin() bool {
for i := 0; i < len(u.RolesLst); i++ {
if u.RolesLst[i] == config.C.AdminRole {
return true
}
}
return false
}
func (u *User) Verify() error {
u.Username = strings.TrimSpace(u.Username)
if u.Username == "" {
return errors.New("Username is blank")
}
if str.Dangerous(u.Username) {
return errors.New("Username has invalid characters")
}
if str.Dangerous(u.Nickname) {
return errors.New("Nickname has invalid characters")
}
if u.Phone != "" && !str.IsPhone(u.Phone) {
return errors.New("Phone invalid")
}
if u.Email != "" && !str.IsMail(u.Email) {
return errors.New("Email invalid")
}
return nil
}
func (u *User) Add() error {
user, err := UserGetByUsername(u.Username)
if err != nil {
return errors.WithMessage(err, "failed to query user")
}
if user != nil {
return errors.New("Username already exists")
}
now := time.Now().Unix()
u.CreateAt = now
u.UpdateAt = now
return Insert(u)
}
func (u *User) UpdateAllFields() error {
if err := u.Verify(); err != nil {
return err
}
u.UpdateAt = time.Now().Unix()
return DB().Model(u).Select("*").Updates(u).Error
}
func (u *User) UpdatePassword(password, updateBy string) error {
return DB().Model(u).Updates(map[string]interface{}{
"password": password,
"update_at": time.Now().Unix(),
"update_by": updateBy,
}).Error
}
func (u *User) Del() error {
return DB().Transaction(func(tx *gorm.DB) error {
if err := tx.Where("user_id=?", u.Id).Delete(&UserGroupMember{}).Error; err != nil {
return err
}
if err := tx.Where("id=?", u.Id).Delete(&User{}).Error; err != nil {
return err
}
return nil
})
}
func (u *User) ChangePassword(oldpass, newpass string) error {
_oldpass, err := CryptoPass(oldpass)
if err != nil {
return err
}
_newpass, err := CryptoPass(newpass)
if err != nil {
return err
}
if u.Password != _oldpass {
return errors.New("Incorrect old password")
}
return u.UpdatePassword(_newpass, u.Username)
}
func UserGet(where string, args ...interface{}) (*User, error) {
var lst []*User
err := DB().Where(where, args...).Find(&lst).Error
if err != nil {
return nil, err
}
if len(lst) == 0 {
return nil, nil
}
lst[0].RolesLst = strings.Fields(lst[0].Roles)
lst[0].Admin = lst[0].IsAdmin()
return lst[0], nil
}
func UserGetByUsername(username string) (*User, error) {
return UserGet("username=?", username)
}
func UserGetById(id int64) (*User, error) {
return UserGet("id=?", id)
}
func InitRoot() {
user, err := UserGetByUsername("root")
if err != nil {
fmt.Println("failed to query user root:", err)
os.Exit(1)
}
if user == nil {
return
}
if len(user.Password) > 31 {
// already done before
return
}
newPass, err := CryptoPass(user.Password)
if err != nil {
fmt.Println("failed to crypto pass:", err)
os.Exit(1)
}
err = DB().Model(user).Update("password", newPass).Error
if err != nil {
fmt.Println("failed to update root password:", err)
os.Exit(1)
}
fmt.Println("root password init done")
}
func PassLogin(username, pass string) (*User, error) {
user, err := UserGetByUsername(username)
if err != nil {
return nil, err
}
if user == nil {
return nil, fmt.Errorf("Username or password invalid")
}
loginPass, err := CryptoPass(pass)
if err != nil {
return nil, err
}
if loginPass != user.Password {
return nil, fmt.Errorf("Username or password invalid")
}
return user, nil
}
func LdapLogin(username, pass string) (*User, error) {
sr, err := ldapx.LdapReq(username, pass)
if err != nil {
return nil, err
}
user, err := UserGetByUsername(username)
if err != nil {
return nil, err
}
if user == nil {
// default user settings
user = &User{
Username: username,
Nickname: username,
}
}
// copy attributes from ldap
attrs := ldapx.LDAP.Attributes
if attrs.Nickname != "" {
user.Nickname = sr.Entries[0].GetAttributeValue(attrs.Nickname)
}
if attrs.Email != "" {
user.Email = sr.Entries[0].GetAttributeValue(attrs.Email)
}
if attrs.Phone != "" {
user.Phone = sr.Entries[0].GetAttributeValue(attrs.Phone)
}
if user.Id > 0 {
if ldapx.LDAP.CoverAttributes {
err := DB().Updates(user).Error
if err != nil {
return nil, errors.WithMessage(err, "failed to update user")
}
}
return user, nil
}
now := time.Now().Unix()
if len(config.C.LDAP.DefaultRoles) == 0 {
config.C.LDAP.DefaultRoles = []string{"Standard"}
}
user.Password = "******"
user.Portrait = ""
user.Roles = strings.Join(config.C.LDAP.DefaultRoles, " ")
user.RolesLst = config.C.LDAP.DefaultRoles
user.Contacts = []byte("{}")
user.CreateAt = now
user.UpdateAt = now
user.CreateBy = "ldap"
user.UpdateBy = "ldap"
err = DB().Create(user).Error
return user, err
}
func UserTotal(query string) (num int64, err error) {
if query != "" {
q := "%" + query + "%"
num, err = Count(DB().Model(&User{}).Where("username like ? or nickname like ? or phone like ? or email like ?", q, q, q, q))
} else {
num, err = Count(DB().Model(&User{}))
}
if err != nil {
return num, errors.WithMessage(err, "failed to count user")
}
return num, nil
}
func UserGets(query string, limit, offset int) ([]User, error) {
session := DB().Limit(limit).Offset(offset).Order("username")
if query != "" {
q := "%" + query + "%"
session = session.Where("username like ? or nickname like ? or phone like ? or email like ?", q, q, q, q)
}
var users []User
err := session.Find(&users).Error
if err != nil {
return users, errors.WithMessage(err, "failed to query user")
}
for i := 0; i < len(users); i++ {
users[i].RolesLst = strings.Fields(users[i].Roles)
users[i].Admin = users[i].IsAdmin()
}
return users, nil
}
func UserGetAll() ([]*User, error) {
var lst []*User
err := DB().Find(&lst).Error
if err == nil {
for i := 0; i < len(lst); i++ {
lst[i].RolesLst = strings.Fields(lst[i].Roles)
lst[i].Admin = lst[i].IsAdmin()
}
}
return lst, err
}
func UserGetsByIds(ids []int64) ([]User, error) {
if len(ids) == 0 {
return []User{}, nil
}
var lst []User
err := DB().Where("id in ?", ids).Order("username").Find(&lst).Error
if err == nil {
for i := 0; i < len(lst); i++ {
lst[i].RolesLst = strings.Fields(lst[i].Roles)
lst[i].Admin = lst[i].IsAdmin()
}
}
return lst, err
}
func (u *User) CanModifyUserGroup(ug *UserGroup) (bool, error) {
// 我是管理员,自然可以
if u.IsAdmin() {
return true, nil
}
// 我是创建者,自然可以
if ug.CreateBy == u.Username {
return true, nil
}
// 我是成员,也可以吧,简单搞
num, err := UserGroupMemberCount("user_id=? and group_id=?", u.Id, ug.Id)
if err != nil {
return false, err
}
return num > 0, nil
}
func (u *User) CanDoBusiGroup(bg *BusiGroup, permFlag ...string) (bool, error) {
if u.IsAdmin() {
return true, nil
}
// 我在任意一个UserGroup里就有权限
ugids, err := UserGroupIdsOfBusiGroup(bg.Id, permFlag...)
if err != nil {
return false, err
}
if len(ugids) == 0 {
return false, nil
}
num, err := UserGroupMemberCount("user_id = ? and group_id in ?", u.Id, ugids)
return num > 0, err
}
func (u *User) CheckPerm(operation string) (bool, error) {
if u.IsAdmin() {
return true, nil
}
return RoleHasOperation(u.RolesLst, operation)
}
func UserStatistics() (*Statistics, error) {
session := DB().Model(&User{}).Select("count(*) as total", "max(update_at) as last_updated")
var stats []*Statistics
err := session.Find(&stats).Error
if err != nil {
return nil, err
}
return stats[0], nil
}
func (u *User) NopriIdents(idents []string) ([]string, error) {
if u.IsAdmin() {
return []string{}, nil
}
ugids, err := MyGroupIds(u.Id)
if err != nil {
return []string{}, err
}
if len(ugids) == 0 {
return idents, nil
}
bgids, err := BusiGroupIds(ugids, "rw")
if err != nil {
return []string{}, err
}
if len(bgids) == 0 {
return idents, nil
}
var arr []string
err = DB().Model(&Target{}).Where("group_id in ?", bgids).Pluck("ident", &arr).Error
if err != nil {
return []string{}, err
}
return slice.SubString(idents, arr), nil
}
// 我是管理员,返回所有
// 或者我是成员
func (u *User) BusiGroups(limit int, query string, all ...bool) ([]BusiGroup, error) {
session := DB().Order("name").Limit(limit)
var lst []BusiGroup
if u.IsAdmin() || (len(all) > 0 && all[0]) {
err := session.Where("name like ?", "%"+query+"%").Find(&lst).Error
return lst, err
}
userGroupIds, err := MyGroupIds(u.Id)
if err != nil {
return nil, errors.WithMessage(err, "failed to get MyGroupIds")
}
busiGroupIds, err := BusiGroupIds(userGroupIds)
if err != nil {
return nil, errors.WithMessage(err, "failed to get BusiGroupIds")
}
if len(busiGroupIds) == 0 {
return lst, nil
}
err = session.Where("id in ?", busiGroupIds).Where("name like ?", "%"+query+"%").Find(&lst).Error
return lst, err
}
func (u *User) UserGroups(limit int, query string) ([]UserGroup, error) {
session := DB().Order("name").Limit(limit)
var lst []UserGroup
if u.IsAdmin() {
err := session.Where("name like ?", "%"+query+"%").Find(&lst).Error
return lst, err
}
ids, err := MyGroupIds(u.Id)
if err != nil {
return nil, errors.WithMessage(err, "failed to get MyGroupIds")
}
session = session.Where("create_by = ? and name like ?", u.Username, "%"+query+"%")
if len(ids) > 0 {
session = session.Or("id in ?", ids)
}
err = session.Find(&lst).Error
return lst, err
}