Skip to content

Commit

Permalink
Add V2alpha1 integration tests (#1169)
Browse files Browse the repository at this point in the history
* Add first v2alpha1 integration test feature

* Add first v2alpha1 integration test feature

* Add first jwt v2alpha1 integration test feature

* Add more jwt v2alpha1 integration test feature and make JWT authorization fields optional

* Move services tests to v2alpha1

* Add conversion webhook test

* Fix CORS tests and use v2alpha1 VS processor

* Remove unused init function

* Remove unused functions

* Remove unused file and reuse test-integration-v2alpha1 target

* Update internal/processing/processors/v2alpha1/reconciliation.go

Co-authored-by: Kamil Kasperski <[email protected]>

---------

Co-authored-by: Kamil Kasperski <[email protected]>
  • Loading branch information
Tim Riffer and Ressetkk authored Jul 18, 2024
1 parent 11651c8 commit bfb52be
Show file tree
Hide file tree
Showing 48 changed files with 1,656 additions and 11 deletions.
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,13 @@ test: manifests generate fmt vet envtest ## Generate manifests and run tests.
KUBEBUILDER_CONTROLPLANE_START_TIMEOUT=2m KUBEBUILDER_CONTROLPLANE_STOP_TIMEOUT=2m KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test $(shell go list ./... | grep -v /tests/integration) -coverprofile cover.out

.PHONY: test-integration
test-integration: generate fmt vet envtest ## Run integration tests.
test-integration: generate fmt vet envtest test-integration-v2alpha1 ## Run integration tests.
source ./tests/integration/env_vars.sh && go test -timeout 1h ./tests/integration -v -race -run TestIstioJwt . && go test -timeout 1h ./tests/integration -v -race -run TestOryJwt . && TEST_CONCURRENCY=1 go test -timeout 1h ./tests/integration -v -race -run TestGateway .

.PHONY: test-integration-v2alpha1
test-integration-v2alpha1: generate fmt vet envtest ## Run integration tests.
source ./tests/integration/env_vars.sh && go test -timeout 1h ./tests/integration -v -race -run TestV2alpha1

.PHONY: test-upgrade
test-upgrade: generate fmt vet generate-upgrade-test-manifest install-istio deploy-latest-release ## Run API Gateway upgrade tests.
source ./tests/integration/env_vars.sh && go test -timeout 1h ./tests/integration -v -race -run TestUpgrade .
Expand Down
10 changes: 6 additions & 4 deletions apis/gateway/v2alpha1/apirule_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func init() {
SchemeBuilder.Register(&APIRule{}, &APIRuleList{})
}

// JwtConfig is the configuration for the Istio JWT authentication
// JwtConfig is the configuration for the Istio JWT authentication and authorization.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type JwtConfig struct {
Authentications []*JwtAuthentication `json:"authentications,omitempty"`
Expand All @@ -156,10 +156,12 @@ func (j *JwtConfig) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind
}

// JwtAuthorization contains an array of required scopes
// JwtAuthorization contains scopes and audiences required for the JWT token.
type JwtAuthorization struct {
RequiredScopes []string `json:"requiredScopes"`
Audiences []string `json:"audiences"`
// +optional
RequiredScopes []string `json:"requiredScopes,omitempty"`
// +optional
Audiences []string `json:"audiences,omitempty"`
}

// JwtAuthentication Config for Jwt Istio authentication
Expand Down
7 changes: 2 additions & 5 deletions config/crd/bases/gateway.kyma-project.io_apirules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -412,8 +412,8 @@ spec:
type: array
authorizations:
items:
description: JwtAuthorization contains an array of required
scopes
description: JwtAuthorization contains scopes and audiences
required for the JWT token.
properties:
audiences:
items:
Expand All @@ -423,9 +423,6 @@ spec:
items:
type: string
type: array
required:
- audiences
- requiredScopes
type: object
type: array
type: object
Expand Down
2 changes: 1 addition & 1 deletion internal/processing/processors/v2alpha1/reconciliation.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func (r Reconciliation) GetProcessors() []processing.ReconciliationProcessor {
}

