diff --git a/auth0/provider.go b/auth0/provider.go index 55411fdd..7d226cd6 100644 --- a/auth0/provider.go +++ b/auth0/provider.go @@ -22,14 +22,24 @@ func Provider() *schema.Provider { DefaultFunc: schema.EnvDefaultFunc("AUTH0_DOMAIN", nil), }, "client_id": { - Type: schema.TypeString, - Required: true, - DefaultFunc: schema.EnvDefaultFunc("AUTH0_CLIENT_ID", nil), + Type: schema.TypeString, + Optional: true, + DefaultFunc: schema.EnvDefaultFunc("AUTH0_CLIENT_ID", nil), + RequiredWith: []string{"client_secret"}, + ConflictsWith: []string{"api_token"}, }, "client_secret": { - Type: schema.TypeString, - Required: true, - DefaultFunc: schema.EnvDefaultFunc("AUTH0_CLIENT_SECRET", nil), + Type: schema.TypeString, + Optional: true, + DefaultFunc: schema.EnvDefaultFunc("AUTH0_CLIENT_SECRET", nil), + RequiredWith: []string{"client_id"}, + ConflictsWith: []string{"api_token"}, + }, + "api_token": { + Type: schema.TypeString, + Optional: true, + DefaultFunc: schema.EnvDefaultFunc("AUTH0_API_TOKEN", nil), + ConflictsWith: []string{"client_id", "client_secret"}, }, "debug": { Type: schema.TypeBool, @@ -82,11 +92,6 @@ func Provider() *schema.Provider { // client is stored and passed into the subsequent resources as the meta parameter. func ConfigureProvider(terraformVersion string) func(data *schema.ResourceData) (interface{}, error) { return func(data *schema.ResourceData) (interface{}, error) { - domain := data.Get("domain").(string) - id := data.Get("client_id").(string) - secret := data.Get("client_secret").(string) - debug := data.Get("debug").(bool) - providerVersion := version.ProviderVersion sdkVersion := auth0.Version terraformSDKVersion := meta.SDKVersionString() @@ -99,9 +104,21 @@ func ConfigureProvider(terraformVersion string) func(data *schema.ResourceData) terraformVersion, ) - return management.New( - domain, - management.WithClientCredentials(id, secret), + domain := data.Get("domain").(string) + debug := data.Get("debug").(bool) + clientID := data.Get("client_id").(string) + clientSecret := data.Get("client_secret").(string) + apiToken := data.Get("api_token").(string) + + authenticationOption := management.WithStaticToken(apiToken) + // if api_token is not specified, authenticate with client ID and client secret. + // This is safe because of the provider schema. + if apiToken == "" { + authenticationOption = management.WithClientCredentials(clientID, clientSecret) + } + + return management.New(domain, + authenticationOption, management.WithDebug(debug), management.WithUserAgent(userAgent), ) diff --git a/auth0/provider_test.go b/auth0/provider_test.go index 1e154e6f..1d7f6361 100644 --- a/auth0/provider_test.go +++ b/auth0/provider_test.go @@ -1,7 +1,10 @@ package auth0 import ( + "errors" "os" + "sort" + "strings" "testing" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" @@ -71,3 +74,80 @@ func TestProvider_debugDefaults(t *testing.T) { } } } + +func TestProvider_configValidation(t *testing.T) { + testCases := []struct { + name string + resourceConfig map[string]interface{} + expectedErrors []error + }{ + { + name: "missing client id", + resourceConfig: map[string]interface{}{"domain": "test", "client_secret": "test"}, + expectedErrors: []error{errors.New("\"client_secret\": all of `client_id,client_secret` must be specified")}, + }, + { + name: "missing client secret", + resourceConfig: map[string]interface{}{"domain": "test", "client_id": "test"}, + expectedErrors: []error{errors.New("\"client_id\": all of `client_id,client_secret` must be specified")}, + }, + { + name: "conflicting auth0 client and management token without domain", + resourceConfig: map[string]interface{}{"client_id": "test", "client_secret": "test", "api_token": "test"}, + expectedErrors: []error{ + errors.New("\"domain\": required field is not set"), + errors.New("\"client_id\": conflicts with api_token"), + errors.New("\"client_secret\": conflicts with api_token"), + errors.New("\"api_token\": conflicts with client_id"), + }, + }, + { + name: "valid auth0 client", + resourceConfig: map[string]interface{}{"domain": "valid_domain", "client_id": "test", "client_secret": "test"}, + expectedErrors: nil, + }, + { + name: "valid auth0 token", + resourceConfig: map[string]interface{}{"domain": "valid_domain", "api_token": "test"}, + expectedErrors: nil, + }, + } + + originalEnviroment := os.Environ() + os.Clearenv() + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + c := terraform.NewResourceConfigRaw(test.resourceConfig) + p := Provider() + + _, errs := p.Validate(c) + assertErrorsSliceEqual(t, test.expectedErrors, errs) + }) + } + + for _, e := range originalEnviroment { + environmentPair := strings.Split(e, "=") + os.Setenv(environmentPair[0], environmentPair[1]) + } +} + +func sortErrors(errs []error) { + sort.Slice(errs, func(i, j int) bool { + return errs[i].Error() < errs[j].Error() + }) +} + +func assertErrorsSliceEqual(t *testing.T, expected, actual []error) { + if len(expected) != len(actual) { + t.Fatalf("actual did not match expected. len(expected) != len(actual). expected: %v, actual: %v", expected, actual) + } + + sortErrors(expected) + sortErrors(actual) + + for i := range expected { + if expected[i].Error() != actual[i].Error() { + t.Fatalf("actual did not match expected. expected[%d] != actual[%d]. expected: %v, actual: %v", i, i, expected, actual) + } + } +} diff --git a/docs/index.md b/docs/index.md index 09394103..fa098a39 100644 --- a/docs/index.md +++ b/docs/index.md @@ -27,8 +27,12 @@ provider "auth0" { ## Argument Reference * `domain` - (Required) Your Auth0 domain name. It can also be sourced from the `AUTH0_DOMAIN` environment variable. -* `client_id` - (Required) Your Auth0 client ID. It can also be sourced from the `AUTH0_CLIENT_ID` environment variable. -* `client_secret` - (Required) Your Auth0 client secret. It can also be sourced from the `AUTH0_CLIENT_SECRET` environment variable. +* `client_id` - (Optional) Your Auth0 client ID. It can also be sourced from the `AUTH0_CLIENT_ID` environment variable. +* `client_secret` - (Optional) Your Auth0 client secret. It can also be sourced from the `AUTH0_CLIENT_SECRET` environment variable. +* `api_token` - (Optional) Your Auth0 [management api access token](https://auth0.com/docs/security/tokens/access-tokens/management-api-access-tokens). + It can also be sourced from the `AUTH0_API_TOKEN` environment variable. Can be + used instead of `client_id` + `client_secret`. If both are specified, + `management_token` will be used over `client_id` + `client_secret` fields. * `debug` - (Optional) Indicates whether or not to turn on debug mode. ## Environment Variables @@ -51,3 +55,4 @@ $ terraform plan ## Importing resources To import Auth0 resources, you will need to know their id. You can use the [Auth0 API Explorer](https://auth0.com/docs/api/management/v2) to easily find your resource id. +