add: api key middleware
This commit is contained in:
@@ -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(®Data.Model)
|
||||
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) {
|
||||
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())
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -3,13 +3,16 @@ package errors
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrNotFound = errors.New("not found")
|
||||
ErrUserNotFound = errors.New("user not found")
|
||||
ErrInvalidEmail = errors.New("invalid email")
|
||||
ErrInvalidCredentials = errors.New("invalid credentials: unauthorized")
|
||||
ErrAlreadyVerified = errors.New("user is already verified")
|
||||
ErrTokenNotFound = errors.New("verification token not found")
|
||||
ErrTokenNotSet = errors.New("verification token has not been set")
|
||||
ErrNoData = errors.New("no data provided")
|
||||
ErrNoRowsAffected = errors.New("no rows affected")
|
||||
ErrNotFound = errors.New("not found")
|
||||
ErrUserNotFound = errors.New("user not found")
|
||||
ErrInvalidEmail = errors.New("invalid email")
|
||||
ErrInvalidCredentials = errors.New("invalid credentials: unauthorized")
|
||||
ErrAlreadyVerified = errors.New("user is already verified")
|
||||
ErrTokenNotFound = errors.New("verification token not found")
|
||||
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")
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user