Merge pull request #4 from zdam-egzamin-zawodowy/gin-configuration
Add some gin middlewares (Authenticate, Logger, GinContextToContext)
This commit is contained in:
commit
4f9c6b67df
|
@ -0,0 +1,48 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/auth"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/models"
|
||||
)
|
||||
|
||||
const (
|
||||
authorizationHeader = "Authorization"
|
||||
)
|
||||
|
||||
var (
|
||||
authenticateKey contextKey = "current_user"
|
||||
)
|
||||
|
||||
func Authenticate(ucase auth.Usecase) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
token := extractToken(c.GetHeader(authorizationHeader))
|
||||
if token != "" {
|
||||
ctx := c.Request.Context()
|
||||
user, err := ucase.ExtractAccessTokenMetadata(ctx, token)
|
||||
if err == nil && user != nil {
|
||||
ctx = context.WithValue(ctx, authenticateKey, user)
|
||||
c.Request = c.Request.WithContext(ctx)
|
||||
}
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func UserFromContext(ctx context.Context) (*models.User, error) {
|
||||
user := ctx.Value(authenticateKey)
|
||||
if user == nil {
|
||||
err := fmt.Errorf("Could not retrieve *models.User")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gc, ok := user.(*models.User)
|
||||
if !ok {
|
||||
err := fmt.Errorf("*models.User has wrong type")
|
||||
return nil, err
|
||||
}
|
||||
return gc, nil
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package middleware
|
||||
|
||||
type contextKey string
|
|
@ -0,0 +1,35 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var (
|
||||
ginContextToContextKey contextKey = "gin_context"
|
||||
)
|
||||
|
||||
func GinContextToContext() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := context.WithValue(c.Request.Context(), ginContextToContextKey, c)
|
||||
c.Request = c.Request.WithContext(ctx)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func GinContextFromContext(ctx context.Context) (*gin.Context, error) {
|
||||
ginContext := ctx.Value(ginContextToContextKey)
|
||||
if ginContext == nil {
|
||||
err := fmt.Errorf("could not retrieve gin.Context")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gc, ok := ginContext.(*gin.Context)
|
||||
if !ok {
|
||||
err := fmt.Errorf("gin.Context has wrong type")
|
||||
return nil, err
|
||||
}
|
||||
return gc, nil
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func extractToken(bearToken string) string {
|
||||
strArr := strings.Split(bearToken, " ")
|
||||
if len(strArr) == 2 {
|
||||
return strArr[1]
|
||||
}
|
||||
return ""
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var timeFormat = "02/Jan/2006:15:04:05 -0700"
|
||||
|
||||
// Logger is the logrus logger handler
|
||||
func Logger(logger logrus.FieldLogger) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// other handler can change c.Path so:
|
||||
path := c.Request.URL.Path
|
||||
start := time.Now()
|
||||
c.Next()
|
||||
stop := time.Since(start)
|
||||
statusCode := c.Writer.Status()
|
||||
clientIP := c.ClientIP()
|
||||
clientUserAgent := c.Request.UserAgent()
|
||||
referer := c.Request.Referer()
|
||||
dataLength := c.Writer.Size()
|
||||
if dataLength < 0 {
|
||||
dataLength = 0
|
||||
}
|
||||
|
||||
entry := logger.WithFields(logrus.Fields{
|
||||
"statusCode": statusCode,
|
||||
"latency": stop.String(), // time to process
|
||||
"clientIP": clientIP,
|
||||
"method": c.Request.Method,
|
||||
"path": path,
|
||||
"referer": referer,
|
||||
"dataLength": dataLength,
|
||||
"userAgent": clientUserAgent,
|
||||
})
|
||||
|
||||
if len(c.Errors) > 0 {
|
||||
entry.Error(c.Errors.ByType(gin.ErrorTypePrivate).String())
|
||||
} else {
|
||||
msg := fmt.Sprintf(
|
||||
"%s - [%s] \"%s %s\" %d %d \"%s\" \"%s\" (%s)",
|
||||
clientIP,
|
||||
time.Now().Format(timeFormat),
|
||||
c.Request.Method,
|
||||
path,
|
||||
statusCode,
|
||||
dataLength,
|
||||
referer,
|
||||
clientUserAgent,
|
||||
stop,
|
||||
)
|
||||
if statusCode >= http.StatusInternalServerError {
|
||||
entry.Error(msg)
|
||||
} else if statusCode >= http.StatusBadRequest {
|
||||
entry.Warn(msg)
|
||||
} else {
|
||||
entry.Info(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
36
main.go
36
main.go
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/db"
|
||||
"github.com/zdam-egzamin-zawodowy/backend/internal/gin/middleware"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -37,20 +38,7 @@ func main() {
|
|||
}
|
||||
logrus.Info("Database connection established")
|
||||
|
||||
router := gin.Default()
|
||||
if mode.Get() == mode.DevelopmentMode {
|
||||
router.Use(cors.New(cors.Config{
|
||||
AllowOriginFunc: func(string) bool {
|
||||
return true
|
||||
},
|
||||
AllowCredentials: true,
|
||||
ExposeHeaders: []string{"X-Access-Token", "X-Refresh-Token"},
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"},
|
||||
AllowHeaders: []string{"Origin", "Content-Length", "Content-Type", "Authorization"},
|
||||
AllowWebSockets: false,
|
||||
}))
|
||||
}
|
||||
|
||||
router := setupRouter()
|
||||
srv := &http.Server{
|
||||
Addr: ":8080",
|
||||
Handler: router,
|
||||
|
@ -86,3 +74,23 @@ func setupLogger() {
|
|||
customFormatter.FullTimestamp = true
|
||||
logrus.SetFormatter(customFormatter)
|
||||
}
|
||||
|
||||
func setupRouter() *gin.Engine {
|
||||
router := gin.New()
|
||||
|
||||
router.Use(middleware.Logger(logrus.WithField("hostname", "api")), gin.Recovery())
|
||||
if mode.Get() == mode.DevelopmentMode {
|
||||
router.Use(cors.New(cors.Config{
|
||||
AllowOriginFunc: func(string) bool {
|
||||
return true
|
||||
},
|
||||
AllowCredentials: true,
|
||||
ExposeHeaders: []string{"Authorization"},
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"},
|
||||
AllowHeaders: []string{"Origin", "Content-Length", "Content-Type", "Authorization"},
|
||||
AllowWebSockets: false,
|
||||
}))
|
||||
}
|
||||
|
||||
return router
|
||||
}
|
||||
|
|
Reference in New Issue