Skip to content

Commit b069fab

Browse files
authored
Merge pull request #20 from ConductorOne/goldschmidt/provisioning
2 parents 730bb97 + 68b456a commit b069fab

File tree

8 files changed

+667
-79
lines changed

8 files changed

+667
-79
lines changed

go.mod

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ module github.com/conductorone/baton-sql-server
22

33
go 1.23.4
44

5-
toolchain go1.24.2
6-
75
require (
86
github.com/conductorone/baton-sdk v0.2.98
97
github.com/ennyjfrick/ruleguard-logfatal v0.0.2

pkg/connector/database.go

Lines changed: 104 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -24,80 +24,6 @@ type databaseSyncer struct {
2424
client *mssqldb.Client
2525
}
2626

27-
var databasePermissions = map[string]string{
28-
"AADS": "Alter Any Database Event Session",
29-
"AAMK": "Alter Any Mask",
30-
"AEDS": "Alter Any External Data Source",
31-
"AEFF": "Alter Any External File Format",
32-
"AL": "Alter",
33-
"ALAK": "Alter Any Asymmetric Key",
34-
"ALAR": "Alter Any Application Role",
35-
"ALAS": "Alter Any Assembly",
36-
"ALCF": "Alter Any Certificate",
37-
"ALDS": "Alter Any Dataspace",
38-
"ALED": "Alter Any Database Event Notification",
39-
"ALFT": "Alter Any Fulltext Catalog",
40-
"ALMT": "Alter Any Message Type",
41-
"ALRL": "Alter Any Role",
42-
"ALRT": "Alter Any Route",
43-
"ALSB": "Alter Any Remote Service Binding",
44-
"ALSC": "Alter Any Contract",
45-
"ALSK": "Alter Any Symmetric Key",
46-
"ALSM": "Alter Any Schema",
47-
"ALSV": "Alter Any Service",
48-
"ALTG": "Alter Any Database DDL Trigger",
49-
"ALUS": "Alter Any User",
50-
"AUTH": "Authenticate",
51-
"BADB": "Backup Database",
52-
"BALO": "Backup Log",
53-
"CL": "Control",
54-
"CO": "Connect",
55-
"CORP": "Connect Replication",
56-
"CP": "Checkpoint",
57-
"CRAG": "Create Aggregate",
58-
"CRAK": "Create Asymmetric Key",
59-
"CRAS": "Create Certificate",
60-
"CRDB": "Create Fatabase",
61-
"CRDF": "Create Default",
62-
"CRED": "Create Database DDL Event Notification",
63-
"CRFN": "Create Function",
64-
"CRFT": "Create Fulltext Catalog",
65-
"CRMT": "Create Message Type",
66-
"CRPR": "Create Procedure",
67-
"CRQU": "Create Queue",
68-
"CRRL": "Create Role",
69-
"CRRT": "Create Route",
70-
"CRRU": "Create Rule",
71-
"CRSB": "Create Remote Service Binding",
72-
"CRSC": "Create contract",
73-
"CRSK": "Create symmetric key",
74-
"CRSM": "Create Schema",
75-
"CRSN": "Create Synonym",
76-
"CRSO": "Create Sequence",
77-
"CRSV": "Create Service",
78-
"CRTB": "Create Table",
79-
"CRTY": "Create Type",
80-
"CRVW": "Create View",
81-
"CRXS": "Create XML Schema Collection",
82-
"DL": "Delete",
83-
"DABO": "Administer Database Bulk Operations",
84-
"EAES": "Execute Any External Script",
85-
"EX": "Execute",
86-
"IN": "Insert",
87-
"RC": "Receive Object",
88-
"RF": "References",
89-
"SL": "Select",
90-
"SPLN": "Showplan",
91-
"SUQN": "Subscribe Query Notifications",
92-
"TO": "Take Ownership",
93-
"UP": "Update",
94-
"VW": "View Definition",
95-
"VWCK": "View Any Column Encryption Key Definition",
96-
"VWCM": "View Any Column Master Key Definition",
97-
"VWCT": "View Change Tracking",
98-
"VWDS": "View Database State Database",
99-
}
100-
10127
func (d *databaseSyncer) ResourceType(ctx context.Context) *v2.ResourceType {
10228
return d.resourceType
10329
}
@@ -137,7 +63,7 @@ func (d *databaseSyncer) List(ctx context.Context, parentResourceID *v2.Resource
13763
func (d *databaseSyncer) Entitlements(ctx context.Context, resource *v2.Resource, pToken *pagination.Token) ([]*v2.Entitlement, string, annotations.Annotations, error) {
13864
var ret []*v2.Entitlement
13965

140-
for key, name := range databasePermissions {
66+
for key, name := range mssqldb.DatabasePermissions {
14167
grantSlug := fmt.Sprintf("%s (With Grant)", name)
14268
ret = append(ret,
14369
&v2.Entitlement{
@@ -146,13 +72,15 @@ func (d *databaseSyncer) Entitlements(ctx context.Context, resource *v2.Resource
14672
Slug: name,
14773
Purpose: v2.Entitlement_PURPOSE_VALUE_PERMISSION,
14874
Resource: resource,
75+
GrantableTo: []*v2.ResourceType{resourceTypeUser},
14976
},
15077
&v2.Entitlement{
15178
Id: enTypes.NewEntitlementID(resource, key+"-grant"),
15279
DisplayName: grantSlug,
15380
Slug: grantSlug,
15481
Purpose: v2.Entitlement_PURPOSE_VALUE_PERMISSION,
15582
Resource: resource,
83+
GrantableTo: []*v2.ResourceType{resourceTypeUser},
15684
})
15785
}
15886

@@ -182,7 +110,7 @@ func (d *databaseSyncer) Grants(ctx context.Context, resource *v2.Resource, pTok
182110
perms := strings.Split(p.Permissions, ",")
183111
for _, perm := range perms {
184112
perm = strings.TrimSpace(perm)
185-
if _, ok := databasePermissions[perm]; ok {
113+
if _, ok := mssqldb.DatabasePermissions[perm]; ok {
186114
rt, err := resourceTypeFromDatabasePrincipal(p.PrincipalType)
187115
if err != nil {
188116
l.Error("unexpected principal type", zap.String("principal_type", p.PrincipalType))
@@ -232,6 +160,106 @@ func (d *databaseSyncer) Grants(ctx context.Context, resource *v2.Resource, pTok
232160
return ret, nextPageToken, nil, nil
233161
}
234162

163+
func (d *databaseSyncer) Grant(ctx context.Context, resource *v2.Resource, entitlement *v2.Entitlement) ([]*v2.Grant, annotations.Annotations, error) {
164+
l := ctxzap.Extract(ctx)
165+
166+
if resource.Id.ResourceType != resourceTypeUser.Id {
167+
return nil, nil, fmt.Errorf("resource type %s is not supported for granting", resource.Id.ResourceType)
168+
}
169+
170+
splitId := strings.Split(entitlement.Id, ":")
171+
if len(splitId) != 3 {
172+
return nil, nil, fmt.Errorf("unexpected entitlement id: %s", entitlement.Id)
173+
}
174+
175+
dbId, err := strconv.ParseInt(splitId[1], 10, 64)
176+
if err != nil {
177+
return nil, nil, fmt.Errorf("unexpected database id: %s", splitId[1])
178+
}
179+
180+
permission := splitId[2]
181+
isGrant := strings.Contains(permission, "-grant")
182+
if isGrant {
183+
permission = strings.Replace(permission, "-grant", "", 1)
184+
}
185+
186+
database, err := d.client.GetDatabase(ctx, dbId)
187+
if err != nil {
188+
return nil, nil, err
189+
}
190+
191+
user, err := d.client.GetUserPrincipal(ctx, resource.Id.Resource)
192+
if err != nil {
193+
return nil, nil, err
194+
}
195+
196+
dbUser, err := d.client.GetUserFromDb(ctx, database.Name, resource.Id.Resource)
197+
if err != nil {
198+
return nil, nil, err
199+
}
200+
201+
if dbUser == nil {
202+
l.Info("user not found in database, creating user for principal", zap.String("user", resource.Id.Resource))
203+
204+
err = d.client.CreateDatabaseUserForPrincipal(ctx, database.Name, user.Name)
205+
if err != nil {
206+
return nil, nil, err
207+
}
208+
}
209+
210+
err = d.client.GrantPermissionOnDatabase(ctx, permission, database.Name, user.Name)
211+
if err != nil {
212+
return nil, nil, err
213+
}
214+
215+
entitlementName := permission
216+
if isGrant {
217+
entitlementName = permission + "-grant"
218+
}
219+
220+
newGrant := grTypes.NewGrant(resource, entitlementName, &v2.ResourceId{
221+
Resource: user.ID,
222+
ResourceType: resourceTypeUser.Id,
223+
})
224+
225+
return []*v2.Grant{newGrant}, nil, nil
226+
}
227+
228+
func (d *databaseSyncer) Revoke(ctx context.Context, grant *v2.Grant) (annotations.Annotations, error) {
229+
l := ctxzap.Extract(ctx)
230+
231+
if grant.Principal.Id.ResourceType != resourceTypeUser.Id {
232+
return nil, fmt.Errorf("resource type %s is not supported for revoking", grant.Principal.Id.ResourceType)
233+
}
234+
235+
splitId := strings.Split(grant.Entitlement.Id, ":")
236+
237+
dbId, err := strconv.ParseInt(splitId[1], 10, 64)
238+
if err != nil {
239+
return nil, fmt.Errorf("unexpected database id: %s", splitId[1])
240+
}
241+
242+
permission := strings.Replace(splitId[2], "-grant", "", 1)
243+
244+
database, err := d.client.GetDatabase(ctx, dbId)
245+
if err != nil {
246+
return nil, err
247+
}
248+
249+
user, err := d.client.GetUserPrincipal(ctx, grant.Principal.Id.Resource)
250+
if err != nil {
251+
return nil, err
252+
}
253+
254+
err = d.client.RevokePermissionOnDatabase(ctx, permission, database.Name, user.Name)
255+
if err != nil {
256+
return nil, err
257+
}
258+
259+
l.Debug("revoked permission", zap.String("permission", permission), zap.String("user", user.Name), zap.String("database", database.Name))
260+
return nil, nil
261+
}
262+
235263
func newDatabaseSyncer(ctx context.Context, c *mssqldb.Client) *databaseSyncer {
236264
return &databaseSyncer{
237265
resourceType: resourceTypeDatabase,

pkg/connector/database_role.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,95 @@ func (d *databaseRolePrincipalSyncer) Grants(
233233
return ret, npt, nil, nil
234234
}
235235

236+
func (d *databaseRolePrincipalSyncer) Grant(ctx context.Context, resource *v2.Resource, entitlement *v2.Entitlement) ([]*v2.Grant, annotations.Annotations, error) {
237+
var err error
238+
239+
l := ctxzap.Extract(ctx)
240+
241+
if resource.Id.ResourceType != resourceTypeUser.Id {
242+
return nil, nil, fmt.Errorf("resource type %s is not supported for granting", resource.Id.ResourceType)
243+
}
244+
245+
// database-role:baton_test:6:member
246+
splitId := strings.Split(entitlement.Id, ":")
247+
if len(splitId) != 4 {
248+
return nil, nil, fmt.Errorf("unexpected entitlement id: %s", entitlement.Id)
249+
}
250+
251+
dbName := splitId[1]
252+
roleId := splitId[2]
253+
254+
var role *mssqldb.RoleModel
255+
256+
role, err = d.client.GetDatabaseRole(ctx, dbName, roleId)
257+
if err != nil {
258+
return nil, nil, err
259+
}
260+
261+
dbUser, err := d.client.GetUserFromDb(ctx, dbName, resource.Id.Resource)
262+
if err != nil {
263+
return nil, nil, err
264+
}
265+
266+
if dbUser == nil {
267+
l.Info("user not found in database, creating user for principal", zap.String("user", resource.Id.Resource))
268+
269+
user, err := d.client.GetUserPrincipal(ctx, resource.Id.Resource)
270+
if err != nil {
271+
return nil, nil, err
272+
}
273+
274+
err = d.client.CreateDatabaseUserForPrincipal(ctx, dbName, user.Name)
275+
if err != nil {
276+
return nil, nil, err
277+
}
278+
}
279+
280+
err = d.client.AddUserToDatabaseRole(ctx, role.Name, dbName, resource.Id.Resource)
281+
if err != nil {
282+
return nil, nil, err
283+
}
284+
285+
grants := []*v2.Grant{
286+
grTypes.NewGrant(resource, "member", &v2.ResourceId{
287+
Resource: resource.Id.Resource,
288+
ResourceType: resourceTypeUser.Id,
289+
}),
290+
}
291+
292+
return grants, nil, nil
293+
}
294+
295+
func (d *databaseRolePrincipalSyncer) Revoke(ctx context.Context, grant *v2.Grant) (annotations.Annotations, error) {
296+
userId := grant.Principal.Id.Resource
297+
298+
user, err := d.client.GetUserPrincipal(ctx, userId)
299+
if err != nil {
300+
return nil, err
301+
}
302+
303+
// database-role:baton_test:6:member
304+
splitId := strings.Split(grant.Entitlement.Id, ":")
305+
if len(splitId) != 4 {
306+
return nil, fmt.Errorf("unexpected entitlement id: %s", grant.Entitlement.Id)
307+
}
308+
309+
dbName := splitId[1]
310+
roleId := splitId[2]
311+
312+
role, err := d.client.GetDatabaseRole(ctx, dbName, roleId)
313+
if err != nil {
314+
return nil, err
315+
}
316+
317+
err = d.client.RevokeUserToDatabaseRole(ctx, role.Name, dbName, user.Name)
318+
if err != nil {
319+
return nil, err
320+
}
321+
322+
return nil, err
323+
}
324+
236325
func newDatabaseRolePrincipalSyncer(ctx context.Context, c *mssqldb.Client) *databaseRolePrincipalSyncer {
237326
return &databaseRolePrincipalSyncer{
238327
resourceType: resourceTypeDatabaseRole,

0 commit comments

Comments
 (0)