// Copyright 2014 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package storage import ( "context" "net/http" "reflect" "cloud.google.com/go/internal/trace" storagepb "cloud.google.com/go/storage/internal/apiv2/stubs" "google.golang.org/api/googleapi" raw "google.golang.org/api/storage/v1" ) // ACLRole is the level of access to grant. type ACLRole string const ( RoleOwner ACLRole = "OWNER" RoleReader ACLRole = "READER" RoleWriter ACLRole = "WRITER" ) // ACLEntity refers to a user or group. // They are sometimes referred to as grantees. // // It could be in the form of: // "user-", "user-", "group-", "group-", // "domain-" and "project-team-". // // Or one of the predefined constants: AllUsers, AllAuthenticatedUsers. type ACLEntity string const ( AllUsers ACLEntity = "allUsers" AllAuthenticatedUsers ACLEntity = "allAuthenticatedUsers" ) // ACLRule represents a grant for a role to an entity (user, group or team) for a // Google Cloud Storage object or bucket. type ACLRule struct { Entity ACLEntity EntityID string Role ACLRole Domain string Email string ProjectTeam *ProjectTeam } // ProjectTeam is the project team associated with the entity, if any. type ProjectTeam struct { ProjectNumber string Team string } // ACLHandle provides operations on an access control list for a Google Cloud Storage bucket or object. // ACLHandle on an object operates on the latest generation of that object by default. // Selecting a specific generation of an object is not currently supported by the client. type ACLHandle struct { c *Client bucket string object string isDefault bool userProject string // for requester-pays buckets retry *retryConfig } // Delete permanently deletes the ACL entry for the given entity. func (a *ACLHandle) Delete(ctx context.Context, entity ACLEntity) (err error) { ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.ACL.Delete") defer func() { trace.EndSpan(ctx, err) }() if a.object != "" { return a.objectDelete(ctx, entity) } if a.isDefault { return a.bucketDefaultDelete(ctx, entity) } return a.bucketDelete(ctx, entity) } // Set sets the role for the given entity. func (a *ACLHandle) Set(ctx context.Context, entity ACLEntity, role ACLRole) (err error) { ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.ACL.Set") defer func() { trace.EndSpan(ctx, err) }() if a.object != "" { return a.objectSet(ctx, entity, role, false) } if a.isDefault { return a.objectSet(ctx, entity, role, true) } return a.bucketSet(ctx, entity, role) } // List retrieves ACL entries. func (a *ACLHandle) List(ctx context.Context) (rules []ACLRule, err error) { ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.ACL.List") defer func() { trace.EndSpan(ctx, err) }() if a.object != "" { return a.objectList(ctx) } if a.isDefault { return a.bucketDefaultList(ctx) } return a.bucketList(ctx) } func (a *ACLHandle) bucketDefaultList(ctx context.Context) ([]ACLRule, error) { var acls *raw.ObjectAccessControls var err error req := a.c.raw.DefaultObjectAccessControls.List(a.bucket) a.configureCall(ctx, req) err = run(ctx, func() error { acls, err = req.Do() return err }, a.retry, true, setRetryHeaderHTTP(req)) if err != nil { return nil, err } return toObjectACLRules(acls.Items), nil } func (a *ACLHandle) bucketDefaultDelete(ctx context.Context, entity ACLEntity) error { req := a.c.raw.DefaultObjectAccessControls.Delete(a.bucket, string(entity)) a.configureCall(ctx, req) return run(ctx, func() error { return req.Do() }, a.retry, false, setRetryHeaderHTTP(req)) } func (a *ACLHandle) bucketList(ctx context.Context) ([]ACLRule, error) { var acls *raw.BucketAccessControls var err error req := a.c.raw.BucketAccessControls.List(a.bucket) a.configureCall(ctx, req) err = run(ctx, func() error { acls, err = req.Do() return err }, a.retry, true, setRetryHeaderHTTP(req)) if err != nil { return nil, err } return toBucketACLRules(acls.Items), nil } func (a *ACLHandle) bucketSet(ctx context.Context, entity ACLEntity, role ACLRole) error { acl := &raw.BucketAccessControl{ Bucket: a.bucket, Entity: string(entity), Role: string(role), } req := a.c.raw.BucketAccessControls.Update(a.bucket, string(entity), acl) a.configureCall(ctx, req) return run(ctx, func() error { _, err := req.Do() return err }, a.retry, false, setRetryHeaderHTTP(req)) } func (a *ACLHandle) bucketDelete(ctx context.Context, entity ACLEntity) error { req := a.c.raw.BucketAccessControls.Delete(a.bucket, string(entity)) a.configureCall(ctx, req) return run(ctx, func() error { return req.Do() }, a.retry, false, setRetryHeaderHTTP(req)) } func (a *ACLHandle) objectList(ctx context.Context) ([]ACLRule, error) { var acls *raw.ObjectAccessControls var err error req := a.c.raw.ObjectAccessControls.List(a.bucket, a.object) a.configureCall(ctx, req) err = run(ctx, func() error { acls, err = req.Do() return err }, a.retry, true, setRetryHeaderHTTP(req)) if err != nil { return nil, err } return toObjectACLRules(acls.Items), nil } func (a *ACLHandle) objectSet(ctx context.Context, entity ACLEntity, role ACLRole, isBucketDefault bool) error { type setRequest interface { Do(opts ...googleapi.CallOption) (*raw.ObjectAccessControl, error) Header() http.Header } acl := &raw.ObjectAccessControl{ Bucket: a.bucket, Entity: string(entity), Role: string(role), } var req setRequest if isBucketDefault { req = a.c.raw.DefaultObjectAccessControls.Update(a.bucket, string(entity), acl) } else { req = a.c.raw.ObjectAccessControls.Update(a.bucket, a.object, string(entity), acl) } a.configureCall(ctx, req) return run(ctx, func() error { _, err := req.Do() return err }, a.retry, false, setRetryHeaderHTTP(req)) } func (a *ACLHandle) objectDelete(ctx context.Context, entity ACLEntity) error { req := a.c.raw.ObjectAccessControls.Delete(a.bucket, a.object, string(entity)) a.configureCall(ctx, req) return run(ctx, func() error { return req.Do() }, a.retry, false, setRetryHeaderHTTP(req)) } func (a *ACLHandle) configureCall(ctx context.Context, call interface{ Header() http.Header }) { vc := reflect.ValueOf(call) vc.MethodByName("Context").Call([]reflect.Value{reflect.ValueOf(ctx)}) if a.userProject != "" { vc.MethodByName("UserProject").Call([]reflect.Value{reflect.ValueOf(a.userProject)}) } setClientHeader(call.Header()) } func toObjectACLRules(items []*raw.ObjectAccessControl) []ACLRule { var rs []ACLRule for _, item := range items { rs = append(rs, toObjectACLRule(item)) } return rs } func toObjectACLRulesFromProto(items []*storagepb.ObjectAccessControl) []ACLRule { var rs []ACLRule for _, item := range items { rs = append(rs, toObjectACLRuleFromProto(item)) } return rs } func toBucketACLRules(items []*raw.BucketAccessControl) []ACLRule { var rs []ACLRule for _, item := range items { rs = append(rs, toBucketACLRule(item)) } return rs } func toBucketACLRulesFromProto(items []*storagepb.BucketAccessControl) []ACLRule { var rs []ACLRule for _, item := range items { rs = append(rs, toBucketACLRuleFromProto(item)) } return rs } func toObjectACLRule(a *raw.ObjectAccessControl) ACLRule { return ACLRule{ Entity: ACLEntity(a.Entity), EntityID: a.EntityId, Role: ACLRole(a.Role), Domain: a.Domain, Email: a.Email, ProjectTeam: toObjectProjectTeam(a.ProjectTeam), } } func toObjectACLRuleFromProto(a *storagepb.ObjectAccessControl) ACLRule { return ACLRule{ Entity: ACLEntity(a.GetEntity()), EntityID: a.GetEntityId(), Role: ACLRole(a.GetRole()), Domain: a.GetDomain(), Email: a.GetEmail(), ProjectTeam: toProjectTeamFromProto(a.GetProjectTeam()), } } func toBucketACLRule(a *raw.BucketAccessControl) ACLRule { return ACLRule{ Entity: ACLEntity(a.Entity), EntityID: a.EntityId, Role: ACLRole(a.Role), Domain: a.Domain, Email: a.Email, ProjectTeam: toBucketProjectTeam(a.ProjectTeam), } } func toBucketACLRuleFromProto(a *storagepb.BucketAccessControl) ACLRule { return ACLRule{ Entity: ACLEntity(a.GetEntity()), EntityID: a.GetEntityId(), Role: ACLRole(a.GetRole()), Domain: a.GetDomain(), Email: a.GetEmail(), ProjectTeam: toProjectTeamFromProto(a.GetProjectTeam()), } } func toRawObjectACL(rules []ACLRule) []*raw.ObjectAccessControl { if len(rules) == 0 { return nil } r := make([]*raw.ObjectAccessControl, 0, len(rules)) for _, rule := range rules { r = append(r, rule.toRawObjectAccessControl("")) // bucket name unnecessary } return r } func toProtoObjectACL(rules []ACLRule) []*storagepb.ObjectAccessControl { if len(rules) == 0 { return nil } r := make([]*storagepb.ObjectAccessControl, 0, len(rules)) for _, rule := range rules { r = append(r, rule.toProtoObjectAccessControl("")) // bucket name unnecessary } return r } func toRawBucketACL(rules []ACLRule) []*raw.BucketAccessControl { if len(rules) == 0 { return nil } r := make([]*raw.BucketAccessControl, 0, len(rules)) for _, rule := range rules { r = append(r, rule.toRawBucketAccessControl("")) // bucket name unnecessary } return r } func toProtoBucketACL(rules []ACLRule) []*storagepb.BucketAccessControl { if len(rules) == 0 { return nil } r := make([]*storagepb.BucketAccessControl, 0, len(rules)) for _, rule := range rules { r = append(r, rule.toProtoBucketAccessControl()) } return r } func (r ACLRule) toRawBucketAccessControl(bucket string) *raw.BucketAccessControl { return &raw.BucketAccessControl{ Bucket: bucket, Entity: string(r.Entity), Role: string(r.Role), // The other fields are not settable. } } func (r ACLRule) toRawObjectAccessControl(bucket string) *raw.ObjectAccessControl { return &raw.ObjectAccessControl{ Bucket: bucket, Entity: string(r.Entity), Role: string(r.Role), // The other fields are not settable. } } func (r ACLRule) toProtoObjectAccessControl(bucket string) *storagepb.ObjectAccessControl { return &storagepb.ObjectAccessControl{ Entity: string(r.Entity), Role: string(r.Role), // The other fields are not settable. } } func (r ACLRule) toProtoBucketAccessControl() *storagepb.BucketAccessControl { return &storagepb.BucketAccessControl{ Entity: string(r.Entity), Role: string(r.Role), // The other fields are not settable. } } func toBucketProjectTeam(p *raw.BucketAccessControlProjectTeam) *ProjectTeam { if p == nil { return nil } return &ProjectTeam{ ProjectNumber: p.ProjectNumber, Team: p.Team, } } func toProjectTeamFromProto(p *storagepb.ProjectTeam) *ProjectTeam { if p == nil { return nil } return &ProjectTeam{ ProjectNumber: p.GetProjectNumber(), Team: p.GetTeam(), } } func toObjectProjectTeam(p *raw.ObjectAccessControlProjectTeam) *ProjectTeam { if p == nil { return nil } return &ProjectTeam{ ProjectNumber: p.ProjectNumber, Team: p.Team, } }