Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TIC-958] Signup domain allowlist/blocklist #11

Merged
merged 8 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/resources/basic_auth_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ resource "propelauth_basic_auth_configuration" "example" {

### Optional

- `allow_users_to_signup_with_personal_email` (Boolean) If true, your users will be able to sign up using personal email domains (@gmail.com, @yahoo.com, etc.).The default setting is true.
- `allow_users_to_signup_with_personal_email` (Boolean) If true, your users will be able to sign up using personal email domains (@gmail.com, @yahoo.com, etc.).The default setting is true. This is only enabled if `signup_domain_allowlist` is empty.
- `has_password_login` (Boolean) If true, your users will be able to log in using their email and password. The default setting is true.
- `has_passwordless_login` (Boolean) If true, your users will be able to log in using a magic link sent to their email. The default setting is false.
- `include_login_method` (Boolean) If true, the login method will be included in the access token. The default setting is false.See `https://docs.propelauth.com/overview/user-management/user-properties#login-method-property` for more information.
- `signup_domain_allowlist` (List of String) A list of email domains that are allowed to sign up. This is only used if `signup_domain_allowlist_enabled` is true.
- `signup_domain_blocklist` (List of String) A list of email domains that are blocked from signing up. This is only used if `signup_domain_blocklist_enabled` is true and `signup_domain_allowlist` is empty.
- `user_autologout_seconds` (Number) The number of seconds before a user is automatically logged out. The default setting is 1209600 (14 days).See also "user_autologout_type" for more information.
- `user_autologout_type` (String) This sets the behavior for when the counting for "user_autologout_seconds" starts. Valid values are "AfterInactivity" and the stricter "AfterLogin". The default setting is "AfterInactivity".
- `users_can_change_email` (Boolean) If true, your users will be able to change their email address. The default setting is true.
Expand Down
8 changes: 8 additions & 0 deletions internal/propelauth/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ type ProjectInfoUpdateRequest struct {

type EnvironmentConfigUpdate struct {
AllowUsersToSignupWithPersonalEmail *bool `json:"allow_users_to_signup_with_personal_email,omitempty"`
SignupDomainAllowlistEnabled *bool `json:"signup_domain_allowlist_enabled,omitempty"`
SignupDomainAllowlist []string `json:"signup_domain_allowlist,omitempty"`
SignupDomainBlocklistEnabled *bool `json:"signup_domain_blocklist_enabled,omitempty"`
SignupDomainBlocklist []string `json:"signup_domain_blocklist,omitempty"`
HasPasswordLogin *bool `json:"has_password_login,omitempty"`
HasPasswordlessLogin *bool `json:"has_passwordless_login,omitempty"`
WaitlistUsersEnabled *bool `json:"waitlist_users_enabled,omitempty"`
Expand Down Expand Up @@ -48,6 +52,10 @@ type EnvironmentConfigUpdate struct {

type EnvironmentConfigResponse struct {
AllowUsersToSignupWithPersonalEmail bool `json:"allow_users_to_signup_with_personal_email"`
SignupDomainAllowlistEnabled bool `json:"signup_domain_allowlist_enabled"`
SignupDomainAllowlist []string `json:"signup_domain_allowlist"`
SignupDomainBlocklistEnabled bool `json:"signup_domain_blocklist_enabled"`
SignupDomainBlocklist []string `json:"signup_domain_blocklist"`
HasPasswordLogin bool `json:"has_password_login"`
HasPasswordlessLogin bool `json:"has_passwordless_login"`
WaitlistUsersEnabled bool `json:"waitlist_users_enabled"`
Expand Down
183 changes: 173 additions & 10 deletions internal/provider/basic_auth_configuration_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"terraform-provider-propelauth/internal/propelauth"

"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
Expand All @@ -17,6 +18,7 @@ import (
// Ensure provider defined types fully satisfy framework interfaces.
var _ resource.Resource = &basicAuthConfigurationResource{}
var _ resource.ResourceWithConfigure = &basicAuthConfigurationResource{}
var _ resource.ResourceWithValidateConfig = &basicAuthConfigurationResource{}

func NewBasicAuthConfigurationResource() resource.Resource {
return &basicAuthConfigurationResource{}
Expand All @@ -29,15 +31,17 @@ type basicAuthConfigurationResource struct {

// basicAuthConfigurationResourceModel describes the resource data model.
type basicAuthConfigurationResourceModel struct {
AllowUsersToSignupWithPersonalEmail types.Bool `tfsdk:"allow_users_to_signup_with_personal_email"`
HasPasswordLogin types.Bool `tfsdk:"has_password_login"`
HasPasswordlessLogin types.Bool `tfsdk:"has_passwordless_login"`
WaitlistUsersEnabled types.Bool `tfsdk:"waitlist_users_enabled"`
UserAutologoutSeconds types.Int64 `tfsdk:"user_autologout_seconds"`
UserAutologoutType types.String `tfsdk:"user_autologout_type"`
UsersCanDeleteOwnAccount types.Bool `tfsdk:"users_can_delete_own_account"`
UsersCanChangeEmail types.Bool `tfsdk:"users_can_change_email"`
IncludeLoginMethod types.Bool `tfsdk:"include_login_method"`
AllowUsersToSignupWithPersonalEmail types.Bool `tfsdk:"allow_users_to_signup_with_personal_email"`
SignupDomainAllowlist []types.String `tfsdk:"signup_domain_allowlist"`
SignupDomainBlocklist []types.String `tfsdk:"signup_domain_blocklist"`
HasPasswordLogin types.Bool `tfsdk:"has_password_login"`
HasPasswordlessLogin types.Bool `tfsdk:"has_passwordless_login"`
WaitlistUsersEnabled types.Bool `tfsdk:"waitlist_users_enabled"`
UserAutologoutSeconds types.Int64 `tfsdk:"user_autologout_seconds"`
UserAutologoutType types.String `tfsdk:"user_autologout_type"`
UsersCanDeleteOwnAccount types.Bool `tfsdk:"users_can_delete_own_account"`
UsersCanChangeEmail types.Bool `tfsdk:"users_can_change_email"`
IncludeLoginMethod types.Bool `tfsdk:"include_login_method"`
}

func (r *basicAuthConfigurationResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
Expand All @@ -52,8 +56,26 @@ func (r *basicAuthConfigurationResource) Schema(ctx context.Context, req resourc
"allow_users_to_signup_with_personal_email": schema.BoolAttribute{
Optional: true,
Description: "If true, your users will be able to sign up using personal email domains (@gmail.com, @yahoo.com, etc.)." +
"The default setting is true.",
"The default setting is true. This is only enabled if `signup_domain_allowlist` is empty.",
},
// "signup_domain_allowlist_enabled": schema.BoolAttribute{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Can we deleted the commented out code here and below?

// Optional: true,
// Description: "If true, only users with email domains in the allowlist will be able to sign up. The default setting is false.",
// },
"signup_domain_allowlist": schema.ListAttribute{
ElementType: types.StringType,
Optional: true,
Description: "A list of email domains that are allowed to sign up. This is only used if `signup_domain_allowlist_enabled` is true.",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove any references in these field docstrings to the now deleted fields such as signup_domain_allowlist_enabled?

},
"signup_domain_blocklist": schema.ListAttribute{
ElementType: types.StringType,
Optional: true,
Description: "A list of email domains that are blocked from signing up. This is only used if `signup_domain_blocklist_enabled` is true and `signup_domain_allowlist` is empty.",
},
// "signup_domain_blocklist_enabled": schema.BoolAttribute{
// Optional: true,
// Description: "If true, users with email domains in the blocklist will not be able to sign up. The default setting is false. This is only used if `signup_domain_allowlist_enabled` is false.",
// },
"has_password_login": schema.BoolAttribute{
Optional: true,
Description: "If true, your users will be able to log in using their email and password. The default setting is true.",
Expand Down Expand Up @@ -116,6 +138,27 @@ func (r *basicAuthConfigurationResource) Configure(ctx context.Context, req reso
r.client = client
}

func (r *basicAuthConfigurationResource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) {
var plan basicAuthConfigurationResourceModel

// Read Terraform plan data into the model
diags := req.Config.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

// Validate the plan data
if plan.SignupDomainAllowlist != nil && plan.SignupDomainBlocklist != nil {
resp.Diagnostics.AddAttributeError(
path.Root("signup_domain_allowlist"),
"Invalid `signup_domain_allowlist`",
"`signup_domain_allowlist` and `signup_domain_blocklist` cannot both be set",
)
return
}
}

func (r *basicAuthConfigurationResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var plan basicAuthConfigurationResourceModel

Expand All @@ -139,6 +182,30 @@ func (r *basicAuthConfigurationResource) Create(ctx context.Context, req resourc
IncludeLoginMethod: plan.IncludeLoginMethod.ValueBoolPointer(),
}

var signupDomainAllowlistEnabled bool
if plan.SignupDomainAllowlist != nil {
signupDomainAllowlistEnabled = true
environmentConfigUpdate.SignupDomainAllowlist = make([]string, len(plan.SignupDomainAllowlist))
for i, domain := range plan.SignupDomainAllowlist {
environmentConfigUpdate.SignupDomainAllowlist[i] = domain.ValueString()
}
} else {
signupDomainAllowlistEnabled = false
}
environmentConfigUpdate.SignupDomainAllowlistEnabled = &signupDomainAllowlistEnabled

var signupDomainBlocklistEnabled bool
if plan.SignupDomainBlocklist != nil {
signupDomainBlocklistEnabled = true
environmentConfigUpdate.SignupDomainBlocklist = make([]string, len(plan.SignupDomainBlocklist))
for i, domain := range plan.SignupDomainBlocklist {
environmentConfigUpdate.SignupDomainBlocklist[i] = domain.ValueString()
}
} else {
signupDomainBlocklistEnabled = false
}
environmentConfigUpdate.SignupDomainBlocklistEnabled = &signupDomainBlocklistEnabled

environmentConfigResponse, err := r.client.UpdateEnvironmentConfig(&environmentConfigUpdate)
if err != nil {
resp.Diagnostics.AddError(
Expand Down Expand Up @@ -221,6 +288,42 @@ func (r *basicAuthConfigurationResource) Create(ctx context.Context, req resourc
)
return
}
if plan.SignupDomainAllowlist != nil {
if len(plan.SignupDomainAllowlist) != len(environmentConfigResponse.SignupDomainAllowlist) {
resp.Diagnostics.AddError(
"Error updating basic auth configuration",
"SignupDomainAllowlist failed to update. The `signup_domain_allowlist` is instead "+fmt.Sprintf("%v", environmentConfigResponse.SignupDomainAllowlist),
)
return
}
for i, domain := range plan.SignupDomainAllowlist {
if domain.ValueString() != environmentConfigResponse.SignupDomainAllowlist[i] {
resp.Diagnostics.AddError(
"Error updating basic auth configuration",
"SignupDomainAllowlist failed to update. The `signup_domain_allowlist` is instead "+fmt.Sprintf("%v", environmentConfigResponse.SignupDomainAllowlist),
)
return
}
}
}
if plan.SignupDomainBlocklist != nil {
if len(plan.SignupDomainBlocklist) != len(environmentConfigResponse.SignupDomainBlocklist) {
resp.Diagnostics.AddError(
"Error updating basic auth configuration",
"SignupDomainBlocklist failed to update. The `signup_domain_blocklist` is instead "+fmt.Sprintf("%v", environmentConfigResponse.SignupDomainBlocklist),
)
return
}
for i, domain := range plan.SignupDomainBlocklist {
if domain.ValueString() != environmentConfigResponse.SignupDomainBlocklist[i] {
resp.Diagnostics.AddError(
"Error updating basic auth configuration",
"SignupDomainBlocklist failed to update. The `signup_domain_blocklist` is instead "+fmt.Sprintf("%v", environmentConfigResponse.SignupDomainBlocklist),
)
return
}
}
}

// Write logs using the tflog package
// Documentation: https://terraform.io/plugin/log
Expand Down Expand Up @@ -305,6 +408,30 @@ func (r *basicAuthConfigurationResource) Update(ctx context.Context, req resourc
IncludeLoginMethod: plan.IncludeLoginMethod.ValueBoolPointer(),
}

var signupDomainAllowlistEnabled bool
if plan.SignupDomainAllowlist != nil {
signupDomainAllowlistEnabled = true
environmentConfigUpdate.SignupDomainAllowlist = make([]string, len(plan.SignupDomainAllowlist))
for i, domain := range plan.SignupDomainAllowlist {
environmentConfigUpdate.SignupDomainAllowlist[i] = domain.ValueString()
}
} else {
signupDomainAllowlistEnabled = false
}
environmentConfigUpdate.SignupDomainAllowlistEnabled = &signupDomainAllowlistEnabled

var signupDomainBlocklistEnabled bool
if plan.SignupDomainBlocklist != nil {
signupDomainBlocklistEnabled = true
environmentConfigUpdate.SignupDomainBlocklist = make([]string, len(plan.SignupDomainBlocklist))
for i, domain := range plan.SignupDomainBlocklist {
environmentConfigUpdate.SignupDomainBlocklist[i] = domain.ValueString()
}
} else {
signupDomainBlocklistEnabled = false
}
environmentConfigUpdate.SignupDomainBlocklistEnabled = &signupDomainBlocklistEnabled

environmentConfigResponse, err := r.client.UpdateEnvironmentConfig(&environmentConfigUpdate)
if err != nil {
resp.Diagnostics.AddError(
Expand Down Expand Up @@ -387,6 +514,42 @@ func (r *basicAuthConfigurationResource) Update(ctx context.Context, req resourc
)
return
}
if plan.SignupDomainAllowlist != nil {
if len(plan.SignupDomainAllowlist) != len(environmentConfigResponse.SignupDomainAllowlist) {
resp.Diagnostics.AddError(
"Error updating basic auth configuration",
"SignupDomainAllowlist failed to update. The `signup_domain_allowlist` is instead "+fmt.Sprintf("%v", environmentConfigResponse.SignupDomainAllowlist),
)
return
}
for i, domain := range plan.SignupDomainAllowlist {
if domain.ValueString() != environmentConfigResponse.SignupDomainAllowlist[i] {
resp.Diagnostics.AddError(
"Error updating basic auth configuration",
"SignupDomainAllowlist failed to update. The `signup_domain_allowlist` is instead "+fmt.Sprintf("%v", environmentConfigResponse.SignupDomainAllowlist),
)
return
}
}
}
if plan.SignupDomainBlocklist != nil {
if len(plan.SignupDomainBlocklist) != len(environmentConfigResponse.SignupDomainBlocklist) {
resp.Diagnostics.AddError(
"Error updating basic auth configuration",
"SignupDomainBlocklist failed to update. The `signup_domain_blocklist` is instead "+fmt.Sprintf("%v", environmentConfigResponse.SignupDomainBlocklist),
)
return
}
for i, domain := range plan.SignupDomainBlocklist {
if domain.ValueString() != environmentConfigResponse.SignupDomainBlocklist[i] {
resp.Diagnostics.AddError(
"Error updating basic auth configuration",
"SignupDomainBlocklist failed to update. The `signup_domain_blocklist` is instead "+fmt.Sprintf("%v", environmentConfigResponse.SignupDomainBlocklist),
)
return
}
}
}

// Write logs using the tflog package
// Documentation: https://terraform.io/plugin/log
Expand Down