first commit
This commit is contained in:
254
src/client/api/rest/midlewares.go
Normal file
254
src/client/api/rest/midlewares.go
Normal file
@ -0,0 +1,254 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"quantex.com/qfixpt/src/app"
|
||||
jwttoken "quantex.com/qfixpt/src/common/jwttoken"
|
||||
"quantex.com/qfixpt/src/common/tracerr"
|
||||
)
|
||||
|
||||
const ErrorField = "error"
|
||||
|
||||
func (cont *Controller) PartyAdmin(c *gin.Context) {
|
||||
if cont.GetUser(c).IsPartyAdmin {
|
||||
return
|
||||
}
|
||||
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ErrorField: "Unauthorized Admin"})
|
||||
}
|
||||
|
||||
func (cont *Controller) CanSendOrder(c *gin.Context) {
|
||||
if cont.GetUser(c).IsViewer {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ErrorField: "Rol Viewer is unauthorized to send orders"})
|
||||
}
|
||||
}
|
||||
|
||||
func (cont *Controller) Middleman(c *gin.Context) {
|
||||
if cont.GetUser(c).IsMiddleman {
|
||||
return
|
||||
}
|
||||
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ErrorField: "Unauthorized Middleman"})
|
||||
}
|
||||
|
||||
func (cont *Controller) BackOfficeUser(c *gin.Context) {
|
||||
if cont.GetUser(c).IsBackOffice {
|
||||
return
|
||||
}
|
||||
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ErrorField: "Unauthorized User"})
|
||||
}
|
||||
|
||||
func (cont *Controller) SuperUser(c *gin.Context) {
|
||||
if cont.GetUser(c).IsSuperUser {
|
||||
return
|
||||
}
|
||||
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ErrorField: "Unauthorized Admin User"})
|
||||
}
|
||||
|
||||
func (cont *Controller) Options(c *gin.Context) {
|
||||
if c.Request.Method != http.MethodOptions {
|
||||
c.Next()
|
||||
} else {
|
||||
setHeaders(c, cont.config)
|
||||
c.AbortWithStatus(http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func (cont *Controller) AuthRequired(ctx *gin.Context) {
|
||||
setHeaders(ctx, cont.config)
|
||||
|
||||
if c, err := ctx.Cookie("session_token"); c != "" && err == nil {
|
||||
cont.SessionCookieAuth(ctx)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// check header for Token Auth
|
||||
reqToken := ctx.GetHeader("Authorization")
|
||||
if reqToken != "" {
|
||||
cont.AuthorizationAuth(reqToken, ctx)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
log.Error().Msg("Token Auth Unauthorized: missing session cookie and Authorization header")
|
||||
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ErrorField: "Unauthorized - AuthRequired 1"})
|
||||
}
|
||||
|
||||
func (cont *Controller) AuthorizationAuth(reqToken string, ctx *gin.Context) {
|
||||
token := strings.TrimSpace(strings.TrimPrefix(reqToken, "Bearer"))
|
||||
|
||||
if cont.config.EnableJWTAuth && jwttoken.IsJWT(token) {
|
||||
cont.JWTTokenAuth(token, ctx)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
cont.BearerTokenAuth(token, ctx)
|
||||
}
|
||||
|
||||
func (cont *Controller) BearerTokenAuth(reqToken string, ctx *gin.Context) {
|
||||
if !strings.HasPrefix(reqToken, "Bearer ") {
|
||||
log.Error().Msg("Token Auth Unauthorized: missing Bearer prefix")
|
||||
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ErrorField: "Token Auth Unauthorized"})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
token := strings.Split(reqToken, "Bearer ")
|
||||
if len(token) != 2 {
|
||||
|
||||
log.Error().Msg("Token Auth Unauthorized at TokenAuth: invalid token format")
|
||||
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ErrorField: "Token Auth Unauthorized 1"})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
user, err := cont.validateUserToken(token[1])
|
||||
if err != nil {
|
||||
err = errors.New("Token Auth Unauthorized at TokenAuth - %s" + err.Error())
|
||||
|
||||
log.Error().Msg(err.Error())
|
||||
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ErrorField: "Token Auth Unauthorized 2"})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
log.Info().Msgf("User %s authenticated successfully at TokenAuth", user.Email)
|
||||
|
||||
ctx.Set(responseKey, []byte(user.Email))
|
||||
|
||||
ctx.Next()
|
||||
}
|
||||
|
||||
func (cont *Controller) JWTTokenAuth(token string, ctx *gin.Context) {
|
||||
from := ctx.Query("from")
|
||||
|
||||
serviceAuth, err := jwttoken.Validate(from, token, cont.config.AuthorizedServices)
|
||||
if err != nil || serviceAuth == nil || serviceAuth.Token == nil {
|
||||
err := tracerr.Errorf("invalid token or claims: %w", err)
|
||||
|
||||
log.Error().Msg(err.Error())
|
||||
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized,
|
||||
gin.H{ErrorField: err.Error()})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
user, err := cont.validateUserToken(*serviceAuth.Token)
|
||||
if err != nil {
|
||||
err = tracerr.Errorf("Token Auth Unauthorized at JWTTokenAuth: %w", err)
|
||||
|
||||
log.Error().Msg(err.Error())
|
||||
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ErrorField: "Invalid credentials"})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Set("authorized_service", serviceAuth)
|
||||
ctx.Set(responseKey, []byte(user.Email)) // TODO: will services be treated as users? if not remove this line
|
||||
|
||||
log.Info().Str("issuer", serviceAuth.Name).Msg("Service authenticated successfully")
|
||||
|
||||
ctx.Next()
|
||||
}
|
||||
|
||||
func (cont *Controller) validateUserToken(token string) (user *app.User, err error) {
|
||||
userInfo := strings.Split(token, ":")
|
||||
if len(userInfo) != 2 || userInfo[1] == "" {
|
||||
err = tracerr.Errorf("invalid token format at validateUserToken")
|
||||
|
||||
log.Error().Msg(err.Error())
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
email := userInfo[0]
|
||||
|
||||
user, err = cont.store.UserByEmail(email)
|
||||
if user == nil || err != nil {
|
||||
err = tracerr.Errorf("user not found at validateUserToken: %w", err)
|
||||
|
||||
log.Error().Msg(err.Error())
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tkn := userInfo[1]
|
||||
|
||||
if user.Token != tkn {
|
||||
err = tracerr.Errorf("invalid token credentials at validateUserToken")
|
||||
|
||||
log.Error().Msg(err.Error())
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Info().Str("email", user.Email).Msg("Service user validated successfully")
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (cont *Controller) SessionCookieAuth(ctx *gin.Context) {
|
||||
// We can obtain the session token from the requests cookies, which come with every handler
|
||||
sessionToken, err := ctx.Cookie("session_token")
|
||||
if err != nil {
|
||||
if errors.Is(err, http.ErrNoCookie) {
|
||||
// If the cookie is not set, return an unauthorized status
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ErrorField: "Unauthorized - AuthRequired 1"})
|
||||
|
||||
return
|
||||
}
|
||||
// For any other type of error, return a bad handler status
|
||||
ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{ErrorField: "Bad handler - AuthRequired 2"})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
cont.validateCookieToExternal(sessionToken, ctx)
|
||||
}
|
||||
|
||||
func (cont *Controller) validateCookieToExternal(sessionToken string, ctx *gin.Context) {
|
||||
ok, err := cont.store.ValidateSession(sessionToken)
|
||||
if err != nil {
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ErrorField: "Unauthorized - AuthRequired 4"})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !ok {
|
||||
// If the session token is not valid, return an unauthorized error
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ErrorField: "Unauthorized - AuthRequired 5"})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Next()
|
||||
}
|
||||
|
||||
func IPWhiteList(whitelist map[string]bool) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if !whitelist[c.ClientIP()] {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
|
||||
"status": http.StatusForbidden,
|
||||
"message": "Permission denied",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user