487 lines
12 KiB
Go
487 lines
12 KiB
Go
package service
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"next-terminal/server/common/nt"
|
|
"strings"
|
|
|
|
"next-terminal/server/branding"
|
|
"next-terminal/server/common"
|
|
"next-terminal/server/dto"
|
|
"next-terminal/server/env"
|
|
"next-terminal/server/global/cache"
|
|
"next-terminal/server/log"
|
|
"next-terminal/server/model"
|
|
"next-terminal/server/repository"
|
|
"next-terminal/server/utils"
|
|
|
|
"golang.org/x/net/context"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
const SuperAdminID = `abcdefghijklmnopqrstuvwxyz`
|
|
|
|
var UserService = new(userService)
|
|
|
|
type userService struct {
|
|
baseService
|
|
}
|
|
|
|
func (service userService) InitUser() (err error) {
|
|
|
|
users, err := repository.UserRepository.FindAll(context.TODO())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(users) == 0 {
|
|
initPassword := "admin"
|
|
var pass []byte
|
|
if pass, err = utils.Encoder.Encode([]byte(initPassword)); err != nil {
|
|
return err
|
|
}
|
|
|
|
user := model.User{
|
|
ID: SuperAdminID,
|
|
Username: "admin",
|
|
Password: string(pass),
|
|
Nickname: "超级管理员",
|
|
Type: nt.TypeAdmin,
|
|
Created: common.NowJsonTime(),
|
|
Status: nt.StatusEnabled,
|
|
}
|
|
if err := repository.UserRepository.Create(context.TODO(), &user); err != nil {
|
|
return err
|
|
}
|
|
|
|
} else {
|
|
for i := range users {
|
|
// 修正默认用户类型为管理员
|
|
if users[i].Type == "" {
|
|
user := model.User{
|
|
Type: nt.TypeUser,
|
|
ID: users[i].ID,
|
|
}
|
|
if err := repository.UserRepository.Update(context.TODO(), &user); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if users[i].Type == nt.TypeAdmin {
|
|
roles, err := RoleService.GetRolesByUserId(users[i].ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(roles) == 0 {
|
|
users[i].Roles = []string{"system-administrator"}
|
|
if err := service.saveUserRoles(context.Background(), users[i]); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (service userService) IsSuperAdmin(userId string) bool {
|
|
return SuperAdminID == userId
|
|
}
|
|
|
|
func (service userService) FixUserOnlineState() error {
|
|
// 修正用户登录状态
|
|
onlineUsers, err := repository.UserRepository.FindOnlineUsers(context.TODO())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(onlineUsers) > 0 {
|
|
for i := range onlineUsers {
|
|
logs, err := repository.LoginLogRepository.FindAliveLoginLogsByUsername(context.TODO(), onlineUsers[i].Username)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(logs) == 0 {
|
|
if err := repository.UserRepository.UpdateOnlineByUsername(context.TODO(), onlineUsers[i].Username, false); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (service userService) Logout(token string) {
|
|
cache.TokenManager.Delete(token)
|
|
}
|
|
|
|
func (service userService) LogoutByToken(token string) (err error) {
|
|
loginLog, err := repository.LoginLogRepository.FindById(context.TODO(), token)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
loginLogForUpdate := &model.LoginLog{LogoutTime: common.NowJsonTime(), ID: token}
|
|
err = repository.LoginLogRepository.Update(context.TODO(), loginLogForUpdate)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
loginLogs, err := repository.LoginLogRepository.FindAliveLoginLogsByUsername(context.TODO(), loginLog.Username)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(loginLogs) == 0 {
|
|
err = repository.UserRepository.UpdateOnlineByUsername(context.TODO(), loginLog.Username, false)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (service userService) LogoutById(c context.Context, id string) error {
|
|
user, err := repository.UserRepository.FindById(c, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
username := user.Username
|
|
loginLogs, err := repository.LoginLogRepository.FindAliveLoginLogsByUsername(c, username)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for j := range loginLogs {
|
|
token := loginLogs[j].ID
|
|
service.Logout(token)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (service userService) GetUserLoginToken(c context.Context, username string) ([]string, error) {
|
|
|
|
loginLogs, err := repository.LoginLogRepository.FindAliveLoginLogsByUsername(c, username)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var tokens []string
|
|
for j := range loginLogs {
|
|
token := loginLogs[j].ID
|
|
tokens = append(tokens, token)
|
|
}
|
|
return tokens, nil
|
|
}
|
|
|
|
func (service userService) OnEvicted(token string, value interface{}) {
|
|
|
|
if strings.HasPrefix(token, "forever") {
|
|
} else {
|
|
err := service.LogoutByToken(token)
|
|
if err != nil && !errors.Is(gorm.ErrRecordNotFound, err) {
|
|
}
|
|
}
|
|
}
|
|
|
|
func (service userService) UpdateStatusById(id string, status string) error {
|
|
if nt.StatusDisabled == status {
|
|
// 将该用户下线
|
|
if err := service.LogoutById(context.TODO(), id); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
u := model.User{
|
|
ID: id,
|
|
Status: status,
|
|
}
|
|
return repository.UserRepository.Update(context.TODO(), &u)
|
|
|
|
}
|
|
|
|
func (service userService) ReloadToken() error {
|
|
loginLogs, err := repository.LoginLogRepository.FindAliveLoginLogs(context.TODO())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for i := range loginLogs {
|
|
loginLog := loginLogs[i]
|
|
token := loginLog.ID
|
|
user, err := repository.UserRepository.FindByUsername(context.TODO(), loginLog.Username)
|
|
if err != nil {
|
|
if errors.Is(gorm.ErrRecordNotFound, err) {
|
|
_ = repository.LoginLogRepository.DeleteById(context.TODO(), token)
|
|
}
|
|
continue
|
|
}
|
|
|
|
authorization := dto.Authorization{
|
|
Token: token,
|
|
Type: nt.LoginToken,
|
|
Remember: loginLog.Remember,
|
|
User: &user,
|
|
}
|
|
|
|
if authorization.Remember {
|
|
// 记住登录有效期两周
|
|
cache.TokenManager.Set(token, authorization, cache.RememberMeExpiration)
|
|
} else {
|
|
cache.TokenManager.Set(token, authorization, cache.NotRememberExpiration)
|
|
}
|
|
log.Debug("重新加载用户授权Token", log.String("username", user.Nickname), log.String("token", token))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (service userService) CreateUser(user model.User) (err error) {
|
|
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
|
c := service.Context(tx)
|
|
exist, err := repository.UserRepository.ExistByUsername(c, user.Username)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if exist {
|
|
return fmt.Errorf("username %s is already used", user.Username)
|
|
}
|
|
password := user.Password
|
|
|
|
var pass []byte
|
|
if pass, err = utils.Encoder.Encode([]byte(password)); err != nil {
|
|
return err
|
|
}
|
|
user.Password = string(pass)
|
|
|
|
user.ID = utils.UUID()
|
|
user.Created = common.NowJsonTime()
|
|
user.Status = nt.StatusEnabled
|
|
|
|
if err := repository.UserRepository.Create(c, &user); err != nil {
|
|
return err
|
|
}
|
|
if err := service.saveUserRoles(c, user); err != nil {
|
|
return err
|
|
}
|
|
if err := StorageService.CreateStorageByUser(c, &user); err != nil {
|
|
return err
|
|
}
|
|
|
|
if user.Mail != "" {
|
|
subject := fmt.Sprintf("%s 注册通知", branding.Name)
|
|
text := fmt.Sprintf(`您好,%s。
|
|
管理员为你开通了账户。
|
|
账号:%s
|
|
密码:%s
|
|
`, user.Username, user.Username, password)
|
|
go MailService.SendMail(user.Mail, subject, text)
|
|
}
|
|
return nil
|
|
})
|
|
|
|
}
|
|
|
|
func (service userService) saveUserRoles(c context.Context, user model.User) error {
|
|
for _, role := range user.Roles {
|
|
ref := &model.UserRoleRef{
|
|
ID: utils.UUID(),
|
|
UserId: user.ID,
|
|
RoleId: role,
|
|
}
|
|
if err := repository.UserRoleRefRepository.Create(c, ref); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (service userService) DeleteUserById(userId string) error {
|
|
user, err := repository.UserRepository.FindById(context.TODO(), userId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
username := user.Username
|
|
// 下线该用户
|
|
loginTokens, err := service.GetUserLoginToken(context.TODO(), username)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = env.GetDB().Transaction(func(tx *gorm.DB) error {
|
|
c := service.Context(tx)
|
|
// 删除用户与用户组的关系
|
|
if err := repository.UserGroupMemberRepository.DeleteByUserId(c, userId); err != nil {
|
|
return err
|
|
}
|
|
// 删除用户与资产的关系
|
|
if err := repository.AuthorisedRepository.DeleteByUserId(c, userId); err != nil {
|
|
return err
|
|
}
|
|
// 删除用户的默认磁盘空间
|
|
if err := StorageService.DeleteStorageById(c, userId, true); err != nil {
|
|
return err
|
|
}
|
|
// 删除用户与角色的关系
|
|
if err := repository.UserRoleRefRepository.DeleteByUserId(c, user.ID); err != nil {
|
|
return err
|
|
}
|
|
// 删除用户
|
|
if err := repository.UserRepository.DeleteById(c, userId); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, token := range loginTokens {
|
|
service.Logout(token)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (service userService) DeleteLoginLogs(tokens []string) error {
|
|
if len(tokens) > 0 {
|
|
for _, token := range tokens {
|
|
// 手动触发用户退出登录
|
|
if err := service.LogoutByToken(token); err != nil {
|
|
return err
|
|
}
|
|
// 移除缓存中的token
|
|
service.Logout(token)
|
|
// 删除登录日志
|
|
if err := repository.LoginLogRepository.DeleteById(context.TODO(), token); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (service userService) SaveLoginLog(clientIP, clientUserAgent string, username string, success, remember bool, id, reason string) error {
|
|
loginLog := model.LoginLog{
|
|
Username: username,
|
|
ClientIP: clientIP,
|
|
ClientUserAgent: clientUserAgent,
|
|
LoginTime: common.NowJsonTime(),
|
|
Reason: reason,
|
|
Remember: remember,
|
|
}
|
|
if success {
|
|
loginLog.State = "1"
|
|
loginLog.ID = id
|
|
} else {
|
|
loginLog.State = "0"
|
|
loginLog.ID = utils.LongUUID()
|
|
}
|
|
|
|
if err := repository.LoginLogRepository.Create(context.TODO(), &loginLog); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (service userService) DeleteALlLdapUser(ctx context.Context) error {
|
|
return repository.UserRepository.DeleteBySource(ctx, nt.SourceLdap)
|
|
}
|
|
|
|
func (service userService) UpdateUser(id string, user model.User) error {
|
|
|
|
return env.GetDB().Transaction(func(tx *gorm.DB) error {
|
|
ctx := service.Context(tx)
|
|
|
|
dbUser, err := repository.UserRepository.FindById(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if dbUser.Username != user.Username {
|
|
// 修改了登录账号
|
|
exist, err := repository.UserRepository.ExistByUsername(ctx, user.Username)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if exist {
|
|
return fmt.Errorf("username %s is already used", user.Username)
|
|
}
|
|
}
|
|
|
|
if err := repository.UserRoleRefRepository.DeleteByUserId(ctx, user.ID); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := service.saveUserRoles(ctx, user); err != nil {
|
|
return err
|
|
}
|
|
|
|
// 移除用户角色的缓存
|
|
cache.UserRolesManager.Delete(id)
|
|
|
|
return repository.UserRepository.Update(ctx, &user)
|
|
})
|
|
|
|
}
|
|
|
|
func (service userService) FindById(id string) (*model.User, error) {
|
|
item, err := repository.UserRepository.FindById(context.TODO(), id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
roles, err := RoleService.GetRolesByUserId(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
item.Roles = roles
|
|
|
|
return &item, nil
|
|
}
|
|
|
|
func (service userService) ResetTotp(ids []string) error {
|
|
return service.Transaction(context.Background(), func(ctx context.Context) error {
|
|
for _, id := range ids {
|
|
u := &model.User{
|
|
TOTPSecret: "-",
|
|
ID: id,
|
|
}
|
|
if err := repository.UserRepository.Update(ctx, u); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func (service userService) ChangePassword(ids []string, password string) error {
|
|
|
|
passwd, err := utils.Encoder.Encode([]byte(password))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return service.Transaction(context.Background(), func(ctx context.Context) error {
|
|
for _, id := range ids {
|
|
u := &model.User{
|
|
Password: string(passwd),
|
|
ID: id,
|
|
}
|
|
if err := repository.UserRepository.Update(ctx, u); err != nil {
|
|
return err
|
|
}
|
|
|
|
user, err := repository.UserRepository.FindById(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if user.Mail != "" {
|
|
subject := "密码修改通知"
|
|
text := fmt.Sprintf(`您好,%s。
|
|
管理员已将你的密码修改为:%s。
|
|
`, user.Username, password)
|
|
go MailService.SendMail(user.Mail, subject, text)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|