add models / services / handlers of user / token / 2fa, add web server command

This commit is contained in:
MaysWind
2020-10-17 20:01:24 +08:00
parent c9ae001aef
commit 60c31e8894
18 changed files with 1649 additions and 2 deletions
+182
View File
@@ -0,0 +1,182 @@
package api
import (
"github.com/pquerna/otp/totp"
"github.com/mayswind/lab/pkg/core"
"github.com/mayswind/lab/pkg/errs"
"github.com/mayswind/lab/pkg/log"
"github.com/mayswind/lab/pkg/models"
"github.com/mayswind/lab/pkg/services"
)
type AuthorizationsApi struct {
users *services.UserService
tokens *services.TokenService
twoFactorAuthorizations *services.TwoFactorAuthorizationService
}
var (
Authorizations = &AuthorizationsApi{
users: services.Users,
tokens: services.Tokens,
twoFactorAuthorizations: services.TwoFactorAuthorizations,
}
)
func (a *AuthorizationsApi) AuthorizeHandler(c *core.Context) (interface{}, *errs.Error) {
var credential models.UserLoginRequest
err := c.ShouldBindJSON(&credential)
if err != nil {
log.WarnfWithRequestId(c, "[authorizations.AuthorizeHandler] parse request failed, because %s", err.Error())
return nil, errs.ErrLoginNameOrPasswordInvalid
}
user, err := a.users.GetUserByUsernameOrEmailAndPassword(credential.LoginName, credential.Password)
if err != nil {
log.WarnfWithRequestId(c, "[authorizations.AuthorizeHandler] login failed for user \"%s\", because %s", credential.LoginName, err.Error())
return nil, errs.ErrLoginNameOrPasswordWrong
}
err = a.users.UpdateUserLastLoginTime(user.Uid)
if err != nil {
log.WarnfWithRequestId(c, "[authorizations.AuthorizeHandler] failed to update last login time for user \"uid:%d\", because %s", user.Uid, err.Error())
}
twoFactorEnable := a.tokens.CurrentConfig().EnableTwoFactor
if twoFactorEnable {
twoFactorEnable, err = a.twoFactorAuthorizations.ExistsTwoFactorSetting(user.Uid)
if err != nil {
log.ErrorfWithRequestId(c, "[authorizations.AuthorizeHandler] failed to check two factor setting for user \"uid:%d\", because %s", user.Uid, err.Error())
return nil, errs.Or(err, errs.ErrSystemError)
}
}
var token string
var claims *core.UserTokenClaims
if twoFactorEnable {
token, claims, err = a.tokens.CreateRequire2FAToken(user, c)
} else {
token, claims, err = a.tokens.CreateToken(user, c)
}
if err != nil {
log.ErrorfWithRequestId(c, "[authorizations.AuthorizeHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
return nil, errs.ErrTokenGenerating
}
c.SetTokenClaims(claims)
log.InfofWithRequestId(c, "[authorizations.AuthorizeHandler] user \"uid:%d\" has logined, token type is %d, token will be expired at %d", user.Uid, claims.Type, claims.ExpiresAt)
return token, nil
}
func (a *AuthorizationsApi) TwoFactorAuthorizeHandler(c *core.Context) (interface{}, *errs.Error) {
var credential models.TwoFactorLoginRequest
err := c.ShouldBindJSON(&credential)
if err != nil {
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] parse request failed, because %s", err.Error())
return nil, errs.ErrPasscodeInvalid
}
uid := c.GetCurrentUid()
twoFactorSetting, err := a.twoFactorAuthorizations.GetUserTwoFactorSettingByUid(uid)
if err != nil {
log.ErrorfWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] failed to get two factor setting for user \"uid:%d\", because %s", uid, err.Error())
return nil, errs.Or(err, errs.ErrSystemError)
}
if !totp.Validate(credential.Passcode, twoFactorSetting.Secret) {
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] passcode is invalid for user \"uid:%d\"", uid)
return nil, errs.ErrPasscodeInvalid
}
user, err := a.users.GetUserById(uid)
if err != nil {
log.ErrorfWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] failed to get user \"uid:%d\" info, because %s", user.Uid, err.Error())
return nil, errs.ErrUserNotFound
}
oldTokenClaims := c.GetTokenClaims()
err = a.tokens.DeleteTokenByClaims(oldTokenClaims)
if err != nil {
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] failed to revoke temporary token \"utid:%s\" for user \"uid:%d\", because %s", oldTokenClaims.UserTokenId, user.Uid, err.Error())
}
token, claims, err := a.tokens.CreateToken(user, c)
if err != nil {
log.ErrorfWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
return nil, errs.ErrTokenGenerating
}
c.SetTokenClaims(claims)
log.InfofWithRequestId(c, "[authorizations.TwoFactorAuthorizeHandler] user \"uid:%d\" has authorized two factor via passcode, token will be expired at %d", user.Uid, claims.ExpiresAt)
return token, nil
}
func (a *AuthorizationsApi) TwoFactorAuthorizeByRecoveryCodeHandler(c *core.Context) (interface{}, *errs.Error) {
var credential models.TwoFactorRecoveryCodeLoginRequest
err := c.ShouldBindJSON(&credential)
if err != nil {
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] parse request failed, because %s", err.Error())
return nil, errs.ErrTwoFactorRecoveryCodeInvalid
}
uid := c.GetCurrentUid()
enableTwoFactor, err := a.twoFactorAuthorizations.ExistsTwoFactorSetting(uid)
if err != nil {
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to get two factor setting for user \"uid:%d\", because %s", uid, err.Error())
return nil, errs.Or(err, errs.ErrSystemError)
}
if !enableTwoFactor {
return nil, errs.ErrTwoFactorKeyIsNotEnabled
}
user, err := a.users.GetUserById(uid)
if err != nil {
log.ErrorfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to get user \"uid:%d\" info, because %s", user.Uid, err.Error())
return nil, errs.ErrUserNotFound
}
err = a.twoFactorAuthorizations.GetAndUseUserTwoFactorRecoveryCode(uid, credential.RecoveryCode, user.Salt)
if err != nil {
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to get two factor recovery code for user \"uid:%d\", because %s", uid, err.Error())
return nil, errs.Or(err, errs.ErrTwoFactorRecoveryCodeNotExist)
}
oldTokenClaims := c.GetTokenClaims()
err = a.tokens.DeleteTokenByClaims(oldTokenClaims)
if err != nil {
log.WarnfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to revoke temporary token \"utid:%s\" for user \"uid:%d\", because %s", oldTokenClaims.UserTokenId, user.Uid, err.Error())
}
token, claims, err := a.tokens.CreateToken(user, c)
if err != nil {
log.ErrorfWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
return nil, errs.ErrTokenGenerating
}
c.SetTokenClaims(claims)
log.InfofWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] user \"uid:%d\" has authorized two factor via recovery code \"%s\", token will be expired at %d", user.Uid, credential.RecoveryCode, claims.ExpiresAt)
return token, nil
}
+20
View File
@@ -0,0 +1,20 @@
package api
import (
"github.com/mayswind/lab/pkg/core"
"github.com/mayswind/lab/pkg/errs"
)
type DefaultApi struct {}
var (
Default = &DefaultApi{}
)
func (a *DefaultApi) ApiNotFound(c *core.Context) (interface{}, *errs.Error) {
return nil, errs.ErrApiNotFound
}
func (a *DefaultApi) MethodNotAllowed(c *core.Context) (interface{}, *errs.Error) {
return nil, errs.ErrMethodNotAllowed
}
+120
View File
@@ -0,0 +1,120 @@
package api
import (
"github.com/mayswind/lab/pkg/core"
"github.com/mayswind/lab/pkg/errs"
"github.com/mayswind/lab/pkg/log"
"github.com/mayswind/lab/pkg/models"
"github.com/mayswind/lab/pkg/services"
"github.com/mayswind/lab/pkg/utils"
)
type TokensApi struct {
tokens *services.TokenService
users *services.UserService
}
var (
Tokens = &TokensApi{
tokens: services.Tokens,
users: services.Users,
}
)
func (a *TokensApi) TokenListHandler(c *core.Context) (interface{}, *errs.Error) {
uid := c.GetCurrentUid()
tokens, err := a.tokens.GetAllTokensByUid(uid)
if err != nil {
log.ErrorfWithRequestId(c, "[tokens.TokenListHandler] failed to get all tokens for user \"uid:%d\", because %s", uid, err.Error())
return nil, errs.ErrOperationFailed
}
tokenResps := make([]*models.TokenInfoResponse, len(tokens))
claims := c.GetTokenClaims()
for i := 0; i < len(tokens); i++ {
token := tokens[i]
tokenResp := &models.TokenInfoResponse{
TokenId: a.tokens.GenerateTokenId(token),
TokenType: token.TokenType,
UserAgent: token.UserAgent,
CreatedAt: token.CreatedUnixTime,
ExpiredAt: token.ExpiredUnixTime,
}
if utils.Int64ToString(token.Uid) == claims.Id && utils.Int64ToString(token.UserTokenId) == claims.UserTokenId && token.CreatedUnixTime == claims.IssuedAt {
tokenResp.IsCurrent = true
}
tokenResps[i] = tokenResp
}
return tokenResps, nil
}
func (a *TokensApi) TokenRevokeHandler(c *core.Context) (interface{}, *errs.Error) {
var tokenRevokeReq models.TokenRevokeRequest
err := c.ShouldBindJSON(&tokenRevokeReq)
if err != nil {
log.WarnfWithRequestId(c, "[tokens.TokenRevokeHandler] parse request failed, because %s", err.Error())
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
}
tokenRecord, err := a.tokens.ParseFromTokenId(tokenRevokeReq.TokenId)
if err != nil {
if !errs.IsCustomError(err) {
log.ErrorfWithRequestId(c, "[token.TokenRevokeHandler] failed to parse token \"id:%s\", because %s", tokenRevokeReq.TokenId, err.Error())
}
return nil, errs.Or(err, errs.ErrInvalidTokenId)
}
uid := c.GetCurrentUid()
if tokenRecord.Uid != uid {
log.WarnfWithRequestId(c, "[token.TokenRevokeHandler] token \"id:%s\" is not owned by user \"uid:%d\"", tokenRevokeReq.TokenId, uid)
return nil, errs.ErrInvalidTokenId
}
err = a.tokens.DeleteToken(tokenRecord)
if err != nil {
log.ErrorfWithRequestId(c, "[token.TokenRevokeHandler] failed to revoke token \"id:%s\" for user \"uid:%d\", because %s", tokenRevokeReq.TokenId, uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
log.InfofWithRequestId(c, "[token.TokenRevokeHandler] user \"uid:%d\" has revoked token \"id:%s\"", uid, tokenRevokeReq.TokenId)
return true, nil
}
func (a *TokensApi) TokenRefreshHandler(c *core.Context) (interface{}, *errs.Error) {
uid := c.GetCurrentUid()
user, err := a.users.GetUserById(uid)
if err != nil {
log.WarnfWithRequestId(c, "[token.TokenRefreshHandler] failed to get user \"uid:%d\" info, because %s", uid, err.Error())
return nil, errs.ErrUserNotFound
}
token, claims, err := a.tokens.CreateToken(user, c)
if err != nil {
log.ErrorfWithRequestId(c, "[token.TokenRefreshHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
return nil, errs.Or(err, errs.ErrTokenGenerating)
}
oldTokenClaims := c.GetTokenClaims()
err = a.tokens.DeleteTokenByClaims(oldTokenClaims)
if err != nil {
log.WarnfWithRequestId(c, "[token.TokenRefreshHandler] failed to revoke token \"id:%s\" for user \"uid:%d\", because %s", oldTokenClaims.UserTokenId, user.Uid, err.Error())
}
c.SetTokenClaims(claims)
log.InfofWithRequestId(c, "[token.TokenRefreshHandler] user \"uid:%d\" token refreshed, new token will be expired at %d", user.Uid, claims.ExpiresAt)
return token, nil
}
+278
View File
@@ -0,0 +1,278 @@
package api
import (
"bytes"
"encoding/base64"
"image/png"
"time"
"github.com/pquerna/otp/totp"
"github.com/mayswind/lab/pkg/core"
"github.com/mayswind/lab/pkg/errs"
"github.com/mayswind/lab/pkg/log"
"github.com/mayswind/lab/pkg/models"
"github.com/mayswind/lab/pkg/services"
)
type TwoFactorAuthorizationsApi struct {
twoFactorAuthorizations *services.TwoFactorAuthorizationService
users *services.UserService
tokens *services.TokenService
}
var (
TwoFactorAuthorizations = &TwoFactorAuthorizationsApi{
twoFactorAuthorizations: services.TwoFactorAuthorizations,
users: services.Users,
tokens: services.Tokens,
}
)
func (a *TwoFactorAuthorizationsApi) TwoFactorStatusHandler(c *core.Context) (interface{}, *errs.Error) {
uid := c.GetCurrentUid()
twoFactorSetting, err := a.twoFactorAuthorizations.GetUserTwoFactorSettingByUid(uid)
if err == errs.ErrTwoFactorKeyIsNotEnabled {
statusResp := &models.TwoFactorStatusResponse{
Enable: false,
}
return statusResp, nil
}
if err != nil {
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorStatusHandler] failed to get two factor setting, because %s", err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
statusResp := &models.TwoFactorStatusResponse{
Enable: true,
CreatedAt: twoFactorSetting.CreatedUnixTime,
}
return statusResp, nil
}
func (a *TwoFactorAuthorizationsApi) TwoFactorEnableRequestHandler(c *core.Context) (interface{}, *errs.Error) {
uid := c.GetCurrentUid()
enabled, err := a.twoFactorAuthorizations.ExistsTwoFactorSetting(uid)
if err != nil {
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableRequestHandler] failed to check two factor setting, because %s", err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
if enabled {
return nil, errs.ErrTwoFactorKeyAlreadyEnabled
}
user, err := a.users.GetUserById(uid)
if err != nil {
if !errs.IsCustomError(err) {
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableRequestHandler] failed to get user, because %s", err.Error())
}
return nil, errs.ErrUserNotFound
}
key, err := a.twoFactorAuthorizations.GenerateTwoFactorSecret(user)
if err != nil {
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableRequestHandler] failed to generate two factor secret, because %s", err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
img, err := key.Image(240, 240)
if err != nil {
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableRequestHandler] failed to generate two factor qrcode, because %s", err.Error())
return nil, errs.ErrOperationFailed
}
imgData := &bytes.Buffer{}
if err = png.Encode(imgData, img); err != nil {
return nil, errs.ErrOperationFailed
}
enableResp := &models.TwoFactorEnableResponse{
Secret: key.Secret(),
QRCode: "data:image/png;base64," + base64.StdEncoding.EncodeToString(imgData.Bytes()),
}
return enableResp, nil
}
func (a *TwoFactorAuthorizationsApi) TwoFactorEnableConfirmHandler(c *core.Context) (interface{}, *errs.Error) {
var confirmReq models.TwoFactorEnableConfirmRequest
err := c.ShouldBindJSON(&confirmReq)
if err != nil {
log.WarnfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] parse request failed, because %s", err.Error())
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
}
uid := c.GetCurrentUid()
exists, err := a.twoFactorAuthorizations.ExistsTwoFactorSetting(uid)
if err != nil {
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] failed to check two factor setting, because %s", err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
if exists {
return nil, errs.ErrTwoFactorKeyAlreadyEnabled
}
user, err := a.users.GetUserById(uid)
if err != nil {
if !errs.IsCustomError(err) {
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] failed to get user, because %s", err.Error())
}
return nil, errs.ErrUserNotFound
}
twoFactorSetting := &models.TwoFactor{
Uid: uid,
Secret: confirmReq.Secret,
}
if !totp.Validate(confirmReq.Passcode, confirmReq.Secret) {
log.WarnfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] passcode is invalid")
return nil, errs.ErrPasscodeInvalid
}
recoveryCodes, err := a.twoFactorAuthorizations.GenerateTwoFactorRecoveryCodes()
if err != nil {
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] failed to generate two factor recovery codes for user \"uid:%d\", because %s", uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
err = a.twoFactorAuthorizations.CreateTwoFactorRecoveryCodes(uid, recoveryCodes, user.Salt)
if err != nil {
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] failed to create two factor recovery codes for user \"uid:%d\", because %s", uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
err = a.twoFactorAuthorizations.CreateTwoFactorSetting(twoFactorSetting)
if err != nil {
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] failed to create two factor setting for user \"uid:%d\", because %s", uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
log.InfofWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] user \"uid:%d\" has enabled two factor authorization", uid)
now := time.Now().Unix()
err = a.tokens.DeleteTokensBeforeTime(uid, now)
if err == nil {
log.InfofWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] revoke old tokens before unix time \"%d\" for user \"uid:%d\"", now, user.Uid)
} else {
log.WarnfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] failed to revoke old tokens for user \"uid:%d\", because %s", user.Uid, err.Error())
}
token, claims, err := a.tokens.CreateToken(user, c)
if err != nil {
log.WarnfWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
confirmResp := &models.TwoFactorEnableConfirmResponse{
RecoveryCodes: recoveryCodes,
}
return confirmResp, nil
}
c.SetTokenClaims(claims)
log.InfofWithRequestId(c, "[twofactor_authorizations.TwoFactorEnableConfirmHandler] user \"uid:%d\" token refreshed, new token will be expired at %d", user.Uid, claims.ExpiresAt)
confirmResp := &models.TwoFactorEnableConfirmResponse{
Token: token,
RecoveryCodes: recoveryCodes,
}
return confirmResp, nil
}
func (a *TwoFactorAuthorizationsApi) TwoFactorDisableHandler(c *core.Context) (interface{}, *errs.Error) {
uid := c.GetCurrentUid()
enableTwoFactor, err := a.twoFactorAuthorizations.ExistsTwoFactorSetting(uid)
if err != nil {
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorDisableHandler] failed to check two factor setting, because %s", err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
if !enableTwoFactor {
return nil, errs.ErrTwoFactorKeyIsNotEnabled
}
err = a.twoFactorAuthorizations.DeleteTwoFactorRecoveryCodes(uid)
if err != nil {
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorDisableHandler] failed to delete two factor recovery codes for user \"uid:%d\"", uid)
return nil, errs.Or(err, errs.ErrOperationFailed)
}
err = a.twoFactorAuthorizations.DeleteTwoFactorSetting(uid)
if err != nil {
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorDisableHandler] failed to delete two factor setting for user \"uid:%d\"", uid)
return nil, errs.Or(err, errs.ErrOperationFailed)
}
log.InfofWithRequestId(c, "[twofactor_authorizations.TwoFactorDisableHandler] user \"uid:%d\" has disabled two factor authorization", uid)
return true, nil
}
func (a *TwoFactorAuthorizationsApi) TwoFactorRecoveryCodeRegenerateHandler(c *core.Context) (interface{}, *errs.Error) {
uid := c.GetCurrentUid()
enableTwoFactor, err := a.twoFactorAuthorizations.ExistsTwoFactorSetting(uid)
if err != nil {
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorRecoveryCodeRegenerateHandler] failed to check two factor setting, because %s", err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
if !enableTwoFactor {
return nil, errs.ErrTwoFactorKeyIsNotEnabled
}
recoveryCodes, err := a.twoFactorAuthorizations.GenerateTwoFactorRecoveryCodes()
if err != nil {
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorRecoveryCodeRegenerateHandler] failed to generate two factor recovery codes for user \"uid:%d\", because %s", uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
user, err := a.users.GetUserById(uid)
if err != nil {
log.WarnfWithRequestId(c, "[twofactor_authorizations.TwoFactorRecoveryCodeRegenerateHandler] failed to get user for user \"uid:%d\", because %s", uid, err.Error())
return nil, errs.ErrUserNotFound
}
err = a.twoFactorAuthorizations.CreateTwoFactorRecoveryCodes(uid, recoveryCodes, user.Salt)
if err != nil {
log.ErrorfWithRequestId(c, "[twofactor_authorizations.TwoFactorRecoveryCodeRegenerateHandler] failed to create two factor recovery codes for user \"uid:%d\", because %s", uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
recoveryCodesResp := &models.TwoFactorEnableConfirmResponse{
RecoveryCodes: recoveryCodes,
}
log.InfofWithRequestId(c, "[twofactor_authorizations.TwoFactorRecoveryCodeRegenerateHandler] user \"uid:%d\" has regenerated two factor recovery codes", uid)
return recoveryCodesResp, nil
}
+179
View File
@@ -0,0 +1,179 @@
package api
import (
"strings"
"time"
"github.com/mayswind/lab/pkg/core"
"github.com/mayswind/lab/pkg/errs"
"github.com/mayswind/lab/pkg/log"
"github.com/mayswind/lab/pkg/models"
"github.com/mayswind/lab/pkg/services"
"github.com/mayswind/lab/pkg/utils"
)
type UsersApi struct {
users *services.UserService
tokens *services.TokenService
}
var (
Users = &UsersApi{
users: services.Users,
tokens: services.Tokens,
}
)
func (a *UsersApi) UserRegisterHandler(c *core.Context) (interface{}, *errs.Error) {
var userRegisterReq models.UserRegisterRequest
err := c.ShouldBindJSON(&userRegisterReq)
if err != nil {
log.WarnfWithRequestId(c, "[users.UserRegisterHandler] parse request failed, because %s", err.Error())
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
}
userRegisterReq.Username = strings.TrimSpace(userRegisterReq.Username)
userRegisterReq.Email = strings.TrimSpace(userRegisterReq.Email)
userRegisterReq.Nickname = strings.TrimSpace(userRegisterReq.Nickname)
user := &models.User{
Username: userRegisterReq.Username,
Email: userRegisterReq.Email,
Nickname: userRegisterReq.Nickname,
Password: userRegisterReq.Password,
}
err = a.users.CreateUser(user)
if err != nil {
log.ErrorfWithRequestId(c, "[users.UserRegisterHandler] failed to create user \"%s\", because %s", user.Username, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
log.InfofWithRequestId(c, "[users.UserRegisterHandler] user \"%s\" has registered successfully, uid is %d", user.Username, user.Uid)
token, claims, err := a.tokens.CreateToken(user, c)
if err != nil {
log.WarnfWithRequestId(c, "[users.UserRegisterHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
return true, nil
}
c.SetTokenClaims(claims)
log.InfofWithRequestId(c, "[users.UserRegisterHandler] user \"uid:%d\" has logined, token will be expired at %d", user.Uid, claims.ExpiresAt)
return token, nil
}
func (a *UsersApi) UserProfileHandler(c *core.Context) (interface{}, *errs.Error) {
uid := c.GetCurrentUid()
user, err := a.users.GetUserById(uid)
if err != nil {
if !errs.IsCustomError(err) {
log.ErrorfWithRequestId(c, "[users.UserRegisterHandler] failed to get user, because %s", err.Error())
}
return nil, errs.ErrUserNotFound
}
userResp := &models.UserProfileResponse{
Uid : utils.Int64ToString(user.Uid),
Username: user.Username,
Email: user.Email,
Nickname: user.Nickname,
Type: user.Type,
CreatedAt: user.CreatedUnixTime,
UpdatedAt: user.UpdatedUnixTime,
LastLoginAt: user.LastLoginUnixTime,
}
return userResp, nil
}
func (a *UsersApi) UserUpdateProfileHandler(c *core.Context) (interface{}, *errs.Error) {
var userUpdateReq models.UserProfileUpdateRequest
err := c.ShouldBindJSON(&userUpdateReq)
if err != nil {
log.WarnfWithRequestId(c, "[users.UserUpdateProfileHandler] parse request failed, because %s", err.Error())
return nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
}
uid := c.GetCurrentUid()
user, err := a.users.GetUserById(uid)
if err != nil {
if !errs.IsCustomError(err) {
log.ErrorfWithRequestId(c, "[users.UserUpdateProfileHandler] failed to get user, because %s", err.Error())
}
return nil, errs.ErrUserNotFound
}
userUpdateReq.Email = strings.TrimSpace(userUpdateReq.Email)
userUpdateReq.Nickname = strings.TrimSpace(userUpdateReq.Nickname)
anythingUpdate := false
if userUpdateReq.Email != "" && userUpdateReq.Email != user.Email {
anythingUpdate = true
} else {
userUpdateReq.Email = ""
}
if userUpdateReq.Password != "" && !a.users.IsPasswordEqualsUserPassword(userUpdateReq.Password, user) {
anythingUpdate = true
} else {
userUpdateReq.Password = ""
}
if userUpdateReq.Nickname != "" && userUpdateReq.Nickname != user.Nickname {
anythingUpdate = true
} else {
userUpdateReq.Nickname = ""
}
if !anythingUpdate {
return nil, errs.ErrNothingWillBeUpdated
}
user.Email = userUpdateReq.Email
user.Password = userUpdateReq.Password
user.Nickname = userUpdateReq.Nickname
keyProfileUpdated, err := a.users.UpdateUser(user)
if err != nil {
log.ErrorfWithRequestId(c, "[users.UserUpdateProfileHandler] failed to update user \"uid:%d\", because %s", user.Uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
log.InfofWithRequestId(c, "[users.UserUpdateProfileHandler] user \"uid:%d\" has updated successfully", user.Uid)
if keyProfileUpdated {
now := time.Now().Unix()
err = a.tokens.DeleteTokensBeforeTime(uid, now)
if err == nil {
log.InfofWithRequestId(c, "[users.UserUpdateProfileHandler] revoke old tokens before unix time \"%d\" for user \"uid:%d\"", now, user.Uid)
} else {
log.WarnfWithRequestId(c, "[users.UserUpdateProfileHandler] failed to revoke old tokens for user \"uid:%d\", because %s", user.Uid, err.Error())
}
token, claims, err := a.tokens.CreateToken(user, c)
if err != nil {
log.WarnfWithRequestId(c, "[users.UserUpdateProfileHandler] failed to create token for user \"uid:%d\", because %s", user.Uid, err.Error())
return true, nil
}
c.SetTokenClaims(claims)
log.InfofWithRequestId(c, "[users.UserUpdateProfileHandler] user \"uid:%d\" token refreshed, new token will be expired at %d", user.Uid, claims.ExpiresAt)
return token, nil
} else {
return true, nil
}
}