code refactor
This commit is contained in:
@@ -31,7 +31,6 @@ func (p *MapImageProxy) OpenStreetMapTileImageProxyHandler(c *core.Context) (*ht
|
|||||||
imageRawUrl := fmt.Sprintf(openStreetMapTileImageUrlFormat, zoomLevel, coordinateX, fileName)
|
imageRawUrl := fmt.Sprintf(openStreetMapTileImageUrlFormat, zoomLevel, coordinateX, fileName)
|
||||||
imageUrl, _ := url.Parse(imageRawUrl)
|
imageUrl, _ := url.Parse(imageRawUrl)
|
||||||
|
|
||||||
req.Header.Del("Authorization")
|
|
||||||
req.URL = imageUrl
|
req.URL = imageUrl
|
||||||
req.RequestURI = req.URL.RequestURI()
|
req.RequestURI = req.URL.RequestURI()
|
||||||
req.Host = imageUrl.Host
|
req.Host = imageUrl.Host
|
||||||
|
|||||||
+1
-1
@@ -62,7 +62,7 @@ func (a *TokensApi) TokenListHandler(c *core.Context) (interface{}, *errs.Error)
|
|||||||
|
|
||||||
// TokenRevokeCurrentHandler revokes current token of current user
|
// TokenRevokeCurrentHandler revokes current token of current user
|
||||||
func (a *TokensApi) TokenRevokeCurrentHandler(c *core.Context) (interface{}, *errs.Error) {
|
func (a *TokensApi) TokenRevokeCurrentHandler(c *core.Context) (interface{}, *errs.Error) {
|
||||||
_, claims, err := a.tokens.ParseToken(c)
|
_, claims, err := a.tokens.ParseTokenByHeader(c)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errs.Or(err, errs.NewIncompleteOrIncorrectSubmissionError(err))
|
return nil, errs.Or(err, errs.NewIncompleteOrIncorrectSubmissionError(err))
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package middlewares
|
package middlewares
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/core"
|
"github.com/mayswind/ezbookkeeping/pkg/core"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
"github.com/mayswind/ezbookkeeping/pkg/errs"
|
||||||
"github.com/mayswind/ezbookkeeping/pkg/log"
|
"github.com/mayswind/ezbookkeeping/pkg/log"
|
||||||
@@ -8,11 +10,20 @@ import (
|
|||||||
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
"github.com/mayswind/ezbookkeeping/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TokenSourceType represents token source
|
||||||
|
type TokenSourceType byte
|
||||||
|
|
||||||
|
// Token source types
|
||||||
|
const (
|
||||||
|
TOKEN_SOURCE_TYPE_HEADER TokenSourceType = 1
|
||||||
|
TOKEN_SOURCE_TYPE_ARGUMENT TokenSourceType = 2
|
||||||
|
)
|
||||||
|
|
||||||
const tokenQueryStringParam = "token"
|
const tokenQueryStringParam = "token"
|
||||||
|
|
||||||
// JWTAuthorization verifies whether current request is valid by jwt token
|
// JWTAuthorization verifies whether current request is valid by jwt token
|
||||||
func JWTAuthorization(c *core.Context) {
|
func JWTAuthorization(c *core.Context) {
|
||||||
claims, err := getTokenClaims(c)
|
claims, err := getTokenClaims(c, TOKEN_SOURCE_TYPE_HEADER)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.PrintJsonErrorResult(c, err)
|
utils.PrintJsonErrorResult(c, err)
|
||||||
@@ -37,22 +48,32 @@ func JWTAuthorization(c *core.Context) {
|
|||||||
|
|
||||||
// JWTAuthorizationByQueryString verifies whether current request is valid by jwt token
|
// JWTAuthorizationByQueryString verifies whether current request is valid by jwt token
|
||||||
func JWTAuthorizationByQueryString(c *core.Context) {
|
func JWTAuthorizationByQueryString(c *core.Context) {
|
||||||
token, exists := c.GetQuery(tokenQueryStringParam)
|
claims, err := getTokenClaims(c, TOKEN_SOURCE_TYPE_ARGUMENT)
|
||||||
|
|
||||||
if !exists {
|
if err != nil {
|
||||||
log.WarnfWithRequestId(c, "[authorization.JWTAuthorizationByQueryString] no token provided")
|
utils.PrintJsonErrorResult(c, err)
|
||||||
utils.PrintJsonErrorResult(c, errs.ErrUnauthorizedAccess)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Request.Header.Set("Authorization", token)
|
if claims.Type == core.USER_TOKEN_TYPE_REQUIRE_2FA {
|
||||||
|
log.WarnfWithRequestId(c, "[authorization.JWTAuthorizationByQueryString] user \"uid:%d\" token requires 2fa", claims.Uid)
|
||||||
|
utils.PrintJsonErrorResult(c, errs.ErrCurrentTokenRequire2FA)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
JWTAuthorization(c)
|
if claims.Type != core.USER_TOKEN_TYPE_NORMAL {
|
||||||
|
log.WarnfWithRequestId(c, "[authorization.JWTAuthorizationByQueryString] user \"uid:%d\" token type is invalid", claims.Uid)
|
||||||
|
utils.PrintJsonErrorResult(c, errs.ErrCurrentInvalidTokenType)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetTokenClaims(claims)
|
||||||
|
c.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
// JWTTwoFactorAuthorization verifies whether current request is valid by 2fa passcode
|
// JWTTwoFactorAuthorization verifies whether current request is valid by 2fa passcode
|
||||||
func JWTTwoFactorAuthorization(c *core.Context) {
|
func JWTTwoFactorAuthorization(c *core.Context) {
|
||||||
claims, err := getTokenClaims(c)
|
claims, err := getTokenClaims(c, TOKEN_SOURCE_TYPE_HEADER)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.PrintJsonErrorResult(c, err)
|
utils.PrintJsonErrorResult(c, err)
|
||||||
@@ -69,8 +90,8 @@ func JWTTwoFactorAuthorization(c *core.Context) {
|
|||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTokenClaims(c *core.Context) (*core.UserTokenClaims, *errs.Error) {
|
func getTokenClaims(c *core.Context, source TokenSourceType) (*core.UserTokenClaims, *errs.Error) {
|
||||||
token, claims, err := services.Tokens.ParseToken(c)
|
token, claims, err := parseToken(c, source)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WarnfWithRequestId(c, "[authorization.getTokenClaims] failed to parse token, because %s", err.Error())
|
log.WarnfWithRequestId(c, "[authorization.getTokenClaims] failed to parse token, because %s", err.Error())
|
||||||
@@ -89,3 +110,11 @@ func getTokenClaims(c *core.Context) (*core.UserTokenClaims, *errs.Error) {
|
|||||||
|
|
||||||
return claims, nil
|
return claims, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseToken(c *core.Context, source TokenSourceType) (*jwt.Token, *core.UserTokenClaims, error) {
|
||||||
|
if source == TOKEN_SOURCE_TYPE_ARGUMENT {
|
||||||
|
return services.Tokens.ParseTokenByArgument(c, tokenQueryStringParam)
|
||||||
|
}
|
||||||
|
|
||||||
|
return services.Tokens.ParseTokenByHeader(c)
|
||||||
|
}
|
||||||
|
|||||||
+63
-54
@@ -63,61 +63,14 @@ func (s *TokenService) GetAllUnexpiredNormalTokensByUid(uid int64) ([]*models.To
|
|||||||
return tokenRecords, err
|
return tokenRecords, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseToken returns the token model according to request data
|
// ParseTokenByHeader returns the token model according to request data
|
||||||
func (s *TokenService) ParseToken(c *core.Context) (*jwt.Token, *core.UserTokenClaims, error) {
|
func (s *TokenService) ParseTokenByHeader(c *core.Context) (*jwt.Token, *core.UserTokenClaims, error) {
|
||||||
claims := &core.UserTokenClaims{}
|
return s.parseToken(c, request.BearerExtractor{})
|
||||||
|
}
|
||||||
|
|
||||||
token, err := request.ParseFromRequest(c.Request, request.AuthorizationHeaderExtractor,
|
// ParseTokenByArgument returns the token model according to request data
|
||||||
func(token *jwt.Token) (interface{}, error) {
|
func (s *TokenService) ParseTokenByArgument(c *core.Context, tokenParameterName string) (*jwt.Token, *core.UserTokenClaims, error) {
|
||||||
now := time.Now().Unix()
|
return s.parseToken(c, request.ArgumentExtractor{tokenParameterName})
|
||||||
userTokenId, err := utils.StringToInt64(claims.UserTokenId)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.WarnfWithRequestId(c, "[tokens.ParseToken] token \"utid:%s\" in token of user \"uid:%d\" is invalid, because %s", claims.UserTokenId, claims.Uid, err.Error())
|
|
||||||
return nil, errs.ErrInvalidUserTokenId
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenRecord, err := s.getTokenRecord(claims.Uid, userTokenId, claims.IssuedAt)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.WarnfWithRequestId(c, "[tokens.ParseToken] token \"utid:%s\" of user \"uid:%d\" record not found, because %s", claims.UserTokenId, claims.Uid, err.Error())
|
|
||||||
return nil, errs.ErrTokenRecordNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
if tokenRecord.ExpiredUnixTime < now {
|
|
||||||
log.WarnfWithRequestId(c, "[tokens.ParseToken] token \"utid:%s\" of user \"uid:%d\" record is expired", claims.UserTokenId, claims.Uid)
|
|
||||||
return nil, errs.ErrTokenExpired
|
|
||||||
}
|
|
||||||
|
|
||||||
return []byte(tokenRecord.Secret), nil
|
|
||||||
},
|
|
||||||
request.WithClaims(claims),
|
|
||||||
request.WithParser(jwt.NewParser(jwt.WithIssuedAt())),
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if err == request.ErrNoTokenInRequest {
|
|
||||||
return nil, nil, errs.ErrTokenIsEmpty
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == jwt.ErrTokenMalformed || err == jwt.ErrTokenUnverifiable || err == jwt.ErrTokenSignatureInvalid {
|
|
||||||
log.WarnfWithRequestId(c, "[tokens.ParseToken] token is invalid, because %s", err.Error())
|
|
||||||
return nil, nil, errs.ErrCurrentInvalidToken
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == jwt.ErrTokenExpired {
|
|
||||||
return nil, nil, errs.ErrCurrentTokenExpired
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == jwt.ErrTokenUsedBeforeIssued {
|
|
||||||
log.WarnfWithRequestId(c, "[tokens.ParseToken] token is invalid, because issue time is later than now")
|
|
||||||
return nil, nil, errs.ErrCurrentInvalidToken
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return token, claims, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateToken generates a new normal token and saves to database
|
// CreateToken generates a new normal token and saves to database
|
||||||
@@ -242,6 +195,62 @@ func (s *TokenService) GenerateTokenId(tokenRecord *models.TokenRecord) string {
|
|||||||
return fmt.Sprintf("%d:%d:%d", tokenRecord.Uid, tokenRecord.CreatedUnixTime, tokenRecord.UserTokenId)
|
return fmt.Sprintf("%d:%d:%d", tokenRecord.Uid, tokenRecord.CreatedUnixTime, tokenRecord.UserTokenId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *TokenService) parseToken(c *core.Context, extractor request.Extractor) (*jwt.Token, *core.UserTokenClaims, error) {
|
||||||
|
claims := &core.UserTokenClaims{}
|
||||||
|
|
||||||
|
token, err := request.ParseFromRequest(c.Request, extractor,
|
||||||
|
func(token *jwt.Token) (interface{}, error) {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
userTokenId, err := utils.StringToInt64(claims.UserTokenId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[tokens.ParseToken] token \"utid:%s\" in token of user \"uid:%d\" is invalid, because %s", claims.UserTokenId, claims.Uid, err.Error())
|
||||||
|
return nil, errs.ErrInvalidUserTokenId
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenRecord, err := s.getTokenRecord(claims.Uid, userTokenId, claims.IssuedAt)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.WarnfWithRequestId(c, "[tokens.ParseToken] token \"utid:%s\" of user \"uid:%d\" record not found, because %s", claims.UserTokenId, claims.Uid, err.Error())
|
||||||
|
return nil, errs.ErrTokenRecordNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if tokenRecord.ExpiredUnixTime < now {
|
||||||
|
log.WarnfWithRequestId(c, "[tokens.ParseToken] token \"utid:%s\" of user \"uid:%d\" record is expired", claims.UserTokenId, claims.Uid)
|
||||||
|
return nil, errs.ErrTokenExpired
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(tokenRecord.Secret), nil
|
||||||
|
},
|
||||||
|
request.WithClaims(claims),
|
||||||
|
request.WithParser(jwt.NewParser(jwt.WithIssuedAt())),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if err == request.ErrNoTokenInRequest {
|
||||||
|
return nil, nil, errs.ErrTokenIsEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == jwt.ErrTokenMalformed || err == jwt.ErrTokenUnverifiable || err == jwt.ErrTokenSignatureInvalid {
|
||||||
|
log.WarnfWithRequestId(c, "[tokens.ParseToken] token is invalid, because %s", err.Error())
|
||||||
|
return nil, nil, errs.ErrCurrentInvalidToken
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == jwt.ErrTokenExpired {
|
||||||
|
return nil, nil, errs.ErrCurrentTokenExpired
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == jwt.ErrTokenUsedBeforeIssued {
|
||||||
|
log.WarnfWithRequestId(c, "[tokens.ParseToken] token is invalid, because issue time is later than now")
|
||||||
|
return nil, nil, errs.ErrCurrentInvalidToken
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return token, claims, err
|
||||||
|
}
|
||||||
|
|
||||||
func (s *TokenService) createToken(user *models.User, tokenType core.TokenType, userAgent string, expiryDate time.Duration) (string, *core.UserTokenClaims, error) {
|
func (s *TokenService) createToken(user *models.User, tokenType core.TokenType, userAgent string, expiryDate time.Duration) (string, *core.UserTokenClaims, error) {
|
||||||
var err error
|
var err error
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|||||||
Reference in New Issue
Block a user