add: api key middleware

This commit is contained in:
$(pass /github/name)
2024-09-02 17:05:15 +02:00
parent 6ac2b32a1f
commit 1ad2f2090f
7 changed files with 121 additions and 35 deletions

View File

@@ -1,7 +1,6 @@
package controllers
import (
"GoMembership/internal/config"
"GoMembership/internal/models"
"GoMembership/internal/services"
@@ -28,19 +27,6 @@ func (mc *MembershipController) RegisterSubscription(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": "Couldn't decode subscription data"})
}
logger.Info.Printf("Using API key: %v", config.Auth.APIKEY)
if regData.APIKey == "" {
logger.Error.Println("API Key is missing")
c.JSON(http.StatusUnauthorized, "API Key is missing")
return
}
if regData.APIKey != config.Auth.APIKEY {
logger.Error.Printf("API Key not valid: %v", regData.APIKey)
c.JSON(http.StatusUnauthorized, "API Key is missing")
return
}
// Register Subscription
id, err := mc.Service.RegisterSubscription(&regData.Model)
if err != nil {

View File

@@ -0,0 +1,31 @@
package middlewares
import (
"crypto/subtle"
"net/http"
"github.com/gin-gonic/gin"
"GoMembership/internal/config"
)
func APIKeyMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
clientAPIKey := c.GetHeader("X-API-Key")
if clientAPIKey == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "API key is missing"})
c.Abort()
return
}
// Using subtle.ConstantTimeCompare to mitigate timing attacks
if subtle.ConstantTimeCompare([]byte(clientAPIKey), []byte(config.Auth.APIKEY)) != 1 {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid API key"})
c.Abort()
return
}
c.Next()
}
}

View File

@@ -0,0 +1,61 @@
package middlewares
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"GoMembership/internal/config"
)
func TestAPIKeyMiddleware(t *testing.T) {
// Set up a test API key
testAPIKey := "test-api-key-12345"
config.Auth.APIKEY = testAPIKey
// Set Gin to Test Mode
gin.SetMode(gin.TestMode)
// Tests table
tests := []struct {
name string
apiKey string
wantStatus int
}{
{"Valid API Key", testAPIKey, http.StatusOK},
{"Missing API Key", "", http.StatusUnauthorized},
{"Invalid API Key", "wrong-key", http.StatusUnauthorized},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Set up a new test router and handler
router := gin.New()
router.Use(APIKeyMiddleware())
router.GET("/test", func(c *gin.Context) {
c.Status(http.StatusOK)
})
// Create a test request
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/test", nil)
if tt.apiKey != "" {
req.Header.Set("X-API-Key", tt.apiKey)
}
// Serve the request
router.ServeHTTP(w, req)
// Assert the response
assert.Equal(t, tt.wantStatus, w.Code)
// Additional assertions for specific cases
if tt.wantStatus == http.StatusUnauthorized {
assert.Contains(t, w.Body.String(), "API key")
}
})
}
}

View File

@@ -8,11 +8,20 @@ import (
)
func RegisterRoutes(router *gin.Engine, userController *controllers.UserController, membershipcontroller *controllers.MembershipController, contactController *controllers.ContactController) {
router.GET("/backend/verify", userController.VerifyMailHandler)
router.POST("/backend/api/register", userController.RegisterUser)
router.POST("/backend/api/register/subscription", membershipcontroller.RegisterSubscription)
router.POST("/backend/api/contact", contactController.RelayContactRequest)
// router.HandleFunc("/login", userController.LoginUser).Methods("POST")
router.GET("/verify", userController.VerifyMailHandler)
router.POST("/h/register", userController.RegisterUser)
router.POST("/h/contact", contactController.RelayContactRequest)
router.POST("/csp-report", middlewares.CSPReportHandling)
// create subrouter for teh authenticated area /account
// also pthprefix matches everything below /account
// accountRouter := router.PathPrefix("/account").Subrouter()
// accountRouter.Use(middlewares.AuthMiddleware)
//create api key required router
apiRouter := router.Group("/api")
{
router.POST("/subscription", membershipcontroller.RegisterSubscription)
}
apiRouter.Use(middlewares.APIKeyMiddleware())
}

View File

@@ -61,10 +61,6 @@ func Run() {
router.Use(middlewares.RateLimitMiddleware(limiter))
routes.RegisterRoutes(router, userController, membershipController, contactController)
// create subrouter for teh authenticated area /account
// also pthprefix matches everything below /account
// accountRouter := router.PathPrefix("/account").Subrouter()
// accountRouter.Use(middlewares.AuthMiddleware)
logger.Info.Println("Starting server on :8080")
srv = &http.Server{

View File

@@ -12,4 +12,7 @@ var (
ErrTokenNotSet = errors.New("verification token has not been set")
ErrNoData = errors.New("no data provided")
ErrNoRowsAffected = errors.New("no rows affected")
ErrValueTooLong = errors.New("cookie value too long")
ErrInvalidValue = errors.New("invalid cookie value")
ErrInvalidSigningAlgorithm = errors.New("invalid signing algorithm")
)

View File

@@ -8,7 +8,7 @@ noch Ihre Emailadresse indem Sie hier klicken:
E-Mail Adresse bestätigen
{{.BASEURL}}/backend/verify?token={{.Token}}
{{.BASEURL}}/verify?token={{.Token}}
Nachdem wir Ihre E-Mail Adresse bestätigen konnten, schicken wir
Ihnen alle weiteren Informationen zu. Wir freuen uns auf die
@@ -94,7 +94,7 @@ Der Vorstand
</div>
<div style="text-align: center; padding: 16px 24px 16px 24px">
<a
href="{{.BASEURL}}/backend/verify?token={{.Token}}"
href="{{.BASEURL}}/verify?token={{.Token}}"
style="
color: #ffffff;
font-size: 26px;
@@ -146,7 +146,7 @@ Der Vorstand
padding: 4px 24px 16px 24px;
"
>
{{.BASEURL}}/backend/verify?token={{.Token}}
{{.BASEURL}}/verify?token={{.Token}}
</div>
<div style="font-weight: normal; padding: 16px 24px 16px 24px">
Nachdem wir Ihre E-Mail Adresse bestätigen konnten, schicken wir