From 67ac9e8a103d926b178e10185a24b3f0ce6aee53 Mon Sep 17 00:00:00 2001 From: martinstibbe <33664051+martinstibbe@users.noreply.github.com> Date: Wed, 29 Jun 2022 19:13:34 -0500 Subject: [PATCH] INTMDB-301: Feature add: Add support for managment of federated authentication configuration (#742) * Add support for federated settings * Refactor for service pattern change * Refactor options and plural singular names * Update go SDK version * Add initial federated website docs * Provide examples for Federated settings * Sync go.mod to contain breaking change SDK change * Fix examples for lint error sync SDK to master * Add menu items for federation settings and upgrade guides * Remove Federated Docs from PR * PR Review updates * Add support for datasource tests * Fix lint fmt * Add identity_provider_id variable * Move identity_provider_id * Add role mappings datasource test * Add additional tests for federated resources * Lint check * Add additional datasources and tests * Remove unused function * Add support for role mappings and update tests and examples * Fix role mapping import to fit new style of parameters * Remove commented code * go mod tidy * Remove extra parameter user_conflicts * Add missing parameters and refactor name of variables * Update example * Update mongodbatlas/resource_mongodbatlas_federated_settings_identity_provider.go Co-authored-by: Andrea Angiolillo * go fmt * Add support for import tests * Add federated settings env variables to README remove upgrade guide * Remove cloud prefix from federated examples and resources idp_id to okta_idp_id * Add new sort to role mapping * Add README for federated example * Refactor alias mongodbatlas to matlas dead code removal import spaces Co-authored-by: Andrea Angiolillo --- README.md | 13 + examples/Federated-Settings/Readme.md | 59 +++ examples/Federated-Settings/main.tf | 50 ++ examples/Federated-Settings/output.tf | 15 + examples/Federated-Settings/provider.tf | 4 + examples/Federated-Settings/variables.tf | 28 ++ examples/Federated-Settings/versions.tf | 11 + go.sum | 4 - ..._source_mongodbatlas_federated_settings.go | 99 ++++ ...derated_settings_connected_organization.go | 170 +++++++ ...ed_settings_connected_organization_test.go | 70 +++ ...erated_settings_connected_organizations.go | 191 ++++++++ ...d_settings_connected_organizations_test.go | 69 +++ ...as_federated_settings_identity_provider.go | 273 +++++++++++ ...derated_settings_identity_provider_test.go | 45 ++ ...s_federated_settings_identity_providers.go | 427 ++++++++++++++++++ ...erated_settings_identity_providers_test.go | 69 +++ ...ated_settings_organization_role_mapping.go | 98 ++++ ...settings_organization_role_mapping_test.go | 49 ++ ...ted_settings_organization_role_mappings.go | 123 +++++ ...ettings_organization_role_mappings_test.go | 70 +++ ...ce_mongodbatlas_federated_settings_test.go | 72 +++ mongodbatlas/provider.go | 10 + mongodbatlas/provider_test.go | 8 + ...derated_settings_connected_organization.go | 211 +++++++++ ...ed_settings_connected_organization_test.go | 122 +++++ ...as_federated_settings_identity_provider.go | 265 +++++++++++ ...derated_settings_identity_provider_test.go | 128 ++++++ ...ated_settings_organization_role_mapping.go | 367 +++++++++++++++ ...settings_organization_role_mapping_test.go | 145 ++++++ 30 files changed, 3261 insertions(+), 4 deletions(-) create mode 100644 examples/Federated-Settings/Readme.md create mode 100644 examples/Federated-Settings/main.tf create mode 100644 examples/Federated-Settings/output.tf create mode 100644 examples/Federated-Settings/provider.tf create mode 100644 examples/Federated-Settings/variables.tf create mode 100644 examples/Federated-Settings/versions.tf create mode 100644 mongodbatlas/data_source_mongodbatlas_federated_settings.go create mode 100644 mongodbatlas/data_source_mongodbatlas_federated_settings_connected_organization.go create mode 100644 mongodbatlas/data_source_mongodbatlas_federated_settings_connected_organization_test.go create mode 100644 mongodbatlas/data_source_mongodbatlas_federated_settings_connected_organizations.go create mode 100644 mongodbatlas/data_source_mongodbatlas_federated_settings_connected_organizations_test.go create mode 100644 mongodbatlas/data_source_mongodbatlas_federated_settings_identity_provider.go create mode 100644 mongodbatlas/data_source_mongodbatlas_federated_settings_identity_provider_test.go create mode 100644 mongodbatlas/data_source_mongodbatlas_federated_settings_identity_providers.go create mode 100644 mongodbatlas/data_source_mongodbatlas_federated_settings_identity_providers_test.go create mode 100644 mongodbatlas/data_source_mongodbatlas_federated_settings_organization_role_mapping.go create mode 100644 mongodbatlas/data_source_mongodbatlas_federated_settings_organization_role_mapping_test.go create mode 100644 mongodbatlas/data_source_mongodbatlas_federated_settings_organization_role_mappings.go create mode 100644 mongodbatlas/data_source_mongodbatlas_federated_settings_organization_role_mappings_test.go create mode 100644 mongodbatlas/data_source_mongodbatlas_federated_settings_test.go create mode 100644 mongodbatlas/resource_mongodbatlas_federated_settings_connected_organization.go create mode 100644 mongodbatlas/resource_mongodbatlas_federated_settings_connected_organization_test.go create mode 100644 mongodbatlas/resource_mongodbatlas_federated_settings_identity_provider.go create mode 100644 mongodbatlas/resource_mongodbatlas_federated_settings_identity_provider_test.go create mode 100644 mongodbatlas/resource_mongodbatlas_federated_settings_organization_role_mapping.go create mode 100644 mongodbatlas/resource_mongodbatlas_federated_settings_organization_role_mapping_test.go diff --git a/README.md b/README.md index 168fc04712..b4406f78c6 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,19 @@ $ export MONGODB_ATLAS_API_KEYS_IDS= export SKIP_TEST_EXTERNAL_CREDENTIALS=TRUE ``` +- For `Federated Settings` resource configuration: +```sh +$ export MONGODB_ATLAS_FEDERATION_SETTINGS_ID= +$ export ONGODB_ATLAS_FEDERATED_ORG_ID= +$ export MONGODB_ATLAS_FEDERATED_PROJECT_ID= +$ export MONGODB_ATLAS_FEDERATED_GROUP_ID= +$ export MONGODB_ATLAS_FEDERATED_ROLE_MAPPING_ID= +$ export MONGODB_ATLAS_FEDERATED_OKTA_IDP_ID= +$ export MONGODB_ATLAS_FEDERATED_SSO_URL= +$ export MONGODB_ATLAS_FEDERATED_ISSUER_URI= +``` +~> **Notice:** For more information about the Federation configuration resource, see: https://www.mongodb.com/docs/atlas/reference/api/federation-configuration/ + ##### AWS env variables - For `Network Peering` resource configuration: diff --git a/examples/Federated-Settings/Readme.md b/examples/Federated-Settings/Readme.md new file mode 100644 index 0000000000..c536c0cfa8 --- /dev/null +++ b/examples/Federated-Settings/Readme.md @@ -0,0 +1,59 @@ +# Example - Okta and MongoDB Atlas Federated Settings Configuration + +This project aims to provide an example of using Okta and MongoDB Atlas together. + + +## Dependencies + +* Terraform v0.13 +* Okta account +* A MongoDB Atlas account + +``` +Terraform v0.13.0 ++ provider registry.terraform.io/terraform-providers/mongodbatlas v1.4.0 +``` + +## Usage + +**1\. Ensure your Okta/Mongodb Atlas Federal settings configuration is set up to have a working set of organizations, verified domains, and identity providers.** + +**2\. TFVARS** + +Now create **terraform.tfvars** file with all the variable values and make sure **not to commit it**. + +**3\. Review the Terraform plan. ** + +Execute the below command and ensure you are happy with the plan. + +``` bash +$ terraform plan +``` +This project currently does the below deployments: + +- MongoDB Atlas Federated Settings Organizational Role Mapping +- MongoDB Atlas Federated Settings Organizational Identity Provider +- MongoDB Atlas Federated Settings Organizational configuration + +**4\. Execute the Terraform import for 2 resources that do not support create.** +``` bash +$ terraform import mongodbatlas_federated_settings_identity_provider.identity_provider 6287a67f7f7f7f7f441c6c-0oad7f7f7f7fk1297 + terraform import mongodbatlas_federated_settings_org_config.org_connections_import 6287a67f7f7f7f7f441c6c-627a96837f7f7f7f7e306f14 + +``` + +**5\. Execute the Terraform apply.** + +Now execute the plan to provision the Federated settings resources. + +``` bash +$ terraform apply +``` + +**6\. Destroy the resources.** + +Once you are finished your testing, ensure you destroy the resources to avoid unnecessary Atlas charges. + +``` bash +$ terraform destroy +``` diff --git a/examples/Federated-Settings/main.tf b/examples/Federated-Settings/main.tf new file mode 100644 index 0000000000..c35ec1488f --- /dev/null +++ b/examples/Federated-Settings/main.tf @@ -0,0 +1,50 @@ +data "mongodbatlas_federated_settings" "federated_settings" { + org_id = var.org_id +} +data "mongodbatlas_federated_settings_identity_providers" "identity_provider" { + federation_settings_id = data.mongodbatlas_federated_settings.federated_settings.id +} + +data "mongodbatlas_federated_settings_org_configs" "org_configs_ds" { + federation_settings_id = data.mongodbatlas_federated_settings.federated_settings.id +} + +data "mongodbatlas_federated_settings_org_role_mappings" "org_role_mapping" { + federation_settings_id = data.mongodbatlas_federated_settings.federated_settings.id + org_id = var.org_id +} +resource "mongodbatlas_federated_settings_org_role_mapping" "org_role_mapping" { + federation_settings_id = data.mongodbatlas_federated_settings.federated_settings.id + org_id = var.org_id + external_group_name = "newgroup" + + role_assignments { + group_id = var.group_id + roles = ["GROUP_OWNER", "GROUP_DATA_ACCESS_ADMIN", "GROUP_SEARCH_INDEX_EDITOR", "GROUP_DATA_ACCESS_READ_ONLY"] + } + + role_assignments { + org_id = var.org_id + roles = ["ORG_OWNER", "ORG_MEMBER"] + } + +} +resource "mongodbatlas_federated_settings_org_config" "org_connections_import" { + federation_settings_id = data.mongodbatlas_federated_settings.federated_settings.id + org_id = var.org_id + identity_provider_id = var.identity_provider_id + domain_restriction_enabled = false + domain_allow_list = ["yourdomain.com"] +} + +resource "mongodbatlas_federated_settings_identity_provider" "identity_provider" { + federation_settings_id = data.mongodbatlas_federated_settings.federated_settings.id + name = var.name + associated_domains = ["yourdomain.com"] + sso_debug_enabled = true + status = "ACTIVE" + sso_url = "https://mysso.oktapreview.com/app/mysso_terrafssotesdev_1/exk1f7f7f7fk5wp50h8/sso/saml" + issuer_uri = "http://www.okta.com/exk1f716hf7f750h8" + request_binding = "HTTP-POST" + response_signature_algorithm = "SHA-256" +} diff --git a/examples/Federated-Settings/output.tf b/examples/Federated-Settings/output.tf new file mode 100644 index 0000000000..57fdd7f211 --- /dev/null +++ b/examples/Federated-Settings/output.tf @@ -0,0 +1,15 @@ +output "federated_settings_ds" { + value = data.mongodbatlas_federated_settings.federated_settings.id +} + +output "identity_provider" { + value = data.mongodbatlas_federated_settings_identity_providers.identity_provider.id +} + +output "org_configs_ds" { + value = data.mongodbatlas_federated_settings_org_configs.org_configs_ds.id +} + +output "org_role_mapping" { + value = data.mongodbatlas_federated_settings_org_role_mappings.org_role_mapping.id +} diff --git a/examples/Federated-Settings/provider.tf b/examples/Federated-Settings/provider.tf new file mode 100644 index 0000000000..18c430e061 --- /dev/null +++ b/examples/Federated-Settings/provider.tf @@ -0,0 +1,4 @@ +provider "mongodbatlas" { + public_key = var.public_key + private_key = var.private_key +} diff --git a/examples/Federated-Settings/variables.tf b/examples/Federated-Settings/variables.tf new file mode 100644 index 0000000000..003979e4fe --- /dev/null +++ b/examples/Federated-Settings/variables.tf @@ -0,0 +1,28 @@ +variable "public_key" { + type = string + description = "Public Programmatic API key to authenticate to Atlas" +} +variable "private_key" { + type = string + description = "Private Programmatic API key to authenticate to Atlas" +} +variable "org_id" { + type = string + description = "MongoDB Organization ID" +} +variable "group_id" { + type = string + description = "MongoDB Group ID" +} + +variable "name" { + type = string + description = "MongoDB Identity Provider Name" + default = "mongodb_federation_test" +} + +variable "identity_provider_id" { + type = string + description = "MongoDB Identity Provider ID" + default = "5754gdhgd758" +} diff --git a/examples/Federated-Settings/versions.tf b/examples/Federated-Settings/versions.tf new file mode 100644 index 0000000000..8d2a6743de --- /dev/null +++ b/examples/Federated-Settings/versions.tf @@ -0,0 +1,11 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + } + mongodbatlas = { + source = "mongodb/mongodbatlas" + } + } + required_version = ">= 0.13" +} diff --git a/go.sum b/go.sum index 098f5c3822..a31863d61e 100644 --- a/go.sum +++ b/go.sum @@ -1230,10 +1230,6 @@ go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQc go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.mongodb.org/atlas v0.12.0/go.mod h1:wVCnHcm/7/IfTjEB6K8K35PLG70yGz8BdkRwX0oK9/M= -go.mongodb.org/atlas v0.15.1-0.20220215171307-4b760c3c624f h1:IvKkFdSSBLC5kqB1X87vn8CRAI7eXoMSK7u2lG+WUg8= -go.mongodb.org/atlas v0.15.1-0.20220215171307-4b760c3c624f/go.mod h1:lQhRHIxc6jQHEK3/q9WLu/SdBkPj2fQYhjLGUF6Z3U8= -go.mongodb.org/atlas v0.16.1-0.20220527133640-ba676d378a30 h1:UVbPMJSXVDyvIA/JEHI2HAwc+B4R6xpnmSya/pbANpY= -go.mongodb.org/atlas v0.16.1-0.20220527133640-ba676d378a30/go.mod h1:lQhRHIxc6jQHEK3/q9WLu/SdBkPj2fQYhjLGUF6Z3U8= go.mongodb.org/atlas v0.16.1-0.20220531163122-551edbfb2f27 h1:rGTb8CaE9ZKNjmdUJ58jFcHopLg6o6Kzfm9AIayq1Hw= go.mongodb.org/atlas v0.16.1-0.20220531163122-551edbfb2f27/go.mod h1:lQhRHIxc6jQHEK3/q9WLu/SdBkPj2fQYhjLGUF6Z3U8= go.mongodb.org/realm v0.1.0 h1:zJiXyLaZrznQ+Pz947ziSrDKUep39DO4SfA0Fzx8M4M= diff --git a/mongodbatlas/data_source_mongodbatlas_federated_settings.go b/mongodbatlas/data_source_mongodbatlas_federated_settings.go new file mode 100644 index 0000000000..305ca71d24 --- /dev/null +++ b/mongodbatlas/data_source_mongodbatlas_federated_settings.go @@ -0,0 +1,99 @@ +package mongodbatlas + +import ( + "context" + "errors" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + matlas "go.mongodb.org/atlas/mongodbatlas" +) + +func dataSourceMongoDBAtlasFederatedSettings() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceMongoDBAtlasFederatedSettingsRead, + Schema: map[string]*schema.Schema{ + "org_id": { + Type: schema.TypeString, + Required: true, + }, + "federated_domains": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "has_role_mappings": { + Type: schema.TypeBool, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "identity_provider_id": { + Type: schema.TypeString, + Computed: true, + }, + "identity_provider_status": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceMongoDBAtlasFederatedSettingsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // Get client connection. + conn := meta.(*MongoDBClient).Atlas + + orgID, orgIDOk := d.GetOk("org_id") + + if !orgIDOk { + return diag.FromErr(errors.New("org_id must be configured")) + } + + var ( + err error + org *matlas.Organization + ) + + if orgIDOk { + org, _, err = conn.Organizations.Get(ctx, orgID.(string)) + } + + if err != nil { + return diag.Errorf("Error reading Organization %s %s", orgID, err) + } + + federationSettings, _, err := conn.FederatedSettings.Get(ctx, org.ID) + if err != nil { + return diag.Errorf("error getting Federated settings (%s): %s", orgID, err) + } + + if err := d.Set("org_id", org.ID); err != nil { + return diag.Errorf("error getting Federated settings (%s): %s %s", `org_id`, org.ID, err) + } + + if err := d.Set("federated_domains", federationSettings.FederatedDomains); err != nil { + return diag.Errorf("error getting Federated settings (%s): %s %s", `federated_domains`, federationSettings.FederatedDomains, err) + } + + if err := d.Set("identity_provider_status", federationSettings.IdentityProviderStatus); err != nil { + return diag.Errorf("error getting Federated settings (%s): %s %s", `identityProviderStatus`, federationSettings.IdentityProviderStatus, err) + } + + if err := d.Set("identity_provider_id", federationSettings.IdentityProviderID); err != nil { + return diag.Errorf("error getting Federated settings (%s): %s %s", `IdentityProviderID`, federationSettings.IdentityProviderID, err) + } + + if err := d.Set("has_role_mappings", federationSettings.HasRoleMappings); err != nil { + return diag.Errorf("error getting Federated settings (%s): flag %s ", `HasRoleMappings`, err) + } + + d.SetId(federationSettings.ID) + + return nil +} diff --git a/mongodbatlas/data_source_mongodbatlas_federated_settings_connected_organization.go b/mongodbatlas/data_source_mongodbatlas_federated_settings_connected_organization.go new file mode 100644 index 0000000000..963a50e196 --- /dev/null +++ b/mongodbatlas/data_source_mongodbatlas_federated_settings_connected_organization.go @@ -0,0 +1,170 @@ +package mongodbatlas + +import ( + "context" + "errors" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceMongoDBAtlasFederatedSettingsOrganizationConfig() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceMongoDBAtlasFederatedSettingsOrganizationConfigRead, + Schema: map[string]*schema.Schema{ + "federation_settings_id": { + Type: schema.TypeString, + Required: true, + }, + "org_id": { + Type: schema.TypeString, + Required: true, + }, + "domain_allow_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "domain_restriction_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "identity_provider_id": { + Type: schema.TypeString, + Computed: true, + }, + "post_auth_role_grants": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "role_mappings": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "external_group_name": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "role_assignments": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "group_id": { + Type: schema.TypeString, + Computed: true, + }, + "org_id": { + Type: schema.TypeString, + Computed: true, + }, + "role": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "user_conflicts": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "email_address": { + Type: schema.TypeString, + Computed: true, + }, + "federation_settings_id": { + Type: schema.TypeString, + Computed: true, + }, + "first_name": { + Type: schema.TypeString, + Computed: true, + }, + "last_name": { + Type: schema.TypeString, + Computed: true, + }, + "user_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} +func dataSourceMongoDBAtlasFederatedSettingsOrganizationConfigRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // Get client connection. + conn := meta.(*MongoDBClient).Atlas + + federationSettingsID, federationSettingsIDOk := d.GetOk("federation_settings_id") + + if !federationSettingsIDOk { + return diag.FromErr(errors.New("federation_settings_id must be configured")) + } + + orgID, orgIDOk := d.GetOk("org_id") + + if !orgIDOk { + return diag.FromErr(errors.New("org_id must be configured")) + } + + federatedSettingsConnectedOrganization, _, err := conn.FederatedSettings.GetConnectedOrg(ctx, federationSettingsID.(string), orgID.(string)) + if err != nil { + return diag.Errorf("error getting federatedSettings connected organizations assigned (%s): %s", federationSettingsID, err) + } + + if err := d.Set("domain_allow_list", federatedSettingsConnectedOrganization.DomainAllowList); err != nil { + return diag.FromErr(fmt.Errorf("error setting `domain_allow_list` for federatedSettings IdentityProviders: %s", err)) + } + + if err := d.Set("domain_restriction_enabled", federatedSettingsConnectedOrganization.DomainRestrictionEnabled); err != nil { + return diag.FromErr(fmt.Errorf("error setting `domain_restriction_enabled` for federatedSettings IdentityProviders: %s", err)) + } + + if err := d.Set("identity_provider_id", federatedSettingsConnectedOrganization.IdentityProviderID); err != nil { + return diag.FromErr(fmt.Errorf("error setting `identity_provider_id` for federatedSettings IdentityProviders: %s", err)) + } + + if err := d.Set("org_id", federatedSettingsConnectedOrganization.OrgID); err != nil { + return diag.FromErr(fmt.Errorf("error setting `org_id` for federatedSettings IdentityProviders: %s", err)) + } + + if err := d.Set("post_auth_role_grants", federatedSettingsConnectedOrganization.PostAuthRoleGrants); err != nil { + return diag.FromErr(fmt.Errorf("error setting `post_auth_role_grants` for federatedSettings IdentityProviders: %s", err)) + } + + if err := d.Set("role_mappings", flattenRoleMappings(federatedSettingsConnectedOrganization.RoleMappings)); err != nil { + return diag.FromErr(fmt.Errorf("error setting `role_mappings` for federatedSettings IdentityProviders: %s", err)) + } + if federatedSettingsConnectedOrganization.UserConflicts == nil { + if err := d.Set("user_conflicts", federatedSettingsConnectedOrganization.UserConflicts); err != nil { + return diag.FromErr(fmt.Errorf("error setting `user_conflicts` for federatedSettings IdentityProviders: %s", err)) + } + } else { + if err := d.Set("user_conflicts", flattenUserConflicts(*federatedSettingsConnectedOrganization.UserConflicts)); err != nil { + return diag.FromErr(fmt.Errorf("error setting `user_conflicts` for federatedSettings IdentityProviders: %s", err)) + } + } + + d.SetId(federatedSettingsConnectedOrganization.OrgID) + + return nil +} diff --git a/mongodbatlas/data_source_mongodbatlas_federated_settings_connected_organization_test.go b/mongodbatlas/data_source_mongodbatlas_federated_settings_connected_organization_test.go new file mode 100644 index 0000000000..2fe561d70f --- /dev/null +++ b/mongodbatlas/data_source_mongodbatlas_federated_settings_connected_organization_test.go @@ -0,0 +1,70 @@ +package mongodbatlas + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccDataSourceMongoDBAtlasFederatedSettingsOrganizationConfig_basic(t *testing.T) { + SkipTestExtCred(t) + var ( + resourceName = "data.mongodbatlas_federated_settings_org_config.test" + federatedSettingsID = os.Getenv("MONGODB_ATLAS_FEDERATION_SETTINGS_ID") + orgID = os.Getenv("MONGODB_ATLAS_FEDERATED_ORG_ID") + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { checkFederatedSettings(t) }, + ProviderFactories: testAccProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccMongoDBAtlasDataSourceFederatedSettingsOrganizationConfigConfig(federatedSettingsID, orgID), + Check: resource.ComposeTestCheckFunc( + testAccCheckMongoDBAtlasFederatedSettingsOrganizationConfigExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "federation_settings_id"), + resource.TestCheckResourceAttrSet(resourceName, "role_mappings.#"), + resource.TestCheckResourceAttrSet(resourceName, "identity_provider_id"), + resource.TestCheckResourceAttrSet(resourceName, "org_id"), + resource.TestCheckResourceAttr(resourceName, "identity_provider_id", "0oad4fas87jL5Xnk1297"), + ), + }, + }, + }) +} + +func testAccMongoDBAtlasDataSourceFederatedSettingsOrganizationConfigConfig(federatedSettingsID, orgID string) string { + return fmt.Sprintf(` + data "mongodbatlas_federated_settings_org_config" "test" { + federation_settings_id = "%[1]s" + org_id = "%[2]s" + + } +`, federatedSettingsID, orgID) +} + +func testAccCheckMongoDBAtlasFederatedSettingsOrganizationConfigExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*MongoDBClient).Atlas + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("no ID is set") + } + + _, _, err := conn.FederatedSettings.ListConnectedOrgs(context.Background(), rs.Primary.Attributes["federation_settings_id"], nil) + if err != nil { + return fmt.Errorf("FederatedSettingsConnectedOrganization (%s) does not exist", rs.Primary.ID) + } + + return nil + } +} diff --git a/mongodbatlas/data_source_mongodbatlas_federated_settings_connected_organizations.go b/mongodbatlas/data_source_mongodbatlas_federated_settings_connected_organizations.go new file mode 100644 index 0000000000..affd59fd73 --- /dev/null +++ b/mongodbatlas/data_source_mongodbatlas_federated_settings_connected_organizations.go @@ -0,0 +1,191 @@ +package mongodbatlas + +import ( + "context" + "errors" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + matlas "go.mongodb.org/atlas/mongodbatlas" +) + +func dataSourceMongoDBAtlasFederatedSettingsOrganizationConfigs() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceMongoDBAtlasFederatedSettingsOrganizationConfigsRead, + Schema: map[string]*schema.Schema{ + "federation_settings_id": { + Type: schema.TypeString, + Required: true, + }, + "page_num": { + Type: schema.TypeInt, + Optional: true, + }, + "items_per_page": { + Type: schema.TypeInt, + Optional: true, + }, + "results": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "domain_allow_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "domain_restriction_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "identity_provider_id": { + Type: schema.TypeString, + Computed: true, + }, + "org_id": { + Type: schema.TypeString, + Computed: true, + }, + "post_auth_role_grants": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "role_mappings": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "external_group_name": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "role_assignments": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "group_id": { + Type: schema.TypeString, + Computed: true, + }, + "org_id": { + Type: schema.TypeString, + Computed: true, + }, + "role": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "user_conflicts": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "email_address": { + Type: schema.TypeString, + Computed: true, + }, + "federation_settings_id": { + Type: schema.TypeString, + Computed: true, + }, + "first_name": { + Type: schema.TypeString, + Computed: true, + }, + "last_name": { + Type: schema.TypeString, + Computed: true, + }, + "user_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + }, + } +} +func dataSourceMongoDBAtlasFederatedSettingsOrganizationConfigsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // Get client connection. + conn := meta.(*MongoDBClient).Atlas + + federationSettingsID, federationSettingsIDOk := d.GetOk("federation_settings_id") + + options := &matlas.ListOptions{ + PageNum: d.Get("page_num").(int), + ItemsPerPage: d.Get("items_per_page").(int), + } + + if !federationSettingsIDOk { + return diag.FromErr(errors.New("either federation_settings_id must be configured")) + } + + federatedSettingsConnectedOrganizations, _, err := conn.FederatedSettings.ListConnectedOrgs(ctx, federationSettingsID.(string), options) + if err != nil { + return diag.Errorf("error getting federatedSettings connected organizations assigned (%s): %s", federationSettingsID, err) + } + + if err := d.Set("results", flattenFederatedSettingsOrganizationConfigs(*federatedSettingsConnectedOrganizations)); err != nil { + return diag.FromErr(fmt.Errorf("error setting `result` for federatedSettings IdentityProviders: %s", err)) + } + + d.SetId(federationSettingsID.(string)) + + return nil +} + +func flattenFederatedSettingsOrganizationConfigs(federatedSettingsConnectedOrganizations matlas.FederatedSettingsConnectedOrganizations) []map[string]interface{} { + var federatedSettingsConnectedOrganizationsMap []map[string]interface{} + + if (federatedSettingsConnectedOrganizations.TotalCount) > 0 { + federatedSettingsConnectedOrganizationsMap = make([]map[string]interface{}, federatedSettingsConnectedOrganizations.TotalCount) + + for i := range federatedSettingsConnectedOrganizations.Results { + if federatedSettingsConnectedOrganizations.Results[i].UserConflicts == nil { + federatedSettingsConnectedOrganizationsMap[i] = map[string]interface{}{ + "domain_allow_list": federatedSettingsConnectedOrganizations.Results[i].DomainAllowList, + "domain_restriction_enabled": federatedSettingsConnectedOrganizations.Results[i].DomainRestrictionEnabled, + "identity_provider_id": federatedSettingsConnectedOrganizations.Results[i].IdentityProviderID, + "org_id": federatedSettingsConnectedOrganizations.Results[i].OrgID, + "post_auth_role_grants": federatedSettingsConnectedOrganizations.Results[i].PostAuthRoleGrants, + "role_mappings": flattenRoleMappings(federatedSettingsConnectedOrganizations.Results[i].RoleMappings), + "user_conflicts": nil, + } + } else { + federatedSettingsConnectedOrganizationsMap[i] = map[string]interface{}{ + "domain_allow_list": federatedSettingsConnectedOrganizations.Results[i].DomainAllowList, + "domain_restriction_enabled": federatedSettingsConnectedOrganizations.Results[i].DomainRestrictionEnabled, + "identity_provider_id": federatedSettingsConnectedOrganizations.Results[i].IdentityProviderID, + "org_id": federatedSettingsConnectedOrganizations.Results[i].OrgID, + "post_auth_role_grants": federatedSettingsConnectedOrganizations.Results[i].PostAuthRoleGrants, + "role_mappings": flattenRoleMappings(federatedSettingsConnectedOrganizations.Results[i].RoleMappings), + "user_conflicts": flattenUserConflicts(*federatedSettingsConnectedOrganizations.Results[i].UserConflicts), + } + } + } + } + + return federatedSettingsConnectedOrganizationsMap +} diff --git a/mongodbatlas/data_source_mongodbatlas_federated_settings_connected_organizations_test.go b/mongodbatlas/data_source_mongodbatlas_federated_settings_connected_organizations_test.go new file mode 100644 index 0000000000..6a1fc8913e --- /dev/null +++ b/mongodbatlas/data_source_mongodbatlas_federated_settings_connected_organizations_test.go @@ -0,0 +1,69 @@ +package mongodbatlas + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccDataSourceMongoDBAtlasFederatedSettingsOrganizationConfigs_basic(t *testing.T) { + SkipTestExtCred(t) + var ( + resourceName = "data.mongodbatlas_federated_settings_org_configs.test" + federatedSettingsID = os.Getenv("MONGODB_ATLAS_FEDERATION_SETTINGS_ID") + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { checkFederatedSettings(t) }, + ProviderFactories: testAccProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccMongoDBAtlasDataSourceFederatedSettingsOrganizationConfigsConfig(federatedSettingsID), + Check: resource.ComposeTestCheckFunc( + testAccCheckMongoDBAtlasFederatedSettingsOrganizationConfigsExists(resourceName), + + resource.TestCheckResourceAttrSet(resourceName, "federation_settings_id"), + resource.TestCheckResourceAttrSet(resourceName, "results.#"), + resource.TestCheckResourceAttrSet(resourceName, "results.0.identity_provider_id"), + resource.TestCheckResourceAttrSet(resourceName, "results.0.org_id"), + ), + }, + }, + }) +} + +func testAccMongoDBAtlasDataSourceFederatedSettingsOrganizationConfigsConfig(federatedSettingsID string) string { + return fmt.Sprintf(` + data "mongodbatlas_federated_settings_org_configs" "test" { + federation_settings_id = "%[1]s" + page_num = 1 + items_per_page = 100 + } +`, federatedSettingsID) +} + +func testAccCheckMongoDBAtlasFederatedSettingsOrganizationConfigsExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*MongoDBClient).Atlas + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("no ID is set") + } + + _, _, err := conn.FederatedSettings.ListConnectedOrgs(context.Background(), rs.Primary.Attributes["federation_settings_id"], nil) + if err != nil { + return fmt.Errorf("FederatedSettingsConnectedOrganization (%s) does not exist", rs.Primary.ID) + } + + return nil + } +} diff --git a/mongodbatlas/data_source_mongodbatlas_federated_settings_identity_provider.go b/mongodbatlas/data_source_mongodbatlas_federated_settings_identity_provider.go new file mode 100644 index 0000000000..9a1954b00f --- /dev/null +++ b/mongodbatlas/data_source_mongodbatlas_federated_settings_identity_provider.go @@ -0,0 +1,273 @@ +package mongodbatlas + +import ( + "context" + "errors" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceMongoDBAtlasFederatedSettingsIdentityProvider() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceMongoDBAtlasFederatedSettingsIdentityProviderRead, + Schema: map[string]*schema.Schema{ + "federation_settings_id": { + Type: schema.TypeString, + Required: true, + }, + "identity_provider_id": { + Type: schema.TypeString, + Required: true, + }, + + "acs_url": { + Type: schema.TypeString, + Computed: true, + }, + "associated_domains": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "associated_orgs": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "domain_allow_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "domain_restriction_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "identity_provider_id": { + Type: schema.TypeString, + Computed: true, + }, + "org_id": { + Type: schema.TypeString, + Computed: true, + }, + "post_auth_role_grants": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "role_mappings": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "external_group_name": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "role_assignments": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "group_id": { + Type: schema.TypeString, + Computed: true, + }, + "org_id": { + Type: schema.TypeString, + Computed: true, + }, + "role": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "user_conflicts": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "email_address": { + Type: schema.TypeString, + Computed: true, + }, + "federation_settings_id": { + Type: schema.TypeString, + Computed: true, + }, + "first_name": { + Type: schema.TypeString, + Computed: true, + }, + "last_name": { + Type: schema.TypeString, + Computed: true, + }, + "user_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "audience_uri": { + Type: schema.TypeString, + Computed: true, + }, + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + "issuer_uri": { + Type: schema.TypeString, + Computed: true, + }, + "okta_idp_id": { + Type: schema.TypeString, + Computed: true, + }, + "pem_file_info": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "certificates": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "not_after": { + Type: schema.TypeString, + Computed: true, + }, + "not_before": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "file_name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "request_binding": { + Type: schema.TypeString, + Computed: true, + }, + "response_signature_algorithm": { + Type: schema.TypeString, + Computed: true, + }, + "sso_debug_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "sso_url": { + Type: schema.TypeString, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} +func dataSourceMongoDBAtlasFederatedSettingsIdentityProviderRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // Get client connection. + conn := meta.(*MongoDBClient).Atlas + + federationSettingsID, federationSettingsIDOk := d.GetOk("federation_settings_id") + + if !federationSettingsIDOk { + return diag.FromErr(errors.New("federation_settings_id must be configured")) + } + + idpID, idpIDOk := d.GetOk("identity_provider_id") + + if !idpIDOk { + return diag.FromErr(errors.New("identity_provider_id must be configured")) + } + + federatedSettingsIdentityProvider, _, err := conn.FederatedSettings.GetIdentityProvider(ctx, federationSettingsID.(string), idpID.(string)) + if err != nil { + return diag.Errorf("error getting federatedSettings IdentityProviders assigned (%s): %s", federationSettingsID, err) + } + + if err := d.Set("acs_url", federatedSettingsIdentityProvider.AcsURL); err != nil { + return diag.FromErr(fmt.Errorf("error setting `acs_url` for federatedSettings IdentityProviders: %s", err)) + } + + if err := d.Set("associated_domains", federatedSettingsIdentityProvider.AssociatedDomains); err != nil { + return diag.FromErr(fmt.Errorf("error setting `associated_domains` for federatedSettings IdentityProviders: %s", err)) + } + + if err := d.Set("associated_orgs", flattenAssociatedOrgs(federatedSettingsIdentityProvider.AssociatedOrgs)); err != nil { + return diag.FromErr(fmt.Errorf("error setting `associated_orgs` for federatedSettings IdentityProviders: %s", err)) + } + + if err := d.Set("display_name", federatedSettingsIdentityProvider.DisplayName); err != nil { + return diag.FromErr(fmt.Errorf("error setting `display_name` for federatedSettings IdentityProviders: %s", err)) + } + + if err := d.Set("issuer_uri", federatedSettingsIdentityProvider.IssuerURI); err != nil { + return diag.FromErr(fmt.Errorf("error setting `issuer_uri` for federatedSettings IdentityProviders: %s", err)) + } + + if err := d.Set("okta_idp_id", federatedSettingsIdentityProvider.OktaIdpID); err != nil { + return diag.FromErr(fmt.Errorf("error setting `idp_id` for federatedSettings IdentityProviders: %s", err)) + } + + if err := d.Set("pem_file_info", flattenPemFileInfo(*federatedSettingsIdentityProvider.PemFileInfo)); err != nil { + return diag.FromErr(fmt.Errorf("error setting `pem_file_info` for federatedSettings IdentityProviders: %s", err)) + } + + if err := d.Set("request_binding", federatedSettingsIdentityProvider.RequestBinding); err != nil { + return diag.FromErr(fmt.Errorf("error setting `request_binding` for federatedSettings IdentityProviders: %s", err)) + } + + if err := d.Set("response_signature_algorithm", federatedSettingsIdentityProvider.ResponseSignatureAlgorithm); err != nil { + return diag.FromErr(fmt.Errorf("error setting `response_signature_algorithm` for federatedSettings IdentityProviders: %s", err)) + } + + if err := d.Set("sso_debug_enabled", federatedSettingsIdentityProvider.SsoDebugEnabled); err != nil { + return diag.FromErr(fmt.Errorf("error setting `sso_debug_enabled` for federatedSettings IdentityProviders: %s", err)) + } + + if err := d.Set("sso_url", federatedSettingsIdentityProvider.SsoURL); err != nil { + return diag.FromErr(fmt.Errorf("error setting `sso_url` for federatedSettings IdentityProviders: %s", err)) + } + + if err := d.Set("status", federatedSettingsIdentityProvider.Status); err != nil { + return diag.FromErr(fmt.Errorf("error setting `status` for federatedSettings IdentityProviders: %s", err)) + } + + d.SetId(federatedSettingsIdentityProvider.OktaIdpID) + + return nil +} diff --git a/mongodbatlas/data_source_mongodbatlas_federated_settings_identity_provider_test.go b/mongodbatlas/data_source_mongodbatlas_federated_settings_identity_provider_test.go new file mode 100644 index 0000000000..ffd3fe27c7 --- /dev/null +++ b/mongodbatlas/data_source_mongodbatlas_federated_settings_identity_provider_test.go @@ -0,0 +1,45 @@ +package mongodbatlas + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceMongoDBAtlasFederatedSettingsIdentityProvider_basic(t *testing.T) { + SkipTestExtCred(t) + var ( + resourceName = "data.mongodbatlas_federated_settings_identity_provider.test" + federatedSettingsID = os.Getenv("MONGODB_ATLAS_FEDERATION_SETTINGS_ID") + idpID = os.Getenv("MONGODB_ATLAS_FEDERATED_IDP_ID") + ) + resource.Test(t, resource.TestCase{ + PreCheck: func() { checkFederatedSettings(t) }, + ProviderFactories: testAccProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccMongoDBAtlasDataSourceFederatedSettingsIdentityProviderConfig(federatedSettingsID, idpID), + Check: resource.ComposeTestCheckFunc( + testAccCheckMongoDBAtlasFederatedSettingsIdentityProvidersExists(resourceName), + + resource.TestCheckResourceAttrSet(resourceName, "federation_settings_id"), + resource.TestCheckResourceAttrSet(resourceName, "associated_orgs.#"), + resource.TestCheckResourceAttrSet(resourceName, "acs_url"), + resource.TestCheckResourceAttrSet(resourceName, "display_name"), + resource.TestCheckResourceAttr(resourceName, "display_name", "mongodb_federation_test"), + ), + }, + }, + }) +} + +func testAccMongoDBAtlasDataSourceFederatedSettingsIdentityProviderConfig(federatedSettingsID, idpID string) string { + return fmt.Sprintf(` + data "mongodbatlas_federated_settings_identity_provider" "test" { + federation_settings_id = "%[1]s" + identity_provider_id = "%[2]s" + } +`, federatedSettingsID, idpID) +} diff --git a/mongodbatlas/data_source_mongodbatlas_federated_settings_identity_providers.go b/mongodbatlas/data_source_mongodbatlas_federated_settings_identity_providers.go new file mode 100644 index 0000000000..3efe2ad67c --- /dev/null +++ b/mongodbatlas/data_source_mongodbatlas_federated_settings_identity_providers.go @@ -0,0 +1,427 @@ +package mongodbatlas + +import ( + "context" + "errors" + "fmt" + "sort" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + matlas "go.mongodb.org/atlas/mongodbatlas" +) + +func dataSourceMongoDBAtlasFederatedSettingsIdentityProviders() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceMongoDBAtlasFederatedSettingsIdentityProvidersRead, + Schema: map[string]*schema.Schema{ + "federation_settings_id": { + Type: schema.TypeString, + Required: true, + }, + "page_num": { + Type: schema.TypeInt, + Optional: true, + }, + "items_per_page": { + Type: schema.TypeInt, + Optional: true, + }, + "results": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "acs_url": { + Type: schema.TypeString, + Computed: true, + }, + "associated_domains": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "associated_orgs": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "domain_allow_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "domain_restriction_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "identity_provider_id": { + Type: schema.TypeString, + Computed: true, + }, + "org_id": { + Type: schema.TypeString, + Computed: true, + }, + "post_auth_role_grants": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "role_mappings": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "external_group_name": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "role_assignments": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "group_id": { + Type: schema.TypeString, + Computed: true, + }, + "org_id": { + Type: schema.TypeString, + Computed: true, + }, + "role": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "user_conflicts": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "email_address": { + Type: schema.TypeString, + Computed: true, + }, + "federation_settings_id": { + Type: schema.TypeString, + Computed: true, + }, + "first_name": { + Type: schema.TypeString, + Computed: true, + }, + "last_name": { + Type: schema.TypeString, + Computed: true, + }, + "user_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "audience_uri": { + Type: schema.TypeString, + Computed: true, + }, + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + "issuer_uri": { + Type: schema.TypeString, + Computed: true, + }, + "okta_idp_id": { + Type: schema.TypeString, + Computed: true, + }, + "pem_file_info": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "certificates": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "not_after": { + Type: schema.TypeString, + Computed: true, + }, + "not_before": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "file_name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "request_binding": { + Type: schema.TypeString, + Computed: true, + }, + "response_signature_algorithm": { + Type: schema.TypeString, + Computed: true, + }, + "sso_debug_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "sso_url": { + Type: schema.TypeString, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} +func dataSourceMongoDBAtlasFederatedSettingsIdentityProvidersRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // Get client connection. + conn := meta.(*MongoDBClient).Atlas + + federationSettingsID, federationSettingsIDOk := d.GetOk("federation_settings_id") + + options := &matlas.ListOptions{ + PageNum: d.Get("page_num").(int), + ItemsPerPage: d.Get("items_per_page").(int), + } + + if !federationSettingsIDOk { + return diag.FromErr(errors.New("federation_settings_id must be configured")) + } + + federatedSettingsIdentityProviders, _, err := conn.FederatedSettings.ListIdentityProviders(ctx, federationSettingsID.(string), options) + if err != nil { + return diag.Errorf("error getting federatedSettings IdentityProviders assigned (%s): %s", federationSettingsID, err) + } + + if err := d.Set("results", flattenFederatedSettingsIdentityProvider(federatedSettingsIdentityProviders)); err != nil { + return diag.FromErr(fmt.Errorf("error setting `result` for federatedSettings IdentityProviders: %s", err)) + } + + d.SetId(federationSettingsID.(string)) + + return nil +} + +func flattenFederatedSettingsIdentityProvider(federatedSettingsIdentityProvider []matlas.FederatedSettingsIdentityProvider) []map[string]interface{} { + var federatedSettingsIdentityProviderMap []map[string]interface{} + + if len(federatedSettingsIdentityProvider) > 0 { + federatedSettingsIdentityProviderMap = make([]map[string]interface{}, len(federatedSettingsIdentityProvider)) + + for i := range federatedSettingsIdentityProvider { + federatedSettingsIdentityProviderMap[i] = map[string]interface{}{ + "acs_url": federatedSettingsIdentityProvider[i].AcsURL, + "associated_domains": federatedSettingsIdentityProvider[i].AssociatedDomains, + "associated_orgs": flattenAssociatedOrgs(federatedSettingsIdentityProvider[i].AssociatedOrgs), + "audience_uri": federatedSettingsIdentityProvider[i].AudienceURI, + "display_name": federatedSettingsIdentityProvider[i].DisplayName, + "issuer_uri": federatedSettingsIdentityProvider[i].IssuerURI, + "okta_idp_id": federatedSettingsIdentityProvider[i].OktaIdpID, + "pem_file_info": flattenPemFileInfo(*federatedSettingsIdentityProvider[i].PemFileInfo), + "request_binding": federatedSettingsIdentityProvider[i].RequestBinding, + "response_signature_algorithm": federatedSettingsIdentityProvider[i].ResponseSignatureAlgorithm, + "sso_debug_enabled": federatedSettingsIdentityProvider[i].SsoDebugEnabled, + "sso_url": federatedSettingsIdentityProvider[i].SsoURL, + "status": federatedSettingsIdentityProvider[i].Status, + } + } + } + + return federatedSettingsIdentityProviderMap +} + +func flattenAssociatedOrgs(associatedOrgs []*matlas.AssociatedOrgs) []map[string]interface{} { + var associatedOrgsMap []map[string]interface{} + + if len(associatedOrgs) == 0 { + return nil + } + associatedOrgsMap = make([]map[string]interface{}, len(associatedOrgs)) + + for i := range associatedOrgs { + if associatedOrgs[i].UserConflicts == nil { + associatedOrgsMap[i] = map[string]interface{}{ + "domain_allow_list": associatedOrgs[i].DomainAllowList, + "domain_restriction_enabled": associatedOrgs[i].DomainRestrictionEnabled, + "identity_provider_id": associatedOrgs[i].IdentityProviderID, + "org_id": associatedOrgs[i].OrgID, + "post_auth_role_grants": associatedOrgs[i].PostAuthRoleGrants, + "role_mappings": flattenRoleMappings(associatedOrgs[i].RoleMappings), + "user_conflicts": nil, + } + } else { + associatedOrgsMap[i] = map[string]interface{}{ + "domain_allow_list": associatedOrgs[i].DomainAllowList, + "domain_restriction_enabled": associatedOrgs[i].DomainRestrictionEnabled, + "identity_provider_id": associatedOrgs[i].IdentityProviderID, + "org_id": associatedOrgs[i].OrgID, + "post_auth_role_grants": associatedOrgs[i].PostAuthRoleGrants, + "role_mappings": flattenRoleMappings(associatedOrgs[i].RoleMappings), + "user_conflicts": flattenUserConflicts(*associatedOrgs[i].UserConflicts), + } + } + } + + return associatedOrgsMap +} + +func flattenUserConflicts(userConflicts matlas.UserConflicts) []map[string]interface{} { + var userConflictsMap []map[string]interface{} + + if len(userConflicts) == 0 { + return nil + } + userConflictsMap = make([]map[string]interface{}, len(userConflicts)) + + for i := range userConflicts { + userConflictsMap[i] = map[string]interface{}{ + "email_address": userConflicts[i].EmailAddress, + "federation_settings_id": userConflicts[i].FederationSettingsID, + "first_name": userConflicts[i].FirstName, + "last_name": userConflicts[i].LastName, + "user_id": userConflicts[i].UserID, + } + } + + return userConflictsMap +} + +func flattenPemFileInfo(pemFileInfo matlas.PemFileInfo) []map[string]interface{} { + var pemFileInfoMap []map[string]interface{} + + if len(pemFileInfo.Certificates) > 0 { + pemFileInfoMap = make([]map[string]interface{}, 1) + + pemFileInfoMap[0] = map[string]interface{}{ + "certificates": flattenFederatedSettingsCertificates(pemFileInfo.Certificates), + "file_name": pemFileInfo.FileName, + } + } + + return pemFileInfoMap +} + +func flattenFederatedSettingsCertificates(certificates []*matlas.Certificates) []map[string]interface{} { + var certificatesMap []map[string]interface{} + + if len(certificates) > 0 { + certificatesMap = make([]map[string]interface{}, len(certificates)) + + for i := range certificates { + certificatesMap[i] = map[string]interface{}{ + "not_after": certificates[i].NotAfter.String(), + "not_before": certificates[i].NotBefore.String(), + } + } + } + + return certificatesMap +} + +type mRoleAssignment []*matlas.RoleAssignments + +func (ra mRoleAssignment) Len() int { return len(ra) } +func (ra mRoleAssignment) Swap(i, j int) { ra[i], ra[j] = ra[j], ra[i] } +func (ra mRoleAssignment) Less(i, j int) bool { + compareVal := strings.Compare(ra[i].OrgID, ra[j].OrgID) + + if compareVal != 0 { + return compareVal < 0 + } + + compareVal = strings.Compare(ra[i].GroupID, ra[j].GroupID) + + if compareVal != 0 { + return compareVal < 0 + } + + return ra[i].Role < ra[j].Role +} + +type roleMappingsByGroupName []*matlas.RoleMappings + +func (ra roleMappingsByGroupName) Len() int { return len(ra) } +func (ra roleMappingsByGroupName) Swap(i, j int) { ra[i], ra[j] = ra[j], ra[i] } + +func (ra roleMappingsByGroupName) Less(i, j int) bool { + return ra[i].ExternalGroupName < ra[j].ExternalGroupName +} + +func flattenRoleMappings(roleMappings []*matlas.RoleMappings) []map[string]interface{} { + sort.Sort(roleMappingsByGroupName(roleMappings)) + + var roleMappingsMap []map[string]interface{} + + if len(roleMappings) > 0 { + roleMappingsMap = make([]map[string]interface{}, len(roleMappings)) + + for i := range roleMappings { + roleMappingsMap[i] = map[string]interface{}{ + "external_group_name": roleMappings[i].ExternalGroupName, + "id": roleMappings[i].ID, + "role_assignments": flattenRoleAssignments(roleMappings[i].RoleAssignments), + } + } + } + + return roleMappingsMap +} + +func flattenRoleAssignments(roleAssignments []*matlas.RoleAssignments) []map[string]interface{} { + sort.Sort(mRoleAssignment(roleAssignments)) + + var roleAssignmentsMap []map[string]interface{} + + if len(roleAssignments) > 0 { + roleAssignmentsMap = make([]map[string]interface{}, len(roleAssignments)) + + for i := range roleAssignments { + roleAssignmentsMap[i] = map[string]interface{}{ + "group_id": roleAssignments[i].GroupID, + "org_id": roleAssignments[i].OrgID, + "role": roleAssignments[i].Role, + } + } + } + + return roleAssignmentsMap +} diff --git a/mongodbatlas/data_source_mongodbatlas_federated_settings_identity_providers_test.go b/mongodbatlas/data_source_mongodbatlas_federated_settings_identity_providers_test.go new file mode 100644 index 0000000000..33dd04a7df --- /dev/null +++ b/mongodbatlas/data_source_mongodbatlas_federated_settings_identity_providers_test.go @@ -0,0 +1,69 @@ +package mongodbatlas + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccDataSourceMongoDBAtlasFederatedSettingsIdentityProviders_basic(t *testing.T) { + SkipTestExtCred(t) + var ( + resourceName = "data.mongodbatlas_federated_settings_identity_providers.test" + federatedSettingsID = os.Getenv("MONGODB_ATLAS_FEDERATION_SETTINGS_ID") + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { checkFederatedSettings(t) }, + ProviderFactories: testAccProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccMongoDBAtlasDataSourceFederatedSettingsIdentityProvidersConfig(federatedSettingsID), + Check: resource.ComposeTestCheckFunc( + testAccCheckMongoDBAtlasFederatedSettingsIdentityProvidersExists(resourceName), + + resource.TestCheckResourceAttrSet(resourceName, "federation_settings_id"), + resource.TestCheckResourceAttrSet(resourceName, "results.#"), + resource.TestCheckResourceAttrSet(resourceName, "results.0.acs_url"), + resource.TestCheckResourceAttrSet(resourceName, "results.0.display_name"), + ), + }, + }, + }) +} + +func testAccMongoDBAtlasDataSourceFederatedSettingsIdentityProvidersConfig(federatedSettingsID string) string { + return fmt.Sprintf(` + data "mongodbatlas_federated_settings_identity_providers" "test" { + federation_settings_id = "%[1]s" + page_num = 1 + items_per_page = 100 + } +`, federatedSettingsID) +} + +func testAccCheckMongoDBAtlasFederatedSettingsIdentityProvidersExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*MongoDBClient).Atlas + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("no ID is set") + } + + _, _, err := conn.FederatedSettings.ListIdentityProviders(context.Background(), rs.Primary.Attributes["federation_settings_id"], nil) + if err != nil { + return fmt.Errorf("FederatedSettingsIdentityProviders (%s) does not exist", rs.Primary.ID) + } + + return nil + } +} diff --git a/mongodbatlas/data_source_mongodbatlas_federated_settings_organization_role_mapping.go b/mongodbatlas/data_source_mongodbatlas_federated_settings_organization_role_mapping.go new file mode 100644 index 0000000000..d533d5cc87 --- /dev/null +++ b/mongodbatlas/data_source_mongodbatlas_federated_settings_organization_role_mapping.go @@ -0,0 +1,98 @@ +package mongodbatlas + +import ( + "context" + "errors" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceMongoDBAtlasFederatedSettingsOrganizationRoleMapping() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceMongoDBAtlasFederatedSettingsOrganizationRoleMappingRead, + Schema: map[string]*schema.Schema{ + "federation_settings_id": { + Type: schema.TypeString, + Required: true, + }, + "org_id": { + Type: schema.TypeString, + Required: true, + }, + "role_mapping_id": { + Type: schema.TypeString, + Required: true, + }, + + "external_group_name": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "role_assignments": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "group_id": { + Type: schema.TypeString, + Computed: true, + }, + "org_id": { + Type: schema.TypeString, + Computed: true, + }, + "role": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} +func dataSourceMongoDBAtlasFederatedSettingsOrganizationRoleMappingRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // Get client connection. + conn := meta.(*MongoDBClient).Atlas + + federationSettingsID, federationSettingsIDOk := d.GetOk("federation_settings_id") + + if !federationSettingsIDOk { + return diag.FromErr(errors.New("federation_settings_id must be configured")) + } + + orgID, orgIDOk := d.GetOk("org_id") + + if !orgIDOk { + return diag.FromErr(errors.New("org_id must be configured")) + } + + roleMappingID, roleMappingOk := d.GetOk("role_mapping_id") + + if !roleMappingOk { + return diag.FromErr(errors.New("role_mapping_id must be configured")) + } + + federatedSettingsOrganizationRoleMapping, _, err := conn.FederatedSettings.GetRoleMapping(ctx, federationSettingsID.(string), orgID.(string), roleMappingID.(string)) + if err != nil { + return diag.Errorf("error getting federatedSettings Role Mapping assigned (%s): %s", federationSettingsID, err) + } + + if err := d.Set("external_group_name", federatedSettingsOrganizationRoleMapping.ExternalGroupName); err != nil { + return diag.FromErr(fmt.Errorf("error setting `result` for federatedSettings Role Mapping: %s", err)) + } + + if err := d.Set("role_assignments", flattenRoleAssignments(federatedSettingsOrganizationRoleMapping.RoleAssignments)); err != nil { + return diag.FromErr(fmt.Errorf("error setting `result` for federatedSettings Role Mapping: %s", err)) + } + + d.SetId(federatedSettingsOrganizationRoleMapping.ID) + + return nil +} diff --git a/mongodbatlas/data_source_mongodbatlas_federated_settings_organization_role_mapping_test.go b/mongodbatlas/data_source_mongodbatlas_federated_settings_organization_role_mapping_test.go new file mode 100644 index 0000000000..c10322c5e3 --- /dev/null +++ b/mongodbatlas/data_source_mongodbatlas_federated_settings_organization_role_mapping_test.go @@ -0,0 +1,49 @@ +package mongodbatlas + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + matlas "go.mongodb.org/atlas/mongodbatlas" +) + +func TestAccDataSourceMongoDBAtlasFederatedSettingsOrganizationRoleMapping_basic(t *testing.T) { + SkipTestExtCred(t) + var ( + federatedSettingsOrganizationRoleMapping matlas.FederatedSettingsOrganizationRoleMapping + resourceName = "data.mongodbatlas_federated_settings_org_role_mapping.test" + federatedSettingsID = os.Getenv("MONGODB_ATLAS_FEDERATION_SETTINGS_ID") + orgID = os.Getenv("MONGODB_ATLAS_FEDERATED_ORG_ID") + roleMappingID = os.Getenv("MONGODB_ATLAS_FEDERATED_ROLE_MAPPING_ID") + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { checkFederatedSettings(t) }, + ProviderFactories: testAccProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccMongoDBAtlasDataSourceFederatedSettingsOrganizationRoleMappingConfig(federatedSettingsID, orgID, roleMappingID), + Check: resource.ComposeTestCheckFunc( + testAccCheckMongoDBAtlasFederatedSettingsOrganizationRoleMappingExists(resourceName, &federatedSettingsOrganizationRoleMapping), + resource.TestCheckResourceAttrSet(resourceName, "federation_settings_id"), + resource.TestCheckResourceAttrSet(resourceName, "external_group_name"), + resource.TestCheckResourceAttrSet(resourceName, "role_assignments.#"), + resource.TestCheckResourceAttr(resourceName, "org_id", orgID), + resource.TestCheckResourceAttr(resourceName, "external_group_name", "group2"), + ), + }, + }, + }) +} + +func testAccMongoDBAtlasDataSourceFederatedSettingsOrganizationRoleMappingConfig(federatedSettingsID, orgID, roleMappingID string) string { + return fmt.Sprintf(` + data "mongodbatlas_federated_settings_org_role_mapping" "test" { + federation_settings_id = "%[1]s" + org_id = "%[2]s" + role_mapping_id = "%[3]s" + } +`, federatedSettingsID, orgID, roleMappingID) +} diff --git a/mongodbatlas/data_source_mongodbatlas_federated_settings_organization_role_mappings.go b/mongodbatlas/data_source_mongodbatlas_federated_settings_organization_role_mappings.go new file mode 100644 index 0000000000..6028a6efc8 --- /dev/null +++ b/mongodbatlas/data_source_mongodbatlas_federated_settings_organization_role_mappings.go @@ -0,0 +1,123 @@ +package mongodbatlas + +import ( + "context" + "errors" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + matlas "go.mongodb.org/atlas/mongodbatlas" +) + +func dataSourceMongoDBAtlasFederatedSettingsOrganizationRoleMappings() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceMongoDBAtlasFederatedSettingsOrganizationRoleMappingsRead, + Schema: map[string]*schema.Schema{ + "federation_settings_id": { + Type: schema.TypeString, + Required: true, + }, + "org_id": { + Type: schema.TypeString, + Required: true, + }, + "page_num": { + Type: schema.TypeInt, + Optional: true, + }, + "items_per_page": { + Type: schema.TypeInt, + Optional: true, + }, + "results": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "external_group_name": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "role_assignments": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "group_id": { + Type: schema.TypeString, + Computed: true, + }, + "org_id": { + Type: schema.TypeString, + Computed: true, + }, + "role": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + }, + } +} +func dataSourceMongoDBAtlasFederatedSettingsOrganizationRoleMappingsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // Get client connection. + conn := meta.(*MongoDBClient).Atlas + + federationSettingsID, federationSettingsIDOk := d.GetOk("federation_settings_id") + + if !federationSettingsIDOk { + return diag.FromErr(errors.New("federation_settings_id must be configured")) + } + + orgID, orgIDOk := d.GetOk("org_id") + + if !orgIDOk { + return diag.FromErr(errors.New("org_id must be configured")) + } + + options := &matlas.ListOptions{ + PageNum: d.Get("page_num").(int), + ItemsPerPage: d.Get("items_per_page").(int), + } + + federatedSettingsOrganizationRoleMappings, _, err := conn.FederatedSettings.ListRoleMappings(ctx, federationSettingsID.(string), orgID.(string), options) + if err != nil { + return diag.Errorf("error getting federatedSettings Role Mapping: assigned (%s): %s", federationSettingsID, err) + } + + if err := d.Set("results", flattenFederatedSettingsOrganizationRoleMappings(federatedSettingsOrganizationRoleMappings)); err != nil { + return diag.FromErr(fmt.Errorf("error setting `result` for federatedSettings Role Mapping:: %s", err)) + } + + d.SetId(federationSettingsID.(string)) + + return nil +} + +func flattenFederatedSettingsOrganizationRoleMappings(federatedSettingsOrganizationRoleMapping *matlas.FederatedSettingsOrganizationRoleMappings) []map[string]interface{} { + var federatedSettingsOrganizationRoleMappingMap []map[string]interface{} + + if federatedSettingsOrganizationRoleMapping.TotalCount > 0 { + federatedSettingsOrganizationRoleMappingMap = make([]map[string]interface{}, federatedSettingsOrganizationRoleMapping.TotalCount) + + for i := range federatedSettingsOrganizationRoleMapping.Results { + federatedSettingsOrganizationRoleMappingMap[i] = map[string]interface{}{ + "external_group_name": federatedSettingsOrganizationRoleMapping.Results[i].ExternalGroupName, + "id": federatedSettingsOrganizationRoleMapping.Results[i].ID, + "role_assignments": flattenRoleAssignments(federatedSettingsOrganizationRoleMapping.Results[i].RoleAssignments), + } + } + } + + return federatedSettingsOrganizationRoleMappingMap +} diff --git a/mongodbatlas/data_source_mongodbatlas_federated_settings_organization_role_mappings_test.go b/mongodbatlas/data_source_mongodbatlas_federated_settings_organization_role_mappings_test.go new file mode 100644 index 0000000000..0bd47da0a8 --- /dev/null +++ b/mongodbatlas/data_source_mongodbatlas_federated_settings_organization_role_mappings_test.go @@ -0,0 +1,70 @@ +package mongodbatlas + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccDataSourceMongoDBAtlasFederatedSettingsOrganizationRoleMappings_basic(t *testing.T) { + SkipTestExtCred(t) + var ( + resourceName = "data.mongodbatlas_federated_settings_org_role_mappings.test" + federatedSettingsID = os.Getenv("MONGODB_ATLAS_FEDERATION_SETTINGS_ID") + orgID = os.Getenv("MONGODB_ATLAS_FEDERATED_ORG_ID") + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { checkFederatedSettings(t) }, + ProviderFactories: testAccProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccMongoDBAtlasDataSourceFederatedSettingsOrganizationRoleMappingsConfig(federatedSettingsID, orgID), + Check: resource.ComposeTestCheckFunc( + testAccCheckMongoDBAtlasFederatedSettingsOrganizationRoleMappingsExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "federation_settings_id"), + resource.TestCheckResourceAttrSet(resourceName, "results.#"), + resource.TestCheckResourceAttrSet(resourceName, "results.0.external_group_name"), + resource.TestCheckResourceAttrSet(resourceName, "results.0.role_assignments.#"), + ), + }, + }, + }) +} + +func testAccMongoDBAtlasDataSourceFederatedSettingsOrganizationRoleMappingsConfig(federatedSettingsID, orgID string) string { + return fmt.Sprintf(` + data "mongodbatlas_federated_settings_org_role_mappings" "test" { + federation_settings_id = "%[1]s" + org_id = "%[2]s" + page_num = 1 + items_per_page = 100 + } +`, federatedSettingsID, orgID) +} + +func testAccCheckMongoDBAtlasFederatedSettingsOrganizationRoleMappingsExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*MongoDBClient).Atlas + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("no ID is set") + } + + _, _, err := conn.FederatedSettings.ListRoleMappings(context.Background(), rs.Primary.Attributes["federation_settings_id"], rs.Primary.Attributes["org_id"], nil) + if err != nil { + return fmt.Errorf("FederatedSettingsOrganizationRoleMappings (%s) does not exist", rs.Primary.ID) + } + + return nil + } +} diff --git a/mongodbatlas/data_source_mongodbatlas_federated_settings_test.go b/mongodbatlas/data_source_mongodbatlas_federated_settings_test.go new file mode 100644 index 0000000000..58635a8328 --- /dev/null +++ b/mongodbatlas/data_source_mongodbatlas_federated_settings_test.go @@ -0,0 +1,72 @@ +package mongodbatlas + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + matlas "go.mongodb.org/atlas/mongodbatlas" +) + +func TestAccDataSourceMongoDBAtlasFederatedSettings_basic(t *testing.T) { + SkipTestExtCred(t) + var ( + federatedSettings matlas.FederatedSettings + resourceName = "data.mongodbatlas_federated_settings.test" + orgID = os.Getenv("MONGODB_ATLAS_FEDERATED_ORG_ID") + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { checkFederatedSettings(t) }, + ProviderFactories: testAccProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccMongoDBAtlasDataSourceFederatedSettingsConfig(orgID), + Check: resource.ComposeTestCheckFunc( + testAccCheckMongoDBAtlasFederatedSettingsExists(resourceName, &federatedSettings), + + resource.TestCheckResourceAttrSet(resourceName, "org_id"), + resource.TestCheckResourceAttrSet(resourceName, "identity_provider_id"), + resource.TestCheckResourceAttrSet(resourceName, "identity_provider_status"), + resource.TestCheckResourceAttrSet(resourceName, "has_role_mappings"), + ), + }, + }, + }) +} + +func testAccMongoDBAtlasDataSourceFederatedSettingsConfig(orgID string) string { + return fmt.Sprintf(` + data "mongodbatlas_federated_settings" "test" { + org_id = "%[1]s" + } +`, orgID) +} + +func testAccCheckMongoDBAtlasFederatedSettingsExists(resourceName string, federatedSettings *matlas.FederatedSettings) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*MongoDBClient).Atlas + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("no ID is set") + } + + federatedSettingsRes, _, err := conn.FederatedSettings.Get(context.Background(), rs.Primary.Attributes["org_id"]) + if err != nil { + return fmt.Errorf("FederatedSettings (%s) does not exist", rs.Primary.ID) + } + + federatedSettings = federatedSettingsRes + + return nil + } +} diff --git a/mongodbatlas/provider.go b/mongodbatlas/provider.go index 8f069d9d5c..caeb93b54c 100644 --- a/mongodbatlas/provider.go +++ b/mongodbatlas/provider.go @@ -129,6 +129,13 @@ func getDataSourcesMap() map[string]*schema.Resource { "mongodbatlas_cloud_backup_snapshot_export_buckets": datasourceMongoDBAtlasCloudBackupSnapshotExportBuckets(), "mongodbatlas_cloud_backup_snapshot_export_job": datasourceMongoDBAtlasCloudBackupSnapshotExportJob(), "mongodbatlas_cloud_backup_snapshot_export_jobs": datasourceMongoDBAtlasCloudBackupSnapshotExportJobs(), + "mongodbatlas_federated_settings": dataSourceMongoDBAtlasFederatedSettings(), + "mongodbatlas_federated_settings_identity_provider": dataSourceMongoDBAtlasFederatedSettingsIdentityProvider(), + "mongodbatlas_federated_settings_identity_providers": dataSourceMongoDBAtlasFederatedSettingsIdentityProviders(), + "mongodbatlas_federated_settings_org_config": dataSourceMongoDBAtlasFederatedSettingsOrganizationConfig(), + "mongodbatlas_federated_settings_org_configs": dataSourceMongoDBAtlasFederatedSettingsOrganizationConfigs(), + "mongodbatlas_federated_settings_org_role_mapping": dataSourceMongoDBAtlasFederatedSettingsOrganizationRoleMapping(), + "mongodbatlas_federated_settings_org_role_mappings": dataSourceMongoDBAtlasFederatedSettingsOrganizationRoleMappings(), } return dataSourcesMap } @@ -177,6 +184,9 @@ func getResourcesMap() map[string]*schema.Resource { "mongodbatlas_cloud_backup_snapshot_restore_job": resourceMongoDBAtlasCloudBackupSnapshotRestoreJob(), "mongodbatlas_cloud_backup_snapshot_export_bucket": resourceMongoDBAtlasCloudBackupSnapshotExportBucket(), "mongodbatlas_cloud_backup_snapshot_export_job": resourceMongoDBAtlasCloudBackupSnapshotExportJob(), + "mongodbatlas_federated_settings_org_config": resourceMongoDBAtlasFederatedSettingsOrganizationConfig(), + "mongodbatlas_federated_settings_org_role_mapping": resourceMongoDBAtlasFederatedSettingsOrganizationRoleMapping(), + "mongodbatlas_federated_settings_identity_provider": resourceMongoDBAtlasFederatedSettingsIdentityProvider(), } return resourcesMap } diff --git a/mongodbatlas/provider_test.go b/mongodbatlas/provider_test.go index 3cae5035f7..228094a1a3 100644 --- a/mongodbatlas/provider_test.go +++ b/mongodbatlas/provider_test.go @@ -198,3 +198,11 @@ func checkLDAP(t *testing.T) { t.Fatal("`MONGODB_ATLAS_LDAP_HOSTNAME`, `MONGODB_ATLAS_LDAP_USERNAME`, `MONGODB_ATLAS_LDAP_PASSWORD` and `MONGODB_ATLAS_LDAP_PORT` must be set for ldap configuration/verify acceptance testing") } } + +func checkFederatedSettings(t *testing.T) { + if os.Getenv("MONGODB_ATLAS_FEDERATED_PROJECT_ID") == "" || + os.Getenv("MONGODB_ATLAS_FEDERATION_SETTINGS_ID") == "" || + os.Getenv("MONGODB_ATLAS_FEDERATED_ORG_ID") == "" { + t.Fatal("`MONGODB_ATLAS_FEDERATED_PROJECT_ID`, `MONGODB_ATLAS_FEDERATED_ORG_ID` and `MONGODB_ATLAS_FEDERATION_SETTINGS_ID` must be set for federated settings/verify acceptance testing") + } +} diff --git a/mongodbatlas/resource_mongodbatlas_federated_settings_connected_organization.go b/mongodbatlas/resource_mongodbatlas_federated_settings_connected_organization.go new file mode 100644 index 0000000000..68b2e2917b --- /dev/null +++ b/mongodbatlas/resource_mongodbatlas_federated_settings_connected_organization.go @@ -0,0 +1,211 @@ +package mongodbatlas + +import ( + "context" + "errors" + "fmt" + "net/http" + "regexp" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/spf13/cast" +) + +func resourceMongoDBAtlasFederatedSettingsOrganizationConfig() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceMongoDBAtlasFederatedSettingsOrganizationConfigRead, + ReadContext: resourceMongoDBAtlasFederatedSettingsOrganizationConfigRead, + UpdateContext: resourceMongoDBAtlasFederatedSettingsOrganizationConfigUpdate, + DeleteContext: resourceMongoDBAtlasFederatedSettingsOrganizationConfigDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceMongoDBAtlasFederatedSettingsOrganizationConfigImportState, + }, + Schema: map[string]*schema.Schema{ + "federation_settings_id": { + Type: schema.TypeString, + Required: true, + }, + "org_id": { + Type: schema.TypeString, + Required: true, + }, + "identity_provider_id": { + Type: schema.TypeString, + Required: true, + }, + "domain_allow_list": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "post_auth_role_grants": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "domain_restriction_enabled": { + Type: schema.TypeBool, + Required: true, + }, + }, + } +} + +func resourceMongoDBAtlasFederatedSettingsOrganizationConfigRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // Get client connection. + conn := meta.(*MongoDBClient).Atlas + + if d.Id() == "" { + d.SetId("") + return nil + } + ids := decodeStateID(d.Id()) + federationSettingsID := ids["federation_settings_id"] + orgID := ids["org_id"] + + federatedSettingsConnectedOrganization, resp, err := conn.FederatedSettings.GetConnectedOrg(context.Background(), federationSettingsID, orgID) + if err != nil { + // case 404 + // deleted in the backend case + if resp != nil && resp.StatusCode == http.StatusNotFound { + d.SetId("") + return nil + } + + return diag.FromErr(fmt.Errorf("error getting federated settings organization config: %s", err)) + } + + if err := d.Set("domain_restriction_enabled", federatedSettingsConnectedOrganization.DomainRestrictionEnabled); err != nil { + return diag.FromErr(fmt.Errorf("error setting domain restriction enabled (%s): %s", d.Id(), err)) + } + + if err := d.Set("domain_allow_list", federatedSettingsConnectedOrganization.DomainAllowList); err != nil { + return diag.FromErr(fmt.Errorf("error setting domain allow list (%s): %s", d.Id(), err)) + } + + if err := d.Set("post_auth_role_grants", federatedSettingsConnectedOrganization.PostAuthRoleGrants); err != nil { + return diag.FromErr(fmt.Errorf("error setting post_auth_role_grants (%s): %s", d.Id(), err)) + } + + d.SetId(encodeStateID(map[string]string{ + "federation_settings_id": federationSettingsID, + "org_id": orgID, + })) + + return nil +} + +func resourceMongoDBAtlasFederatedSettingsOrganizationConfigUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // Get client connection. + conn := meta.(*MongoDBClient).Atlas + ids := decodeStateID(d.Id()) + federationSettingsID := ids["federation_settings_id"] + orgID := ids["org_id"] + + federatedSettingsConnectedOrganizationUpdate, _, err := conn.FederatedSettings.GetConnectedOrg(context.Background(), federationSettingsID, orgID) + if err != nil { + return diag.FromErr(fmt.Errorf("error retreiving federation settings connected organization (%s): %s", federationSettingsID, err)) + } + + if d.HasChange("domain_restriction_enabled") { + domainRestrictionEnabled := d.Get("domain_restriction_enabled").(bool) + federatedSettingsConnectedOrganizationUpdate.DomainRestrictionEnabled = &domainRestrictionEnabled + } + + if d.HasChange("domain_allow_list") { + domainAllowList := d.Get("domain_allow_list") + federatedSettingsConnectedOrganizationUpdate.DomainAllowList = cast.ToStringSlice(domainAllowList) + } + + if d.HasChange("identity_provider_id") { + identityProviderID := d.Get("identity_provider_id").(string) + federatedSettingsConnectedOrganizationUpdate.IdentityProviderID = identityProviderID + } + + if d.HasChange("post_auth_role_grants") { + postAuthRoleGrants := d.Get("post_auth_role_grants") + federatedSettingsConnectedOrganizationUpdate.PostAuthRoleGrants = cast.ToStringSlice(postAuthRoleGrants) + } + + _, _, err = conn.FederatedSettings.UpdateConnectedOrg(ctx, federationSettingsID, orgID, federatedSettingsConnectedOrganizationUpdate) + if err != nil { + return diag.FromErr(fmt.Errorf("error updating federation settings connected organization (%s): %s", federationSettingsID, err)) + } + + return resourceMongoDBAtlasFederatedSettingsOrganizationConfigRead(ctx, d, meta) +} + +func resourceMongoDBAtlasFederatedSettingsOrganizationConfigDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // Get client connection. + conn := meta.(*MongoDBClient).Atlas + ids := decodeStateID(d.Id()) + federationSettingsID := ids["federation_settings_id"] + orgID := ids["org_id"] + + _, err := conn.FederatedSettings.DeleteConnectedOrg(ctx, federationSettingsID, orgID) + if err != nil { + return diag.FromErr(fmt.Errorf("error deleting federation settings connected organization (%s): %s", federationSettingsID, err)) + } + + return nil +} + +func resourceMongoDBAtlasFederatedSettingsOrganizationConfigImportState(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + conn := meta.(*MongoDBClient).Atlas + federationSettingsID, orgID, err := splitFederatedSettingsOrganizationConfigImportID(d.Id()) + if err != nil { + return nil, err + } + + federatedSettingsConnectedOrganization, _, err := conn.FederatedSettings.GetConnectedOrg(context.Background(), *federationSettingsID, *orgID) + if err != nil { + return nil, fmt.Errorf("couldn't import Organization config (%s) in Federation settings (%s), error: %s", *orgID, *federationSettingsID, err) + } + + if err := d.Set("federation_settings_id", *federationSettingsID); err != nil { + return nil, fmt.Errorf("error setting Organization config in Federation settings (%s): %s", d.Id(), err) + } + + if err := d.Set("domain_restriction_enabled", federatedSettingsConnectedOrganization.DomainRestrictionEnabled); err != nil { + return nil, fmt.Errorf("error setting domain restriction enabled (%s): %s", d.Id(), err) + } + + if err := d.Set("domain_allow_list", federatedSettingsConnectedOrganization.DomainAllowList); err != nil { + return nil, fmt.Errorf("error setting domain allow list (%s): %s", d.Id(), err) + } + + if err := d.Set("org_id", federatedSettingsConnectedOrganization.OrgID); err != nil { + return nil, fmt.Errorf("error setting org id (%s): %s", d.Id(), err) + } + + if err := d.Set("identity_provider_id", federatedSettingsConnectedOrganization.IdentityProviderID); err != nil { + return nil, fmt.Errorf("error setting identity provider id (%s): %s", d.Id(), err) + } + + d.SetId(encodeStateID(map[string]string{ + "federation_settings_id": *federationSettingsID, + "org_id": *orgID, + })) + + return []*schema.ResourceData{d}, nil +} + +func splitFederatedSettingsOrganizationConfigImportID(id string) (federationSettingsID, orgID *string, err error) { + var re = regexp.MustCompile(`(?s)^(.*)-(.*)$`) + parts := re.FindStringSubmatch(id) + + if len(parts) != 3 { + err = errors.New("import format error: to import a Federated Settings Orgnization Config, use the format {federation_settings_id}-{org_id}") + return + } + + federationSettingsID = &parts[1] + orgID = &parts[2] + + return +} diff --git a/mongodbatlas/resource_mongodbatlas_federated_settings_connected_organization_test.go b/mongodbatlas/resource_mongodbatlas_federated_settings_connected_organization_test.go new file mode 100644 index 0000000000..5dd9a6d8f7 --- /dev/null +++ b/mongodbatlas/resource_mongodbatlas_federated_settings_connected_organization_test.go @@ -0,0 +1,122 @@ +package mongodbatlas + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + matlas "go.mongodb.org/atlas/mongodbatlas" +) + +func TestAccResourceMongoDBAtlasFederatedSettingsOrganizationConfig_basic(t *testing.T) { + SkipTestExtCred(t) + var ( + federatedSettingsIdentityProvider matlas.FederatedSettingsConnectedOrganization + resourceName = "mongodbatlas_federated_settings_org_config.test" + federationSettingsID = os.Getenv("MONGODB_ATLAS_FEDERATION_SETTINGS_ID") + orgID = os.Getenv("MONGODB_ATLAS_FEDERATED_ORG_ID") + idpID = os.Getenv("MONGODB_ATLAS_FEDERATED_IDP_ID") + ) + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { checkFederatedSettings(t) }, + ProviderFactories: testAccProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccMongoDBAtlasFederatedSettingsOrganizationConfig(federationSettingsID, orgID, idpID), + ResourceName: resourceName, + ImportStateIdFunc: testAccCheckMongoDBAtlasFederatedSettingsOrganizationConfigImportStateIDFunc(resourceName, federationSettingsID, orgID), + ImportState: true, + ImportStateVerify: false, + }, + { + Config: testAccMongoDBAtlasFederatedSettingsOrganizationConfig(federationSettingsID, orgID, idpID), + ResourceName: resourceName, + ImportStateIdFunc: testAccCheckMongoDBAtlasFederatedSettingsOrganizationConfigImportStateIDFunc(resourceName, federationSettingsID, orgID), + + ImportState: true, + Check: resource.ComposeTestCheckFunc( + testAccCheckMongoDBAtlasFederatedSettingsOrganizationConfigRExists(resourceName, &federatedSettingsIdentityProvider), + resource.TestCheckResourceAttr(resourceName, "federation_settings_id", federationSettingsID), + resource.TestCheckResourceAttr(resourceName, "name", "mongodb_federation_test"), + ), + }, + }, + }) +} + +func TestAccResourceMongoDBAtlasFederatedSettingsOrganizationConfig_importBasic(t *testing.T) { + SkipTestExtCred(t) + var ( + resourceName = "mongodbatlas_federated_settings_org_config.test" + federationSettingsID = os.Getenv("MONGODB_ATLAS_FEDERATION_SETTINGS_ID") + orgID = os.Getenv("MONGODB_ATLAS_FEDERATED_ORG_ID") + idpID = os.Getenv("MONGODB_ATLAS_FEDERATED_OKTA_IDP_ID") + ) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { checkFederatedSettings(t) }, + ProviderFactories: testAccProviderFactories, + Steps: []resource.TestStep{ + + { + Config: testAccMongoDBAtlasFederatedSettingsOrganizationConfig(federationSettingsID, orgID, idpID), + ResourceName: resourceName, + ImportStateIdFunc: testAccCheckMongoDBAtlasFederatedSettingsOrganizationConfigImportStateIDFunc(resourceName, federationSettingsID, orgID), + ImportState: true, + ImportStateVerify: false, + }, + }, + }) +} + +func testAccCheckMongoDBAtlasFederatedSettingsOrganizationConfigRExists(resourceName string, + federatedSettingsIdentityProvider *matlas.FederatedSettingsConnectedOrganization) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*MongoDBClient).Atlas + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("no ID is set") + } + + response, _, err := conn.FederatedSettings.GetConnectedOrg(context.Background(), + rs.Primary.Attributes["federation_settings_id"], + rs.Primary.Attributes["org_id"]) + if err == nil { + *federatedSettingsIdentityProvider = *response + return nil + } + + return fmt.Errorf("connected org (%s) does not exist", rs.Primary.Attributes["org_id"]) + } +} + +func testAccCheckMongoDBAtlasFederatedSettingsOrganizationConfigImportStateIDFunc(resourceName, federationSettingsID, orgID string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + ID := encodeStateID(map[string]string{ + "federation_settings_id": federationSettingsID, + "org_id": orgID, + }) + + ids := decodeStateID(ID) + return fmt.Sprintf("%s-%s", ids["federation_settings_id"], ids["org_id"]), nil + } +} + +func testAccMongoDBAtlasFederatedSettingsOrganizationConfig(federationSettingsID, orgID, identityProviderID string) string { + return fmt.Sprintf(` + resource "mongodbatlas_federated_settings_org_config" "test" { + federation_settings_id = "%[1]s" + org_id = "%[2]s" + domain_restriction_enabled = false + domain_allow_list = ["reorganizeyourworld.com"] + identity_provider_id = "%[3]s" + }`, federationSettingsID, orgID, identityProviderID) +} diff --git a/mongodbatlas/resource_mongodbatlas_federated_settings_identity_provider.go b/mongodbatlas/resource_mongodbatlas_federated_settings_identity_provider.go new file mode 100644 index 0000000000..b976d9076e --- /dev/null +++ b/mongodbatlas/resource_mongodbatlas_federated_settings_identity_provider.go @@ -0,0 +1,265 @@ +package mongodbatlas + +import ( + "context" + "errors" + "fmt" + "net/http" + "regexp" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/spf13/cast" +) + +func resourceMongoDBAtlasFederatedSettingsIdentityProvider() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceMongoDBAtlasFederatedSettingsIdentityProviderRead, + ReadContext: resourceMongoDBAtlasFederatedSettingsIdentityProviderRead, + UpdateContext: resourceMongoDBAtlasFederatedSettingsIdentityProviderUpdate, + DeleteContext: resourceMongoDBAtlasFederatedSettingsIdentityProviderDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceMongoDBAtlasFederatedSettingsIdentityProviderImportState, + }, + Schema: map[string]*schema.Schema{ + "federation_settings_id": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "issuer_uri": { + Type: schema.TypeString, + Required: true, + }, + "request_binding": { + Type: schema.TypeString, + Required: true, + }, + "response_signature_algorithm": { + Type: schema.TypeString, + Required: true, + }, + "associated_domains": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "sso_debug_enabled": { + Type: schema.TypeBool, + Required: true, + }, + "sso_url": { + Type: schema.TypeString, + Required: true, + }, + "status": { + Type: schema.TypeString, + Required: true, + }, + "okta_idp_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceMongoDBAtlasFederatedSettingsIdentityProviderRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // Get client connection. + conn := meta.(*MongoDBClient).Atlas + + if d.Id() == "" { + d.SetId("") + return nil + } + + ids := decodeStateID(d.Id()) + federationSettingsID := ids["federation_settings_id"] + oktaIdpID := ids["okta_idp_id"] + + federatedSettingsIdentityProvider, resp, err := conn.FederatedSettings.GetIdentityProvider(context.Background(), federationSettingsID, oktaIdpID) + if err != nil { + // case 404 + // deleted in the backend case + if resp != nil && resp.StatusCode == http.StatusNotFound { + d.SetId("") + return nil + } + + return diag.FromErr(fmt.Errorf("error getting federated settings identity provider: %s", err)) + } + + if err := d.Set("sso_debug_enabled", federatedSettingsIdentityProvider.SsoDebugEnabled); err != nil { + return diag.FromErr(fmt.Errorf("error setting sso debug enabled (%s): %s", d.Id(), err)) + } + + if err := d.Set("associated_domains", federatedSettingsIdentityProvider.AssociatedDomains); err != nil { + return diag.FromErr(fmt.Errorf("error setting associated domains list (%s): %s", d.Id(), err)) + } + + if err := d.Set("okta_idp_id", federatedSettingsIdentityProvider.OktaIdpID); err != nil { + return diag.FromErr(fmt.Errorf("error setting OktaIdpID (%s): %s", d.Id(), err)) + } + + if err := d.Set("status", federatedSettingsIdentityProvider.Status); err != nil { + return diag.FromErr(fmt.Errorf("error setting Status (%s): %s", d.Id(), err)) + } + + if err := d.Set("issuer_uri", federatedSettingsIdentityProvider.IssuerURI); err != nil { + return diag.FromErr(fmt.Errorf("error setting issuer uri (%s): %s", d.Id(), err)) + } + + if err := d.Set("request_binding", federatedSettingsIdentityProvider.RequestBinding); err != nil { + return diag.FromErr(fmt.Errorf("error setting request binding (%s): %s", d.Id(), err)) + } + + if err := d.Set("response_signature_algorithm", federatedSettingsIdentityProvider.ResponseSignatureAlgorithm); err != nil { + return diag.FromErr(fmt.Errorf("error setting response signature algorithm (%s): %s", d.Id(), err)) + } + + if err := d.Set("sso_url", federatedSettingsIdentityProvider.SsoURL); err != nil { + return diag.FromErr(fmt.Errorf("error setting sso url (%s): %s", d.Id(), err)) + } + + d.SetId(encodeStateID(map[string]string{ + "federation_settings_id": federationSettingsID, + "okta_idp_id": oktaIdpID, + })) + + return nil +} + +func resourceMongoDBAtlasFederatedSettingsIdentityProviderUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // Get client connection. + conn := meta.(*MongoDBClient).Atlas + ids := decodeStateID(d.Id()) + federationSettingsID := ids["federation_settings_id"] + oktaIdpID := ids["okta_idp_id"] + + federatedSettingsIdentityProviderUpdate, _, err := conn.FederatedSettings.GetIdentityProvider(context.Background(), federationSettingsID, oktaIdpID) + if err != nil { + return diag.FromErr(fmt.Errorf("error retreiving federation settings identity provider (%s): %s", federationSettingsID, err)) + } + + if d.HasChange("sso_debug_enabled") { + ssoDebugEnabled := d.Get("sso_debug_enabled").(bool) + federatedSettingsIdentityProviderUpdate.SsoDebugEnabled = &ssoDebugEnabled + } + + if d.HasChange("associated_domains") { + associatedDomains := d.Get("associated_domains") + federatedSettingsIdentityProviderUpdate.AssociatedDomains = cast.ToStringSlice(associatedDomains) + } + + if d.HasChange("name") { + identityName := d.Get("name").(string) + federatedSettingsIdentityProviderUpdate.DisplayName = identityName + } + + if d.HasChange("status") { + status := d.Get("status").(string) + federatedSettingsIdentityProviderUpdate.Status = status + } + + if d.HasChange("issuer_uri") { + status := d.Get("issuer_uri").(string) + federatedSettingsIdentityProviderUpdate.IssuerURI = status + } + + if d.HasChange("request_binding") { + status := d.Get("request_binding").(string) + federatedSettingsIdentityProviderUpdate.RequestBinding = status + } + + if d.HasChange("response_signature_algorithm") { + status := d.Get("response_signature_algorithm").(string) + federatedSettingsIdentityProviderUpdate.ResponseSignatureAlgorithm = status + } + + if d.HasChange("sso_url") { + status := d.Get("sso_url").(string) + federatedSettingsIdentityProviderUpdate.SsoURL = status + } + + federatedSettingsIdentityProviderUpdate.PemFileInfo = nil + + _, _, err = conn.FederatedSettings.UpdateIdentityProvider(ctx, federationSettingsID, oktaIdpID, federatedSettingsIdentityProviderUpdate) + if err != nil { + return diag.FromErr(fmt.Errorf("error updating federation settings identity provider (%s): %s", federationSettingsID, err)) + } + + return resourceMongoDBAtlasFederatedSettingsIdentityProviderRead(ctx, d, meta) +} + +func resourceMongoDBAtlasFederatedSettingsIdentityProviderDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + d.SetId("") + return nil +} + +func resourceMongoDBAtlasFederatedSettingsIdentityProviderImportState(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + conn := meta.(*MongoDBClient).Atlas + federationSettingsID, oktaIdpID, err := splitFederatedSettingsIdentityProviderImportID(d.Id()) + if err != nil { + return nil, err + } + + federatedSettingsIdentityProvider, _, err := conn.FederatedSettings.GetIdentityProvider(context.Background(), *federationSettingsID, *oktaIdpID) + if err != nil { + return nil, fmt.Errorf("couldn't import Organization config (%s) in Federation settings (%s), error: %s", *oktaIdpID, *federationSettingsID, err) + } + + if err := d.Set("federation_settings_id", *federationSettingsID); err != nil { + return nil, fmt.Errorf("error setting Identity Provider in Federation settings (%s): %s", d.Id(), err) + } + + if err := d.Set("sso_debug_enabled", federatedSettingsIdentityProvider.SsoDebugEnabled); err != nil { + return nil, fmt.Errorf("error setting sso debug enabled (%s): %s", d.Id(), err) + } + + if err := d.Set("associated_domains", federatedSettingsIdentityProvider.AssociatedDomains); err != nil { + return nil, fmt.Errorf("error setting associaed domains list (%s): %s", d.Id(), err) + } + + if err := d.Set("issuer_uri", federatedSettingsIdentityProvider.IssuerURI); err != nil { + return nil, fmt.Errorf("error setting issuer uri (%s): %s", d.Id(), err) + } + + if err := d.Set("request_binding", federatedSettingsIdentityProvider.RequestBinding); err != nil { + return nil, fmt.Errorf("error setting request binding (%s): %s", d.Id(), err) + } + + if err := d.Set("response_signature_algorithm", federatedSettingsIdentityProvider.ResponseSignatureAlgorithm); err != nil { + return nil, fmt.Errorf("error setting response signature algorithm (%s): %s", d.Id(), err) + } + + if err := d.Set("sso_url", federatedSettingsIdentityProvider.SsoURL); err != nil { + return nil, fmt.Errorf("error setting sso url (%s): %s", d.Id(), err) + } + + d.SetId(encodeStateID(map[string]string{ + "federation_settings_id": *federationSettingsID, + "okta_idp_id": *oktaIdpID, + })) + + return []*schema.ResourceData{d}, nil +} + +func splitFederatedSettingsIdentityProviderImportID(id string) (federationSettingsID, oktaIdpID *string, err error) { + var re = regexp.MustCompile(`(?s)^(.*)-(.*)$`) + parts := re.FindStringSubmatch(id) + + if len(parts) != 3 { + err = errors.New("import format error: to import a Federated SettingsIdentity Provider, use the format {federation_settings_id}-{okta_idp_id}") + return + } + + federationSettingsID = &parts[1] + oktaIdpID = &parts[2] + + return +} diff --git a/mongodbatlas/resource_mongodbatlas_federated_settings_identity_provider_test.go b/mongodbatlas/resource_mongodbatlas_federated_settings_identity_provider_test.go new file mode 100644 index 0000000000..e704b4c5f6 --- /dev/null +++ b/mongodbatlas/resource_mongodbatlas_federated_settings_identity_provider_test.go @@ -0,0 +1,128 @@ +package mongodbatlas + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + matlas "go.mongodb.org/atlas/mongodbatlas" +) + +func TestAccResourceMongoDBAtlasFederatedSettingsIdentityProvider_basic(t *testing.T) { + SkipTestExtCred(t) + var ( + federatedSettingsIdentityProvider matlas.FederatedSettingsIdentityProvider + resourceName = "mongodbatlas_federated_settings_identity_provider.test" + federationSettingsID = os.Getenv("MONGODB_ATLAS_FEDERATION_SETTINGS_ID") + idpID = os.Getenv("MONGODB_ATLAS_FEDERATED_OKTA_IDP_ID") + ssoURL = os.Getenv("MONGODB_ATLAS_FEDERATED_SSO_URL") + issuerURI = os.Getenv("MONGODB_ATLAS_FEDERATED_ISSUER_URI") + ) + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { checkFederatedSettings(t) }, + ProviderFactories: testAccProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccMongoDBAtlasFederatedSettingsIdentityProviderConfig(federationSettingsID, ssoURL, issuerURI), + ResourceName: resourceName, + ImportStateIdFunc: testAccCheckMongoDBAtlasFederatedSettingsIdentityProviderImportStateIDFunc(resourceName, federationSettingsID, idpID), + ImportState: true, + ImportStateVerify: false, + }, + { + Config: testAccMongoDBAtlasFederatedSettingsIdentityProviderConfig(federationSettingsID, ssoURL, issuerURI), + ResourceName: resourceName, + ImportStateIdFunc: testAccCheckMongoDBAtlasFederatedSettingsIdentityProviderImportStateIDFunc(resourceName, federationSettingsID, idpID), + + ImportState: true, + Check: resource.ComposeTestCheckFunc( + testAccCheckMongoDBAtlasFederatedSettingsIdentityProviderExists(resourceName, &federatedSettingsIdentityProvider, idpID), + resource.TestCheckResourceAttr(resourceName, "federation_settings_id", federationSettingsID), + resource.TestCheckResourceAttr(resourceName, "name", "mongodb_federation_test"), + ), + }, + }, + }) +} + +func TestAccResourceMongoDBAtlasFederatedSettingsIdentityProvider_importBasic(t *testing.T) { + SkipTestExtCred(t) + var ( + resourceName = "mongodbatlas_federated_settings_identity_provider.test" + federationSettingsID = os.Getenv("MONGODB_ATLAS_FEDERATION_SETTINGS_ID") + idpID = os.Getenv("MONGODB_ATLAS_FEDERATED_OKTA_IDP_ID") + ssoURL = os.Getenv("MONGODB_ATLAS_FEDERATED_SSO_URL") + issuerURI = os.Getenv("MONGODB_ATLAS_FEDERATED_ISSUER_URI") + ) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { checkFederatedSettings(t) }, + ProviderFactories: testAccProviderFactories, + Steps: []resource.TestStep{ + + { + Config: testAccMongoDBAtlasFederatedSettingsIdentityProviderConfig(federationSettingsID, ssoURL, issuerURI), + ResourceName: resourceName, + ImportStateIdFunc: testAccCheckMongoDBAtlasFederatedSettingsIdentityProviderImportStateIDFunc(resourceName, federationSettingsID, idpID), + ImportState: true, + ImportStateVerify: false, + }, + }, + }) +} + +func testAccCheckMongoDBAtlasFederatedSettingsIdentityProviderExists(resourceName string, + federatedSettingsIdentityProvider *matlas.FederatedSettingsIdentityProvider, idpID string) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*MongoDBClient).Atlas + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("no ID is set") + } + + response, _, err := conn.FederatedSettings.GetIdentityProvider(context.Background(), + rs.Primary.Attributes["federation_settings_id"], + idpID) + if err == nil { + *federatedSettingsIdentityProvider = *response + return nil + } + + return fmt.Errorf("identity provider (%s) does not exist", idpID) + } +} + +func testAccCheckMongoDBAtlasFederatedSettingsIdentityProviderImportStateIDFunc(resourceName, federationSettingsID, idpID string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + ID := encodeStateID(map[string]string{ + "federation_settings_id": federationSettingsID, + "okta_idp_id": idpID, + }) + + ids := decodeStateID(ID) + return fmt.Sprintf("%s-%s", ids["federation_settings_id"], ids["okta_idp_id"]), nil + } +} + +func testAccMongoDBAtlasFederatedSettingsIdentityProviderConfig(federationSettingsID, ssoURL, issuerURI string) string { + return fmt.Sprintf(` + resource "mongodbatlas_federated_settings_identity_provider" "test" { + federation_settings_id = "%[1]s" + name = "mongodb_federation_test" + associated_domains = ["reorganizeyourworld.com"] + sso_debug_enabled = true + status = "ACTIVE" + sso_url = "%[2]s" + issuer_uri = "%[3]s" + request_binding = "HTTP-POST" + response_signature_algorithm = "SHA-256" + }`, federationSettingsID, ssoURL, issuerURI) +} diff --git a/mongodbatlas/resource_mongodbatlas_federated_settings_organization_role_mapping.go b/mongodbatlas/resource_mongodbatlas_federated_settings_organization_role_mapping.go new file mode 100644 index 0000000000..847bb59394 --- /dev/null +++ b/mongodbatlas/resource_mongodbatlas_federated_settings_organization_role_mapping.go @@ -0,0 +1,367 @@ +package mongodbatlas + +import ( + "context" + "errors" + "fmt" + "net/http" + "regexp" + "sort" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + matlas "go.mongodb.org/atlas/mongodbatlas" +) + +func resourceMongoDBAtlasFederatedSettingsOrganizationRoleMapping() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceMongoDBAtlasFederatedSettingsOrganizationRoleMappingCreate, + ReadContext: resourceMongoDBAtlasFederatedSettingsOrganizationRoleMappingRead, + UpdateContext: resourceMongoDBAtlasFederatedSettingsOrganizationRoleMappingUpdate, + DeleteContext: resourceMongoDBAtlasFederatedSettingsOrganizationRoleMappingDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceMongoDBAtlasFederatedSettingsOrganizationRoleMappingImportState, + }, + Schema: map[string]*schema.Schema{ + "federation_settings_id": { + Type: schema.TypeString, + Required: true, + }, + "org_id": { + Type: schema.TypeString, + Required: true, + }, + + "external_group_name": { + Type: schema.TypeString, + Required: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "role_assignments": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "group_id": { + Type: schema.TypeString, + Optional: true, + }, + "org_id": { + Type: schema.TypeString, + Optional: true, + }, + "roles": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + }, + } +} + +func resourceMongoDBAtlasFederatedSettingsOrganizationRoleMappingRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // Get client connection. + conn := meta.(*MongoDBClient).Atlas + + ids := decodeStateID(d.Id()) + federationSettingsID := ids["federation_settings_id"] + orgID := ids["org_id"] + roleMappingID := ids["role_mapping_id"] + + federatedSettingsOrganizationRoleMapping, resp, err := conn.FederatedSettings.GetRoleMapping(context.Background(), federationSettingsID, orgID, roleMappingID) + + if err != nil { + // case 404 + // deleted in the backend case + if resp != nil && resp.StatusCode == http.StatusNotFound { + d.SetId("") + return nil + } + + return diag.FromErr(fmt.Errorf("error getting federated settings organization config: %s", err)) + } + + if err := d.Set("external_group_name", federatedSettingsOrganizationRoleMapping.ExternalGroupName); err != nil { + return diag.FromErr(fmt.Errorf("error setting external group name (%s): %s", d.Id(), err)) + } + + if err := d.Set("role_assignments", flattenRoleAssignmentsSpecial(federatedSettingsOrganizationRoleMapping.RoleAssignments)); err != nil { + return diag.FromErr(fmt.Errorf("error setting role_assignments (%s): %s", d.Id(), err)) + } + + d.SetId(encodeStateID(map[string]string{ + "federation_settings_id": federationSettingsID, + "org_id": orgID, + "role_mapping_id": roleMappingID, + })) + + return nil +} + +func resourceMongoDBAtlasFederatedSettingsOrganizationRoleMappingCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // Get client connection. + conn := meta.(*MongoDBClient).Atlas + federationSettingsID, federationSettingsIDOk := d.GetOk("federation_settings_id") + + if !federationSettingsIDOk { + return diag.FromErr(errors.New("federation_settings_id must be configured")) + } + + orgID, orgIDOk := d.GetOk("org_id") + + if !orgIDOk { + return diag.FromErr(errors.New("org_id must be configured")) + } + + externalGroupName := d.Get("external_group_name").(string) + + body := &matlas.FederatedSettingsOrganizationRoleMapping{} + + ra := []*matlas.RoleAssignments{} + + body.ExternalGroupName = externalGroupName + roleAssignments := expandRoleAssignments(d) + + for i := range roleAssignments { + ra = append(ra, &roleAssignments[i]) + } + + body.RoleAssignments = ra + + federatedSettingsOrganizationRoleMapping, resp, err := conn.FederatedSettings.CreateRoleMapping(context.Background(), federationSettingsID.(string), orgID.(string), body) + if err != nil { + // case 404 + // deleted in the backend case + if resp != nil && resp.StatusCode == http.StatusNotFound { + d.SetId("") + return nil + } + + return diag.FromErr(fmt.Errorf("error getting federated settings organization config: %s", err)) + } + + d.SetId(encodeStateID(map[string]string{ + "federation_settings_id": federationSettingsID.(string), + "org_id": orgID.(string), + "role_mapping_id": federatedSettingsOrganizationRoleMapping.ID, + })) + + return resourceMongoDBAtlasFederatedSettingsOrganizationRoleMappingRead(ctx, d, meta) +} + +func resourceMongoDBAtlasFederatedSettingsOrganizationRoleMappingUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // Get client connection. + conn := meta.(*MongoDBClient).Atlas + ids := decodeStateID(d.Id()) + federationSettingsID := ids["federation_settings_id"] + orgID := ids["org_id"] + roleMappingID := ids["role_mapping_id"] + + federatedSettingsOrganizationRoleMappingUpdate, _, err := conn.FederatedSettings.GetRoleMapping(context.Background(), federationSettingsID, orgID, roleMappingID) + if err != nil { + return diag.FromErr(fmt.Errorf("error retreiving federation settings connected organization (%s): %s", federationSettingsID, err)) + } + + if d.HasChange("external_group_name") { + externalGroupName := d.Get("external_group_name").(string) + federatedSettingsOrganizationRoleMappingUpdate.ExternalGroupName = externalGroupName + } + + if d.HasChange("role_assignments") { + federatedSettingsOrganizationRoleMappingUpdate.RoleAssignments = nil + + ra := []*matlas.RoleAssignments{} + + roleAssignments := expandRoleAssignments(d) + + for i := range roleAssignments { + ra = append(ra, &roleAssignments[i]) + } + + federatedSettingsOrganizationRoleMappingUpdate.RoleAssignments = ra + } + _, _, err = conn.FederatedSettings.UpdateRoleMapping(ctx, federationSettingsID, orgID, roleMappingID, federatedSettingsOrganizationRoleMappingUpdate) + if err != nil { + return diag.FromErr(fmt.Errorf("error updating federation settings connected organization (%s): %s", federationSettingsID, err)) + } + + return resourceMongoDBAtlasFederatedSettingsOrganizationRoleMappingRead(ctx, d, meta) +} + +func resourceMongoDBAtlasFederatedSettingsOrganizationRoleMappingDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + // Get client connection. + conn := meta.(*MongoDBClient).Atlas + ids := decodeStateID(d.Id()) + federationSettingsID := ids["federation_settings_id"] + orgID := ids["org_id"] + roleMappingID := ids["role_mapping_id"] + + _, err := conn.FederatedSettings.DeleteRoleMapping(ctx, federationSettingsID, orgID, roleMappingID) + if err != nil { + return diag.FromErr(fmt.Errorf("error deleting federation settings connected organization (%s): %s", federationSettingsID, err)) + } + + return nil +} + +func resourceMongoDBAtlasFederatedSettingsOrganizationRoleMappingImportState(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + conn := meta.(*MongoDBClient).Atlas + + federationSettingsID, orgID, roleMappingID, err := splitFederatedSettingsOrganizationRoleMappingImportID(d.Id()) + if err != nil { + return nil, err + } + + federatedSettingsOrganizationRoleMapping, _, err := conn.FederatedSettings.GetRoleMapping(context.Background(), *federationSettingsID, *orgID, *roleMappingID) + if err != nil { + return nil, fmt.Errorf("couldn't import Role Mappings (%s) in Federation settings (%s), error: %s", *roleMappingID, *federationSettingsID, err) + } + + if err := d.Set("federation_settings_id", *federationSettingsID); err != nil { + return nil, fmt.Errorf("error setting role mapping in Federation settings (%s): %s", d.Id(), err) + } + + if err := d.Set("org_id", *orgID); err != nil { + return nil, fmt.Errorf("error setting role mapping in Federation settings (%s): %s", d.Id(), err) + } + + if err := d.Set("role_assignments", flattenRoleAssignmentsSpecial(federatedSettingsOrganizationRoleMapping.RoleAssignments)); err != nil { + return nil, fmt.Errorf("error setting role_assignments (%s): %s", d.Id(), err) + } + + d.SetId(encodeStateID(map[string]string{ + "federation_settings_id": *federationSettingsID, + "org_id": *orgID, + "role_mapping_id": *roleMappingID, + })) + + return []*schema.ResourceData{d}, nil +} + +func splitFederatedSettingsOrganizationRoleMappingImportID(id string) (federationSettingsID, orgID, roleMappingID *string, err error) { + var re = regexp.MustCompile(`(?s)^(.*)-(.*)-(.*)$`) + parts := re.FindStringSubmatch(id) + + if len(parts) != 4 { + err = errors.New("import format error: to import a Federated Settings Role Mappings, use the format {federation_settings_id}-{org_id}-{role_mapping_id}") + return + } + + federationSettingsID = &parts[1] + orgID = &parts[2] + roleMappingID = &parts[3] + + return +} + +type roleAssignmentsByFields []matlas.RoleAssignments + +func (ra roleAssignmentsByFields) Len() int { return len(ra) } +func (ra roleAssignmentsByFields) Swap(i, j int) { ra[i], ra[j] = ra[j], ra[i] } + +func (ra roleAssignmentsByFields) Less(i, j int) bool { + compareVal := strings.Compare(ra[i].OrgID, ra[j].OrgID) + + if compareVal != 0 { + return compareVal < 0 + } + + compareVal = strings.Compare(ra[i].GroupID, ra[j].GroupID) + + if compareVal != 0 { + return compareVal < 0 + } + + return ra[i].Role < ra[j].Role +} + +type roleAssignmentRefsByFields []*matlas.RoleAssignments + +func (ra roleAssignmentRefsByFields) Len() int { return len(ra) } +func (ra roleAssignmentRefsByFields) Swap(i, j int) { ra[i], ra[j] = ra[j], ra[i] } + +func (ra roleAssignmentRefsByFields) Less(i, j int) bool { + compareVal := strings.Compare(ra[i].OrgID, ra[j].OrgID) + + if compareVal != 0 { + return compareVal < 0 + } + + compareVal = strings.Compare(ra[i].GroupID, ra[j].GroupID) + + if compareVal != 0 { + return compareVal < 0 + } + + return ra[i].Role < ra[j].Role +} + +func expandRoleAssignments(d *schema.ResourceData) []matlas.RoleAssignments { + var roleAssignmentsReturn []matlas.RoleAssignments + + if v, ok := d.GetOk("role_assignments"); ok { + if rs := v.(*schema.Set); rs.Len() > 0 { + roleAssignments := []matlas.RoleAssignments{} + roleAssignment := matlas.RoleAssignments{} + + for _, r := range rs.List() { + roleMap := r.(map[string]interface{}) + + for _, role := range roleMap["roles"].(*schema.Set).List() { + roleAssignment.OrgID = roleMap["org_id"].(string) + roleAssignment.GroupID = roleMap["group_id"].(string) + roleAssignment.Role = role.(string) + roleAssignments = append(roleAssignments, roleAssignment) + } + roleAssignmentsReturn = roleAssignments + } + } + } + + sort.Sort(roleAssignmentsByFields(roleAssignmentsReturn)) + + return roleAssignmentsReturn +} + +func flattenRoleAssignmentsSpecial(roleAssignments []*matlas.RoleAssignments) []map[string]interface{} { + if len(roleAssignments) == 0 { + return nil + } + + sort.Sort(roleAssignmentRefsByFields(roleAssignments)) + + var flattenedRoleAssignments []map[string]interface{} + var roleAssignment = map[string]interface{}{ + "group_id": roleAssignments[0].GroupID, + "org_id": roleAssignments[0].OrgID, + "roles": []string{}, + } + + for _, row := range roleAssignments { + if (roleAssignment["org_id"] != "" && roleAssignment["org_id"] != row.OrgID) || + (roleAssignment["group_id"] != "" && roleAssignment["group_id"] != row.GroupID) { + flattenedRoleAssignments = append(flattenedRoleAssignments, roleAssignment) + + roleAssignment = map[string]interface{}{ + "group_id": row.GroupID, + "org_id": row.OrgID, + "roles": []string{}, + } + } + + roleAssignment["roles"] = append(roleAssignment["roles"].([]string), row.Role) + } + + flattenedRoleAssignments = append(flattenedRoleAssignments, roleAssignment) + + return flattenedRoleAssignments +} diff --git a/mongodbatlas/resource_mongodbatlas_federated_settings_organization_role_mapping_test.go b/mongodbatlas/resource_mongodbatlas_federated_settings_organization_role_mapping_test.go new file mode 100644 index 0000000000..283ba2251a --- /dev/null +++ b/mongodbatlas/resource_mongodbatlas_federated_settings_organization_role_mapping_test.go @@ -0,0 +1,145 @@ +package mongodbatlas + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + matlas "go.mongodb.org/atlas/mongodbatlas" +) + +func TestAccResourceMongoDBAtlasFederatedSettingsOrganizationRoleMapping_basic(t *testing.T) { + SkipTestExtCred(t) + var ( + federatedSettingsOrganizationRoleMapping matlas.FederatedSettingsOrganizationRoleMapping + resourceName = "mongodbatlas_federated_settings_org_role_mapping.test" + federationSettingsID = os.Getenv("MONGODB_ATLAS_FEDERATION_SETTINGS_ID") + orgID = os.Getenv("MONGODB_ATLAS_FEDERATED_ORG_ID") + groupID = os.Getenv("MONGODB_ATLAS_FEDERATED_GROUP_ID") + ) + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { checkFederatedSettings(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckMongoDBAtlasFederatedSettingsOrganizationRoleMappingDestroy, + Steps: []resource.TestStep{ + { + Config: testAccMongoDBAtlasFederatedSettingsOrganizationRoleMappingConfig(federationSettingsID, orgID, groupID), + + Check: resource.ComposeTestCheckFunc( + testAccCheckMongoDBAtlasFederatedSettingsOrganizationRoleMappingExists(resourceName, &federatedSettingsOrganizationRoleMapping), + resource.TestCheckResourceAttr(resourceName, "federation_settings_id", federationSettingsID), + resource.TestCheckResourceAttr(resourceName, "org_id", orgID), + resource.TestCheckResourceAttr(resourceName, "external_group_name", "newtestgroup"), + ), + }, + }, + }) +} + +func TestAccResourceMongoDBAtlasFederatedSettingsOrganizationRoleMapping_importBasic(t *testing.T) { + SkipTestExtCred(t) + var ( + resourceName = "mongodbatlas_federated_settings_org_role_mapping.test" + federationSettingsID = os.Getenv("MONGODB_ATLAS_FEDERATION_SETTINGS_ID") + orgID = os.Getenv("MONGODB_ATLAS_FEDERATED_ORG_ID") + groupID = os.Getenv("MONGODB_ATLAS_FEDERATED_GROUP_ID") + ) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { checkFederatedSettings(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckMongoDBAtlasFederatedSettingsOrganizationRoleMappingDestroy, + Steps: []resource.TestStep{ + + { + Config: testAccMongoDBAtlasFederatedSettingsOrganizationRoleMappingConfig(federationSettingsID, orgID, groupID), + ResourceName: resourceName, + ImportStateIdFunc: testAccCheckMongoDBAtlasFederatedSettingsOrganizationRoleMappingImportStateIDFunc(resourceName), + ImportState: false, + ImportStateVerify: false, + }, + }, + }) +} + +func testAccCheckMongoDBAtlasFederatedSettingsOrganizationRoleMappingExists(resourceName string, + federatedSettingsOrganizationRoleMapping *matlas.FederatedSettingsOrganizationRoleMapping) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*MongoDBClient).Atlas + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("no ID is set") + } + + response, _, err := conn.FederatedSettings.GetRoleMapping(context.Background(), + rs.Primary.Attributes["federation_settings_id"], + rs.Primary.Attributes["org_id"], + rs.Primary.Attributes["role_mapping_id"]) + if err == nil { + *federatedSettingsOrganizationRoleMapping = *response + + return nil + } + + return fmt.Errorf("role mapping (%s) does not exist", rs.Primary.Attributes["role_mapping_id"]) + } +} + +func testAccCheckMongoDBAtlasFederatedSettingsOrganizationRoleMappingDestroy(state *terraform.State) error { + conn := testAccProvider.Meta().(*MongoDBClient).Atlas + + for _, rs := range state.RootModule().Resources { + if rs.Type != "mongodbatlas_federated_settings_org_role_mapping" { + continue + } + + ids := decodeStateID(rs.Primary.ID) + + roleMapping, _, err := conn.FederatedSettings.GetRoleMapping(context.Background(), ids["federation_settings_id"], ids["org_id"], ids["role_mapping_id"]) + if err == nil && roleMapping != nil { + return fmt.Errorf("role mapping (%s) still exists", ids["okta_idp_id"]) + } + } + + return nil +} + +func testAccCheckMongoDBAtlasFederatedSettingsOrganizationRoleMappingImportStateIDFunc(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("not found: %s", resourceName) + } + + ids := decodeStateID(rs.Primary.ID) + + return fmt.Sprintf("%s-%s-%s", ids["federation_settings_id"], ids["org_id"], ids["role_mapping_id"]), nil + } +} + +func testAccMongoDBAtlasFederatedSettingsOrganizationRoleMappingConfig(federationSettingsID, orgID, groupID string) string { + return fmt.Sprintf(` + resource "mongodbatlas_federated_settings_org_role_mapping" "test" { + federation_settings_id = "%[1]s" + org_id = "%[2]s" + external_group_name = "newtestgroup" + role_assignments { + org_id = "%[2]s" + roles = ["ORG_MEMBER","ORG_GROUP_CREATOR"] + } + + role_assignments { + group_id = "%[3]s" + roles = ["GROUP_OWNER","GROUP_DATA_ACCESS_ADMIN","GROUP_SEARCH_INDEX_EDITOR","GROUP_DATA_ACCESS_READ_ONLY"] + } + + }`, federationSettingsID, orgID, groupID) +}