Skip to content

Commit

Permalink
APIGW: Update HTTPRouteConfigEntry for JWT Auth (#18422)
Browse files Browse the repository at this point in the history
* Updated httproute config entry for JWT Filters

* Added manual deepcopy method for httproute jwt filter

* Fix test

* Update JWTFilter to be in oss file

* Add changelog

* Add build tags for deepcopy oss file
  • Loading branch information
jm96441n authored Aug 10, 2023
1 parent 6981658 commit df11e4e
Show file tree
Hide file tree
Showing 12 changed files with 881 additions and 609 deletions.
3 changes: 3 additions & 0 deletions .changelog/_18422.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:feature
config-entry(api-gateway): (Enterprise only) Add JWTFilter to HTTPRoute Filters
```
3 changes: 3 additions & 0 deletions agent/structs/config_entry_apigw_jwt_oss.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ package structs

// APIGatewayJWTRequirement holds the list of JWT providers to be verified against
type APIGatewayJWTRequirement struct{}

// JWTFilter holds the JWT Filter configuration for an HTTPRoute
type JWTFilter struct{}
1 change: 1 addition & 0 deletions agent/structs/config_entry_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ type HTTPFilters struct {
URLRewrite *URLRewrite
RetryFilter *RetryFilter
TimeoutFilter *TimeoutFilter
JWT *JWTFilter
}

// HTTPHeaderFilter specifies how HTTP headers should be modified.
Expand Down
6 changes: 6 additions & 0 deletions agent/structs/structs.deepcopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,9 @@ func (o *HTTPRouteConfigEntry) DeepCopy() *HTTPRouteConfigEntry {
cp.Rules[i2].Filters.TimeoutFilter = new(TimeoutFilter)
*cp.Rules[i2].Filters.TimeoutFilter = *o.Rules[i2].Filters.TimeoutFilter
}
if o.Rules[i2].Filters.JWT != nil {
cp.Rules[i2].Filters.JWT = o.Rules[i2].Filters.JWT.DeepCopy()
}
if o.Rules[i2].Matches != nil {
cp.Rules[i2].Matches = make([]HTTPMatch, len(o.Rules[i2].Matches))
copy(cp.Rules[i2].Matches, o.Rules[i2].Matches)
Expand Down Expand Up @@ -489,6 +492,9 @@ func (o *HTTPRouteConfigEntry) DeepCopy() *HTTPRouteConfigEntry {
cp.Rules[i2].Services[i4].Filters.TimeoutFilter = new(TimeoutFilter)
*cp.Rules[i2].Services[i4].Filters.TimeoutFilter = *o.Rules[i2].Services[i4].Filters.TimeoutFilter
}
if o.Rules[i2].Services[i4].Filters.JWT != nil {
cp.Rules[i2].Services[i4].Filters.JWT = o.Rules[i2].Services[i4].Filters.JWT.DeepCopy()
}
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions agent/structs/structs.deepcopy_oss.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

//go:build !consulent
// +build !consulent

package structs

// DeepCopy generates a deep copy of *APIGatewayJWTRequirement
func (o *APIGatewayJWTRequirement) DeepCopy() *APIGatewayJWTRequirement {
return new(APIGatewayJWTRequirement)
}

// DeepCopy generates a deep copy of *JWTFilter
func (o *JWTFilter) DeepCopy() *JWTFilter {
return new(JWTFilter)
}
6 changes: 6 additions & 0 deletions api/config_entry_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ type HTTPFilters struct {
URLRewrite *URLRewrite
RetryFilter *RetryFilter
TimeoutFilter *TimeoutFilter
JWT *JWTFilter
}

// HTTPHeaderFilter specifies how HTTP headers should be modified.
Expand All @@ -226,6 +227,11 @@ type TimeoutFilter struct {
IdleTimeout time.Duration
}

// JWTFilter specifies the JWT configuration for a route
type JWTFilter struct {
Providers []*APIGatewayJWTProvider `json:",omitempty"`
}

// HTTPRouteRule specifies the routing rules used to determine what upstream
// service an HTTP request is routed to.
type HTTPRouteRule struct {
Expand Down
134 changes: 134 additions & 0 deletions api/config_entry_routes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package api

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestAPI_ConfigEntries_HTTPRoute(t *testing.T) {
t.Parallel()
c, s := makeClient(t)
defer s.Stop()

configEntries := c.ConfigEntries()
route1 := &HTTPRouteConfigEntry{
Kind: HTTPRoute,
Name: "route1",
}

route2 := &HTTPRouteConfigEntry{
Kind: HTTPRoute,
Name: "route2",
}

// set it
_, wm, err := configEntries.Set(route1, nil)
require.NoError(t, err)
require.NotNil(t, wm)
require.NotEqual(t, 0, wm.RequestTime)

// also set the second one
_, wm, err = configEntries.Set(route2, nil)
require.NoError(t, err)
require.NotNil(t, wm)
require.NotEqual(t, 0, wm.RequestTime)

// get it
entry, qm, err := configEntries.Get(HTTPRoute, "route1", nil)
require.NoError(t, err)
require.NotNil(t, qm)
require.NotEqual(t, 0, qm.RequestTime)

// verify it
readRoute, ok := entry.(*HTTPRouteConfigEntry)
require.True(t, ok)
require.Equal(t, route1.Kind, readRoute.Kind)
require.Equal(t, route1.Name, readRoute.Name)
require.Equal(t, route1.Meta, readRoute.Meta)
require.Equal(t, route1.Meta, readRoute.GetMeta())

// update it
route1.Rules = []HTTPRouteRule{
{
Filters: HTTPFilters{
URLRewrite: &URLRewrite{
Path: "abc",
},
},
},
}

// CAS fail
written, _, err := configEntries.CAS(route1, 0, nil)
require.NoError(t, err)
require.False(t, written)

// CAS success
written, wm, err = configEntries.CAS(route1, readRoute.ModifyIndex, nil)
require.NoError(t, err)
require.NotNil(t, wm)
require.NotEqual(t, 0, wm.RequestTime)
require.True(t, written)

// re-setting should not yield an error
_, wm, err = configEntries.Set(route1, nil)
require.NoError(t, err)
require.NotNil(t, wm)
require.NotEqual(t, 0, wm.RequestTime)

route2.Rules = []HTTPRouteRule{
{
Filters: HTTPFilters{
URLRewrite: &URLRewrite{
Path: "def",
},
},
},
}

_, wm, err = configEntries.Set(route2, nil)
require.NoError(t, err)
require.NotNil(t, wm)
require.NotEqual(t, 0, wm.RequestTime)

// list them
entries, qm, err := configEntries.List(HTTPRoute, nil)
require.NoError(t, err)
require.NotNil(t, qm)
require.NotEqual(t, 0, qm.RequestTime)
require.Len(t, entries, 2)

for _, entry = range entries {
switch entry.GetName() {
case "route1":
// this also verifies that the update value was persisted and
// the updated values are seen
readRoute, ok = entry.(*HTTPRouteConfigEntry)
require.True(t, ok)
require.Equal(t, route1.Kind, readRoute.Kind)
require.Equal(t, route1.Name, readRoute.Name)
require.Len(t, readRoute.Rules, 1)

require.Equal(t, route1.Rules, readRoute.Rules)
case "route2":
readRoute, ok = entry.(*HTTPRouteConfigEntry)
require.True(t, ok)
require.Equal(t, route2.Kind, readRoute.Kind)
require.Equal(t, route2.Name, readRoute.Name)
require.Len(t, readRoute.Rules, 1)

require.Equal(t, route2.Rules, readRoute.Rules)
}
}

// delete it
wm, err = configEntries.Delete(HTTPRoute, "route1", nil)
require.NoError(t, err)
require.NotNil(t, wm)
require.NotEqual(t, 0, wm.RequestTime)

// verify deletion
_, _, err = configEntries.Get(HTTPRoute, "route1", nil)
require.Error(t, err)
}
2 changes: 2 additions & 0 deletions proto/private/pbconfigentry/config_entry.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions proto/private/pbconfigentry/config_entry.pb.binary.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit df11e4e

Please sign in to comment.