Merge pull request #4 from zdam-egzamin-zawodowy/gin-configuration

Add some gin middlewares (Authenticate, Logger, GinContextToContext)
This commit is contained in:
Dawid Wysokiński 2021-03-06 10:35:28 +01:00 committed by GitHub
commit 4f9c6b67df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 187 additions and 14 deletions

View File

@ -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
}

View File

@ -0,0 +1,3 @@
package middleware
type contextKey string

View File

@ -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
}

View File

@ -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 ""
}

View File

@ -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
View File

@ -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
}