-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Michael Freeman
committed
Oct 6, 2024
1 parent
e767500
commit 6d1d287
Showing
6 changed files
with
433 additions
and
96 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,33 @@ | ||
package middleware | ||
|
||
import "github.com/google/uuid" | ||
import ( | ||
"context" | ||
|
||
//go:generate mockgen -destination=mock_custom_context.go -package=middleware github.com/carverauto/eventrunner/pkg/api/middleware CustomContext | ||
customctx "github.com/carverauto/eventrunner/pkg/context" | ||
"github.com/google/uuid" | ||
"gofr.dev/pkg/gofr" | ||
) | ||
|
||
//go:generate mockgen -destination=mock_middleware.go -package=middleware github.com/carverauto/eventrunner/pkg/api/middleware CustomContext,JWTMiddlewareInterface,IDTokenVerifier,Token | ||
|
||
// CustomContext interface for mocking CustomContext. | ||
type CustomContext interface { | ||
GetAPIKey() (string, bool) | ||
FindAPIKey(apiKey string) (uuid.UUID, uuid.UUID, error) | ||
SetClaim(key string, value interface{}) | ||
} | ||
|
||
// JWTMiddlewareInterface interface for mocking JWTMiddleware. | ||
type JWTMiddlewareInterface interface { | ||
Validate(next func(customctx.Context) (interface{}, error)) gofr.Handler | ||
} | ||
|
||
// IDTokenVerifier interface for mocking oidc.IDTokenVerifier. | ||
type IDTokenVerifier interface { | ||
Verify(ctx context.Context, rawToken string) (Token, error) | ||
} | ||
|
||
// Token interface to abstract oidc.IDToken. | ||
type Token interface { | ||
Claims(v interface{}) error | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package middleware | ||
|
||
import ( | ||
"context" | ||
"github.com/coreos/go-oidc/v3/oidc" | ||
) | ||
|
||
// OIDCVerifier is a wrapper around oidc.IDTokenVerifier | ||
type OIDCVerifier struct { | ||
verifier *oidc.IDTokenVerifier | ||
} | ||
|
||
// NewOIDCVerifier returns an instance of OIDCVerifier | ||
func NewOIDCVerifier(verifier *oidc.IDTokenVerifier) *OIDCVerifier { | ||
return &OIDCVerifier{verifier: verifier} | ||
} | ||
|
||
// Verify method to conform to the IDTokenVerifier interface | ||
func (o *OIDCVerifier) Verify(ctx context.Context, rawToken string) (Token, error) { | ||
idToken, err := o.verifier.Verify(ctx, rawToken) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &OIDCToken{idToken: idToken}, nil | ||
} | ||
|
||
// OIDCToken is a wrapper around oidc.IDToken to implement the Token interface | ||
type OIDCToken struct { | ||
idToken *oidc.IDToken | ||
} | ||
|
||
// Claims wraps the oidc.IDToken.Claims method to conform to the Token interface | ||
func (o *OIDCToken) Claims(v interface{}) error { | ||
return o.idToken.Claims(v) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
package middleware | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"net/http" | ||
"testing" | ||
|
||
"github.com/carverauto/eventrunner/pkg/config" | ||
customctx "github.com/carverauto/eventrunner/pkg/context" | ||
"github.com/carverauto/eventrunner/pkg/eventingest" | ||
"github.com/stretchr/testify/assert" | ||
"go.uber.org/mock/gomock" | ||
"gofr.dev/pkg/gofr" | ||
"gofr.dev/pkg/gofr/container" | ||
) | ||
|
||
// MockRequest for use in testing, similar to what you used in another test file. | ||
type MockRequest struct { | ||
ctx context.Context | ||
params map[string][]string // Adjusted to hold slices of strings | ||
body []byte | ||
header http.Header | ||
} | ||
|
||
func (r *MockRequest) Context() context.Context { | ||
return r.ctx | ||
} | ||
|
||
func (r *MockRequest) Param(key string) string { | ||
if vals, ok := r.params[key]; ok && len(vals) > 0 { | ||
return vals[0] | ||
} | ||
|
||
return "" | ||
} | ||
|
||
func (r *MockRequest) Params(key string) []string { | ||
return r.params[key] | ||
} | ||
|
||
func (r *MockRequest) PathParam(key string) string { | ||
if vals, ok := r.params[key]; ok && len(vals) > 0 { | ||
return vals[0] | ||
} | ||
|
||
return "" | ||
} | ||
|
||
func (r *MockRequest) Bind(i interface{}) error { | ||
return json.Unmarshal(r.body, i) | ||
} | ||
|
||
func (r *MockRequest) HostName() string { | ||
return "localhost" | ||
} | ||
|
||
func (r *MockRequest) Header() http.Header { | ||
return r.header | ||
} | ||
|
||
func TestJWTMiddleware_Validate(t *testing.T) { | ||
ctrl := gomock.NewController(t) | ||
defer ctrl.Finish() | ||
|
||
mockVerifier := NewMockIDTokenVerifier(ctrl) | ||
mockToken := NewMockToken(ctrl) | ||
|
||
jwtMiddleware := &JWTMiddleware{ | ||
verifier: mockVerifier, | ||
config: &config.OAuthConfig{}, | ||
} | ||
|
||
tests := []struct { | ||
name string | ||
setupRequest func(*MockRequest) | ||
setupMocks func() | ||
expectedResult interface{} | ||
expectedError error | ||
}{ | ||
{ | ||
name: "Valid Token", | ||
setupRequest: func(req *MockRequest) { | ||
req.ctx = context.WithValue(context.Background(), "Authorization", "Bearer valid_token") | ||
}, | ||
setupMocks: func() { | ||
mockVerifier.EXPECT().Verify(gomock.Any(), "valid_token").Return(mockToken, nil).Times(1) | ||
mockToken.EXPECT().Claims(gomock.Any()).DoAndReturn(func(v interface{}) error { | ||
claims := struct { | ||
TenantID string `json:"tenant_id"` | ||
CustomerID string `json:"customer_id"` | ||
}{ | ||
TenantID: "test-tenant-id", | ||
CustomerID: "test-customer-id", | ||
} | ||
claimsBytes, _ := json.Marshal(claims) | ||
return json.Unmarshal(claimsBytes, v) | ||
}).Times(1) | ||
}, | ||
expectedResult: "success", | ||
expectedError: nil, | ||
}, | ||
{ | ||
name: "Missing Authorization Header", | ||
setupRequest: func(req *MockRequest) {}, // No Authorization set in context | ||
setupMocks: func() {}, | ||
expectedResult: nil, | ||
expectedError: eventingest.NewAuthError("Missing or invalid authorization header"), | ||
}, | ||
{ | ||
name: "Invalid Authorization Header", | ||
setupRequest: func(req *MockRequest) { | ||
req.ctx = context.WithValue(context.Background(), "Authorization", "InvalidHeader") | ||
}, | ||
setupMocks: func() {}, | ||
expectedResult: nil, | ||
expectedError: eventingest.NewAuthError("Invalid authorization header format"), | ||
}, | ||
{ | ||
name: "Invalid Token", | ||
setupRequest: func(req *MockRequest) { | ||
req.ctx = context.WithValue(context.Background(), "Authorization", "Bearer invalid_token") | ||
}, | ||
setupMocks: func() { | ||
mockVerifier.EXPECT().Verify(gomock.Any(), "invalid_token").Return(nil, eventingest.NewAuthError("Invalid token")).Times(1) | ||
}, | ||
expectedResult: nil, | ||
expectedError: eventingest.NewAuthError("Invalid token"), | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
mockRequest := &MockRequest{ | ||
ctx: context.Background(), | ||
params: make(map[string][]string), | ||
header: http.Header{}, | ||
} | ||
tt.setupRequest(mockRequest) | ||
|
||
// Create a new GoFr container | ||
c, _ := container.NewMockContainer(t) | ||
|
||
// Create the GoFr context | ||
gofrCtx := &gofr.Context{ | ||
Context: mockRequest.Context(), | ||
Request: mockRequest, | ||
Container: c, | ||
} | ||
|
||
tt.setupMocks() | ||
|
||
handler := jwtMiddleware.Validate(func(cc customctx.Context) (interface{}, error) { | ||
return "success", nil | ||
}) | ||
|
||
result, err := handler(gofrCtx) | ||
|
||
assert.Equal(t, tt.expectedResult, result) | ||
assert.Equal(t, tt.expectedError, err) | ||
}) | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.