Skip to content

Commit 1b606bd

Browse files
authored
Merge pull request #24 from ConductorOne/jirwin/multiple-entitlements-grants
Add support for multiple entitlements or grants per query
2 parents 128835f + 3386895 commit 1b606bd

File tree

6 files changed

+60
-49
lines changed

6 files changed

+60
-49
lines changed

examples/wordpress.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,10 @@ resource_types:
7777
WHERE um.meta_key = 'wp_capabilities'
7878
LIMIT ?<Limit> OFFSET ?<Offset>
7979
map:
80-
skip_if: "(size(phpDeserializeStringArray(string(.role_name))) < 1 || phpDeserializeStringArray(string(.role_name))[0] != resource.ID)"
81-
principal_id: ".user_id"
82-
principal_type: "user"
83-
entitlement_id: "member"
80+
- skip_if: "(size(phpDeserializeStringArray(string(.role_name))) < 1 || phpDeserializeStringArray(string(.role_name))[0] != resource.ID)"
81+
principal_id: ".user_id"
82+
principal_type: "user"
83+
entitlement_id: "member"
8484
pagination:
8585
strategy: "offset"
8686
primary_key: "user_id"

pkg/bsql/config.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ import (
99
)
1010

1111
type Config struct {
12-
AppName string `yaml:"app_name" json:"app_name"`
13-
Connect DatabaseConfig `yaml:"connect" json:"connect"`
14-
ResourceTypes map[string]ResourceType `yaml:"resource_types" json:"resource_types"`
12+
AppName string `yaml:"app_name" json:"app_name"`
13+
AppDescription string `yaml:"app_description" json:"app_description"`
14+
Connect DatabaseConfig `yaml:"connect" json:"connect"`
15+
ResourceTypes map[string]ResourceType `yaml:"resource_types" json:"resource_types"`
1516
}
1617

