summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-11 15:58:16 -0600
committermo khan <mo@mokhan.ca>2025-07-11 15:58:16 -0600
commit00e0aa6b8adf1eab0b821aa26ec2f97d51d15dd8 (patch)
tree665d300682dfab4a98bcf7395a579e5757b9613d /pkg
parenta8d2fb21c72dddf4589cca45594fbcd6bed3f273 (diff)
feat: add a composite service to provide fallback mechanisms
Diffstat (limited to 'pkg')
-rw-r--r--pkg/authz/composite_check_service.go34
-rw-r--r--pkg/authz/composite_check_service_test.go56
2 files changed, 90 insertions, 0 deletions
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)
+ })
+}