Skip to content

Commit

Permalink
Add an identity_group_member_entity_ids resource to manage group enti…
Browse files Browse the repository at this point in the history
…ty members in a decoupled manner (#724)
  • Loading branch information
shwuandwing committed Jun 8, 2020
1 parent 29f66d1 commit af1db7e
Show file tree
Hide file tree
Showing 7 changed files with 856 additions and 4 deletions.
4 changes: 4 additions & 0 deletions vault/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,10 @@ var (
Resource: identityGroupAliasResource(),
PathInventory: []string{"/identity/group-alias"},
},
"vault_identity_group_member_entity_ids": {
Resource: identityGroupMemberEntityIdsResource(),
PathInventory: []string{"/identity/group/id/{id}"},
},
"vault_identity_group_policies": {
Resource: identityGroupPoliciesResource(),
PathInventory: []string{"/identity/lookup/group"},
Expand Down
30 changes: 27 additions & 3 deletions vault/resource_identity_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,20 +86,26 @@ func identityGroupResource() *schema.Resource {
"member_entity_ids": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Description: "Entity IDs to be assigned as group members.",
// Suppress the diff if group type is "external" because we cannot manage
// group members
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
if d.Get("type").(string) == "external" {
if d.Get("type").(string) == "external" || d.Get("external_member_entity_ids").(bool) == true {
return true
}
return false
},
},

"external_member_entity_ids": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Manage member entities externally through `vault_identity_group_policies_member_entity_ids`",
},
},
}
}
Expand All @@ -116,7 +122,10 @@ func identityGroupUpdateFields(d *schema.ResourceData, data map[string]interface
// Member groups and entities can't be set for external groups
if d.Get("type").(string) == "internal" {
data["member_group_ids"] = d.Get("member_group_ids").(*schema.Set).List()
data["member_entity_ids"] = d.Get("member_entity_ids").(*schema.Set).List()

if externalMemberEntityIds, ok := d.GetOk("external_member_entity_ids"); !(ok && externalMemberEntityIds.(bool)) {
data["member_entity_ids"] = d.Get("member_entity_ids").(*schema.Set).List()
}
}

if metadata, ok := d.GetOk("metadata"); ok {
Expand Down Expand Up @@ -268,6 +277,21 @@ func readIdentityGroupPolicies(client *api.Client, groupID string) ([]interface{
return make([]interface{}, 0), nil
}

func readIdentityGroupMemberEntityIds(client *api.Client, groupID string) ([]interface{}, error) {
resp, err := readIdentityGroup(client, groupID)
if err != nil {
return nil, err
}
if resp == nil {
return nil, fmt.Errorf("error IdentityGroup %s does not exist", groupID)
}

if v, ok := resp.Data["member_entity_ids"]; ok && v != nil {
return v.([]interface{}), nil
}
return make([]interface{}, 0), nil
}

// This function may return `nil` for the IdentityGroup if it does not exist
func readIdentityGroup(client *api.Client, groupID string) (*api.Secret, error) {
path := identityGroupIDPath(groupID)
Expand Down
183 changes: 183 additions & 0 deletions vault/resource_identity_group_member_entity_ids.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package vault

import (
"fmt"
"log"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/vault/api"
"github.com/terraform-providers/terraform-provider-vault/util"
)

func identityGroupMemberEntityIdsResource() *schema.Resource {
return &schema.Resource{
Create: identityGroupMemberEntityIdsUpdate,
Update: identityGroupMemberEntityIdsUpdate,
Read: identityGroupMemberEntityIdsRead,
Delete: identityGroupMemberEntityIdsDelete,

Schema: map[string]*schema.Schema{
"member_entity_ids": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Description: "Entity IDs to be assigned as group members.",
},

"exclusive": {
Type: schema.TypeBool,
Optional: true,
Default: true,
Description: "Should the resource manage member entity ids exclusively? Beware of race conditions when disabling exclusive management",
},

"group_id": {
Type: schema.TypeString,
Required: true,
Description: "ID of the group.",
},

"group_name": {
Type: schema.TypeString,
Computed: true,
Description: "Name of the group.",
},
},
}
}

func identityGroupMemberEntityIdsUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*api.Client)
id := d.Get("group_id").(string)

log.Printf("[DEBUG] Updating IdentityGroupMemberEntityIds %q", id)
path := identityGroupIDPath(id)

vaultMutexKV.Lock(path)
defer vaultMutexKV.Unlock(path)

data := make(map[string]interface{})
memberEntityIds := d.Get("member_entity_ids").(*schema.Set).List()

resp, err := readIdentityGroup(client, id)
if err != nil {
return err
}

t, ok := resp.Data["type"]
if ok && t != "external" {
if d.Get("exclusive").(bool) {
data["member_entity_ids"] = memberEntityIds
} else {
apiMemberEntityIds, err := readIdentityGroupMemberEntityIds(client, id)
if err != nil {
return err
}
if d.HasChange("member_entity_ids") {
oldMemberEntityIdsI, _ := d.GetChange("member_entity_ids")
oldMemberEntityIds := oldMemberEntityIdsI.(*schema.Set).List()
for _, memberEntityId := range oldMemberEntityIds {
apiMemberEntityIds = util.SliceRemoveIfPresent(apiMemberEntityIds, memberEntityId)
}
}
for _, memberEntityId := range memberEntityIds {
apiMemberEntityIds = util.SliceAppendIfMissing(apiMemberEntityIds, memberEntityId)
}
data["member_entity_ids"] = apiMemberEntityIds
}
}

_, err = client.Logical().Write(path, data)
if err != nil {
return fmt.Errorf("error updating IdentityGroupMemberEntityIds %q: %s", id, err)
}
log.Printf("[DEBUG] Updated IdentityGroupMemberEntityIds %q", id)

d.SetId(id)

return identityGroupMemberEntityIdsRead(d, meta)
}

func identityGroupMemberEntityIdsRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*api.Client)
id := d.Id()

resp, err := readIdentityGroup(client, id)
if err != nil {
return err
}
log.Printf("[DEBUG] Read IdentityGroupMemberEntityIds %s", id)
if resp == nil {
log.Printf("[WARN] IdentityGroupMemberEntityIds %q not found, removing from state", id)
d.SetId("")
return nil
}

d.Set("group_id", id)
d.Set("group_name", resp.Data["name"])

if d.Get("exclusive").(bool) {
respdata := resp.Data["member_entity_ids"]
if err = d.Set("member_entity_ids", respdata); err != nil {
return fmt.Errorf("error setting member entity ids for IdentityGroupMemberEntityIds %q: %s", id, err)
}
} else {
userMemberEntityIds := d.Get("member_entity_ids").(*schema.Set).List()
newMemberEntityIds := make([]string, 0)
apiMemberEntityIds := resp.Data["member_entity_ids"].([]interface{})

for _, memberEntityId := range userMemberEntityIds {
if found, _ := util.SliceHasElement(apiMemberEntityIds, memberEntityId); found {
newMemberEntityIds = append(newMemberEntityIds, memberEntityId.(string))
}
}
if err = d.Set("member_entity_ids", newMemberEntityIds); err != nil {
return fmt.Errorf("error setting member entity ids for IdentityGroupMemberEntityIds %q: %s", id, err)
}
}
return nil
}

func identityGroupMemberEntityIdsDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*api.Client)
id := d.Get("group_id").(string)

log.Printf("[DEBUG] Deleting IdentityGroupMemberEntityIds %q", id)
path := identityGroupIDPath(id)

vaultMutexKV.Lock(path)
defer vaultMutexKV.Unlock(path)

data := make(map[string]interface{})

resp, err := readIdentityGroup(client, id)
if err != nil {
return err
}

t, ok := resp.Data["type"]
if ok && t != "external" {
if d.Get("exclusive").(bool) {
data["member_entity_ids"] = make([]string, 0)
} else {
apiMemberEntityIds, err := readIdentityGroupMemberEntityIds(client, id)
if err != nil {
return err
}
for _, memberEntityId := range d.Get("member_entity_ids").(*schema.Set).List() {
apiMemberEntityIds = util.SliceRemoveIfPresent(apiMemberEntityIds, memberEntityId)
}
data["member_entity_ids"] = apiMemberEntityIds
}
}

_, err = client.Logical().Write(path, data)
if err != nil {
return fmt.Errorf("error updating IdentityGroupMemberEntityIds %q: %s", id, err)
}
log.Printf("[DEBUG] Updated IdentityGroupMemberEntityIds %q", id)

return nil
}
Loading

0 comments on commit af1db7e

Please sign in to comment.