1718
type DatabaseConfig struct {
@@ -86,9 +87,9 @@ type Pagination struct {
8687
}
8788

8889
type EntitlementsQuery struct {
89-
Query string `yaml:"query" json:"query"`
90-
Pagination *Pagination `yaml:"pagination" json:"pagination"`
91-
Map *EntitlementMapping `yaml:"map" json:"map"`
90+
Query string `yaml:"query" json:"query"`
91+
Pagination *Pagination `yaml:"pagination" json:"pagination"`
92+
Map []*EntitlementMapping `yaml:"map" json:"map"`
9293
}
9394

9495
type EntitlementMapping struct {
@@ -103,9 +104,9 @@ type EntitlementMapping struct {
103104
}
104105

105106
type GrantsQuery struct {
106-
Query string `yaml:"query" json:"query"`
107-
Pagination *Pagination `yaml:"pagination" json:"pagination"`
108-
Map *GrantMapping `yaml:"map" json:"map"`
107+
Query string `yaml:"query" json:"query"`
108+
Pagination *Pagination `yaml:"pagination" json:"pagination"`
109+
Map []*GrantMapping `yaml:"map" json:"map"`
109110
}
110111

111112
type GrantMapping struct {

pkg/bsql/config_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ func TestParse(t *testing.T) {
9393
// Validate `roleResourceType` grants
9494
require.NotNil(t, roleResourceType.Grants)
9595
require.Len(t, roleResourceType.Grants, 1)
96-
require.Equal(t, ".user_id", roleResourceType.Grants[0].Map.PrincipalId)
97-
require.Equal(t, "user", roleResourceType.Grants[0].Map.PrincipalType)
98-
require.Equal(t, "member", roleResourceType.Grants[0].Map.Entitlement)
96+
require.Equal(t, ".user_id", roleResourceType.Grants[0].Map[0].PrincipalId)
97+
require.Equal(t, "user", roleResourceType.Grants[0].Map[0].PrincipalType)
98+
require.Equal(t, "member", roleResourceType.Grants[0].Map[0].Entitlement)
9999
require.Equal(t, "offset", roleResourceType.Grants[0].Pagination.Strategy)
100100
require.Equal(t, "user_id", roleResourceType.Grants[0].Pagination.PrimaryKey)
101101
},

pkg/bsql/entitlements.go

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -80,17 +80,17 @@ func (s *SQLSyncer) dynamicEntitlements(ctx context.Context, resource *v2.Resour
8080
var ret []*v2.Entitlement
8181

8282
npt, err := s.runQuery(ctx, pToken, s.config.Entitlements.Query, s.config.Entitlements.Pagination, func(ctx context.Context, rowMap map[string]any) (bool, error) {
83-
r, err := s.mapEntitlement(ctx, resource, rowMap)
84-
if err != nil {
85-
return false, err
86-
}
87-
// No error and no entitlement means we should skip this row
88-
if r == nil {
89-
return true, nil
90-
}
83+
for _, mapping := range s.config.Entitlements.Map {
84+
r, ok, err := s.mapEntitlement(ctx, resource, mapping, rowMap)
85+
if err != nil {
86+
return false, err
87+
}
9188

92-
r.Resource = resource
93-
ret = append(ret, r)
89+
if ok {
90+
r.Resource = resource
91+
ret = append(ret, r)
92+
}
93+
}
9494
return true, nil
9595
})
9696
if err != nil {
@@ -100,7 +100,7 @@ func (s *SQLSyncer) dynamicEntitlements(ctx context.Context, resource *v2.Resour
100100
return ret, npt, nil, nil
101101
}
102102

103-
func (s *SQLSyncer) mapEntitlement(ctx context.Context, resource *v2.Resource, rowMap map[string]any) (*v2.Entitlement, error) {
103+
func (s *SQLSyncer) mapEntitlement(ctx context.Context, resource *v2.Resource, mappings *EntitlementMapping, rowMap map[string]any) (*v2.Entitlement, bool, error) {
104104
ret := &v2.Entitlement{}
105105

106106
inputs := s.env.BaseInputsWithResource(rowMap, resource)
@@ -111,48 +111,46 @@ func (s *SQLSyncer) mapEntitlement(ctx context.Context, resource *v2.Resource, r
111111
"DisplayName": resource.DisplayName,
112112
}
113113

114-
mappings := s.config.Entitlements.Map
115-
116114
if mappings.SkipIf != "" {
117115
skip, err := s.env.EvaluateBool(ctx, mappings.SkipIf, inputs)
118116
if err != nil {
119-
return nil, err
117+
return nil, false, err
120118
}
121119

122120
if skip {
123-
return nil, nil
121+
return nil, false, nil
124122
}
125123
}
126124

127125
if mappings.Id == "" {
128-
return nil, fmt.Errorf("entitlements mapping id is required")
126+
return nil, false, fmt.Errorf("entitlements mapping id is required")
129127
}
130128
v, err := s.env.EvaluateString(ctx, mappings.Id, inputs)
131129
if err != nil {
132-
return nil, err
130+
return nil, false, err
133131
}
134132
ret.Id = sdkEntitlement.NewEntitlementID(resource, v)
135133

136134
if mappings.DisplayName == "" {
137-
return nil, fmt.Errorf("entitlements mapping display_name is required")
135+
return nil, false, fmt.Errorf("entitlements mapping display_name is required")
138136
}
139137
v, err = s.env.EvaluateString(ctx, mappings.DisplayName, inputs)
140138
if err != nil {
141-
return nil, err
139+
return nil, false, err
142140
}
143141
ret.DisplayName = v
144142

145143
if mappings.Description != "" {
146144
v, err = s.env.EvaluateString(ctx, mappings.Description, inputs)
147145
if err != nil {
148-
return nil, err
146+
return nil, false, err
149147
}
150148
ret.Description = v
151149
}
152150

153151
resourceTypes, err := s.fullConfig.GetResourceTypes(ctx)
154152
if err != nil {
155-
return nil, err
153+
return nil, false, err
156154
}
157155
for _, rt := range mappings.GrantableTo {
158156
for _, r := range resourceTypes {
@@ -164,19 +162,19 @@ func (s *SQLSyncer) mapEntitlement(ctx context.Context, resource *v2.Resource, r
164162

165163
// TODO(jirwin): Should entitlement slugs be required?
166164
if mappings.Slug == "" {
167-
return nil, fmt.Errorf("entitlements mapping slug is required")
165+
return nil, false, fmt.Errorf("entitlements mapping slug is required")
168166
}
169167
v, err = s.env.EvaluateString(ctx, mappings.Slug, inputs)
170168
if err != nil {
171-
return nil, err
169+
return nil, false, err
172170
}
173171
ret.Slug = v
174172

175173
var purpose string
176174
if mappings.Purpose != "" {
177175
purpose, err = s.env.EvaluateString(ctx, mappings.Purpose, inputs)
178176
if err != nil {
179-
return nil, err
177+
return nil, false, err
180178
}
181179
}
182180
switch purpose {
@@ -194,5 +192,5 @@ func (s *SQLSyncer) mapEntitlement(ctx context.Context, resource *v2.Resource, r
194192
}
195193
ret.Annotations = annos
196194

197-
return ret, nil
195+
return ret, true, nil
198196
}

pkg/bsql/grants.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,15 @@ func (s *SQLSyncer) listGrants(ctx context.Context, resource *v2.Resource, pToke
7676
var ret []*v2.Grant
7777

7878
npt, err := s.runQuery(ctx, pToken, grantConfig.Query, grantConfig.Pagination, func(ctx context.Context, rowMap map[string]any) (bool, error) {
79-
g, ok, err := s.mapGrant(ctx, resource, grantConfig.Map, rowMap)
80-
if err != nil {
81-
return false, err
82-
}
83-
if ok {
84-
ret = append(ret, g)
79+
for _, mapping := range grantConfig.Map {
80+
g, ok, err := s.mapGrant(ctx, resource, mapping, rowMap)
81+
if err != nil {
82+
return false, err
83+
}
84+
85+
if ok {
86+
ret = append(ret, g)
87+
}
8588
}
8689
return true, nil
8790
})

pkg/connector/connector.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,19 @@ func (c *Connector) Asset(ctx context.Context, asset *v2.AssetRef) (string, io.R
5151

5252
// Metadata returns metadata about the connector.
5353
func (c *Connector) Metadata(ctx context.Context) (*v2.ConnectorMetadata, error) {
54-
return &v2.ConnectorMetadata{
54+
md := &v2.ConnectorMetadata{
5555
DisplayName: "Generic SQL Connector",
5656
Description: "A baton connector that allows you to sync from an arbitrary SQL database",
57-
}, nil
57+
}
58+
59+
if c.config.AppName != "" {
60+
md.DisplayName = c.config.AppName
61+
}
62+
63+
if c.config.AppDescription != "" {
64+
md.Description = c.config.AppDescription
65+
}
66+
return md, nil
5867
}
5968

6069
// Validate is called to ensure that the connector is properly configured. It should exercise any API credentials

0 commit comments

Comments
 (0)