diff --git a/Gopkg.lock b/Gopkg.lock index 39944992..81109714 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -359,8 +359,8 @@ ".", "management" ] - revision = "6443f7f784cfda5477010d9b1c9ce42caf6280b2" - version = "v1.0.0-beta.6" + revision = "1a19d7bd18347bccc4fdd49baa936b2febbfd312" + version = "v1.0.0-beta.8" [[projects]] branch = "master" @@ -504,6 +504,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "710e99e73075c8a8ea7a15124bcf2d3cc984f882235db6f32791897b93e8b442" + inputs-digest = "39a33dbbc7e050861c90b25c2f155e06c70614b420558778292aaff36d9d3ab4" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 5ccc70e2..74232136 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -4,7 +4,7 @@ [[constraint]] name = "github.com/yieldr/go-auth0" - version = "v1.0.0-beta.6" + version = "v1.0.0-beta.8" [prune] go-tests = true diff --git a/auth0/resource_auth0_client.go b/auth0/resource_auth0_client.go index 65813045..243b18ad 100644 --- a/auth0/resource_auth0_client.go +++ b/auth0/resource_auth0_client.go @@ -1,6 +1,8 @@ package auth0 import ( + "strconv" + "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" auth0 "github.com/yieldr/go-auth0" @@ -149,7 +151,227 @@ func newClient() *schema.Resource { Type: schema.TypeList, Optional: true, MaxItems: 1, - Elem: &schema.Schema{Type: schema.TypeMap}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "aws": { + Type: schema.TypeMap, + Optional: true, + }, + "azure_blob": { + Type: schema.TypeMap, + Optional: true, + }, + "azure_sb": { + Type: schema.TypeMap, + Optional: true, + }, + "rms": { + Type: schema.TypeMap, + Optional: true, + }, + "mscrm": { + Type: schema.TypeMap, + Optional: true, + }, + "slack": { + Type: schema.TypeMap, + Optional: true, + }, + "sentry": { + Type: schema.TypeMap, + Optional: true, + }, + "box": { + Type: schema.TypeMap, + Optional: true, + }, + "cloudbees": { + Type: schema.TypeMap, + Optional: true, + }, + "concur": { + Type: schema.TypeMap, + Optional: true, + }, + "dropbox": { + Type: schema.TypeMap, + Optional: true, + }, + "echosign": { + Type: schema.TypeMap, + Optional: true, + }, + "egnyte": { + Type: schema.TypeMap, + Optional: true, + }, + "firebase": { + Type: schema.TypeMap, + Optional: true, + }, + "newrelic": { + Type: schema.TypeMap, + Optional: true, + }, + "office365": { + Type: schema.TypeMap, + Optional: true, + }, + "salesforce": { + Type: schema.TypeMap, + Optional: true, + }, + "salesforce_api": { + Type: schema.TypeMap, + Optional: true, + }, + "salesforce_sandbox_api": { + Type: schema.TypeMap, + Optional: true, + }, + "samlp": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "audience": { + Type: schema.TypeString, + Optional: true, + }, + "recipient": { + Type: schema.TypeString, + Optional: true, + }, + "create_upn_claim": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "passthrough_claims_with_no_mapping": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "map_unknown_claims_as_is": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "map_identities": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "signature_algorithm": { + Type: schema.TypeString, + Optional: true, + Default: "rsa-sha1", + }, + "digest_algorithm": { + Type: schema.TypeString, + Optional: true, + Default: "sha1", + }, + "destination": { + Type: schema.TypeString, + Optional: true, + }, + "lifetime_in_seconds": { + Type: schema.TypeInt, + Optional: true, + Default: 3600, + }, + "sign_response": { + Type: schema.TypeBool, + Optional: true, + }, + "typed_attributes": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "include_attribute_name_format": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "name_identifier_format": { + Type: schema.TypeString, + Optional: true, + Default: "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", + }, + "authn_context_class_ref": { + Type: schema.TypeString, + Optional: true, + }, + "binding": { + Type: schema.TypeString, + Optional: true, + }, + "mappings": { + Type: schema.TypeMap, + Optional: true, + Elem: schema.TypeString, + }, + "logout": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "callback": { + Type: schema.TypeString, + Optional: true, + }, + "slo_enabled": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, + "name_identifier_probes": { + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + }, + }, + }, + "layer": { + Type: schema.TypeMap, + Optional: true, + }, + "sap_api": { + Type: schema.TypeMap, + Optional: true, + }, + "sharepoint": { + Type: schema.TypeMap, + Optional: true, + }, + "springcm": { + Type: schema.TypeMap, + Optional: true, + }, + "wams": { + Type: schema.TypeMap, + Optional: true, + }, + "wsfed": { + Type: schema.TypeMap, + Optional: true, + }, + "zendesk": { + Type: schema.TypeMap, + Optional: true, + }, + "zoom": { + Type: schema.TypeMap, + Optional: true, + }, + }, + }, }, "token_endpoint_auth_method": { Type: schema.TypeString, @@ -311,65 +533,95 @@ func buildClient(d *schema.ResourceData) *management.Client { TokenEndpointAuthMethod: String(d, "token_endpoint_auth_method"), } - if v, ok := d.GetOk("jwt_configuration"); ok { - vL := v.([]interface{}) - for _, v := range vL { - jwtConfiguration := v.(map[string]interface{}) - - c.JWTConfiguration = &management.ClientJWTConfiguration{ - LifetimeInSeconds: auth0.Int(jwtConfiguration["lifetime_in_seconds"].(int)), - Scopes: jwtConfiguration["scopes"], - Algorithm: auth0.String(jwtConfiguration["alg"].(string)), - } - } - } + List(d, "jwt_configuration").First(func(v interface{}) { - if v, ok := d.GetOk("encryption_key"); ok { - c.EncryptionKey = make(map[string]string) + m := v.(map[string]interface{}) - for _, item := range v.([]interface{}) { - for key, val := range item.(map[string]string) { - c.EncryptionKey[key] = val - } + c.JWTConfiguration = &management.ClientJWTConfiguration{ + LifetimeInSeconds: auth0.Int(m["lifetime_in_seconds"].(int)), + Algorithm: auth0.String(m["alg"].(string)), + Scopes: m["scopes"], } - } + }) + + List(d, "encryption_key").First(func(v interface{}) { + c.EncryptionKey = v.(map[string]string) + }) - if v, ok := d.GetOk("addons"); ok { + List(d, "addons").First(func(v interface{}) { c.Addons = make(map[string]interface{}) - for _, item := range v.([]interface{}) { - for key, val := range item.(map[string]interface{}) { - c.Addons[key] = val + for addonKey, addonValue := range v.(map[string]interface{}) { + + switch addonKey { + + case "samlp": + for _, v := range addonValue.([]interface{}) { + + addon := v.(map[string]interface{}) + + if len(addon) > 0 { + c.Addons[addonKey] = buildClientAddon(addon) + } + } + + default: + addon := addonValue.(map[string]interface{}) + if len(addon) > 0 { + c.Addons[addonKey] = buildClientAddon(addon) + } } } - } + }) - if v, ok := d.GetOk("client_metadata"); ok { + List(d, "client_metadata").First(func(v interface{}) { + c.ClientMetadata = v.(map[string]string) + }) - c.ClientMetadata = make(map[string]string) + List(d, "mobile").First(func(v interface{}) { - for key, val := range v.(map[string]interface{}) { - c.ClientMetadata[key] = val.(string) + c.Mobile = make(map[string]interface{}) + + for mobileKey, mobileValues := range v.(map[string]interface{}) { + + for _, mobile := range mobileValues.([]interface{}) { + c.Mobile[mobileKey] = mobile + } } + }) - } + return c +} - if v, ok := d.GetOk("mobile"); ok { +func buildClientAddon(d map[string]interface{}) map[string]interface{} { - c.Mobile = make(map[string]interface{}) + addon := make(map[string]interface{}) - for _, item := range v.([]interface{}) { + for key, value := range d { - for key, val := range item.(map[string]interface{}) { + switch v := value.(type) { - for _, valItem := range val.([]interface{}) { - c.Mobile[key] = valItem - } + case string: + if i, err := strconv.ParseInt(v, 10, 64); err == nil { + addon[key] = i + } else if f, err := strconv.ParseFloat(v, 64); err == nil { + addon[key] = f + } else if b, err := strconv.ParseBool(v); err == nil { + addon[key] = b + } else { + addon[key] = v } - } - } + case map[string]interface{}: + addon[key] = buildClientAddon(v) - return c + case []interface{}: + addon[key] = v + + default: + addon[key] = v + } + } + return addon } diff --git a/auth0/resource_auth0_client_test.go b/auth0/resource_auth0_client_test.go index d9f3eeba..a057ff93 100644 --- a/auth0/resource_auth0_client_test.go +++ b/auth0/resource_auth0_client_test.go @@ -18,6 +18,11 @@ func TestAccClient(t *testing.T) { Config: testAccClientConfig, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("auth0_client.my_client", "name", "Application - Acceptance Test"), + resource.TestCheckResourceAttr("auth0_client.my_client", "addons.0.firebase.client_email", "john.doe@example.com"), + resource.TestCheckResourceAttr("auth0_client.my_client", "addons.0.firebase.lifetime_in_seconds", "1"), + resource.TestCheckResourceAttr("auth0_client.my_client", "addons.0.samlp.0.audience", "https://example.com/saml"), + resource.TestCheckResourceAttr("auth0_client.my_client", "addons.0.samlp.0.map_identities", "false"), + resource.TestCheckResourceAttr("auth0_client.my_client", "addons.0.samlp.0.name_identifier_format", "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"), ), }, }, @@ -47,6 +52,29 @@ resource "auth0_client" "my_client" { foo = "bar" } } + addons = { + firebase = { + client_email = "john.doe@example.com" + lifetime_in_seconds = 1 + private_key = "wer" + private_key_id = "qwreerwerwe" + }, + samlp = { + audience = "https://example.com/saml", + mappings = { + email = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", + name = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" + }, + create_upn_claim = false, + passthrough_claims_with_no_mapping = false, + map_unknown_claims_as_is = false, + map_identities = false, + name_identifier_format = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", + name_identifier_probes = [ + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" + ] + } + } mobile = { ios = { team_id = "9JA89QQLNQ" diff --git a/auth0/util.go b/auth0/util.go index 1ecfe6c4..fd3aecc6 100644 --- a/auth0/util.go +++ b/auth0/util.go @@ -1,11 +1,40 @@ package auth0 import ( - "github.com/hashicorp/terraform/helper/schema" auth0 "github.com/yieldr/go-auth0" ) -func String(d *schema.ResourceData, key string) (s *string) { +// Data generalises schema.ResourceData so that we can reuse the accessor +// methods we define below. +type Data interface { + + // HasChange reports whether or not the given key has been changed. + HasChange(key string) bool + + // GetOkExists returns the data for a given key and whether or not the key + // has been set to a non-zero value. This is only useful for determining + // if boolean attributes have been set, if they are Optional but do not + // have a Default value. + GetOkExists(key string) (interface{}, bool) +} + +func MapData(m map[string]interface{}) Data { + return mapData(m) +} + +type mapData map[string]interface{} + +func (md mapData) HasChange(key string) bool { + _, ok := md[key] + return ok +} + +func (md mapData) GetOkExists(key string) (interface{}, bool) { + v, ok := md[key] + return v, ok +} + +func String(d Data, key string) (s *string) { if d.HasChange(key) { v, ok := d.GetOkExists(key) if ok { @@ -23,7 +52,7 @@ func MapString(m map[string]interface{}, key string) (s *string) { return } -func Int(d *schema.ResourceData, key string) (i *int) { +func Int(d Data, key string) (i *int) { if d.HasChange(key) { v, ok := d.GetOkExists(key) if ok { @@ -41,7 +70,7 @@ func MapInt(m map[string]interface{}, key string) (i *int) { return } -func Bool(d *schema.ResourceData, key string) (b *bool) { +func Bool(d Data, key string) (b *bool) { if d.HasChange(key) { v, ok := d.GetOkExists(key) if ok { @@ -59,7 +88,7 @@ func MapBool(m map[string]interface{}, key string) (b *bool) { return } -func Slice(d *schema.ResourceData, key string) (s []interface{}) { +func Slice(d Data, key string) (s []interface{}) { if d.HasChange(key) { v, ok := d.GetOkExists(key) if ok { @@ -69,7 +98,7 @@ func Slice(d *schema.ResourceData, key string) (s []interface{}) { return } -func Map(d *schema.ResourceData, key string) (m map[string]interface{}) { +func Map(d Data, key string) (m map[string]interface{}) { if d.HasChange(key) { v, ok := d.GetOkExists(key) if ok { @@ -78,3 +107,30 @@ func Map(d *schema.ResourceData, key string) (m map[string]interface{}) { } return } + +type Iterator struct { + i []interface{} +} + +func (i *Iterator) All(f func(key int, value interface{})) { + for key, value := range i.i { + f(key, value) + } +} + +func (i *Iterator) First(f func(value interface{})) { + for _, value := range i.i { + f(value) + return + } +} + +func List(d Data, key string) *Iterator { + if d.HasChange(key) { + v, ok := d.GetOkExists(key) + if ok { + return &Iterator{v.([]interface{})} + } + } + return &Iterator{} +} diff --git a/example/client/main.tf b/example/client/main.tf index ba47ef2a..350a29fb 100644 --- a/example/client/main.tf +++ b/example/client/main.tf @@ -3,7 +3,7 @@ provider "auth0" {} resource "auth0_client" "my_app_client" { name = "Example Application (Managed by Terraform)" description = "Example Application Loooooong Description" - app_type = "non_interactive" + app_type = "regular_web" is_first_party = true oidc_conformant = false callbacks = ["https://example.com/callback"] @@ -16,4 +16,31 @@ resource "auth0_client" "my_app_client" { secret_encoded = true alg = "RS256" } + + custom_login_page_on = "true" + + addons = { + firebase = { + client_email = "wer" + lifetime_in_seconds = 1 + private_key = "wer" + private_key_id = "qwreerwerwe" + } + + samlp = { + audience = "https://example.com/saml", + mappings = { + email = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", + name = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" + }, + create_upn_claim = false, + passthrough_claims_with_no_mapping = true, + map_unknown_claims_as_is = false, + map_identities = false, + name_identifier_format = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", + name_identifier_probes = [ + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" + ] + } + } } diff --git a/vendor/github.com/yieldr/go-auth0/management/connection.go b/vendor/github.com/yieldr/go-auth0/management/connection.go index c7395e0a..0cf381e3 100644 --- a/vendor/github.com/yieldr/go-auth0/management/connection.go +++ b/vendor/github.com/yieldr/go-auth0/management/connection.go @@ -66,6 +66,7 @@ type ConnectionOptions struct { // Options for password dictionary policy. PasswordDictionary map[string]interface{} `json:"password_dictionary,omitempty"` + APIEnableUsers *bool `json:"api_enable_users,omitempty"` BasicProfile *bool `json:"basic_profile,omitempty"` ExtAdmin *bool `json:"ext_admin,omitempty"` diff --git a/vendor/github.com/yieldr/go-auth0/management/management.go b/vendor/github.com/yieldr/go-auth0/management/management.go index c956dd67..4cf4e698 100644 --- a/vendor/github.com/yieldr/go-auth0/management/management.go +++ b/vendor/github.com/yieldr/go-auth0/management/management.go @@ -86,7 +86,7 @@ func New(domain, clientID, clientSecret string, options ...apiOption) (*Manageme option(m) } - m.http = newClient(domain, clientID, clientSecret) + m.http = newClient(domain, clientID, clientSecret, m.debug) m.Client = NewClientManager(m) m.ClientGrant = NewClientGrantManager(m) diff --git a/vendor/github.com/yieldr/go-auth0/management/user.go b/vendor/github.com/yieldr/go-auth0/management/user.go index 322bc7e6..b9dcaa88 100644 --- a/vendor/github.com/yieldr/go-auth0/management/user.go +++ b/vendor/github.com/yieldr/go-auth0/management/user.go @@ -8,7 +8,7 @@ type User struct { ID *string `json:"user_id,omitempty"` // The connection the user belongs to. - Connection *string `json:"connection"` + Connection *string `json:"connection,omitempty"` // The user's email Email *string `json:"email,omitempty"` diff --git a/vendor/github.com/yieldr/go-auth0/management/utils.go b/vendor/github.com/yieldr/go-auth0/management/utils.go index 2ec11f48..9f2e2704 100644 --- a/vendor/github.com/yieldr/go-auth0/management/utils.go +++ b/vendor/github.com/yieldr/go-auth0/management/utils.go @@ -65,7 +65,7 @@ func wrapDebugClient(c *http.Client) *http.Client { } } -func newClient(domain, clientID, clientSecret string) *http.Client { +func newClient(domain, clientID, clientSecret string, debug bool) *http.Client { cc := &clientcredentials.Config{ ClientID: clientID, @@ -80,5 +80,9 @@ func newClient(domain, clientID, clientSecret string) *http.Client { c = wrapRetryClient(c) c = wrapUserAgentClient(c) + if debug { + c = wrapDebugClient(c) + } + return c }