func NewReconciliation(apiRuleV2alpha1 *gatewayv2alpha1.APIRule, apiRuleV1beta1 *gatewayv1beta1.APIRule, validator validation.ApiRuleValidator, config processing.ReconciliationConfig, log *logr.Logger) Reconciliation {
vsProcessor := istio.Newv1beta1VirtualServiceProcessor(config, apiRuleV1beta1)
vsProcessor := NewVirtualServiceProcessor(config, apiRuleV2alpha1)
apProcessor := istio.Newv1beta1AuthorizationPolicyProcessor(config, log, apiRuleV1beta1)
raProcessor := istio.Newv1beta1RequestAuthenticationProcessor(config, apiRuleV1beta1)

Expand Down
16 changes: 16 additions & 0 deletions tests/integration/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package api_gateway

import (
"context"
"github.com/kyma-project/api-gateway/tests/integration/testsuites/v2alpha1"
"log"
"os"
"testing"
Expand Down Expand Up @@ -87,6 +88,21 @@ func TestGateway(t *testing.T) {
runTestsuite(t, ts, config)
}

func TestV2alpha1(t *testing.T) {
config := testcontext.GetConfig()
ts, err := testcontext.New(config, v2alpha1.NewTestsuite)
if err != nil {
t.Fatalf("Failed to create v2alpha1 testsuite %s", err.Error())
}
originalJwtHandler, err := SwitchJwtHandler(ts, "ory")
if err != nil {
log.Print(err.Error())
t.Fatalf("unable to switch to Ory jwtHandler")
}
defer cleanUp(ts, originalJwtHandler)
runTestsuite(t, ts, config)
}

func runTestsuite(t *testing.T, testsuite testcontext.Testsuite, config testcontext.Config) {
opts := createGoDogOpts(t, testsuite.FeaturePath(), config.TestConcurrency)
suite := godog.TestSuite{
Expand Down
23 changes: 23 additions & 0 deletions tests/integration/testsuites/v2alpha1/features/cors.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Feature: CORS

Scenario: No headers are returned when CORS is not specified in APIRule
Given DefaultCORS: There is an httpbin service
And DefaultCORS: The APIRule without CORS set up is applied
Then DefaultCORS: Preflight calling the "/ip" endpoint with header Origin:"localhost" should result in status code 200 and no response header "Access-Control-Allow-Origin"
And DefaultCORS: Preflight calling the "/ip" endpoint with header Origin:"localhost" should result in status code 200 and no response header "Access-Control-Allow-Methods"
And DefaultCORS: Preflight calling the "/ip" endpoint with header Origin:"localhost" should result in status code 200 and no response header "Access-Control-Allow-Headers"
And DefaultCORS: Preflight calling the "/ip" endpoint with header Origin:"localhost" should result in status code 200 and no response header "Access-Control-Expose-Headers"
And DefaultCORS: Preflight calling the "/ip" endpoint with header Origin:"localhost" should result in status code 200 and no response header "Access-Control-Allow-Credentials"
And DefaultCORS: Preflight calling the "/ip" endpoint with header Origin:"localhost" should result in status code 200 and no response header "Access-Control-Max-Age"
And DefaultCORS: Teardown httpbin service

Scenario: CORS is set up to custom values in APIRule
Given CustomCORS: There is an httpbin service
And CustomCORS: The APIRule with following CORS setup is applied AllowOrigins:'["regex": ".*local.kyma.dev"]', AllowMethods:'["GET", "POST"]', AllowHeaders:'["x-custom-allow-headers"]', AllowCredentials:"false", ExposeHeaders:'["x-custom-expose-headers"]', MaxAge:"300"
Then CustomCORS: Preflight calling the "/ip" endpoint with header Origin:"test.local.kyma.dev" should result in status code 200 and response header "Access-Control-Allow-Origin" with value "test.local.kyma.dev"
And CustomCORS: Preflight calling the "/ip" endpoint with header Origin:"localhost" should result in status code 200 and no response header "Access-Control-Allow-Origin"
And CustomCORS: Preflight calling the "/ip" endpoint with header Origin:"a.local.kyma.dev" should result in status code 200 and response header "Access-Control-Allow-Methods" with value "GET,POST"
And CustomCORS: Preflight calling the "/ip" endpoint with header Origin:"b.local.kyma.dev" should result in status code 200 and response header "Access-Control-Allow-Headers" with value "x-custom-allow-headers"
And CustomCORS: Preflight calling the "/ip" endpoint with header Origin:"c.local.kyma.dev" should result in status code 200 and response header "Access-Control-Expose-Headers" with value "x-custom-expose-headers"
And CustomCORS: Preflight calling the "/ip" endpoint with header Origin:"d.local.kyma.dev" should result in status code 200 and response header "Access-Control-Max-Age" with value "300"
Then CustomCORS: Teardown httpbin service
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Feature: Exposing specific methods on paths

Scenario: ExposeMethodsOnPathsNoAuthHandler: Expose GET, POST method for "/anything" and only PUT for "/anything/put" with noAuth
Given ExposeMethodsOnPathsNoAuthHandler: There is a httpbin service
When ExposeMethodsOnPathsNoAuthHandler: The APIRule is applied
Then ExposeMethodsOnPathsNoAuthHandler: Calling the "/anything" endpoint with "GET" method with any token should result in status between 200 and 200
Then ExposeMethodsOnPathsNoAuthHandler: Calling the "/anything" endpoint with "POST" method with any token should result in status between 200 and 200
And ExposeMethodsOnPathsNoAuthHandler: Calling the "/anything" endpoint with "PUT" method with any token should result in status between 404 and 404
And ExposeMethodsOnPathsNoAuthHandler: Calling the "/anything/put" endpoint with "PUT" method with any token should result in status between 200 and 200
And ExposeMethodsOnPathsNoAuthHandler: Calling the "/anything/put" endpoint with "POST" method with any token should result in status between 404 and 404
And ExposeMethodsOnPathsNoAuthHandler: Teardown httpbin service

Scenario: ExposeMethodsOnPathsJwtHandler: Expose GET, POST method for "/anything" and only PUT for "/anything/put" secured by JWT
Given ExposeMethodsOnPathsJwtHandler: There is a httpbin service
When ExposeMethodsOnPathsJwtHandler: The APIRule is applied
Then ExposeMethodsOnPathsJwtHandler: Calling the "/anything" endpoint with "GET" method with a valid "JWT" token should result in status between 200 and 200
Then ExposeMethodsOnPathsJwtHandler: Calling the "/anything" endpoint with "POST" method with a valid "JWT" token should result in status between 200 and 200
And ExposeMethodsOnPathsJwtHandler: Calling the "/anything" endpoint with "PUT" method with a valid "JWT" token should result in status between 404 and 404
And ExposeMethodsOnPathsJwtHandler: Calling the "/anything/put" endpoint with "PUT" method with a valid "JWT" token should result in status between 200 and 200
And ExposeMethodsOnPathsJwtHandler: Calling the "/anything/put" endpoint with "POST" method with a valid "JWT" token should result in status between 404 and 404
And ExposeMethodsOnPathsJwtHandler: Teardown httpbin service
97 changes: 97 additions & 0 deletions tests/integration/testsuites/v2alpha1/features/jwt.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
Feature: Exposing endpoints with JWT

Scenario: Calling a httpbin endpoint secured
Given Common: There is a httpbin service
And Common: There is an endpoint secured with JWT on path "/ip"
When Common: The APIRule is applied
Then Common: Calling the "/ip" endpoint without a token should result in status between 400 and 403
And Common: Calling the "/ip" endpoint with an invalid token should result in status between 400 and 403
And Common: Calling the "/ip" endpoint with a valid "JWT" token should result in status between 200 and 299
And Common: Teardown httpbin service

Scenario: Calling a httpbin endpoint secured on wildcard `/.*` path
Given Regex: There is a httpbin service
And Regex: There is an endpoint secured with JWT on path "/.*"
When Regex: The APIRule is applied
Then Regex: Calling the "/ip" endpoint without a token should result in status between 400 and 403
And Regex: Calling the "/ip" endpoint with an invalid token should result in status between 400 and 403
And Regex: Calling the "/ip" endpoint with a valid "JWT" token should result in status between 200 and 299
Then Regex: Calling the "/headers" endpoint without a token should result in status between 400 and 403
And Regex: Calling the "/headers" endpoint with an invalid token should result in status between 400 and 403
And Regex: Calling the "/headers" endpoint with a valid "JWT" token should result in status between 200 and 299
And Regex: Teardown httpbin service

Scenario: Calling a httpbin endpoint secured on wildcard `/*` path
Given Prefix: There is a httpbin service
And Prefix: There is an endpoint secured with JWT on path "/*"
When Prefix: The APIRule is applied
Then Prefix: Calling the "/ip" endpoint without a token should result in status between 400 and 403
And Prefix: Calling the "/ip" endpoint with an invalid token should result in status between 400 and 403
And Prefix: Calling the "/ip" endpoint with a valid "JWT" token should result in status between 200 and 299
Then Prefix: Calling the "/headers" endpoint without a token should result in status between 400 and 403
And Prefix: Calling the "/headers" endpoint with an invalid token should result in status between 400 and 403
And Prefix: Calling the "/headers" endpoint with a valid "JWT" token should result in status between 200 and 299
And Prefix: Teardown httpbin service

Scenario: Calling httpbin that has an endpoint secured by JWT and unrestricted endpoint
Given JwtAndUnrestricted: There is a httpbin service
And JwtAndUnrestricted: There is an endpoint secured with JWT on path "/ip" and /headers endpoint exposed with noAuth
When JwtAndUnrestricted: The APIRule is applied
Then JwtAndUnrestricted: Calling the "/ip" endpoint with a valid "JWT" token should result in status between 200 and 299
And JwtAndUnrestricted: Calling the "/headers" endpoint without token should result in status between 200 and 299
And JwtAndUnrestricted: Teardown httpbin service

Scenario: Calling a httpbin endpoint secured with JWT that requires scopes claims
Given JwtScopes: There is a httpbin service
And JwtScopes: There is an endpoint secured with JWT on path "/ip" requiring scopes '["read", "write"]'
And JwtScopes: There is an endpoint secured with JWT on path "/get" requiring scopes '["test", "write"]'
And JwtScopes: There is an endpoint secured with JWT on path "/headers" requiring scopes '["read"]'
When JwtScopes: The APIRule is applied
Then JwtScopes: Calling the "/ip" endpoint with a valid "JWT" token with "scopes" "read" and "write" should result in status between 200 and 299
And JwtScopes: Calling the "/get" endpoint with a valid "JWT" token with "scopes" "read" and "write" should result in status between 400 and 403
And JwtScopes: Calling the "/headers" endpoint with a valid "JWT" token with "scopes" "read" and "write" should result in status between 200 and 299
And JwtScopes: Teardown httpbin service

Scenario: Calling a httpbin endpoint secured with JWT that requires aud claim
Given JwtAudiences: There is a httpbin service
And JwtAudiences: There is an endpoint secured with JWT on path "/get" requiring audiences '["https://example.com"]'
And JwtAudiences: There is an endpoint secured with JWT on path "/ip" requiring audiences '["https://example.com", "https://example.com/user"]'
And JwtAudiences: There is an endpoint secured with JWT on path "/cache" requiring audience '["https://example.com"]' or '["audienceNotInJWT"]'
And JwtAudiences: There is an endpoint secured with JWT on path "/headers" requiring audiences '["https://example.com", "https://example.com/admin"]'
When JwtAudiences: The APIRule is applied
Then JwtAudiences: Calling the "/get" endpoint with a valid "JWT" token with "audiences" "https://example.com" and "https://example.com/user" should result in status between 200 and 299
And JwtAudiences: Calling the "/ip" endpoint with a valid "JWT" token with "audiences" "https://example.com" and "https://example.com/user" should result in status between 200 and 299
And JwtAudiences: Calling the "/cache" endpoint with a valid "JWT" token with "audiences" "https://example.com" and "https://example.com/user" should result in status between 200 and 299
And JwtAudiences: Calling the "/headers" endpoint with a valid "JWT" token with "audiences" "https://example.com" and "https://example.com/user" should result in status between 400 and 403
And JwtAudiences: Teardown httpbin service

Scenario: Exposing a JWT secured endpoint with unavailable issuer and jwks URL
Given JwtUnavailableIssuer: There is a httpbin service
Given JwtUnavailableIssuer: There is an endpoint secured with JWT on path "/ip" with invalid issuer and jwks
When JwtUnavailableIssuer: The APIRule is applied
And JwtUnavailableIssuer: Calling the "/ip" endpoint with a valid "JWT" token should result in body containing "Jwt issuer is not configured"
And JwtUnavailableIssuer: Teardown httpbin service

Scenario: Exposing a JWT secured endpoint where issuer URL doesn't belong to jwks URL
Given JwtIssuerJwksNotMatch: There is a httpbin service
And JwtIssuerJwksNotMatch: There is an endpoint secured with JWT on path "/ip" with invalid issuer and jwks
When JwtIssuerJwksNotMatch: The APIRule is applied
And JwtIssuerJwksNotMatch: Calling the "/ip" endpoint with a valid "JWT" token should result in body containing "Jwt verification fails"
And JwtIssuerJwksNotMatch: Teardown httpbin service

Scenario: Exposing an endpoint secured with different JWT token from headers
Given JwtTokenFromHeader: There is a httpbin service
When JwtTokenFromHeader: The APIRule is applied
Then JwtTokenFromHeader: Calling the "/headers" endpoint without a token should result in status between 400 and 403
And JwtTokenFromHeader: Calling the "/headers" endpoint with a valid "JWT" token from default header should result in status between 400 and 403
And JwtTokenFromHeader: Calling the "/headers" endpoint with a valid "JWT" token from header "x-jwt-token" and prefix "JwtToken" should result in status between 200 and 299
And JwtTokenFromHeader: Teardown httpbin service

Scenario: Calling a httpbin endpoint secured with different JWT token from params
Given JwtTokenFromParam: There is a httpbin service
When JwtTokenFromParam: The APIRule is applied
And JwtTokenFromParam: Calling the "/ip" endpoint without a token should result in status between 400 and 403
And JwtTokenFromParam: Calling the "/ip" endpoint with a valid "JWT" token from default header should result in status between 400 and 403
And JwtTokenFromParam: Calling the "/ip" endpoint with a valid "JWT" token from parameter "jwt_token" should result in status between 200 and 299
And JwtTokenFromParam: Teardown httpbin service

Loading

0 comments on commit bfb52be

Please sign in to comment.