From 00e0aa6b8adf1eab0b821aa26ec2f97d51d15dd8 Mon Sep 17 00:00:00 2001 From: mo khan Date: Fri, 11 Jul 2025 15:58:16 -0600 Subject: feat: add a composite service to provide fallback mechanisms --- pkg/authz/composite_check_service.go | 34 +++++++++++++++++++ pkg/authz/composite_check_service_test.go | 56 +++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 pkg/authz/composite_check_service.go create mode 100644 pkg/authz/composite_check_service_test.go diff --git a/pkg/authz/composite_check_service.go b/pkg/authz/composite_check_service.go new file mode 100644 index 0000000..5dfdfa8 --- /dev/null +++ b/pkg/authz/composite_check_service.go @@ -0,0 +1,34 @@ +package authz + +import ( + "context" + "errors" + + auth "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" + "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/pls" +) + +type CompositeCheckService struct { + services []auth.AuthorizationServer + auth.UnimplementedAuthorizationServer +} + +func NewCheckService(services []auth.AuthorizationServer) auth.AuthorizationServer { + return &CompositeCheckService{ + services: services, + } +} + +func (svc *CompositeCheckService) Check(ctx context.Context, request *auth.CheckRequest) (*auth.CheckResponse, error) { + for _, client := range svc.services { + response, err := client.Check(ctx, request) + if err != nil { + pls.LogError(ctx, err) + continue + } + + return response, err + } + + return nil, errors.New("Unable to perform check") +} diff --git a/pkg/authz/composite_check_service_test.go b/pkg/authz/composite_check_service_test.go new file mode 100644 index 0000000..dc0286c --- /dev/null +++ b/pkg/authz/composite_check_service_test.go @@ -0,0 +1,56 @@ +package authz + +import ( + "testing" + + auth "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" +) + +func TestCompositeCheckService(t *testing.T) { + t.Run("without any services", func(t *testing.T) { + svc := NewCheckService([]auth.AuthorizationServer{}) + + response, err := svc.Check(t.Context(), &auth.CheckRequest{}) + + require.Error(t, err) + require.Nil(t, response) + }) + + t.Run("with a single service", func(t *testing.T) { + svc := NewCheckService([]auth.AuthorizationServer{ + NewLocalCheckService(), + }) + + response, err := svc.Check(t.Context(), &auth.CheckRequest{}) + + require.NoError(t, err) + assert.Equal(t, int32(codes.PermissionDenied), response.Status.Code) + }) + + t.Run("with a multiple services", func(t *testing.T) { + svc := NewCheckService([]auth.AuthorizationServer{ + NewRemoteCheckService(nil), + NewLocalCheckService(), + }) + + response, err := svc.Check(t.Context(), &auth.CheckRequest{}) + + require.NoError(t, err) + assert.Equal(t, int32(codes.PermissionDenied), response.Status.Code) + }) + + t.Run("with a multiple failing services", func(t *testing.T) { + svc := NewCheckService([]auth.AuthorizationServer{ + NewRemoteCheckService(nil), + NewRemoteCheckService(nil), + }) + + response, err := svc.Check(t.Context(), &auth.CheckRequest{}) + + require.Error(t, err) + require.Nil(t, response) + }) +} -- cgit v1.2.3