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 15package storage 16 17import ( 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. 28type ACLRole string 29 30const ( 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. 44type ACLEntity string 45 46const ( 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. 53type 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. 63type 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. 69type 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. 78func (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. 92func (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. 106func (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 119func (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 134func (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 142func (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 157func (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 175func (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 183func (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 198func (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 222func (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 230func (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 239func 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 247func 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 255func 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 266func 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 277func 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 288func 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 299func (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 308func (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 317func 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 327func 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} 336