Skip to content

Commit

Permalink
Add IAM Conditions support; enable it in service account IAM
Browse files Browse the repository at this point in the history
Signed-off-by: Modular Magician <[email protected]>
  • Loading branch information
danawillow authored and modular-magician committed Oct 15, 2019
1 parent df6ba8c commit 4dee17c
Show file tree
Hide file tree
Showing 10 changed files with 885 additions and 116 deletions.
27 changes: 25 additions & 2 deletions google-beta/data_source_google_iam_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,27 @@ func dataSourceGoogleIamPolicy() *schema.Resource {
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
"condition": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"expression": {
Type: schema.TypeString,
Required: true,
},
"title": {
Type: schema.TypeString,
Required: true,
},
"description": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
},
},
},
Expand Down Expand Up @@ -99,13 +120,15 @@ func dataSourceGoogleIamPolicyRead(d *schema.ResourceData, meta interface{}) err
for i, v := range bset.List() {
binding := v.(map[string]interface{})
members := convertStringSet(binding["members"].(*schema.Set))
condition := expandIamCondition(binding["condition"])

// Sort members to get simpler diffs as it's what the API does
sort.Strings(members)

policy.Bindings[i] = &cloudresourcemanager.Binding{
Role: binding["role"].(string),
Members: members,
Role: binding["role"].(string),
Members: members,
Condition: condition,
}
}

Expand Down
61 changes: 43 additions & 18 deletions google-beta/iam.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,51 +152,71 @@ func iamPolicyReadModifyWrite(updater ResourceIamUpdater, modify iamPolicyModify
return nil
}

// Flattens AuditConfigs so each role has a single Binding with combined members
// Flattens AuditConfigs so each role+condition has a single Binding with combined members
func mergeBindings(bindings []*cloudresourcemanager.Binding) []*cloudresourcemanager.Binding {
bm := createIamBindingsMap(bindings)
return listFromIamBindingMap(bm)
}

// Flattens Bindings so each role has a single Binding with combined members
func removeAllBindingsWithRole(b []*cloudresourcemanager.Binding, role string) []*cloudresourcemanager.Binding {
type conditionKey struct {
Description string
Expression string
Title string
}

func conditionKeyFromCondition(condition *cloudresourcemanager.Expr) conditionKey {
if condition == nil {
return conditionKey{}
}
return conditionKey{condition.Description, condition.Expression, condition.Title}
}

type iamBindingKey struct {
Role string
Condition conditionKey
}

// Flattens Bindings so each role+condition has a single Binding with combined members
func removeAllBindingsWithRoleAndCondition(b []*cloudresourcemanager.Binding, role string, condition *cloudresourcemanager.Expr) []*cloudresourcemanager.Binding {
bMap := createIamBindingsMap(b)
delete(bMap, role)
key := iamBindingKey{role, conditionKeyFromCondition(condition)}
delete(bMap, key)
return listFromIamBindingMap(bMap)
}

// Removes given role/bound-member pairs from the given Bindings (i.e subtraction).
// Removes given role+condition/bound-member pairs from the given Bindings (i.e subtraction).
func subtractFromBindings(bindings []*cloudresourcemanager.Binding, toRemove ...*cloudresourcemanager.Binding) []*cloudresourcemanager.Binding {
currMap := createIamBindingsMap(bindings)
toRemoveMap := createIamBindingsMap(toRemove)

for role, removeSet := range toRemoveMap {
members, ok := currMap[role]
for key, removeSet := range toRemoveMap {
members, ok := currMap[key]
if !ok {
continue
}
// Remove all removed members
for m := range removeSet {
delete(members, m)
}
// Remove role from bindings
// Remove role+condition from bindings
if len(members) == 0 {
delete(currMap, role)
delete(currMap, key)
}
}

return listFromIamBindingMap(currMap)
}

// Construct map of role to set of members from list of bindings.
func createIamBindingsMap(bindings []*cloudresourcemanager.Binding) map[string]map[string]struct{} {
bm := make(map[string]map[string]struct{})
func createIamBindingsMap(bindings []*cloudresourcemanager.Binding) map[iamBindingKey]map[string]struct{} {
bm := make(map[iamBindingKey]map[string]struct{})
// Get each binding
for _, b := range bindings {
members := make(map[string]struct{})
key := iamBindingKey{b.Role, conditionKeyFromCondition(b.Condition)}
// Initialize members map
if _, ok := bm[b.Role]; ok {
members = bm[b.Role]
if _, ok := bm[key]; ok {
members = bm[key]
}
// Get each member (user/principal) for the binding
for _, m := range b.Members {
Expand All @@ -214,24 +234,29 @@ func createIamBindingsMap(bindings []*cloudresourcemanager.Binding) map[string]m
members[m] = struct{}{}
}
if len(members) > 0 {
bm[b.Role] = members
bm[key] = members
} else {
delete(bm, b.Role)
delete(bm, key)
}
}
return bm
}

// Return list of Bindings for a map of role to member sets
func listFromIamBindingMap(bm map[string]map[string]struct{}) []*cloudresourcemanager.Binding {
func listFromIamBindingMap(bm map[iamBindingKey]map[string]struct{}) []*cloudresourcemanager.Binding {
rb := make([]*cloudresourcemanager.Binding, 0, len(bm))
for role, members := range bm {
for key, members := range bm {
if len(members) == 0 {
continue
}
rb = append(rb, &cloudresourcemanager.Binding{
Role: role,
Role: key.Role,
Members: stringSliceFromGolangSet(members),
Condition: &cloudresourcemanager.Expr{
Description: key.Condition.Description,
Expression: key.Condition.Expression,
Title: key.Condition.Title,
},
})
}
return rb
Expand Down
3 changes: 2 additions & 1 deletion google-beta/iam_service_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package google

import (
"fmt"

"github.com/hashicorp/errwrap"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"google.golang.org/api/cloudresourcemanager/v1"
Expand Down Expand Up @@ -35,7 +36,7 @@ func ServiceAccountIdParseFunc(d *schema.ResourceData, _ *Config) error {
}

func (u *ServiceAccountIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) {
p, err := u.Config.clientIAM.Projects.ServiceAccounts.GetIamPolicy(u.serviceAccountId).Do()
p, err := u.Config.clientIAM.Projects.ServiceAccounts.GetIamPolicy(u.serviceAccountId).OptionsRequestedPolicyVersion(3).Do()

if err != nil {
return nil, errwrap.Wrapf(fmt.Sprintf("Error retrieving IAM policy for %s: {{err}}", u.DescribeResource()), err)
Expand Down
Loading

0 comments on commit 4dee17c

Please sign in to comment.