]>
Commit | Line | Data |
---|---|---|
1 | // Copyright 2014 Google LLC | |
2 | // | |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | // you may not use this file except in compliance with the License. | |
5 | // You may obtain a copy of the License at | |
6 | // | |
7 | // http://www.apache.org/licenses/LICENSE-2.0 | |
8 | // | |
9 | // Unless required by applicable law or agreed to in writing, software | |
10 | // distributed under the License is distributed on an "AS IS" BASIS, | |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | // See the License for the specific language governing permissions and | |
13 | // limitations under the License. | |
14 | ||
15 | package storage | |
16 | ||
17 | import ( | |
18 | "context" | |
19 | "net/http" | |
20 | "reflect" | |
21 | ||
22 | "cloud.google.com/go/internal/trace" | |
23 | "google.golang.org/api/googleapi" | |
24 | raw "google.golang.org/api/storage/v1" | |
25 | ) | |
26 | ||
27 | // ACLRole is the level of access to grant. | |
28 | type ACLRole string | |
29 | ||
30 | const ( | |
31 | RoleOwner ACLRole = "OWNER" | |
32 | RoleReader ACLRole = "READER" | |
33 | RoleWriter ACLRole = "WRITER" | |
34 | ) | |
35 | ||
36 | // ACLEntity refers to a user or group. | |
37 | // They are sometimes referred to as grantees. | |
38 | // | |
39 | // It could be in the form of: | |
40 | // "user-<userId>", "user-<email>", "group-<groupId>", "group-<email>", | |
41 | // "domain-<domain>" and "project-team-<projectId>". | |
42 | // | |
43 | // Or one of the predefined constants: AllUsers, AllAuthenticatedUsers. | |
44 | type ACLEntity string | |
45 | ||
46 | const ( | |
47 | AllUsers ACLEntity = "allUsers" | |
48 | AllAuthenticatedUsers ACLEntity = "allAuthenticatedUsers" | |
49 | ) | |
50 | ||
51 | // ACLRule represents a grant for a role to an entity (user, group or team) for a | |
52 | // Google Cloud Storage object or bucket. | |
53 | type ACLRule struct { | |
54 | Entity ACLEntity | |
55 | EntityID string | |
56 | Role ACLRole | |
57 | Domain string | |
58 | Email string | |
59 | ProjectTeam *ProjectTeam | |
60 | } | |
61 | ||
62 | // ProjectTeam is the project team associated with the entity, if any. | |
63 | type ProjectTeam struct { | |
64 | ProjectNumber string | |
65 | Team string | |
66 | } | |
67 | ||
68 | // ACLHandle provides operations on an access control list for a Google Cloud Storage bucket or object. | |
69 | type ACLHandle struct { | |
70 | c *Client | |
71 | bucket string | |
72 | object string | |
73 | isDefault bool | |
74 | userProject string // for requester-pays buckets | |
75 | } | |
76 | ||
77 | // Delete permanently deletes the ACL entry for the given entity. | |
78 | func (a *ACLHandle) Delete(ctx context.Context, entity ACLEntity) (err error) { | |
79 | ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.ACL.Delete") | |
80 | defer func() { trace.EndSpan(ctx, err) }() | |
81 | ||
82 | if a.object != "" { | |
83 | return a.objectDelete(ctx, entity) | |
84 | } | |
85 | if a.isDefault { | |
86 | return a.bucketDefaultDelete(ctx, entity) | |
87 | } | |
88 | return a.bucketDelete(ctx, entity) | |
89 | } | |
90 | ||
91 | // Set sets the role for the given entity. | |
92 | func (a *ACLHandle) Set(ctx context.Context, entity ACLEntity, role ACLRole) (err error) { | |
93 | ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.ACL.Set") | |
94 | defer func() { trace.EndSpan(ctx, err) }() | |
95 | ||
96 | if a.object != "" { | |
97 | return a.objectSet(ctx, entity, role, false) | |
98 | } | |
99 | if a.isDefault { | |
100 | return a.objectSet(ctx, entity, role, true) | |
101 | } | |
102 | return a.bucketSet(ctx, entity, role) | |
103 | } | |
104 | ||
105 | // List retrieves ACL entries. | |
106 | func (a *ACLHandle) List(ctx context.Context) (rules []ACLRule, err error) { | |
107 | ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.ACL.List") | |
108 | defer func() { trace.EndSpan(ctx, err) }() | |
109 | ||
110 | if a.object != "" { | |
111 | return a.objectList(ctx) | |
112 | } | |
113 | if a.isDefault { | |
114 | return a.bucketDefaultList(ctx) | |
115 | } | |
116 | return a.bucketList(ctx) | |
117 | } | |
118 | ||
119 | func (a *ACLHandle) bucketDefaultList(ctx context.Context) ([]ACLRule, error) { | |
120 | var acls *raw.ObjectAccessControls | |
121 | var err error | |
122 | err = runWithRetry(ctx, func() error { | |
123 | req := a.c.raw.DefaultObjectAccessControls.List(a.bucket) | |
124 | a.configureCall(ctx, req) | |
125 | acls, err = req.Do() | |
126 | return err | |
127 | }) | |
128 | if err != nil { | |
129 | return nil, err | |
130 | } | |
131 | return toObjectACLRules(acls.Items), nil | |
132 | } | |
133 | ||
134 | func (a *ACLHandle) bucketDefaultDelete(ctx context.Context, entity ACLEntity) error { | |
135 | return runWithRetry(ctx, func() error { | |
136 | req := a.c.raw.DefaultObjectAccessControls.Delete(a.bucket, string(entity)) | |
137 | a.configureCall(ctx, req) | |
138 | return req.Do() | |
139 | }) | |
140 | } | |
141 | ||
142 | func (a *ACLHandle) bucketList(ctx context.Context) ([]ACLRule, error) { | |
143 | var acls *raw.BucketAccessControls | |
144 | var err error | |
145 | err = runWithRetry(ctx, func() error { | |
146 | req := a.c.raw.BucketAccessControls.List(a.bucket) | |
147 | a.configureCall(ctx, req) | |
148 | acls, err = req.Do() | |
149 | return err | |
150 | }) | |
151 | if err != nil { | |
152 | return nil, err | |
153 | } | |
154 | return toBucketACLRules(acls.Items), nil | |
155 | } | |
156 | ||
157 | func (a *ACLHandle) bucketSet(ctx context.Context, entity ACLEntity, role ACLRole) error { | |
158 | acl := &raw.BucketAccessControl{ | |
159 | Bucket: a.bucket, | |
160 | Entity: string(entity), | |
161 | Role: string(role), | |
162 | } | |
163 | err := runWithRetry(ctx, func() error { | |
164 | req := a.c.raw.BucketAccessControls.Update(a.bucket, string(entity), acl) | |
165 | a.configureCall(ctx, req) | |
166 | _, err := req.Do() | |
167 | return err | |
168 | }) | |
169 | if err != nil { | |
170 | return err | |
171 | } | |
172 | return nil | |
173 | } | |
174 | ||
175 | func (a *ACLHandle) bucketDelete(ctx context.Context, entity ACLEntity) error { | |
176 | return runWithRetry(ctx, func() error { | |
177 | req := a.c.raw.BucketAccessControls.Delete(a.bucket, string(entity)) | |
178 | a.configureCall(ctx, req) | |
179 | return req.Do() | |
180 | }) | |
181 | } | |
182 | ||
183 | func (a *ACLHandle) objectList(ctx context.Context) ([]ACLRule, error) { | |
184 | var acls *raw.ObjectAccessControls | |
185 | var err error | |
186 | err = runWithRetry(ctx, func() error { | |
187 | req := a.c.raw.ObjectAccessControls.List(a.bucket, a.object) | |
188 | a.configureCall(ctx, req) | |
189 | acls, err = req.Do() | |
190 | return err | |
191 | }) | |
192 | if err != nil { | |
193 | return nil, err | |
194 | } | |
195 | return toObjectACLRules(acls.Items), nil | |
196 | } | |
197 | ||
198 | func (a *ACLHandle) objectSet(ctx context.Context, entity ACLEntity, role ACLRole, isBucketDefault bool) error { | |
199 | type setRequest interface { | |
200 | Do(opts ...googleapi.CallOption) (*raw.ObjectAccessControl, error) | |
201 | Header() http.Header | |
202 | } | |
203 | ||
204 | acl := &raw.ObjectAccessControl{ | |
205 | Bucket: a.bucket, | |
206 | Entity: string(entity), | |
207 | Role: string(role), | |
208 | } | |
209 | var req setRequest | |
210 | if isBucketDefault { | |
211 | req = a.c.raw.DefaultObjectAccessControls.Update(a.bucket, string(entity), acl) | |
212 | } else { | |
213 | req = a.c.raw.ObjectAccessControls.Update(a.bucket, a.object, string(entity), acl) | |
214 | } | |
215 | a.configureCall(ctx, req) | |
216 | return runWithRetry(ctx, func() error { | |
217 | _, err := req.Do() | |
218 | return err | |
219 | }) | |
220 | } | |
221 | ||
222 | func (a *ACLHandle) objectDelete(ctx context.Context, entity ACLEntity) error { | |
223 | return runWithRetry(ctx, func() error { | |
224 | req := a.c.raw.ObjectAccessControls.Delete(a.bucket, a.object, string(entity)) | |
225 | a.configureCall(ctx, req) | |
226 | return req.Do() | |
227 | }) | |
228 | } | |
229 | ||
230 | func (a *ACLHandle) configureCall(ctx context.Context, call interface{ Header() http.Header }) { | |
231 | vc := reflect.ValueOf(call) | |
232 | vc.MethodByName("Context").Call([]reflect.Value{reflect.ValueOf(ctx)}) | |
233 | if a.userProject != "" { | |
234 | vc.MethodByName("UserProject").Call([]reflect.Value{reflect.ValueOf(a.userProject)}) | |
235 | } | |
236 | setClientHeader(call.Header()) | |
237 | } | |
238 | ||
239 | func toObjectACLRules(items []*raw.ObjectAccessControl) []ACLRule { | |
240 | var rs []ACLRule | |
241 | for _, item := range items { | |
242 | rs = append(rs, toObjectACLRule(item)) | |
243 | } | |
244 | return rs | |
245 | } | |
246 | ||
247 | func toBucketACLRules(items []*raw.BucketAccessControl) []ACLRule { | |
248 | var rs []ACLRule | |
249 | for _, item := range items { | |
250 | rs = append(rs, toBucketACLRule(item)) | |
251 | } | |
252 | return rs | |
253 | } | |
254 | ||
255 | func toObjectACLRule(a *raw.ObjectAccessControl) ACLRule { | |
256 | return ACLRule{ | |
257 | Entity: ACLEntity(a.Entity), | |
258 | EntityID: a.EntityId, | |
259 | Role: ACLRole(a.Role), | |
260 | Domain: a.Domain, | |
261 | Email: a.Email, | |
262 | ProjectTeam: toObjectProjectTeam(a.ProjectTeam), | |
263 | } | |
264 | } | |
265 | ||
266 | func toBucketACLRule(a *raw.BucketAccessControl) ACLRule { | |
267 | return ACLRule{ | |
268 | Entity: ACLEntity(a.Entity), | |
269 | EntityID: a.EntityId, | |
270 | Role: ACLRole(a.Role), | |
271 | Domain: a.Domain, | |
272 | Email: a.Email, | |
273 | ProjectTeam: toBucketProjectTeam(a.ProjectTeam), | |
274 | } | |
275 | } | |
276 | ||
277 | func toRawObjectACL(rules []ACLRule) []*raw.ObjectAccessControl { | |
278 | if len(rules) == 0 { | |
279 | return nil | |
280 | } | |
281 | r := make([]*raw.ObjectAccessControl, 0, len(rules)) | |
282 | for _, rule := range rules { | |
283 | r = append(r, rule.toRawObjectAccessControl("")) // bucket name unnecessary | |
284 | } | |
285 | return r | |
286 | } | |
287 | ||
288 | func toRawBucketACL(rules []ACLRule) []*raw.BucketAccessControl { | |
289 | if len(rules) == 0 { | |
290 | return nil | |
291 | } | |
292 | r := make([]*raw.BucketAccessControl, 0, len(rules)) | |
293 | for _, rule := range rules { | |
294 | r = append(r, rule.toRawBucketAccessControl("")) // bucket name unnecessary | |
295 | } | |
296 | return r | |
297 | } | |
298 | ||
299 | func (r ACLRule) toRawBucketAccessControl(bucket string) *raw.BucketAccessControl { | |
300 | return &raw.BucketAccessControl{ | |
301 | Bucket: bucket, | |
302 | Entity: string(r.Entity), | |
303 | Role: string(r.Role), | |
304 | // The other fields are not settable. | |
305 | } | |
306 | } | |
307 | ||
308 | func (r ACLRule) toRawObjectAccessControl(bucket string) *raw.ObjectAccessControl { | |
309 | return &raw.ObjectAccessControl{ | |
310 | Bucket: bucket, | |
311 | Entity: string(r.Entity), | |
312 | Role: string(r.Role), | |
313 | // The other fields are not settable. | |
314 | } | |
315 | } | |
316 | ||
317 | func toBucketProjectTeam(p *raw.BucketAccessControlProjectTeam) *ProjectTeam { | |
318 | if p == nil { | |
319 | return nil | |
320 | } | |
321 | return &ProjectTeam{ | |
322 | ProjectNumber: p.ProjectNumber, | |
323 | Team: p.Team, | |
324 | } | |
325 | } | |
326 | ||
327 | func toObjectProjectTeam(p *raw.ObjectAccessControlProjectTeam) *ProjectTeam { | |
328 | if p == nil { | |
329 | return nil | |
330 | } | |
331 | return &ProjectTeam{ | |
332 | ProjectNumber: p.ProjectNumber, | |
333 | Team: p.Team, | |
334 | } | |
335 | } |