diff --git a/docs/auth0_terraform_generate.md b/docs/auth0_terraform_generate.md index 14c899396..8f273d9f5 100644 --- a/docs/auth0_terraform_generate.md +++ b/docs/auth0_terraform_generate.md @@ -33,7 +33,7 @@ auth0 terraform generate [flags] ``` --force Skip confirmation. -o, --output-dir string Output directory for the generated Terraform config files. If not provided, the files will be saved in the current working directory. (default "./") - -r, --resources strings Resource types to generate Terraform config for. If not provided, config files for all available resources will be generated. (default [auth0_action,auth0_attack_protection,auth0_branding,auth0_phone_provider,auth0_client,auth0_client_grant,auth0_connection,auth0_custom_domain,auth0_flow,auth0_flow_vault_connection,auth0_form,auth0_email_provider,auth0_email_template,auth0_guardian,auth0_log_stream,auth0_network_acl,auth0_organization,auth0_pages,auth0_prompt,auth0_prompt_custom_text,auth0_prompt_screen_renderer,auth0_resource_server,auth0_role,auth0_self_service_profile,auth0_tenant,auth0_trigger_actions]) + -r, --resources strings Resource types to generate Terraform config for. If not provided, config files for all available resources will be generated. (default [auth0_action,auth0_attack_protection,auth0_branding,auth0_phone_provider,auth0_client,auth0_client_grant,auth0_connection,auth0_custom_domain,auth0_flow,auth0_flow_vault_connection,auth0_form,auth0_email_provider,auth0_email_template,auth0_guardian,auth0_log_stream,auth0_network_acl,auth0_organization,auth0_pages,auth0_prompt,auth0_prompt_custom_text,auth0_prompt_screen_renderer,auth0_resource_server,auth0_role,auth0_self_service_profile,auth0_tenant,auth0_trigger_actions,auth0_user_attribute_profile]) -v, --tf-version string Terraform version that ought to be used while generating the terraform files for resources. If not provided, 1.5.0 is used by default (default "1.5.0") ``` diff --git a/internal/auth0/auth0.go b/internal/auth0/auth0.go index 583a017c5..438d7828f 100644 --- a/internal/auth0/auth0.go +++ b/internal/auth0/auth0.go @@ -8,67 +8,69 @@ import ( // API mimics `management.Management`s general interface, except it refers to // the interfaces instead of the concrete structs. type API struct { - Action ActionAPI - Anomaly AnomalyAPI - AttackProtection AttackProtectionAPI - Branding BrandingAPI - BrandingTheme BrandingThemeAPI - Client ClientAPI - ClientGrant ClientGrantAPI - Connection ConnectionAPI - CustomDomain CustomDomainAPI - EmailTemplate EmailTemplateAPI - EmailProvider EmailProviderAPI - EventStream EventStreamAPI - Flow FlowAPI - FlowVaultConnection FlowVaultConnectionAPI - Form FormAPI - Log LogAPI - LogStream LogStreamAPI - Organization OrganizationAPI - NetworkACL NetworkACLAPI - Prompt PromptAPI - ResourceServer ResourceServerAPI - Role RoleAPI - Rule RuleAPI - Tenant TenantAPI - User UserAPI - Jobs JobsAPI - SelfServiceProfile SelfServiceProfileAPI + Action ActionAPI + Anomaly AnomalyAPI + AttackProtection AttackProtectionAPI + Branding BrandingAPI + BrandingTheme BrandingThemeAPI + Client ClientAPI + ClientGrant ClientGrantAPI + Connection ConnectionAPI + CustomDomain CustomDomainAPI + EmailTemplate EmailTemplateAPI + EmailProvider EmailProviderAPI + EventStream EventStreamAPI + Flow FlowAPI + FlowVaultConnection FlowVaultConnectionAPI + Form FormAPI + Log LogAPI + LogStream LogStreamAPI + Organization OrganizationAPI + NetworkACL NetworkACLAPI + Prompt PromptAPI + ResourceServer ResourceServerAPI + Role RoleAPI + Rule RuleAPI + Tenant TenantAPI + User UserAPI + Jobs JobsAPI + SelfServiceProfile SelfServiceProfileAPI + UserAttributeProfile UserAttributeProfilesAPI HTTPClient HTTPClientAPI } func NewAPI(m *management.Management) *API { return &API{ - Action: m.Action, - Anomaly: m.Anomaly, - AttackProtection: m.AttackProtection, - Branding: m.Branding, - BrandingTheme: m.BrandingTheme, - Client: m.Client, - ClientGrant: m.ClientGrant, - Connection: m.Connection, - CustomDomain: m.CustomDomain, - EmailTemplate: m.EmailTemplate, - EmailProvider: m.EmailProvider, - EventStream: m.EventStream, - Flow: m.Flow, - FlowVaultConnection: m.Flow.Vault, - Form: m.Form, - Log: m.Log, - LogStream: m.LogStream, - Organization: m.Organization, - NetworkACL: m.NetworkACL, - Prompt: m.Prompt, - ResourceServer: m.ResourceServer, - Role: m.Role, - Rule: m.Rule, - Tenant: m.Tenant, - User: m.User, - Jobs: m.Job, - SelfServiceProfile: m.SelfServiceProfile, - HTTPClient: m, + Action: m.Action, + Anomaly: m.Anomaly, + AttackProtection: m.AttackProtection, + Branding: m.Branding, + BrandingTheme: m.BrandingTheme, + Client: m.Client, + ClientGrant: m.ClientGrant, + Connection: m.Connection, + CustomDomain: m.CustomDomain, + EmailTemplate: m.EmailTemplate, + EmailProvider: m.EmailProvider, + EventStream: m.EventStream, + Flow: m.Flow, + FlowVaultConnection: m.Flow.Vault, + Form: m.Form, + Log: m.Log, + LogStream: m.LogStream, + Organization: m.Organization, + NetworkACL: m.NetworkACL, + Prompt: m.Prompt, + ResourceServer: m.ResourceServer, + Role: m.Role, + Rule: m.Rule, + Tenant: m.Tenant, + User: m.User, + Jobs: m.Job, + SelfServiceProfile: m.SelfServiceProfile, + UserAttributeProfile: m.UserAttributeProfile, + HTTPClient: m, } } diff --git a/internal/auth0/mock/user_attribute_profiles.go b/internal/auth0/mock/user_attribute_profiles.go new file mode 100644 index 000000000..e1539ea1a --- /dev/null +++ b/internal/auth0/mock/user_attribute_profiles.go @@ -0,0 +1,173 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: user_attribute_profiles.go + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + reflect "reflect" + + management "github.com/auth0/go-auth0/management" + gomock "github.com/golang/mock/gomock" +) + +// MockUserAttributeProfilesAPI is a mock of UserAttributeProfilesAPI interface. +type MockUserAttributeProfilesAPI struct { + ctrl *gomock.Controller + recorder *MockUserAttributeProfilesAPIMockRecorder +} + +// MockUserAttributeProfilesAPIMockRecorder is the mock recorder for MockUserAttributeProfilesAPI. +type MockUserAttributeProfilesAPIMockRecorder struct { + mock *MockUserAttributeProfilesAPI +} + +// NewMockUserAttributeProfilesAPI creates a new mock instance. +func NewMockUserAttributeProfilesAPI(ctrl *gomock.Controller) *MockUserAttributeProfilesAPI { + mock := &MockUserAttributeProfilesAPI{ctrl: ctrl} + mock.recorder = &MockUserAttributeProfilesAPIMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockUserAttributeProfilesAPI) EXPECT() *MockUserAttributeProfilesAPIMockRecorder { + return m.recorder +} + +// Create mocks base method. +func (m *MockUserAttributeProfilesAPI) Create(ctx context.Context, p *management.UserAttributeProfile, opts ...management.RequestOption) error { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, p} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Create", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Create indicates an expected call of Create. +func (mr *MockUserAttributeProfilesAPIMockRecorder) Create(ctx, p interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, p}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockUserAttributeProfilesAPI)(nil).Create), varargs...) +} + +// Delete mocks base method. +func (m *MockUserAttributeProfilesAPI) Delete(ctx context.Context, id string, opts ...management.RequestOption) error { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, id} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Delete", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockUserAttributeProfilesAPIMockRecorder) Delete(ctx, id interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, id}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockUserAttributeProfilesAPI)(nil).Delete), varargs...) +} + +// GetTemplate mocks base method. +func (m *MockUserAttributeProfilesAPI) GetTemplate(ctx context.Context, id string, opts ...management.RequestOption) (*management.UserAttributeProfileTemplateItem, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, id} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetTemplate", varargs...) + ret0, _ := ret[0].(*management.UserAttributeProfileTemplateItem) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetTemplate indicates an expected call of GetTemplate. +func (mr *MockUserAttributeProfilesAPIMockRecorder) GetTemplate(ctx, id interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, id}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTemplate", reflect.TypeOf((*MockUserAttributeProfilesAPI)(nil).GetTemplate), varargs...) +} + +// List mocks base method. +func (m *MockUserAttributeProfilesAPI) List(ctx context.Context, opts ...management.RequestOption) (*management.UserAttributeProfileList, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "List", varargs...) + ret0, _ := ret[0].(*management.UserAttributeProfileList) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockUserAttributeProfilesAPIMockRecorder) List(ctx interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockUserAttributeProfilesAPI)(nil).List), varargs...) +} + +// ListTemplates mocks base method. +func (m *MockUserAttributeProfilesAPI) ListTemplates(ctx context.Context, opts ...management.RequestOption) (*management.UserAttributeProfileTemplateList, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ListTemplates", varargs...) + ret0, _ := ret[0].(*management.UserAttributeProfileTemplateList) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListTemplates indicates an expected call of ListTemplates. +func (mr *MockUserAttributeProfilesAPIMockRecorder) ListTemplates(ctx interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListTemplates", reflect.TypeOf((*MockUserAttributeProfilesAPI)(nil).ListTemplates), varargs...) +} + +// Read mocks base method. +func (m *MockUserAttributeProfilesAPI) Read(ctx context.Context, id string, opts ...management.RequestOption) (*management.UserAttributeProfile, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, id} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Read", varargs...) + ret0, _ := ret[0].(*management.UserAttributeProfile) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Read indicates an expected call of Read. +func (mr *MockUserAttributeProfilesAPIMockRecorder) Read(ctx, id interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, id}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockUserAttributeProfilesAPI)(nil).Read), varargs...) +} + +// Update mocks base method. +func (m *MockUserAttributeProfilesAPI) Update(ctx context.Context, id string, p *management.UserAttributeProfile, opts ...management.RequestOption) error { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, id, p} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Update", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// Update indicates an expected call of Update. +func (mr *MockUserAttributeProfilesAPIMockRecorder) Update(ctx, id, p interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, id, p}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockUserAttributeProfilesAPI)(nil).Update), varargs...) +} diff --git a/internal/auth0/user_attribute_profiles.go b/internal/auth0/user_attribute_profiles.go new file mode 100644 index 000000000..458264810 --- /dev/null +++ b/internal/auth0/user_attribute_profiles.go @@ -0,0 +1,25 @@ +//go:generate mockgen -source=user_attribute_profiles.go -destination=mock/user_attribute_profiles.go -package=mock + +package auth0 + +import ( + "context" + + "github.com/auth0/go-auth0/management" +) + +type UserAttributeProfilesAPI interface { + Create(ctx context.Context, p *management.UserAttributeProfile, opts ...management.RequestOption) error + + List(ctx context.Context, opts ...management.RequestOption) (p *management.UserAttributeProfileList, err error) + + Read(ctx context.Context, id string, opts ...management.RequestOption) (p *management.UserAttributeProfile, err error) + + Update(ctx context.Context, id string, p *management.UserAttributeProfile, opts ...management.RequestOption) error + + Delete(ctx context.Context, id string, opts ...management.RequestOption) error + + ListTemplates(ctx context.Context, opts ...management.RequestOption) (p *management.UserAttributeProfileTemplateList, err error) + + GetTemplate(ctx context.Context, id string, opts ...management.RequestOption) (p *management.UserAttributeProfileTemplateItem, err error) +} diff --git a/internal/cli/terraform.go b/internal/cli/terraform.go index 050f6f33c..dc0c02aab 100644 --- a/internal/cli/terraform.go +++ b/internal/cli/terraform.go @@ -119,6 +119,8 @@ func (i *terraformInputs) parseResourceFetchers(api *auth0.API) ([]resourceDataF fetchers = append(fetchers, &tenantResourceFetcher{}) case "auth0_trigger_actions": fetchers = append(fetchers, &triggerActionsResourceFetcher{api}) + case "auth0_user_attribute_profile": + fetchers = append(fetchers, &userAttributeProfilesResourceFetcher{api}) default: err = errors.Join(err, fmt.Errorf("unsupported resource type: %s", resource)) } diff --git a/internal/cli/terraform_fetcher.go b/internal/cli/terraform_fetcher.go index dfffbffff..531518643 100644 --- a/internal/cli/terraform_fetcher.go +++ b/internal/cli/terraform_fetcher.go @@ -12,7 +12,7 @@ import ( ) var ( - defaultResources = []string{"auth0_action", "auth0_attack_protection", "auth0_branding", "auth0_phone_provider", "auth0_client", "auth0_client_grant", "auth0_connection", "auth0_custom_domain", "auth0_flow", "auth0_flow_vault_connection", "auth0_form", "auth0_email_provider", "auth0_email_template", "auth0_guardian", "auth0_log_stream", "auth0_network_acl", "auth0_organization", "auth0_pages", "auth0_prompt", "auth0_prompt_custom_text", "auth0_prompt_screen_renderer", "auth0_resource_server", "auth0_role", "auth0_self_service_profile", "auth0_tenant", "auth0_trigger_actions"} + defaultResources = []string{"auth0_action", "auth0_attack_protection", "auth0_branding", "auth0_phone_provider", "auth0_client", "auth0_client_grant", "auth0_connection", "auth0_custom_domain", "auth0_flow", "auth0_flow_vault_connection", "auth0_form", "auth0_email_provider", "auth0_email_template", "auth0_guardian", "auth0_log_stream", "auth0_network_acl", "auth0_organization", "auth0_pages", "auth0_prompt", "auth0_prompt_custom_text", "auth0_prompt_screen_renderer", "auth0_resource_server", "auth0_role", "auth0_self_service_profile", "auth0_tenant", "auth0_trigger_actions", "auth0_user_attribute_profile"} ) type ( @@ -116,6 +116,10 @@ type ( triggerActionsResourceFetcher struct { api *auth0.API } + + userAttributeProfilesResourceFetcher struct { + api *auth0.API + } ) func (f *attackProtectionResourceFetcher) FetchData(_ context.Context) (importDataList, error) { @@ -688,6 +692,33 @@ func (f *triggerActionsResourceFetcher) FetchData(ctx context.Context) (importDa return data, nil } +func (f *userAttributeProfilesResourceFetcher) FetchData(ctx context.Context) (importDataList, error) { + var data importDataList + + from := "" + for { + profiles, err := f.api.UserAttributeProfile.List(ctx, management.From(from)) + if err != nil { + return nil, err + } + + for _, profile := range profiles.UserAttributeProfiles { + data = append(data, importDataItem{ + ResourceName: "auth0_user_attribute_profile." + sanitizeResourceName(profile.GetName()), + ImportID: profile.GetID(), + }) + } + + if !profiles.HasNext() { + break + } + + from = profiles.Next + } + + return data, nil +} + func (f *actionResourceFetcher) FetchData(ctx context.Context) (importDataList, error) { var data importDataList diff --git a/internal/cli/terraform_fetcher_test.go b/internal/cli/terraform_fetcher_test.go index d59a4b8ff..f324a38f4 100644 --- a/internal/cli/terraform_fetcher_test.go +++ b/internal/cli/terraform_fetcher_test.go @@ -2069,3 +2069,33 @@ func TestTriggerActionsResourceFetcher_FetchData(t *testing.T) { assert.EqualError(t, err, "failed to list action triggers") }) } + +func TestUserAttributeProfileResourceFetcher(t *testing.T) { + t.Run("it successfully generates user attribute profile import data", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + userAttributeProfileAPI := mock.NewMockUserAttributeProfilesAPI(ctrl) + + userAttributeProfileAPI.EXPECT(). + List(gomock.Any(), gomock.Any()). + Return(&management.UserAttributeProfileList{ + UserAttributeProfiles: []*management.UserAttributeProfile{ + { + ID: auth0.String("uap_123456"), + Name: auth0.String("User Attribute Profile 1"), + }}}, nil) + + fetcher := userAttributeProfilesResourceFetcher{ + api: &auth0.API{ + UserAttributeProfile: userAttributeProfileAPI, + }, + } + + data, err := fetcher.FetchData(context.Background()) + assert.NoError(t, err) + assert.Len(t, data, 1) + assert.Equal(t, data[0].ResourceName, "auth0_user_attribute_profile.user_attribute_profile_1") + assert.Greater(t, len(data[0].ImportID), 0) + }) +}