Skip to content

Commit 7e06fa4

Browse files
committed
chore: support set roles for user in workspace level
1 parent be77d5c commit 7e06fa4

File tree

10 files changed

+405
-69
lines changed

10 files changed

+405
-69
lines changed

api/client.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ type Client interface {
7676
// GetProjectIAMPolicy gets the project IAM policy by project full name.
7777
GetProjectIAMPolicy(ctx context.Context, projectName string) (*v1pb.IamPolicy, error)
7878
// SetProjectIAMPolicy sets the project IAM policy.
79-
SetProjectIAMPolicy(ctx context.Context, projectName string, iamPolicy *v1pb.IamPolicy) (*v1pb.IamPolicy, error)
79+
SetProjectIAMPolicy(ctx context.Context, projectName string, update *v1pb.SetIamPolicyRequest) (*v1pb.IamPolicy, error)
8080

8181
// Setting
8282
// ListSettings lists all settings.
@@ -98,7 +98,7 @@ type Client interface {
9898
// CreateVCSProvider creates the vcs provider.
9999
CreateVCSProvider(ctx context.Context, vcsID string, vcs *v1pb.VCSProvider) (*v1pb.VCSProvider, error)
100100
// UpdateVCSProvider updates the vcs provider.
101-
UpdateVCSProvider(ctx context.Context, patch *v1pb.VCSProvider, updateMasks []string) (*v1pb.VCSConnector, error)
101+
UpdateVCSProvider(ctx context.Context, patch *v1pb.VCSProvider, updateMasks []string) (*v1pb.VCSProvider, error)
102102
// DeleteVCSProvider deletes the vcs provider.
103103
DeleteVCSProvider(ctx context.Context, name string) error
104104

@@ -127,4 +127,10 @@ type Client interface {
127127
DeleteUser(ctx context.Context, userName string) error
128128
// UndeleteUser undeletes the user by name.
129129
UndeleteUser(ctx context.Context, userName string) (*v1pb.User, error)
130+
131+
// Workspace
132+
// GetWorkspaceIAMPolicy gets the workspace IAM policy.
133+
GetWorkspaceIAMPolicy(ctx context.Context) (*v1pb.IamPolicy, error)
134+
// SetWorkspaceIAMPolicy sets the workspace IAM policy.
135+
SetWorkspaceIAMPolicy(ctx context.Context, setIamPolicyRequest *v1pb.SetIamPolicyRequest) (*v1pb.IamPolicy, error)
130136
}

client/project.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,8 @@ func (c *client) GetProjectIAMPolicy(ctx context.Context, projectName string) (*
4141
}
4242

4343
// SetProjectIAMPolicy sets the project IAM policy.
44-
func (c *client) SetProjectIAMPolicy(ctx context.Context, projectName string, iamPolicy *v1pb.IamPolicy) (*v1pb.IamPolicy, error) {
45-
payload, err := protojson.Marshal(&v1pb.SetIamPolicyRequest{
46-
Policy: iamPolicy,
47-
})
44+
func (c *client) SetProjectIAMPolicy(ctx context.Context, projectName string, update *v1pb.SetIamPolicyRequest) (*v1pb.IamPolicy, error) {
45+
payload, err := protojson.Marshal(update)
4846
if err != nil {
4947
return nil, err
5048
}

client/vcs.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,13 @@ func (c *client) CreateVCSProvider(ctx context.Context, vcsID string, vcs *v1pb.
7272
}
7373

7474
// UpdateVCSProvider updates the vcs provider.
75-
func (c *client) UpdateVCSProvider(ctx context.Context, patch *v1pb.VCSProvider, updateMasks []string) (*v1pb.VCSConnector, error) {
75+
func (c *client) UpdateVCSProvider(ctx context.Context, patch *v1pb.VCSProvider, updateMasks []string) (*v1pb.VCSProvider, error) {
7676
body, err := c.updateResource(ctx, patch.Name, patch, updateMasks, false /* allow missing = false*/)
7777
if err != nil {
7878
return nil, err
7979
}
8080

81-
var res v1pb.VCSConnector
81+
var res v1pb.VCSProvider
8282
if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil {
8383
return nil, err
8484
}

client/workspace.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package client
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
"strings"
8+
9+
v1pb "github.com/bytebase/bytebase/proto/generated-go/v1"
10+
"google.golang.org/protobuf/encoding/protojson"
11+
)
12+
13+
// GetWorkspaceIAMPolicy gets the workspace IAM policy.
14+
func (c *client) GetWorkspaceIAMPolicy(ctx context.Context) (*v1pb.IamPolicy, error) {
15+
body, err := c.getResource(ctx, "workspaces/-:getIamPolicy")
16+
if err != nil {
17+
return nil, err
18+
}
19+
20+
var res v1pb.IamPolicy
21+
if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil {
22+
return nil, err
23+
}
24+
25+
return &res, nil
26+
}
27+
28+
// SetWorkspaceIAMPolicy sets the workspace IAM policy.
29+
func (c *client) SetWorkspaceIAMPolicy(ctx context.Context, setIamPolicyRequest *v1pb.SetIamPolicyRequest) (*v1pb.IamPolicy, error) {
30+
payload, err := protojson.Marshal(setIamPolicyRequest)
31+
if err != nil {
32+
return nil, err
33+
}
34+
35+
req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/%s/%s:setIamPolicy", c.url, c.version, "workspaces/-"), strings.NewReader(string(payload)))
36+
37+
if err != nil {
38+
return nil, err
39+
}
40+
41+
body, err := c.doRequest(req)
42+
if err != nil {
43+
return nil, err
44+
}
45+
46+
var res v1pb.IamPolicy
47+
if err := ProtojsonUnmarshaler.Unmarshal(body, &res); err != nil {
48+
return nil, err
49+
}
50+
51+
return &res, nil
52+
}

examples/setup/main.tf

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,22 @@ resource "bytebase_instance" "prod" {
9696
}
9797
}
9898

99+
# Create a new user.
100+
resource "bytebase_user" "workspace_dba" {
101+
title = "DBA"
102+
103+
104+
# Grant workspace level roles.
105+
roles = ["roles/workspaceDBA"]
106+
}
107+
99108
# Create a new user.
100109
resource "bytebase_user" "project_developer" {
101110
title = "Developer"
102111
112+
113+
# Grant workspace level roles, will grant projectViewer for this user in all projects.
114+
roles = ["roles/projectViewer"]
103115
}
104116

105117
# Create a new project

provider/data_source_user.go

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ func dataSourceUser() *schema.Resource {
4343
Computed: true,
4444
Description: "The user phone.",
4545
},
46+
"roles": {
47+
Type: schema.TypeSet,
48+
Computed: true,
49+
Elem: &schema.Schema{
50+
Type: schema.TypeString,
51+
},
52+
Description: "The user's roles in the workspace level",
53+
},
4654
"type": {
4755
Type: schema.TypeString,
4856
Computed: true,
@@ -88,10 +96,29 @@ func dataSourceUserRead(ctx context.Context, d *schema.ResourceData, m interface
8896

8997
d.SetId(user.Name)
9098

91-
return setUser(d, user)
99+
return setUser(ctx, c, d, user)
100+
}
101+
102+
func getUserRoles(iamPolicy *v1pb.IamPolicy, email string) []string {
103+
userBinding := fmt.Sprintf("user:%s", email)
104+
roles := []string{}
105+
106+
for _, binding := range iamPolicy.Bindings {
107+
for _, member := range binding.Members {
108+
if member == userBinding {
109+
roles = append(roles, binding.Role)
110+
}
111+
}
112+
}
113+
return roles
92114
}
93115

94-
func setUser(d *schema.ResourceData, user *v1pb.User) diag.Diagnostics {
116+
func setUser(ctx context.Context, client api.Client, d *schema.ResourceData, user *v1pb.User) diag.Diagnostics {
117+
workspaceIAM, err := client.GetWorkspaceIAMPolicy(ctx)
118+
if err != nil {
119+
return diag.Errorf("cannot get workspace IAM with error: %s", err.Error())
120+
}
121+
95122
if err := d.Set("title", user.Title); err != nil {
96123
return diag.Errorf("cannot set title for user: %s", err.Error())
97124
}
@@ -121,6 +148,9 @@ func setUser(d *schema.ResourceData, user *v1pb.User) diag.Diagnostics {
121148
return diag.Errorf("cannot set source for user: %s", err.Error())
122149
}
123150
}
151+
if err := d.Set("roles", getUserRoles(workspaceIAM, user.Email)); err != nil {
152+
return diag.Errorf("cannot set roles for user: %s", err.Error())
153+
}
124154

125155
return nil
126156
}

provider/data_source_user_list.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ func dataSourceUserList() *schema.Resource {
5252
Computed: true,
5353
Description: "The user type.",
5454
},
55+
"roles": {
56+
Type: schema.TypeSet,
57+
Computed: true,
58+
Elem: &schema.Schema{
59+
Type: schema.TypeString,
60+
},
61+
Description: "The user's roles in the workspace level",
62+
},
5563
"mfa_enabled": {
5664
Type: schema.TypeBool,
5765
Computed: true,
@@ -92,6 +100,11 @@ func dataSourceUserListRead(ctx context.Context, d *schema.ResourceData, m inter
92100
return diag.FromErr(err)
93101
}
94102

103+
workspaceIAM, err := c.GetWorkspaceIAMPolicy(ctx)
104+
if err != nil {
105+
return diag.Errorf("cannot get workspace IAM with error: %s", err.Error())
106+
}
107+
95108
users := make([]map[string]interface{}, 0)
96109
for _, user := range response.Users {
97110
raw := make(map[string]interface{})
@@ -107,6 +120,7 @@ func dataSourceUserListRead(ctx context.Context, d *schema.ResourceData, m inter
107120
raw["last_login_time"] = p.LastLoginTime.AsTime().UTC().Format(time.RFC3339)
108121
raw["last_change_password_time"] = p.LastChangePasswordTime.AsTime().UTC().Format(time.RFC3339)
109122
}
123+
raw["roles"] = getUserRoles(workspaceIAM, user.Email)
110124
users = append(users, raw)
111125
}
112126
if err := d.Set("users", users); err != nil {

0 commit comments

Comments
 (0)