Skip to content

Commit

Permalink
Add "auth0_clients" data source for listing multiple clients with fil…
Browse files Browse the repository at this point in the history
…tering
  • Loading branch information
devin-brenton committed Nov 19, 2024
1 parent 10b2331 commit a4efad3
Show file tree
Hide file tree
Showing 11 changed files with 1,596 additions and 13 deletions.
47 changes: 47 additions & 0 deletions docs/data-sources/clients.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
page_title: "Data Source: auth0_clients"
description: |-
Data source to retrieve a list of Auth0 application clients with optional filtering.
---

# Data Source: auth0_clients

Data source to retrieve a list of Auth0 application clients with optional filtering.



<!-- schema generated by tfplugindocs -->
## Schema

### Optional

- `app_types` (Set of String) Filter clients by application types.
- `is_first_party` (Boolean) Filter clients by first party status.
- `name_filter` (String) Filter clients by name (partial matches supported).

### Read-Only

- `clients` (List of Object) List of clients matching the filter criteria. (see [below for nested schema](#nestedatt--clients))
- `id` (String) The ID of this resource.

<a id="nestedatt--clients"></a>
### Nested Schema for `clients`

Read-Only:

- `allowed_clients` (List of String)
- `allowed_logout_urls` (List of String)
- `allowed_origins` (List of String)
- `app_type` (String)
- `callbacks` (List of String)
- `client_id` (String)
- `client_metadata` (Map of String)
- `client_secret` (String)
- `description` (String)
- `grant_types` (List of String)
- `is_first_party` (Boolean)
- `is_token_endpoint_ip_header_trusted` (Boolean)
- `name` (String)
- `web_origins` (List of String)


39 changes: 34 additions & 5 deletions internal/acctest/http_recorder.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,27 +108,56 @@ func redactDomain(i *cassette.Interaction, domain string) {
}

func redactSensitiveDataInClient(t *testing.T, i *cassette.Interaction, domain string) {
create := i.Request.URL == "https://"+domain+"/api/v2/clients" &&
baseURL := "https://" + domain + "/api/v2/clients"
urlPath := strings.Split(i.Request.URL, "?")[0] // Strip query params

create := i.Request.URL == baseURL &&
i.Request.Method == http.MethodPost

read := strings.Contains(i.Request.URL, "https://"+domain+"/api/v2/clients/") &&
readList := urlPath == baseURL &&
i.Request.Method == http.MethodGet

readOne := strings.Contains(i.Request.URL, baseURL+"/") &&
!strings.Contains(i.Request.URL, "credentials") &&
i.Request.Method == http.MethodGet

update := strings.Contains(i.Request.URL, "https://"+domain+"/api/v2/clients/") &&
update := strings.Contains(i.Request.URL, baseURL+"/") &&
!strings.Contains(i.Request.URL, "credentials") &&
i.Request.Method == http.MethodPatch

if create || read || update {
if create || readList || readOne || update {
if i.Response.Code == http.StatusNotFound {
return
}

redacted := "[REDACTED]"

// Handle list response
if readList {
var response management.ClientList
err := json.Unmarshal([]byte(i.Response.Body), &response)
require.NoError(t, err)

for _, client := range response.Clients {
client.SigningKeys = []map[string]string{
{"cert": redacted},
}
if client.GetClientSecret() != "" {
client.ClientSecret = &redacted
}
}

responseBody, err := json.Marshal(response)
require.NoError(t, err)
i.Response.Body = string(responseBody)
return
}

// Handle single client response
var client management.Client
err := json.Unmarshal([]byte(i.Response.Body), &client)
require.NoError(t, err)

redacted := "[REDACTED]"
client.SigningKeys = []map[string]string{
{"cert": redacted},
}
Expand Down
18 changes: 10 additions & 8 deletions internal/auth0/client/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ import (
internalValidation "github.com/auth0/terraform-provider-auth0/internal/validation"
)

var ValidAppTypes = []string{

Check warning on line 18 in internal/auth0/client/resource.go

View workflow job for this annotation

GitHub Actions / Checks

exported: exported var ValidAppTypes should have comment or be unexported (revive)
"native", "spa", "regular_web", "non_interactive", "rms",
"box", "cloudbees", "concur", "dropbox", "mscrm", "echosign",
"egnyte", "newrelic", "office365", "salesforce", "sentry",
"sharepoint", "slack", "springcm", "sso_integration", "zendesk", "zoom",
}

// NewResource will return a new auth0_client resource.
func NewResource() *schema.Resource {
return &schema.Resource{
Expand Down Expand Up @@ -53,14 +60,9 @@ func NewResource() *schema.Resource {
Description: "List of audiences/realms for SAML protocol. Used by the wsfed addon.",
},
"app_type": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{
"native", "spa", "regular_web", "non_interactive", "rms",
"box", "cloudbees", "concur", "dropbox", "mscrm", "echosign",
"egnyte", "newrelic", "office365", "salesforce", "sentry",
"sharepoint", "slack", "springcm", "sso_integration", "zendesk", "zoom",
}, false),
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice(ValidAppTypes, false),
Description: "Type of application the client represents. Possible values are: `native`, `spa`, " +
"`regular_web`, `non_interactive`, `sso_integration`. Specific SSO integrations types accepted " +
"as well are: `rms`, `box`, `cloudbees`, `concur`, `dropbox`, `mscrm`, `echosign`, `egnyte`, " +
Expand Down
161 changes: 161 additions & 0 deletions internal/auth0/clients/data_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package clients

import (
"context"
"crypto/sha256"
"fmt"
"strings"

"github.com/auth0/go-auth0/management"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"

"github.com/auth0/terraform-provider-auth0/internal/auth0/client"
"github.com/auth0/terraform-provider-auth0/internal/config"
)

// NewDataSource will return a new auth0_clients data source.
func NewDataSource() *schema.Resource {
return &schema.Resource{
ReadContext: readClientsForDataSource,
Description: "Data source to retrieve a list of Auth0 application clients with optional filtering.",
Schema: map[string]*schema.Schema{
"name_filter": {
Type: schema.TypeString,
Optional: true,
Description: "Filter clients by name (partial matches supported).",
},
"app_types": {
Type: schema.TypeSet,
Optional: true,
Description: "Filter clients by application types.",
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice(client.ValidAppTypes, false),
},
},
"is_first_party": {
Type: schema.TypeBool,
Optional: true,
Description: "Filter clients by first party status.",
},
"clients": {
Type: schema.TypeList,
Computed: true,
Description: "List of clients matching the filter criteria.",
Elem: &schema.Resource{
Schema: CoreClientDataSourceSchema(),
},
},
},
}
}

func CoreClientDataSourceSchema() map[string]*schema.Schema {

Check warning on line 55 in internal/auth0/clients/data_source.go

View workflow job for this annotation

GitHub Actions / Checks

exported: exported function CoreClientDataSourceSchema should have comment or be unexported (revive)
clientSchema := client.NewDataSource().Schema

// Remove unused fields from the client schema

Check failure on line 58 in internal/auth0/clients/data_source.go

View workflow job for this annotation

GitHub Actions / Checks

Comment should end in a period (godot)
fieldsToRemove := []string{
"client_aliases",
"logo_uri",
"oidc_conformant",
"oidc_backchannel_logout_urls",
"organization_usage",
"organization_require_behavior",
"cross_origin_auth",
"cross_origin_loc",
"custom_login_page_on",
"custom_login_page",
"form_template",
"require_pushed_authorization_requests",
"mobile",
"initiate_login_uri",
"native_social_login",
"refresh_token",
"signing_keys",
"encryption_key",
"sso",
"sso_disabled",
"jwt_configuration",
"addons",
"default_organization",
"compliance_level",
"require_proof_of_possession",
"token_endpoint_auth_method",
"signed_request_object",
"client_authentication_methods",
}

for _, field := range fieldsToRemove {
delete(clientSchema, field)
}

return clientSchema
}

func readClientsForDataSource(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
api := meta.(*config.Config).GetAPI()

nameFilter := data.Get("name_filter").(string)
appTypesSet := data.Get("app_types").(*schema.Set)
isFirstParty := data.Get("is_first_party").(bool)

appTypes := make([]string, 0, appTypesSet.Len())
for _, v := range appTypesSet.List() {
appTypes = append(appTypes, v.(string))
}

var clients []*management.Client

params := []management.RequestOption{
management.PerPage(100),
}

if len(appTypes) > 0 {
params = append(params, management.Parameter("app_type", strings.Join(appTypes, ",")))
}
if isFirstParty {
params = append(params, management.Parameter("is_first_party", "true"))
}

var page int
for {
// Add current page parameter

Check failure on line 124 in internal/auth0/clients/data_source.go

View workflow job for this annotation

GitHub Actions / Checks

Comment should end in a period (godot)
params = append(params, management.Page(page))

list, err := api.Client.List(ctx, params...)
if err != nil {
return diag.FromErr(err)
}

for _, client := range list.Clients {
if nameFilter == "" || strings.Contains(client.GetName(), nameFilter) {
clients = append(clients, client)
}
}

if !list.HasNext() {
break
}

// Remove the page parameter and increment for next iteration

Check failure on line 142 in internal/auth0/clients/data_source.go

View workflow job for this annotation

GitHub Actions / Checks

Comment should end in a period (godot)
params = params[:len(params)-1]
page++
}

filterID := generateFilterID(nameFilter, appTypes, isFirstParty)
data.SetId(filterID)

if err := flattenClientList(data, clients); err != nil {
return diag.FromErr(err)
}

return nil
}

func generateFilterID(nameFilter string, appTypes []string, isFirstParty bool) string {
h := sha256.New()
h.Write([]byte(fmt.Sprintf("%s-%v-%v", nameFilter, appTypes, isFirstParty)))
return fmt.Sprintf("clients-%x", h.Sum(nil))
}
Loading

0 comments on commit a4efad3

Please sign in to comment.