Skip to content

Commit beabcce

Browse files
authored
chore: support workspace profile setting (#84)
* chore: support set roles for user in workspace level * fix: lint * fix: test * chore: support group * fix: test * fix: test * chore: update examples * chore: support database catalog * fix: lint * chore: support workspace profile setting * fix: lint
1 parent 4e6e80b commit beabcce

File tree

10 files changed

+178
-4
lines changed

10 files changed

+178
-4
lines changed

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.0.6
1+
1.0.7

api/setting.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ type SettingName string
66
const (
77
// SettingWorkspaceApproval is the setting name for workspace approval config.
88
SettingWorkspaceApproval SettingName = "bb.workspace.approval"
9+
// SettingWorkspaceProfile is the setting name for workspace profile settings.
10+
SettingWorkspaceProfile SettingName = "bb.workspace.profile"
911
// SettingWorkspaceExternalApproval is the setting name for workspace external approval config.
1012
SettingWorkspaceExternalApproval SettingName = "bb.workspace.approval.external"
1113
)

docs/data-sources/setting.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,28 @@ The setting data source.
1919

2020
- `name` (String)
2121

22+
### Optional
23+
24+
- `workspace_profile` (Block List, Max: 1) (see [below for nested schema](#nestedblock--workspace_profile))
25+
2226
### Read-Only
2327

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

32+
<a id="nestedblock--workspace_profile"></a>
33+
### Nested Schema for `workspace_profile`
34+
35+
Optional:
36+
37+
- `disallow_password_signin` (Boolean) Whether to disallow password signin. (Except workspace admins). Require ENTERPRISE subscription
38+
- `disallow_signup` (Boolean) Disallow self-service signup, users can only be invited by the owner. Require PRO subscription.
39+
- `domains` (List of String) The workspace domain, e.g. bytebase.com. Required for the group
40+
- `enforce_identity_domain` (Boolean) Only user and group from the domains can be created and login.
41+
- `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.
42+
43+
2844
<a id="nestedblock--approval_flow"></a>
2945
### Nested Schema for `approval_flow`
3046

docs/resources/setting.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ The setting resource.
2323

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

2728
### Read-Only
2829

@@ -100,3 +101,16 @@ Required:
100101
- `title` (String) The external node title.
101102

102103

104+
105+
<a id="nestedblock--workspace_profile"></a>
106+
### Nested Schema for `workspace_profile`
107+
108+
Optional:
109+
110+
- `disallow_password_signin` (Boolean) Whether to disallow password signin. (Except workspace admins). Require ENTERPRISE subscription
111+
- `disallow_signup` (Boolean) Disallow self-service signup, users can only be invited by the owner. Require PRO subscription.
112+
- `domains` (List of String) The workspace domain, e.g. bytebase.com. Required for the group
113+
- `enforce_identity_domain` (Boolean) Only user and group from the domains can be created and login.
114+
- `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.
115+
116+

examples/settings/main.tf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,18 @@ data "bytebase_setting" "external_approval" {
2525
name = "bb.workspace.approval.external"
2626
}
2727

28+
data "bytebase_setting" "workspace_profile" {
29+
name = "bb.workspace.profile"
30+
}
31+
2832
output "approval_flow" {
2933
value = data.bytebase_setting.approval_flow
3034
}
3135

3236
output "external_approval" {
3337
value = data.bytebase_setting.external_approval
3438
}
39+
40+
output "workspace_profile" {
41+
value = data.bytebase_setting.workspace_profile
42+
}

examples/setup/gitops.tf

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ resource "bytebase_vcs_provider" "github" {
1010
resource "bytebase_vcs_connector" "github" {
1111
depends_on = [
1212
bytebase_project.sample_project,
13-
bytebase_vcs_provider.github
13+
bytebase_vcs_provider.github,
14+
# vcs connector requires the external_url.
15+
bytebase_setting.workspace_profile
1416
]
1517

1618
resource_id = "connector-github"

examples/setup/main.tf

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,12 @@ locals {
2525
instance_id_prod = "prod-sample-instance"
2626
project_id = "project-sample"
2727
}
28+
29+
resource "bytebase_setting" "workspace_profile" {
30+
name = "bb.workspace.profile"
31+
32+
workspace_profile {
33+
external_url = "https://bytebase.example.com"
34+
domains = ["bytebase.com"]
35+
}
36+
}

examples/setup/users.tf

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ resource "bytebase_user" "project_developer" {
2020
resource "bytebase_group" "developers" {
2121
depends_on = [
2222
bytebase_user.workspace_dba,
23-
bytebase_user.project_developer
23+
bytebase_user.project_developer,
24+
# group requires the domain.
25+
bytebase_setting.workspace_profile
2426
]
2527

2628
email = "developers@bytebase.com"

provider/data_source_setting.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,60 @@ func dataSourceSetting() *schema.Resource {
2828
ValidateFunc: validation.StringInSlice([]string{
2929
string(api.SettingWorkspaceApproval),
3030
string(api.SettingWorkspaceExternalApproval),
31+
string(api.SettingWorkspaceProfile),
3132
}, false),
3233
},
3334
"approval_flow": getWorkspaceApprovalSetting(true),
3435
"external_approval_nodes": getExternalApprovalSetting(true),
36+
"workspace_profile": getWorkspaceProfileSetting(true),
37+
},
38+
}
39+
}
40+
41+
func getWorkspaceProfileSetting(computed bool) *schema.Schema {
42+
return &schema.Schema{
43+
Computed: computed,
44+
Optional: true,
45+
Default: nil,
46+
Type: schema.TypeList,
47+
MaxItems: 1,
48+
MinItems: 1,
49+
Elem: &schema.Resource{
50+
Schema: map[string]*schema.Schema{
51+
"external_url": {
52+
Type: schema.TypeString,
53+
Computed: computed,
54+
Optional: true,
55+
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.",
56+
},
57+
"disallow_signup": {
58+
Type: schema.TypeBool,
59+
Computed: computed,
60+
Optional: true,
61+
Description: "Disallow self-service signup, users can only be invited by the owner. Require PRO subscription.",
62+
},
63+
"disallow_password_signin": {
64+
Type: schema.TypeBool,
65+
Computed: computed,
66+
Optional: true,
67+
Description: "Whether to disallow password signin. (Except workspace admins). Require ENTERPRISE subscription",
68+
},
69+
"domains": {
70+
Type: schema.TypeList,
71+
Computed: computed,
72+
Optional: true,
73+
Description: "The workspace domain, e.g. bytebase.com. Required for the group",
74+
Elem: &schema.Schema{
75+
Type: schema.TypeString,
76+
},
77+
},
78+
"enforce_identity_domain": {
79+
Type: schema.TypeBool,
80+
Computed: computed,
81+
Optional: true,
82+
Description: "Only user and group from the domains can be created and login.",
83+
},
84+
},
3585
},
3686
}
3787
}
@@ -215,6 +265,12 @@ func setSettingMessage(ctx context.Context, d *schema.ResourceData, client api.C
215265
return diag.Errorf("cannot set external_approval_nodes: %s", err.Error())
216266
}
217267
}
268+
if value := setting.Value.GetWorkspaceProfileSettingValue(); value != nil {
269+
settingVal := flattenWorkspaceProfileSetting(value)
270+
if err := d.Set("workspace_profile", settingVal); err != nil {
271+
return diag.Errorf("cannot set workspace_profile: %s", err.Error())
272+
}
273+
}
218274

219275
return nil
220276
}
@@ -354,3 +410,15 @@ func flattenExternalApprovalSetting(setting *v1pb.ExternalApprovalSetting) []int
354410
}
355411
return []interface{}{approvalSetting}
356412
}
413+
414+
func flattenWorkspaceProfileSetting(setting *v1pb.WorkspaceProfileSetting) []interface{} {
415+
raw := map[string]interface{}{}
416+
417+
raw["external_url"] = setting.ExternalUrl
418+
raw["disallow_signup"] = setting.DisallowSignup
419+
raw["disallow_password_signin"] = setting.DisallowPasswordSignin
420+
raw["enforce_identity_domain"] = setting.EnforceIdentityDomain
421+
raw["domains"] = setting.Domains
422+
423+
return []interface{}{raw}
424+
}

