diff --git a/.github/workflows/ci-go.yml b/.github/workflows/ci-go.yml index 03faa5a..af8386c 100644 --- a/.github/workflows/ci-go.yml +++ b/.github/workflows/ci-go.yml @@ -28,7 +28,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go-version: [ '1.19' ] + go-version: [ '1.21' ] steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 diff --git a/go.mod b/go.mod index d9c4f84..586ef65 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/real-digital/terraform-provider-cidaas go 1.21 -toolchain go1.22.1 +toolchain go1.21.6 require ( github.com/hashicorp/terraform-plugin-docs v0.16.0 diff --git a/internal/client/client.go b/internal/client/client.go index e388dd6..3d21de0 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -40,6 +40,8 @@ type Client interface { GetSocialProvider(providerName string, name string) (*SocialProvider, error) + GetCustomProvider(providerName string) (*CustomProvider, error) + GetConsentInstance(name string) (*ConsentInstance, error) UpdatePasswordPolicy(policy PasswordPolicy) (*PasswordPolicy, error) diff --git a/internal/client/custom_provider.go b/internal/client/custom_provider.go new file mode 100644 index 0000000..9a04e3b --- /dev/null +++ b/internal/client/custom_provider.go @@ -0,0 +1,36 @@ +package client + +import ( + "encoding/json" + "fmt" + "net/http" +) + +type customProvidersResponse struct { + Status int `json:"status"` + Data CustomProvider `json:"data"` +} + +func (c *client) GetCustomProvider(providerName string) (*CustomProvider, error) { + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/providers-srv/custom/%s", c.HostUrl, providerName), nil) + + if err != nil { + return nil, err + } + + body, err := c.doRequest(req) + + if err != nil { + return nil, err + } + + var response customProvidersResponse + err = json.Unmarshal(body, &response) + + if err != nil { + return nil, err + } + + return &response.Data, nil + +} diff --git a/internal/client/model.go b/internal/client/model.go index 659dc85..667db7d 100644 --- a/internal/client/model.go +++ b/internal/client/model.go @@ -118,6 +118,7 @@ type App struct { AllowedLogoutUrls []string `json:"allowed_logout_urls"` AllowedScopes []string `json:"allowed_scopes"` SocialProviders []SocialProvider `json:"social_providers"` + CustomProviders []CustomProvider `json:"custom_providers"` AdditionalAccessTokenPayload []string `json:"additional_access_token_payload"` AllowedFields []string `json:"allowed_fields"` RequiredFields []string `json:"required_fields"` @@ -209,3 +210,8 @@ type Template struct { Subject string `json:"subject"` Content string `json:"content"` } + +type CustomProvider struct { + DisplayName string `json:"display_name"` + ProviderName string `json:"provider_name"` +} diff --git a/internal/provider/data_source_custom_provider.go b/internal/provider/data_source_custom_provider.go new file mode 100644 index 0000000..41c5d53 --- /dev/null +++ b/internal/provider/data_source_custom_provider.go @@ -0,0 +1,73 @@ +package provider + +import ( + "context" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type customProviderDataSource struct { + provider *cidaasProvider +} + +var _ datasource.DataSource = (*customProviderDataSource)(nil) + +func NewCustomProviderDataSource() datasource.DataSource { + return &customProviderDataSource{} +} + +func (d *customProviderDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Allows reading custom login providers that are configured", + Attributes: map[string]schema.Attribute{ + "display_name": schema.StringAttribute{ + Computed: true, + }, + "provider_name": schema.StringAttribute{ + Required: true, + }, + }, + } +} + +func (d *customProviderDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_custom_provider" +} + +func (d *customProviderDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + d.provider, resp.Diagnostics = toProvider(req.ProviderData) +} + +func (d customProviderDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var providerName string + + var state CustomProvider + + diags := req.Config.GetAttribute(ctx, path.Root("provider_name"), &providerName) + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + customProvider, err := d.provider.client.GetCustomProvider(providerName) + + if err != nil { + resp.Diagnostics.AddError("Could not fetch custom provider", + err.Error(), + ) + return + } + + state.ProviderName = types.StringValue(customProvider.ProviderName) + state.DisplayName = types.StringValue(customProvider.DisplayName) + + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } +} diff --git a/internal/provider/models.go b/internal/provider/models.go index 7bf822e..557013b 100644 --- a/internal/provider/models.go +++ b/internal/provider/models.go @@ -31,6 +31,11 @@ type SocialProvider struct { Name types.String `tfsdk:"name"` } +type CustomProvider struct { + DisplayName types.String `tfsdk:"display_name"` + ProviderName types.String `tfsdk:"provider_name"` +} + type ConsentInstance struct { ID types.String `tfsdk:"id"` ConsentName types.String `tfsdk:"consent_name"` @@ -69,6 +74,7 @@ type App struct { EnablePasswordlessAuth types.Bool `tfsdk:"enable_passwordless_auth"` EnableDeduplication types.Bool `tfsdk:"enable_deduplication"` MobileNumberVerificationRequired types.Bool `tfsdk:"mobile_number_verification_required"` + AcceptRolesInTheRegistration types.Bool `tfsdk:"accept_roles_in_the_registration"` HostedPageGroup types.String `tfsdk:"hosted_page_group"` PrimaryColor types.String `tfsdk:"primary_color"` AccentColor types.String `tfsdk:"accent_color"` @@ -96,6 +102,7 @@ type App struct { RedirectUris []string `tfsdk:"redirect_uris"` AllowedLogoutUrls []string `tfsdk:"allowed_logout_urls"` SocialProviders []SocialProvider `tfsdk:"social_providers"` + CustomProviders []CustomProvider `tfsdk:"custom_providers"` AdditionalAccessTokenPayload []string `tfsdk:"additional_access_token_payload"` Scopes []string `tfsdk:"allowed_scopes"` AllowedFields []string `tfsdk:"allowed_fields"` diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 515a380..f3c879b 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -147,6 +147,7 @@ func (p *cidaasProvider) DataSources(context.Context) []func() datasource.DataSo NewPasswordPolicyDataSource, NewSocialProviderDataSource, NewTenantInfoDataSource, + NewCustomProviderDataSource, } } diff --git a/internal/provider/resource_app.go b/internal/provider/resource_app.go index 9b13098..6945383 100644 --- a/internal/provider/resource_app.go +++ b/internal/provider/resource_app.go @@ -198,6 +198,20 @@ func (r *appResource) Schema(_ context.Context, _ resource.SchemaRequest, resp * Required: true, }, + "custom_providers": schema.ListNestedAttribute{ + Required: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "display_name": schema.StringAttribute{ + Required: true, + }, + "provider_name": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + // Login Provider "social_providers": schema.ListNestedAttribute{ Required: true, @@ -301,6 +315,9 @@ func (r *appResource) Schema(_ context.Context, _ resource.SchemaRequest, resp * }, }, }, + "accept_roles_in_the_registration": schema.BoolAttribute{ + Required: true, + }, "operations_allowed_groups": schema.ListNestedAttribute{ Optional: true, NestedObject: schema.NestedAttributeObject{ @@ -592,6 +609,7 @@ func applyAppToState(ctx context.Context, state *App, app *client.App) diag.Diag state.PrimaryColor = types.StringValue(app.PrimaryColor) state.AccentColor = types.StringValue(app.AccentColor) state.AutoLoginAfterRegister = types.BoolValue(app.AutoLoginAfterRegister) + state.AcceptRolesInTheRegistration = types.BoolValue(app.AcceptRolesInTheRegistration) state.CompanyName = types.StringValue(app.CompanyName) state.CompanyAddress = types.StringValue(app.CompanyAddress) state.CompanyWebsite = types.StringValue(app.CompanyWebsite) @@ -650,6 +668,14 @@ func applyAppToState(ctx context.Context, state *App, app *client.App) diag.Diag }) } + state.CustomProviders = []CustomProvider{} + for _, item := range app.CustomProviders { + state.CustomProviders = append(state.CustomProviders, CustomProvider{ + DisplayName: types.StringValue(item.DisplayName), + ProviderName: types.StringValue(item.ProviderName), + }) + } + state.AppKey, diags = types.ObjectValue( map[string]attr.Type{ "id": types.StringType, @@ -703,6 +729,7 @@ func planToApp(ctx context.Context, plan *App, state *App) (*client.App, diag.Di JweEnabled: plan.JweEnabled.ValueBool(), AlwaysAskMfa: plan.AlwaysAskMfa.ValueBool(), RegisterWithLoginInformation: plan.RegisterWithLoginInformation.ValueBool(), + AcceptRolesInTheRegistration: plan.AcceptRolesInTheRegistration.ValueBool(), AllowLoginWith: plan.AllowLoginWith, RedirectUris: plan.RedirectUris, @@ -719,6 +746,7 @@ func planToApp(ctx context.Context, plan *App, state *App) (*client.App, diag.Di AllowedMfa: plan.AllowedMfa, SocialProviders: []client.SocialProvider{}, + CustomProviders: []client.CustomProvider{}, } for _, socialProvider := range plan.SocialProviders { @@ -731,6 +759,16 @@ func planToApp(ctx context.Context, plan *App, state *App) (*client.App, diag.Di ) } + for _, customProvider := range plan.CustomProviders { + plannedApp.CustomProviders = append( + plannedApp.CustomProviders, + client.CustomProvider{ + DisplayName: customProvider.DisplayName.ValueString(), + ProviderName: customProvider.ProviderName.ValueString(), + }, + ) + } + diags = tfsdk.ValueAs(ctx, plan.AllowedGroups, &plannedApp.AllowedGroups) ret.Append(diags...)