Skip to content

Commit

Permalink
feat: add middlewares
Browse files Browse the repository at this point in the history
  • Loading branch information
isaqueveras committed Oct 17, 2023
1 parent 68f9305 commit 25b0196
Show file tree
Hide file tree
Showing 11 changed files with 139 additions and 277 deletions.
5 changes: 2 additions & 3 deletions delivery/http/auth/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@ import (

"github.com/gin-gonic/gin"
"github.com/google/uuid"
gopowersso "github.com/isaqueveras/go-powersso"

app "github.com/isaqueveras/powersso/application/auth"
domain "github.com/isaqueveras/powersso/domain/auth"
"github.com/isaqueveras/powersso/i18n"
"github.com/isaqueveras/powersso/middleware"
"github.com/isaqueveras/powersso/oops"
"github.com/isaqueveras/powersso/utils"
)
Expand Down Expand Up @@ -83,7 +82,7 @@ func changePassword(ctx *gin.Context) {

// @Router /v1/auth/logout [DELETE]
func logout(ctx *gin.Context) {
sessionID, err := uuid.Parse(gopowersso.GetSession(ctx).SessionID)
sessionID, err := uuid.Parse(middleware.GetSession(ctx).SessionID)
if err != nil {
oops.Handling(ctx, err)
return
Expand Down
4 changes: 2 additions & 2 deletions delivery/http/auth/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ package auth

import (
"github.com/gin-gonic/gin"
gopowersso "github.com/isaqueveras/go-powersso"
"github.com/isaqueveras/powersso/middleware"
)

// Router is the router for the auth module.
Expand All @@ -25,7 +25,7 @@ func RouterAuthorization(r *gin.RouterGroup) {
user.PUT("disable", disable)

otp := user.Group("otp")
otp.Use(gopowersso.SameUser())
otp.Use(middleware.Yourself())

otp.GET("qrcode", qrcode)
otp.POST("configure", configure)
Expand Down
5 changes: 2 additions & 3 deletions delivery/http/project/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import (
"net/http"

"github.com/gin-gonic/gin"
gopowersso "github.com/isaqueveras/go-powersso"

"github.com/isaqueveras/powersso/application/project"
"github.com/isaqueveras/powersso/middleware"
"github.com/isaqueveras/powersso/oops"
"github.com/isaqueveras/powersso/utils"
)
Expand All @@ -19,7 +18,7 @@ import (
func create(ctx *gin.Context) {
var (
input = new(project.CreateProjectReq)
session = gopowersso.GetSession(ctx)
session = middleware.GetSession(ctx)
err error
)

Expand Down
4 changes: 2 additions & 2 deletions delivery/http/project/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ package project

import (
"github.com/gin-gonic/gin"
gopowersso "github.com/isaqueveras/go-powersso"
"github.com/isaqueveras/powersso/middleware"
)

// RouterAuthorization is the router for the project module.
func RouterAuthorization(r *gin.RouterGroup) {
r.POST("create", gopowersso.OnlyAdmin(), create)
r.POST("create", middleware.OnlyAdmin(), create)
}
85 changes: 85 additions & 0 deletions middleware/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package middleware

import (
"log"
"net/http"

"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v4"
"github.com/isaqueveras/powersso/config"
"github.com/isaqueveras/powersso/domain/auth"
"github.com/isaqueveras/powersso/tokens"
)

// Session models the session data
type Session struct {
SessionID string
UserID string
UserLevel string
FirstName string
}

// GetSession gets the session data from the context
func GetSession(ctx *gin.Context) *Session {
session, ok := ctx.Get("SESSION")
if !ok {
ctx.AbortWithStatus(http.StatusUnauthorized)
return nil
}

value := session.(jwt.MapClaims)
return &Session{
SessionID: value["SessionID"].(string),
UserID: value["UserID"].(string),
UserLevel: value["UserLevel"].(string),
FirstName: value["FirstName"].(string),
}
}

// Auth is a middleware to check if the user is authorized to access the resource
func Auth() gin.HandlerFunc {
return func(ctx *gin.Context) {
var token string
if token = ctx.GetHeader("Authorization"); token == "" || len(token) < 30 {
ctx.AbortWithStatus(http.StatusUnauthorized)
return
}

if claims := tokens.ParseJWT(token[7:], config.Get().GetSecrets()); claims != nil {
ctx.Set("UID", claims["UserID"])
ctx.Set("SESSION", claims)
return
}

ctx.AbortWithStatus(http.StatusUnauthorized)
}
}

// OnlyAdmin check if the user is an administrator
func OnlyAdmin() gin.HandlerFunc {
return func(ctx *gin.Context) {
if GetSession(ctx).UserLevel != string(auth.AdminLevel) {
session := GetSession(ctx)
log.Printf("WARNING: user (%v - %v) tried to access user tried to access route for administrators only", session.UserID, session.FirstName)
ctx.AbortWithStatus(http.StatusForbidden)
return
}
ctx.Next()
}
}

// Yourself validates if the logged in user is the same as the request
func Yourself() gin.HandlerFunc {
return func(ctx *gin.Context) {
session := GetSession(ctx)
userIn := ctx.Param("user_uuid")

if session.UserID != userIn {
log.Printf("WARNING: user (%v - %v) tried to access information for user (%v)", session.UserID, session.FirstName, userIn)
ctx.AbortWithStatus(http.StatusForbidden)
return
}

ctx.Next()
}
}
1 change: 0 additions & 1 deletion oops/error_handling_grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ func HandlingGRPC(err error) error {
}

e = handling(e.Err).(*Error)

return buildGRPCStatus(e)
}

Expand Down
12 changes: 4 additions & 8 deletions server/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (

"github.com/gin-gonic/gin"
"github.com/isaqueveras/endless"
gopowersso "github.com/isaqueveras/go-powersso"

"github.com/isaqueveras/powersso/delivery/http/auth"
"github.com/isaqueveras/powersso/delivery/http/project"
Expand All @@ -32,19 +31,16 @@ func (s *Server) ServerHTTP() (err error) {
router.Use(
middleware.CORS(),
middleware.VersionInfo(),
middleware.SetupI18n(),
middleware.RequestIdentifier(),
middleware.RecoveryWithZap(s.logg.ZapLogger(), true),
middleware.GinZap(s.logg.ZapLogger(), *s.cfg),
middleware.SetupI18n(),
)

// FIXME: fix "gopowersso.Authorization" to accept list of tokens
secret := &s.cfg.GetSecrets()[0]

v1 := router.Group("v1")
auth.Router(v1.Group("auth"))
auth.RouterAuthorization(v1.Group("auth", gopowersso.Authorization(secret)))
project.RouterAuthorization(v1.Group("project", gopowersso.Authorization(secret)))
auth.RouterAuthorization(v1.Group("auth", middleware.Auth()))
project.RouterAuthorization(v1.Group("project", middleware.Auth()))

endless.DefaultReadTimeOut = s.cfg.Server.ReadTimeout * time.Second
endless.DefaultWriteTimeOut = s.cfg.Server.WriteTimeout * time.Second
Expand All @@ -64,7 +60,7 @@ func (s *Server) ServerHTTP() (err error) {

func (s *Server) routerDebugPProf(router *gin.Engine) {
r := router.Group("debug/pprof")
r.Use(gopowersso.Authorization(&s.cfg.GetSecrets()[1]), gopowersso.OnlyAdmin())
r.Use(middleware.Auth(), middleware.OnlyAdmin())
r.GET("/", func(c *gin.Context) { pprof.Index(c.Writer, c.Request) })
r.GET("/cmdline", func(c *gin.Context) { pprof.Cmdline(c.Writer, c.Request) })
r.GET("/profile", func(c *gin.Context) { pprof.Profile(c.Writer, c.Request) })
Expand Down
37 changes: 37 additions & 0 deletions tokens/jwt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) 2022 Isaque Veras
// Use of this source code is governed by MIT
// license that can be found in the LICENSE file.

package tokens

import (
"time"

"github.com/golang-jwt/jwt/v4"
)

// NewToken generates and returns new HS256 signed JWT token.
func NewToken(payload jwt.MapClaims, key string, duration int64) (string, error) {
var (
seconds = time.Duration(duration) * time.Second
claims = jwt.MapClaims{"exp": time.Now().Add(seconds).Unix()}
)

for key, value := range payload {
claims[key] = value
}

return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(key))
}

// ParseJWT verifies and parses JWT token and returns its claims.
func ParseJWT(token string, keys []string) jwt.MapClaims {
parser := jwt.NewParser(jwt.WithValidMethods([]string{"HS256"}))
for _, key := range keys {
parsed, _ := parser.Parse(token, func(t *jwt.Token) (interface{}, error) { return []byte(key), nil })
if claims, ok := parsed.Claims.(jwt.MapClaims); ok && parsed.Valid {
return claims
}
}
return nil
}
12 changes: 5 additions & 7 deletions tokens/user.go → tokens/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,12 @@ import (
// NewAuthToken generates and returns a new authentication token
func NewAuthToken(user *auth.User, sessionID *uuid.UUID) (*string, error) {
claims := jwt.MapClaims{
"session_id": sessionID,
"user_id": user.ID,
"user_level": user.Level,
"first_name": user.FirstName,
"last_name": user.LastName,
"email": user.Email,
"SessionID": sessionID,
"UserID": user.ID,
"UserLevel": user.Level,
"FirstName": user.FirstName,
}

token, err := utils.NewToken(claims, user.GetUserLevel(&config.Get().SecretsTokens), config.Get().SecretsDuration)
token, err := NewToken(claims, user.GetUserLevel(&config.Get().SecretsTokens), config.Get().SecretsDuration)
return utils.Pointer(token), err
}
61 changes: 0 additions & 61 deletions utils/jwt.go

This file was deleted.

Loading

0 comments on commit 25b0196

Please sign in to comment.