diff --git a/pkg/bsql/config.go b/pkg/bsql/config.go index e0ebcaa6..159851c4 100644 --- a/pkg/bsql/config.go +++ b/pkg/bsql/config.go @@ -134,6 +134,9 @@ type ResourceMapping struct { // Annotations includes additional metadata such as entitlement immutability and external links. Annotations *Annotations `yaml:"annotations" json:"annotations"` + + // SkipEntitlementsAndGrants is a CEL expression that skips entitlement and grant processing when true. + SkipEntitlementsAndGrants string `yaml:"skip_entitlements_and_grants" json:"skip_entitlements_and_grants"` } // Annotations holds extra metadata for resource or grant mappings. diff --git a/pkg/bsql/config_test.go b/pkg/bsql/config_test.go index f0b385b5..c94c6a83 100644 --- a/pkg/bsql/config_test.go +++ b/pkg/bsql/config_test.go @@ -165,3 +165,53 @@ func TestParse(t *testing.T) { }) } } + +func TestParse_SparseACLs(t *testing.T) { + configYAML := ` +app_name: Sparse ACL Test +connect: + dsn: "postgres://localhost/test" +resource_types: + database: + name: "Database" + list: + query: "SELECT id, name, is_public FROM databases LIMIT ? OFFSET ?" + map: + id: ".id" + display_name: ".name" + skip_entitlements_and_grants: ".is_public == true" + traits: + app: + profile: + name: ".name" + pagination: + strategy: "offset" + primary_key: "id" + user: + name: "User" + skip_entitlements_and_grants: true + list: + query: "SELECT id, name FROM users LIMIT ? OFFSET ?" + map: + id: ".id" + display_name: ".name" + traits: + user: + emails: + - ".email" + status: "active" + pagination: + strategy: "offset" + primary_key: "id" +` + c, err := Parse([]byte(configYAML)) + require.NoError(t, err) + + dbRT := c.ResourceTypes["database"] + require.Equal(t, ".is_public == true", dbRT.List.Map.SkipEntitlementsAndGrants) + require.False(t, dbRT.SkipEntitlementsAndGrants) + + userRT := c.ResourceTypes["user"] + require.Empty(t, userRT.List.Map.SkipEntitlementsAndGrants) + require.True(t, userRT.SkipEntitlementsAndGrants) +} diff --git a/pkg/bsql/resource_types_test.go b/pkg/bsql/resource_types_test.go index 6de6ab3e..f1540bbf 100644 --- a/pkg/bsql/resource_types_test.go +++ b/pkg/bsql/resource_types_test.go @@ -4,6 +4,7 @@ import ( "testing" v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2" + "github.com/conductorone/baton-sdk/pkg/annotations" "github.com/stretchr/testify/require" ) @@ -33,3 +34,61 @@ func TestConfig_GetResourceTypes_wordpress(t *testing.T) { } } } + +func TestConfig_GetResourceTypes_SkipEntitlementsAndGrants(t *testing.T) { + ctx := t.Context() + configYAML := ` +app_name: Skip Test +connect: + dsn: "postgres://localhost/test" +resource_types: + database: + name: "Database" + skip_entitlements_and_grants: true + list: + query: "SELECT id, name FROM databases LIMIT ? OFFSET ?" + map: + id: ".id" + display_name: ".name" + traits: + app: + profile: + name: ".name" + pagination: + strategy: "offset" + primary_key: "id" + user: + name: "User" + list: + query: "SELECT id, name FROM users LIMIT ? OFFSET ?" + map: + id: ".id" + display_name: ".name" + traits: + user: + emails: + - ".email" + status: "active" + pagination: + strategy: "offset" + primary_key: "id" +` + c, err := Parse([]byte(configYAML)) + require.NoError(t, err) + + ret, err := c.GetResourceTypes(ctx) + require.NoError(t, err) + require.Len(t, ret, 2) + + for _, rt := range ret { + annos := annotations.Annotations(rt.Annotations) + switch rt.Id { + case "database": + require.True(t, annos.Contains(&v2.SkipEntitlementsAndGrants{})) + case "user": + require.False(t, annos.Contains(&v2.SkipEntitlementsAndGrants{})) + default: + require.Fail(t, "unexpected resource type") + } + } +} diff --git a/pkg/bsql/resources.go b/pkg/bsql/resources.go index 4713c10f..c3278c6e 100644 --- a/pkg/bsql/resources.go +++ b/pkg/bsql/resources.go @@ -461,5 +461,18 @@ func (s *SQLSyncer) getMappedResource(ctx context.Context, r *v2.Resource, rowMa r.Description = v } + if mapping.SkipEntitlementsAndGrants != "" { + skip, err := s.env.EvaluateBool(ctx, mapping.SkipEntitlementsAndGrants, inputs) + if err != nil { + return err + } + + if skip { + annos := annotations.Annotations(r.Annotations) + annos.Update(&v2.SkipEntitlementsAndGrants{}) + r.Annotations = annos + } + } + return nil }