From 814ccc5a32c4cba4e058852d4083b1bc675668ed Mon Sep 17 00:00:00 2001 From: Ignasi Barrera Date: Mon, 12 Feb 2024 13:55:48 +0100 Subject: [PATCH] Added in-memory session store --- internal/authz/handler.go | 27 +++++ internal/authz/mock.go | 57 +++++++++++ internal/authz/mock_test.go | 51 ++++++++++ internal/authz/oidc.go | 50 +++++++++ internal/authz/oidc/memory.go | 156 +++++++++++++++++++++++++++++ internal/authz/oidc/memory_test.go | 115 +++++++++++++++++++++ internal/authz/oidc/session.go | 26 +++++ internal/authz/oidc/state.go | 22 ++++ internal/authz/oidc/time.go | 31 ++++++ internal/authz/oidc/time_test.go | 37 +++++++ internal/authz/oidc/token.go | 25 +++++ internal/logging.go | 2 + internal/server/authz.go | 59 +++++------ internal/server/authz_test.go | 27 ----- 14 files changed, 630 insertions(+), 55 deletions(-) create mode 100644 internal/authz/handler.go create mode 100644 internal/authz/mock.go create mode 100644 internal/authz/mock_test.go create mode 100644 internal/authz/oidc.go create mode 100644 internal/authz/oidc/memory.go create mode 100644 internal/authz/oidc/memory_test.go create mode 100644 internal/authz/oidc/session.go create mode 100644 internal/authz/oidc/state.go create mode 100644 internal/authz/oidc/time.go create mode 100644 internal/authz/oidc/time_test.go create mode 100644 internal/authz/oidc/token.go diff --git a/internal/authz/handler.go b/internal/authz/handler.go new file mode 100644 index 0000000..e86cc3f --- /dev/null +++ b/internal/authz/handler.go @@ -0,0 +1,27 @@ +// Copyright 2024 Tetrate +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package authz + +import ( + "context" + + envoy "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" +) + +// Authz is an interface for handling authorization requests. +type Authz interface { + // Process a CheckRequest and populate a CheckResponse. + Process(ctx context.Context, req *envoy.CheckRequest, resp *envoy.CheckResponse) error +} diff --git a/internal/authz/mock.go b/internal/authz/mock.go new file mode 100644 index 0000000..7bb0a96 --- /dev/null +++ b/internal/authz/mock.go @@ -0,0 +1,57 @@ +// Copyright 2024 Tetrate +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package authz + +import ( + "context" + + envoy "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" + "github.com/tetratelabs/telemetry" + "google.golang.org/genproto/googleapis/rpc/status" + "google.golang.org/grpc/codes" + + mockv1 "github.com/tetrateio/authservice-go/config/gen/go/v1/mock" + "github.com/tetrateio/authservice-go/internal" +) + +var _ Authz = (*mockHandler)(nil) + +// mockHandler handler is an implementation of the Authz interface. +type mockHandler struct { + log telemetry.Logger + config *mockv1.MockConfig +} + +// NewMockHandler creates a new Mock implementation of the Authz interface. +func NewMockHandler(cfg *mockv1.MockConfig) Authz { + return &mockHandler{ + log: internal.Logger(internal.Authz).With("type", "mockHandler"), + config: cfg, + } +} + +// Process a CheckRequest and populate a CheckResponse according to the mockHandler configuration. +func (m *mockHandler) Process(ctx context.Context, _ *envoy.CheckRequest, resp *envoy.CheckResponse) error { + log := m.log.Context(ctx) + + code := codes.PermissionDenied + if m.config.GetAllow() { + code = codes.OK + } + + log.Debug("process", "status", code.String()) + resp.Status = &status.Status{Code: int32(code)} + return nil +} diff --git a/internal/authz/mock_test.go b/internal/authz/mock_test.go new file mode 100644 index 0000000..7ccbb4c --- /dev/null +++ b/internal/authz/mock_test.go @@ -0,0 +1,51 @@ +// Copyright 2024 Tetrate +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package authz + +import ( + "context" + "testing" + + envoy "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" + "github.com/stretchr/testify/require" + "github.com/tetratelabs/telemetry" + "google.golang.org/grpc/codes" + + mockv1 "github.com/tetrateio/authservice-go/config/gen/go/v1/mock" +) + +func TestProcessMock(t *testing.T) { + tests := []struct { + name string + allow bool + want codes.Code + }{ + {"allow", true, codes.OK}, + {"deny", false, codes.PermissionDenied}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var ( + m = &mockHandler{log: telemetry.NoopLogger(), config: &mockv1.MockConfig{Allow: tt.allow}} + req = &envoy.CheckRequest{} + resp = &envoy.CheckResponse{} + ) + err := m.Process(context.Background(), req, resp) + require.NoError(t, err) + require.Equal(t, int32(tt.want), resp.Status.Code) + }) + } +} diff --git a/internal/authz/oidc.go b/internal/authz/oidc.go new file mode 100644 index 0000000..408f790 --- /dev/null +++ b/internal/authz/oidc.go @@ -0,0 +1,50 @@ +// Copyright 2024 Tetrate +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package authz + +import ( + "context" + + envoy "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" + "github.com/tetratelabs/telemetry" + + oidcv1 "github.com/tetrateio/authservice-go/config/gen/go/v1/oidc" + "github.com/tetrateio/authservice-go/internal" + "github.com/tetrateio/authservice-go/internal/authz/oidc" +) + +var _ Authz = (*oidcHandler)(nil) + +// oidc handler is a mockHandler implementation of the Authz interface that implements +// the OpenID connect protocol. +type oidcHandler struct { + log telemetry.Logger + config *oidcv1.OIDCConfig + store oidc.SessionStore +} + +// NewOIDCHandler creates a new OIDC implementation of the Authz interface. +func NewOIDCHandler(cfg *oidcv1.OIDCConfig, store oidc.SessionStore) Authz { + return &oidcHandler{ + log: internal.Logger(internal.Authz).With("type", "oidc"), + config: cfg, + store: store, + } +} + +// Process a CheckRequest and populate a CheckResponse according to the mockHandler configuration. +func (o *oidcHandler) Process(_ context.Context, _ *envoy.CheckRequest, _ *envoy.CheckResponse) error { + return nil +} diff --git a/internal/authz/oidc/memory.go b/internal/authz/oidc/memory.go new file mode 100644 index 0000000..2470356 --- /dev/null +++ b/internal/authz/oidc/memory.go @@ -0,0 +1,156 @@ +// Copyright 2024 Tetrate +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oidc + +import ( + "sync" + "time" + + "github.com/tetratelabs/telemetry" + + "github.com/tetrateio/authservice-go/internal" +) + +var _ SessionStore = (*memoryStore)(nil) + +// memoryStore is an in-memory implementation of the SessionStore interface. +type memoryStore struct { + log telemetry.Logger + clock Clock + absoluteSessionTimeout time.Duration + idleSessionTimeout time.Duration + + mu sync.Mutex + sessions map[string]*session +} + +// NewMemoryStore creates a new in-memory session store. +func NewMemoryStore(clock Clock, absoluteSessionTimeout, idleSessionTimeout time.Duration) SessionStore { + return &memoryStore{ + log: internal.Logger(internal.Session).With("type", "memory"), + clock: clock, + absoluteSessionTimeout: absoluteSessionTimeout, + idleSessionTimeout: idleSessionTimeout, + sessions: make(map[string]*session), + } +} + +func (m *memoryStore) SetTokenResponse(sessionID string, tokenResponse *TokenResponse) { + m.set(sessionID, func(s *session) { + s.tokenResponse = tokenResponse + }) +} + +func (m *memoryStore) GetTokenResponse(sessionID string) *TokenResponse { + m.mu.Lock() + defer m.mu.Unlock() + + s := m.sessions[sessionID] + if s == nil { + return nil + } + + s.accessed = m.clock.Now() + return s.tokenResponse +} + +func (m *memoryStore) SetAuthorizationState(sessionID string, authorizationState *AuthorizationState) { + m.set(sessionID, func(s *session) { + s.authorizationState = authorizationState + }) +} + +func (m *memoryStore) GetAuthorizationState(sessionID string) *AuthorizationState { + m.mu.Lock() + defer m.mu.Unlock() + + s := m.sessions[sessionID] + if s == nil { + return nil + } + + s.accessed = m.clock.Now() + return s.authorizationState +} + +func (m *memoryStore) ClearAuthorizationState(sessionID string) { + m.mu.Lock() + defer m.mu.Unlock() + + if s := m.sessions[sessionID]; s != nil { + s.accessed = m.clock.Now() + s.authorizationState = nil + } +} + +func (m *memoryStore) RemoveSession(sessionID string) { + m.mu.Lock() + defer m.mu.Unlock() + + delete(m.sessions, sessionID) +} + +func (m *memoryStore) RemoveAllExpired() { + var ( + earliestTimeAddedToKeep = m.clock.Now().Add(-m.absoluteSessionTimeout) + earliestTimeIdleToKeep = m.clock.Now().Add(-m.idleSessionTimeout) + shouldCheckAbsoluteTimeout = m.absoluteSessionTimeout > 0 + shouldCheckIdleTimeout = m.idleSessionTimeout > 0 + ) + + m.mu.Lock() + defer m.mu.Unlock() + + for sessionID, s := range m.sessions { + expiredBasedOnTimeAdded := shouldCheckAbsoluteTimeout && s.added.Before(earliestTimeAddedToKeep) + expiredBasedOnIdleTime := shouldCheckIdleTimeout && s.accessed.Before(earliestTimeIdleToKeep) + + if expiredBasedOnTimeAdded || expiredBasedOnIdleTime { + delete(m.sessions, sessionID) + } + } +} + +// set the given session with the given setter function and record the access time. +func (m *memoryStore) set(sessionID string, setter func(s *session)) { + m.mu.Lock() + defer m.mu.Unlock() + + s := m.sessions[sessionID] + if s != nil { + s.accessed = m.clock.Now() + setter(s) + } else { + s = newSession(m.clock.Now()) + setter(s) + m.sessions[sessionID] = s + } +} + +// session holds the data of a session stored int he in-memory cache +type session struct { + tokenResponse *TokenResponse + authorizationState *AuthorizationState + added time.Time + accessed time.Time +} + +// newSession creates a new session with the given creation time. +func newSession(t time.Time) *session { + return &session{ + added: t, + accessed: t, + } +} diff --git a/internal/authz/oidc/memory_test.go b/internal/authz/oidc/memory_test.go new file mode 100644 index 0000000..29c4b1a --- /dev/null +++ b/internal/authz/oidc/memory_test.go @@ -0,0 +1,115 @@ +// Copyright 2024 Tetrate +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oidc + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestTokenResponse(t *testing.T) { + m := NewMemoryStore(Clock{}, 0, 0).(*memoryStore) + + require.Nil(t, m.GetTokenResponse("s1")) + + // Create a session and verify it's added and accessed time + tr := &TokenResponse{} + m.SetTokenResponse("s1", &TokenResponse{}) + require.Greater(t, m.sessions["s1"].added.Unix(), int64(0)) + require.Equal(t, m.sessions["s1"].added, m.sessions["s1"].accessed) + + // Verify that the right token response is returned and the accessed time is updated + require.Equal(t, tr, m.GetTokenResponse("s1")) + require.True(t, m.sessions["s1"].accessed.After(m.sessions["s1"].added)) + lastAccessed := m.sessions["s1"].accessed + + // Verify that updating the token response also updates the session access timestamp + m.SetTokenResponse("s1", &TokenResponse{}) + require.True(t, m.sessions["s1"].accessed.After(lastAccessed)) +} + +func TestAuthorizationState(t *testing.T) { + m := NewMemoryStore(Clock{}, 0, 0).(*memoryStore) + + as := m.GetAuthorizationState("s1") + require.Nil(t, as) + + // Create a session and verify it's added and accessed time + as = &AuthorizationState{} + m.SetAuthorizationState("s1", as) + require.Greater(t, m.sessions["s1"].added.Unix(), int64(0)) + require.Equal(t, m.sessions["s1"].added, m.sessions["s1"].accessed) + + // Verify that the right state is returned and the accessed time is updated + require.Equal(t, as, m.GetAuthorizationState("s1")) + lastAccessed := m.sessions["s1"].accessed + require.True(t, lastAccessed.After(m.sessions["s1"].added)) + + // Verify that updating the authz state also updates the session access timestamp + m.SetAuthorizationState("s1", &AuthorizationState{}) + require.True(t, m.sessions["s1"].accessed.After(lastAccessed)) + + // Verify that clearing the authz state also updates the session access timestamp + m.ClearAuthorizationState("s1") + require.Nil(t, m.GetAuthorizationState("s1")) + require.True(t, m.sessions["s1"].accessed.After(lastAccessed)) +} + +func TestRemoveResponse(t *testing.T) { + m := NewMemoryStore(Clock{}, 0, 0).(*memoryStore) + + m.SetTokenResponse("s1", &TokenResponse{}) + require.NotNil(t, m.sessions["s1"]) + + m.RemoveSession("s1") + require.Nil(t, m.sessions["s1"]) +} + +func TestRemoveAllExpired(t *testing.T) { + m := NewMemoryStore(Clock{}, 0, 0).(*memoryStore) + + m.SetTokenResponse("s1", &TokenResponse{}) + m.SetTokenResponse("s2", &TokenResponse{}) + m.SetTokenResponse("abs-expired", &TokenResponse{}) + m.SetTokenResponse("idle-expired", &TokenResponse{}) + + m.sessions["abs-expired"].added = time.Now().Add(-time.Hour) + m.sessions["idle-expired"].accessed = time.Now().Add(-time.Hour) + + t.Run("no-expiration", func(t *testing.T) { + m.RemoveAllExpired() + + require.Len(t, m.sessions, 4) + require.NotNil(t, m.sessions["s1"]) + require.NotNil(t, m.sessions["s2"]) + require.NotNil(t, m.sessions["abs-expired"]) + require.NotNil(t, m.sessions["idle-expired"]) + }) + + t.Run("expiration", func(t *testing.T) { + m.absoluteSessionTimeout = time.Minute + m.idleSessionTimeout = time.Minute + m.RemoveAllExpired() + + require.Len(t, m.sessions, 2) + require.NotNil(t, m.sessions["s1"]) + require.NotNil(t, m.sessions["s2"]) + require.Nil(t, m.sessions["abs-expired"]) + require.Nil(t, m.sessions["idle-expired"]) + + }) +} diff --git a/internal/authz/oidc/session.go b/internal/authz/oidc/session.go new file mode 100644 index 0000000..9c0cba7 --- /dev/null +++ b/internal/authz/oidc/session.go @@ -0,0 +1,26 @@ +// Copyright 2024 Tetrate +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oidc + +// SessionStore is an interface for storing session data. +type SessionStore interface { + SetTokenResponse(sessionID string, tokenResponse *TokenResponse) + GetTokenResponse(sessionID string) *TokenResponse + SetAuthorizationState(sessionID string, authorizationState *AuthorizationState) + GetAuthorizationState(sessionID string) *AuthorizationState + ClearAuthorizationState(sessionID string) + RemoveSession(sessionID string) + RemoveAllExpired() +} diff --git a/internal/authz/oidc/state.go b/internal/authz/oidc/state.go new file mode 100644 index 0000000..96236e7 --- /dev/null +++ b/internal/authz/oidc/state.go @@ -0,0 +1,22 @@ +// Copyright 2024 Tetrate +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oidc + +// AuthorizationState contains information about the state of the authorization process. +type AuthorizationState struct { + State string + Nonce string + RequestedURL string +} diff --git a/internal/authz/oidc/time.go b/internal/authz/oidc/time.go new file mode 100644 index 0000000..1de4102 --- /dev/null +++ b/internal/authz/oidc/time.go @@ -0,0 +1,31 @@ +// Copyright 2024 Tetrate +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oidc + +import "time" + +// Clock represents a source of current time. +type Clock struct { + // Override for time.Now. + NowFn func() time.Time +} + +// Now returns the current local time. +func (s *Clock) Now() time.Time { + if s.NowFn != nil { + return s.NowFn() + } + return time.Now() +} diff --git a/internal/authz/oidc/time_test.go b/internal/authz/oidc/time_test.go new file mode 100644 index 0000000..2ec18e8 --- /dev/null +++ b/internal/authz/oidc/time_test.go @@ -0,0 +1,37 @@ +// Copyright 2024 Tetrate +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oidc + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestClockReal(t *testing.T) { + c := Clock{} + require.Greater(t, c.Now().Unix(), int64(0)) +} + +func TestClockCustom(t *testing.T) { + instant := time.Date(2020, time.January, 2, 3, 4, 5, 6, time.UTC) + c := Clock{ + NowFn: func() time.Time { + return instant + }, + } + require.Equal(t, instant, c.Now()) +} diff --git a/internal/authz/oidc/token.go b/internal/authz/oidc/token.go new file mode 100644 index 0000000..858ccf1 --- /dev/null +++ b/internal/authz/oidc/token.go @@ -0,0 +1,25 @@ +// Copyright 2024 Tetrate +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package oidc + +import "time" + +// TokenResponse contains information about the tokens returned by the Identity Provider. +type TokenResponse struct { + IDToken string + AccessToken string + RefreshToken string + AccessTokenExpiry time.Duration +} diff --git a/internal/logging.go b/internal/logging.go index 7612309..9aba02f 100644 --- a/internal/logging.go +++ b/internal/logging.go @@ -31,6 +31,7 @@ const ( Default = "default" Requests = "requests" Server = "server" + Session = "session" ) // scopes contains the list of all logging scopes @@ -39,6 +40,7 @@ var scopes = map[string]string{ Default: "Default", Requests: "Logs all requests and responses received by the server", Server: "Server request handling messages", + Session: "Session store messages", } // ErrInvalidLogLevel is returned when the configured log level is invalid. diff --git a/internal/server/authz.go b/internal/server/authz.go index 08985c5..dfcf741 100644 --- a/internal/server/authz.go +++ b/internal/server/authz.go @@ -18,6 +18,7 @@ import ( "context" "fmt" "strings" + "time" envoy "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" "github.com/tetratelabs/telemetry" @@ -26,9 +27,9 @@ import ( "google.golang.org/grpc/codes" configv1 "github.com/tetrateio/authservice-go/config/gen/go/v1" - mockv1 "github.com/tetrateio/authservice-go/config/gen/go/v1/mock" - oidcv1 "github.com/tetrateio/authservice-go/config/gen/go/v1/oidc" "github.com/tetrateio/authservice-go/internal" + "github.com/tetrateio/authservice-go/internal/authz" + "github.com/tetrateio/authservice-go/internal/authz/oidc" ) var ( @@ -85,32 +86,45 @@ func (e *ExtAuthZFilter) Check(ctx context.Context, req *envoy.CheckRequest) (re // Inside a filter chain, all filters must match for i, f := range c.Filters { var ( - ok bool - err error + h authz.Authz + resp = &envoy.CheckResponse{} ) + log.Debug("applying filter", "type", fmt.Sprintf("%T", f.Type), "index", i) + switch ft := f.Type.(type) { case *configv1.Filter_Mock: - e.log.Debug("applying filter", "type", "mock", "index", i) - ok, err = e.checkMock(context.Background(), req, ft.Mock) + h = authz.NewMockHandler(ft.Mock) case *configv1.Filter_Oidc: - e.log.Debug("applying filter", "type", "oidc", "index", i) - ok, err = e.checkOidc(context.Background(), req, ft.Oidc) + // TODO(nacx): Read the redis store config to configure the redi store + store := oidc.NewMemoryStore( + oidc.Clock{}, + time.Duration(ft.Oidc.AbsoluteSessionTimeout), + time.Duration(ft.Oidc.IdleSessionTimeout), + ) + // TODO(nacx): Check if the Oidc setting is enough or we have to pull the default Oidc settings + h = authz.NewOIDCHandler(ft.Oidc, store) case *configv1.Filter_OidcOverride: - e.log.Debug("applying filter", "type", "oidc_override", "index", i) - ok, err = e.checkOidc(context.Background(), req, ft.OidcOverride) + // TODO(nacx): Read the redis store config to configure the redi store + store := oidc.NewMemoryStore( + oidc.Clock{}, + time.Duration(ft.OidcOverride.AbsoluteSessionTimeout), + time.Duration(ft.OidcOverride.IdleSessionTimeout), + ) + // TODO(nacx): Check if the OidcOverride is enough or we have to pull the default Oidc settings + h = authz.NewOIDCHandler(ft.OidcOverride, store) } - // If there is an error just return it without a verdict, and let the Envoy ext_authz - // failure policy decide if the request can continue or not. - if err != nil { + if err = h.Process(ctx, req, resp); err != nil { + // If there is an error just return it without a verdict, and let the Envoy ext_authz + // failure policy decide if the request can continue or not. return nil, err } - log.Debug("filter evaluation", "index", i, "result", ok, "error", err) - - if !ok { - return deny(codes.PermissionDenied, fmt.Sprintf("%s[%d] filter denied the request", c.Name, i)), nil + allow := codes.Code(resp.Status.Code) == codes.OK + log.Debug("filter result", "index", i, "allow", allow, "error", err) + if !allow { + return resp, nil } } @@ -125,17 +139,6 @@ func (e *ExtAuthZFilter) Check(ctx context.Context, req *envoy.CheckRequest) (re return deny(codes.PermissionDenied, "no chains matched"), nil } -// checkMock checks the given request against the given mock configuration. -func (e *ExtAuthZFilter) checkMock(_ context.Context, _ *envoy.CheckRequest, mock *mockv1.MockConfig) (bool, error) { - return mock.Allow, nil -} - -// checkOidc checks the given request against the given oidc configuration. -func (e *ExtAuthZFilter) checkOidc(_ context.Context, _ *envoy.CheckRequest, _ *oidcv1.OIDCConfig) (bool, error) { - // TODO - return false, nil -} - // matches returns true if the given request matches the given match configuration func matches(m *configv1.Match, req *envoy.CheckRequest) bool { if m == nil { diff --git a/internal/server/authz_test.go b/internal/server/authz_test.go index 39a3e41..c6f764a 100644 --- a/internal/server/authz_test.go +++ b/internal/server/authz_test.go @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Copyright (c) Tetrate, Inc 2024 All Rights Reserved. - package server import ( @@ -99,31 +97,6 @@ func TestUseFirstMatchingChain(t *testing.T) { require.Equal(t, int32(codes.OK), got.Status.Code) } -func TestCheckMock(t *testing.T) { - tests := []struct { - name string - allow bool - want bool - }{ - {"allow", true, true}, - {"deny", false, false}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - e := &ExtAuthZFilter{} - got, err := e.checkMock( - context.Background(), - &envoy.CheckRequest{}, - &mockv1.MockConfig{Allow: tt.allow}, - ) - require.NoError(t, err) - require.Equal(t, tt.want, got) - }) - } - -} - func TestMatch(t *testing.T) { tests := []struct { name string