255 lines
6.3 KiB
Go
255 lines
6.3 KiB
Go
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
|
|
}
|
|
}
|
|
}
|