diff --git a/anypoint/data_source_idp.go b/anypoint/data_source_idp.go new file mode 100644 index 0000000..0cc43df --- /dev/null +++ b/anypoint/data_source_idp.go @@ -0,0 +1,371 @@ +package anypoint + +import ( + "context" + "fmt" + "io/ioutil" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/mulesoft-consulting/anypoint-client-go/idp" +) + +func dataSourceIDP() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceIDPRead, + Description: ` + Reads a specific ` + "`" + `identity provider` + "`" + ` in your business group. + `, + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Required: true, + Description: "The provider id", + }, + "org_id": { + Type: schema.TypeString, + Required: true, + Description: "The business group id", + }, + "provider_id": { + Type: schema.TypeString, + Computed: true, + Description: "The provider id", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "The name of the provider", + }, + "type": { + Type: schema.TypeMap, + Computed: true, + Description: "The type of the provider, contains description and the name of the type of the provider (saml or oidc)", + }, + "oidc_provider": { + Type: schema.TypeSet, + Description: "The description of provider specific for OIDC types", + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "token_url": { + Type: schema.TypeString, + Computed: true, + Description: "The token url of the openid-connect provider", + }, + "redirect_url": { + Type: schema.TypeString, + Computed: true, + Description: "The redirect url of the openid-connect provider", + }, + "userinfo_url": { + Type: schema.TypeString, + Computed: true, + Description: "The userinfo url of the openid-connect provider", + }, + "authorize_url": { + Type: schema.TypeString, + Computed: true, + Description: "The authorization url of the openid-connect provider", + }, + "client_registration_url": { + Type: schema.TypeString, + Computed: true, + Description: "The registration url, for dynamic client registration, of the openid-connect provider", + }, + "client_credentials_id": { + Type: schema.TypeString, + Computed: true, + Description: "The client's credentials id", + }, + "client_token_endpoint_auth_methods_supported": { + Type: schema.TypeList, + Computed: true, + Description: "The list of authentication methods supported", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "issuer": { + Type: schema.TypeString, + Computed: true, + Description: "The provider token issuer url", + }, + "group_scope": { + Type: schema.TypeString, + Computed: true, + Description: "The provider group scopes", + }, + "allow_untrusted_certificates": { + Type: schema.TypeBool, + Computed: true, + Description: "The certification validation trigger", + }, + }, + }, + }, + "saml": { + Type: schema.TypeSet, + Description: "The description of provider specific for SAML types", + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "issuer": { + Type: schema.TypeString, + Computed: true, + Description: "The provider issuer", + }, + "audience": { + Type: schema.TypeString, + Computed: true, + Description: "The provider audience", + }, + "public_key": { + Type: schema.TypeList, + Computed: true, + Description: "The list of public keys", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "claims_mapping_email_attribute": { + Type: schema.TypeString, + Computed: true, + Description: "Field name in the SAML AttributeStatements that maps to Email. By default, the email attribute in the SAML assertion is used.", + }, + "claims_mapping_group_attribute": { + Type: schema.TypeString, + Computed: true, + Description: "Field name in the SAML AttributeStatements that maps to Group.", + }, + "claims_mapping_lastname_attribute": { + Type: schema.TypeString, + Computed: true, + Description: "Field name in the SAML AttributeStatements that maps to Last Name. By default, the lastname attribute in the SAML assertion is used.", + }, + "claims_mapping_username_attribute": { + Type: schema.TypeString, + Computed: true, + Description: "Field name in the SAML AttributeStatements that maps to username. By default, the NameID attribute in the SAML assertion is used.", + }, + "claims_mapping_firstname_attribute": { + Type: schema.TypeString, + Computed: true, + Description: "Field name in the SAML AttributeStatements that maps to First Name. By default, the firstname attribute in the SAML assertion is used.", + }, + "sp_initiated_sso_enabled": { + Type: schema.TypeBool, + Computed: true, + Description: "True if the Service Provider initiated SSO enabled", + }, + "idp_initiated_sso_enabled": { + Type: schema.TypeBool, + Computed: true, + Description: "True if the Identity Provider initiated SSO enabled", + }, + "require_encrypted_saml_assertions": { + Type: schema.TypeBool, + Computed: true, + Description: "True if the encryption of saml assertions requirement is enabled", + }, + }, + }, + }, + "sp_sign_on_url": { + Type: schema.TypeString, + Computed: true, + Description: "The provider's sign on url", + }, + "sp_sign_out_url": { + Type: schema.TypeString, + Computed: true, + Description: "The provider's sign out url, only available for SAML", + }, + }, + } +} + +func dataSourceIDPRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + pco := m.(ProviderConfOutput) + idpid := d.Get("id").(string) + orgid := d.Get("org_id").(string) + authctx := getIDPAuthCtx(ctx, &pco) + + //request idp + res, httpr, err := pco.idpclient.DefaultApi.OrganizationsOrgIdIdentityProvidersIdpIdGet(authctx, orgid, idpid).Execute() + defer httpr.Body.Close() + if err != nil { + var details string + if httpr != nil { + b, _ := ioutil.ReadAll(httpr.Body) + details = string(b) + } else { + details = err.Error() + } + diags := append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Unable to Get IDP " + idpid + " in org " + orgid, + Detail: details, + }) + return diags + } + //process data + idpinstance := flattenIDPData(&res) + //save in data source schema + if err := setIDPAttributesToResourceData(d, idpinstance); err != nil { + diags := append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Unable to set IDP " + idpid + " in org " + orgid, + Detail: err.Error(), + }) + return diags + } + + d.SetId(idpid) + + return diags +} + +/* +* Transforms a idp.Idp object to the dataSourceIDP schema + */ +func flattenIDPData(idpitem *idp.Idp) map[string]interface{} { + if idpitem != nil { + item := make(map[string]interface{}) + + item["provider_id"] = idpitem.GetProviderId() + item["name"] = idpitem.GetName() + t := idpitem.GetType() + t_tmp := make(map[string]string) + t_tmp["description"] = t.GetDescription() + t_tmp["name"] = t.GetName() + item["type"] = t_tmp + + if _, ok := idpitem.GetOidcProviderOk(); ok { + item["oidc_provider"] = flattenOIDCData(idpitem) + } else if _, ok := idpitem.GetSamlOk(); ok { + item["saml"] = flattenSAMLData(idpitem) + } + + if sp, ok := idpitem.GetServiceProviderOk(); ok { + if urls, ok := sp.GetUrlsOk(); ok { + if signon, ok := urls.GetSignOnOk(); ok { + item["sp_sign_on_url"] = *signon + } else { + item["sp_sign_on_url"] = "" + } + if signout, ok := urls.GetSignOutOk(); ok { + item["sp_sign_out_url"] = *signout + } else { + item["sp_sign_out_url"] = "" + } + } else { + item["sp_sign_on_url"] = "" + item["sp_sign_out_url"] = "" + } + } else { + item["sp_sign_on_url"] = "" + item["sp_sign_out_url"] = "" + } + + return item + } + + return nil +} + +func flattenOIDCData(idpitem *idp.Idp) []interface{} { + array := make([]interface{}, 0) + if idpitem != nil { + item := make(map[string]interface{}) + oidcdata := idpitem.GetOidcProvider() + if urls, ok := oidcdata.GetUrlsOk(); ok { + item["token_url"] = urls.GetToken() + item["redirect_url"] = urls.GetRedirect() + item["userinfo_url"] = urls.GetUserinfo() + item["authorize_url"] = urls.GetAuthorize() + } + if client, ok := oidcdata.GetClientOk(); ok { + if urls, ok := client.GetUrlsOk(); ok { + item["client_registration_url"] = urls.GetRegister() + } + if creds, ok := client.GetCredentialsOk(); ok { + item["client_credentials_id"] = creds.GetId() + } + if meth, ok := client.GetTokenEndpointAuthMethodsSupportedOk(); ok { + item["client_token_endpoint_auth_methods_supported"] = *meth + } + } + item["issuer"] = oidcdata.GetIssuer() + item["group_scope"] = oidcdata.GetGroupScope() + + if atc, ok := idpitem.GetAllowUntrustedCertificatesOk(); ok { + item["allow_untrusted_certificates"] = *atc + } + + array = append(array, item) + } + + return array +} + +func flattenSAMLData(idpitem *idp.Idp) []interface{} { + array := make([]interface{}, 0) + if idpitem != nil { + item := make(map[string]interface{}) + samldata := idpitem.GetSaml() + + item["issuer"] = samldata.GetIssuer() + item["audience"] = samldata.GetAudience() + item["public_key"] = samldata.GetPublicKey() + if claims, ok := samldata.GetClaimsMappingOk(); ok { + if email, ok := claims.GetEmailAttributeOk(); ok { + item["claims_mapping_email_attribute"] = *email + } + if group, ok := claims.GetGroupAttributeOk(); ok { + item["claims_mapping_group_attribute"] = *group + } + if lastname, ok := claims.GetLastnameAttributeOk(); ok { + item["claims_mapping_lastname_attribute"] = *lastname + } + if username, ok := claims.GetUsernameAttributeOk(); ok { + item["claims_mapping_username_attribute"] = *username + } + if firstname, ok := claims.GetFirstnameAttributeOk(); ok { + item["claims_mapping_firstname_attribute"] = *firstname + } + } + item["sp_initiated_sso_enabled"] = samldata.GetSpInitiatedSsoEnabled() + item["idp_initiated_sso_enabled"] = samldata.GetIdpInitiatedSsoEnabled() + item["require_encrypted_saml_assertions"] = samldata.GetRequireEncryptedSamlAssertions() + + array = append(array, item) + } + + return array +} + +/* +* Copies the given idp instance into the given resource data + */ +func setIDPAttributesToResourceData(d *schema.ResourceData, idpitem map[string]interface{}) error { + attributes := getIDPAttributes() + if idpitem != nil { + for _, attr := range attributes { + if err := d.Set(attr, idpitem[attr]); err != nil { + if attr != "saml" && attr != "oidc_provider" { + return fmt.Errorf("unable to set IDP attribute %s\n details: %s", attr, err) + } + } + } + } + return nil +} + +func getIDPAttributes() []string { + attributes := [...]string{ + "provider_id", "name", "type", "oidc_provider", "saml", "sp_sign_on_url", "sp_sign_out_url", + } + return attributes[:] +} diff --git a/anypoint/data_source_idps.go b/anypoint/data_source_idps.go new file mode 100644 index 0000000..b3ccd0a --- /dev/null +++ b/anypoint/data_source_idps.go @@ -0,0 +1,133 @@ +package anypoint + +import ( + "context" + "io/ioutil" + "strconv" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + idp "github.com/mulesoft-consulting/anypoint-client-go/idp" +) + +func dataSourceIDPs() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceIDPsRead, + Description: ` + Reads all ` + "`" + `identity providers` + "`" + ` in your business group. + `, + Schema: map[string]*schema.Schema{ + "org_id": { + Type: schema.TypeString, + Required: true, + Description: "The business group id", + }, + "idps": { + Type: schema.TypeList, + Description: "List of providers for the given org", + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "provider_id": { + Type: schema.TypeString, + Computed: true, + Description: "The provider id", + }, + "org_id": { + Type: schema.TypeString, + Computed: true, + Description: "The business group id", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "The name fo the provider", + }, + "type": { + Type: schema.TypeMap, + Computed: true, + Description: "The type of the provider. Contains the name (saml or oidc) and the description", + }, + }, + }, + }, + "total": { + Type: schema.TypeInt, + Description: "The total number of available results", + Computed: true, + }, + }, + } +} + +func dataSourceIDPsRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + // Warning or errors can be collected in a slice type + var diags diag.Diagnostics + pco := m.(ProviderConfOutput) + orgid := d.Get("org_id").(string) + authctx := getIDPAuthCtx(ctx, &pco) + + //request env + res, httpr, err := pco.idpclient.DefaultApi.OrganizationsOrgIdIdentityProvidersGet(authctx, orgid).Execute() + defer httpr.Body.Close() + if err != nil { + var details string + if httpr != nil { + b, _ := ioutil.ReadAll(httpr.Body) + details = string(b) + } else { + details = err.Error() + } + diags := append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Unable to Get IDPs for org " + orgid, + Detail: details, + }) + return diags + } + //process data + idps := flattenIDPsData(res.GetData()) + //save in data source schema + if err := d.Set("idps", idps); err != nil { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Unable to set IDPs for org " + orgid, + Detail: err.Error(), + }) + return diags + } + + if err := d.Set("total", res.GetTotal()); err != nil { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Unable to set total number IDPs for org " + orgid, + Detail: err.Error(), + }) + return diags + } + + d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) + + return diags +} + +/* +* Transforms a list of idp summaries object to the dataSourceIDPs schema + */ +func flattenIDPsData(idps []idp.IdpSummary) []interface{} { + result := make([]interface{}, len(idps)) + for i, idpItem := range idps { + item := make(map[string]interface{}) + item["provider_id"] = idpItem.GetProviderId() + item["org_id"] = idpItem.GetOrgId() + item["name"] = idpItem.GetName() + t := idpItem.GetType() + tmp := make(map[string]string) + tmp["description"] = t.GetDescription() + tmp["name"] = t.GetName() + item["type"] = tmp + result[i] = item + } + return result +} diff --git a/anypoint/provider.go b/anypoint/provider.go index ef9909b..bb2a245 100644 --- a/anypoint/provider.go +++ b/anypoint/provider.go @@ -11,6 +11,7 @@ import ( auth "github.com/mulesoft-consulting/anypoint-client-go/authorization" dlb "github.com/mulesoft-consulting/anypoint-client-go/dlb" env "github.com/mulesoft-consulting/anypoint-client-go/env" + idp "github.com/mulesoft-consulting/anypoint-client-go/idp" org "github.com/mulesoft-consulting/anypoint-client-go/org" role "github.com/mulesoft-consulting/anypoint-client-go/role" rolegroup "github.com/mulesoft-consulting/anypoint-client-go/rolegroup" @@ -82,6 +83,8 @@ func Provider() *schema.Provider { "anypoint_team_member": resourceTeamMember(), "anypoint_team_group_mappings": resourceTeamGroupMappings(), "anypoint_dlb": resourceDLB(), + "anypoint_idp_oidc": resourceOIDC(), + "anypoint_idp_saml": resourceSAML(), }, DataSourcesMap: map[string]*schema.Resource{ "anypoint_vpcs": dataSourceVPCs(), @@ -102,6 +105,8 @@ func Provider() *schema.Provider { "anypoint_team_group_mappings": dataSourceTeamGroupMappings(), "anypoint_dlb": dataSourceDLB(), "anypoint_dlbs": dataSourceDLBs(), + "anypoint_idp": dataSourceIDP(), + "anypoint_idps": dataSourceIDPs(), }, ConfigureContextFunc: providerConfigure, TerraformVersion: "v1.0.1", @@ -231,6 +236,7 @@ type ProviderConfOutput struct { teamrolesclient *team_roles.APIClient teamgroupmappingsclient *team_group_mappings.APIClient dlbclient *dlb.APIClient + idpclient *idp.APIClient } func newProviderConfOutput(access_token string, server_index int) ProviderConfOutput { @@ -248,6 +254,7 @@ func newProviderConfOutput(access_token string, server_index int) ProviderConfOu teamrolescfg := team_roles.NewConfiguration() teamgroupmappingscfg := team_group_mappings.NewConfiguration() dlbcfg := dlb.NewConfiguration() + idpcfg := idp.NewConfiguration() vpcclient := vpc.NewAPIClient(vpccfg) orgclient := org.NewAPIClient(orgcfg) @@ -261,6 +268,7 @@ func newProviderConfOutput(access_token string, server_index int) ProviderConfOu teamrolesclient := team_roles.NewAPIClient(teamrolescfg) teamgroupmappingsclient := team_group_mappings.NewAPIClient(teamgroupmappingscfg) dlbclient := dlb.NewAPIClient(dlbcfg) + idpclient := idp.NewAPIClient(idpcfg) return ProviderConfOutput{ access_token: access_token, @@ -277,5 +285,6 @@ func newProviderConfOutput(access_token string, server_index int) ProviderConfOu teamrolesclient: teamrolesclient, teamgroupmappingsclient: teamgroupmappingsclient, dlbclient: dlbclient, + idpclient: idpclient, } } diff --git a/anypoint/resource_idp_oidc.go b/anypoint/resource_idp_oidc.go new file mode 100644 index 0000000..d6b1ced --- /dev/null +++ b/anypoint/resource_idp_oidc.go @@ -0,0 +1,418 @@ +package anypoint + +import ( + "context" + "io/ioutil" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + idp "github.com/mulesoft-consulting/anypoint-client-go/idp" +) + +func resourceOIDC() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceOIDCCreate, + ReadContext: resourceOIDCRead, + UpdateContext: resourceOIDCUpdate, + DeleteContext: resourceOIDCDelete, + Description: ` + Creates an ` + "`" + `identity provider` + "`" + ` OIDC type configuration in your account. + `, + Schema: map[string]*schema.Schema{ + "last_updated": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "org_id": { + Type: schema.TypeString, + Required: true, + Description: "The business group id", + }, + "provider_id": { + Type: schema.TypeString, + Computed: true, + Description: "The provider id", + }, + "name": { + Type: schema.TypeString, + Required: true, + Description: "The name of the provider", + }, + "type": { + Type: schema.TypeMap, + Computed: true, + Description: "The type of the provider, contains description and the name of the type of the provider (saml or oidc)", + }, + "oidc_provider": { + Type: schema.TypeSet, + Description: "The description of provider specific for OIDC types", + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "token_url": { + Type: schema.TypeString, + Required: true, + Description: "The token url of the openid-connect provider", + }, + "redirect_url": { + Type: schema.TypeString, + Computed: true, + Description: "The redirect url of the openid-connect provider", + }, + "userinfo_url": { + Type: schema.TypeString, + Required: true, + Description: "The userinfo url of the openid-connect provider", + }, + "authorize_url": { + Type: schema.TypeString, + Required: true, + Description: "The authorization url of the openid-connect provider", + }, + "client_registration_url": { + Type: schema.TypeString, + Optional: true, + Description: "The registration url, for dynamic client registration, of the openid-connect provider. Mutually exclusive with credentials id/secret, if both are given registration url is prioritized.", + }, + "client_credentials_id": { + Type: schema.TypeString, + Optional: true, + Description: "The client's credentials id. This should only be provided if manual registration is wanted. Mutually exclusive with registration url, if both are given registration url is prioritized.", + }, + "client_credentials_secret": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + Description: "The client's credentials secret. This should only be provided if manual registration is wanted. Mutually exclusive with registration url, if both are given registration url is prioritized.", + }, + "client_token_endpoint_auth_methods_supported": { + Type: schema.TypeList, + Computed: true, + Description: "The list of authentication methods supported", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "issuer": { + Type: schema.TypeString, + Required: true, + Description: "The provider token issuer url", + }, + "group_scope": { + Type: schema.TypeString, + Optional: true, + Description: "The provider group scopes", + }, + "allow_untrusted_certificates": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "The certification validation trigger", + }, + }, + }, + }, + "sp_sign_on_url": { + Type: schema.TypeString, + Computed: true, + Description: "The provider's sign on url", + }, + "sp_sign_out_url": { + Type: schema.TypeString, + Computed: true, + Description: "The provider's sign out url, only available for SAML", + }, + }, + } +} + +func resourceOIDCCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + pco := m.(ProviderConfOutput) + orgid := d.Get("org_id").(string) + + authctx := getIDPAuthCtx(ctx, &pco) + body, errDiags := newOIDCPostBody(d) + if errDiags.HasError() { + diags = append(diags, errDiags...) + return diags + } + + res, httpr, err := pco.idpclient.DefaultApi.OrganizationsOrgIdIdentityProvidersPost(authctx, orgid).IdpPostBody(*body).Execute() + defer httpr.Body.Close() + if err != nil { + var details string + if httpr != nil { + b, _ := ioutil.ReadAll(httpr.Body) + details = string(b) + } else { + details = err.Error() + } + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Unable to create OIDC provider for org " + orgid, + Detail: details, + }) + return diags + } + + d.SetId(res.GetProviderId()) + + return resourceOIDCRead(ctx, d, m) +} + +func resourceOIDCRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + pco := m.(ProviderConfOutput) + idpid := d.Id() + orgid := d.Get("org_id").(string) + authctx := getIDPAuthCtx(ctx, &pco) + + //request idp + res, httpr, err := pco.idpclient.DefaultApi.OrganizationsOrgIdIdentityProvidersIdpIdGet(authctx, orgid, idpid).Execute() + defer httpr.Body.Close() + if err != nil { + var details string + if httpr != nil { + b, _ := ioutil.ReadAll(httpr.Body) + details = string(b) + } else { + details = err.Error() + } + diags := append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Unable to Get IDP " + idpid + " in org " + orgid, + Detail: details, + }) + return diags + } + //process data + idpinstance := flattenIDPData(&res) + //save in data source schema + if err := setIDPAttributesToResourceData(d, idpinstance); err != nil { + diags := append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Unable to set IDP " + idpid + " in org " + orgid, + Detail: err.Error(), + }) + return diags + } + + return diags +} + +func resourceOIDCUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + pco := m.(ProviderConfOutput) + idpid := d.Id() + orgid := d.Get("org_id").(string) + + if d.HasChanges(getIDPAttributes()...) { + authctx := getIDPAuthCtx(ctx, &pco) + body, errDiags := newOIDCPatchBody(d) + if errDiags.HasError() { + diags = append(diags, errDiags...) + return diags + } + _, httpr, err := pco.idpclient.DefaultApi.OrganizationsOrgIdIdentityProvidersIdpIdPatch(authctx, orgid, idpid).IdpPatchBody(*body).Execute() + if err != nil { + var details string + if httpr != nil { + b, _ := ioutil.ReadAll(httpr.Body) + details = string(b) + } else { + details = err.Error() + } + diags := append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Unable to Update IDP " + idpid + " in org " + orgid, + Detail: details, + }) + return diags + } + defer httpr.Body.Close() + + d.Set("last_updated", time.Now().Format(time.RFC850)) + } + + return resourceOIDCRead(ctx, d, m) +} + +func resourceOIDCDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + pco := m.(ProviderConfOutput) + idpid := d.Id() + orgid := d.Get("org_id").(string) + authctx := getIDPAuthCtx(ctx, &pco) + + httpr, err := pco.idpclient.DefaultApi.OrganizationsOrgIdIdentityProvidersIdpIdDelete(authctx, orgid, idpid).Execute() + if err != nil { + var details string + if httpr != nil { + b, _ := ioutil.ReadAll(httpr.Body) + details = string(b) + } else { + details = err.Error() + } + diags := append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Unable to Delete OIDC provider " + idpid, + Detail: details, + }) + return diags + } + defer httpr.Body.Close() + // d.SetId("") is automatically called assuming delete returns no errors, but + // it is added here for explicitness. + d.SetId("") + + return diags +} + +/* Prepares the body required to post an OIDC provider*/ +func newOIDCPostBody(d *schema.ResourceData) (*idp.IdpPostBody, diag.Diagnostics) { + var diags diag.Diagnostics + + name := d.Get("name").(string) + oidc_provider_input := d.Get("oidc_provider") + + body := idp.NewIdpPostBody() + + oidc_type := idp.NewIdpPostBodyType() + oidc_type.SetName("openid") + oidc_type.SetDescription("OpenID Connect") + + oidc_provider := idp.NewOidcProvider1() + + if oidc_provider_input != nil { + set := oidc_provider_input.(*schema.Set) + list := set.List() + if len(list) > 0 { + item := list[0] + data := item.(map[string]interface{}) + // reads client registration or credentials depending on which one is added + client := idp.NewClient1() + client_urls := idp.NewUrls1() + if client_registration_url, ok := data["client_registration_url"]; ok { + client_urls.SetRegister(client_registration_url.(string)) + client.SetUrls(*client_urls) + } else { + credentials := idp.NewCredentials1() + if client_credentials_id, ok := data["client_credentials_id"]; ok { + credentials.SetId(client_credentials_id.(string)) + } + if client_credentials_secret, ok := data["client_credentials_secret"]; ok { + credentials.SetSecret(client_credentials_secret.(string)) + } + client.SetCredentials(*credentials) + } + oidc_provider.SetClient(*client) + + //Parsing URLs + urls := idp.NewUrls3() + if token_url, ok := data["token_url"]; ok { + urls.SetToken(token_url.(string)) + } + if userinfo_url, ok := data["userinfo_url"]; ok { + urls.SetUserinfo(userinfo_url.(string)) + } + if authorize_url, ok := data["authorize_url"]; ok { + urls.SetAuthorize(authorize_url.(string)) + } + oidc_provider.SetUrls(*urls) + + if issuer, ok := data["issuer"]; ok { + oidc_provider.SetIssuer(issuer.(string)) + } + if group_scope, ok := data["group_scope"]; ok { + oidc_provider.SetGroupScope(group_scope.(string)) + } + if allow_untrusted_certificates, ok := data["allow_untrusted_certificates"]; ok { + body.SetAllowUntrustedCertificates(allow_untrusted_certificates.(bool)) + } + } + } + + body.SetType(*oidc_type) + body.SetName(name) + body.SetOidcProvider(*oidc_provider) + + return body, diags +} + +/* Prepares the body required to patch an OIDC provider*/ +func newOIDCPatchBody(d *schema.ResourceData) (*idp.IdpPatchBody, diag.Diagnostics) { + var diags diag.Diagnostics + + name := d.Get("name").(string) + oidc_provider_input := d.Get("oidc_provider") + + body := idp.NewIdpPatchBody() + + oidc_type := idp.NewIdpPatchBodyType() + oidc_type.SetDescription("OpenID Connect") + + oidc_provider := idp.NewOidcProvider1() + + if oidc_provider_input != nil { + set := oidc_provider_input.(*schema.Set) + list := set.List() + if set.Len() > 0 { + item := list[0] + data := item.(map[string]interface{}) + // reads client registration or credentials depending on which one is added + client := idp.NewClient1() + client_urls := idp.NewUrls1() + if client_registration_url, ok := data["client_registration_url"]; ok { + client_urls.SetRegister(client_registration_url.(string)) + client.SetUrls(*client_urls) + } else { + credentials := idp.NewCredentials1() + if client_credentials_id, ok := data["client_credentials_id"]; ok { + credentials.SetId(client_credentials_id.(string)) + } + if client_credentials_secret, ok := data["client_credentials_secret"]; ok { + credentials.SetSecret(client_credentials_secret.(string)) + } + client.SetCredentials(*credentials) + } + oidc_provider.SetClient(*client) + + //Parsing URLs + urls := idp.NewUrls3() + if token_url, ok := data["token_url"]; ok { + urls.SetToken(token_url.(string)) + } + if userinfo_url, ok := data["userinfo_url"]; ok { + urls.SetUserinfo(userinfo_url.(string)) + } + if authorize_url, ok := data["authorize_url"]; ok { + urls.SetAuthorize(authorize_url.(string)) + } + oidc_provider.SetUrls(*urls) + + if issuer, ok := data["issuer"]; ok { + oidc_provider.SetIssuer(issuer.(string)) + } + if group_scope, ok := data["group_scope"]; ok { + oidc_provider.SetGroupScope(group_scope.(string)) + } + if allow_untrusted_certificates, ok := data["allow_untrusted_certificates"]; ok { + body.SetAllowUntrustedCertificates(allow_untrusted_certificates.(bool)) + } + } + } + + body.SetType(*oidc_type) + body.SetName(name) + body.SetOidcProvider(*oidc_provider) + + return body, diags +} + +func getIDPAuthCtx(ctx context.Context, pco *ProviderConfOutput) context.Context { + tmp := context.WithValue(ctx, idp.ContextAccessToken, pco.access_token) + return context.WithValue(tmp, idp.ContextServerIndex, pco.server_index) +} diff --git a/anypoint/resource_idp_saml.go b/anypoint/resource_idp_saml.go new file mode 100644 index 0000000..a016038 --- /dev/null +++ b/anypoint/resource_idp_saml.go @@ -0,0 +1,438 @@ +package anypoint + +import ( + "context" + "io/ioutil" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/mulesoft-consulting/anypoint-client-go/idp" +) + +func resourceSAML() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceSAMLCreate, + ReadContext: resourceSAMLRead, + UpdateContext: resourceSAMLUpdate, + DeleteContext: resourceSAMLDelete, + Description: ` + Creates an ` + "`" + `identity provider` + "`" + ` SAML type configuration in your account. + `, + Schema: map[string]*schema.Schema{ + "last_updated": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "org_id": { + Type: schema.TypeString, + Required: true, + Description: "The business group id", + }, + "provider_id": { + Type: schema.TypeString, + Computed: true, + Description: "The provider id", + }, + "name": { + Type: schema.TypeString, + Required: true, + Description: "The name of the provider", + }, + "type": { + Type: schema.TypeMap, + Computed: true, + Description: "The type of the provider, contains description and the name of the type of the provider (saml or oidc)", + }, + "saml": { + Type: schema.TypeSet, + Description: "The description of provider specific for SAML types", + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "issuer": { + Type: schema.TypeString, + Required: true, + Description: "The provider issuer", + }, + "audience": { + Type: schema.TypeString, + Required: true, + Description: "The provider audience", + }, + "public_key": { + Type: schema.TypeList, + Required: true, + Description: "The list of public keys", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "claims_mapping_email_attribute": { + Type: schema.TypeString, + Optional: true, + Default: "", + Description: "Field name in the SAML AttributeStatements that maps to Email. By default, the email attribute in the SAML assertion is used.", + }, + "claims_mapping_group_attribute": { + Type: schema.TypeString, + Optional: true, + Default: "", + Description: "Field name in the SAML AttributeStatements that maps to Group.", + }, + "claims_mapping_lastname_attribute": { + Type: schema.TypeString, + Optional: true, + Default: "", + Description: "Field name in the SAML AttributeStatements that maps to Last Name. By default, the lastname attribute in the SAML assertion is used.", + }, + "claims_mapping_username_attribute": { + Type: schema.TypeString, + Optional: true, + Default: "", + Description: "Field name in the SAML AttributeStatements that maps to username. By default, the NameID attribute in the SAML assertion is used.", + }, + "claims_mapping_firstname_attribute": { + Type: schema.TypeString, + Optional: true, + Default: "", + Description: "Field name in the SAML AttributeStatements that maps to First Name. By default, the firstname attribute in the SAML assertion is used.", + }, + "sp_initiated_sso_enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "True if the Service Provider initiated SSO enabled", + }, + "idp_initiated_sso_enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "True if the Identity Provider initiated SSO enabled", + }, + "require_encrypted_saml_assertions": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "True if the encryption of saml assertions requirement is enabled", + }, + }, + }, + }, + "sp_sign_on_url": { + Type: schema.TypeString, + Required: true, + Description: "The provider's sign on url", + }, + "sp_sign_out_url": { + Type: schema.TypeString, + Required: true, + Description: "The provider's sign out url, only available for SAML", + }, + }, + } +} + +func resourceSAMLCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + pco := m.(ProviderConfOutput) + orgid := d.Get("org_id").(string) + + authctx := getIDPAuthCtx(ctx, &pco) + body, errDiags := newSAMLPostBody(d) + if errDiags.HasError() { + diags = append(diags, errDiags...) + return diags + } + + res, httpr, err := pco.idpclient.DefaultApi.OrganizationsOrgIdIdentityProvidersPost(authctx, orgid).IdpPostBody(*body).Execute() + defer httpr.Body.Close() + if err != nil { + var details string + if httpr != nil { + b, _ := ioutil.ReadAll(httpr.Body) + details = string(b) + } else { + details = err.Error() + } + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Unable to create OIDC provider for org " + orgid, + Detail: details, + }) + return diags + } + + d.SetId(res.GetProviderId()) + + return resourceSAMLRead(ctx, d, m) +} + +func resourceSAMLRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + pco := m.(ProviderConfOutput) + idpid := d.Id() + orgid := d.Get("org_id").(string) + authctx := getIDPAuthCtx(ctx, &pco) + + //request idp + res, httpr, err := pco.idpclient.DefaultApi.OrganizationsOrgIdIdentityProvidersIdpIdGet(authctx, orgid, idpid).Execute() + defer httpr.Body.Close() + if err != nil { + var details string + if httpr != nil { + b, _ := ioutil.ReadAll(httpr.Body) + details = string(b) + } else { + details = err.Error() + } + diags := append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Unable to Get IDP " + idpid + " in org " + orgid, + Detail: details, + }) + return diags + } + //process data + idpinstance := flattenIDPData(&res) + //save in data source schema + if err := setIDPAttributesToResourceData(d, idpinstance); err != nil { + diags := append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Unable to set IDP " + idpid + " in org " + orgid, + Detail: err.Error(), + }) + return diags + } + + return diags +} + +func resourceSAMLUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + pco := m.(ProviderConfOutput) + idpid := d.Id() + orgid := d.Get("org_id").(string) + + if d.HasChanges(getIDPAttributes()...) { + authctx := getIDPAuthCtx(ctx, &pco) + body, errDiags := newSAMLPatchBody(d) + if errDiags.HasError() { + diags = append(diags, errDiags...) + return diags + } + _, httpr, err := pco.idpclient.DefaultApi.OrganizationsOrgIdIdentityProvidersIdpIdPatch(authctx, orgid, idpid).IdpPatchBody(*body).Execute() + if err != nil { + var details string + if httpr != nil { + b, _ := ioutil.ReadAll(httpr.Body) + details = string(b) + } else { + details = err.Error() + } + diags := append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Unable to Update IDP " + idpid + " in org " + orgid, + Detail: details, + }) + return diags + } + defer httpr.Body.Close() + + d.Set("last_updated", time.Now().Format(time.RFC850)) + } + + return resourceSAMLRead(ctx, d, m) +} + +func resourceSAMLDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + pco := m.(ProviderConfOutput) + idpid := d.Id() + orgid := d.Get("org_id").(string) + authctx := getIDPAuthCtx(ctx, &pco) + + httpr, err := pco.idpclient.DefaultApi.OrganizationsOrgIdIdentityProvidersIdpIdDelete(authctx, orgid, idpid).Execute() + if err != nil { + var details string + if httpr != nil { + b, _ := ioutil.ReadAll(httpr.Body) + details = string(b) + } else { + details = err.Error() + } + diags := append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Unable to Delete OIDC provider " + idpid, + Detail: details, + }) + return diags + } + defer httpr.Body.Close() + // d.SetId("") is automatically called assuming delete returns no errors, but + // it is added here for explicitness. + d.SetId("") + + return diags +} + +/* Prepares the body required to post an OIDC provider*/ +func newSAMLPostBody(d *schema.ResourceData) (*idp.IdpPostBody, diag.Diagnostics) { + var diags diag.Diagnostics + + name := d.Get("name").(string) + saml_input := d.Get("saml") + sp_sign_on_url := d.Get("sp_sign_on_url").(string) + sp_sign_out_url := d.Get("sp_sign_out_url").(string) + + body := idp.NewIdpPostBody() + + saml_type := idp.NewIdpPostBodyType() + saml_type.SetName("saml") + saml_type.SetDescription("SAML 2.0") + + saml := idp.NewSaml1() + + if saml_input != nil { + set := saml_input.(*schema.Set) + list := set.List() + if set.Len() > 0 { + item := list[0] + data := item.(map[string]interface{}) + if issuer, ok := data["issuer"]; ok { + saml.SetIssuer(issuer.(string)) + } + if audience, ok := data["audience"]; ok { + saml.SetAudience(audience.(string)) + } + if public_key, ok := data["public_key"]; ok { + l := public_key.([]interface{}) + keys := make([]string, len(l)) + for i, k := range l { + keys[i] = k.(string) + } + saml.SetPublicKey(keys) + } + //parsing claims + claims := idp.NewClaimsMapping2() + if claims_mapping_email_attribute, ok := data["claims_mapping_email_attribute"]; ok { + claims.SetEmailAttribute(claims_mapping_email_attribute.(string)) + } + if claims_mapping_group_attribute, ok := data["claims_mapping_group_attribute"]; ok { + claims.SetGroupAttribute(claims_mapping_group_attribute.(string)) + } + if claims_mapping_lastname_attribute, ok := data["claims_mapping_lastname_attribute"]; ok { + claims.SetLastnameAttribute(claims_mapping_lastname_attribute.(string)) + } + if claims_mapping_username_attribute, ok := data["claims_mapping_username_attribute"]; ok { + claims.SetUsernameAttribute(claims_mapping_username_attribute.(string)) + } + if claims_mapping_firstname_attribute, ok := data["claims_mapping_firstname_attribute"]; ok { + claims.SetFirstnameAttribute(claims_mapping_firstname_attribute.(string)) + } + saml.SetClaimsMapping(*claims) + + if sp_initiated_sso_enabled, ok := data["sp_initiated_sso_enabled"]; ok { + saml.SetSpInitiatedSsoEnabled(sp_initiated_sso_enabled.(bool)) + } + if idp_initiated_sso_enabled, ok := data["idp_initiated_sso_enabled"]; ok { + saml.SetIdpInitiatedSsoEnabled(idp_initiated_sso_enabled.(bool)) + } + if require_encrypted_saml_assertions, ok := data["require_encrypted_saml_assertions"]; ok { + saml.SetRequireEncryptedSamlAssertions(require_encrypted_saml_assertions.(bool)) + } + } + } + + sp := idp.NewServiceProvider1() + sp_urls := idp.NewUrls4() + sp_urls.SetSignOn(sp_sign_on_url) + sp_urls.SetSignOut(sp_sign_out_url) + sp.SetUrls(*sp_urls) + body.SetServiceProvider(*sp) + body.SetSaml(*saml) + body.SetName(name) + body.SetType(*saml_type) + + return body, diags +} + +/* Prepares the body required to patch an OIDC provider*/ +func newSAMLPatchBody(d *schema.ResourceData) (*idp.IdpPatchBody, diag.Diagnostics) { + var diags diag.Diagnostics + + name := d.Get("name").(string) + saml_input := d.Get("saml") + sp_sign_on_url := d.Get("sp_sign_on_url").(string) + sp_sign_out_url := d.Get("sp_sign_out_url").(string) + + body := idp.NewIdpPatchBody() + + saml_type := idp.NewIdpPatchBodyType() + saml_type.SetDescription("SAML 2.0") + + saml := idp.NewSaml1() + + if saml_input != nil { + set := saml_input.(*schema.Set) + list := set.List() + if set.Len() > 0 { + item := list[0] + data := item.(map[string]interface{}) + if issuer, ok := data["issuer"]; ok { + saml.SetIssuer(issuer.(string)) + } + if audience, ok := data["audience"]; ok { + saml.SetAudience(audience.(string)) + } + if public_key, ok := data["public_key"]; ok { + l := public_key.([]interface{}) + keys := make([]string, len(l)) + for i, k := range l { + keys[i] = k.(string) + } + saml.SetPublicKey(keys) + } + //parsing claims + claims := idp.NewClaimsMapping2() + if claims_mapping_email_attribute, ok := data["claims_mapping_email_attribute"]; ok { + claims.SetEmailAttribute(claims_mapping_email_attribute.(string)) + } + if claims_mapping_group_attribute, ok := data["claims_mapping_group_attribute"]; ok { + claims.SetGroupAttribute(claims_mapping_group_attribute.(string)) + } + if claims_mapping_lastname_attribute, ok := data["claims_mapping_lastname_attribute"]; ok { + claims.SetLastnameAttribute(claims_mapping_lastname_attribute.(string)) + } + if claims_mapping_username_attribute, ok := data["claims_mapping_username_attribute"]; ok { + claims.SetUsernameAttribute(claims_mapping_username_attribute.(string)) + } + if claims_mapping_firstname_attribute, ok := data["claims_mapping_firstname_attribute"]; ok { + claims.SetFirstnameAttribute(claims_mapping_firstname_attribute.(string)) + } + saml.SetClaimsMapping(*claims) + + if sp_initiated_sso_enabled, ok := data["sp_initiated_sso_enabled"]; ok { + saml.SetSpInitiatedSsoEnabled(sp_initiated_sso_enabled.(bool)) + } + if idp_initiated_sso_enabled, ok := data["idp_initiated_sso_enabled"]; ok { + saml.SetIdpInitiatedSsoEnabled(idp_initiated_sso_enabled.(bool)) + } + if require_encrypted_saml_assertions, ok := data["require_encrypted_saml_assertions"]; ok { + saml.SetRequireEncryptedSamlAssertions(require_encrypted_saml_assertions.(bool)) + } + } + } + sp := idp.NewServiceProvider1() + sp_urls := idp.NewUrls4() + sp_urls.SetSignOn(sp_sign_on_url) + sp_urls.SetSignOut(sp_sign_out_url) + sp.SetUrls(*sp_urls) + body.SetServiceProvider(*sp) + body.SetSaml(*saml) + body.SetName(name) + body.SetType(*saml_type) + + return body, diags +} diff --git a/docs/data-sources/idp.md b/docs/data-sources/idp.md new file mode 100644 index 0000000..4dd54a5 --- /dev/null +++ b/docs/data-sources/idp.md @@ -0,0 +1,74 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "anypoint_idp Data Source - terraform-provider-anypoint" +subcategory: "" +description: |- + Reads a specific `identity provider` in your business group. +--- + +# anypoint_idp (Data Source) + +Reads a specific `identity provider` in your business group. + +## Example Usage + +```terraform +data "anypoint_idp" "idp" { + org_id = "xxxx-xxx-xxx" # the business group id + id = "xxxx-xxx-xxxx" # provider id +} +``` + + +## Schema + +### Required + +- **id** (String) The provider id +- **org_id** (String) The business group id + +### Read-Only + +- **name** (String) The name of the provider +- **oidc_provider** (Set of Object) The description of provider specific for OIDC types (see [below for nested schema](#nestedatt--oidc_provider)) +- **provider_id** (String) The provider id +- **saml** (Set of Object) The description of provider specific for SAML types (see [below for nested schema](#nestedatt--saml)) +- **sp_sign_on_url** (String) The provider's sign on url +- **sp_sign_out_url** (String) The provider's sign out url, only available for SAML +- **type** (Map of String) The type of the provider, contains description and the name of the type of the provider (saml or oidc) + + +### Nested Schema for `oidc_provider` + +Read-Only: + +- **allow_untrusted_certificates** (Boolean) +- **authorize_url** (String) +- **client_credentials_id** (String) +- **client_registration_url** (String) +- **client_token_endpoint_auth_methods_supported** (List of String) +- **group_scope** (String) +- **issuer** (String) +- **redirect_url** (String) +- **token_url** (String) +- **userinfo_url** (String) + + + +### Nested Schema for `saml` + +Read-Only: + +- **audience** (String) +- **claims_mapping_email_attribute** (String) +- **claims_mapping_firstname_attribute** (String) +- **claims_mapping_group_attribute** (String) +- **claims_mapping_lastname_attribute** (String) +- **claims_mapping_username_attribute** (String) +- **idp_initiated_sso_enabled** (Boolean) +- **issuer** (String) +- **public_key** (List of String) +- **require_encrypted_saml_assertions** (Boolean) +- **sp_initiated_sso_enabled** (Boolean) + + diff --git a/docs/data-sources/idps.md b/docs/data-sources/idps.md new file mode 100644 index 0000000..d73abc0 --- /dev/null +++ b/docs/data-sources/idps.md @@ -0,0 +1,47 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "anypoint_idps Data Source - terraform-provider-anypoint" +subcategory: "" +description: |- + Reads all `identity providers` in your business group. +--- + +# anypoint_idps (Data Source) + +Reads all `identity providers` in your business group. + +## Example Usage + +```terraform +data "anypoint_idps" "idp" { + org_id = "xxxx-xxx-xxx" # the business group id +} +``` + + +## Schema + +### Required + +- **org_id** (String) The business group id + +### Optional + +- **id** (String) The ID of this resource. + +### Read-Only + +- **idps** (List of Object) List of providers for the given org (see [below for nested schema](#nestedatt--idps)) +- **total** (Number) The total number of available results + + +### Nested Schema for `idps` + +Read-Only: + +- **name** (String) +- **org_id** (String) +- **provider_id** (String) +- **type** (Map of String) + + diff --git a/docs/resources/idp_oidc.md b/docs/resources/idp_oidc.md new file mode 100644 index 0000000..c682b2a --- /dev/null +++ b/docs/resources/idp_oidc.md @@ -0,0 +1,95 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "anypoint_idp_oidc Resource - terraform-provider-anypoint" +subcategory: "" +description: |- + Creates an `identity provider` OIDC type configuration in your account. +--- + +# anypoint_idp_oidc (Resource) + +Creates an `identity provider` OIDC type configuration in your account. + +## Example Usage + +```terraform +resource "anypoint_idp_oidc" "example1" { + org_id = var.root_org + name = "openid connect provider" + oidc_provider { + authorize_url = "http://idp.example.com/auth/realms/master/protocol/openid-connect/auth" + token_url = "http://idp.example.com/auth/realms/master/protocol/openid-connect/token" + userinfo_url = "http://idp.example.com/auth/realms/master/protocol/openid-connect/userinfo" + + client_registration_url = "http://idp.example.com/auth/realms/master/clients-registrations/openid-connect" + + issuer = "http://idp.example.com/auth/realms/master" + + allow_untrusted_certificates = true + } +} + + +resource "anypoint_idp_oidc" "example2" { + org_id = var.root_org + name = "openid connect provider 2" + oidc_provider { + authorize_url = "http://idp.example.com/auth/realms/master/protocol/openid-connect/auth" + token_url = "http://idp.example.com/auth/realms/master/protocol/openid-connect/token" + userinfo_url = "http://idp.example.com/auth/realms/master/protocol/openid-connect/userinfo" + + issuer = "http://idp.example.com/auth/realms/master" + + client_credentials_id = "anypoint-oidc" + client_credentials_secret = "63b376f8-3ece-44f6-869c-33fe9022fdc4" + + allow_untrusted_certificates = true + } +} +``` + + +## Schema + +### Required + +- **name** (String) The name of the provider +- **oidc_provider** (Block Set, Min: 1) The description of provider specific for OIDC types (see [below for nested schema](#nestedblock--oidc_provider)) +- **org_id** (String) The business group id + +### Optional + +- **id** (String) The ID of this resource. +- **last_updated** (String) + +### Read-Only + +- **provider_id** (String) The provider id +- **sp_sign_on_url** (String) The provider's sign on url +- **sp_sign_out_url** (String) The provider's sign out url, only available for SAML +- **type** (Map of String) The type of the provider, contains description and the name of the type of the provider (saml or oidc) + + +### Nested Schema for `oidc_provider` + +Required: + +- **authorize_url** (String) The authorization url of the openid-connect provider +- **issuer** (String) The provider token issuer url +- **token_url** (String) The token url of the openid-connect provider +- **userinfo_url** (String) The userinfo url of the openid-connect provider + +Optional: + +- **allow_untrusted_certificates** (Boolean) The certification validation trigger +- **client_credentials_id** (String) The client's credentials id. This should only be provided if manual registration is wanted. Mutually exclusive with registration url, if both are given registration url is prioritized. +- **client_credentials_secret** (String, Sensitive) The client's credentials secret. This should only be provided if manual registration is wanted. Mutually exclusive with registration url, if both are given registration url is prioritized. +- **client_registration_url** (String) The registration url, for dynamic client registration, of the openid-connect provider. Mutually exclusive with credentials id/secret, if both are given registration url is prioritized. +- **group_scope** (String) The provider group scopes + +Read-Only: + +- **client_token_endpoint_auth_methods_supported** (List of String) The list of authentication methods supported +- **redirect_url** (String) The redirect url of the openid-connect provider + + diff --git a/docs/resources/idp_saml.md b/docs/resources/idp_saml.md new file mode 100644 index 0000000..61e0d1c --- /dev/null +++ b/docs/resources/idp_saml.md @@ -0,0 +1,98 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "anypoint_idp_saml Resource - terraform-provider-anypoint" +subcategory: "" +description: |- + Creates an `identity provider` SAML type configuration in your account. +--- + +# anypoint_idp_saml (Resource) + +Creates an `identity provider` SAML type configuration in your account. + +## Example Usage + +```terraform +resource "anypoint_idp_saml" "example1" { + org_id = var.root_org + name = "SAML 2.0 provider" + saml { + issuer = "http://idp.example.com/auth/realms/master" + audience = "example1.anypoint.mulesoft.com" + + public_key = tolist(["MIICmzCCAYMCBgF+m6ogEzANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjIwMTI3MTMxMDI0WhcNMzIwMTI3MTMxMjA0WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCH91QuAKq0wzQjDExmWEqSNno/wnbNvZbMb33fcnl1gQ64LlY/AFnv2RJySQ2qm5qM1q5zGJc1jy/gxS/3Rp2iUQ+NBntgdAUg/4h64lh/sM76xoRv0T9zc8tvZn5PWkqJTgksACOnAGQaxCAHqKpLS6pEHJELvlUK6sTOJSAHB1KNd7ixOW6TLkFXXdQRiN1AkJG8shOiX/lb2Vnj3KwK3+/5JmRziuWiZHKR/2TeMuAD+8GJ8AWGrGbDkQe04kbSDYNWXKjZlqfPXYx+Vfrzyijun99f+WBQSGbyakgHTDszSMkcnHI0MniotCm4mEsMroUY16YIGIpQUpB+Ghg9AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAF9WeaNB9nA+Ri03IU5slROnzgSB49FOAbcrtO4ml2p1UPdwb8X1QBJSPKwRMEbXQxGddq1HtOyyayL9Ii5ogMwz8uhxxLym2MlzMUb/KAbI7cJJrRcvwhCGqIyfe932VN4v5a3/FYIHbfmmo8CKDUQmybLB8+LQ+"]) + + sp_initiated_sso_enabled = true + idp_initiated_sso_enabled = true + require_encrypted_saml_assertions = true + } + sp_sign_on_url = "http://idp.example.com/auth/realms/master/protocol/saml" + sp_sign_out_url = "http://idp.example.com/auth/realms/master/protocol/saml" +} + +resource "anypoint_idp_saml" "example2" { + org_id = var.root_org + name = "SAML 2.0 provider" + saml { + issuer = "http://idp.example.com/auth/realms/master" + audience = "example1.anypoint.mulesoft.com" + + public_key = tolist(["MIICmzCCAYMCBgF+m6ogEzANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjIwMTI3MTMxMDI0WhcNMzIwMTI3MTMxMjA0WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCH91QuAKq0wzQjDExmWEqSNno/wnbNvZbMb33fcnl1gQ64LlY/AFnv2RJySQ2qm5qM1q5zGJc1jy/gxS/3Rp2iUQ+NBntgdAUg/4h64lh/sM76xoRv0T9zc8tvZn5PWkqJTgksACOnAGQaxCAHqKpLS6pEHJELvlUK6sTOJSAHB1KNd7ixOW6TLkFXXdQRiN1AkJG8shOiX/lb2Vnj3KwK3+/5JmRziuWiZHKR/2TeMuAD+8GJ8AWGrGbDkQe04kbSDYNWXKjZlqfPXYx+Vfrzyijun99f+WBQSGbyakgHTDszSMkcnHI0MniotCm4mEsMroUY16YIGIpQUpB+Ghg9AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAF9WeaNB9nA+Ri03IU5slROnzgSB49FOAbcrtO4ml2p1UPdwb8X1QBJSPKwRMEbXQxGddq1HtOyyayL9Ii5ogMwz8uhxxLym2MlzMUb/KAbI7cJJrRcvwhCGqIyfe932VN4v5a3/FYIHbfmmo8CKDUQmybLB8+LQ+"]) + + sp_initiated_sso_enabled = true + idp_initiated_sso_enabled = true + require_encrypted_saml_assertions = true + + claims_mapping_email_attribute = "email1" + claims_mapping_group_attribute = "groups1" + claims_mapping_lastname_attribute = "lastname1" + claims_mapping_username_attribute = "username1" + claims_mapping_firstname_attribute = "firstname1" + } + sp_sign_on_url = "http://idp.example.com/auth/realms/master/protocol/saml" + sp_sign_out_url = "http://idp.example.com/auth/realms/master/protocol/saml" +} +``` + + +## Schema + +### Required + +- **name** (String) The name of the provider +- **org_id** (String) The business group id +- **saml** (Block Set, Min: 1) The description of provider specific for SAML types (see [below for nested schema](#nestedblock--saml)) +- **sp_sign_on_url** (String) The provider's sign on url +- **sp_sign_out_url** (String) The provider's sign out url, only available for SAML + +### Optional + +- **id** (String) The ID of this resource. +- **last_updated** (String) + +### Read-Only + +- **provider_id** (String) The provider id +- **type** (Map of String) The type of the provider, contains description and the name of the type of the provider (saml or oidc) + + +### Nested Schema for `saml` + +Required: + +- **audience** (String) The provider audience +- **issuer** (String) The provider issuer +- **public_key** (List of String) The list of public keys + +Optional: + +- **claims_mapping_email_attribute** (String) Field name in the SAML AttributeStatements that maps to Email. By default, the email attribute in the SAML assertion is used. +- **claims_mapping_firstname_attribute** (String) Field name in the SAML AttributeStatements that maps to First Name. By default, the firstname attribute in the SAML assertion is used. +- **claims_mapping_group_attribute** (String) Field name in the SAML AttributeStatements that maps to Group. +- **claims_mapping_lastname_attribute** (String) Field name in the SAML AttributeStatements that maps to Last Name. By default, the lastname attribute in the SAML assertion is used. +- **claims_mapping_username_attribute** (String) Field name in the SAML AttributeStatements that maps to username. By default, the NameID attribute in the SAML assertion is used. +- **idp_initiated_sso_enabled** (Boolean) True if the Identity Provider initiated SSO enabled +- **require_encrypted_saml_assertions** (Boolean) True if the encryption of saml assertions requirement is enabled +- **sp_initiated_sso_enabled** (Boolean) True if the Service Provider initiated SSO enabled + + diff --git a/examples/data-sources/anypoint_idp/data-source.tf b/examples/data-sources/anypoint_idp/data-source.tf new file mode 100644 index 0000000..c5335c5 --- /dev/null +++ b/examples/data-sources/anypoint_idp/data-source.tf @@ -0,0 +1,4 @@ +data "anypoint_idp" "idp" { + org_id = "xxxx-xxx-xxx" # the business group id + id = "xxxx-xxx-xxxx" # provider id +} \ No newline at end of file diff --git a/examples/data-sources/anypoint_idps/data-source.tf b/examples/data-sources/anypoint_idps/data-source.tf new file mode 100644 index 0000000..e538142 --- /dev/null +++ b/examples/data-sources/anypoint_idps/data-source.tf @@ -0,0 +1,3 @@ +data "anypoint_idps" "idp" { + org_id = "xxxx-xxx-xxx" # the business group id +} \ No newline at end of file diff --git a/examples/resources/anypoint_idp_oidc/resource.tf b/examples/resources/anypoint_idp_oidc/resource.tf new file mode 100644 index 0000000..1af4ccb --- /dev/null +++ b/examples/resources/anypoint_idp_oidc/resource.tf @@ -0,0 +1,33 @@ +resource "anypoint_idp_oidc" "example1" { + org_id = var.root_org + name = "openid connect provider" + oidc_provider { + authorize_url = "http://idp.example.com/auth/realms/master/protocol/openid-connect/auth" + token_url = "http://idp.example.com/auth/realms/master/protocol/openid-connect/token" + userinfo_url = "http://idp.example.com/auth/realms/master/protocol/openid-connect/userinfo" + + client_registration_url = "http://idp.example.com/auth/realms/master/clients-registrations/openid-connect" + + issuer = "http://idp.example.com/auth/realms/master" + + allow_untrusted_certificates = true + } +} + + +resource "anypoint_idp_oidc" "example2" { + org_id = var.root_org + name = "openid connect provider 2" + oidc_provider { + authorize_url = "http://idp.example.com/auth/realms/master/protocol/openid-connect/auth" + token_url = "http://idp.example.com/auth/realms/master/protocol/openid-connect/token" + userinfo_url = "http://idp.example.com/auth/realms/master/protocol/openid-connect/userinfo" + + issuer = "http://idp.example.com/auth/realms/master" + + client_credentials_id = "anypoint-oidc" + client_credentials_secret = "63b376f8-3ece-44f6-869c-33fe9022fdc4" + + allow_untrusted_certificates = true + } +} \ No newline at end of file diff --git a/examples/resources/anypoint_idp_oidc/test.auto.tfvars b/examples/resources/anypoint_idp_oidc/test.auto.tfvars new file mode 100644 index 0000000..befec46 --- /dev/null +++ b/examples/resources/anypoint_idp_oidc/test.auto.tfvars @@ -0,0 +1,3 @@ +root_org = "aa1f55d6-213d-4f60-845c-207286484cd1" + + diff --git a/examples/resources/anypoint_idp_oidc/test.tf b/examples/resources/anypoint_idp_oidc/test.tf new file mode 100644 index 0000000..bb9ff57 --- /dev/null +++ b/examples/resources/anypoint_idp_oidc/test.tf @@ -0,0 +1,3 @@ +variable "root_org" { + default = "xx1f55d6-213d-4f60-845c-207286484cd1" +} diff --git a/examples/resources/anypoint_idp_saml/resource.tf b/examples/resources/anypoint_idp_saml/resource.tf new file mode 100644 index 0000000..27d141b --- /dev/null +++ b/examples/resources/anypoint_idp_saml/resource.tf @@ -0,0 +1,39 @@ +resource "anypoint_idp_saml" "example1" { + org_id = var.root_org + name = "SAML 2.0 provider" + saml { + issuer = "http://idp.example.com/auth/realms/master" + audience = "example1.anypoint.mulesoft.com" + + public_key = tolist(["MIICmzCCAYMCBgF+m6ogEzANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjIwMTI3MTMxMDI0WhcNMzIwMTI3MTMxMjA0WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCH91QuAKq0wzQjDExmWEqSNno/wnbNvZbMb33fcnl1gQ64LlY/AFnv2RJySQ2qm5qM1q5zGJc1jy/gxS/3Rp2iUQ+NBntgdAUg/4h64lh/sM76xoRv0T9zc8tvZn5PWkqJTgksACOnAGQaxCAHqKpLS6pEHJELvlUK6sTOJSAHB1KNd7ixOW6TLkFXXdQRiN1AkJG8shOiX/lb2Vnj3KwK3+/5JmRziuWiZHKR/2TeMuAD+8GJ8AWGrGbDkQe04kbSDYNWXKjZlqfPXYx+Vfrzyijun99f+WBQSGbyakgHTDszSMkcnHI0MniotCm4mEsMroUY16YIGIpQUpB+Ghg9AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAF9WeaNB9nA+Ri03IU5slROnzgSB49FOAbcrtO4ml2p1UPdwb8X1QBJSPKwRMEbXQxGddq1HtOyyayL9Ii5ogMwz8uhxxLym2MlzMUb/KAbI7cJJrRcvwhCGqIyfe932VN4v5a3/FYIHbfmmo8CKDUQmybLB8+LQ+"]) + + sp_initiated_sso_enabled = true + idp_initiated_sso_enabled = true + require_encrypted_saml_assertions = true + } + sp_sign_on_url = "http://idp.example.com/auth/realms/master/protocol/saml" + sp_sign_out_url = "http://idp.example.com/auth/realms/master/protocol/saml" +} + +resource "anypoint_idp_saml" "example2" { + org_id = var.root_org + name = "SAML 2.0 provider" + saml { + issuer = "http://idp.example.com/auth/realms/master" + audience = "example1.anypoint.mulesoft.com" + + public_key = tolist(["MIICmzCCAYMCBgF+m6ogEzANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjIwMTI3MTMxMDI0WhcNMzIwMTI3MTMxMjA0WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCH91QuAKq0wzQjDExmWEqSNno/wnbNvZbMb33fcnl1gQ64LlY/AFnv2RJySQ2qm5qM1q5zGJc1jy/gxS/3Rp2iUQ+NBntgdAUg/4h64lh/sM76xoRv0T9zc8tvZn5PWkqJTgksACOnAGQaxCAHqKpLS6pEHJELvlUK6sTOJSAHB1KNd7ixOW6TLkFXXdQRiN1AkJG8shOiX/lb2Vnj3KwK3+/5JmRziuWiZHKR/2TeMuAD+8GJ8AWGrGbDkQe04kbSDYNWXKjZlqfPXYx+Vfrzyijun99f+WBQSGbyakgHTDszSMkcnHI0MniotCm4mEsMroUY16YIGIpQUpB+Ghg9AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAF9WeaNB9nA+Ri03IU5slROnzgSB49FOAbcrtO4ml2p1UPdwb8X1QBJSPKwRMEbXQxGddq1HtOyyayL9Ii5ogMwz8uhxxLym2MlzMUb/KAbI7cJJrRcvwhCGqIyfe932VN4v5a3/FYIHbfmmo8CKDUQmybLB8+LQ+"]) + + sp_initiated_sso_enabled = true + idp_initiated_sso_enabled = true + require_encrypted_saml_assertions = true + + claims_mapping_email_attribute = "email1" + claims_mapping_group_attribute = "groups1" + claims_mapping_lastname_attribute = "lastname1" + claims_mapping_username_attribute = "username1" + claims_mapping_firstname_attribute = "firstname1" + } + sp_sign_on_url = "http://idp.example.com/auth/realms/master/protocol/saml" + sp_sign_out_url = "http://idp.example.com/auth/realms/master/protocol/saml" +} \ No newline at end of file diff --git a/examples/resources/anypoint_idp_saml/test.auto.tfvars b/examples/resources/anypoint_idp_saml/test.auto.tfvars new file mode 100644 index 0000000..befec46 --- /dev/null +++ b/examples/resources/anypoint_idp_saml/test.auto.tfvars @@ -0,0 +1,3 @@ +root_org = "aa1f55d6-213d-4f60-845c-207286484cd1" + + diff --git a/examples/resources/anypoint_idp_saml/test.tf b/examples/resources/anypoint_idp_saml/test.tf new file mode 100644 index 0000000..bb9ff57 --- /dev/null +++ b/examples/resources/anypoint_idp_saml/test.tf @@ -0,0 +1,3 @@ +variable "root_org" { + default = "xx1f55d6-213d-4f60-845c-207286484cd1" +} diff --git a/go.mod b/go.mod index 02a757b..e3d8a81 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/hashicorp/hcl/v2 v2.11.1 // indirect github.com/hashicorp/terraform-plugin-sdk/v2 v2.9.0 github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect - github.com/iancoleman/strcase v0.2.0 // indirect + github.com/iancoleman/strcase v0.2.0 github.com/mattn/go-colorable v0.1.12 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect @@ -21,6 +21,7 @@ require ( github.com/mulesoft-consulting/anypoint-client-go/authorization v0.2.0 github.com/mulesoft-consulting/anypoint-client-go/dlb v0.2.0 github.com/mulesoft-consulting/anypoint-client-go/env v0.1.0 + github.com/mulesoft-consulting/anypoint-client-go/idp v0.1.2 github.com/mulesoft-consulting/anypoint-client-go/org v0.3.0 github.com/mulesoft-consulting/anypoint-client-go/role v0.1.0 github.com/mulesoft-consulting/anypoint-client-go/rolegroup v0.1.0 diff --git a/go.sum b/go.sum index 1a66fba..d779552 100644 --- a/go.sum +++ b/go.sum @@ -279,6 +279,14 @@ github.com/mulesoft-consulting/anypoint-client-go/dlb v0.2.0 h1:hQhOicfFiuutku1o github.com/mulesoft-consulting/anypoint-client-go/dlb v0.2.0/go.mod h1:ftj9Trt79sh7B5bcOiSGlFC/AtDXUabSj0pC7ktUtck= github.com/mulesoft-consulting/anypoint-client-go/env v0.1.0 h1:W3PbikRBYoDE9GM1gAZdHn/W0ImGyaUWZOIZ4ehhhJ0= github.com/mulesoft-consulting/anypoint-client-go/env v0.1.0/go.mod h1:IOpP0enauRkNvlZlACRPrqjmjUMB+mpUMXunUr58wzo= +github.com/mulesoft-consulting/anypoint-client-go/idp v0.0.1 h1:OonJpzvcKj39rM+GFLkxPSqmfCfI26qke0mJNH8AqH4= +github.com/mulesoft-consulting/anypoint-client-go/idp v0.0.1/go.mod h1:J5AmL4bkfDFgYN1h4UWSKTlKPMZo8YDSDaZgXPBoGy0= +github.com/mulesoft-consulting/anypoint-client-go/idp v0.1.0 h1:U7xgR2JEmcs9Ou5O8Zg3ELOLkwZY1uxS7goJ2opEVZA= +github.com/mulesoft-consulting/anypoint-client-go/idp v0.1.0/go.mod h1:J5AmL4bkfDFgYN1h4UWSKTlKPMZo8YDSDaZgXPBoGy0= +github.com/mulesoft-consulting/anypoint-client-go/idp v0.1.1 h1:zY/j6Ysb7d82TgY4ahEZhWrPTV8tk4TlmsZ9w8wTWE8= +github.com/mulesoft-consulting/anypoint-client-go/idp v0.1.1/go.mod h1:J5AmL4bkfDFgYN1h4UWSKTlKPMZo8YDSDaZgXPBoGy0= +github.com/mulesoft-consulting/anypoint-client-go/idp v0.1.2 h1:UHTw39+JWekWc+0mO6BSupfECOdddQ9mlT72hY/lH2o= +github.com/mulesoft-consulting/anypoint-client-go/idp v0.1.2/go.mod h1:J5AmL4bkfDFgYN1h4UWSKTlKPMZo8YDSDaZgXPBoGy0= github.com/mulesoft-consulting/anypoint-client-go/org v0.3.0 h1:uIVwClYi77y2qEFCkK053OCnpkp7XUq9auS1SJfXLjE= github.com/mulesoft-consulting/anypoint-client-go/org v0.3.0/go.mod h1:j5FY31MM+wC2EgKQWXXoQQHduo42O4r2ObbntquyBdk= github.com/mulesoft-consulting/anypoint-client-go/role v0.1.0 h1:caGOx/XqO1Mtf2ZSA8gJv10eTkMGEUrq2H24//mUUJo=