github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/service/user/login.go (about) 1 package user 2 3 import ( 4 "fmt" 5 model "github.com/cloudreve/Cloudreve/v3/models" 6 "github.com/cloudreve/Cloudreve/v3/pkg/auth" 7 "github.com/cloudreve/Cloudreve/v3/pkg/cache" 8 "github.com/cloudreve/Cloudreve/v3/pkg/email" 9 "github.com/cloudreve/Cloudreve/v3/pkg/hashid" 10 "github.com/cloudreve/Cloudreve/v3/pkg/serializer" 11 "github.com/cloudreve/Cloudreve/v3/pkg/util" 12 "github.com/gin-gonic/gin" 13 "github.com/gofrs/uuid" 14 "github.com/pquerna/otp/totp" 15 "net/url" 16 ) 17 18 // UserLoginService 管理用户登录的服务 19 type UserLoginService struct { 20 //TODO 细致调整验证规则 21 UserName string `form:"userName" json:"userName" binding:"required,email"` 22 Password string `form:"Password" json:"Password" binding:"required,min=4,max=64"` 23 } 24 25 // UserResetEmailService 发送密码重设邮件服务 26 type UserResetEmailService struct { 27 UserName string `form:"userName" json:"userName" binding:"required,email"` 28 } 29 30 // UserResetService 密码重设服务 31 type UserResetService struct { 32 Password string `form:"Password" json:"Password" binding:"required,min=4,max=64"` 33 ID string `json:"id" binding:"required"` 34 Secret string `json:"secret" binding:"required"` 35 } 36 37 // Reset 重设密码 38 func (service *UserResetService) Reset(c *gin.Context) serializer.Response { 39 // 取得原始用户ID 40 uid, err := hashid.DecodeHashID(service.ID, hashid.UserID) 41 if err != nil { 42 return serializer.Err(serializer.CodeInvalidTempLink, "Invalid link", err) 43 } 44 45 // 检查重设会话 46 resetSession, exist := cache.Get(fmt.Sprintf("user_reset_%d", uid)) 47 if !exist || resetSession.(string) != service.Secret { 48 return serializer.Err(serializer.CodeTempLinkExpired, "Link is expired", err) 49 } 50 51 // 重设用户密码 52 user, err := model.GetActiveUserByID(uid) 53 if err != nil { 54 return serializer.Err(serializer.CodeUserNotFound, "User not found", nil) 55 } 56 57 user.SetPassword(service.Password) 58 if err := user.Update(map[string]interface{}{"password": user.Password}); err != nil { 59 return serializer.DBErr("Failed to reset password", err) 60 } 61 62 cache.Deletes([]string{fmt.Sprintf("%d", uid)}, "user_reset_") 63 return serializer.Response{} 64 } 65 66 // Reset 发送密码重设邮件 67 func (service *UserResetEmailService) Reset(c *gin.Context) serializer.Response { 68 // 查找用户 69 if user, err := model.GetUserByEmail(service.UserName); err == nil { 70 71 if user.Status == model.Baned || user.Status == model.OveruseBaned { 72 return serializer.Err(serializer.CodeUserBaned, "This user is banned", nil) 73 } 74 if user.Status == model.NotActivicated { 75 return serializer.Err(serializer.CodeUserNotActivated, "This user is not activated", nil) 76 } 77 // 创建密码重设会话 78 secret := util.RandStringRunes(32) 79 cache.Set(fmt.Sprintf("user_reset_%d", user.ID), secret, 3600) 80 81 // 生成用户访问的重设链接 82 controller, _ := url.Parse("/reset") 83 finalURL := model.GetSiteURL().ResolveReference(controller) 84 queries := finalURL.Query() 85 queries.Add("id", hashid.HashID(user.ID, hashid.UserID)) 86 queries.Add("sign", secret) 87 finalURL.RawQuery = queries.Encode() 88 89 // 发送密码重设邮件 90 title, body := email.NewResetEmail(user.Nick, finalURL.String()) 91 if err := email.Send(user.Email, title, body); err != nil { 92 return serializer.Err(serializer.CodeFailedSendEmail, "Failed to send email", err) 93 } 94 95 } 96 97 return serializer.Response{} 98 } 99 100 // Login 二步验证继续登录 101 func (service *Enable2FA) Login(c *gin.Context) serializer.Response { 102 if uid, ok := util.GetSession(c, "2fa_user_id").(uint); ok { 103 // 查找用户 104 expectedUser, err := model.GetActiveUserByID(uid) 105 if err != nil { 106 return serializer.Err(serializer.CodeUserNotFound, "User not found", nil) 107 } 108 109 // 验证二步验证代码 110 if !totp.Validate(service.Code, expectedUser.TwoFactor) { 111 return serializer.Err(serializer.Code2FACodeErr, "2FA code not correct", nil) 112 } 113 114 //登陆成功,清空并设置session 115 util.DeleteSession(c, "2fa_user_id") 116 util.SetSession(c, map[string]interface{}{ 117 "user_id": expectedUser.ID, 118 }) 119 120 return serializer.BuildUserResponse(expectedUser) 121 } 122 123 return serializer.Err(serializer.CodeLoginSessionNotExist, "Login session not exist", nil) 124 } 125 126 // Login 用户登录函数 127 func (service *UserLoginService) Login(c *gin.Context) serializer.Response { 128 expectedUser, err := model.GetUserByEmail(service.UserName) 129 // 一系列校验 130 if err != nil { 131 return serializer.Err(serializer.CodeCredentialInvalid, "Wrong password or email address", err) 132 } 133 if authOK, _ := expectedUser.CheckPassword(service.Password); !authOK { 134 return serializer.Err(serializer.CodeCredentialInvalid, "Wrong password or email address", nil) 135 } 136 if expectedUser.Status == model.Baned || expectedUser.Status == model.OveruseBaned { 137 return serializer.Err(serializer.CodeUserBaned, "This account has been blocked", nil) 138 } 139 if expectedUser.Status == model.NotActivicated { 140 return serializer.Err(serializer.CodeUserNotActivated, "This account is not activated", nil) 141 } 142 143 if expectedUser.TwoFactor != "" { 144 // 需要二步验证 145 util.SetSession(c, map[string]interface{}{ 146 "2fa_user_id": expectedUser.ID, 147 }) 148 return serializer.Response{Code: 203} 149 } 150 151 //登陆成功,清空并设置session 152 util.SetSession(c, map[string]interface{}{ 153 "user_id": expectedUser.ID, 154 }) 155 156 return serializer.BuildUserResponse(expectedUser) 157 158 } 159 160 // CopySessionService service for copy user session 161 type CopySessionService struct { 162 ID string `uri:"id" binding:"required,uuid4"` 163 } 164 165 const CopySessionTTL = 60 166 167 // Prepare generates the URL with short expiration duration 168 func (s *CopySessionService) Prepare(c *gin.Context, user *model.User) serializer.Response { 169 // 用户组有效期 170 urlID := uuid.Must(uuid.NewV4()) 171 if err := cache.Set(fmt.Sprintf("copy_session_%s", urlID.String()), user.ID, CopySessionTTL); err != nil { 172 return serializer.Err(serializer.CodeInternalSetting, "Failed to create copy session", err) 173 } 174 175 base := model.GetSiteURL() 176 apiBaseURI, _ := url.Parse("/api/v3/user/session/copy/" + urlID.String()) 177 apiURL := base.ResolveReference(apiBaseURI) 178 res, err := auth.SignURI(auth.General, apiURL.String(), CopySessionTTL) 179 if err != nil { 180 return serializer.Err(serializer.CodeInternalSetting, "Failed to sign temp URL", err) 181 } 182 183 return serializer.Response{ 184 Data: res.String(), 185 } 186 } 187 188 // Copy a new session from active session, refresh max-age 189 func (s *CopySessionService) Copy(c *gin.Context) serializer.Response { 190 // 用户组有效期 191 cacheKey := fmt.Sprintf("copy_session_%s", s.ID) 192 uid, ok := cache.Get(cacheKey) 193 if !ok { 194 return serializer.Err(serializer.CodeNotFound, "", nil) 195 } 196 197 cache.Deletes([]string{cacheKey}, "") 198 util.SetSession(c, map[string]interface{}{ 199 "user_id": uid.(uint), 200 }) 201 202 return serializer.Response{} 203 }