From d57a8f037bd9d4f263dd707fe973d1f4ced3f490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Mon, 26 Aug 2024 20:20:16 +0200 Subject: [PATCH 1/5] feat: adds spiffe extractor. --- extauthz/Makefile | 8 +- extauthz/cmd/extauthz/main.go | 4 +- extauthz/cmd/extauthz/main_test.go | 8 +- extauthz/e2e/config.yaml.tmpl | 4 +- extauthz/e2e/store.fga.yaml | 14 +-- extauthz/internal/extractor/extractor.go | 62 ++++++++++- .../extractor/{method.go => http_method.go} | 11 +- extauthz/internal/extractor/mock.go | 65 ++++++++++- extauthz/internal/extractor/mock_test.go | 38 +++++++ extauthz/internal/extractor/spiffe.go | 101 ++++++++++++++++++ extauthz/internal/extractor/spiffe_test.go | 82 ++++++++++++++ extauthz/internal/server/authz/authz.go | 91 +++------------- .../internal/server/config/config_test.go | 6 +- .../server/config/testdata/config.yaml | 6 +- 14 files changed, 385 insertions(+), 115 deletions(-) rename extauthz/internal/extractor/{method.go => http_method.go} (53%) create mode 100644 extauthz/internal/extractor/mock_test.go create mode 100644 extauthz/internal/extractor/spiffe.go create mode 100644 extauthz/internal/extractor/spiffe_test.go diff --git a/extauthz/Makefile b/extauthz/Makefile index 347d6b1..698d679 100644 --- a/extauthz/Makefile +++ b/extauthz/Makefile @@ -10,10 +10,10 @@ test: .PHONY: build build: @mkdir -p ./build - make build-platform BUILD_OS="linux" BUILD_ARCH="amd64" - make build-platform BUILD_OS="linux" BUILD_ARCH="arm64" - make build-platform BUILD_OS="darwin" BUILD_ARCH="amd64" - make build-platform BUILD_OS="darwin" BUILD_ARCH="arm64" + @make build-platform BUILD_OS="linux" BUILD_ARCH="amd64" + @make build-platform BUILD_OS="linux" BUILD_ARCH="arm64" + @make build-platform BUILD_OS="darwin" BUILD_ARCH="amd64" + @make build-platform BUILD_OS="darwin" BUILD_ARCH="arm64" build-platform: @mkdir -p ./build diff --git a/extauthz/cmd/extauthz/main.go b/extauthz/cmd/extauthz/main.go index 558adfe..5de6994 100644 --- a/extauthz/cmd/extauthz/main.go +++ b/extauthz/cmd/extauthz/main.go @@ -41,10 +41,10 @@ func main() { log.Fatalf("failed to initialize OpenFGA client: %v", err) } - extractionSet := make([]extractor.ExtractorSet, 0, len(cfg.ExtractionSet)) + extractionSet := make([]extractor.ExtractorKit, 0, len(cfg.ExtractionSet)) for _, es := range cfg.ExtractionSet { var ( - eSet extractor.ExtractorSet + eSet extractor.ExtractorKit err error ) diff --git a/extauthz/cmd/extauthz/main_test.go b/extauthz/cmd/extauthz/main_test.go index 4e88876..844bdef 100644 --- a/extauthz/cmd/extauthz/main_test.go +++ b/extauthz/cmd/extauthz/main_test.go @@ -17,7 +17,7 @@ import ( "google.golang.org/grpc/test/bufconn" ) -func server(ctx context.Context, e extractor.ExtractorSet) (auth_pb.AuthorizationClient, func()) { +func server(ctx context.Context, e extractor.ExtractorKit) (auth_pb.AuthorizationClient, func()) { buffer := 101024 * 1024 lis := bufconn.Listen(buffer) @@ -28,7 +28,7 @@ func server(ctx context.Context, e extractor.ExtractorSet) (auth_pb.Authorizatio panic(err) } - filter := authz.NewExtAuthZFilter(fgaClient, []extractor.ExtractorSet{e}) + filter := authz.NewExtAuthZFilter(fgaClient, []extractor.ExtractorKit{e}) baseServer := grpc.NewServer() auth_pb.RegisterAuthorizationServer(baseServer, filter) @@ -63,10 +63,10 @@ func TestNoUserExtractedFails(t *testing.T) { expectedErr := errors.New("no user") - e := extractor.ExtractorSet{ + e := extractor.ExtractorKit{ Name: "extauthz", User: func(ctx context.Context, value *auth_pb.CheckRequest) (extractor.Extraction, bool, error) { - return extractor.Extraction{}, false, expectedErr + return nil, false, expectedErr }, } diff --git a/extauthz/e2e/config.yaml.tmpl b/extauthz/e2e/config.yaml.tmpl index cbe0937..f7ea113 100644 --- a/extauthz/e2e/config.yaml.tmpl +++ b/extauthz/e2e/config.yaml.tmpl @@ -8,10 +8,10 @@ extraction_sets: user: type: mock config: - value: subject:user_123 + user: subject:user_123 object: type: mock config: - value: resource:service_abc + object: resource:service_abc relation: type: method diff --git a/extauthz/e2e/store.fga.yaml b/extauthz/e2e/store.fga.yaml index 6bca0ff..7e9de74 100644 --- a/extauthz/e2e/store.fga.yaml +++ b/extauthz/e2e/store.fga.yaml @@ -6,7 +6,7 @@ model: | type resource relations - define can_call: [ subject with allowed_methods ] + define access: [ subject with allowed_methods ] condition allowed_methods(allowed: list, method: string) { allowed.exists_one(r, r == method) || allowed.exists_one(r, r == "*") @@ -14,14 +14,14 @@ model: | tuples: - user: subject:user_123 - relation: can_call + relation: access object: resource:service_abc condition: name: allowed_methods context: allowed: ["GET"] - user: subject:user_456 - relation: can_call + relation: access object: resource:service_xyz condition: name: allowed_methods @@ -33,13 +33,13 @@ tests: check: - user: subject:user_123 assertions: - can_call: true + access: true object: resource:service_abc context: method: "GET" - user: subject:user_123 assertions: - can_call: false + access: false object: resource:service_abc context: method: "POST" @@ -47,13 +47,13 @@ tests: check: - user: subject:user_456 assertions: - can_call: true + access: true object: resource:service_xyz context: method: "GET" - user: subject:user_456 assertions: - can_call: true + access: true object: resource:service_xyz context: method: "POST" diff --git a/extauthz/internal/extractor/extractor.go b/extauthz/internal/extractor/extractor.go index 22c4e36..9a60a40 100644 --- a/extauthz/internal/extractor/extractor.go +++ b/extauthz/internal/extractor/extractor.go @@ -3,25 +3,77 @@ package extractor import ( "context" "errors" + "fmt" authv3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" ) -type Extraction struct { - Value string - Context map[string]any +type Check struct { + User string + Relation string + Object string + Context map[string]interface{} } +type Extraction func(*Check) error + // Extractor is the interface for extracting values from a CheckRequest. type Extractor func(ctx context.Context, value *authv3.CheckRequest) (Extraction, bool, error) -type ExtractorSet struct { +type ExtractorKit struct { Name string - Object Extractor User Extractor + Object Extractor Relation Extractor } +var ErrValueNotFound = errors.New("extraction value not found") + +func (ek ExtractorKit) Extract(ctx context.Context, req *authv3.CheckRequest) (*Check, error) { + check := &Check{} + + eUser, found, err := ek.User(ctx, req) + if err != nil { + return nil, err + } + + if !found { + return nil, fmt.Errorf("extracting user: %w", ErrValueNotFound) + } + + if err := eUser(check); err != nil { + return nil, err + } + + eObject, found, err := ek.Object(ctx, req) + if err != nil { + return nil, err + } + + if !found { + return nil, fmt.Errorf("extracting object: %w", ErrValueNotFound) + } + + if err := eObject(check); err != nil { + return nil, err + } + + eRelation, found, err := ek.Relation(ctx, req) + if err != nil { + return nil, err + } + + if !found { + return nil, fmt.Errorf("extracting relation: %w", ErrValueNotFound) + } + + if err := eRelation(check); err != nil { + return nil, err + } + + return check, nil +} + type Config interface{} func GetExtractorConfig(name string) (Config, error) { diff --git a/extauthz/internal/extractor/method.go b/extauthz/internal/extractor/http_method.go similarity index 53% rename from extauthz/internal/extractor/method.go rename to extauthz/internal/extractor/http_method.go index b1bd8ca..47b1b1a 100644 --- a/extauthz/internal/extractor/method.go +++ b/extauthz/internal/extractor/http_method.go @@ -6,13 +6,12 @@ import ( authv3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" ) -func NewMethod(cfg any) Extractor { +func NewMethod(any) Extractor { return func(ctx context.Context, value *authv3.CheckRequest) (Extraction, bool, error) { - return Extraction{ - Value: "can_call", - Context: map[string]interface{}{ - "method": value.GetAttributes().GetRequest().GetHttp().GetMethod(), - }, + return func(c *Check) error { + c.Relation = "access" + c.Context["method"] = value.GetAttributes().GetRequest().GetHttp().GetMethod() + return nil }, true, nil } } diff --git a/extauthz/internal/extractor/mock.go b/extauthz/internal/extractor/mock.go index 890311d..1f6d8d3 100644 --- a/extauthz/internal/extractor/mock.go +++ b/extauthz/internal/extractor/mock.go @@ -2,18 +2,75 @@ package extractor import ( "context" + "errors" authv3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" + "gopkg.in/yaml.v3" ) type MockConfig struct { - Val string `yaml:"value"` - Context map[string]interface{} `yaml:"context"` - Err error `yaml:"error"` + found bool + User string `yaml:"user"` + Object string `yaml:"object"` + Relation string `yaml:"relation"` + Context map[string]interface{} `yaml:"context"` + Err error `yaml:"error"` + CheckErr error `yaml:"check_error"` +} + +const ( + hasUser = 1 << iota + hasObject + hasRelation +) + +func (c *MockConfig) UnmarshalYAML(value *yaml.Node) error { + type mockConfig MockConfig + if err := value.Decode((*mockConfig)(c)); err != nil { + return err + } + + ec := 0 + if c.User != "" { + ec |= hasUser + } + + if c.Object != "" { + ec |= hasObject + } + + if c.Relation != "" { + ec |= hasRelation + } + + if ec == 0 { + return nil + } + + c.found = true + + if (ec & -ec) != ec { + return errors.New("only one of user, object, or relation can be set") + } + + return nil } func NewMock(cfg *MockConfig) Extractor { return func(ctx context.Context, value *authv3.CheckRequest) (Extraction, bool, error) { - return Extraction{Value: cfg.Val, Context: cfg.Context}, cfg.Val != "", cfg.Err + return func(c *Check) error { + if cfg.CheckErr != nil { + return cfg.CheckErr + } + + c.User = cfg.User + c.Object = cfg.Object + c.Relation = cfg.Relation + for k, v := range cfg.Context { + c.Context[k] = v + } + + return nil + }, cfg.found, cfg.Err } } diff --git a/extauthz/internal/extractor/mock_test.go b/extauthz/internal/extractor/mock_test.go new file mode 100644 index 0000000..7cdbcd9 --- /dev/null +++ b/extauthz/internal/extractor/mock_test.go @@ -0,0 +1,38 @@ +package extractor + +import ( + "testing" + + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +func TestMockUnmarshal(t *testing.T) { + t.Run("empty", func(t *testing.T) { + c := &MockConfig{} + err := yaml.Unmarshal(nil, c) + require.NoError(t, err) + require.Empty(t, c.User) + require.Empty(t, c.Object) + require.Empty(t, c.Relation) + require.False(t, c.found) + }) + + t.Run("success", func(t *testing.T) { + c := &MockConfig{} + err := yaml.Unmarshal([]byte(`user: "subject:user_123"`), c) + require.NoError(t, err) + require.Equal(t, "subject:user_123", c.User) + require.Empty(t, c.Object) + require.Empty(t, c.Relation) + require.True(t, c.found) + }) + + t.Run("fails because subject and object are passed", func(t *testing.T) { + c := &MockConfig{} + err := yaml.Unmarshal([]byte(` +user: "subject:user_123" +object: resource:service_abc`), c) + require.Error(t, err) + }) +} diff --git a/extauthz/internal/extractor/spiffe.go b/extauthz/internal/extractor/spiffe.go new file mode 100644 index 0000000..e854b33 --- /dev/null +++ b/extauthz/internal/extractor/spiffe.go @@ -0,0 +1,101 @@ +package extractor + +import ( + "context" + "errors" + "strings" + + authv3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" + "gopkg.in/yaml.v3" +) + +const ( + clientCertHeader = "x-forwarded-client-cert" + spiffeKey = "By=" + spiffeCurrentClientKey = "URI=" +) + +type spiffeExtractionType int8 + +func (t spiffeExtractionType) String() string { + switch t { + case spiffeTypeUser: + return "user" + case spiffeTypeObject: + return "object" + } + + return "unknown" +} + +func (t spiffeExtractionType) MarshalYAML() (interface{}, error) { + return t.String(), nil +} + +func (t *spiffeExtractionType) UnmarshalYAML(value *yaml.Node) error { + switch value.Value { + case "user": + *t = spiffeTypeUser + case "object": + *t = spiffeTypeObject + default: + return errors.New("unknown spiffe extraction type") + } + + return nil +} + +const ( + spiffeTypeUser spiffeExtractionType = iota + spiffeTypeObject +) + +type SpiffeConfig struct { + _type spiffeExtractionType `yaml:"type"` +} + +func NewSpiffe(config *SpiffeConfig) Extractor { + return func(ctx context.Context, value *authv3.CheckRequest) (Extraction, bool, error) { + headers := value.GetAttributes().GetRequest().GetHttp().GetHeaders() + val, ok := headers[clientCertHeader] + if !ok { + return nil, false, nil + } + + var prefix string + if config._type == spiffeTypeUser { + prefix = spiffeCurrentClientKey + } else { + prefix = spiffeKey + } + + var segments = strings.Split(val, ",") + + for _, seg := range segments { + parts := strings.Split(seg, ";") + for _, part := range parts { + if !strings.HasPrefix(part, prefix) { + continue + } + + if part[len(prefix):] == "" { + continue + } + + if config._type == spiffeTypeUser { + return func(c *Check) error { + c.User = part[len(prefix):] + return nil + }, true, nil + } else { + return func(c *Check) error { + c.Object = part[len(prefix):] + return nil + }, true, nil + } + } + } + + return nil, false, nil + } +} diff --git a/extauthz/internal/extractor/spiffe_test.go b/extauthz/internal/extractor/spiffe_test.go new file mode 100644 index 0000000..058ddb6 --- /dev/null +++ b/extauthz/internal/extractor/spiffe_test.go @@ -0,0 +1,82 @@ +package extractor + +import ( + "context" + "testing" + + authv3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +func TestSpiffeUnmarshal(t *testing.T) { + c := &SpiffeConfig{} + yaml.Unmarshal([]byte("type: user"), c) + require.Equal(t, spiffeTypeUser, c._type) +} + +func TestSpiffeExtractor(t *testing.T) { + t.Run("empty", func(t *testing.T) { + extractor := NewSpiffe(nil) + + extraction, found, err := extractor(context.Background(), &authv3.CheckRequest{ + Attributes: &authv3.AttributeContext{ + Request: &authv3.AttributeContext_Request{ + Http: &authv3.AttributeContext_HttpRequest{}, + }, + }, + }) + + require.NoError(t, err) + require.False(t, found) + require.Nil(t, extraction) + }) + + t.Run("success for subject", func(t *testing.T) { + extractor := NewSpiffe(&SpiffeConfig{ + _type: spiffeTypeUser, + }) + + extraction, found, err := extractor(context.Background(), &authv3.CheckRequest{ + Attributes: &authv3.AttributeContext{ + Request: &authv3.AttributeContext_Request{ + Http: &authv3.AttributeContext_HttpRequest{ + Headers: map[string]string{ + "x-forwarded-client-cert": "Hash=519dbf0d617cd943359dcf71f4d26d35e95347b616e62dc9c5ce4f3a7492ec76;Cert=\"-----BEGIN%20CERTIFICATE-----%0AMIIC3DCCAcQCAQEwDQYJKoZIhvcNAQEFBQAwLTEVMBMGA1UECgwMZXhhbXBsZSBJ%0AbmMuMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0yMTA2MDcxNDUwMDBaFw0yMjA2%0AMDcxNDUwMDBaMDsxGzAZBgNVBAMMEmNsaWVudC5leGFtcGxlLmNvbTEcMBoGA1UE%0ACgwTY2xpZW50IG9yZ2FuaXphdGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC%0AAQoCggEBAMe%2FVGy3syX%2BkMpqe6MfStPuFlwwEzwqz3lAaMm9YqASOk9uv0Qc%2FZPm%0AwIMrV1dnnLtLo6nZaRfsMgz1XiYBp%2BR2O87dFw2WN5AjD98zUT3XqUzGHF63cZvH%0AmoVkqrHiwh35HCFiwh9KIS3CtIdYe1n1%2FSkJpj0tVszIY%2Bi288Hu5K5fVYyYzyk%2F%0ANYQKG7A1KEgZ39SkRCHIyK%2FWeGhNT1VCfn%2Bx3D62RUVDjK6Au9yu5pJsKAyB76eg%0AZkXdvBv92dIN2iPhe9DzJ99MfpkjG9JfZG43svc2I2BAIj1eLzh5ZUnpILIHP42S%0AQUd6IBX9X%2BvFH%2FsFVnoySKewcMRK%2BkcCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA%0AswYNs6M4UT8epcW3MOjA%2B5c8EWfMI7SjNuShpRUNaJAhzdvpfj3PFIW%2BROLNs0Tj%0AwGVwkkZW8daxBQw8yC9kEE%2Broj7eJmV9SE%2BozZwa6L4hf18pcNaJlKIyvQUS3mgB%0ApGYO9YvC%2Bsg%2B0gfbSWfbzL17jRS1UI%2BOiW%2BWS5o85SOpusSHDtrG4qcISm7jpgyb%0AudzCZQHOkknO4e%2BrWiGKLpGBE1LkS5Cl%2FJkU1qJWspa4JaFtQxNCdT2Tmo6XDRZ7%0AKfoZiH6c1lI7C07duz9iPkNATc2w%2BNP7bzQgp4BlC0zQ3MwEbcR5uVxvC3vTRsIa%0AznXIRj23jj3NmidA4DTASQ%3D%3D%0A-----END%20CERTIFICATE-----%0A\";Subject=\"O=client organization,CN=client.example.com\";URI=,By=spiffe://cluster.local/ns/default/sa/httpbin;Hash=58531cf54811dc1fd60ee4aaea52866daecb353cb23d2fa237c580cbc217b4be;Subject=\"\";URI=spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account", + }, + }, + }, + }, + }) + + require.NoError(t, err) + require.True(t, found) + c := &Check{} + require.NoError(t, extraction(c)) + require.Equal(t, "spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account", c.User) + }) + + t.Run("success for object", func(t *testing.T) { + extractor := NewSpiffe(&SpiffeConfig{ + _type: spiffeTypeObject, + }) + + extraction, found, err := extractor(context.Background(), &authv3.CheckRequest{ + Attributes: &authv3.AttributeContext{ + Request: &authv3.AttributeContext_Request{ + Http: &authv3.AttributeContext_HttpRequest{ + Headers: map[string]string{ + "x-forwarded-client-cert": "Hash=519dbf0d617cd943359dcf71f4d26d35e95347b616e62dc9c5ce4f3a7492ec76;Cert=\"-----BEGIN%20CERTIFICATE-----%0AMIIC3DCCAcQCAQEwDQYJKoZIhvcNAQEFBQAwLTEVMBMGA1UECgwMZXhhbXBsZSBJ%0AbmMuMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0yMTA2MDcxNDUwMDBaFw0yMjA2%0AMDcxNDUwMDBaMDsxGzAZBgNVBAMMEmNsaWVudC5leGFtcGxlLmNvbTEcMBoGA1UE%0ACgwTY2xpZW50IG9yZ2FuaXphdGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC%0AAQoCggEBAMe%2FVGy3syX%2BkMpqe6MfStPuFlwwEzwqz3lAaMm9YqASOk9uv0Qc%2FZPm%0AwIMrV1dnnLtLo6nZaRfsMgz1XiYBp%2BR2O87dFw2WN5AjD98zUT3XqUzGHF63cZvH%0AmoVkqrHiwh35HCFiwh9KIS3CtIdYe1n1%2FSkJpj0tVszIY%2Bi288Hu5K5fVYyYzyk%2F%0ANYQKG7A1KEgZ39SkRCHIyK%2FWeGhNT1VCfn%2Bx3D62RUVDjK6Au9yu5pJsKAyB76eg%0AZkXdvBv92dIN2iPhe9DzJ99MfpkjG9JfZG43svc2I2BAIj1eLzh5ZUnpILIHP42S%0AQUd6IBX9X%2BvFH%2FsFVnoySKewcMRK%2BkcCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA%0AswYNs6M4UT8epcW3MOjA%2B5c8EWfMI7SjNuShpRUNaJAhzdvpfj3PFIW%2BROLNs0Tj%0AwGVwkkZW8daxBQw8yC9kEE%2Broj7eJmV9SE%2BozZwa6L4hf18pcNaJlKIyvQUS3mgB%0ApGYO9YvC%2Bsg%2B0gfbSWfbzL17jRS1UI%2BOiW%2BWS5o85SOpusSHDtrG4qcISm7jpgyb%0AudzCZQHOkknO4e%2BrWiGKLpGBE1LkS5Cl%2FJkU1qJWspa4JaFtQxNCdT2Tmo6XDRZ7%0AKfoZiH6c1lI7C07duz9iPkNATc2w%2BNP7bzQgp4BlC0zQ3MwEbcR5uVxvC3vTRsIa%0AznXIRj23jj3NmidA4DTASQ%3D%3D%0A-----END%20CERTIFICATE-----%0A\";Subject=\"O=client organization,CN=client.example.com\";URI=,By=spiffe://cluster.local/ns/default/sa/httpbin;Hash=58531cf54811dc1fd60ee4aaea52866daecb353cb23d2fa237c580cbc217b4be;Subject=\"\";URI=spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account", + }, + }, + }, + }, + }) + + require.NoError(t, err) + require.True(t, found) + c := &Check{} + require.NoError(t, extraction(c)) + require.Equal(t, "spiffe://cluster.local/ns/default/sa/httpbin", c.Object) + }) +} diff --git a/extauthz/internal/server/authz/authz.go b/extauthz/internal/server/authz/authz.go index ea62f52..6db80fb 100644 --- a/extauthz/internal/server/authz/authz.go +++ b/extauthz/internal/server/authz/authz.go @@ -2,6 +2,7 @@ package authz import ( "context" + "errors" "fmt" "log" @@ -36,15 +37,15 @@ var ( // ExtAuthZFilter is an implementation of the Envoy AuthZ filter. type ExtAuthZFilter struct { client *client.OpenFgaClient - extractionSet []extractor.ExtractorSet + extractionKit []extractor.ExtractorKit modelID string } var _ envoy.AuthorizationServer = (*ExtAuthZFilter)(nil) // NewExtAuthZFilter creates a new ExtAuthZFilter -func NewExtAuthZFilter(c *client.OpenFgaClient, es []extractor.ExtractorSet) *ExtAuthZFilter { - return &ExtAuthZFilter{client: c, extractionSet: es} +func NewExtAuthZFilter(c *client.OpenFgaClient, es []extractor.ExtractorKit) *ExtAuthZFilter { + return &ExtAuthZFilter{client: c, extractionKit: es} } func (e ExtAuthZFilter) Register(server *grpc.Server) { @@ -58,74 +59,28 @@ func (e ExtAuthZFilter) Check(ctx context.Context, req *envoy.CheckRequest) (res return nil, err } + // TODO: replace with logging library log.Println(res) return res, nil } -type extracted struct { - user extractor.Extraction - object extractor.Extraction - relation extractor.Extraction -} - -func (e ExtAuthZFilter) extract(ctx context.Context, req *envoy.CheckRequest) (*extracted, error) { - var user, object, relation extractor.Extraction - for _, es := range e.extractionSet { - var ( - found bool - err error - ) - user, found, err = es.User(ctx, req) - if err != nil { - return nil, err - } - - if !found { - continue - } - - object, found, err = es.Object(ctx, req) - if err != nil { - return nil, err - } - if !found { - continue +func (e ExtAuthZFilter) extract(ctx context.Context, req *envoy.CheckRequest) (*extractor.Check, error) { + for _, es := range e.extractionKit { + check, err := es.Extract(ctx, req) + if err == nil { + return check, nil } - relation, found, err = es.Relation(ctx, req) - if err != nil { - return nil, err - } - if !found { + if errors.Is(err, extractor.ErrValueNotFound) { continue } - return &extracted{ - user: user, - object: object, - relation: relation, - }, nil + return nil, err } return nil, nil } -func mergeMaps(map1, map2 map[string]any) map[string]any { - UniqueMap := make(map[string]any) - - // for loop for the first map - for key, val := range map1 { - UniqueMap[key] = val - } - - // for loop for the second map - for key, val := range map2 { - UniqueMap[key] = val - } - // return merged result - return UniqueMap -} - // Check implements the Check method of the Authorization interface. func (e ExtAuthZFilter) check(ctx context.Context, req *envoy.CheckRequest) (response *envoy.CheckResponse, err error) { extracted, err := e.extract(ctx, req) @@ -137,25 +92,11 @@ func (e ExtAuthZFilter) check(ctx context.Context, req *envoy.CheckRequest) (res return deny(codes.InvalidArgument, "No extraction set found"), nil } - context := map[string]any{} - - if extracted.user.Context != nil { - context = mergeMaps(context, extracted.user.Context) - } - - if extracted.object.Context != nil { - context = mergeMaps(context, extracted.object.Context) - } - - if extracted.relation.Context != nil { - context = mergeMaps(context, extracted.relation.Context) - } - body := client.ClientCheckRequest{ - User: extracted.user.Value, - Relation: extracted.relation.Value, - Object: extracted.object.Value, - Context: &context, + User: extracted.User, + Relation: extracted.Relation, + Object: extracted.Object, + Context: &extracted.Context, } options := client.ClientCheckOptions{ diff --git a/extauthz/internal/server/config/config_test.go b/extauthz/internal/server/config/config_test.go index 1400a34..02c3291 100644 --- a/extauthz/internal/server/config/config_test.go +++ b/extauthz/internal/server/config/config_test.go @@ -16,9 +16,9 @@ func TestConfig(t *testing.T) { require.Len(t, cfg.ExtractionSet, 1) require.Equal(t, "test", cfg.ExtractionSet[0].Name) require.Equal(t, "mock", cfg.ExtractionSet[0].User.Type) - require.Equal(t, "my_user", cfg.ExtractionSet[0].User.Config.(*extractor.MockConfig).Val) + require.Equal(t, "my_user", cfg.ExtractionSet[0].User.Config.(*extractor.MockConfig).User) require.Equal(t, "mock", cfg.ExtractionSet[0].Object.Type) - require.Equal(t, "my_object", cfg.ExtractionSet[0].Object.Config.(*extractor.MockConfig).Val) + require.Equal(t, "my_object", cfg.ExtractionSet[0].Object.Config.(*extractor.MockConfig).Object) require.Equal(t, "mock", cfg.ExtractionSet[0].Relation.Type) - require.Equal(t, "my_relation", cfg.ExtractionSet[0].Relation.Config.(*extractor.MockConfig).Val) + require.Equal(t, "my_relation", cfg.ExtractionSet[0].Relation.Config.(*extractor.MockConfig).Relation) } diff --git a/extauthz/internal/server/config/testdata/config.yaml b/extauthz/internal/server/config/testdata/config.yaml index b77fb93..58c3d80 100644 --- a/extauthz/internal/server/config/testdata/config.yaml +++ b/extauthz/internal/server/config/testdata/config.yaml @@ -8,12 +8,12 @@ extraction_sets: user: type: mock config: - value: my_user + user: my_user object: type: mock config: - value: my_object + object: my_object relation: type: mock config: - value: my_relation + relation: my_relation From ff4e53799b372d3438dc937c8acbec7517bf6a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Mon, 26 Aug 2024 20:32:16 +0200 Subject: [PATCH 2/5] chore: improves spiffe creation. --- extauthz/internal/extractor/spiffe.go | 46 ++++++++++++++++----------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/extauthz/internal/extractor/spiffe.go b/extauthz/internal/extractor/spiffe.go index e854b33..8c1b090 100644 --- a/extauthz/internal/extractor/spiffe.go +++ b/extauthz/internal/extractor/spiffe.go @@ -55,6 +55,33 @@ type SpiffeConfig struct { } func NewSpiffe(config *SpiffeConfig) Extractor { + if config == nil { + config = &SpiffeConfig{} + } + + var ( + prefix string + identityExtractor func(identity string) Extraction + ) + + if config._type == spiffeTypeUser { + prefix = spiffeCurrentClientKey + identityExtractor = func(identity string) Extraction { + return func(c *Check) error { + c.User = identity + return nil + } + } + } else { + prefix = spiffeKey + identityExtractor = func(identity string) Extraction { + return func(c *Check) error { + c.Object = identity + return nil + } + } + } + return func(ctx context.Context, value *authv3.CheckRequest) (Extraction, bool, error) { headers := value.GetAttributes().GetRequest().GetHttp().GetHeaders() val, ok := headers[clientCertHeader] @@ -62,13 +89,6 @@ func NewSpiffe(config *SpiffeConfig) Extractor { return nil, false, nil } - var prefix string - if config._type == spiffeTypeUser { - prefix = spiffeCurrentClientKey - } else { - prefix = spiffeKey - } - var segments = strings.Split(val, ",") for _, seg := range segments { @@ -82,17 +102,7 @@ func NewSpiffe(config *SpiffeConfig) Extractor { continue } - if config._type == spiffeTypeUser { - return func(c *Check) error { - c.User = part[len(prefix):] - return nil - }, true, nil - } else { - return func(c *Check) error { - c.Object = part[len(prefix):] - return nil - }, true, nil - } + return identityExtractor(part[len(prefix):]), true, nil } } From 5a18298eb7e770053a9c80c8fab49d36e3dad72c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Tue, 27 Aug 2024 08:32:31 +0200 Subject: [PATCH 3/5] chore: returns back to old signature for extraction as it has less side effects. --- extauthz/e2e/config.yaml.tmpl | 6 +- extauthz/e2e/run.sh | 4 +- extauthz/e2e/store.fga.yaml | 12 ++-- extauthz/internal/extractor/extractor.go | 70 ++++++++++++++----- extauthz/internal/extractor/mock.go | 68 ++---------------- extauthz/internal/extractor/mock_test.go | 20 +----- .../{http_method.go => request_method.go} | 11 +-- extauthz/internal/extractor/spiffe.go | 23 ++---- extauthz/internal/extractor/spiffe_test.go | 8 +-- extauthz/internal/server/authz/authz.go | 18 ++--- .../internal/server/config/config_test.go | 6 +- .../server/config/testdata/config.yaml | 6 +- 12 files changed, 101 insertions(+), 151 deletions(-) rename extauthz/internal/extractor/{http_method.go => request_method.go} (52%) diff --git a/extauthz/e2e/config.yaml.tmpl b/extauthz/e2e/config.yaml.tmpl index f7ea113..a9d3e87 100644 --- a/extauthz/e2e/config.yaml.tmpl +++ b/extauthz/e2e/config.yaml.tmpl @@ -8,10 +8,10 @@ extraction_sets: user: type: mock config: - user: subject:user_123 + value: "subject:user_123" object: type: mock config: - object: resource:service_abc + value: "resource:service_abc" relation: - type: method + type: request_method diff --git a/extauthz/e2e/run.sh b/extauthz/e2e/run.sh index 26cbb27..717cbc5 100755 --- a/extauthz/e2e/run.sh +++ b/extauthz/e2e/run.sh @@ -11,8 +11,8 @@ STORE_FILE='e2e/store.fga.yaml' FGA_API_URL='http://localhost:18080' TARGET_URL='http://localhost:8080' -which yq || (echo "yq is not installed. Please install it using make e2e-tools." && exit 1) -which fga || (echo "fga is not installed. Please install it make e2e-tools." && exit 1) +which yq > /dev/null || (echo "yq is not installed. Please install it using make e2e-tools." && exit 1) +which fga > /dev/null || (echo "fga is not installed. Please install it make e2e-tools." && exit 1) TMPDIR=$(mktemp -d) MODEL=$TMPDIR/model.fga diff --git a/extauthz/e2e/store.fga.yaml b/extauthz/e2e/store.fga.yaml index 7e9de74..a4621a9 100644 --- a/extauthz/e2e/store.fga.yaml +++ b/extauthz/e2e/store.fga.yaml @@ -8,8 +8,8 @@ model: | relations define access: [ subject with allowed_methods ] - condition allowed_methods(allowed: list, method: string) { - allowed.exists_one(r, r == method) || allowed.exists_one(r, r == "*") + condition allowed_methods(allowed: list, request_method: string) { + allowed.exists_one(r, r == request_method) || allowed.exists_one(r, r == "*") } tuples: @@ -36,13 +36,13 @@ tests: access: true object: resource:service_abc context: - method: "GET" + request_method: "GET" - user: subject:user_123 assertions: access: false object: resource:service_abc context: - method: "POST" + request_method: "POST" - name: user_456 can do only GET to service_xyz check: - user: subject:user_456 @@ -50,10 +50,10 @@ tests: access: true object: resource:service_xyz context: - method: "GET" + request_method: "GET" - user: subject:user_456 assertions: access: true object: resource:service_xyz context: - method: "POST" + request_method: "POST" diff --git a/extauthz/internal/extractor/extractor.go b/extauthz/internal/extractor/extractor.go index 9a60a40..67e135e 100644 --- a/extauthz/internal/extractor/extractor.go +++ b/extauthz/internal/extractor/extractor.go @@ -15,7 +15,37 @@ type Check struct { Context map[string]interface{} } -type Extraction func(*Check) error +func (c Check) Validate() error { + if c.User == "" { + return errors.New("user is required") + } + + if c.Object == "" { + return errors.New("object is required") + } + + if c.Relation == "" { + return errors.New("relation is required") + } + + return nil +} + +type Extraction struct { + Value string + Context map[string]interface{} +} + +func (e *Extraction) applyExtraction(v *string, context map[string]interface{}) error { + *v = e.Value + for k, v := range e.Context { + if _, ok := context[k]; ok { + return fmt.Errorf("context key %s already exists", k) + } + context[k] = v + } + return nil +} // Extractor is the interface for extracting values from a CheckRequest. type Extractor func(ctx context.Context, value *authv3.CheckRequest) (Extraction, bool, error) @@ -30,45 +60,51 @@ type ExtractorKit struct { var ErrValueNotFound = errors.New("extraction value not found") func (ek ExtractorKit) Extract(ctx context.Context, req *authv3.CheckRequest) (*Check, error) { - check := &Check{} + check := &Check{ + Context: make(map[string]interface{}), + } eUser, found, err := ek.User(ctx, req) if err != nil { - return nil, err + return nil, fmt.Errorf("getting user extraction: %w", err) } if !found { - return nil, fmt.Errorf("extracting user: %w", ErrValueNotFound) + return nil, fmt.Errorf("getting user extraction: %w", ErrValueNotFound) } - if err := eUser(check); err != nil { - return nil, err + if err := eUser.applyExtraction(&check.User, check.Context); err != nil { + return nil, fmt.Errorf("extracting user: %w", err) } eObject, found, err := ek.Object(ctx, req) if err != nil { - return nil, err + return nil, fmt.Errorf("getting object extraction: %w", err) } if !found { - return nil, fmt.Errorf("extracting object: %w", ErrValueNotFound) + return nil, fmt.Errorf("getting object extraction: %w", ErrValueNotFound) } - if err := eObject(check); err != nil { - return nil, err + if err := eObject.applyExtraction(&check.Object, check.Context); err != nil { + return nil, fmt.Errorf("extracting object: %w", err) } eRelation, found, err := ek.Relation(ctx, req) if err != nil { - return nil, err + return nil, fmt.Errorf("getting relation extraction: %w", err) } if !found { - return nil, fmt.Errorf("extracting relation: %w", ErrValueNotFound) + return nil, fmt.Errorf("getting relation extraction: %w", ErrValueNotFound) + } + + if err := eRelation.applyExtraction(&check.Relation, check.Context); err != nil { + return nil, fmt.Errorf("extracting relation: %w", err) } - if err := eRelation(check); err != nil { - return nil, err + if err := check.Validate(); err != nil { + return nil, fmt.Errorf("validating check: %v", err) } return check, nil @@ -80,7 +116,7 @@ func GetExtractorConfig(name string) (Config, error) { switch name { case "mock": return &MockConfig{}, nil - case "method": + case "request_method": return nil, nil default: return nil, errors.New("extractor not found") @@ -91,8 +127,8 @@ func MakeExtractor(name string, cfg Config) (Extractor, error) { switch name { case "mock": return NewMock(cfg.(*MockConfig)), nil - case "method": - return NewMethod(cfg), nil + case "request_method": + return NewRequestMethod(cfg), nil default: return nil, errors.New("extractor not found") } diff --git a/extauthz/internal/extractor/mock.go b/extauthz/internal/extractor/mock.go index 1f6d8d3..42e6143 100644 --- a/extauthz/internal/extractor/mock.go +++ b/extauthz/internal/extractor/mock.go @@ -2,75 +2,21 @@ package extractor import ( "context" - "errors" authv3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" - "gopkg.in/yaml.v3" ) type MockConfig struct { - found bool - User string `yaml:"user"` - Object string `yaml:"object"` - Relation string `yaml:"relation"` - Context map[string]interface{} `yaml:"context"` - Err error `yaml:"error"` - CheckErr error `yaml:"check_error"` -} - -const ( - hasUser = 1 << iota - hasObject - hasRelation -) - -func (c *MockConfig) UnmarshalYAML(value *yaml.Node) error { - type mockConfig MockConfig - if err := value.Decode((*mockConfig)(c)); err != nil { - return err - } - - ec := 0 - if c.User != "" { - ec |= hasUser - } - - if c.Object != "" { - ec |= hasObject - } - - if c.Relation != "" { - ec |= hasRelation - } - - if ec == 0 { - return nil - } - - c.found = true - - if (ec & -ec) != ec { - return errors.New("only one of user, object, or relation can be set") - } - - return nil + Value string `yaml:"value"` + Context map[string]interface{} `yaml:"context"` + Err error `yaml:"error"` } func NewMock(cfg *MockConfig) Extractor { return func(ctx context.Context, value *authv3.CheckRequest) (Extraction, bool, error) { - return func(c *Check) error { - if cfg.CheckErr != nil { - return cfg.CheckErr - } - - c.User = cfg.User - c.Object = cfg.Object - c.Relation = cfg.Relation - for k, v := range cfg.Context { - c.Context[k] = v - } - - return nil - }, cfg.found, cfg.Err + return Extraction{ + Value: cfg.Value, + Context: cfg.Context, + }, cfg.Value != "", cfg.Err } } diff --git a/extauthz/internal/extractor/mock_test.go b/extauthz/internal/extractor/mock_test.go index 7cdbcd9..d5f8d9a 100644 --- a/extauthz/internal/extractor/mock_test.go +++ b/extauthz/internal/extractor/mock_test.go @@ -12,27 +12,13 @@ func TestMockUnmarshal(t *testing.T) { c := &MockConfig{} err := yaml.Unmarshal(nil, c) require.NoError(t, err) - require.Empty(t, c.User) - require.Empty(t, c.Object) - require.Empty(t, c.Relation) - require.False(t, c.found) + require.Empty(t, c.Value) }) t.Run("success", func(t *testing.T) { c := &MockConfig{} - err := yaml.Unmarshal([]byte(`user: "subject:user_123"`), c) + err := yaml.Unmarshal([]byte(`value: "subject:user_123"`), c) require.NoError(t, err) - require.Equal(t, "subject:user_123", c.User) - require.Empty(t, c.Object) - require.Empty(t, c.Relation) - require.True(t, c.found) - }) - - t.Run("fails because subject and object are passed", func(t *testing.T) { - c := &MockConfig{} - err := yaml.Unmarshal([]byte(` -user: "subject:user_123" -object: resource:service_abc`), c) - require.Error(t, err) + require.Equal(t, "subject:user_123", c.Value) }) } diff --git a/extauthz/internal/extractor/http_method.go b/extauthz/internal/extractor/request_method.go similarity index 52% rename from extauthz/internal/extractor/http_method.go rename to extauthz/internal/extractor/request_method.go index 47b1b1a..cedc252 100644 --- a/extauthz/internal/extractor/http_method.go +++ b/extauthz/internal/extractor/request_method.go @@ -6,12 +6,13 @@ import ( authv3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" ) -func NewMethod(any) Extractor { +func NewRequestMethod(any) Extractor { return func(ctx context.Context, value *authv3.CheckRequest) (Extraction, bool, error) { - return func(c *Check) error { - c.Relation = "access" - c.Context["method"] = value.GetAttributes().GetRequest().GetHttp().GetMethod() - return nil + return Extraction{ + Value: "access", + Context: map[string]interface{}{ + "request_method": value.GetAttributes().GetRequest().GetHttp().GetMethod(), + }, }, true, nil } } diff --git a/extauthz/internal/extractor/spiffe.go b/extauthz/internal/extractor/spiffe.go index 8c1b090..33b9c7c 100644 --- a/extauthz/internal/extractor/spiffe.go +++ b/extauthz/internal/extractor/spiffe.go @@ -59,34 +59,19 @@ func NewSpiffe(config *SpiffeConfig) Extractor { config = &SpiffeConfig{} } - var ( - prefix string - identityExtractor func(identity string) Extraction - ) + var prefix string if config._type == spiffeTypeUser { prefix = spiffeCurrentClientKey - identityExtractor = func(identity string) Extraction { - return func(c *Check) error { - c.User = identity - return nil - } - } } else { prefix = spiffeKey - identityExtractor = func(identity string) Extraction { - return func(c *Check) error { - c.Object = identity - return nil - } - } } return func(ctx context.Context, value *authv3.CheckRequest) (Extraction, bool, error) { headers := value.GetAttributes().GetRequest().GetHttp().GetHeaders() val, ok := headers[clientCertHeader] if !ok { - return nil, false, nil + return Extraction{}, false, nil } var segments = strings.Split(val, ",") @@ -102,10 +87,10 @@ func NewSpiffe(config *SpiffeConfig) Extractor { continue } - return identityExtractor(part[len(prefix):]), true, nil + return Extraction{Value: part[len(prefix):]}, true, nil } } - return nil, false, nil + return Extraction{}, false, nil } } diff --git a/extauthz/internal/extractor/spiffe_test.go b/extauthz/internal/extractor/spiffe_test.go index 058ddb6..e20f3c4 100644 --- a/extauthz/internal/extractor/spiffe_test.go +++ b/extauthz/internal/extractor/spiffe_test.go @@ -51,9 +51,7 @@ func TestSpiffeExtractor(t *testing.T) { require.NoError(t, err) require.True(t, found) - c := &Check{} - require.NoError(t, extraction(c)) - require.Equal(t, "spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account", c.User) + require.Equal(t, "spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account", extraction.Value) }) t.Run("success for object", func(t *testing.T) { @@ -75,8 +73,6 @@ func TestSpiffeExtractor(t *testing.T) { require.NoError(t, err) require.True(t, found) - c := &Check{} - require.NoError(t, extraction(c)) - require.Equal(t, "spiffe://cluster.local/ns/default/sa/httpbin", c.Object) + require.Equal(t, "spiffe://cluster.local/ns/default/sa/httpbin", extraction.Value) }) } diff --git a/extauthz/internal/server/authz/authz.go b/extauthz/internal/server/authz/authz.go index 6db80fb..b9c9edc 100644 --- a/extauthz/internal/server/authz/authz.go +++ b/extauthz/internal/server/authz/authz.go @@ -55,12 +55,9 @@ func (e ExtAuthZFilter) Register(server *grpc.Server) { func (e ExtAuthZFilter) Check(ctx context.Context, req *envoy.CheckRequest) (response *envoy.CheckResponse, err error) { res, err := e.check(ctx, req) if err != nil { - log.Println(err) return nil, err } - // TODO: replace with logging library - log.Println(res) return res, nil } @@ -83,20 +80,21 @@ func (e ExtAuthZFilter) extract(ctx context.Context, req *envoy.CheckRequest) (* // Check implements the Check method of the Authorization interface. func (e ExtAuthZFilter) check(ctx context.Context, req *envoy.CheckRequest) (response *envoy.CheckResponse, err error) { - extracted, err := e.extract(ctx, req) + check, err := e.extract(ctx, req) if err != nil { + fmt.Printf("extracting from request: %v", err) return nil, err } - if extracted == nil { + if check == nil { return deny(codes.InvalidArgument, "No extraction set found"), nil } body := client.ClientCheckRequest{ - User: extracted.User, - Relation: extracted.Relation, - Object: extracted.Object, - Context: &extracted.Context, + User: check.User, + Relation: check.Relation, + Object: check.Object, + Context: &check.Context, } options := client.ClientCheckOptions{ @@ -105,6 +103,7 @@ func (e ExtAuthZFilter) check(ctx context.Context, req *envoy.CheckRequest) (res data, err := e.client.Check(ctx).Body(body).Options(options).Execute() if err != nil { + log.Printf("%v for %v\n", err, body) return deny(codes.Internal, fmt.Sprintf("Error checking permissions: %v", err)), nil } @@ -112,5 +111,6 @@ func (e ExtAuthZFilter) check(ctx context.Context, req *envoy.CheckRequest) (res return allow, nil } + log.Printf("unauthorized request for %v\n", body) return deny(codes.PermissionDenied, fmt.Sprintf("Access denied: %s", data.GetResolution())), nil } diff --git a/extauthz/internal/server/config/config_test.go b/extauthz/internal/server/config/config_test.go index 02c3291..792a7a6 100644 --- a/extauthz/internal/server/config/config_test.go +++ b/extauthz/internal/server/config/config_test.go @@ -16,9 +16,9 @@ func TestConfig(t *testing.T) { require.Len(t, cfg.ExtractionSet, 1) require.Equal(t, "test", cfg.ExtractionSet[0].Name) require.Equal(t, "mock", cfg.ExtractionSet[0].User.Type) - require.Equal(t, "my_user", cfg.ExtractionSet[0].User.Config.(*extractor.MockConfig).User) + require.Equal(t, "subject:my_user", cfg.ExtractionSet[0].User.Config.(*extractor.MockConfig).Value) require.Equal(t, "mock", cfg.ExtractionSet[0].Object.Type) - require.Equal(t, "my_object", cfg.ExtractionSet[0].Object.Config.(*extractor.MockConfig).Object) + require.Equal(t, "resource:my_object", cfg.ExtractionSet[0].Object.Config.(*extractor.MockConfig).Value) require.Equal(t, "mock", cfg.ExtractionSet[0].Relation.Type) - require.Equal(t, "my_relation", cfg.ExtractionSet[0].Relation.Config.(*extractor.MockConfig).Relation) + require.Equal(t, "my_relation", cfg.ExtractionSet[0].Relation.Config.(*extractor.MockConfig).Value) } diff --git a/extauthz/internal/server/config/testdata/config.yaml b/extauthz/internal/server/config/testdata/config.yaml index 58c3d80..9e68fb4 100644 --- a/extauthz/internal/server/config/testdata/config.yaml +++ b/extauthz/internal/server/config/testdata/config.yaml @@ -8,12 +8,12 @@ extraction_sets: user: type: mock config: - user: my_user + value: subject:my_user object: type: mock config: - object: my_object + value: resource:my_object relation: type: mock config: - relation: my_relation + value: my_relation From ddca99580ceec4ebd770687e659c34594c4fa877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Tue, 27 Aug 2024 08:38:27 +0200 Subject: [PATCH 4/5] chore: fixes unmarshaling for spiffe. --- extauthz/internal/extractor/spiffe.go | 4 ++-- extauthz/internal/extractor/spiffe_test.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extauthz/internal/extractor/spiffe.go b/extauthz/internal/extractor/spiffe.go index 33b9c7c..dc10c9e 100644 --- a/extauthz/internal/extractor/spiffe.go +++ b/extauthz/internal/extractor/spiffe.go @@ -51,7 +51,7 @@ const ( ) type SpiffeConfig struct { - _type spiffeExtractionType `yaml:"type"` + Type spiffeExtractionType `yaml:"type"` } func NewSpiffe(config *SpiffeConfig) Extractor { @@ -61,7 +61,7 @@ func NewSpiffe(config *SpiffeConfig) Extractor { var prefix string - if config._type == spiffeTypeUser { + if config.Type == spiffeTypeUser { prefix = spiffeCurrentClientKey } else { prefix = spiffeKey diff --git a/extauthz/internal/extractor/spiffe_test.go b/extauthz/internal/extractor/spiffe_test.go index e20f3c4..05e16b9 100644 --- a/extauthz/internal/extractor/spiffe_test.go +++ b/extauthz/internal/extractor/spiffe_test.go @@ -12,7 +12,7 @@ import ( func TestSpiffeUnmarshal(t *testing.T) { c := &SpiffeConfig{} yaml.Unmarshal([]byte("type: user"), c) - require.Equal(t, spiffeTypeUser, c._type) + require.Equal(t, spiffeTypeUser, c.Type) } func TestSpiffeExtractor(t *testing.T) { @@ -34,7 +34,7 @@ func TestSpiffeExtractor(t *testing.T) { t.Run("success for subject", func(t *testing.T) { extractor := NewSpiffe(&SpiffeConfig{ - _type: spiffeTypeUser, + Type: spiffeTypeUser, }) extraction, found, err := extractor(context.Background(), &authv3.CheckRequest{ @@ -56,7 +56,7 @@ func TestSpiffeExtractor(t *testing.T) { t.Run("success for object", func(t *testing.T) { extractor := NewSpiffe(&SpiffeConfig{ - _type: spiffeTypeObject, + Type: spiffeTypeObject, }) extraction, found, err := extractor(context.Background(), &authv3.CheckRequest{ From e1c82f146ae9d27ecb23940f9ffc41ce991a7e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Tue, 27 Aug 2024 08:59:08 +0200 Subject: [PATCH 5/5] tests: fixes. --- extauthz/cmd/extauthz/main_test.go | 2 +- extauthz/internal/extractor/spiffe_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extauthz/cmd/extauthz/main_test.go b/extauthz/cmd/extauthz/main_test.go index 844bdef..1852e80 100644 --- a/extauthz/cmd/extauthz/main_test.go +++ b/extauthz/cmd/extauthz/main_test.go @@ -66,7 +66,7 @@ func TestNoUserExtractedFails(t *testing.T) { e := extractor.ExtractorKit{ Name: "extauthz", User: func(ctx context.Context, value *auth_pb.CheckRequest) (extractor.Extraction, bool, error) { - return nil, false, expectedErr + return extractor.Extraction{}, false, expectedErr }, } diff --git a/extauthz/internal/extractor/spiffe_test.go b/extauthz/internal/extractor/spiffe_test.go index 05e16b9..c29f5a0 100644 --- a/extauthz/internal/extractor/spiffe_test.go +++ b/extauthz/internal/extractor/spiffe_test.go @@ -29,7 +29,7 @@ func TestSpiffeExtractor(t *testing.T) { require.NoError(t, err) require.False(t, found) - require.Nil(t, extraction) + require.Empty(t, extraction.Value) }) t.Run("success for subject", func(t *testing.T) {