From b8422d7b6b4e66c9126a6fca5a8793a8faf2b8bc Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Tue, 8 Nov 2022 15:22:13 +0000 Subject: [PATCH] Adding new resource identity_platform_project_default_config to support Email/Password Auth (#6679) * Adding new resource identity_platform_project_default_config to support Email/Password Auth * Addressing the review comments Signed-off-by: Modular Magician --- .changelog/6679.txt | 3 + google/provider.go | 5 +- ...dentity_platform_project_default_config.go | 753 ++++++++++++++++++ ...m_project_default_config_generated_test.go | 129 +++ ...orm_project_default_config_sweeper_test.go | 124 +++ ...tform_project_default_config.html.markdown | 186 +++++ 6 files changed, 1198 insertions(+), 2 deletions(-) create mode 100644 .changelog/6679.txt create mode 100644 google/resource_identity_platform_project_default_config.go create mode 100644 google/resource_identity_platform_project_default_config_generated_test.go create mode 100644 google/resource_identity_platform_project_default_config_sweeper_test.go create mode 100644 website/docs/r/identity_platform_project_default_config.html.markdown diff --git a/.changelog/6679.txt b/.changelog/6679.txt new file mode 100644 index 00000000000..1c01ce8f3fc --- /dev/null +++ b/.changelog/6679.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +`identity_platform_project_default_config` +``` diff --git a/google/provider.go b/google/provider.go index 9102558dbb9..4355d463220 100644 --- a/google/provider.go +++ b/google/provider.go @@ -917,9 +917,9 @@ func Provider() *schema.Provider { return provider } -// Generated resources: 245 +// Generated resources: 246 // Generated IAM resources: 150 -// Total generated resources: 395 +// Total generated resources: 396 func ResourceMap() map[string]*schema.Resource { resourceMap, _ := ResourceMapWithErrors() return resourceMap @@ -1210,6 +1210,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_identity_platform_oauth_idp_config": resourceIdentityPlatformOauthIdpConfig(), "google_identity_platform_tenant_oauth_idp_config": resourceIdentityPlatformTenantOauthIdpConfig(), "google_identity_platform_tenant": resourceIdentityPlatformTenant(), + "google_identity_platform_project_default_config": resourceIdentityPlatformProjectDefaultConfig(), "google_kms_key_ring": resourceKMSKeyRing(), "google_kms_crypto_key": resourceKMSCryptoKey(), "google_kms_crypto_key_version": resourceKMSCryptoKeyVersion(), diff --git a/google/resource_identity_platform_project_default_config.go b/google/resource_identity_platform_project_default_config.go new file mode 100644 index 00000000000..04197a24617 --- /dev/null +++ b/google/resource_identity_platform_project_default_config.go @@ -0,0 +1,753 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "log" + "reflect" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceIdentityPlatformProjectDefaultConfig() *schema.Resource { + return &schema.Resource{ + Create: resourceIdentityPlatformProjectDefaultConfigCreate, + Read: resourceIdentityPlatformProjectDefaultConfigRead, + Update: resourceIdentityPlatformProjectDefaultConfigUpdate, + Delete: resourceIdentityPlatformProjectDefaultConfigDelete, + + Importer: &schema.ResourceImporter{ + State: resourceIdentityPlatformProjectDefaultConfigImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(20 * time.Minute), + Update: schema.DefaultTimeout(20 * time.Minute), + Delete: schema.DefaultTimeout(20 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "sign_in": { + Type: schema.TypeList, + Optional: true, + Description: `Configuration related to local sign in methods.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "allow_duplicate_emails": { + Type: schema.TypeBool, + Optional: true, + Description: `Whether to allow more than one account to have the same email.`, + }, + "anonymous": { + Type: schema.TypeList, + Optional: true, + Description: `Configuration options related to authenticating an anonymous user.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": { + Type: schema.TypeBool, + Required: true, + Description: `Whether anonymous user auth is enabled for the project or not.`, + }, + }, + }, + }, + "email": { + Type: schema.TypeList, + Optional: true, + Description: `Configuration options related to authenticating a user by their email address.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": { + Type: schema.TypeBool, + Optional: true, + Description: `Whether email auth is enabled for the project or not.`, + }, + "password_required": { + Type: schema.TypeBool, + Optional: true, + Description: `Whether a password is required for email auth or not. If true, both an email and +password must be provided to sign in. If false, a user may sign in via either +email/password or email link.`, + }, + }, + }, + }, + "phone_number": { + Type: schema.TypeList, + Optional: true, + Description: `Configuration options related to authenticated a user by their phone number.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": { + Type: schema.TypeBool, + Optional: true, + Description: `Whether phone number auth is enabled for the project or not.`, + }, + "test_phone_numbers": { + Type: schema.TypeMap, + Optional: true, + Description: `A map of that can be used for phone auth testing.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + "hash_config": { + Type: schema.TypeList, + Computed: true, + Description: `Output only. Hash config information.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "algorithm": { + Type: schema.TypeString, + Computed: true, + Description: `Different password hash algorithms used in Identity Toolkit.`, + }, + "memory_cost": { + Type: schema.TypeInt, + Computed: true, + Description: `Memory cost for hash calculation. Used by scrypt and other similar password derivation algorithms. See https://tools.ietf.org/html/rfc7914 for explanation of field.`, + }, + "rounds": { + Type: schema.TypeInt, + Computed: true, + Description: `How many rounds for hash calculation. Used by scrypt and other similar password derivation algorithms.`, + }, + "salt_separator": { + Type: schema.TypeString, + Computed: true, + Description: `Non-printable character to be inserted between the salt and plain text password in base64.`, + }, + "signer_key": { + Type: schema.TypeString, + Computed: true, + Description: `Signer key in base64.`, + }, + }, + }, + }, + }, + }, + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: `The name of the Config resource. Example: "projects/my-awesome-project/config"`, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + UseJSONNumber: true, + } +} + +func resourceIdentityPlatformProjectDefaultConfigCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + signInProp, err := expandIdentityPlatformProjectDefaultConfigSignIn(d.Get("sign_in"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("sign_in"); !isEmptyValue(reflect.ValueOf(signInProp)) && (ok || !reflect.DeepEqual(v, signInProp)) { + obj["signIn"] = signInProp + } + + url, err := replaceVars(d, config, "{{IdentityPlatformBasePath}}projects/{{project}}/config") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new ProjectDefaultConfig: %#v", obj) + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for ProjectDefaultConfig: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "PATCH", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return fmt.Errorf("Error creating ProjectDefaultConfig: %s", err) + } + if err := d.Set("name", flattenIdentityPlatformProjectDefaultConfigName(res["name"], d, config)); err != nil { + return fmt.Errorf(`Error setting computed identity field "name": %s`, err) + } + + // Store the ID now + id, err := replaceVars(d, config, "{{project}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + log.Printf("[DEBUG] Finished creating ProjectDefaultConfig %q: %#v", d.Id(), res) + + return resourceIdentityPlatformProjectDefaultConfigRead(d, meta) +} + +func resourceIdentityPlatformProjectDefaultConfigRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{IdentityPlatformBasePath}}projects/{{project}}/config") + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for ProjectDefaultConfig: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequest(config, "GET", billingProject, url, userAgent, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("IdentityPlatformProjectDefaultConfig %q", d.Id())) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading ProjectDefaultConfig: %s", err) + } + + if err := d.Set("name", flattenIdentityPlatformProjectDefaultConfigName(res["name"], d, config)); err != nil { + return fmt.Errorf("Error reading ProjectDefaultConfig: %s", err) + } + if err := d.Set("sign_in", flattenIdentityPlatformProjectDefaultConfigSignIn(res["signIn"], d, config)); err != nil { + return fmt.Errorf("Error reading ProjectDefaultConfig: %s", err) + } + + return nil +} + +func resourceIdentityPlatformProjectDefaultConfigUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for ProjectDefaultConfig: %s", err) + } + billingProject = project + + obj := make(map[string]interface{}) + signInProp, err := expandIdentityPlatformProjectDefaultConfigSignIn(d.Get("sign_in"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("sign_in"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, signInProp)) { + obj["signIn"] = signInProp + } + + url, err := replaceVars(d, config, "{{IdentityPlatformBasePath}}projects/{{project}}/config") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating ProjectDefaultConfig %q: %#v", d.Id(), obj) + updateMask := []string{} + + if d.HasChange("sign_in") { + updateMask = append(updateMask, "signIn") + } + // updateMask is a URL parameter but not present in the schema, so replaceVars + // won't set it + url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")}) + if err != nil { + return err + } + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "PATCH", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return fmt.Errorf("Error updating ProjectDefaultConfig %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating ProjectDefaultConfig %q: %#v", d.Id(), res) + } + + return resourceIdentityPlatformProjectDefaultConfigRead(d, meta) +} + +func resourceIdentityPlatformProjectDefaultConfigDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for ProjectDefaultConfig: %s", err) + } + billingProject = project + + url, err := replaceVars(d, config, "{{IdentityPlatformBasePath}}projects/{{project}}/config") + if err != nil { + return err + } + + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting ProjectDefaultConfig %q", d.Id()) + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "PATCH", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "ProjectDefaultConfig") + } + + log.Printf("[DEBUG] Finished deleting ProjectDefaultConfig %q: %#v", d.Id(), res) + return nil +} + +func resourceIdentityPlatformProjectDefaultConfigImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if err := parseImportId([]string{ + "projects/(?P[^/]+)/config/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := replaceVars(d, config, "{{project}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenIdentityPlatformProjectDefaultConfigName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenIdentityPlatformProjectDefaultConfigSignIn(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["email"] = + flattenIdentityPlatformProjectDefaultConfigSignInEmail(original["email"], d, config) + transformed["phone_number"] = + flattenIdentityPlatformProjectDefaultConfigSignInPhoneNumber(original["phoneNumber"], d, config) + transformed["anonymous"] = + flattenIdentityPlatformProjectDefaultConfigSignInAnonymous(original["anonymous"], d, config) + transformed["allow_duplicate_emails"] = + flattenIdentityPlatformProjectDefaultConfigSignInAllowDuplicateEmails(original["allowDuplicateEmails"], d, config) + transformed["hash_config"] = + flattenIdentityPlatformProjectDefaultConfigSignInHashConfig(original["hashConfig"], d, config) + return []interface{}{transformed} +} +func flattenIdentityPlatformProjectDefaultConfigSignInEmail(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["enabled"] = + flattenIdentityPlatformProjectDefaultConfigSignInEmailEnabled(original["enabled"], d, config) + transformed["password_required"] = + flattenIdentityPlatformProjectDefaultConfigSignInEmailPasswordRequired(original["passwordRequired"], d, config) + return []interface{}{transformed} +} +func flattenIdentityPlatformProjectDefaultConfigSignInEmailEnabled(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenIdentityPlatformProjectDefaultConfigSignInEmailPasswordRequired(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenIdentityPlatformProjectDefaultConfigSignInPhoneNumber(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["enabled"] = + flattenIdentityPlatformProjectDefaultConfigSignInPhoneNumberEnabled(original["enabled"], d, config) + transformed["test_phone_numbers"] = + flattenIdentityPlatformProjectDefaultConfigSignInPhoneNumberTestPhoneNumbers(original["testPhoneNumbers"], d, config) + return []interface{}{transformed} +} +func flattenIdentityPlatformProjectDefaultConfigSignInPhoneNumberEnabled(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenIdentityPlatformProjectDefaultConfigSignInPhoneNumberTestPhoneNumbers(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenIdentityPlatformProjectDefaultConfigSignInAnonymous(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["enabled"] = + flattenIdentityPlatformProjectDefaultConfigSignInAnonymousEnabled(original["enabled"], d, config) + return []interface{}{transformed} +} +func flattenIdentityPlatformProjectDefaultConfigSignInAnonymousEnabled(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenIdentityPlatformProjectDefaultConfigSignInAllowDuplicateEmails(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenIdentityPlatformProjectDefaultConfigSignInHashConfig(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["algorithm"] = + flattenIdentityPlatformProjectDefaultConfigSignInHashConfigAlgorithm(original["algorithm"], d, config) + transformed["signer_key"] = + flattenIdentityPlatformProjectDefaultConfigSignInHashConfigSignerKey(original["signerKey"], d, config) + transformed["salt_separator"] = + flattenIdentityPlatformProjectDefaultConfigSignInHashConfigSaltSeparator(original["saltSeparator"], d, config) + transformed["rounds"] = + flattenIdentityPlatformProjectDefaultConfigSignInHashConfigRounds(original["rounds"], d, config) + transformed["memory_cost"] = + flattenIdentityPlatformProjectDefaultConfigSignInHashConfigMemoryCost(original["memoryCost"], d, config) + return []interface{}{transformed} +} +func flattenIdentityPlatformProjectDefaultConfigSignInHashConfigAlgorithm(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenIdentityPlatformProjectDefaultConfigSignInHashConfigSignerKey(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenIdentityPlatformProjectDefaultConfigSignInHashConfigSaltSeparator(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenIdentityPlatformProjectDefaultConfigSignInHashConfigRounds(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenIdentityPlatformProjectDefaultConfigSignInHashConfigMemoryCost(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func expandIdentityPlatformProjectDefaultConfigSignIn(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedEmail, err := expandIdentityPlatformProjectDefaultConfigSignInEmail(original["email"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEmail); val.IsValid() && !isEmptyValue(val) { + transformed["email"] = transformedEmail + } + + transformedPhoneNumber, err := expandIdentityPlatformProjectDefaultConfigSignInPhoneNumber(original["phone_number"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPhoneNumber); val.IsValid() && !isEmptyValue(val) { + transformed["phoneNumber"] = transformedPhoneNumber + } + + transformedAnonymous, err := expandIdentityPlatformProjectDefaultConfigSignInAnonymous(original["anonymous"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAnonymous); val.IsValid() && !isEmptyValue(val) { + transformed["anonymous"] = transformedAnonymous + } + + transformedAllowDuplicateEmails, err := expandIdentityPlatformProjectDefaultConfigSignInAllowDuplicateEmails(original["allow_duplicate_emails"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAllowDuplicateEmails); val.IsValid() && !isEmptyValue(val) { + transformed["allowDuplicateEmails"] = transformedAllowDuplicateEmails + } + + transformedHashConfig, err := expandIdentityPlatformProjectDefaultConfigSignInHashConfig(original["hash_config"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHashConfig); val.IsValid() && !isEmptyValue(val) { + transformed["hashConfig"] = transformedHashConfig + } + + return transformed, nil +} + +func expandIdentityPlatformProjectDefaultConfigSignInEmail(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedEnabled, err := expandIdentityPlatformProjectDefaultConfigSignInEmailEnabled(original["enabled"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnabled); val.IsValid() && !isEmptyValue(val) { + transformed["enabled"] = transformedEnabled + } + + transformedPasswordRequired, err := expandIdentityPlatformProjectDefaultConfigSignInEmailPasswordRequired(original["password_required"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPasswordRequired); val.IsValid() && !isEmptyValue(val) { + transformed["passwordRequired"] = transformedPasswordRequired + } + + return transformed, nil +} + +func expandIdentityPlatformProjectDefaultConfigSignInEmailEnabled(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandIdentityPlatformProjectDefaultConfigSignInEmailPasswordRequired(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandIdentityPlatformProjectDefaultConfigSignInPhoneNumber(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedEnabled, err := expandIdentityPlatformProjectDefaultConfigSignInPhoneNumberEnabled(original["enabled"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnabled); val.IsValid() && !isEmptyValue(val) { + transformed["enabled"] = transformedEnabled + } + + transformedTestPhoneNumbers, err := expandIdentityPlatformProjectDefaultConfigSignInPhoneNumberTestPhoneNumbers(original["test_phone_numbers"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTestPhoneNumbers); val.IsValid() && !isEmptyValue(val) { + transformed["testPhoneNumbers"] = transformedTestPhoneNumbers + } + + return transformed, nil +} + +func expandIdentityPlatformProjectDefaultConfigSignInPhoneNumberEnabled(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandIdentityPlatformProjectDefaultConfigSignInPhoneNumberTestPhoneNumbers(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} + +func expandIdentityPlatformProjectDefaultConfigSignInAnonymous(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedEnabled, err := expandIdentityPlatformProjectDefaultConfigSignInAnonymousEnabled(original["enabled"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnabled); val.IsValid() && !isEmptyValue(val) { + transformed["enabled"] = transformedEnabled + } + + return transformed, nil +} + +func expandIdentityPlatformProjectDefaultConfigSignInAnonymousEnabled(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandIdentityPlatformProjectDefaultConfigSignInAllowDuplicateEmails(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandIdentityPlatformProjectDefaultConfigSignInHashConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedAlgorithm, err := expandIdentityPlatformProjectDefaultConfigSignInHashConfigAlgorithm(original["algorithm"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAlgorithm); val.IsValid() && !isEmptyValue(val) { + transformed["algorithm"] = transformedAlgorithm + } + + transformedSignerKey, err := expandIdentityPlatformProjectDefaultConfigSignInHashConfigSignerKey(original["signer_key"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSignerKey); val.IsValid() && !isEmptyValue(val) { + transformed["signerKey"] = transformedSignerKey + } + + transformedSaltSeparator, err := expandIdentityPlatformProjectDefaultConfigSignInHashConfigSaltSeparator(original["salt_separator"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSaltSeparator); val.IsValid() && !isEmptyValue(val) { + transformed["saltSeparator"] = transformedSaltSeparator + } + + transformedRounds, err := expandIdentityPlatformProjectDefaultConfigSignInHashConfigRounds(original["rounds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRounds); val.IsValid() && !isEmptyValue(val) { + transformed["rounds"] = transformedRounds + } + + transformedMemoryCost, err := expandIdentityPlatformProjectDefaultConfigSignInHashConfigMemoryCost(original["memory_cost"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMemoryCost); val.IsValid() && !isEmptyValue(val) { + transformed["memoryCost"] = transformedMemoryCost + } + + return transformed, nil +} + +func expandIdentityPlatformProjectDefaultConfigSignInHashConfigAlgorithm(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandIdentityPlatformProjectDefaultConfigSignInHashConfigSignerKey(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandIdentityPlatformProjectDefaultConfigSignInHashConfigSaltSeparator(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandIdentityPlatformProjectDefaultConfigSignInHashConfigRounds(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandIdentityPlatformProjectDefaultConfigSignInHashConfigMemoryCost(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} diff --git a/google/resource_identity_platform_project_default_config_generated_test.go b/google/resource_identity_platform_project_default_config_generated_test.go new file mode 100644 index 00000000000..bd66be6f14b --- /dev/null +++ b/google/resource_identity_platform_project_default_config_generated_test.go @@ -0,0 +1,129 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccIdentityPlatformProjectDefaultConfig_identityPlatformProjectDefaultConfigExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckIdentityPlatformProjectDefaultConfigDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccIdentityPlatformProjectDefaultConfig_identityPlatformProjectDefaultConfigExample(context), + }, + { + ResourceName: "google_identity_platform_project_default_config.default", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccIdentityPlatformProjectDefaultConfig_identityPlatformProjectDefaultConfigExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_identity_platform_project_default_config" "default" { + sign_in { + allow_duplicate_emails = true + + anonymous { + enabled = true + } + + email { + enabled = true + password_required = false + } + + phone_number { + enabled = true + test_phone_numbers = { + "+11231231234" = "000000" + } + } + } +} +`, context) +} + +func testAccCheckIdentityPlatformProjectDefaultConfigDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "google_identity_platform_project_default_config" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := googleProviderConfig(t) + + url, err := replaceVarsForTest(config, rs, "{{IdentityPlatformBasePath}}projects/{{project}}/config") + if err != nil { + return err + } + + billingProject := "" + + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + res, err := sendRequest(config, "GET", billingProject, url, config.userAgent, nil) + if err != nil { + return fmt.Errorf("something went wrong trying to get the IdentityPlatformProjectDefaultConfig at %s", url) + } + + signIn := res["signIn"] + if signIn == nil { + return nil + } + + original := signIn.(map[string]interface{}) + if len(original) == 0 { + return nil + } + + transformed := make(map[string]interface{}) + transformed["email"] = + flattenIdentityPlatformProjectDefaultConfigSignInEmail(original["email"], nil, nil) + transformed["phone_number"] = + flattenIdentityPlatformProjectDefaultConfigSignInPhoneNumber(original["phoneNumber"], nil, nil) + transformed["anonymous"] = + flattenIdentityPlatformProjectDefaultConfigSignInAnonymous(original["anonymous"], nil, nil) + + if transformed["email"] != nil || transformed["phone_number"] != nil || transformed["anonymous"] != nil { + return fmt.Errorf("IdentityPlatformProjectDefaultConfig still exists at %s", url) + } + } + + return nil + } +} diff --git a/google/resource_identity_platform_project_default_config_sweeper_test.go b/google/resource_identity_platform_project_default_config_sweeper_test.go new file mode 100644 index 00000000000..36ec863b81c --- /dev/null +++ b/google/resource_identity_platform_project_default_config_sweeper_test.go @@ -0,0 +1,124 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "context" + "log" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func init() { + resource.AddTestSweepers("IdentityPlatformProjectDefaultConfig", &resource.Sweeper{ + Name: "IdentityPlatformProjectDefaultConfig", + F: testSweepIdentityPlatformProjectDefaultConfig, + }) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepIdentityPlatformProjectDefaultConfig(region string) error { + resourceName := "IdentityPlatformProjectDefaultConfig" + log.Printf("[INFO][SWEEPER_LOG] Starting sweeper for %s", resourceName) + + config, err := sharedConfigForRegion(region) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error getting shared config for region: %s", err) + return err + } + + err = config.LoadAndValidate(context.Background()) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error loading: %s", err) + return err + } + + t := &testing.T{} + billingId := getTestBillingAccountFromEnv(t) + + // Setup variables to replace in list template + d := &ResourceDataMock{ + FieldsInSchema: map[string]interface{}{ + "project": config.Project, + "region": region, + "location": region, + "zone": "-", + "billing_account": billingId, + }, + } + + listTemplate := strings.Split("https://identitytoolkit.googleapis.com/v2/projects/{{project}}/config", "?")[0] + listUrl, err := replaceVars(d, config, listTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing sweeper list url: %s", err) + return nil + } + + res, err := sendRequest(config, "GET", config.Project, listUrl, config.userAgent, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) + return nil + } + + resourceList, ok := res["projectDefaultConfigs"] + if !ok { + log.Printf("[INFO][SWEEPER_LOG] Nothing found in response.") + return nil + } + + rl := resourceList.([]interface{}) + + log.Printf("[INFO][SWEEPER_LOG] Found %d items in %s list response.", len(rl), resourceName) + // Keep count of items that aren't sweepable for logging. + nonPrefixCount := 0 + for _, ri := range rl { + obj := ri.(map[string]interface{}) + if obj["name"] == nil { + log.Printf("[INFO][SWEEPER_LOG] %s resource name was nil", resourceName) + return nil + } + + name := GetResourceNameFromSelfLink(obj["name"].(string)) + // Skip resources that shouldn't be sweeped + if !isSweepableTestResource(name) { + nonPrefixCount++ + continue + } + + deleteTemplate := "https://identitytoolkit.googleapis.com/v2/projects/{{project}}/config" + deleteUrl, err := replaceVars(d, config, deleteTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing delete url: %s", err) + return nil + } + deleteUrl = deleteUrl + name + + // Don't wait on operations as we may have a lot to delete + _, err = sendRequest(config, "DELETE", config.Project, deleteUrl, config.userAgent, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error deleting for url %s : %s", deleteUrl, err) + } else { + log.Printf("[INFO][SWEEPER_LOG] Sent delete request for %s resource: %s", resourceName, name) + } + } + + if nonPrefixCount > 0 { + log.Printf("[INFO][SWEEPER_LOG] %d items were non-sweepable and skipped.", nonPrefixCount) + } + + return nil +} diff --git a/website/docs/r/identity_platform_project_default_config.html.markdown b/website/docs/r/identity_platform_project_default_config.html.markdown new file mode 100644 index 00000000000..431d0a0b8e6 --- /dev/null +++ b/website/docs/r/identity_platform_project_default_config.html.markdown @@ -0,0 +1,186 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** Type: MMv1 *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in +# .github/CONTRIBUTING.md. +# +# ---------------------------------------------------------------------------- +subcategory: "Identity Platform" +page_title: "Google: google_identity_platform_project_default_config" +description: |- + There is no persistent data associated with this resource. +--- + +# google\_identity\_platform\_project\_default\_config + +There is no persistent data associated with this resource. + + + +~> **Warning:** If you are using User ADCs (Application Default Credentials) with this resource, +you must specify a `billing_project` and set `user_project_override` to true +in the provider configuration. Otherwise the ACM API will return a 403 error. +Your account must have the `serviceusage.services.use` permission on the +`billing_project` you defined. + + +## Example Usage - Identity Platform Project Default Config + + +```hcl +resource "google_identity_platform_project_default_config" "default" { + sign_in { + allow_duplicate_emails = true + + anonymous { + enabled = true + } + + email { + enabled = true + password_required = false + } + + phone_number { + enabled = true + test_phone_numbers = { + "+11231231234" = "000000" + } + } + } +} +``` + +## Argument Reference + +The following arguments are supported: + + + +- - - + + +* `sign_in` - + (Optional) + Configuration related to local sign in methods. + Structure is [documented below](#nested_sign_in). + +* `project` - (Optional) The ID of the project in which the resource belongs. + If it is not provided, the provider project is used. + + +The `sign_in` block supports: + +* `email` - + (Optional) + Configuration options related to authenticating a user by their email address. + Structure is [documented below](#nested_email). + +* `phone_number` - + (Optional) + Configuration options related to authenticated a user by their phone number. + Structure is [documented below](#nested_phone_number). + +* `anonymous` - + (Optional) + Configuration options related to authenticating an anonymous user. + Structure is [documented below](#nested_anonymous). + +* `allow_duplicate_emails` - + (Optional) + Whether to allow more than one account to have the same email. + +* `hash_config` - + Output only. Hash config information. + Structure is [documented below](#nested_hash_config). + + +The `email` block supports: + +* `enabled` - + (Optional) + Whether email auth is enabled for the project or not. + +* `password_required` - + (Optional) + Whether a password is required for email auth or not. If true, both an email and + password must be provided to sign in. If false, a user may sign in via either + email/password or email link. + +The `phone_number` block supports: + +* `enabled` - + (Optional) + Whether phone number auth is enabled for the project or not. + +* `test_phone_numbers` - + (Optional) + A map of that can be used for phone auth testing. + +The `anonymous` block supports: + +* `enabled` - + (Required) + Whether anonymous user auth is enabled for the project or not. + +The `hash_config` block contains: + +* `algorithm` - + Different password hash algorithms used in Identity Toolkit. + +* `signer_key` - + Signer key in base64. + +* `salt_separator` - + Non-printable character to be inserted between the salt and plain text password in base64. + +* `rounds` - + How many rounds for hash calculation. Used by scrypt and other similar password derivation algorithms. + +* `memory_cost` - + Memory cost for hash calculation. Used by scrypt and other similar password derivation algorithms. See https://tools.ietf.org/html/rfc7914 for explanation of field. + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + +* `id` - an identifier for the resource with format `{{project}}` + +* `name` - + The name of the Config resource. Example: "projects/my-awesome-project/config" + + +## Timeouts + +This resource provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - Default is 20 minutes. +- `update` - Default is 20 minutes. +- `delete` - Default is 20 minutes. + +## Import + + +ProjectDefaultConfig can be imported using any of these accepted formats: + +``` +$ terraform import google_identity_platform_project_default_config.default projects/{{project}}/config/{{name}} +$ terraform import google_identity_platform_project_default_config.default {{project}}/{{name}} +$ terraform import google_identity_platform_project_default_config.default {{name}} +``` + +## User Project Overrides + +This resource supports [User Project Overrides](https://www.terraform.io/docs/providers/google/guides/provider_reference.html#user_project_override).