diff --git a/.changelog/2310.txt b/.changelog/2310.txt new file mode 100644 index 0000000000..1362a841ba --- /dev/null +++ b/.changelog/2310.txt @@ -0,0 +1,27 @@ +```release-note:enhancement +resource/mongodbatlas_federated_settings_identity_provider: Adds create and delete operations for Workforce OIDC IdP +``` + +```release-note:enhancement +resource/mongodbatlas_federated_settings_identity_provider: Adds `description` and `authorization_type` fields +``` + +```release-note:enhancement +data-source/mongodbatlas_federated_settings_identity_provider: Adds `description` and `authorization_type` fields +``` + +```release-note:enhancement +data-source/mongodbatlas_federated_settings_identity_providers: Adds `description` and `authorization_type` fields +``` + +```release-note:breaking-change +resource/mongodbatlas_federated_settings_identity_provider: Replaces `audience_claim` field with `audience` +``` + +```release-note:breaking-change +data-source/mongodbatlas_federated_settings_identity_provider: Replaces `audience_claim` field with `audience` +``` + +```release-note:breaking-change +data-source/mongodbatlas_federated_settings_identity_providers: Replaces `audience_claim` field with `audience` +``` diff --git a/.github/workflows/acceptance-tests-runner.yml b/.github/workflows/acceptance-tests-runner.yml index 409fc23f07..e5f1db6719 100644 --- a/.github/workflows/acceptance-tests-runner.yml +++ b/.github/workflows/acceptance-tests-runner.yml @@ -61,9 +61,6 @@ on: mongodb_atlas_federated_idp_id: type: string required: true - mongodb_atlas_federated_oidc_idp_id: - type: string - required: true mongodb_atlas_federated_sso_url: type: string required: true @@ -546,7 +543,6 @@ jobs: MONGODB_ATLAS_FEDERATION_SETTINGS_ID: ${{ inputs.mongodb_atlas_federation_settings_id }} MONGODB_ATLAS_FEDERATED_OKTA_IDP_ID: ${{ inputs.mongodb_atlas_federated_okta_idp_id }} MONGODB_ATLAS_FEDERATED_IDP_ID: ${{ inputs.mongodb_atlas_federated_idp_id }} - MONGODB_ATLAS_FEDERATED_OIDC_IDP_ID: ${{ inputs.mongodb_atlas_federated_oidc_idp_id }} MONGODB_ATLAS_FEDERATED_SSO_URL: ${{ inputs.mongodb_atlas_federated_sso_url }} MONGODB_ATLAS_FEDERATED_ISSUER_URI: ${{ inputs.mongodb_atlas_federated_issuer_uri }} MONGODB_ATLAS_FEDERATED_ORG_ID: ${{ inputs.mongodb_atlas_federated_org_id }} diff --git a/.github/workflows/acceptance-tests.yml b/.github/workflows/acceptance-tests.yml index 0b6e7a703e..204d1543e4 100644 --- a/.github/workflows/acceptance-tests.yml +++ b/.github/workflows/acceptance-tests.yml @@ -90,7 +90,6 @@ jobs: mongodb_atlas_federation_settings_id: ${{ inputs.atlas_cloud_env == 'qa' && vars.MONGODB_ATLAS_FEDERATION_SETTINGS_ID_QA || vars.MONGODB_ATLAS_FEDERATION_SETTINGS_ID }} mongodb_atlas_federated_okta_idp_id: ${{ inputs.atlas_cloud_env == 'qa' && vars.MONGODB_ATLAS_FEDERATED_OKTA_IDP_ID_QA || vars.MONGODB_ATLAS_FEDERATED_OKTA_IDP_ID }} mongodb_atlas_federated_idp_id: ${{ inputs.atlas_cloud_env == 'qa' && vars.MONGODB_ATLAS_FEDERATED_IDP_ID_QA || vars.MONGODB_ATLAS_FEDERATED_IDP_ID }} - mongodb_atlas_federated_oidc_idp_id: ${{ inputs.atlas_cloud_env == 'qa' && vars.MONGODB_ATLAS_FEDERATED_OIDC_IDP_ID_QA || vars.MONGODB_ATLAS_FEDERATED_OIDC_IDP_ID }} mongodb_atlas_federated_sso_url: ${{ vars.MONGODB_ATLAS_FEDERATED_SSO_URL }} mongodb_atlas_federated_issuer_uri: ${{ vars.MONGODB_ATLAS_FEDERATED_ISSUER_URI }} mongodb_atlas_federated_org_id: ${{ inputs.atlas_cloud_env == 'qa' && vars.MONGODB_ATLAS_FEDERATED_ORG_ID_QA || vars.MONGODB_ATLAS_FEDERATED_ORG_ID }} diff --git a/go.mod b/go.mod index 888606b86a..32d8308848 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,6 @@ require ( github.com/stretchr/testify v1.9.0 github.com/zclconf/go-cty v1.14.4 go.mongodb.org/atlas v0.36.0 - go.mongodb.org/atlas-sdk/v20231115008 v20231115008.5.0 go.mongodb.org/atlas-sdk/v20231115014 v20231115014.0.0 go.mongodb.org/realm v0.1.0 ) diff --git a/go.sum b/go.sum index 83c8fdab7e..2321697c70 100644 --- a/go.sum +++ b/go.sum @@ -779,8 +779,6 @@ github.com/zclconf/go-cty-yaml v1.0.2/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgK go.mongodb.org/atlas v0.12.0/go.mod h1:wVCnHcm/7/IfTjEB6K8K35PLG70yGz8BdkRwX0oK9/M= go.mongodb.org/atlas v0.36.0 h1:m05S3AO7zkl+bcG1qaNsEKBnAqnKx2FDwLooHpIG3j4= go.mongodb.org/atlas v0.36.0/go.mod h1:nfPldE9dSama6G2IbIzmEza02Ly7yFZjMMVscaM0uEc= -go.mongodb.org/atlas-sdk/v20231115008 v20231115008.5.0 h1:OuV1HfIpZUZa4+BKvtrvDlNqnilkCkdHspuZok6KAbM= -go.mongodb.org/atlas-sdk/v20231115008 v20231115008.5.0/go.mod h1:0707RpWIrNFZ6Msy/dwRDCzC5JVDon61JoOqcbfCujg= go.mongodb.org/atlas-sdk/v20231115014 v20231115014.0.0 h1:hN7x3m6THf03q/tE48up1j0U/26lJmx+s1LXB/qvHHc= go.mongodb.org/atlas-sdk/v20231115014 v20231115014.0.0/go.mod h1:pCl46YnWOIde8lq27whXDwUseNeUvtAy3vy5ZDeTcBA= go.mongodb.org/realm v0.1.0 h1:zJiXyLaZrznQ+Pz947ziSrDKUep39DO4SfA0Fzx8M4M= diff --git a/internal/config/client.go b/internal/config/client.go index 440543fdb9..d87b0574ce 100644 --- a/internal/config/client.go +++ b/internal/config/client.go @@ -9,7 +9,6 @@ import ( "strings" "time" - admin20231115008 "go.mongodb.org/atlas-sdk/v20231115008/admin" "go.mongodb.org/atlas-sdk/v20231115014/admin" matlasClient "go.mongodb.org/atlas/mongodbatlas" realmAuth "go.mongodb.org/realm/auth" @@ -29,10 +28,9 @@ const ( // MongoDBClient contains the mongodbatlas clients and configurations type MongoDBClient struct { - Atlas *matlasClient.Client - AtlasV2 *admin.APIClient - Atlas20231115008 *admin20231115008.APIClient // Needed to avoid breaking changes in federated_settings_identity_provider resource. - Config *Config + Atlas *matlasClient.Client + AtlasV2 *admin.APIClient + Config *Config } // Config contains the configurations needed to use SDKs @@ -104,16 +102,11 @@ func (c *Config) NewClient(ctx context.Context) (any, error) { if err != nil { return nil, err } - sdk20231115008Client, err := c.newSDK20231115008Client(client) - if err != nil { - return nil, err - } clients := &MongoDBClient{ - Atlas: atlasClient, - AtlasV2: sdkV2Client, - Atlas20231115008: sdk20231115008Client, - Config: c, + Atlas: atlasClient, + AtlasV2: sdkV2Client, + Config: c, } return clients, nil @@ -135,22 +128,6 @@ func (c *Config) newSDKV2Client(client *http.Client) (*admin.APIClient, error) { return sdkv2, nil } -func (c *Config) newSDK20231115008Client(client *http.Client) (*admin20231115008.APIClient, error) { - opts := []admin20231115008.ClientModifier{ - admin20231115008.UseHTTPClient(client), - admin20231115008.UseUserAgent(userAgent(c)), - admin20231115008.UseBaseURL(c.BaseURL), - admin20231115008.UseDebug(false)} - - // Initialize the MongoDB Versioned Atlas Client. - sdkv2, err := admin20231115008.NewClient(opts...) - if err != nil { - return nil, err - } - - return sdkv2, nil -} - func (c *MongoDBClient) GetRealmClient(ctx context.Context) (*realm.Client, error) { // Realm if c.Config.PublicKey == "" && c.Config.PrivateKey == "" { diff --git a/internal/service/federatedsettingsidentityprovider/data_source_federated_settings_identity_provider.go b/internal/service/federatedsettingsidentityprovider/data_source_federated_settings_identity_provider.go index 592e41f048..b2d1dd3c51 100644 --- a/internal/service/federatedsettingsidentityprovider/data_source_federated_settings_identity_provider.go +++ b/internal/service/federatedsettingsidentityprovider/data_source_federated_settings_identity_provider.go @@ -13,7 +13,7 @@ import ( func DataSource() *schema.Resource { return &schema.Resource{ - ReadContext: dataSourceMongoDBAtlasFederatedSettingsIdentityProviderRead, + ReadContext: dataSourceRead, Schema: map[string]*schema.Schema{ "federation_settings_id": { Type: schema.TypeString, @@ -205,12 +205,9 @@ func DataSource() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "audience_claim": { - Type: schema.TypeList, + "audience": { + Type: schema.TypeString, Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, }, "client_id": { Type: schema.TypeString, @@ -231,12 +228,19 @@ func DataSource() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "authorization_type": { + Type: schema.TypeString, + Computed: true, + }, }, } } -func dataSourceMongoDBAtlasFederatedSettingsIdentityProviderRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - // Get client connection. - connV2 := meta.(*config.MongoDBClient).Atlas20231115008 +func dataSourceRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + connV2 := meta.(*config.MongoDBClient).AtlasV2 federationSettingsID, federationSettingsIDOk := d.GetOk("federation_settings_id") @@ -286,7 +290,7 @@ func dataSourceMongoDBAtlasFederatedSettingsIdentityProviderRead(ctx context.Con } if federatedSettingsIdentityProvider.GetProtocol() == OIDC { - if err := d.Set("audience_claim", federatedSettingsIdentityProvider.AudienceClaim); err != nil { + if err := d.Set("audience", federatedSettingsIdentityProvider.Audience); err != nil { return diag.FromErr(fmt.Errorf("error setting `audience_claim` for federatedSettings IdentityProviders: %s", err)) } @@ -305,6 +309,14 @@ func dataSourceMongoDBAtlasFederatedSettingsIdentityProviderRead(ctx context.Con if err := d.Set("user_claim", federatedSettingsIdentityProvider.UserClaim); err != nil { return diag.FromErr(fmt.Errorf("error setting `user_claim` for federatedSettings IdentityProviders: %s", err)) } + + if err := d.Set("authorization_type", federatedSettingsIdentityProvider.AuthorizationType); err != nil { + return diag.FromErr(fmt.Errorf("error setting `authorization_type` for federatedSettings IdentityProviders: %s", err)) + } + } + + if err := d.Set("description", federatedSettingsIdentityProvider.Description); err != nil { + return diag.FromErr(fmt.Errorf("error setting `description` for federatedSettings IdentityProviders: %s", err)) } if err := d.Set("associated_domains", federatedSettingsIdentityProvider.AssociatedDomains); err != nil { diff --git a/internal/service/federatedsettingsidentityprovider/data_source_federated_settings_identity_provider_test.go b/internal/service/federatedsettingsidentityprovider/data_source_federated_settings_identity_provider_test.go index 3099eaa233..5054e6ca49 100644 --- a/internal/service/federatedsettingsidentityprovider/data_source_federated_settings_identity_provider_test.go +++ b/internal/service/federatedsettingsidentityprovider/data_source_federated_settings_identity_provider_test.go @@ -36,35 +36,6 @@ func TestAccFederatedSettingsIdentityProviderDS_samlBasic(t *testing.T) { }) } -func TestAccFederatedSettingsIdentityProviderDS_oidcBasic(t *testing.T) { - var ( - resourceName = "data.mongodbatlas_federated_settings_identity_provider.test" - federatedSettingsID = os.Getenv("MONGODB_ATLAS_FEDERATION_SETTINGS_ID") - idpID = os.Getenv("MONGODB_ATLAS_FEDERATED_OIDC_IDP_ID") - ) - resource.Test(t, resource.TestCase{ - PreCheck: func() { acc.PreCheckFederatedSettings(t) }, - ProtoV6ProviderFactories: acc.TestAccProviderV6Factories, - Steps: []resource.TestStep{ - { - Config: configBasicDS(federatedSettingsID, idpID), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet(resourceName, "associated_orgs.#"), - resource.TestCheckResourceAttrSet(resourceName, "audience_claim.#"), - resource.TestCheckResourceAttrSet(resourceName, "client_id"), - resource.TestCheckResourceAttrSet(resourceName, "groups_claim"), - resource.TestCheckResourceAttrSet(resourceName, "requested_scopes.#"), - resource.TestCheckResourceAttrSet(resourceName, "user_claim"), - resource.TestCheckResourceAttr(resourceName, "protocol", "OIDC"), - resource.TestCheckResourceAttr(resourceName, "okta_idp_id", ""), - resource.TestCheckResourceAttr(resourceName, "idp_id", idpID), - resource.TestCheckResourceAttr(resourceName, "federation_settings_id", federatedSettingsID), - ), - }, - }, - }) -} - func configBasicDS(federatedSettingsID, idpID string) string { return fmt.Sprintf(` data "mongodbatlas_federated_settings_identity_provider" "test" { diff --git a/internal/service/federatedsettingsidentityprovider/data_source_federated_settings_identity_providers.go b/internal/service/federatedsettingsidentityprovider/data_source_federated_settings_identity_providers.go index fe0811a9f0..826fc22714 100644 --- a/internal/service/federatedsettingsidentityprovider/data_source_federated_settings_identity_providers.go +++ b/internal/service/federatedsettingsidentityprovider/data_source_federated_settings_identity_providers.go @@ -5,7 +5,7 @@ import ( "errors" "fmt" - admin20231115008 "go.mongodb.org/atlas-sdk/v20231115008/admin" + "go.mongodb.org/atlas-sdk/v20231115014/admin" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -16,7 +16,7 @@ import ( func PluralDataSource() *schema.Resource { return &schema.Resource{ - ReadContext: dataSourceMongoDBAtlasFederatedSettingsIdentityProvidersRead, + ReadContext: dataSourcePluralRead, Schema: map[string]*schema.Schema{ "federation_settings_id": { Type: schema.TypeString, @@ -218,12 +218,9 @@ func PluralDataSource() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "audience_claim": { - Type: schema.TypeList, + "audience": { + Type: schema.TypeString, Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, }, "client_id": { Type: schema.TypeString, @@ -244,27 +241,32 @@ func PluralDataSource() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "authorization_type": { + Type: schema.TypeString, + Computed: true, + }, }, }, }, }, } } -func dataSourceMongoDBAtlasFederatedSettingsIdentityProvidersRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - // Get client connection. - connV2 := meta.(*config.MongoDBClient).Atlas20231115008 - +func dataSourcePluralRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + connV2 := meta.(*config.MongoDBClient).AtlasV2 federationSettingsID, federationSettingsIDOk := d.GetOk("federation_settings_id") if !federationSettingsIDOk { return diag.FromErr(errors.New("federation_settings_id must be configured")) } - // once the SDK is upgraded above version "go.mongodb.org/atlas-sdk/v20231115012/mockadmin" we can use pagination parameters to iterate over all results (and adjust documentation) - // pagination attributes are deprecated and can be removed as we move towards not exposing these pagination options to the user - params := &admin20231115008.ListIdentityProvidersApiParams{ + params := &admin.ListIdentityProvidersApiParams{ FederationSettingsId: federationSettingsID.(string), Protocol: &[]string{OIDC, SAML}, + IdpType: &[]string{WORKFORCE}, } providers, _, err := connV2.FederatedAuthenticationApi.ListIdentityProvidersWithParams(ctx, params).Execute() diff --git a/internal/service/federatedsettingsidentityprovider/model_federated_settings_identity_provider.go b/internal/service/federatedsettingsidentityprovider/model_federated_settings_identity_provider.go index 532b8ff7bd..680a23946a 100644 --- a/internal/service/federatedsettingsidentityprovider/model_federated_settings_identity_provider.go +++ b/internal/service/federatedsettingsidentityprovider/model_federated_settings_identity_provider.go @@ -4,12 +4,74 @@ import ( "sort" "strings" - admin20231115008 "go.mongodb.org/atlas-sdk/v20231115008/admin" + "go.mongodb.org/atlas-sdk/v20231115014/admin" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/mongodb/terraform-provider-mongodbatlas/internal/common/conversion" + "github.com/spf13/cast" ) -func FlattenFederatedSettingsIdentityProvider(federatedSettingsIdentityProvider []admin20231115008.FederationIdentityProvider) []map[string]any { +const WORKFORCE = "WORKFORCE" + +func ExpandIdentityProviderOIDCCreate(d *schema.ResourceData) *admin.FederationOidcIdentityProviderUpdate { + return &admin.FederationOidcIdentityProviderUpdate{ + Audience: conversion.StringPtr(d.Get("audience").(string)), + AssociatedDomains: expandAssociatedDomains(d), + AuthorizationType: conversion.StringPtr(d.Get("authorization_type").(string)), + ClientId: conversion.StringPtr(d.Get("client_id").(string)), + Description: conversion.StringPtr(d.Get("description").(string)), + DisplayName: conversion.StringPtr(d.Get("name").(string)), + GroupsClaim: conversion.StringPtr(d.Get("groups_claim").(string)), + IdpType: conversion.StringPtr(WORKFORCE), + IssuerUri: conversion.StringPtr(d.Get("issuer_uri").(string)), + Protocol: conversion.StringPtr(d.Get("protocol").(string)), + RequestedScopes: expandRequestedScopes(d), + UserClaim: conversion.StringPtr(d.Get("user_claim").(string)), + } +} + +func expandRequestedScopes(d *schema.ResourceData) *[]string { + requestedScopes := d.Get("requested_scopes") + requestedScopesSlice := cast.ToStringSlice(requestedScopes) + if requestedScopesSlice == nil { + requestedScopesSlice = []string{} + } + return &requestedScopesSlice +} + +func expandAssociatedDomains(d *schema.ResourceData) *[]string { + associatedDomains := d.Get("associated_domains") + associatedDomainsSlice := cast.ToStringSlice(associatedDomains) + if associatedDomainsSlice == nil { + associatedDomainsSlice = []string{} + } + return &associatedDomainsSlice +} + +func ExpandIdentityProviderUpdate(d *schema.ResourceData, existingIdentityProvider *admin.FederationIdentityProvider) *admin.FederationIdentityProviderUpdate { + return &admin.FederationIdentityProviderUpdate{ + AssociatedDomains: existingIdentityProvider.AssociatedDomains, + Audience: existingIdentityProvider.Audience, + AuthorizationType: existingIdentityProvider.AuthorizationType, + ClientId: existingIdentityProvider.ClientId, + Description: existingIdentityProvider.Description, + DisplayName: existingIdentityProvider.DisplayName, + GroupsClaim: existingIdentityProvider.GroupsClaim, + IdpType: existingIdentityProvider.IdpType, + IssuerUri: existingIdentityProvider.IssuerUri, + Protocol: existingIdentityProvider.Protocol, + PemFileInfo: nil, + RequestBinding: existingIdentityProvider.RequestBinding, + RequestedScopes: existingIdentityProvider.RequestedScopes, + ResponseSignatureAlgorithm: existingIdentityProvider.ResponseSignatureAlgorithm, + SsoDebugEnabled: existingIdentityProvider.SsoDebugEnabled, + SsoUrl: existingIdentityProvider.SsoUrl, + Status: existingIdentityProvider.Status, + UserClaim: existingIdentityProvider.UserClaim, + } +} + +func FlattenFederatedSettingsIdentityProvider(federatedSettingsIdentityProvider []admin.FederationIdentityProvider) []map[string]any { var federatedSettingsIdentityProviderMap []map[string]any if len(federatedSettingsIdentityProvider) > 0 { federatedSettingsIdentityProviderMap = make([]map[string]any, len(federatedSettingsIdentityProvider)) @@ -31,11 +93,13 @@ func FlattenFederatedSettingsIdentityProvider(federatedSettingsIdentityProvider "status": federatedSettingsIdentityProvider[i].Status, "idp_id": federatedSettingsIdentityProvider[i].Id, "protocol": federatedSettingsIdentityProvider[i].Protocol, - "audience_claim": federatedSettingsIdentityProvider[i].AudienceClaim, + "audience": federatedSettingsIdentityProvider[i].Audience, "client_id": federatedSettingsIdentityProvider[i].ClientId, "groups_claim": federatedSettingsIdentityProvider[i].GroupsClaim, "requested_scopes": federatedSettingsIdentityProvider[i].RequestedScopes, "user_claim": federatedSettingsIdentityProvider[i].UserClaim, + "authorization_type": federatedSettingsIdentityProvider[i].AuthorizationType, + "description": federatedSettingsIdentityProvider[i].Description, } } } @@ -43,7 +107,7 @@ func FlattenFederatedSettingsIdentityProvider(federatedSettingsIdentityProvider return federatedSettingsIdentityProviderMap } -func FlattenAssociatedOrgs(associatedOrgs []admin20231115008.ConnectedOrgConfig) []map[string]any { +func FlattenAssociatedOrgs(associatedOrgs []admin.ConnectedOrgConfig) []map[string]any { var associatedOrgsMap []map[string]any if len(associatedOrgs) == 0 { @@ -78,7 +142,7 @@ func FlattenAssociatedOrgs(associatedOrgs []admin20231115008.ConnectedOrgConfig) return associatedOrgsMap } -type mRoleAssignmentV2 []admin20231115008.RoleAssignment +type mRoleAssignmentV2 []admin.RoleAssignment func (ra mRoleAssignmentV2) Len() int { return len(ra) } func (ra mRoleAssignmentV2) Swap(i, j int) { ra[i], ra[j] = ra[j], ra[i] } @@ -98,7 +162,7 @@ func (ra mRoleAssignmentV2) Less(i, j int) bool { return *ra[i].Role < *ra[j].Role } -func FlattenRoleAssignments(roleAssignments []admin20231115008.RoleAssignment) []map[string]any { +func FlattenRoleAssignments(roleAssignments []admin.RoleAssignment) []map[string]any { sort.Sort(mRoleAssignmentV2(roleAssignments)) var roleAssignmentsMap []map[string]any @@ -118,7 +182,7 @@ func FlattenRoleAssignments(roleAssignments []admin20231115008.RoleAssignment) [ return roleAssignmentsMap } -func FlattenFederatedUser(federatedUsers []admin20231115008.FederatedUser) []map[string]any { +func FlattenFederatedUser(federatedUsers []admin.FederatedUser) []map[string]any { var userConflictsMap []map[string]any if len(federatedUsers) == 0 { @@ -139,7 +203,7 @@ func FlattenFederatedUser(federatedUsers []admin20231115008.FederatedUser) []map return userConflictsMap } -type authFederationoleMappingsByGroupName []admin20231115008.AuthFederationRoleMapping +type authFederationoleMappingsByGroupName []admin.AuthFederationRoleMapping func (ra authFederationoleMappingsByGroupName) Len() int { return len(ra) } func (ra authFederationoleMappingsByGroupName) Swap(i, j int) { ra[i], ra[j] = ra[j], ra[i] } @@ -148,7 +212,7 @@ func (ra authFederationoleMappingsByGroupName) Less(i, j int) bool { return ra[i].ExternalGroupName < ra[j].ExternalGroupName } -func FlattenAuthFederationRoleMapping(roleMappings []admin20231115008.AuthFederationRoleMapping) []map[string]any { +func FlattenAuthFederationRoleMapping(roleMappings []admin.AuthFederationRoleMapping) []map[string]any { sort.Sort(authFederationoleMappingsByGroupName(roleMappings)) var roleMappingsMap []map[string]any @@ -168,7 +232,7 @@ func FlattenAuthFederationRoleMapping(roleMappings []admin20231115008.AuthFedera return roleMappingsMap } -func FlattenPemFileInfo(pemFileInfo admin20231115008.PemFileInfo) []map[string]any { +func FlattenPemFileInfo(pemFileInfo admin.PemFileInfo) []map[string]any { var pemFileInfoMap []map[string]any if certificates := pemFileInfo.GetCertificates(); len(certificates) > 0 { @@ -183,7 +247,7 @@ func FlattenPemFileInfo(pemFileInfo admin20231115008.PemFileInfo) []map[string]a return pemFileInfoMap } -func FlattenFederatedSettingsCertificates(certificates []admin20231115008.X509Certificate) []map[string]any { +func FlattenFederatedSettingsCertificates(certificates []admin.X509Certificate) []map[string]any { var certificatesMap []map[string]any if len(certificates) > 0 { diff --git a/internal/service/federatedsettingsidentityprovider/model_federated_settings_identity_provider_test.go b/internal/service/federatedsettingsidentityprovider/model_federated_settings_identity_provider_test.go index a1653c4cf5..64e3b9dcda 100644 --- a/internal/service/federatedsettingsidentityprovider/model_federated_settings_identity_provider_test.go +++ b/internal/service/federatedsettingsidentityprovider/model_federated_settings_identity_provider_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - admin20231115008 "go.mongodb.org/atlas-sdk/v20231115008/admin" + "go.mongodb.org/atlas-sdk/v20231115014/admin" "github.com/stretchr/testify/assert" @@ -43,13 +43,15 @@ var ( status = "ACTIVE" samlProtocol = "SAML" oidcProtocol = "OIDC" - audienceClaim = []string{"audienceClaim"} + audience = "audience" clientID = "clientId" groupsClaim = "groupsClaim" requestedScopes = []string{"requestedScopes"} userClaim = "userClaim" + description = "description" + authorizationType = "GROUP" - roleAssignments = []admin20231115008.RoleAssignment{ + roleAssignments = []admin.RoleAssignment{ { GroupId: &groupID, OrgId: &organizationID, @@ -63,7 +65,7 @@ var ( "role": &role, }, } - pemCertificates = []admin20231115008.X509Certificate{ + pemCertificates = []admin.X509Certificate{ { NotAfter: ¬After, NotBefore: ¬Before, @@ -75,7 +77,7 @@ var ( "not_before": conversion.TimePtrToStringPtr(¬Before), }, } - federationRoleMapping = []admin20231115008.AuthFederationRoleMapping{ + federationRoleMapping = []admin.AuthFederationRoleMapping{ { ExternalGroupName: externalGroupName, Id: &roleAssignmentsID, @@ -89,7 +91,7 @@ var ( "role_assignments": flattenedRoleAssignments, }, } - federatedUser = []admin20231115008.FederatedUser{ + federatedUser = []admin.FederatedUser{ { EmailAddress: emailAddress, FederationSettingsId: federationSettingsID, @@ -107,11 +109,11 @@ var ( "user_id": &userID, }, } - associatedOrgs = []admin20231115008.ConnectedOrgConfig{ + associatedOrgs = []admin.ConnectedOrgConfig{ { DomainAllowList: &domainAllowList, DomainRestrictionEnabled: domainRestrictionEnabled, - IdentityProviderId: identityProviderID, + IdentityProviderId: &identityProviderID, OrgId: organizationID, PostAuthRoleGrants: &postAuthRoleGrants, RoleMappings: &federationRoleMapping, @@ -122,14 +124,14 @@ var ( { "domain_allow_list": &domainAllowList, "domain_restriction_enabled": domainRestrictionEnabled, - "identity_provider_id": identityProviderID, + "identity_provider_id": &identityProviderID, "org_id": organizationID, "post_auth_role_grants": &postAuthRoleGrants, "role_mappings": flattenedFederationRoleMapping, "user_conflicts": nil, }, } - pemFileInfo = admin20231115008.PemFileInfo{ + pemFileInfo = admin.PemFileInfo{ FileName: &fileName, Certificates: &pemCertificates, } @@ -144,7 +146,7 @@ var ( func TestFlattenRoleAssignments(t *testing.T) { testCases := []struct { name string - input []admin20231115008.RoleAssignment + input []admin.RoleAssignment output []map[string]any }{ { @@ -154,7 +156,7 @@ func TestFlattenRoleAssignments(t *testing.T) { }, { name: "Empty FlattenRoleAssignments", - input: []admin20231115008.RoleAssignment{}, + input: []admin.RoleAssignment{}, output: nil, }, } @@ -169,7 +171,7 @@ func TestFlattenRoleAssignments(t *testing.T) { func TestFlattenFederatedUser(t *testing.T) { testCases := []struct { name string - input []admin20231115008.FederatedUser + input []admin.FederatedUser output []map[string]any }{ { @@ -179,7 +181,7 @@ func TestFlattenFederatedUser(t *testing.T) { }, { name: "Empty FlattenFederatedUser", - input: []admin20231115008.FederatedUser{}, + input: []admin.FederatedUser{}, output: nil, }, } @@ -194,7 +196,7 @@ func TestFlattenFederatedUser(t *testing.T) { func TestFlattenAuthFederationRoleMapping(t *testing.T) { testCases := []struct { name string - input []admin20231115008.AuthFederationRoleMapping + input []admin.AuthFederationRoleMapping output []map[string]any }{ { @@ -204,7 +206,7 @@ func TestFlattenAuthFederationRoleMapping(t *testing.T) { }, { name: "Empty FlattenAuthFederationRoleMapping", - input: []admin20231115008.AuthFederationRoleMapping{}, + input: []admin.AuthFederationRoleMapping{}, output: nil, }, } @@ -219,7 +221,7 @@ func TestFlattenAuthFederationRoleMapping(t *testing.T) { func TestFlattenFederatedSettingsCertificates(t *testing.T) { testCases := []struct { name string - input []admin20231115008.X509Certificate + input []admin.X509Certificate output []map[string]any }{ { @@ -229,7 +231,7 @@ func TestFlattenFederatedSettingsCertificates(t *testing.T) { }, { name: "Empty FlattenFederatedSettingsCertificates", - input: []admin20231115008.X509Certificate{}, + input: []admin.X509Certificate{}, output: nil, }, } @@ -244,7 +246,7 @@ func TestFlattenFederatedSettingsCertificates(t *testing.T) { func TestFlattenPemFileInfo(t *testing.T) { testCases := []struct { name string - input admin20231115008.PemFileInfo + input admin.PemFileInfo output []map[string]any }{ { @@ -254,7 +256,7 @@ func TestFlattenPemFileInfo(t *testing.T) { }, { name: "Empty FlattenPemFileInfo", - input: admin20231115008.PemFileInfo{}, + input: admin.PemFileInfo{}, output: nil, }, } @@ -269,7 +271,7 @@ func TestFlattenPemFileInfo(t *testing.T) { func TestFlattenAssociatedOrgs(t *testing.T) { testCases := []struct { name string - input []admin20231115008.ConnectedOrgConfig + input []admin.ConnectedOrgConfig output []map[string]any }{ { @@ -279,11 +281,11 @@ func TestFlattenAssociatedOrgs(t *testing.T) { }, { name: "Non empty FlattenAssociatedOrgs with UserConflics", - input: []admin20231115008.ConnectedOrgConfig{ + input: []admin.ConnectedOrgConfig{ { DomainAllowList: &domainAllowList, DomainRestrictionEnabled: domainRestrictionEnabled, - IdentityProviderId: identityProviderID, + IdentityProviderId: &identityProviderID, OrgId: organizationID, PostAuthRoleGrants: &postAuthRoleGrants, RoleMappings: &federationRoleMapping, @@ -294,7 +296,7 @@ func TestFlattenAssociatedOrgs(t *testing.T) { { "domain_allow_list": &domainAllowList, "domain_restriction_enabled": domainRestrictionEnabled, - "identity_provider_id": identityProviderID, + "identity_provider_id": &identityProviderID, "org_id": organizationID, "post_auth_role_grants": &postAuthRoleGrants, "role_mappings": flattenedFederationRoleMapping, @@ -304,7 +306,7 @@ func TestFlattenAssociatedOrgs(t *testing.T) { }, { name: "Empty FlattenAssociatedOrgs", - input: []admin20231115008.ConnectedOrgConfig{}, + input: []admin.ConnectedOrgConfig{}, output: nil, }, } @@ -324,12 +326,12 @@ func TestFlattenFederatedSettingsIdentityProvider(t *testing.T) { var nilBoolPtr *bool testCases := []struct { name string - input []admin20231115008.FederationIdentityProvider + input []admin.FederationIdentityProvider output []map[string]any }{ { name: "Non empty SAML FlattenFederatedSettingsIdentityProvider", - input: []admin20231115008.FederationIdentityProvider{ + input: []admin.FederationIdentityProvider{ { AcsUrl: &acsURL, AssociatedDomains: &associatedDomains, @@ -346,6 +348,7 @@ func TestFlattenFederatedSettingsIdentityProvider(t *testing.T) { Status: &status, Id: identityProviderID, Protocol: &samlProtocol, + Description: &description, }, }, output: []map[string]any{ @@ -365,17 +368,19 @@ func TestFlattenFederatedSettingsIdentityProvider(t *testing.T) { "status": &status, "idp_id": identityProviderID, "protocol": &samlProtocol, - "audience_claim": nilStringSlicePtr, + "audience": nilStringPtr, "client_id": nilStringPtr, "groups_claim": nilStringPtr, "requested_scopes": nilStringSlicePtr, "user_claim": nilStringPtr, + "description": &description, + "authorization_type": nilStringPtr, }, }, }, { name: "Non empty OIDC FlattenFederatedSettingsIdentityProvider", - input: []admin20231115008.FederationIdentityProvider{ + input: []admin.FederationIdentityProvider{ { AssociatedDomains: &associatedDomains, AssociatedOrgs: &associatedOrgs, @@ -383,11 +388,13 @@ func TestFlattenFederatedSettingsIdentityProvider(t *testing.T) { IssuerUri: &issuerURI, Id: identityProviderID, Protocol: &oidcProtocol, - AudienceClaim: &audienceClaim, + Audience: &audience, ClientId: &clientID, GroupsClaim: &groupsClaim, RequestedScopes: &requestedScopes, UserClaim: &userClaim, + Description: &description, + AuthorizationType: &authorizationType, }, }, output: []map[string]any{ @@ -407,17 +414,19 @@ func TestFlattenFederatedSettingsIdentityProvider(t *testing.T) { "status": nilStringPtr, "idp_id": identityProviderID, "protocol": &oidcProtocol, - "audience_claim": &audienceClaim, + "audience": &audience, "client_id": &clientID, "groups_claim": &groupsClaim, "requested_scopes": &requestedScopes, "user_claim": &userClaim, + "description": &description, + "authorization_type": &authorizationType, }, }, }, { name: "Empty FlattenFederatedSettingsIdentityProvider", - input: []admin20231115008.FederationIdentityProvider{}, + input: []admin.FederationIdentityProvider{}, output: nil, }, } diff --git a/internal/service/federatedsettingsidentityprovider/resource_federated_settings_identity_provider.go b/internal/service/federatedsettingsidentityprovider/resource_federated_settings_identity_provider.go index 6eebc7130f..7ecb1a7a36 100644 --- a/internal/service/federatedsettingsidentityprovider/resource_federated_settings_identity_provider.go +++ b/internal/service/federatedsettingsidentityprovider/resource_federated_settings_identity_provider.go @@ -7,8 +7,6 @@ import ( "net/http" "regexp" - admin20231115008 "go.mongodb.org/atlas-sdk/v20231115008/admin" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/spf13/cast" @@ -22,12 +20,12 @@ const OIDC = "OIDC" func Resource() *schema.Resource { return &schema.Resource{ - CreateContext: resourceCreateNotAllowed, - ReadContext: resourceMongoDBAtlasFederatedSettingsIdentityProviderRead, - UpdateContext: resourceMongoDBAtlasFederatedSettingsIdentityProviderUpdate, - DeleteContext: resourceMongoDBAtlasFederatedSettingsIdentityProviderDelete, + CreateContext: resourceCreate, + ReadContext: resourceRead, + UpdateContext: resourceUpdate, + DeleteContext: resourceDelete, Importer: &schema.ResourceImporter{ - StateContext: resourceMongoDBAtlasFederatedSettingsIdentityProviderImportState, + StateContext: resourceImport, }, Schema: map[string]*schema.Schema{ "federation_settings_id": { @@ -82,12 +80,9 @@ func Resource() *schema.Resource { Optional: true, Computed: true, }, - "audience_claim": { - Type: schema.TypeList, + "audience": { + Type: schema.TypeString, Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, }, "client_id": { Type: schema.TypeString, @@ -108,17 +103,36 @@ func Resource() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "authorization_type": { + Type: schema.TypeString, + Optional: true, + }, }, } } -func resourceCreateNotAllowed(_ context.Context, _ *schema.ResourceData, _ any) diag.Diagnostics { - return diag.FromErr(errors.New("this resource must be imported")) +func resourceCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + if d.Get("protocol").(string) != OIDC { + return diag.FromErr(fmt.Errorf("create is only supported by %s, %s must be imported", OIDC, SAML)) + } + connV2 := meta.(*config.MongoDBClient).AtlasV2 + createRequest := ExpandIdentityProviderOIDCCreate(d) + federatedSettingsID := d.Get("federation_settings_id").(string) + + resp, _, err := connV2.FederatedAuthenticationApi.CreateIdentityProvider(ctx, federatedSettingsID, createRequest).Execute() + if err != nil { + return diag.FromErr(fmt.Errorf("error creating federation settings identity provider (%s): %s", federatedSettingsID, err)) + } + d.SetId(encodeStateID(federatedSettingsID, resp.Id)) + return resourceRead(ctx, d, meta) } -func resourceMongoDBAtlasFederatedSettingsIdentityProviderRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - // Get client connection. - connV2 := meta.(*config.MongoDBClient).Atlas20231115008 +func resourceRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + connV2 := meta.(*config.MongoDBClient).AtlasV2 ids := conversion.DecodeStateID(d.Id()) federationSettingsID := ids["federation_settings_id"] @@ -129,8 +143,7 @@ func resourceMongoDBAtlasFederatedSettingsIdentityProviderRead(ctx context.Conte // as few changes as possible, this name will remain. idpID := ids["okta_idp_id"] - // latest version of v2 SDK - federatedSettingsIdentityProvider, resp, err := connV2.FederatedAuthenticationApi.GetIdentityProvider(context.Background(), federationSettingsID, idpID).Execute() + federatedSettingsIdentityProvider, resp, err := connV2.FederatedAuthenticationApi.GetIdentityProvider(ctx, federationSettingsID, idpID).Execute() if err != nil { // case 404 // deleted in the backend case @@ -163,7 +176,7 @@ func resourceMongoDBAtlasFederatedSettingsIdentityProviderRead(ctx context.Conte return diag.FromErr(fmt.Errorf("error setting Status (%s): %s", d.Id(), err)) } } else if federatedSettingsIdentityProvider.GetProtocol() == OIDC { - if err := d.Set("audience_claim", federatedSettingsIdentityProvider.AudienceClaim); err != nil { + if err := d.Set("audience", federatedSettingsIdentityProvider.Audience); err != nil { return diag.FromErr(fmt.Errorf("error setting audience claim list (%s): %s", d.Id(), err)) } @@ -211,26 +224,29 @@ func resourceMongoDBAtlasFederatedSettingsIdentityProviderRead(ctx context.Conte if err := d.Set("protocol", federatedSettingsIdentityProvider.Protocol); err != nil { return diag.FromErr(fmt.Errorf("error setting protocol (%s): %s", d.Id(), err)) } + if err := d.Set("description", federatedSettingsIdentityProvider.Description); err != nil { + return diag.FromErr(fmt.Errorf("error setting description (%s): %s", d.Id(), err)) + } + if err := d.Set("authorization_type", federatedSettingsIdentityProvider.AuthorizationType); err != nil { + return diag.FromErr(fmt.Errorf("error setting authorization_type (%s): %s", d.Id(), err)) + } d.SetId(encodeStateID(federationSettingsID, federatedSettingsIdentityProvider.Id)) return nil } -func resourceMongoDBAtlasFederatedSettingsIdentityProviderUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - // Get client connection. - connV2 := meta.(*config.MongoDBClient).Atlas20231115008 - ids := conversion.DecodeStateID(d.Id()) - federationSettingsID := ids["federation_settings_id"] - oktaIdpID := ids["okta_idp_id"] - - updateRequest := new(admin20231115008.FederationIdentityProviderUpdate) - _, _, err := connV2.FederatedAuthenticationApi.GetIdentityProvider(context.Background(), federationSettingsID, oktaIdpID).Execute() +func resourceUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + connV2 := meta.(*config.MongoDBClient).AtlasV2 + federationSettingsID, idpID := DecodeIDs(d.Id()) + existingIdentityProvider, _, err := connV2.FederatedAuthenticationApi.GetIdentityProvider(context.Background(), federationSettingsID, idpID).Execute() if err != nil { return diag.FromErr(fmt.Errorf("error retreiving federation settings identity provider (%s): %s", federationSettingsID, err)) } + updateRequest := ExpandIdentityProviderUpdate(d, existingIdentityProvider) + if d.HasChange("protocol") { protocol := d.Get("protocol").(string) updateRequest.Protocol = &protocol @@ -280,19 +296,22 @@ func resourceMongoDBAtlasFederatedSettingsIdentityProviderUpdate(ctx context.Con updateRequest.SsoUrl = &status } - if d.HasChange("audience_claim") { - audienceClaim := d.Get("audience_claim") - audienceClaimSlice := cast.ToStringSlice(audienceClaim) - if audienceClaimSlice == nil { - audienceClaimSlice = []string{} - } - updateRequest.AudienceClaim = &audienceClaimSlice + if d.HasChange("audience") { + audience := d.Get("audience").(string) + updateRequest.Audience = &audience } if d.HasChange("client_id") { clientID := d.Get("client_id").(string) updateRequest.ClientId = &clientID } + if d.HasChange("description") { + updateRequest.Description = conversion.StringPtr(d.Get("description").(string)) + } + + if d.HasChange("authorization_type") { + updateRequest.AuthorizationType = conversion.StringPtr(d.Get("authorization_type").(string)) + } if d.HasChange("groups_claim") { groupsClaim := d.Get("groups_claim").(string) @@ -312,23 +331,29 @@ func resourceMongoDBAtlasFederatedSettingsIdentityProviderUpdate(ctx context.Con userClaim := d.Get("user_claim").(string) updateRequest.UserClaim = &userClaim } - - updateRequest.PemFileInfo = nil - - _, _, err = connV2.FederatedAuthenticationApi.UpdateIdentityProvider(ctx, federationSettingsID, oktaIdpID, updateRequest).Execute() + _, _, err = connV2.FederatedAuthenticationApi.UpdateIdentityProvider(ctx, federationSettingsID, idpID, updateRequest).Execute() if err != nil { return diag.FromErr(fmt.Errorf("error updating federation settings identity provider (%s): %s", federationSettingsID, err)) } - return resourceMongoDBAtlasFederatedSettingsIdentityProviderRead(ctx, d, meta) + return resourceRead(ctx, d, meta) } -func resourceMongoDBAtlasFederatedSettingsIdentityProviderDelete(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - d.SetId("") +func resourceDelete(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + connV2 := meta.(*config.MongoDBClient).AtlasV2 + federationSettingsID, idpID := DecodeIDs(d.Id()) + resp, err := connV2.FederatedAuthenticationApi.DeleteIdentityProvider(ctx, federationSettingsID, idpID).Execute() + if err != nil { + if resp.StatusCode == 404 { + d.SetId("") + return nil + } + return diag.FromErr(fmt.Errorf("error deleting federation settings identity provider (%s): %s, error: %s", federationSettingsID, idpID, err)) + } return nil } -func resourceMongoDBAtlasFederatedSettingsIdentityProviderImportState(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) { +func resourceImport(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) { federationSettingsID, idpID, err := splitFederatedSettingsIdentityProviderImportID(d.Id()) if err != nil { return nil, err @@ -364,3 +389,9 @@ func encodeStateID(federationSettingsID, idpID string) string { "okta_idp_id": idpID, }) } +func DecodeIDs(rawID string) (federationSettingsID, idpID string) { + ids := conversion.DecodeStateID(rawID) + federationSettingsID = ids["federation_settings_id"] + idpID = ids["okta_idp_id"] + return federationSettingsID, idpID +} diff --git a/internal/service/federatedsettingsidentityprovider/resource_federated_settings_identity_provider_test.go b/internal/service/federatedsettingsidentityprovider/resource_federated_settings_identity_provider_test.go index fe6b3641be..fa7fdd19ac 100644 --- a/internal/service/federatedsettingsidentityprovider/resource_federated_settings_identity_provider_test.go +++ b/internal/service/federatedsettingsidentityprovider/resource_federated_settings_identity_provider_test.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" - "github.com/mongodb/terraform-provider-mongodbatlas/internal/common/conversion" + "github.com/mongodb/terraform-provider-mongodbatlas/internal/service/federatedsettingsidentityprovider" "github.com/mongodb/terraform-provider-mongodbatlas/internal/testutil/acc" ) @@ -18,18 +18,24 @@ func TestAccFederatedSettingsIdentityProvider_createError(t *testing.T) { ProtoV6ProviderFactories: acc.TestAccProviderV6Factories, Steps: []resource.TestStep{ { - Config: configBasic("not-used", "not-used", "not-used", "not-used"), - ExpectError: regexp.MustCompile("this resource must be imported"), + Config: configSAMLBasic("not-used", "not-used", "not-used", "not-used"), + ExpectError: regexp.MustCompile("create is only supported by OIDC, SAML must be imported"), }, }, }) } func TestAccFederatedSettingsIdentityProviderRS_basic(t *testing.T) { - resource.ParallelTest(t, *basicTestCase(t)) + // SAML IdP can be deleted but not created through the API. If this test is run the resource will be deleted and will have to be created through the UI + acc.SkipTestForCI(t) + resource.ParallelTest(t, *basicSAMLTestCase(t)) } -func basicTestCase(tb testing.TB) *resource.TestCase { +func TestAccFederatedSettingsIdentityProviderRS_OIDCWorkforce(t *testing.T) { + resource.ParallelTest(t, *basicOIDCWorkforceTestCase(t)) +} + +func basicSAMLTestCase(tb testing.TB) *resource.TestCase { tb.Helper() var ( @@ -39,7 +45,7 @@ func basicTestCase(tb testing.TB) *resource.TestCase { ssoURL = os.Getenv("MONGODB_ATLAS_FEDERATED_SSO_URL") issuerURI = os.Getenv("MONGODB_ATLAS_FEDERATED_ISSUER_URI") associatedDomain = os.Getenv("MONGODB_ATLAS_FEDERATED_SETTINGS_ASSOCIATED_DOMAIN") - config = configBasic(federationSettingsID, ssoURL, issuerURI, associatedDomain) + config = configSAMLBasic(federationSettingsID, ssoURL, issuerURI, associatedDomain) ) return &resource.TestCase{ @@ -73,6 +79,65 @@ func basicTestCase(tb testing.TB) *resource.TestCase { } } +func basicOIDCWorkforceTestCase(tb testing.TB) *resource.TestCase { + tb.Helper() + + var ( + resourceName = "mongodbatlas_federated_settings_identity_provider.test" + dataSourceName = "data.mongodbatlas_federated_settings_identity_provider.test" + federationSettingsID = os.Getenv("MONGODB_ATLAS_FEDERATION_SETTINGS_ID") + associatedDomain = os.Getenv("MONGODB_ATLAS_FEDERATED_SETTINGS_ASSOCIATED_DOMAIN") + audience1 = "audience" + audience2 = "audience-updated" + description1 = "tf-acc-test" + description2 = "tf-acc-test-updated" + attrMapCheck = map[string]string{ + "associated_domains.0": associatedDomain, + "audience": audience1, + "authorization_type": "GROUP", + "client_id": "clientId", + "description": description1, + "federation_settings_id": federationSettingsID, + "groups_claim": "groups", + "issuer_uri": "https://token.actions.githubusercontent.com", + "protocol": "OIDC", + "requested_scopes.0": "profiles", + "user_claim": "sub", + } + ) + checks := []resource.TestCheckFunc{checkExistsManaged(resourceName)} + checks = acc.AddAttrChecks(resourceName, checks, attrMapCheck) + checks = acc.AddAttrChecks(dataSourceName, checks, attrMapCheck) + + return &resource.TestCase{ + PreCheck: func() { acc.PreCheckFederatedSettingsIdentityProvider(tb) }, + ProtoV6ProviderFactories: acc.TestAccProviderV6Factories, + Steps: []resource.TestStep{ + { + Config: configOIDCWorkforceBasic(federationSettingsID, associatedDomain, description1, audience1), + Check: resource.ComposeTestCheckFunc(checks...), + }, + { + Config: configOIDCWorkforceBasic(federationSettingsID, associatedDomain, description2, audience2), + Check: resource.ComposeTestCheckFunc( + checkExistsManaged(resourceName), + resource.TestCheckResourceAttr(resourceName, "description", description2), + resource.TestCheckResourceAttr(resourceName, "audience", audience2), + resource.TestCheckResourceAttr(resourceName, "name", "OIDC-CRUD-test"), + resource.TestCheckResourceAttr(dataSourceName, "display_name", "OIDC-CRUD-test"), + ), + }, + { + Config: configOIDCWorkforceBasic(federationSettingsID, associatedDomain, description2, audience2), + ResourceName: resourceName, + ImportStateIdFunc: importStateIDFuncManaged(resourceName), + ImportState: true, + ImportStateVerify: true, + }, + }, + } +} + func checkExists(resourceName, idpID string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] @@ -92,19 +157,52 @@ func checkExists(resourceName, idpID string) resource.TestCheckFunc { } } +func checkExistsManaged(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + federationSettingsID, idpID, err := readIDsFromState(s, resourceName) + if err != nil { + return err + } + _, _, err = acc.ConnV2().FederatedAuthenticationApi.GetIdentityProvider(context.Background(), + federationSettingsID, + idpID).Execute() + if err == nil { + return nil + } + return fmt.Errorf("identity provider (%s) does not exist", idpID) + } +} + +func readIDsFromState(s *terraform.State, resourceName string) (federationSettingsID, idpID string, err error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", "", fmt.Errorf("no ID is set") + } + id := rs.Primary.ID + if id == "" { + return "", "", fmt.Errorf("ID is empty") + } + federationSettingsID, idpID = federatedsettingsidentityprovider.DecodeIDs(id) + return federationSettingsID, idpID, nil +} + func importStateIDFunc(federationSettingsID, idpID string) resource.ImportStateIdFunc { return func(s *terraform.State) (string, error) { - ID := conversion.EncodeStateID(map[string]string{ - "federation_settings_id": federationSettingsID, - "okta_idp_id": idpID, - }) + return fmt.Sprintf("%s-%s", federationSettingsID, idpID), nil + } +} - ids := conversion.DecodeStateID(ID) - return fmt.Sprintf("%s-%s", ids["federation_settings_id"], ids["okta_idp_id"]), nil +func importStateIDFuncManaged(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + federationSettingsID, idpID, err := readIDsFromState(s, resourceName) + if err != nil { + return "", err + } + return fmt.Sprintf("%s-%s", federationSettingsID, idpID), nil } } -func configBasic(federationSettingsID, ssoURL, issuerURI, associatedDomain string) string { +func configSAMLBasic(federationSettingsID, ssoURL, issuerURI, associatedDomain string) string { return fmt.Sprintf(` resource "mongodbatlas_federated_settings_identity_provider" "test" { federation_settings_id = %[1]q @@ -118,3 +216,26 @@ func configBasic(federationSettingsID, ssoURL, issuerURI, associatedDomain strin response_signature_algorithm = "SHA-256" }`, federationSettingsID, ssoURL, issuerURI, associatedDomain) } + +func configOIDCWorkforceBasic(federationSettingsID, associatedDomain, description, audience string) string { + return fmt.Sprintf(` + resource "mongodbatlas_federated_settings_identity_provider" "test" { + federation_settings_id = %[1]q + associated_domains = [%[3]q] + audience = %[2]q + authorization_type = "GROUP" + client_id = "clientId" + description = %[4]q + groups_claim = "groups" + issuer_uri = "https://token.actions.githubusercontent.com" + name = "OIDC-CRUD-test" + protocol = "OIDC" + requested_scopes = ["profiles"] + user_claim = "sub" + } + + data "mongodbatlas_federated_settings_identity_provider" "test" { + federation_settings_id = mongodbatlas_federated_settings_identity_provider.test.federation_settings_id + identity_provider_id = mongodbatlas_federated_settings_identity_provider.test.idp_id + }`, federationSettingsID, audience, associatedDomain, description) +} diff --git a/internal/testutil/acc/pre_check.go b/internal/testutil/acc/pre_check.go index 8eb1cd07a0..97f91a1d7b 100644 --- a/internal/testutil/acc/pre_check.go +++ b/internal/testutil/acc/pre_check.go @@ -269,7 +269,7 @@ func PreCheckFederatedSettingsIdentityProvider(tb testing.TB) { if os.Getenv("MONGODB_ATLAS_FEDERATED_ORG_ID") == "" || os.Getenv("MONGODB_ATLAS_FEDERATED_SETTINGS_ASSOCIATED_DOMAIN") == "" || os.Getenv("MONGODB_ATLAS_FEDERATION_SETTINGS_ID") == "" { - tb.Fatal("`MONGODB_ATLAS_FEDERATED_ORG_ID` and `MONGODB_ATLAS_FEDERATION_SETTINGS_ID` must be set for federated settings/verify acceptance testing") + tb.Fatal("`MONGODB_ATLAS_FEDERATED_SETTINGS_ASSOCIATED_DOMAIN`, MONGODB_ATLAS_FEDERATED_ORG_ID`, and `MONGODB_ATLAS_FEDERATION_SETTINGS_ID` must be set for federated settings acceptance testing") } } diff --git a/website/docs/guides/1.17.0-upgrade-guide.html.markdown b/website/docs/guides/1.17.0-upgrade-guide.html.markdown new file mode 100644 index 0000000000..4cba8b405d --- /dev/null +++ b/website/docs/guides/1.17.0-upgrade-guide.html.markdown @@ -0,0 +1,32 @@ +--- +layout: "mongodbatlas" +page_title: "MongoDB Atlas Provider 1.17.0: Upgrade and Information Guide" +sidebar_current: "docs-mongodbatlas-guides-1170-upgrade-guide" +description: |- +MongoDB Atlas Provider 1.17.0: Upgrade and Information Guide +--- + +# MongoDB Atlas Provider 1.17.0: Upgrade and Information Guide + +The Terraform MongoDB Atlas Provider version 1.17.0 has a number of new and exciting features. + +**New Resources, Data Sources, and Features:** + +- You can now Create and Delete [MongoDB Atlas OIDC Workforce identity providers](https://www.mongodb.com/docs/atlas/security-oidc/#configure-oidc-authorization) with `mongodbatlas_federated_settings_identity_provider` [resource](https://registry.terraform.io/providers/mongodb/mongodbatlas/latest/docs/resources/federated_settings_identity_provider). + +**Breaking Changes:** + +- Attribute `audienceClaim` used in OIDC IdPs has been replaced with `audience` attribute in `mongodbatlas_federated_settings_identity_provider` [resource](https://registry.terraform.io/providers/mongodb/mongodbatlas/latest/docs/resources/federated_settings_identity_provider) and [`mongodbatlas_federated_settings_identity_provider`](https://registry.terraform.io/providers/mongodb/mongodbatlas/latest/docs/data-sources/federated_settings_identity_provider) and [`mongodbatlas_federated_settings_identity_providers`](https://registry.terraform.io/providers/mongodb/mongodbatlas/latest/docs/data-sources/federated_settings_identity_providers) data sources. This change reflects the most recent changes in the Atlas API [Federated Authentication](https://www.mongodb.com/docs/atlas/reference/api-resources-spec/v2/#tag/Federated-Authentication/operation/updateIdentityProvider). + - If you have imported a OIDC Workforce IdP in previous versions of this provider, you should: + 1. Re-import all [`mongodbatlas_federated_settings_identity_provider`](https://registry.terraform.io/providers/mongodb/mongodbatlas/latest/docs/resources/federated_settings_identity_provider) resources: + `terraform import mongodbatlas_federated_settings_identity_provider.identity_provider -` + 2. Run `terraform plan` + 3. Run `terraform apply` + +### Helpful Links + +* [Report bugs](https://github.com/mongodb/terraform-provider-mongodbatlas/issues) + +* [Request Features](https://feedback.mongodb.com/forums/924145-atlas?category_id=370723) + +* [Contact Support](https://docs.atlas.mongodb.com/support/) covered by MongoDB Atlas support plans, Developer and above.