1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package iam
23
24 import (
25 "context"
26 "fmt"
27 "time"
28
29 pb "cloud.google.com/go/iam/apiv1/iampb"
30 gax "github.com/googleapis/gax-go/v2"
31 "google.golang.org/grpc"
32 "google.golang.org/grpc/codes"
33 "google.golang.org/grpc/metadata"
34 )
35
36
37 type client interface {
38 Get(ctx context.Context, resource string) (*pb.Policy, error)
39 Set(ctx context.Context, resource string, p *pb.Policy) error
40 Test(ctx context.Context, resource string, perms []string) ([]string, error)
41 GetWithVersion(ctx context.Context, resource string, requestedPolicyVersion int32) (*pb.Policy, error)
42 }
43
44
45 type grpcClient struct {
46 c pb.IAMPolicyClient
47 }
48
49 var withRetry = gax.WithRetry(func() gax.Retryer {
50 return gax.OnCodes([]codes.Code{
51 codes.DeadlineExceeded,
52 codes.Unavailable,
53 }, gax.Backoff{
54 Initial: 100 * time.Millisecond,
55 Max: 60 * time.Second,
56 Multiplier: 1.3,
57 })
58 })
59
60 func (g *grpcClient) Get(ctx context.Context, resource string) (*pb.Policy, error) {
61 return g.GetWithVersion(ctx, resource, 1)
62 }
63
64 func (g *grpcClient) GetWithVersion(ctx context.Context, resource string, requestedPolicyVersion int32) (*pb.Policy, error) {
65 var proto *pb.Policy
66 md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "resource", resource))
67 ctx = insertMetadata(ctx, md)
68
69 err := gax.Invoke(ctx, func(ctx context.Context, _ gax.CallSettings) error {
70 var err error
71 proto, err = g.c.GetIamPolicy(ctx, &pb.GetIamPolicyRequest{
72 Resource: resource,
73 Options: &pb.GetPolicyOptions{
74 RequestedPolicyVersion: requestedPolicyVersion,
75 },
76 })
77 return err
78 }, withRetry)
79 if err != nil {
80 return nil, err
81 }
82 return proto, nil
83 }
84
85 func (g *grpcClient) Set(ctx context.Context, resource string, p *pb.Policy) error {
86 md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "resource", resource))
87 ctx = insertMetadata(ctx, md)
88
89 return gax.Invoke(ctx, func(ctx context.Context, _ gax.CallSettings) error {
90 _, err := g.c.SetIamPolicy(ctx, &pb.SetIamPolicyRequest{
91 Resource: resource,
92 Policy: p,
93 })
94 return err
95 }, withRetry)
96 }
97
98 func (g *grpcClient) Test(ctx context.Context, resource string, perms []string) ([]string, error) {
99 var res *pb.TestIamPermissionsResponse
100 md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "resource", resource))
101 ctx = insertMetadata(ctx, md)
102
103 err := gax.Invoke(ctx, func(ctx context.Context, _ gax.CallSettings) error {
104 var err error
105 res, err = g.c.TestIamPermissions(ctx, &pb.TestIamPermissionsRequest{
106 Resource: resource,
107 Permissions: perms,
108 })
109 return err
110 }, withRetry)
111 if err != nil {
112 return nil, err
113 }
114 return res.Permissions, nil
115 }
116
117
118 type Handle struct {
119 c client
120 resource string
121 }
122
123
124 type Handle3 struct {
125 c client
126 resource string
127 version int32
128 }
129
130
131
132
133
134 func InternalNewHandle(conn grpc.ClientConnInterface, resource string) *Handle {
135 return InternalNewHandleGRPCClient(pb.NewIAMPolicyClient(conn), resource)
136 }
137
138
139
140
141
142 func InternalNewHandleGRPCClient(c pb.IAMPolicyClient, resource string) *Handle {
143 return InternalNewHandleClient(&grpcClient{c: c}, resource)
144 }
145
146
147
148
149
150 func InternalNewHandleClient(c client, resource string) *Handle {
151 return &Handle{
152 c: c,
153 resource: resource,
154 }
155 }
156
157
158
159
160 func (h *Handle) V3() *Handle3 {
161 return &Handle3{
162 c: h.c,
163 resource: h.resource,
164 version: 3,
165 }
166 }
167
168
169 func (h *Handle) Policy(ctx context.Context) (*Policy, error) {
170 proto, err := h.c.Get(ctx, h.resource)
171 if err != nil {
172 return nil, err
173 }
174 return &Policy{InternalProto: proto}, nil
175 }
176
177
178
179
180
181 func (h *Handle) SetPolicy(ctx context.Context, policy *Policy) error {
182 return h.c.Set(ctx, h.resource, policy.InternalProto)
183 }
184
185
186 func (h *Handle) TestPermissions(ctx context.Context, permissions []string) ([]string, error) {
187 return h.c.Test(ctx, h.resource, permissions)
188 }
189
190
191 type RoleName string
192
193
194 const (
195 Owner RoleName = "roles/owner"
196 Editor RoleName = "roles/editor"
197 Viewer RoleName = "roles/viewer"
198 )
199
200 const (
201
202 AllUsers = "allUsers"
203
204
205 AllAuthenticatedUsers = "allAuthenticatedUsers"
206 )
207
208
209
210
211
212 type Policy struct {
213
214
215
216
217
218 InternalProto *pb.Policy
219 }
220
221
222
223
224 func (p *Policy) Members(r RoleName) []string {
225 b := p.binding(r)
226 if b == nil {
227 return nil
228 }
229 return b.Members
230 }
231
232
233 func (p *Policy) HasRole(member string, r RoleName) bool {
234 return memberIndex(member, p.binding(r)) >= 0
235 }
236
237
238
239 func (p *Policy) Add(member string, r RoleName) {
240 b := p.binding(r)
241 if b == nil {
242 if p.InternalProto == nil {
243 p.InternalProto = &pb.Policy{}
244 }
245 p.InternalProto.Bindings = append(p.InternalProto.Bindings, &pb.Binding{
246 Role: string(r),
247 Members: []string{member},
248 })
249 return
250 }
251 if memberIndex(member, b) < 0 {
252 b.Members = append(b.Members, member)
253 return
254 }
255 }
256
257
258 func (p *Policy) Remove(member string, r RoleName) {
259 bi := p.bindingIndex(r)
260 if bi < 0 {
261 return
262 }
263 bindings := p.InternalProto.Bindings
264 b := bindings[bi]
265 mi := memberIndex(member, b)
266 if mi < 0 {
267 return
268 }
269
270
271 if len(b.Members) == 1 {
272
273 last := len(bindings) - 1
274 bindings[bi] = bindings[last]
275 bindings[last] = nil
276 p.InternalProto.Bindings = bindings[:last]
277 return
278 }
279
280
281 last := len(b.Members) - 1
282 b.Members[mi] = b.Members[last]
283 b.Members[last] = ""
284 b.Members = b.Members[:last]
285 }
286
287
288 func (p *Policy) Roles() []RoleName {
289 if p.InternalProto == nil {
290 return nil
291 }
292 var rns []RoleName
293 for _, b := range p.InternalProto.Bindings {
294 rns = append(rns, RoleName(b.Role))
295 }
296 return rns
297 }
298
299
300 func (p *Policy) binding(r RoleName) *pb.Binding {
301 i := p.bindingIndex(r)
302 if i < 0 {
303 return nil
304 }
305 return p.InternalProto.Bindings[i]
306 }
307
308 func (p *Policy) bindingIndex(r RoleName) int {
309 if p.InternalProto == nil {
310 return -1
311 }
312 for i, b := range p.InternalProto.Bindings {
313 if b.Role == string(r) {
314 return i
315 }
316 }
317 return -1
318 }
319
320
321 func memberIndex(m string, b *pb.Binding) int {
322 if b == nil {
323 return -1
324 }
325 for i, mm := range b.Members {
326 if mm == m {
327 return i
328 }
329 }
330 return -1
331 }
332
333
334 func insertMetadata(ctx context.Context, mds ...metadata.MD) context.Context {
335 out, _ := metadata.FromOutgoingContext(ctx)
336 out = out.Copy()
337 for _, md := range mds {
338 for k, v := range md {
339 out[k] = append(out[k], v...)
340 }
341 }
342 return metadata.NewOutgoingContext(ctx, out)
343 }
344
345
346
347
348
349
350
351
352
353 type Policy3 struct {
354 etag []byte
355 Bindings []*pb.Binding
356 }
357
358
359
360
361 func (h *Handle3) Policy(ctx context.Context) (*Policy3, error) {
362 proto, err := h.c.GetWithVersion(ctx, h.resource, h.version)
363 if err != nil {
364 return nil, err
365 }
366 return &Policy3{
367 Bindings: proto.Bindings,
368 etag: proto.Etag,
369 }, nil
370 }
371
372
373
374
375
376 func (h *Handle3) SetPolicy(ctx context.Context, policy *Policy3) error {
377 return h.c.Set(ctx, h.resource, &pb.Policy{
378 Bindings: policy.Bindings,
379 Etag: policy.etag,
380 Version: h.version,
381 })
382 }
383
384
385 func (h *Handle3) TestPermissions(ctx context.Context, permissions []string) ([]string, error) {
386 return h.c.Test(ctx, h.resource, permissions)
387 }
388
View as plain text