provider/resource_setting.go

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@ func resourceSetting() *schema.Resource {
3434
ValidateFunc: validation.StringInSlice([]string{
3535
string(api.SettingWorkspaceApproval),
3636
string(api.SettingWorkspaceExternalApproval),
37+
string(api.SettingWorkspaceProfile),
3738
}, false),
3839
},
3940
"approval_flow": getWorkspaceApprovalSetting(false),
4041
"external_approval_nodes": getExternalApprovalSetting(false),
42+
"workspace_profile": getWorkspaceProfileSetting(false),
4143
},
4244
}
4345
}
@@ -52,6 +54,7 @@ func resourceSettingUpsert(ctx context.Context, d *schema.ResourceData, m interf
5254
setting := &v1pb.Setting{
5355
Name: settingName,
5456
}
57+
updateMasks := []string{}
5558

5659
switch name {
5760
case api.SettingWorkspaceApproval:
@@ -74,11 +77,22 @@ func resourceSettingUpsert(ctx context.Context, d *schema.ResourceData, m interf
7477
ExternalApprovalSettingValue: externalApproval,
7578
},
7679
}
80+
case api.SettingWorkspaceProfile:
81+
workspaceProfile, updatePathes, err := convertToV1WorkspaceProfileSetting(d)
82+
if err != nil {
83+
return diag.FromErr(err)
84+
}
85+
setting.Value = &v1pb.Value{
86+
Value: &v1pb.Value_WorkspaceProfileSettingValue{
87+
WorkspaceProfileSettingValue: workspaceProfile,
88+
},
89+
}
90+
updateMasks = updatePathes
7791
default:
7892
return diag.FromErr(errors.Errorf("Unsupport setting: %v", name))
7993
}
8094

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

