add: api key middleware
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"GoMembership/internal/config"
|
|
||||||
"GoMembership/internal/models"
|
"GoMembership/internal/models"
|
||||||
"GoMembership/internal/services"
|
"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"})
|
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
|
// Register Subscription
|
||||||
id, err := mc.Service.RegisterSubscription(®Data.Model)
|
id, err := mc.Service.RegisterSubscription(®Data.Model)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
31
internal/middlewares/api.go
Normal file
31
internal/middlewares/api.go
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
61
internal/middlewares/api_test.go
Normal file
61
internal/middlewares/api_test.go
Normal 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")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,11 +8,20 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func RegisterRoutes(router *gin.Engine, userController *controllers.UserController, membershipcontroller *controllers.MembershipController, contactController *controllers.ContactController) {
|
func RegisterRoutes(router *gin.Engine, userController *controllers.UserController, membershipcontroller *controllers.MembershipController, contactController *controllers.ContactController) {
|
||||||
router.GET("/backend/verify", userController.VerifyMailHandler)
|
router.GET("/verify", userController.VerifyMailHandler)
|
||||||
router.POST("/backend/api/register", userController.RegisterUser)
|
router.POST("/h/register", userController.RegisterUser)
|
||||||
router.POST("/backend/api/register/subscription", membershipcontroller.RegisterSubscription)
|
router.POST("/h/contact", contactController.RelayContactRequest)
|
||||||
router.POST("/backend/api/contact", contactController.RelayContactRequest)
|
|
||||||
// router.HandleFunc("/login", userController.LoginUser).Methods("POST")
|
|
||||||
|
|
||||||
router.POST("/csp-report", middlewares.CSPReportHandling)
|
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())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,10 +61,6 @@ func Run() {
|
|||||||
router.Use(middlewares.RateLimitMiddleware(limiter))
|
router.Use(middlewares.RateLimitMiddleware(limiter))
|
||||||
|
|
||||||
routes.RegisterRoutes(router, userController, membershipController, contactController)
|
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")
|
logger.Info.Println("Starting server on :8080")
|
||||||
srv = &http.Server{
|
srv = &http.Server{
|
||||||
|
|||||||
@@ -12,4 +12,7 @@ var (
|
|||||||
ErrTokenNotSet = errors.New("verification token has not been set")
|
ErrTokenNotSet = errors.New("verification token has not been set")
|
||||||
ErrNoData = errors.New("no data provided")
|
ErrNoData = errors.New("no data provided")
|
||||||
ErrNoRowsAffected = errors.New("no rows affected")
|
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")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ noch Ihre Emailadresse indem Sie hier klicken:
|
|||||||
|
|
||||||
E-Mail Adresse bestätigen
|
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
|
Nachdem wir Ihre E-Mail Adresse bestätigen konnten, schicken wir
|
||||||
Ihnen alle weiteren Informationen zu. Wir freuen uns auf die
|
Ihnen alle weiteren Informationen zu. Wir freuen uns auf die
|
||||||
@@ -94,7 +94,7 @@ Der Vorstand
|
|||||||
</div>
|
</div>
|
||||||
<div style="text-align: center; padding: 16px 24px 16px 24px">
|
<div style="text-align: center; padding: 16px 24px 16px 24px">
|
||||||
<a
|
<a
|
||||||
href="{{.BASEURL}}/backend/verify?token={{.Token}}"
|
href="{{.BASEURL}}/verify?token={{.Token}}"
|
||||||
style="
|
style="
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-size: 26px;
|
font-size: 26px;
|
||||||
@@ -146,7 +146,7 @@ Der Vorstand
|
|||||||
padding: 4px 24px 16px 24px;
|
padding: 4px 24px 16px 24px;
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
{{.BASEURL}}/backend/verify?token={{.Token}}
|
{{.BASEURL}}/verify?token={{.Token}}
|
||||||
</div>
|
</div>
|
||||||
<div style="font-weight: normal; padding: 16px 24px 16px 24px">
|
<div style="font-weight: normal; padding: 16px 24px 16px 24px">
|
||||||
Nachdem wir Ihre E-Mail Adresse bestätigen konnten, schicken wir
|
Nachdem wir Ihre E-Mail Adresse bestätigen konnten, schicken wir
|
||||||
|
|||||||
Reference in New Issue
Block a user