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

Add support for configuring advanced multitenant SSO #126

Merged
merged 2 commits into from
Dec 25, 2023
Merged
Changes from all 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
22 changes: 22 additions & 0 deletions docs/resources/workspace.md
Original file line number Diff line number Diff line change
@@ -233,6 +233,8 @@ resource "frontegg_workspace" "example" {
- `reset_password_email` (Block List, Max: 1) Configures the password reset email. (see [below for nested schema](#nestedblock--reset_password_email))
- `reset_phone_number_email` (Block List, Max: 1) Configures the reset phone number email. (see [below for nested schema](#nestedblock--reset_phone_number_email))
- `saml` (Block List, Max: 1) Configures SSO via SAML. (see [below for nested schema](#nestedblock--saml))
- `sso_domain_policy` (Block List, Max: 1) Configures how SSO domains are validated. (see [below for nested schema](#nestedblock--sso_domain_policy))
- `sso_multi_tenant_policy` (Block List, Max: 1) Configures how multiple tenants can claim the same SSO domain. (see [below for nested schema](#nestedblock--sso_multi_tenant_policy))
- `user_activation_email` (Block List, Max: 1) Configures the user activation email. (see [below for nested schema](#nestedblock--user_activation_email))
- `user_invitation_email` (Block List, Max: 1) Configures the user invitation email. (see [below for nested schema](#nestedblock--user_invitation_email))
- `user_used_invitation_email` (Block List, Max: 1) Configures the user used invitation email. (see [below for nested schema](#nestedblock--user_used_invitation_email))
@@ -252,6 +254,7 @@ Required:
- `enable_personal_api_tokens` (Boolean) Enable access to personal API tokens in the admin portal.
- `enable_privacy` (Boolean) Enable access to privacy settings in the admin portal.
- `enable_profile` (Boolean) Enable access to profile settings in the admin portal.
- `enable_provisioning` (Boolean) Enable access to provisioning settings in the admin portal.
- `enable_roles` (Boolean) Enable access to roles and permissions in the admin portal.
- `enable_security` (Boolean) Enable access to security settings in the admin portal.
- `enable_sso` (Boolean) Enable access to SSO settings in the admin portal.
@@ -644,6 +647,25 @@ Optional:
- `redirect_url` (String) The URL to redirect to after the SAML exchange.


<a id="nestedblock--sso_domain_policy"></a>
### Nested Schema for `sso_domain_policy`

Optional:

- `allow_verified_users_to_add_domains` (Boolean) Whether to allow users to add their own email domain without validating the domain through DNS.
- `bypass_domain_cross_validation` (Boolean) Whether to allow users to sign in even via SSO even if the associated domain has not been validated through DNS.
- `skip_domain_verification` (Boolean) Whether to automatically mark new SSO domains as validated, without validating the domain through DNS.


<a id="nestedblock--sso_multi_tenant_policy"></a>
### Nested Schema for `sso_multi_tenant_policy`

Optional:

- `unspecified_tenant_strategy` (String) Strategy for logging in new users that match SSO configurations for multiple tenants when no tenant has been specified. Either BLOCK or FIRST_CREATED.
- `use_active_tenant` (Boolean) Whether users with existing accounts that match SSO configurations for multiple tenants should be logged in using the SSO for their active (last logged into) account, or whether the unspecified tenant strategy should apply.


<a id="nestedblock--user_activation_email"></a>
### Nested Schema for `user_activation_email`

12 changes: 12 additions & 0 deletions examples/basic/main.tf
Original file line number Diff line number Diff line change
@@ -99,6 +99,17 @@ resource "frontegg_workspace" "example" {
redirect_url = "http://localhost:3000"
}

sso_multi_tenant_policy {
unspecified_tenant_strategy = "BLOCK"
use_active_tenant = true
}

sso_domain_policy {
allow_verified_users_to_add_domains = false
skip_domain_verification = false
bypass_domain_cross_validation = true
}

reset_password_email {
from_address = "[email protected]"
from_name = "Your Company"
@@ -114,6 +125,7 @@ resource "frontegg_workspace" "example" {
enable_personal_api_tokens = false
enable_privacy = false
enable_profile = false
enable_provisioning = false
enable_roles = false
enable_security = false
enable_sso = false
124 changes: 124 additions & 0 deletions provider/resource_frontegg_workspace.go
Original file line number Diff line number Diff line change
@@ -30,6 +30,8 @@ const fronteggOAuthURL = "/oauth/resources/configurations/v1"
const fronteggOAuthRedirectURIsURL = "/oauth/resources/configurations/v1/redirect-uri"
const fronteggSSOURL = "/identity/resources/sso/v2"
const fronteggSSOSAMLURL = "/metadata?entityName=saml"
const fronteggSSOMultiTenantURL = "/team/resources/sso/v1/configurations/multiple-sso-per-domain"
const fronteggSSODomainURL = "/team/resources/sso/v1/configurations/domains"
const fronteggOIDCURL = "/team/resources/sso/v1/oidc/configurations"
const fronteggEmailTemplatesURL = "/identity/resources/mail/v1/configs/templates"
const fronteggAdminPortalURL = "/metadata?entityName=adminBox"
@@ -140,6 +142,18 @@ type fronteggSSOSAMLConfiguration struct {
RedirectUrl string `json:"redirectUri"`
}

type fronteggSSOMultiTenant struct {
Active bool `json:"active"`
UnspecifiedTenantStrategy string `json:"unspecifiedTenantStrategy,omitempty"`
UseActiveTenant bool `json:"useActiveTenant"`
}

type fronteggSSODomain struct {
AllowVerifiedUsersToAddDomains bool `json:"allowVerifiedUsersToAddDomains"`
SkipDomainVerification bool `json:"skipDomainVerification"`
BypassDomainCrossValidation bool `json:"bypassDomainCrossValidation"`
}

type fronteggOIDC struct {
Active bool `json:"active"`
RedirectUri string `json:"redirectUri,omitempty"`
@@ -174,6 +188,7 @@ type fronteggAdminPortalNavigation struct {
PersonalAPITokens fronteggAdminPortalVisibility `json:"personalApiTokens"`
Privacy fronteggAdminPortalVisibility `json:"privacy"`
Profile fronteggAdminPortalVisibility `json:"profile"`
Provisioning fronteggAdminPortalVisibility `json:"provisioning"`
Roles fronteggAdminPortalVisibility `json:"roles"`
Security fronteggAdminPortalVisibility `json:"security"`
SSO fronteggAdminPortalVisibility `json:"sso"`
@@ -776,6 +791,52 @@ per Frontegg provider.`,
},
},
},
"sso_multi_tenant_policy": {
Description: "Configures how multiple tenants can claim the same SSO domain.",
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"unspecified_tenant_strategy": {
Description: "Strategy for logging in nonexisting users that match SSO configurations for multiple tenants when no tenant has been specified. Either BLOCK or FIRST_CREATED.",
Type: schema.TypeString,
Optional: true,
Default: "BLOCK",
},
"use_active_tenant": {
Description: "Whether users with existing accounts that match SSO configurations for multiple tenants should be logged in using the SSO for their active (last logged into) account, or whether the unspecified tenant strategy should apply.",
Type: schema.TypeBool,
Optional: true,
},
},
},
},
"sso_domain_policy": {
Description: "Configures how SSO domains are validated.",
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"allow_verified_users_to_add_domains": {
Description: "Whether to allow users to add their own email domain without validating the domain through DNS.",
Type: schema.TypeBool,
Optional: true,
},
"skip_domain_verification": {
Description: "Whether to automatically mark new SSO domains as validated, without validating the domain through DNS.",
Type: schema.TypeBool,
Optional: true,
},
"bypass_domain_cross_validation": {
Description: "Whether to allow users to sign in even via SSO even if the associated domain has not been validated through DNS.",
Type: schema.TypeBool,
Optional: true,
},
},
},
},
"reset_password_email": {
Description: "Configures the password reset email.",
Type: schema.TypeList,
@@ -884,6 +945,11 @@ per Frontegg provider.`,
Type: schema.TypeBool,
Required: true,
},
"enable_provisioning": {
Description: "Enable access to provisioning settings in the admin portal.",
Type: schema.TypeBool,
Required: true,
},
"enable_roles": {
Description: "Enable access to roles and permissions in the admin portal.",
Type: schema.TypeBool,
@@ -1158,6 +1224,38 @@ func resourceFronteggWorkspaceRead(ctx context.Context, d *schema.ResourceData,
return diag.FromErr(err)
}
}
{
var out fronteggSSOMultiTenant
clientHolder.ApiClient.Ignore404()
if err := clientHolder.ApiClient.Get(ctx, fronteggSSOMultiTenantURL, &out); err != nil {
return diag.FromErr(err)
}
items := []interface{}{}
if out.Active {
items = append(items, map[string]interface{}{
"unspecified_tenant_strategy": out.UnspecifiedTenantStrategy,
"use_active_tenant": out.UseActiveTenant,
})
}
if err := d.Set("sso_multi_tenant_policy", items); err != nil {
return diag.FromErr(err)
}
}
{
var out fronteggSSODomain
clientHolder.ApiClient.Ignore404()
if err := clientHolder.ApiClient.Get(ctx, fronteggSSODomainURL, &out); err != nil {
return diag.FromErr(err)
}
domain_policy := map[string]interface{}{
"allow_verified_users_to_add_domains": out.AllowVerifiedUsersToAddDomains,
"skip_domain_verification": out.SkipDomainVerification,
"bypass_domain_cross_validation": out.BypassDomainCrossValidation,
}
if err := d.Set("sso_domain_policy", []interface{}{domain_policy}); err != nil {
return diag.FromErr(err)
}
}
{
var out fronteggOIDC
if err := clientHolder.ApiClient.Get(ctx, fronteggOIDCURL, &out); err != nil {
@@ -1317,6 +1415,7 @@ func resourceFronteggWorkspaceRead(ctx context.Context, d *schema.ResourceData,
"enable_personal_api_tokens": nav.PersonalAPITokens.Visibility == "byPermissions",
"enable_privacy": nav.Privacy.Visibility == "byPermissions",
"enable_profile": nav.Profile.Visibility == "byPermissions",
"enable_provisioning": nav.Profile.Visibility == "byPermissions",
"enable_roles": nav.Roles.Visibility == "byPermissions",
"enable_security": nav.Security.Visibility == "byPermissions",
"enable_sso": nav.SSO.Visibility == "byPermissions",
@@ -1580,6 +1679,30 @@ func resourceFronteggWorkspaceUpdate(ctx context.Context, d *schema.ResourceData
return diag.FromErr(err)
}
}
{
sso_multi_tenant := d.Get("sso_multi_tenant_policy").([]interface{})
in := fronteggSSOMultiTenant{}
if len(sso_multi_tenant) > 0 {
in.Active = true
in.UnspecifiedTenantStrategy = d.Get("sso_multi_tenant_policy.0.unspecified_tenant_strategy").(string)
in.UseActiveTenant = d.Get("sso_multi_tenant_policy.0.use_active_tenant").(bool)
}
if err := clientHolder.ApiClient.Put(ctx, fronteggSSOMultiTenantURL, in, nil); err != nil {
return diag.FromErr(err)
}
}
{
sso_domain := d.Get("sso_domain_policy").([]interface{})
in := fronteggSSODomain{}
if len(sso_domain) > 0 {
in.AllowVerifiedUsersToAddDomains = d.Get("sso_domain_policy.0.allow_verified_users_to_add_domains").(bool)
in.SkipDomainVerification = d.Get("sso_domain_policy.0.skip_domain_verification").(bool)
in.BypassDomainCrossValidation = d.Get("sso_domain_policy.0.bypass_domain_cross_validation").(bool)
}
if err := clientHolder.ApiClient.Put(ctx, fronteggSSODomainURL, in, nil); err != nil {
return diag.FromErr(err)
}
}
{
oidc := d.Get("oidc").([]interface{})
in := fronteggOIDC{}
@@ -1734,6 +1857,7 @@ func resourceFronteggWorkspaceUpdate(ctx context.Context, d *schema.ResourceData
configuration.Navigation.PersonalAPITokens = serializeVisibility("admin_portal.0.enable_personal_api_tokens")
configuration.Navigation.Privacy = serializeVisibility("admin_portal.0.enable_privacy")
configuration.Navigation.Profile = serializeVisibility("admin_portal.0.enable_profile")
configuration.Navigation.Provisioning = serializeVisibility("admin_portal.0.enable_provisioning")
configuration.Navigation.Roles = serializeVisibility("admin_portal.0.enable_roles")
configuration.Navigation.Security = serializeVisibility("admin_portal.0.enable_security")
configuration.Navigation.SSO = serializeVisibility("admin_portal.0.enable_sso")