110+
func convertToV1WorkspaceProfileSetting(d *schema.ResourceData) (*v1pb.WorkspaceProfileSetting, []string, error) {
111+
rawList, ok := d.Get("workspace_profile").([]interface{})
112+
if !ok || len(rawList) != 1 {
113+
return nil, nil, errors.Errorf("invalid workspace_profile")
114+
}
115+
116+
updateMasks := []string{}
117+
raw := rawList[0].(map[string]interface{})
118+
119+
workspacePrfile := &v1pb.WorkspaceProfileSetting{}
120+
121+
if externalURL, ok := raw["external_url"]; ok {
122+
workspacePrfile.ExternalUrl = externalURL.(string)
123+
updateMasks = append(updateMasks, "value.workspace_profile_setting_value.external_url")
124+
}
125+
if disallowSignup, ok := raw["disallow_signup"]; ok {
126+
workspacePrfile.DisallowSignup = disallowSignup.(bool)
127+
updateMasks = append(updateMasks, "value.workspace_profile_setting_value.disallow_signup")
128+
}
129+
if disallowPasswordSignin, ok := raw["disallow_password_signin"]; ok {
130+
workspacePrfile.DisallowPasswordSignin = disallowPasswordSignin.(bool)
131+
updateMasks = append(updateMasks, "value.workspace_profile_setting_value.disallow_password_signin")
132+
}
133+
if domains, ok := raw["domains"]; ok {
134+
if enforceIdentityDomain, ok := raw["enforce_identity_domain"]; ok {
135+
workspacePrfile.EnforceIdentityDomain = enforceIdentityDomain.(bool)
136+
updateMasks = append(updateMasks, "value.workspace_profile_setting_value.enforce_identity_domain")
137+
}
138+
for _, domain := range domains.([]interface{}) {
139+
workspacePrfile.Domains = append(workspacePrfile.Domains, domain.(string))
140+
}
141+
updateMasks = append(updateMasks, "value.workspace_profile_setting_value.domains")
142+
} else if _, ok := raw["enforce_identity_domain"]; ok {
143+
return nil, nil, errors.Errorf("enforce_identity_domain must works with domains")
144+
}
145+
146+
return workspacePrfile, updateMasks, nil
147+
}
148+
96149
func convertToV1ExternalNodesSetting(d *schema.ResourceData) (*v1pb.ExternalApprovalSetting, error) {
97150
rawList, ok := d.Get("external_approval_nodes").([]interface{})
98151
if !ok || len(rawList) != 1 {

0 commit comments

Comments
 (0)