1// Copyright 2016 Google Inc. All Rights Reserved. 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 iam supports the resource-specific operations of Google Cloud 16// IAM (Identity and Access Management) for the Google Cloud Libraries. 17// See https://cloud.google.com/iam for more about IAM. 18// 19// Users of the Google Cloud Libraries will typically not use this package 20// directly. Instead they will begin with some resource that supports IAM, like 21// a pubsub topic, and call its IAM method to get a Handle for that resource. 22package iam 23 24import ( 25 "golang.org/x/net/context" 26 pb "google.golang.org/genproto/googleapis/iam/v1" 27 "google.golang.org/grpc" 28) 29 30// client abstracts the IAMPolicy API to allow multiple implementations. 31type client interface { 32 Get(ctx context.Context, resource string) (*pb.Policy, error) 33 Set(ctx context.Context, resource string, p *pb.Policy) error 34 Test(ctx context.Context, resource string, perms []string) ([]string, error) 35} 36 37// grpcClient implements client for the standard gRPC-based IAMPolicy service. 38type grpcClient struct { 39 c pb.IAMPolicyClient 40} 41 42func (g *grpcClient) Get(ctx context.Context, resource string) (*pb.Policy, error) { 43 proto, err := g.c.GetIamPolicy(ctx, &pb.GetIamPolicyRequest{Resource: resource}) 44 if err != nil { 45 return nil, err 46 } 47 return proto, nil 48} 49func (g *grpcClient) Set(ctx context.Context, resource string, p *pb.Policy) error { 50 _, err := g.c.SetIamPolicy(ctx, &pb.SetIamPolicyRequest{ 51 Resource: resource, 52 Policy: p, 53 }) 54 return err 55} 56 57func (g *grpcClient) Test(ctx context.Context, resource string, perms []string) ([]string, error) { 58 res, err := g.c.TestIamPermissions(ctx, &pb.TestIamPermissionsRequest{ 59 Resource: resource, 60 Permissions: perms, 61 }) 62 if err != nil { 63 return nil, err 64 } 65 return res.Permissions, nil 66} 67 68// A Handle provides IAM operations for a resource. 69type Handle struct { 70 c client 71 resource string 72} 73 74// InternalNewHandle is for use by the Google Cloud Libraries only. 75// 76// InternalNewHandle returns a Handle for resource. 77// The conn parameter refers to a server that must support the IAMPolicy service. 78func InternalNewHandle(conn *grpc.ClientConn, resource string) *Handle { 79 return InternalNewHandleClient(&grpcClient{c: pb.NewIAMPolicyClient(conn)}, resource) 80} 81 82// InternalNewHandleClient is for use by the Google Cloud Libraries only. 83// 84// InternalNewHandleClient returns a Handle for resource using the given 85// client implementation. 86func InternalNewHandleClient(c client, resource string) *Handle { 87 return &Handle{ 88 c: c, 89 resource: resource, 90 } 91} 92 93// Policy retrieves the IAM policy for the resource. 94func (h *Handle) Policy(ctx context.Context) (*Policy, error) { 95 proto, err := h.c.Get(ctx, h.resource) 96 if err != nil { 97 return nil, err 98 } 99 return &Policy{InternalProto: proto}, nil 100} 101 102// SetPolicy replaces the resource's current policy with the supplied Policy. 103// 104// If policy was created from a prior call to Get, then the modification will 105// only succeed if the policy has not changed since the Get. 106func (h *Handle) SetPolicy(ctx context.Context, policy *Policy) error { 107 return h.c.Set(ctx, h.resource, policy.InternalProto) 108} 109 110// TestPermissions returns the subset of permissions that the caller has on the resource. 111func (h *Handle) TestPermissions(ctx context.Context, permissions []string) ([]string, error) { 112 return h.c.Test(ctx, h.resource, permissions) 113} 114 115// A RoleName is a name representing a collection of permissions. 116type RoleName string 117 118// Common role names. 119const ( 120 Owner RoleName = "roles/owner" 121 Editor RoleName = "roles/editor" 122 Viewer RoleName = "roles/viewer" 123) 124 125const ( 126 // AllUsers is a special member that denotes all users, even unauthenticated ones. 127 AllUsers = "allUsers" 128 129 // AllAuthenticatedUsers is a special member that denotes all authenticated users. 130 AllAuthenticatedUsers = "allAuthenticatedUsers" 131) 132 133// A Policy is a list of Bindings representing roles 134// granted to members. 135// 136// The zero Policy is a valid policy with no bindings. 137type Policy struct { 138 // TODO(jba): when type aliases are available, put Policy into an internal package 139 // and provide an exported alias here. 140 141 // This field is exported for use by the Google Cloud Libraries only. 142 // It may become unexported in a future release. 143 InternalProto *pb.Policy 144} 145 146// Members returns the list of members with the supplied role. 147// The return value should not be modified. Use Add and Remove 148// to modify the members of a role. 149func (p *Policy) Members(r RoleName) []string { 150 b := p.binding(r) 151 if b == nil { 152 return nil 153 } 154 return b.Members 155} 156 157// HasRole reports whether member has role r. 158func (p *Policy) HasRole(member string, r RoleName) bool { 159 return memberIndex(member, p.binding(r)) >= 0 160} 161 162// Add adds member member to role r if it is not already present. 163// A new binding is created if there is no binding for the role. 164func (p *Policy) Add(member string, r RoleName) { 165 b := p.binding(r) 166 if b == nil { 167 if p.InternalProto == nil { 168 p.InternalProto = &pb.Policy{} 169 } 170 p.InternalProto.Bindings = append(p.InternalProto.Bindings, &pb.Binding{ 171 Role: string(r), 172 Members: []string{member}, 173 }) 174 return 175 } 176 if memberIndex(member, b) < 0 { 177 b.Members = append(b.Members, member) 178 return 179 } 180} 181 182// Remove removes member from role r if it is present. 183func (p *Policy) Remove(member string, r RoleName) { 184 bi := p.bindingIndex(r) 185 if bi < 0 { 186 return 187 } 188 bindings := p.InternalProto.Bindings 189 b := bindings[bi] 190 mi := memberIndex(member, b) 191 if mi < 0 { 192 return 193 } 194 // Order doesn't matter for bindings or members, so to remove, move the last item 195 // into the removed spot and shrink the slice. 196 if len(b.Members) == 1 { 197 // Remove binding. 198 last := len(bindings) - 1 199 bindings[bi] = bindings[last] 200 bindings[last] = nil 201 p.InternalProto.Bindings = bindings[:last] 202 return 203 } 204 // Remove member. 205 // TODO(jba): worry about multiple copies of m? 206 last := len(b.Members) - 1 207 b.Members[mi] = b.Members[last] 208 b.Members[last] = "" 209 b.Members = b.Members[:last] 210} 211 212// Roles returns the names of all the roles that appear in the Policy. 213func (p *Policy) Roles() []RoleName { 214 if p.InternalProto == nil { 215 return nil 216 } 217 var rns []RoleName 218 for _, b := range p.InternalProto.Bindings { 219 rns = append(rns, RoleName(b.Role)) 220 } 221 return rns 222} 223 224// binding returns the Binding for the suppied role, or nil if there isn't one. 225func (p *Policy) binding(r RoleName) *pb.Binding { 226 i := p.bindingIndex(r) 227 if i < 0 { 228 return nil 229 } 230 return p.InternalProto.Bindings[i] 231} 232 233func (p *Policy) bindingIndex(r RoleName) int { 234 if p.InternalProto == nil { 235 return -1 236 } 237 for i, b := range p.InternalProto.Bindings { 238 if b.Role == string(r) { 239 return i 240 } 241 } 242 return -1 243} 244 245// memberIndex returns the index of m in b's Members, or -1 if not found. 246func memberIndex(m string, b *pb.Binding) int { 247 if b == nil { 248 return -1 249 } 250 for i, mm := range b.Members { 251 if mm == m { 252 return i 253 } 254 } 255 return -1 256} 257