package service import ( "context" "time" "kra/internal/biz" "kra/internal/conf" "kra/pkg/adminauth" ) type UserDisabledError struct{ UserID uint } func (e *UserDisabledError) Error() string { return biz.ErrUserDisabled.Error() } func (e *UserDisabledError) Unwrap() error { return biz.ErrUserDisabled } type LoginResult struct { User map[string]any `json:"user"` Token string `json:"token"` ExpiresAt int64 `json:"expiresAt"` NeedChangePassword bool `json:"needChangePassword"` } type AuthService struct { uc *biz.UserUsecase runtime *conf.Runtime settings *SecurityService } func NewAuthService(uc *biz.UserUsecase, runtime *conf.Runtime, settings *SecurityService) *AuthService { return &AuthService{uc: uc, runtime: runtime, settings: settings} } func (s *AuthService) Login(ctx context.Context, username, password string) (*LoginResult, error) { u, err := s.uc.Login(ctx, username, password) if err != nil { return nil, err } if u.Enable != 1 { return nil, &UserDisabledError{UserID: u.ID} } if security, securityErr := s.settings.CurrentSecurity(ctx); securityErr == nil && security.PwdExpireEnable && u.PasswordUpdatedAt != nil && time.Since(*u.PasswordUpdatedAt) > time.Duration(security.PwdExpireDays)*24*time.Hour { u.MustChangePassword = true } return s.issueLogin(ctx, u, u.AuthorityID) } func (s *AuthService) issueLogin(ctx context.Context, user *biz.User, authorityID uint) (*LoginResult, error) { expires, buffer := 7*24*time.Hour, 24*time.Hour secret, issuer := "", "kra" config := s.runtime.Admin() if config != nil && config.Jwt != nil { secret, issuer = config.Jwt.SigningKey, config.Jwt.Issuer if config.Jwt.ExpiresTime != nil { expires = config.Jwt.ExpiresTime.AsDuration() } if config.Jwt.BufferTime != nil { buffer = config.Jwt.BufferTime.AsDuration() } } token, claims, err := adminauth.Generate(secret, issuer, expires, buffer, user.ID, authorityID, user.UUID, user.Username, user.NickName, user.MustChangePassword) if err != nil { return nil, err } if s.settings.UseMultipoint() { oldToken, _, cacheErr := s.settings.CacheGet(ctx, activeTokenKey(user.Username)) if cacheErr != nil { return nil, cacheErr } if cacheErr = s.settings.RotateActiveToken(ctx, user.Username, oldToken, token, expires); cacheErr != nil { return nil, cacheErr } } return &LoginResult{User: convertUser(user), Token: token, ExpiresAt: claims.ExpiresAt.UnixMilli(), NeedChangePassword: user.MustChangePassword}, nil } func (s *AuthService) SwitchAuthority(ctx context.Context, id, authorityID uint) (*LoginResult, error) { if err := s.uc.SetUserAuthority(ctx, id, authorityID); err != nil { return nil, err } user, err := s.uc.User(ctx, id) if err != nil { return nil, err } return s.issueLogin(ctx, user, authorityID) }