feat(auth): 用户账户 - 完善个人资料与登录日志 (#752)
Co-authored-by: wuligao <1247662379@qq.com>
This commit is contained in:
@@ -29,20 +29,22 @@ var usernamePattern = regexp.MustCompile(`^[a-zA-Z0-9_-]{3,32}$`)
|
||||
type AuthError = kernel.AppError
|
||||
|
||||
type RegisterRequest struct {
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
EmailCode string `json:"emailCode"`
|
||||
Phone string `json:"phone"`
|
||||
SMSCode string `json:"smsCode"`
|
||||
Ticket string `json:"ticket"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Password string `json:"password"`
|
||||
AcceptedTerms bool `json:"acceptedTerms"`
|
||||
LoginContext LoginContext `json:"-"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
EmailCode string `json:"emailCode"`
|
||||
Phone string `json:"phone"`
|
||||
SMSCode string `json:"smsCode"`
|
||||
Ticket string `json:"ticket"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Password string `json:"password"`
|
||||
AcceptedTerms bool `json:"acceptedTerms"`
|
||||
}
|
||||
|
||||
type LoginRequest struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
LoginContext LoginContext `json:"-"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type PublicAuthSettings struct {
|
||||
@@ -247,7 +249,12 @@ func (s *Service) Register(req RegisterRequest) (*AuthSessionResult, error) {
|
||||
if err := s.host.EnsureSignupBonus(user.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.createAuthSession(&user)
|
||||
event := NewLoginLog("password", "registration", req.LoginContext)
|
||||
result, err := s.createAuthSession(&user, event)
|
||||
if err != nil {
|
||||
return nil, s.recordLoginFailure(event, err)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// LoginRateLimitSubject uses the same account lookup as password authentication.
|
||||
@@ -263,7 +270,14 @@ func (s *Service) LoginRateLimitSubject(account string) (string, error) {
|
||||
return "user:" + user.ID, nil
|
||||
}
|
||||
|
||||
func (s *Service) Login(req LoginRequest) (*AuthSessionResult, error) {
|
||||
func (s *Service) Login(req LoginRequest) (result *AuthSessionResult, err error) {
|
||||
event := NewLoginLog("password", "login", req.LoginContext)
|
||||
event.Account = MaskLoginAccount(req.Username)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = s.recordLoginFailure(event, err)
|
||||
}
|
||||
}()
|
||||
account := strings.TrimSpace(req.Username)
|
||||
user, err := s.repo.UserByAccount(account)
|
||||
if err != nil {
|
||||
@@ -272,6 +286,7 @@ func (s *Service) Login(req LoginRequest) (*AuthSessionResult, error) {
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
loginLogUser(event, user)
|
||||
if user.Status != model.UserStatusActive {
|
||||
return nil, kernel.Forbidden("该账号已被禁用")
|
||||
}
|
||||
@@ -287,16 +302,27 @@ func (s *Service) Login(req LoginRequest) (*AuthSessionResult, error) {
|
||||
if err := s.host.EnsureSignupBonus(user.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.host.RecordActivity(user.ID, "login", 1)
|
||||
return s.createAuthSession(user)
|
||||
return s.createAuthSession(user, event)
|
||||
}
|
||||
|
||||
func (s *Service) Logout(cookieValue string) error {
|
||||
sessionID, _ := parseSessionCookie(cookieValue)
|
||||
if sessionID == "" {
|
||||
func (s *Service) Logout(cookieValue string, contexts ...LoginContext) (err error) {
|
||||
sessionID, token := parseSessionCookie(cookieValue)
|
||||
if sessionID == "" || token == "" {
|
||||
return nil
|
||||
}
|
||||
return s.repo.DeleteAuthSession(sessionID)
|
||||
var context LoginContext
|
||||
if len(contexts) > 0 {
|
||||
context = contexts[0]
|
||||
}
|
||||
event := NewLoginLog("unknown", "logout", context)
|
||||
event.Event = "logout"
|
||||
event.SessionRef = HashToken(sessionID)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = s.recordLoginFailure(event, err)
|
||||
}
|
||||
}()
|
||||
return s.repo.RevokeAuthSessionWithLog(sessionID, HashToken(token), event)
|
||||
}
|
||||
|
||||
func (s *Service) CurrentUser(cookieValue string) (*model.User, error) {
|
||||
@@ -330,6 +356,9 @@ func (s *Service) CurrentUser(cookieValue string) (*model.User, error) {
|
||||
// 认证响应只补充当前用户自己的第三方公开身份,不把身份表或密钥字段暴露给其他列表接口。
|
||||
func (s *Service) PublicAuthUser(user *model.User) (AuthUser, error) {
|
||||
result := AuthUser{User: *user}
|
||||
if user.AvatarMode == "custom" {
|
||||
result.AvatarURL = profileAvatarURL(user)
|
||||
}
|
||||
identity, err := s.repo.UserIdentityForUser(user.ID, "linuxdo")
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return result, nil
|
||||
@@ -337,14 +366,17 @@ func (s *Service) PublicAuthUser(user *model.User) (AuthUser, error) {
|
||||
if err != nil {
|
||||
return AuthUser{}, err
|
||||
}
|
||||
result.AvatarURL = identity.AvatarURL
|
||||
if user.AvatarMode == "" {
|
||||
result.AvatarURL = identity.AvatarURL
|
||||
}
|
||||
result.IdentityProvider = identity.Provider
|
||||
result.IdentityID = identity.Subject
|
||||
result.IdentityUsername = identity.ProviderUsername
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *Service) createAuthSession(user *model.User) (*AuthSessionResult, error) {
|
||||
func (s *Service) createAuthSession(user *model.User, event *model.LoginLog) (*AuthSessionResult, error) {
|
||||
loginLogUser(event, user)
|
||||
publicUser, err := s.PublicAuthUser(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -359,9 +391,12 @@ func (s *Service) createAuthSession(user *model.User) (*AuthSessionResult, error
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
if err := s.repo.Create(&session); err != nil {
|
||||
return nil, err
|
||||
event.SessionRef = HashToken(session.ID)
|
||||
event.CreatedAt = now.UTC()
|
||||
if err := s.repo.CreateAuthSessionForUser(user, &session, event); err != nil {
|
||||
return nil, profileWriteError(err)
|
||||
}
|
||||
s.host.RecordActivity(user.ID, "login", 1)
|
||||
return &AuthSessionResult{User: publicUser, Session: session.ID + "." + token, MaxAgeSecs: int(sessionMaxAge.Seconds())}, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user