@@ -7,11 +7,18 @@ import (
77 "slices"
88 "strings"
99
10+ "github.com/databricks/cli/libs/structs/structpath"
1011 "github.com/databricks/cli/libs/structs/structvar"
1112 "github.com/databricks/databricks-sdk-go"
1213 "github.com/databricks/databricks-sdk-go/service/catalog"
1314)
1415
16+ const (
17+ grantsNodeSuffix = ".grants"
18+ grantsPrincipalKey = "principal"
19+ grantsFullNameError = "internal error: grants full_name must be resolved before deployment"
20+ )
21+
1522var grantResourceToSecurableType = map [string ]string {
1623 "catalogs" : "catalog" ,
1724 "schemas" : "schema" ,
@@ -27,9 +34,9 @@ type GrantsState struct {
2734}
2835
2936func PrepareGrantsInputConfig (inputConfig any , node string ) (* structvar.StructVar , error ) {
30- baseNode , ok := strings .CutSuffix (node , ".grants" )
37+ baseNode , ok := strings .CutSuffix (node , grantsNodeSuffix )
3138 if ! ok {
32- return nil , fmt .Errorf ("internal error: node %q does not end with .grants " , node )
39+ return nil , fmt .Errorf ("internal error: node %q does not end with %s " , node , grantsNodeSuffix )
3340 }
3441
3542 resourceType , err := extractGrantResourceType (node )
@@ -49,7 +56,7 @@ func PrepareGrantsInputConfig(inputConfig any, node string) (*structvar.StructVa
4956
5057 // Backend sorts privileges, so we sort here as well.
5158 for i := range * grantsPtr {
52- sortPriviliges ((* grantsPtr )[i ].Privileges )
59+ sortPrivileges ((* grantsPtr )[i ].Privileges )
5360 }
5461
5562 return & structvar.StructVar {
@@ -77,7 +84,7 @@ func (*ResourceGrants) PrepareState(state *GrantsState) *GrantsState {
7784}
7885
7986func grantKey (x catalog.PrivilegeAssignment ) (string , string ) {
80- return "principal" , x .Principal
87+ return grantsPrincipalKey , x .Principal
8188}
8289
8390func (* ResourceGrants ) KeyedSlices () map [string ]any {
@@ -107,23 +114,16 @@ func (r *ResourceGrants) DoRead(ctx context.Context, id string) (*GrantsState, e
107114}
108115
109116func (r * ResourceGrants ) DoCreate (ctx context.Context , state * GrantsState ) (string , * GrantsState , error ) {
110- err := r .applyGrants (ctx , state , nil )
117+ err := r .updateGrants (ctx , state , nil )
111118 if err != nil {
112119 return "" , nil , err
113120 }
114121
115122 return makeGrantsID (state .SecurableType , state .FullName ), nil , nil
116123}
117124
118- func (r * ResourceGrants ) DoUpdate (ctx context.Context , _ string , state * GrantsState , _ Changes ) (* GrantsState , error ) {
119- if state .FullName == "" {
120- return nil , errors .New ("internal error: grants full_name must be resolved before deployment" )
121- }
122- currentAssignments , err := r .listGrants (ctx , state .SecurableType , state .FullName )
123- if err != nil {
124- return nil , err
125- }
126- return nil , r .applyGrants (ctx , state , currentAssignments )
125+ func (r * ResourceGrants ) DoUpdate (ctx context.Context , _ string , state * GrantsState , changes Changes ) (* GrantsState , error ) {
126+ return nil , r .updateGrants (ctx , state , changes )
127127}
128128
129129func (r * ResourceGrants ) DoDelete (ctx context.Context , id string ) error {
@@ -132,13 +132,33 @@ func (r *ResourceGrants) DoDelete(ctx context.Context, id string) error {
132132 return nil
133133}
134134
135- func (r * ResourceGrants ) applyGrants (ctx context.Context , state * GrantsState , currentAssignments []catalog.PrivilegeAssignment ) error {
135+ func (r * ResourceGrants ) updateGrants (ctx context.Context , state * GrantsState , changes Changes ) error {
136+ req , err := buildGrantUpdateRequest (state , changes )
137+ if err != nil {
138+ return err
139+ }
140+ _ , err = r .client .Grants .Update (ctx , req )
141+ return err
142+ }
143+
144+ func buildGrantUpdateRequest (state * GrantsState , changes Changes ) (catalog.UpdatePermissions , error ) {
136145 if state .FullName == "" {
137- return errors .New ("internal error: grants full_name must be resolved before deployment" )
146+ return catalog. UpdatePermissions {}, errors .New (grantsFullNameError )
138147 }
148+ removedPrincipals , err := removedGrantPrincipals (changes , state .EmbeddedSlice )
149+ if err != nil {
150+ return catalog.UpdatePermissions {}, err
151+ }
152+ return catalog.UpdatePermissions {
153+ SecurableType : state .SecurableType ,
154+ FullName : state .FullName ,
155+ Changes : buildGrantChanges (state .EmbeddedSlice , removedPrincipals ),
156+ }, nil
157+ }
139158
140- var changes []catalog.PermissionsChange
141- for _ , ga := range state .EmbeddedSlice {
159+ func buildGrantChanges (desiredAssignments []catalog.PrivilegeAssignment , removedPrincipals []string ) []catalog.PermissionsChange {
160+ changes := make ([]catalog.PermissionsChange , 0 , len (desiredAssignments )+ len (removedPrincipals ))
161+ for _ , ga := range desiredAssignments {
142162 change := catalog.PermissionsChange {
143163 Principal : ga .Principal ,
144164 Add : ga .Privileges ,
@@ -152,17 +172,22 @@ func (r *ResourceGrants) applyGrants(ctx context.Context, state *GrantsState, cu
152172 }
153173 changes = append (changes , change )
154174 }
155- changes = append (changes , removedPrincipalChanges (currentAssignments , state .EmbeddedSlice )... )
156-
157- _ , err := r .client .Grants .Update (ctx , catalog.UpdatePermissions {
158- SecurableType : state .SecurableType ,
159- FullName : state .FullName ,
160- Changes : changes ,
161- })
162- return err
175+ for _ , principal := range removedPrincipals {
176+ changes = append (changes , catalog.PermissionsChange {
177+ Principal : principal ,
178+ Add : nil ,
179+ Remove : []catalog.Privilege {catalog .PrivilegeAllPrivileges },
180+ ForceSendFields : nil ,
181+ })
182+ }
183+ return changes
163184}
164185
165- func removedPrincipalChanges (currentAssignments , desiredAssignments []catalog.PrivilegeAssignment ) []catalog.PermissionsChange {
186+ func removedGrantPrincipals (changes Changes , desiredAssignments []catalog.PrivilegeAssignment ) ([]string , error ) {
187+ if len (changes ) == 0 {
188+ return nil , nil
189+ }
190+
166191 desiredPrincipals := make (map [string ]struct {}, len (desiredAssignments ))
167192 for _ , assignment := range desiredAssignments {
168193 if assignment .Principal == "" {
@@ -171,32 +196,52 @@ func removedPrincipalChanges(currentAssignments, desiredAssignments []catalog.Pr
171196 desiredPrincipals [assignment .Principal ] = struct {}{}
172197 }
173198
174- currentAssignments = slices .Clone (currentAssignments )
175- slices .SortFunc (currentAssignments , func (a , b catalog.PrivilegeAssignment ) int {
176- return strings .Compare (a .Principal , b .Principal )
177- })
178-
179- var changes []catalog.PermissionsChange
180- for _ , assignment := range currentAssignments {
181- if assignment .Principal == "" {
199+ removedPrincipals := make (map [string ]struct {})
200+ for pathString , change := range changes {
201+ if change == nil || change .Remote == nil {
182202 continue
183203 }
184- if _ , ok := desiredPrincipals [assignment .Principal ]; ok {
204+ principal , ok , err := grantPrincipalFromPath (pathString )
205+ if err != nil {
206+ return nil , fmt .Errorf ("internal error: parsing grants change path %q: %w" , pathString , err )
207+ }
208+ if ! ok {
185209 continue
186210 }
187- if len ( assignment . Privileges ) == 0 {
211+ if _ , ok := desiredPrincipals [ principal ]; ok {
188212 continue
189213 }
190- privileges := slices .Clone (assignment .Privileges )
191- sortPriviliges (privileges )
192- changes = append (changes , catalog.PermissionsChange {
193- Principal : assignment .Principal ,
194- Add : nil ,
195- Remove : privileges ,
196- ForceSendFields : nil ,
197- })
214+ removedPrincipals [principal ] = struct {}{}
198215 }
199- return changes
216+
217+ result := make ([]string , 0 , len (removedPrincipals ))
218+ for principal := range removedPrincipals {
219+ result = append (result , principal )
220+ }
221+ if len (result ) == 0 {
222+ return nil , nil
223+ }
224+ slices .Sort (result )
225+ return result , nil
226+ }
227+
228+ func grantPrincipalFromPath (pathString string ) (string , bool , error ) {
229+ path , err := structpath .ParsePath (pathString )
230+ if err != nil {
231+ return "" , false , err
232+ }
233+ if path == nil {
234+ return "" , false , nil
235+ }
236+ segments := path .AsSlice ()
237+ if len (segments ) == 0 {
238+ return "" , false , nil
239+ }
240+ key , value , ok := segments [0 ].KeyValue ()
241+ if ! ok || key != grantsPrincipalKey {
242+ return "" , false , nil
243+ }
244+ return value , true , nil
200245}
201246
202247func (r * ResourceGrants ) listGrants (ctx context.Context , securableType , fullName string ) ([]catalog.PrivilegeAssignment , error ) {
@@ -218,8 +263,7 @@ func (r *ResourceGrants) listGrants(ctx context.Context, securableType, fullName
218263 if assignment .Principal == "" {
219264 continue
220265 }
221- privs := make ([]catalog.Privilege , len (assignment .Privileges ))
222- copy (privs , assignment .Privileges )
266+ privs := slices .Clone (assignment .Privileges )
223267 assignments = append (assignments , catalog.PrivilegeAssignment {
224268 Principal : assignment .Principal ,
225269 Privileges : privs ,
@@ -234,7 +278,7 @@ func (r *ResourceGrants) listGrants(ctx context.Context, securableType, fullName
234278 return assignments , nil
235279}
236280
237- func sortPriviliges (privileges []catalog.Privilege ) {
281+ func sortPrivileges (privileges []catalog.Privilege ) {
238282 slices .Sort (privileges )
239283}
240284
0 commit comments