middleware test
This commit is contained in:
@@ -86,7 +86,14 @@ func (lkp *LocalKeyProvider) GetPrivateKey() (*rsa.PrivateKey, error) {
|
||||
return LoadPrivateKey(lkp.PrivateKeyPath)
|
||||
}
|
||||
|
||||
// jwkToPublicKey converts JWK components to RSA public key
|
||||
// jwkToPublicKey converts JSON Web Key (JWK) components to an RSA public key.
|
||||
// Parameters:
|
||||
// - n: base64 URL-encoded string representing the modulus of the RSA key
|
||||
// - e: base64 URL-encoded string representing the exponent of the RSA key
|
||||
//
|
||||
// Returns:
|
||||
// - *rsa.PublicKey: RSA public key constructed from the provided components
|
||||
// - error: if there's an issue decoding the base64 values or creating the key
|
||||
func jwkToPublicKey(n, e string) (*rsa.PublicKey, error) {
|
||||
// Decode the base64 URL-encoded modulus
|
||||
nBytes, err := base64.RawURLEncoding.DecodeString(n)
|
||||
|
||||
+26
-26
@@ -184,29 +184,29 @@ func tokenFromCookie(name string) func(echo.Context) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// GroupGuard middleware ensures a user has at least one of the required groups
|
||||
func GroupGuard(requiredGroups ...string) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
userClaims, ok := c.Get("user").(jwt.MapClaims)
|
||||
if !ok {
|
||||
return echo.NewHTTPError(http.StatusUnauthorized, "user not authenticated")
|
||||
}
|
||||
|
||||
groups, ok := userClaims["cognito:groups"].([]interface{})
|
||||
if !ok {
|
||||
return echo.NewHTTPError(http.StatusForbidden, "no groups found for user")
|
||||
}
|
||||
|
||||
for _, requiredGroup := range requiredGroups {
|
||||
for _, group := range groups {
|
||||
if groupStr, ok := group.(string); ok && strings.EqualFold(groupStr, requiredGroup) {
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return echo.NewHTTPError(http.StatusForbidden, "insufficient permissions")
|
||||
}
|
||||
}
|
||||
}
|
||||
//// GroupGuard middleware ensures a user has at least one of the required groups
|
||||
//func GroupGuard(requiredGroups ...string) echo.MiddlewareFunc {
|
||||
// return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
// return func(c echo.Context) error {
|
||||
// userClaims, ok := c.Get("user").(jwt.MapClaims)
|
||||
// if !ok {
|
||||
// return echo.NewHTTPError(http.StatusUnauthorized, "user not authenticated")
|
||||
// }
|
||||
//
|
||||
// groups, ok := userClaims["cognito:groups"].([]interface{})
|
||||
// if !ok {
|
||||
// return echo.NewHTTPError(http.StatusForbidden, "no groups found for user")
|
||||
// }
|
||||
//
|
||||
// for _, requiredGroup := range requiredGroups {
|
||||
// for _, group := range groups {
|
||||
// if groupStr, ok := group.(string); ok && strings.EqualFold(groupStr, requiredGroup) {
|
||||
// return next(c)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return echo.NewHTTPError(http.StatusForbidden, "insufficient permissions")
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestJWTMiddleware(t *testing.T) {
|
||||
// Use local key files
|
||||
privateKeyPath := "./private_key.pem"
|
||||
publicKeyPath := "./public_key.pem"
|
||||
|
||||
// Create LocalKeyProvider for testing
|
||||
keyProvider := NewLocalKeyProvider(privateKeyPath, publicKeyPath)
|
||||
|
||||
// Initialize Echo instance
|
||||
e := echo.New()
|
||||
|
||||
// Set up a restricted endpoint that requires the 'foo' group
|
||||
e.GET("/test", func(c echo.Context) error {
|
||||
return c.String(http.StatusOK, "authorized")
|
||||
}, JWTWithConfig(JWTConfig{
|
||||
KeyProvider: keyProvider,
|
||||
RequiredGroups: []string{"foo"},
|
||||
}))
|
||||
|
||||
// Test cases
|
||||
tests := []struct {
|
||||
name string
|
||||
groups []string
|
||||
wantStatus int
|
||||
}{
|
||||
{
|
||||
name: "Authorized user with 'foo' group",
|
||||
groups: []string{"foo"},
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "Unauthorized user with 'bar' group",
|
||||
groups: []string{"bar"},
|
||||
wantStatus: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
name: "User with multiple groups including required",
|
||||
groups: []string{"bar", "foo", "baz"},
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Generate JWT token with test groups
|
||||
token, err := GenerateMockJWT(keyProvider, "test-user-id", "test@example.com", tt.groups)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to generate JWT: %v", err)
|
||||
}
|
||||
|
||||
// Create test request
|
||||
req := httptest.NewRequest(http.MethodGet, "/test", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
rec := httptest.NewRecorder()
|
||||
e.ServeHTTP(rec, req)
|
||||
|
||||
// Verify response
|
||||
assert.Equal(t, tt.wantStatus, rec.Code)
|
||||
if tt.wantStatus == http.StatusOK {
|
||||
assert.Equal(t, "authorized", rec.Body.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test different token extraction methods
|
||||
func TestTokenExtraction(t *testing.T) {
|
||||
// Use local key files
|
||||
privateKeyPath := "./private_key.pem"
|
||||
publicKeyPath := "./public_key.pem"
|
||||
|
||||
// Create LocalKeyProvider for testing
|
||||
keyProvider := NewLocalKeyProvider(privateKeyPath, publicKeyPath)
|
||||
|
||||
// Generate JWT token with 'foo' group
|
||||
token, err := GenerateMockJWT(keyProvider, "test-user-id", "test@example.com", []string{"foo"})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to generate JWT: %v", err)
|
||||
}
|
||||
|
||||
// Test different token extraction methods
|
||||
tests := []struct {
|
||||
name string
|
||||
config JWTConfig
|
||||
setupReq func(req *http.Request)
|
||||
wantStatus int
|
||||
}{
|
||||
{
|
||||
name: "Extract from header",
|
||||
config: JWTConfig{
|
||||
KeyProvider: keyProvider,
|
||||
TokenLookup: "header:Authorization",
|
||||
AuthScheme: "Bearer",
|
||||
RequiredGroups: []string{"foo"},
|
||||
},
|
||||
setupReq: func(req *http.Request) {
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
},
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "Extract from query parameter",
|
||||
config: JWTConfig{
|
||||
KeyProvider: keyProvider,
|
||||
TokenLookup: "query:token",
|
||||
RequiredGroups: []string{"foo"},
|
||||
},
|
||||
setupReq: func(req *http.Request) {
|
||||
q := req.URL.Query()
|
||||
q.Add("token", token)
|
||||
req.URL.RawQuery = q.Encode()
|
||||
},
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "Extract from cookie",
|
||||
config: JWTConfig{
|
||||
KeyProvider: keyProvider,
|
||||
TokenLookup: "cookie:jwt",
|
||||
RequiredGroups: []string{"foo"},
|
||||
},
|
||||
setupReq: func(req *http.Request) {
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: "jwt",
|
||||
Value: token,
|
||||
})
|
||||
},
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Initialize Echo instance
|
||||
e := echo.New()
|
||||
|
||||
// Set up a restricted endpoint with specified config
|
||||
e.GET("/test", func(c echo.Context) error {
|
||||
return c.String(http.StatusOK, "authorized")
|
||||
}, JWTWithConfig(tt.config))
|
||||
|
||||
// Create test request
|
||||
req := httptest.NewRequest(http.MethodGet, "/test", nil)
|
||||
tt.setupReq(req)
|
||||
rec := httptest.NewRecorder()
|
||||
e.ServeHTTP(rec, req)
|
||||
|
||||
// Verify response
|
||||
assert.Equal(t, tt.wantStatus, rec.Code)
|
||||
if tt.wantStatus == http.StatusOK {
|
||||
assert.Equal(t, "authorized", rec.Body.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test the custom error handler
|
||||
func TestCustomErrorHandler(t *testing.T) {
|
||||
// Use local key files
|
||||
privateKeyPath := "./private_key.pem"
|
||||
publicKeyPath := "./public_key.pem"
|
||||
|
||||
// Create LocalKeyProvider for testing
|
||||
keyProvider := NewLocalKeyProvider(privateKeyPath, publicKeyPath)
|
||||
|
||||
// Initialize Echo instance
|
||||
e := echo.New()
|
||||
|
||||
// Custom error handler
|
||||
customErrorHandler := func(c echo.Context, err error) error {
|
||||
return c.JSON(http.StatusUnauthorized, map[string]string{
|
||||
"error": "Custom auth error: " + err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
// Set up a restricted endpoint with custom error handler
|
||||
e.GET("/test", func(c echo.Context) error {
|
||||
return c.String(http.StatusOK, "authorized")
|
||||
}, JWTWithConfig(JWTConfig{
|
||||
KeyProvider: keyProvider,
|
||||
RequiredGroups: []string{"foo"},
|
||||
ErrorHandler: customErrorHandler,
|
||||
}))
|
||||
|
||||
// Create test request with invalid token
|
||||
req := httptest.NewRequest(http.MethodGet, "/test", nil)
|
||||
req.Header.Set("Authorization", "Bearer invalidtoken")
|
||||
rec := httptest.NewRecorder()
|
||||
e.ServeHTTP(rec, req)
|
||||
|
||||
// Verify custom error response
|
||||
assert.Equal(t, http.StatusUnauthorized, rec.Code)
|
||||
assert.Contains(t, rec.Body.String(), "Custom auth error")
|
||||
}
|
||||
|
||||
// This test assumes that you have private_key.pem and public_key.pem
|
||||
// files in the current directory for testing. You need to create these
|
||||
// files before running the tests.
|
||||
Reference in New Issue
Block a user