mirror of https://github.com/status-im/consul.git
APIGW: Update HTTPRouteConfigEntry for JWT Auth (#18422)
* 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
This commit is contained in:
parent
6981658585
commit
df11e4e7b4
|
@ -0,0 +1,3 @@
|
|||
```release-note:feature
|
||||
config-entry(api-gateway): (Enterprise only) Add JWTFilter to HTTPRoute Filters
|
||||
```
|
|
@ -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{}
|
||||
|
|
|
@ -422,6 +422,7 @@ type HTTPFilters struct {
|
|||
URLRewrite *URLRewrite
|
||||
RetryFilter *RetryFilter
|
||||
TimeoutFilter *TimeoutFilter
|
||||
JWT *JWTFilter
|
||||
}
|
||||
|
||||
// HTTPHeaderFilter specifies how HTTP headers should be modified.
|
||||
|
|
|
@ -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)
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -201,6 +201,7 @@ type HTTPFilters struct {
|
|||
URLRewrite *URLRewrite
|
||||
RetryFilter *RetryFilter
|
||||
TimeoutFilter *TimeoutFilter
|
||||
JWT *JWTFilter
|
||||
}
|
||||
|
||||
// HTTPHeaderFilter specifies how HTTP headers should be modified.
|
||||
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -411,6 +411,7 @@ func HTTPFiltersToStructs(s *HTTPFilters, t *structs.HTTPFilters) {
|
|||
TimeoutFilterToStructs(s.TimeoutFilter, &x)
|
||||
t.TimeoutFilter = &x
|
||||
}
|
||||
t.JWT = routeJWTFilterToStructs(s.JWT)
|
||||
}
|
||||
func HTTPFiltersFromStructs(t *structs.HTTPFilters, s *HTTPFilters) {
|
||||
if s == nil {
|
||||
|
@ -441,6 +442,7 @@ func HTTPFiltersFromStructs(t *structs.HTTPFilters, s *HTTPFilters) {
|
|||
TimeoutFilterFromStructs(t.TimeoutFilter, &x)
|
||||
s.TimeoutFilter = &x
|
||||
}
|
||||
s.JWT = routeJWTFilterFromStructs(t.JWT)
|
||||
}
|
||||
func HTTPHeaderFilterToStructs(s *HTTPHeaderFilter, t *structs.HTTPHeaderFilter) {
|
||||
if s == nil {
|
||||
|
|
|
@ -687,6 +687,16 @@ func (msg *TimeoutFilter) UnmarshalBinary(b []byte) error {
|
|||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *JWTFilter) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||
func (msg *JWTFilter) UnmarshalBinary(b []byte) error {
|
||||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *HTTPHeaderFilter) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -912,6 +912,8 @@ message HTTPFilters {
|
|||
URLRewrite URLRewrite = 2;
|
||||
RetryFilter RetryFilter = 3;
|
||||
TimeoutFilter TimeoutFilter = 4;
|
||||
// mog: func-to=routeJWTFilterToStructs func-from=routeJWTFilterFromStructs
|
||||
JWTFilter JWT = 5;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
|
@ -947,6 +949,10 @@ message TimeoutFilter {
|
|||
google.protobuf.Duration IdleTimeout = 2;
|
||||
}
|
||||
|
||||
message JWTFilter {
|
||||
repeated APIGatewayJWTProvider Providers = 1;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
//
|
||||
// target=github.com/hashicorp/consul/agent/structs.HTTPHeaderFilter
|
||||
|
|
|
@ -15,3 +15,11 @@ func gwJWTRequirementToStructs(m *APIGatewayJWTRequirement) *structs.APIGatewayJ
|
|||
func gwJWTRequirementFromStructs(*structs.APIGatewayJWTRequirement) *APIGatewayJWTRequirement {
|
||||
return &APIGatewayJWTRequirement{}
|
||||
}
|
||||
|
||||
func routeJWTFilterToStructs(m *JWTFilter) *structs.JWTFilter {
|
||||
return &structs.JWTFilter{}
|
||||
}
|
||||
|
||||
func routeJWTFilterFromStructs(*structs.JWTFilter) *JWTFilter {
|
||||
return &JWTFilter{}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue