Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for external Access Groups #633

Merged
merged 7 commits into from
Mar 25, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 82 additions & 11 deletions cloudflare/resource_cloudflare_access_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,38 +197,105 @@ func resourceCloudflareAccessGroupImport(d *schema.ResourceData, meta interface{
func appendConditionalAccessGroupFields(group cloudflare.AccessGroup, d *schema.ResourceData) cloudflare.AccessGroup {
exclude := d.Get("exclude").([]interface{})
for _, value := range exclude {
group.Exclude = buildAccessGroupCondition(value.(map[string]interface{}))
group.Exclude = BuildAccessGroupCondition(value.(map[string]interface{}))
}

require := d.Get("require").([]interface{})
for _, value := range require {
group.Require = buildAccessGroupCondition(value.(map[string]interface{}))
group.Require = BuildAccessGroupCondition(value.(map[string]interface{}))
}

include := d.Get("include").([]interface{})
for _, value := range include {
group.Include = buildAccessGroupCondition(value.(map[string]interface{}))
group.Include = BuildAccessGroupCondition(value.(map[string]interface{}))
}

return group
}

// buildAccessGroupCondition iterates the provided `map` of values and
// BuildAccessGroupCondition iterates the provided `map` of values and
// generates the required (repetitive) structs.
//
// Returns the intended combination structure of AccessGroupEmail,
// AccessGroupEmailDomain, AccessGroupIP and AccessGroupEveryone
// structs.
func buildAccessGroupCondition(options map[string]interface{}) []interface{} {
// Returns the intended combination structure of Access Groups to build the
// desired policy.
func BuildAccessGroupCondition(options map[string]interface{}) []interface{} {
var group []interface{}
for accessGroupType, values := range options {
// Since AccessGroupEveryone is a single boolean, we don't need to
// iterate over the values like the others so we treat it a little differently.
if accessGroupType == "everyone" {
if values == true {
log.Printf("[DEBUG] values for everyone %s", values)
group = append(group, cloudflare.AccessGroupEveryone{})
}
} else if accessGroupType == "any_valid_service_token" {
if values == true {
group = append(group, cloudflare.AccessGroupAnyValidServiceToken{})
}
} else if accessGroupType == "certificate" {
if values == true {
group = append(group, cloudflare.AccessGroupCertificate{})
}
} else if accessGroupType == "common_name" {
if values != "" {
group = append(group, cloudflare.AccessGroupCertificateCommonName{CommonName: struct {
CommonName string `json:"common_name"`
}{CommonName: values.(string)}})
}
} else if accessGroupType == "gsuite" {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite the mess. I'm going to look at cleaning this giant block of conditionals up in another PR.

for _, v := range values.([]interface{}) {
gsuiteCfg := v.(map[string]interface{})
group = append(group, cloudflare.AccessGroupGSuite{Gsuite: struct {
Email string `json:"email"`
IdentityProviderID string `json:"identity_provider_id"`
}{
Email: gsuiteCfg["email"].(string),
IdentityProviderID: gsuiteCfg["identity_provider_id"].(string),
}})
}
} else if accessGroupType == "github" {
for _, v := range values.([]interface{}) {
githubCfg := v.(map[string]interface{})
group = append(group, cloudflare.AccessGroupGitHub{GitHubOrganization: struct {
Name string `json:"name"`
IdentityProviderID string `json:"identity_provider_id"`
}{
Name: githubCfg["name"].(string),
IdentityProviderID: githubCfg["identity_provider_id"].(string),
}})
}
} else if accessGroupType == "azure" {
for _, v := range values.([]interface{}) {
azureCfg := v.(map[string]interface{})
group = append(group, cloudflare.AccessGroupAzure{AzureAD: struct {
ID string `json:"id"`
IdentityProviderID string `json:"identity_provider_id"`
}{
ID: azureCfg["id"].(string),
IdentityProviderID: azureCfg["identity_provider_id"].(string),
}})
}
} else if accessGroupType == "okta" {
for _, v := range values.([]interface{}) {
oktaCfg := v.(map[string]interface{})
group = append(group, cloudflare.AccessGroupOkta{Otka: struct {
Name string `json:"name"`
IdentityProviderID string `json:"identity_provider_id"`
}{
Name: oktaCfg["name"].(string),
IdentityProviderID: oktaCfg["identity_provider_id"].(string),
}})
}
} else if accessGroupType == "saml" {
for _, v := range values.([]interface{}) {
samlCfg := v.(map[string]interface{})
group = append(group, cloudflare.AccessGroupSAML{Saml: struct {
AttributeName string `json:"attribute_name"`
AttributeValue string `json:"attribute_value"`
IdentityProviderID string `json:"identity_provider_id"`
}{
AttributeName: samlCfg["attribute_name"].(string),
AttributeValue: samlCfg["attribute_value"].(string),
IdentityProviderID: samlCfg["identity_provider_id"].(string),
}})
}
} else {
for _, value := range values.([]interface{}) {
switch accessGroupType {
Expand All @@ -244,6 +311,10 @@ func buildAccessGroupCondition(options map[string]interface{}) []interface{} {
group = append(group, cloudflare.AccessGroupIP{IP: struct {
IP string `json:"ip"`
}{IP: value.(string)}})
case "service_token":
group = append(group, cloudflare.AccessGroupServiceToken{ServiceToken: struct {
ID string `json:"token_id"`
}{ID: value.(string)}})
case "group":
group = append(group, cloudflare.AccessGroupAccessGroup{Group: struct {
ID string `json:"id"`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ func testSweepCloudflareAccessIdentityProviders(r string) error {
}

for _, idp := range accessIDPs {
// e75d3adf-964b-4b15-acff-e7e20838c33c is a static GitHub integration used
// in acceptance tests so we don't want to remove it.
if idp.ID == "e75d3adf-964b-4b15-acff-e7e20838c33c" {
continue
}

log.Printf("[INFO] Deleting Access Identity Provider ID: %s", idp.ID)
_, err := client.DeleteAccessIdentityProvider(accountID, idp.ID)

Expand Down
152 changes: 96 additions & 56 deletions cloudflare/resource_cloudflare_access_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"log"
"strings"

cloudflare "github.com/cloudflare/cloudflare-go"
"github.com/cloudflare/cloudflare-go"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)
Expand Down Expand Up @@ -108,6 +108,98 @@ var policyOptionElement = &schema.Resource{
Type: schema.TypeBool,
Optional: true,
},
"certificate": {
Type: schema.TypeBool,
Optional: true,
},
"common_name": {
Type: schema.TypeString,
Optional: true,
},
"gsuite": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"email": {
Type: schema.TypeString,
Optional: true,
},
"identity_provider_id": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
"github": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Optional: true,
},
"identity_provider_id": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
"azure": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Optional: true,
},
"identity_provider_id": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
"okta": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Optional: true,
},
"identity_provider_id": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
"saml": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"attribute_name": {
Type: schema.TypeString,
Optional: true,
},
"attribute_value": {
Type: schema.TypeString,
Optional: true,
},
"identity_provider_id": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
},
}

Expand Down Expand Up @@ -231,69 +323,17 @@ func resourceCloudflareAccessPolicyImport(d *schema.ResourceData, meta interface
func appendConditionalAccessPolicyFields(policy cloudflare.AccessPolicy, d *schema.ResourceData) cloudflare.AccessPolicy {
exclude := d.Get("exclude").([]interface{})
for _, value := range exclude {
policy.Exclude = buildAccessPolicyCondition(value.(map[string]interface{}))
policy.Exclude = BuildAccessGroupCondition(value.(map[string]interface{}))
}

require := d.Get("require").([]interface{})
for _, value := range require {
policy.Require = buildAccessPolicyCondition(value.(map[string]interface{}))
policy.Require = BuildAccessGroupCondition(value.(map[string]interface{}))
}

include := d.Get("include").([]interface{})
for _, value := range include {
policy.Include = buildAccessPolicyCondition(value.(map[string]interface{}))
}

return policy
}

// buildAccessPolicyCondition iterates the provided `map` of values and
// generates the required (repetitive) structs.
//
// Returns the intended combination structure of AccessPolicyEmail,
// AccessPolicyEmailDomain, AccessPolicyIP and AccessPolicyEveryone
// structs.
func buildAccessPolicyCondition(options map[string]interface{}) []interface{} {
var policy []interface{}
for accessPolicyType, values := range options {
// Since AccessPolicyEveryone is a single boolean, we don't need to
// iterate over the values like the others so we treat it a little differently.
if accessPolicyType == "everyone" {
if values == true {
log.Printf("[DEBUG] values for everyone %s", values)
policy = append(policy, cloudflare.AccessPolicyEveryone{})
}
} else if accessPolicyType == "any_valid_service_token" {
if values == true {
log.Printf("[DEBUG] values for any_valid_service_token %s", values)
policy = append(policy, cloudflare.AccessPolicyAnyValidServiceToken{})
}
} else {
for _, value := range values.([]interface{}) {
switch accessPolicyType {
case "email":
policy = append(policy, cloudflare.AccessPolicyEmail{Email: struct {
Email string `json:"email"`
}{Email: value.(string)}})
case "email_domain":
policy = append(policy, cloudflare.AccessPolicyEmailDomain{EmailDomain: struct {
Domain string `json:"domain"`
}{Domain: value.(string)}})
case "ip":
policy = append(policy, cloudflare.AccessPolicyIP{IP: struct {
IP string `json:"ip"`
}{IP: value.(string)}})
case "service_token":
policy = append(policy, cloudflare.AccessPolicyServiceToken{ServiceToken: struct {
ID string `json:"token_id"`
}{ID: value.(string)}})
case "group":
policy = append(policy, cloudflare.AccessPolicyAccessGroup{Group: struct {
ID string `json:"id"`
}{ID: value.(string)}})
}
}
}
policy.Include = BuildAccessGroupCondition(value.(map[string]interface{}))
}

return policy
Expand Down
Loading