From 7e014593ae58c7f001859c979ee041cc6ace9bb5 Mon Sep 17 00:00:00 2001 From: Will Vedder Date: Thu, 27 Jan 2022 16:37:30 -0500 Subject: [PATCH 1/7] Reintroducing client data source that was originally written by Yinzara in PR#363 --- auth0/data_source_auth0_client.go | 53 ++++++++++++++++ auth0/data_source_auth0_client_test.go | 74 +++++++++++++++++++++++ auth0/resource_data_test.go | 42 +++++++++++++ docs/datasources/client.md | 83 ++++++++++++++++++++++++++ 4 files changed, 252 insertions(+) create mode 100644 auth0/data_source_auth0_client.go create mode 100644 auth0/data_source_auth0_client_test.go create mode 100644 docs/datasources/client.md diff --git a/auth0/data_source_auth0_client.go b/auth0/data_source_auth0_client.go new file mode 100644 index 00000000..18a2e0b6 --- /dev/null +++ b/auth0/data_source_auth0_client.go @@ -0,0 +1,53 @@ +package auth0 + +import ( + "errors" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "gopkg.in/auth0.v5" + "gopkg.in/auth0.v5/management" +) + +func newDataClient() *schema.Resource { + clientSchema := newComputedClientSchema() + addOptionalFieldsToSchema(clientSchema, "name", "client_id") + + return &schema.Resource{ + Read: readDataClient, + Schema: clientSchema, + } +} + +func newComputedClientSchema() map[string]*schema.Schema { + clientSchema := datasourceSchemaFromResourceSchema(newClient().Schema) + delete(clientSchema, "client_secret_rotation_trigger") + return clientSchema +} + +func readDataClient(d *schema.ResourceData, m interface{}) error { + clientId := auth0.StringValue(String(d, "client_id")) + if clientId == "" { + name := auth0.StringValue(String(d, "name")) + if name != "" { + api := m.(*management.Management) + clients, err := api.Client.List(management.WithFields("client_id", "name")) + if err != nil { + return err + } + for _, client := range clients.Clients { + if auth0.StringValue(client.Name) == name { + clientId = auth0.StringValue(client.ClientID) + break + } + } + if clientId == "" { + return fmt.Errorf("no client found with 'name' = '%s'", name) + } + } else { + return errors.New("no 'client_id' or 'name' was specified") + } + } + d.SetId(clientId) + return readClient(d, m) +} diff --git a/auth0/data_source_auth0_client_test.go b/auth0/data_source_auth0_client_test.go new file mode 100644 index 00000000..ca683376 --- /dev/null +++ b/auth0/data_source_auth0_client_test.go @@ -0,0 +1,74 @@ +package auth0 + +import ( + "fmt" + "testing" + + "github.com/alexkappa/terraform-provider-auth0/auth0/internal/random" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +const testAccDataClientConfigByName = ` +%v +data auth0_client my_client { + name = "Acceptance Test - {{.random}}" +} +` + +const testAccDataClientConfigById = ` +%v +data auth0_client my_client { + client_id = auth0_client.my_client.client_id +} +` + +func clientDataSourceFields() []string { + fields := clientGlobalDataSourceFields() + fields = append( + fields, + "client_metadata.%", + "client_metadata.foo", + ) + return fields +} + +func TestAccDataClientByName(t *testing.T) { + rand := random.String(6) + + resource.Test(t, resource.TestCase{ + Providers: map[string]terraform.ResourceProvider{ + "auth0": Provider(), + }, + PreventPostDestroyRefresh: true, + Steps: []resource.TestStep{ + { + Config: random.Template(testAccClientConfig, rand), // must initialize resource before reading with data source + }, + { + Config: random.Template(fmt.Sprintf(testAccDataClientConfigByName, testAccClientConfig), rand), + Check: checkDataSourceStateMatchesResourceState("data.auth0_client.my_client", "auth0_client.my_client", clientDataSourceFields()), + }, + }, + }) +} + +func TestAccDataClientById(t *testing.T) { + rand := random.String(6) + + resource.Test(t, resource.TestCase{ + Providers: map[string]terraform.ResourceProvider{ + "auth0": Provider(), + }, + PreventPostDestroyRefresh: true, + Steps: []resource.TestStep{ + { + Config: random.Template(testAccClientConfig, rand), + }, + { + Config: random.Template(fmt.Sprintf(testAccDataClientConfigById, testAccClientConfig), rand), + Check: checkDataSourceStateMatchesResourceState("data.auth0_client.my_client", "auth0_client.my_client", clientDataSourceFields()), + }, + }, + }) +} diff --git a/auth0/resource_data_test.go b/auth0/resource_data_test.go index b37c840d..261f9661 100644 --- a/auth0/resource_data_test.go +++ b/auth0/resource_data_test.go @@ -1,7 +1,11 @@ package auth0 import ( + "errors" + "fmt" "testing" + + "github.com/hashicorp/terraform-plugin-sdk/terraform" ) func TestMapData(t *testing.T) { @@ -71,3 +75,41 @@ func TestIsNil(t *testing.T) { } } } + +func checkDataSourceStateMatchesResourceState(dataSourceName, resourceName string, includeFields []string) func(*terraform.State) error { + return func(s *terraform.State) error { + ds, ok := s.RootModule().Resources[dataSourceName] + if !ok { + return fmt.Errorf("can't find %s in state", dataSourceName) + } + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("can't find %s in state", resourceName) + } + + dsAttr := ds.Primary.Attributes + rsAttr := rs.Primary.Attributes + + errMsg := "" + + for _, k := range includeFields { + if k == "%" { + continue + } + if dsAttr[k] != rsAttr[k] { + // ignore data sources where an empty list is being compared against a null list. + if k[len(k)-1:] == "#" && (dsAttr[k] == "" || dsAttr[k] == "0") && (rsAttr[k] == "" || rsAttr[k] == "0") { + continue + } + errMsg += fmt.Sprintf("%s is %s; want %s\n", k, dsAttr[k], rsAttr[k]) + } + } + + if errMsg != "" { + return errors.New(errMsg) + } + + return nil + } +} diff --git a/docs/datasources/client.md b/docs/datasources/client.md new file mode 100644 index 00000000..b15a2919 --- /dev/null +++ b/docs/datasources/client.md @@ -0,0 +1,83 @@ +--- +layout: "auth0" +page_title: "Data Source: auth0_client" +description: |- +Use this data source to get information about a specific Auth0 Application client by its 'client_id' or 'name' +--- + +# Data Source: auth0_client + +Use this data source to get information about a specific Auth0 Application client by its 'client_id' or 'name' + +## Example Usage + +```hcl +data "auth0_client" "some-client-by-name" { + name = "Name of my Application" +} +data "auth0_client" "some-client-by-id" { + client_id = "abcdefghkijklmnopqrstuvwxyz0123456789" +} +``` + +## Argument Reference + +Arguments accepted by this data source include (exactly one is required): + +- `client_id` - (Optional) String. client_id of the application to retrieve +- `name` - (Optional) String. name of the application to retrieve. Ignored if `client_id` is also specified. + +## Attribute Reference + +* `client_id` - String. ID of the client. +* `client_secret`[1](#client-keys) - String. Secret for the client; keep this private. +* `name` - String. Name of the client. +* `description` - String, Description of the purpose of the client. +* `is_first_party` - Boolean. Indicates whether or not this client is a first-party client. +* `is_token_endpoint_ip_header_trusted` - Boolean +* `oidc_conformant` - Boolean. Indicates whether or not this client will conform to strict OIDC specifications. +* `token_endpoint_auth_method` - String. Defines the requested authentication method for the token endpoint. Options include `none` (public client without a client secret), `client_secret_post` (client uses HTTP POST parameters), `client_secret_basic` (client uses HTTP Basic). +* `app_type` - String. Type of application the client represents. Options include `native`, `spa`, `regular_web`, `non_interactive`, `rms`, `box`, `cloudbees`, `concur`, `dropbox`, `mscrm`, `echosign`, `egnyte`, `newrelic`, `office365`, `salesforce`, `sentry`, `sharepoint`, `slack`, `springcm`, `zendesk`, `zoom`. +* `logo_uri` - String. URL of the logo for the client. Recommended size is 150px x 150px. If none is set, the default badge for the application type will be shown. +* `is_first_party` - Boolean. Indicates whether or not this client is a first-party client. +* `is_token_endpoint_ip_header_trusted` - Boolean. Indicates whether or not the token endpoint IP header is trusted. +* `oidc_conformant` - Boolean. Indicates whether or not this client will conform to strict OIDC specifications. +* `callbacks` - List(String). URLs that Auth0 may call back to after a user authenticates for the client. Make sure to specify the protocol (https://) otherwise the callback may fail in some cases. With the exception of custom URI schemes for native clients, all callbacks should use protocol https://. +* `allowed_logout_urls` - List(String). URLs that Auth0 may redirect to after logout. +* `grant_types` - List(String). Types of grants that this client is authorized to use. +* `allowed_origins` - List(String). URLs that represent valid origins for cross-origin resource sharing. By default, all your callback URLs will be allowed. +* `web_origins` - List(String). URLs that represent valid web origins for use with web message response mode. +* `jwt_configuration` - List(Resource). Configuration settings for the JWTs issued for this client. For details, see [JWT Configuration](#jwt-attribute). +* `refresh_token` - List(Resource). Configuration settings for the refresh tokens issued for this client. For details, see [Refresh Token Configuration](#refresh-token-attribute). +* `encryption_key` - Map(String). +* `sso` - Boolean. Indicates whether or not the client should use Auth0 rather than the IdP to perform Single Sign-On (SSO). True = Use Auth0. +* `sso_disabled` - Boolean. Indicates whether or not SSO is disabled. +* `cross_origin_auth` - Boolean. Indicates whether or not the client can be used to make cross-origin authentication requests. +* `cross_origin_loc` - String. URL for the location on your site where the cross-origin verification takes place for the cross-origin auth flow. Used when performing auth in your own domain instead of through the Auth0-hosted login page. +* `custom_login_page_on` - Boolean. Indicates whether or not a custom login page is to be used. +* `custom_login_page` - String. Content of the custom login page. + +### JWT Attribute + +`jwt_configuration` outputs the following attributes: + +* `lifetime_in_seconds` - Integer. Number of seconds during which the JWT will be valid. +* `secret_encoded` - Boolean. Indicates whether or not the client secret is base64 encoded. +* `scopes` - Map(String). Permissions (scopes) included in JWTs. +* `alg` - String. Algorithm used to sign JWTs. + +### Refresh Token Attribute + +`refresh_token` outputs the following attributes: + +* `rotation_type` - String. Options include `rotating`, `non-rotating`. When `rotating`, exchanging a refresh token will cause a new refresh token to be issued and the existing token will be invalidated. This allows for automatic detection of token reuse if the token is leaked. +* `leeway` - Integer. The amount of time in seconds in which a refresh token may be reused without trigging reuse detection. +* `expiration_type` - (Optional unless `rotation_type` is `rotating`) String. Options include `expiring`, `non-expiring`. Whether a refresh token will expire based on an absolute lifetime, after which the token can no longer be used. If rotation is `rotating`, this must be set to `expiring`. +* `token_lifetime` - Integer. The absolute lifetime of a refresh token in seconds. +* `infinite_idle_token_lifetime` - Boolean, (Default=false) Whether or not inactive refresh tokens should be remain valid indefinitely. +* `infinite_token_lifetime` - Boolean, (Default=false) Whether or not refresh tokens should remain valid indefinitely. If false, `token_lifetime` should also be set +* `idle_token_lifetime` - Integer. The time in seconds after which inactive refresh tokens will expire. + +### Client keys + +To access the `client_secret` attribute you need to add the `read:client_keys` scope to the Terraform client. Otherwise, the attribute will contain an empty string. \ No newline at end of file From 9ec55d313a590326a65bbdbd21d33bbe638bb32b Mon Sep 17 00:00:00 2001 From: Will Vedder Date: Thu, 3 Feb 2022 15:29:29 -0500 Subject: [PATCH 2/7] Tweaking and re-integrating client data source back into provider --- auth0/data_source_auth0_client.go | 43 +++++----- auth0/data_source_auth0_client_test.go | 113 ++++++++++++++++++++----- auth0/provider.go | 3 + 3 files changed, 119 insertions(+), 40 deletions(-) diff --git a/auth0/data_source_auth0_client.go b/auth0/data_source_auth0_client.go index 18a2e0b6..e9960992 100644 --- a/auth0/data_source_auth0_client.go +++ b/auth0/data_source_auth0_client.go @@ -27,27 +27,28 @@ func newComputedClientSchema() map[string]*schema.Schema { func readDataClient(d *schema.ResourceData, m interface{}) error { clientId := auth0.StringValue(String(d, "client_id")) - if clientId == "" { - name := auth0.StringValue(String(d, "name")) - if name != "" { - api := m.(*management.Management) - clients, err := api.Client.List(management.WithFields("client_id", "name")) - if err != nil { - return err - } - for _, client := range clients.Clients { - if auth0.StringValue(client.Name) == name { - clientId = auth0.StringValue(client.ClientID) - break - } - } - if clientId == "" { - return fmt.Errorf("no client found with 'name' = '%s'", name) - } - } else { - return errors.New("no 'client_id' or 'name' was specified") + if clientId != "" { + d.SetId(clientId) + return readClient(d, m) + } + + //If not provided ID, perform looking of client by name + name := auth0.StringValue(String(d, "name")) + if name == "" { + return errors.New("no 'client_id' or 'name' was specified") + } + + api := m.(*management.Management) + clients, err := api.Client.List(management.IncludeFields("client_id", "name")) + if err != nil { + return err + } + for _, client := range clients.Clients { + if auth0.StringValue(client.Name) == name { + clientId = auth0.StringValue(client.ClientID) + d.SetId(clientId) + return readClient(d, m) } } - d.SetId(clientId) - return readClient(d, m) + return fmt.Errorf("no client found with 'name' = '%s'", name) } diff --git a/auth0/data_source_auth0_client_test.go b/auth0/data_source_auth0_client_test.go index ca683376..852a8826 100644 --- a/auth0/data_source_auth0_client_test.go +++ b/auth0/data_source_auth0_client_test.go @@ -9,30 +9,96 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/terraform" ) +const testAccDataClientConfigInit = ` +resource "auth0_client" "test" { + name = "Acceptance Test - {{.random}}" + description = "Test Application Long Description" + app_type = "non_interactive" + custom_login_page_on = true + is_first_party = true + is_token_endpoint_ip_header_trusted = true + token_endpoint_auth_method = "client_secret_post" + oidc_conformant = true + callbacks = [ "https://example.com/callback" ] + allowed_origins = [ "https://example.com" ] + allowed_clients = [ "https://allowed.example.com" ] + grant_types = [ "authorization_code", "http://auth0.com/oauth/grant-type/password-realm", "implicit", "password", "refresh_token" ] + organization_usage = "deny" + organization_require_behavior = "no_prompt" + allowed_logout_urls = [ "https://example.com" ] + web_origins = [ "https://example.com" ] + jwt_configuration { + lifetime_in_seconds = 300 + secret_encoded = true + alg = "RS256" + scopes = { + foo = "bar" + } + } + client_metadata = { + foo = "zoo" + } + 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" + ] + logout = { + callback = "http://example.com/callback" + slo_enabled = true + } + signing_cert = "-----BEGIN PUBLIC KEY-----\nMIGf...bpP/t3\n+JGNGIRMj1hF1rnb6QIDAQAB\n-----END PUBLIC KEY-----\n" + } + } + refresh_token { + leeway = 42 + token_lifetime = 424242 + rotation_type = "rotating" + expiration_type = "expiring" + infinite_token_lifetime = true + infinite_idle_token_lifetime = false + idle_token_lifetime = 3600 + } + mobile { + ios { + team_id = "9JA89QQLNQ" + app_bundle_identifier = "com.my.bundle.id" + } + } + initiate_login_uri = "https://example.com/login" +} +` + const testAccDataClientConfigByName = ` %v -data auth0_client my_client { +data auth0_client test { name = "Acceptance Test - {{.random}}" } ` const testAccDataClientConfigById = ` %v -data auth0_client my_client { - client_id = auth0_client.my_client.client_id +data auth0_client test { + client_id = auth0_client.test.client_id } ` -func clientDataSourceFields() []string { - fields := clientGlobalDataSourceFields() - fields = append( - fields, - "client_metadata.%", - "client_metadata.foo", - ) - return fields -} - func TestAccDataClientByName(t *testing.T) { rand := random.String(6) @@ -43,11 +109,16 @@ func TestAccDataClientByName(t *testing.T) { PreventPostDestroyRefresh: true, Steps: []resource.TestStep{ { - Config: random.Template(testAccClientConfig, rand), // must initialize resource before reading with data source + Config: random.Template(testAccDataClientConfigInit, rand), // must initialize resource before reading with data source }, { - Config: random.Template(fmt.Sprintf(testAccDataClientConfigByName, testAccClientConfig), rand), - Check: checkDataSourceStateMatchesResourceState("data.auth0_client.my_client", "auth0_client.my_client", clientDataSourceFields()), + Config: random.Template(fmt.Sprintf(testAccDataClientConfigByName, testAccDataClientConfigInit), rand), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.auth0_client.test", "client_id"), + resource.TestCheckResourceAttr("data.auth0_client.test", "name", fmt.Sprintf("Acceptance Test - %v", rand)), + resource.TestCheckResourceAttr("data.auth0_client.test", "app_type", "non_interactive"), + resource.TestCheckNoResourceAttr("data.auth0_client.test", "client_secret_rotation_trigger"), + ), }, }, }) @@ -63,11 +134,15 @@ func TestAccDataClientById(t *testing.T) { PreventPostDestroyRefresh: true, Steps: []resource.TestStep{ { - Config: random.Template(testAccClientConfig, rand), + Config: random.Template(testAccDataClientConfigInit, rand), }, { - Config: random.Template(fmt.Sprintf(testAccDataClientConfigById, testAccClientConfig), rand), - Check: checkDataSourceStateMatchesResourceState("data.auth0_client.my_client", "auth0_client.my_client", clientDataSourceFields()), + Config: random.Template(fmt.Sprintf(testAccDataClientConfigById, testAccDataClientConfigInit), rand), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.auth0_client.test", "id"), + resource.TestCheckResourceAttrSet("data.auth0_client.test", "name"), + resource.TestCheckNoResourceAttr("data.auth0_client.test", "client_secret_rotation_trigger"), + ), }, }, }) diff --git a/auth0/provider.go b/auth0/provider.go index 3dbf23a2..55411fdd 100644 --- a/auth0/provider.go +++ b/auth0/provider.go @@ -68,6 +68,9 @@ func Provider() *schema.Provider { "auth0_action": newAction(), "auth0_trigger_binding": newTriggerBinding(), }, + DataSourcesMap: map[string]*schema.Resource{ + "auth0_client": newDataClient(), + }, } provider.ConfigureFunc = ConfigureProvider(provider.TerraformVersion) From 051e42c578c2368ac4ae73649f0c7b36d2577c27 Mon Sep 17 00:00:00 2001 From: Will Vedder Date: Thu, 3 Feb 2022 15:56:45 -0500 Subject: [PATCH 3/7] Removing unnecessary checkDataSourceStateMatchesResourceState function --- auth0/resource_data_test.go | 42 ------------------------------------- 1 file changed, 42 deletions(-) diff --git a/auth0/resource_data_test.go b/auth0/resource_data_test.go index 261f9661..b37c840d 100644 --- a/auth0/resource_data_test.go +++ b/auth0/resource_data_test.go @@ -1,11 +1,7 @@ package auth0 import ( - "errors" - "fmt" "testing" - - "github.com/hashicorp/terraform-plugin-sdk/terraform" ) func TestMapData(t *testing.T) { @@ -75,41 +71,3 @@ func TestIsNil(t *testing.T) { } } } - -func checkDataSourceStateMatchesResourceState(dataSourceName, resourceName string, includeFields []string) func(*terraform.State) error { - return func(s *terraform.State) error { - ds, ok := s.RootModule().Resources[dataSourceName] - if !ok { - return fmt.Errorf("can't find %s in state", dataSourceName) - } - - rs, ok := s.RootModule().Resources[resourceName] - if !ok { - return fmt.Errorf("can't find %s in state", resourceName) - } - - dsAttr := ds.Primary.Attributes - rsAttr := rs.Primary.Attributes - - errMsg := "" - - for _, k := range includeFields { - if k == "%" { - continue - } - if dsAttr[k] != rsAttr[k] { - // ignore data sources where an empty list is being compared against a null list. - if k[len(k)-1:] == "#" && (dsAttr[k] == "" || dsAttr[k] == "0") && (rsAttr[k] == "" || rsAttr[k] == "0") { - continue - } - errMsg += fmt.Sprintf("%s is %s; want %s\n", k, dsAttr[k], rsAttr[k]) - } - } - - if errMsg != "" { - return errors.New(errMsg) - } - - return nil - } -} From 070c51e65873a9a5a57e0d8b3486c52e1a959d13 Mon Sep 17 00:00:00 2001 From: Will Vedder Date: Thu, 3 Feb 2022 16:08:55 -0500 Subject: [PATCH 4/7] Updating documentation --- docs/datasources/client.md | 63 ++++---------------------------------- 1 file changed, 6 insertions(+), 57 deletions(-) diff --git a/docs/datasources/client.md b/docs/datasources/client.md index b15a2919..c82e8875 100644 --- a/docs/datasources/client.md +++ b/docs/datasources/client.md @@ -2,12 +2,12 @@ layout: "auth0" page_title: "Data Source: auth0_client" description: |- -Use this data source to get information about a specific Auth0 Application client by its 'client_id' or 'name' +Data source to retrieve a specific Auth0 Application client by 'client_id' or 'name' --- # Data Source: auth0_client -Use this data source to get information about a specific Auth0 Application client by its 'client_id' or 'name' +Data source to retrieve a specific Auth0 Application client by 'client_id' or 'name' ## Example Usage @@ -22,62 +22,11 @@ data "auth0_client" "some-client-by-id" { ## Argument Reference -Arguments accepted by this data source include (exactly one is required): +At least one of the following arguments required: -- `client_id` - (Optional) String. client_id of the application to retrieve -- `name` - (Optional) String. name of the application to retrieve. Ignored if `client_id` is also specified. +- `client_id` - (Optional) String. client_id of the application. +- `name` - (Optional) String. Name of the application. Ignored if `client_id` is also specified. ## Attribute Reference -* `client_id` - String. ID of the client. -* `client_secret`[1](#client-keys) - String. Secret for the client; keep this private. -* `name` - String. Name of the client. -* `description` - String, Description of the purpose of the client. -* `is_first_party` - Boolean. Indicates whether or not this client is a first-party client. -* `is_token_endpoint_ip_header_trusted` - Boolean -* `oidc_conformant` - Boolean. Indicates whether or not this client will conform to strict OIDC specifications. -* `token_endpoint_auth_method` - String. Defines the requested authentication method for the token endpoint. Options include `none` (public client without a client secret), `client_secret_post` (client uses HTTP POST parameters), `client_secret_basic` (client uses HTTP Basic). -* `app_type` - String. Type of application the client represents. Options include `native`, `spa`, `regular_web`, `non_interactive`, `rms`, `box`, `cloudbees`, `concur`, `dropbox`, `mscrm`, `echosign`, `egnyte`, `newrelic`, `office365`, `salesforce`, `sentry`, `sharepoint`, `slack`, `springcm`, `zendesk`, `zoom`. -* `logo_uri` - String. URL of the logo for the client. Recommended size is 150px x 150px. If none is set, the default badge for the application type will be shown. -* `is_first_party` - Boolean. Indicates whether or not this client is a first-party client. -* `is_token_endpoint_ip_header_trusted` - Boolean. Indicates whether or not the token endpoint IP header is trusted. -* `oidc_conformant` - Boolean. Indicates whether or not this client will conform to strict OIDC specifications. -* `callbacks` - List(String). URLs that Auth0 may call back to after a user authenticates for the client. Make sure to specify the protocol (https://) otherwise the callback may fail in some cases. With the exception of custom URI schemes for native clients, all callbacks should use protocol https://. -* `allowed_logout_urls` - List(String). URLs that Auth0 may redirect to after logout. -* `grant_types` - List(String). Types of grants that this client is authorized to use. -* `allowed_origins` - List(String). URLs that represent valid origins for cross-origin resource sharing. By default, all your callback URLs will be allowed. -* `web_origins` - List(String). URLs that represent valid web origins for use with web message response mode. -* `jwt_configuration` - List(Resource). Configuration settings for the JWTs issued for this client. For details, see [JWT Configuration](#jwt-attribute). -* `refresh_token` - List(Resource). Configuration settings for the refresh tokens issued for this client. For details, see [Refresh Token Configuration](#refresh-token-attribute). -* `encryption_key` - Map(String). -* `sso` - Boolean. Indicates whether or not the client should use Auth0 rather than the IdP to perform Single Sign-On (SSO). True = Use Auth0. -* `sso_disabled` - Boolean. Indicates whether or not SSO is disabled. -* `cross_origin_auth` - Boolean. Indicates whether or not the client can be used to make cross-origin authentication requests. -* `cross_origin_loc` - String. URL for the location on your site where the cross-origin verification takes place for the cross-origin auth flow. Used when performing auth in your own domain instead of through the Auth0-hosted login page. -* `custom_login_page_on` - Boolean. Indicates whether or not a custom login page is to be used. -* `custom_login_page` - String. Content of the custom login page. - -### JWT Attribute - -`jwt_configuration` outputs the following attributes: - -* `lifetime_in_seconds` - Integer. Number of seconds during which the JWT will be valid. -* `secret_encoded` - Boolean. Indicates whether or not the client secret is base64 encoded. -* `scopes` - Map(String). Permissions (scopes) included in JWTs. -* `alg` - String. Algorithm used to sign JWTs. - -### Refresh Token Attribute - -`refresh_token` outputs the following attributes: - -* `rotation_type` - String. Options include `rotating`, `non-rotating`. When `rotating`, exchanging a refresh token will cause a new refresh token to be issued and the existing token will be invalidated. This allows for automatic detection of token reuse if the token is leaked. -* `leeway` - Integer. The amount of time in seconds in which a refresh token may be reused without trigging reuse detection. -* `expiration_type` - (Optional unless `rotation_type` is `rotating`) String. Options include `expiring`, `non-expiring`. Whether a refresh token will expire based on an absolute lifetime, after which the token can no longer be used. If rotation is `rotating`, this must be set to `expiring`. -* `token_lifetime` - Integer. The absolute lifetime of a refresh token in seconds. -* `infinite_idle_token_lifetime` - Boolean, (Default=false) Whether or not inactive refresh tokens should be remain valid indefinitely. -* `infinite_token_lifetime` - Boolean, (Default=false) Whether or not refresh tokens should remain valid indefinitely. If false, `token_lifetime` should also be set -* `idle_token_lifetime` - Integer. The time in seconds after which inactive refresh tokens will expire. - -### Client keys - -To access the `client_secret` attribute you need to add the `read:client_keys` scope to the Terraform client. Otherwise, the attribute will contain an empty string. \ No newline at end of file +The client data source possesses the same attributes as the `auth0_client` resource, with the exception of `client_secret_rotation_trigger`. Refer to the [auth0_client resource documentation](../resources/client.md) for a list of returned attributes. \ No newline at end of file From 4905251f765268226c01d660dea17530947b4fc8 Mon Sep 17 00:00:00 2001 From: Will Vedder Date: Thu, 3 Feb 2022 16:20:39 -0500 Subject: [PATCH 5/7] Simplifying the newDataClient generator --- auth0/data_source_auth0_client.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/auth0/data_source_auth0_client.go b/auth0/data_source_auth0_client.go index e9960992..ab4cf89b 100644 --- a/auth0/data_source_auth0_client.go +++ b/auth0/data_source_auth0_client.go @@ -10,7 +10,9 @@ import ( ) func newDataClient() *schema.Resource { - clientSchema := newComputedClientSchema() + clientSchema := datasourceSchemaFromResourceSchema(newClient().Schema) + delete(clientSchema, "client_secret_rotation_trigger") + addOptionalFieldsToSchema(clientSchema, "name", "client_id") return &schema.Resource{ @@ -19,12 +21,6 @@ func newDataClient() *schema.Resource { } } -func newComputedClientSchema() map[string]*schema.Schema { - clientSchema := datasourceSchemaFromResourceSchema(newClient().Schema) - delete(clientSchema, "client_secret_rotation_trigger") - return clientSchema -} - func readDataClient(d *schema.ResourceData, m interface{}) error { clientId := auth0.StringValue(String(d, "client_id")) if clientId != "" { From 56121f795a6eaaeaedf37300131ad99dd460a826 Mon Sep 17 00:00:00 2001 From: Will Vedder Date: Fri, 4 Feb 2022 09:30:18 -0500 Subject: [PATCH 6/7] Reusing existing testAccClientConfig client resource configuration --- auth0/data_source_auth0_client_test.go | 86 ++------------------------ 1 file changed, 5 insertions(+), 81 deletions(-) diff --git a/auth0/data_source_auth0_client_test.go b/auth0/data_source_auth0_client_test.go index 852a8826..0eb4952e 100644 --- a/auth0/data_source_auth0_client_test.go +++ b/auth0/data_source_auth0_client_test.go @@ -9,82 +9,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/terraform" ) -const testAccDataClientConfigInit = ` -resource "auth0_client" "test" { - name = "Acceptance Test - {{.random}}" - description = "Test Application Long Description" - app_type = "non_interactive" - custom_login_page_on = true - is_first_party = true - is_token_endpoint_ip_header_trusted = true - token_endpoint_auth_method = "client_secret_post" - oidc_conformant = true - callbacks = [ "https://example.com/callback" ] - allowed_origins = [ "https://example.com" ] - allowed_clients = [ "https://allowed.example.com" ] - grant_types = [ "authorization_code", "http://auth0.com/oauth/grant-type/password-realm", "implicit", "password", "refresh_token" ] - organization_usage = "deny" - organization_require_behavior = "no_prompt" - allowed_logout_urls = [ "https://example.com" ] - web_origins = [ "https://example.com" ] - jwt_configuration { - lifetime_in_seconds = 300 - secret_encoded = true - alg = "RS256" - scopes = { - foo = "bar" - } - } - client_metadata = { - foo = "zoo" - } - 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" - ] - logout = { - callback = "http://example.com/callback" - slo_enabled = true - } - signing_cert = "-----BEGIN PUBLIC KEY-----\nMIGf...bpP/t3\n+JGNGIRMj1hF1rnb6QIDAQAB\n-----END PUBLIC KEY-----\n" - } - } - refresh_token { - leeway = 42 - token_lifetime = 424242 - rotation_type = "rotating" - expiration_type = "expiring" - infinite_token_lifetime = true - infinite_idle_token_lifetime = false - idle_token_lifetime = 3600 - } - mobile { - ios { - team_id = "9JA89QQLNQ" - app_bundle_identifier = "com.my.bundle.id" - } - } - initiate_login_uri = "https://example.com/login" -} -` - const testAccDataClientConfigByName = ` %v data auth0_client test { @@ -109,14 +33,14 @@ func TestAccDataClientByName(t *testing.T) { PreventPostDestroyRefresh: true, Steps: []resource.TestStep{ { - Config: random.Template(testAccDataClientConfigInit, rand), // must initialize resource before reading with data source + Config: random.Template(testAccClientConfig, rand), // must initialize resource before reading with data source }, { - Config: random.Template(fmt.Sprintf(testAccDataClientConfigByName, testAccDataClientConfigInit), rand), + Config: random.Template(fmt.Sprintf(testAccDataClientConfigByName, testAccClientConfig), rand), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet("data.auth0_client.test", "client_id"), resource.TestCheckResourceAttr("data.auth0_client.test", "name", fmt.Sprintf("Acceptance Test - %v", rand)), - resource.TestCheckResourceAttr("data.auth0_client.test", "app_type", "non_interactive"), + resource.TestCheckResourceAttr("data.auth0_client.test", "app_type", "non_interactive"), // Arbitrary property selection resource.TestCheckNoResourceAttr("data.auth0_client.test", "client_secret_rotation_trigger"), ), }, @@ -134,10 +58,10 @@ func TestAccDataClientById(t *testing.T) { PreventPostDestroyRefresh: true, Steps: []resource.TestStep{ { - Config: random.Template(testAccDataClientConfigInit, rand), + Config: random.Template(testAccClientConfig, rand), }, { - Config: random.Template(fmt.Sprintf(testAccDataClientConfigById, testAccDataClientConfigInit), rand), + Config: random.Template(fmt.Sprintf(testAccDataClientConfigById, testAccClientConfig), rand), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet("data.auth0_client.test", "id"), resource.TestCheckResourceAttrSet("data.auth0_client.test", "name"), From b24dd523649c0c439dd6c397ccd09e3a9e8c0d06 Mon Sep 17 00:00:00 2001 From: Will Vedder Date: Fri, 4 Feb 2022 11:27:29 -0500 Subject: [PATCH 7/7] Fixing integration tests after switching back to the testAccClientConfig --- auth0/data_source_auth0_client_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth0/data_source_auth0_client_test.go b/auth0/data_source_auth0_client_test.go index 0eb4952e..4eb53306 100644 --- a/auth0/data_source_auth0_client_test.go +++ b/auth0/data_source_auth0_client_test.go @@ -19,7 +19,7 @@ data auth0_client test { const testAccDataClientConfigById = ` %v data auth0_client test { - client_id = auth0_client.test.client_id + client_id = auth0_client.my_client.client_id } `