Skip to content
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.0.6
1.0.7
2 changes: 2 additions & 0 deletions api/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ type SettingName string
const (
// SettingWorkspaceApproval is the setting name for workspace approval config.
SettingWorkspaceApproval SettingName = "bb.workspace.approval"
// SettingWorkspaceProfile is the setting name for workspace profile settings.
SettingWorkspaceProfile SettingName = "bb.workspace.profile"
// SettingWorkspaceExternalApproval is the setting name for workspace external approval config.
SettingWorkspaceExternalApproval SettingName = "bb.workspace.approval.external"
)
Expand Down
16 changes: 16 additions & 0 deletions docs/data-sources/setting.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,28 @@ The setting data source.

- `name` (String)

### Optional

- `workspace_profile` (Block List, Max: 1) (see [below for nested schema](#nestedblock--workspace_profile))

### Read-Only

- `approval_flow` (Block List) Configure risk level and approval flow for different tasks. Require ENTERPRISE subscription. (see [below for nested schema](#nestedblock--approval_flow))
- `external_approval_nodes` (Block List) Configure external nodes in the approval flow. Require ENTERPRISE subscription. (see [below for nested schema](#nestedblock--external_approval_nodes))
- `id` (String) The ID of this resource.

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

Optional:

- `disallow_password_signin` (Boolean) Whether to disallow password signin. (Except workspace admins). Require ENTERPRISE subscription
- `disallow_signup` (Boolean) Disallow self-service signup, users can only be invited by the owner. Require PRO subscription.
- `domains` (List of String) The workspace domain, e.g. bytebase.com. Required for the group
- `enforce_identity_domain` (Boolean) Only user and group from the domains can be created and login.
- `external_url` (String) The URL user visits Bytebase. The external URL is used for: 1. Constructing the correct callback URL when configuring the VCS provider. The callback URL points to the frontend; 2. Creating the correct webhook endpoint when configuring the project GitOps workflow. The webhook endpoint points to the backend.


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

Expand Down
14 changes: 14 additions & 0 deletions docs/resources/setting.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ The setting resource.

- `approval_flow` (Block List) Configure risk level and approval flow for different tasks. Require ENTERPRISE subscription. (see [below for nested schema](#nestedblock--approval_flow))
- `external_approval_nodes` (Block List) Configure external nodes in the approval flow. Require ENTERPRISE subscription. (see [below for nested schema](#nestedblock--external_approval_nodes))
- `workspace_profile` (Block List, Max: 1) (see [below for nested schema](#nestedblock--workspace_profile))

### Read-Only

Expand Down Expand Up @@ -100,3 +101,16 @@ Required:
- `title` (String) The external node title.



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

Optional:

- `disallow_password_signin` (Boolean) Whether to disallow password signin. (Except workspace admins). Require ENTERPRISE subscription
- `disallow_signup` (Boolean) Disallow self-service signup, users can only be invited by the owner. Require PRO subscription.
- `domains` (List of String) The workspace domain, e.g. bytebase.com. Required for the group
- `enforce_identity_domain` (Boolean) Only user and group from the domains can be created and login.
- `external_url` (String) The URL user visits Bytebase. The external URL is used for: 1. Constructing the correct callback URL when configuring the VCS provider. The callback URL points to the frontend; 2. Creating the correct webhook endpoint when configuring the project GitOps workflow. The webhook endpoint points to the backend.


8 changes: 8 additions & 0 deletions examples/settings/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,18 @@ data "bytebase_setting" "external_approval" {
name = "bb.workspace.approval.external"
}

data "bytebase_setting" "workspace_profile" {
name = "bb.workspace.profile"
}

output "approval_flow" {
value = data.bytebase_setting.approval_flow
}

output "external_approval" {
value = data.bytebase_setting.external_approval
}

output "workspace_profile" {
value = data.bytebase_setting.workspace_profile
}
4 changes: 3 additions & 1 deletion examples/setup/gitops.tf
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ resource "bytebase_vcs_provider" "github" {
resource "bytebase_vcs_connector" "github" {
depends_on = [
bytebase_project.sample_project,
bytebase_vcs_provider.github
bytebase_vcs_provider.github,
# vcs connector requires the external_url.
bytebase_setting.workspace_profile
]

resource_id = "connector-github"
Expand Down
9 changes: 9 additions & 0 deletions examples/setup/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,12 @@ locals {
instance_id_prod = "prod-sample-instance"
project_id = "project-sample"
}

resource "bytebase_setting" "workspace_profile" {
name = "bb.workspace.profile"

workspace_profile {
external_url = "https://bytebase.example.com"
domains = ["bytebase.com"]
}
}
4 changes: 3 additions & 1 deletion examples/setup/users.tf
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ resource "bytebase_user" "project_developer" {
resource "bytebase_group" "developers" {
depends_on = [
bytebase_user.workspace_dba,
bytebase_user.project_developer
bytebase_user.project_developer,
# group requires the domain.
bytebase_setting.workspace_profile
]

email = "[email protected]"
Expand Down
68 changes: 68 additions & 0 deletions provider/data_source_setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,60 @@ func dataSourceSetting() *schema.Resource {
ValidateFunc: validation.StringInSlice([]string{
string(api.SettingWorkspaceApproval),
string(api.SettingWorkspaceExternalApproval),
string(api.SettingWorkspaceProfile),
}, false),
},
"approval_flow": getWorkspaceApprovalSetting(true),
"external_approval_nodes": getExternalApprovalSetting(true),
"workspace_profile": getWorkspaceProfileSetting(true),
},
}
}

func getWorkspaceProfileSetting(computed bool) *schema.Schema {
return &schema.Schema{
Computed: computed,
Optional: true,
Default: nil,
Type: schema.TypeList,
MaxItems: 1,
MinItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"external_url": {
Type: schema.TypeString,
Computed: computed,
Optional: true,
Description: "The URL user visits Bytebase. The external URL is used for: 1. Constructing the correct callback URL when configuring the VCS provider. The callback URL points to the frontend; 2. Creating the correct webhook endpoint when configuring the project GitOps workflow. The webhook endpoint points to the backend.",
},
"disallow_signup": {
Type: schema.TypeBool,
Computed: computed,
Optional: true,
Description: "Disallow self-service signup, users can only be invited by the owner. Require PRO subscription.",
},
"disallow_password_signin": {
Type: schema.TypeBool,
Computed: computed,
Optional: true,
Description: "Whether to disallow password signin. (Except workspace admins). Require ENTERPRISE subscription",
},
"domains": {
Type: schema.TypeList,
Computed: computed,
Optional: true,
Description: "The workspace domain, e.g. bytebase.com. Required for the group",
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"enforce_identity_domain": {
Type: schema.TypeBool,
Computed: computed,
Optional: true,
Description: "Only user and group from the domains can be created and login.",
},
},
},
}
}
Expand Down Expand Up @@ -215,6 +265,12 @@ func setSettingMessage(ctx context.Context, d *schema.ResourceData, client api.C
return diag.Errorf("cannot set external_approval_nodes: %s", err.Error())
}
}
if value := setting.Value.GetWorkspaceProfileSettingValue(); value != nil {
settingVal := flattenWorkspaceProfileSetting(value)
if err := d.Set("workspace_profile", settingVal); err != nil {
return diag.Errorf("cannot set workspace_profile: %s", err.Error())
}
}

return nil
}
Expand Down Expand Up @@ -354,3 +410,15 @@ func flattenExternalApprovalSetting(setting *v1pb.ExternalApprovalSetting) []int
}
return []interface{}{approvalSetting}
}

func flattenWorkspaceProfileSetting(setting *v1pb.WorkspaceProfileSetting) []interface{} {
raw := map[string]interface{}{}

raw["external_url"] = setting.ExternalUrl
raw["disallow_signup"] = setting.DisallowSignup
raw["disallow_password_signin"] = setting.DisallowPasswordSignin
raw["enforce_identity_domain"] = setting.EnforceIdentityDomain
raw["domains"] = setting.Domains

return []interface{}{raw}
}
55 changes: 54 additions & 1 deletion provider/resource_setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ func resourceSetting() *schema.Resource {
ValidateFunc: validation.StringInSlice([]string{
string(api.SettingWorkspaceApproval),
string(api.SettingWorkspaceExternalApproval),
string(api.SettingWorkspaceProfile),
}, false),
},
"approval_flow": getWorkspaceApprovalSetting(false),
"external_approval_nodes": getExternalApprovalSetting(false),
"workspace_profile": getWorkspaceProfileSetting(false),
},
}
}
Expand All @@ -52,6 +54,7 @@ func resourceSettingUpsert(ctx context.Context, d *schema.ResourceData, m interf
setting := &v1pb.Setting{
Name: settingName,
}
updateMasks := []string{}

switch name {
case api.SettingWorkspaceApproval:
Expand All @@ -74,11 +77,22 @@ func resourceSettingUpsert(ctx context.Context, d *schema.ResourceData, m interf
ExternalApprovalSettingValue: externalApproval,
},
}
case api.SettingWorkspaceProfile:
workspaceProfile, updatePathes, err := convertToV1WorkspaceProfileSetting(d)
if err != nil {
return diag.FromErr(err)
}
setting.Value = &v1pb.Value{
Value: &v1pb.Value_WorkspaceProfileSettingValue{
WorkspaceProfileSettingValue: workspaceProfile,
},
}
updateMasks = updatePathes
default:
return diag.FromErr(errors.Errorf("Unsupport setting: %v", name))
}

updatedSetting, err := c.UpsertSetting(ctx, setting, []string{})
updatedSetting, err := c.UpsertSetting(ctx, setting, updateMasks)
if err != nil {
return diag.FromErr(err)
}
Expand All @@ -93,6 +107,45 @@ func resourceSettingUpsert(ctx context.Context, d *schema.ResourceData, m interf
return diags
}

func convertToV1WorkspaceProfileSetting(d *schema.ResourceData) (*v1pb.WorkspaceProfileSetting, []string, error) {
rawList, ok := d.Get("workspace_profile").([]interface{})
if !ok || len(rawList) != 1 {
return nil, nil, errors.Errorf("invalid workspace_profile")
}

updateMasks := []string{}
raw := rawList[0].(map[string]interface{})

workspacePrfile := &v1pb.WorkspaceProfileSetting{}

if externalURL, ok := raw["external_url"]; ok {
workspacePrfile.ExternalUrl = externalURL.(string)
updateMasks = append(updateMasks, "value.workspace_profile_setting_value.external_url")
}
if disallowSignup, ok := raw["disallow_signup"]; ok {
workspacePrfile.DisallowSignup = disallowSignup.(bool)
updateMasks = append(updateMasks, "value.workspace_profile_setting_value.disallow_signup")
}
if disallowPasswordSignin, ok := raw["disallow_password_signin"]; ok {
workspacePrfile.DisallowPasswordSignin = disallowPasswordSignin.(bool)
updateMasks = append(updateMasks, "value.workspace_profile_setting_value.disallow_password_signin")
}
if domains, ok := raw["domains"]; ok {
if enforceIdentityDomain, ok := raw["enforce_identity_domain"]; ok {
workspacePrfile.EnforceIdentityDomain = enforceIdentityDomain.(bool)
updateMasks = append(updateMasks, "value.workspace_profile_setting_value.enforce_identity_domain")
}
for _, domain := range domains.([]interface{}) {
workspacePrfile.Domains = append(workspacePrfile.Domains, domain.(string))
}
updateMasks = append(updateMasks, "value.workspace_profile_setting_value.domains")
} else if _, ok := raw["enforce_identity_domain"]; ok {
return nil, nil, errors.Errorf("enforce_identity_domain must works with domains")
}

return workspacePrfile, updateMasks, nil
}

func convertToV1ExternalNodesSetting(d *schema.ResourceData) (*v1pb.ExternalApprovalSetting, error) {
rawList, ok := d.Get("external_approval_nodes").([]interface{})
if !ok || len(rawList) != 1 {
Expand Down
Loading