Skip to content

Commit 043ff04

Browse files
feat: default group name is required when creating new users. New user is added to that group when created
1 parent 492d9d9 commit 043ff04

File tree

5 files changed

+153
-78
lines changed

5 files changed

+153
-78
lines changed

pkg/connector/connector.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,16 @@ func (d *Connector) Metadata(ctx context.Context) (*v2.ConnectorMetadata, error)
6767
Placeholder: "Username",
6868
Order: 3,
6969
},
70+
"group_name": {
71+
DisplayName: "Group Name",
72+
Required: true,
73+
Description: "The group indicated will be used assigned to the user.",
74+
Field: &v2.ConnectorAccountCreationSchema_Field_StringField{
75+
StringField: &v2.ConnectorAccountCreationSchema_StringField{},
76+
},
77+
Placeholder: "Group Name",
78+
Order: 4,
79+
},
7080
"group_id_for_saml": {
7181
DisplayName: "Group ID for SAML",
7282
Required: false,
@@ -75,7 +85,7 @@ func (d *Connector) Metadata(ctx context.Context) (*v2.ConnectorMetadata, error)
7585
StringField: &v2.ConnectorAccountCreationSchema_StringField{},
7686
},
7787
Placeholder: "Group ID for SAML",
78-
Order: 4,
88+
Order: 5,
7989
},
8090
},
8191
},

pkg/connector/gitlab/client.go

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ package gitlab
22

33
import (
44
"context"
5-
"net/http"
6-
"strconv"
7-
85
"github.com/conductorone/baton-sdk/pkg/uhttp"
96
gitlabSDK "gitlab.com/gitlab-org/api/client-go"
107
)
@@ -31,39 +28,3 @@ func NewClient(ctx context.Context, accessToken, baseURL string) (*Client, error
3128
Client: client,
3229
}, nil
3330
}
34-
35-
// GetAllUsers retrieves the whole list of Users of the GitLab instance.
36-
// Endpoint: /api/v4/users.
37-
func (c *Client) GetAllUsers(ctx context.Context, nextPageToken string) ([]gitlabSDK.User, *gitlabSDK.Response, error) {
38-
var nextPage int
39-
var err error
40-
41-
if nextPageToken != "" {
42-
nextPage, err = strconv.Atoi(nextPageToken)
43-
if err != nil {
44-
return nil, nil, err
45-
}
46-
}
47-
48-
usersPath := "users"
49-
opt := &gitlabSDK.ListGroupsOptions{
50-
ListOptions: gitlabSDK.ListOptions{
51-
Page: nextPage,
52-
},
53-
}
54-
var options []gitlabSDK.RequestOptionFunc
55-
options = append(options, gitlabSDK.WithContext(ctx))
56-
57-
req, err := c.NewRequest(http.MethodGet, usersPath, opt, options)
58-
if err != nil {
59-
return nil, nil, err
60-
}
61-
62-
var users []gitlabSDK.User
63-
resp, err := c.Do(req, &users)
64-
if err != nil {
65-
return nil, nil, err
66-
}
67-
68-
return users, resp, nil
69-
}

pkg/connector/groups.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ func groupResource(group *gitlabSDK.Group, parentResourceID *v2.ResourceId) (*v2
5757
resourceSdk.WithAnnotation(
5858
&v2.ChildResourceType{ResourceTypeId: projectResourceType.Id},
5959
&v2.ChildResourceType{ResourceTypeId: groupResourceType.Id},
60+
&v2.ChildResourceType{ResourceTypeId: userResourceType.Id},
6061
),
6162
resourceSdk.WithParentResourceID(parentResourceID),
6263
)

pkg/connector/projects.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ func projectResource(project *gitlabSDK.Project, parentResourceID *v2.ResourceId
3838
},
3939
),
4040
},
41+
resourceSdk.WithAnnotation(&v2.ChildResourceType{ResourceTypeId: userResourceType.Id}),
4142
resourceSdk.WithParentResourceID(parentResourceID),
4243
)
4344
}

pkg/connector/users.go

Lines changed: 140 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -24,31 +24,6 @@ func (o *userBuilder) ResourceType(ctx context.Context) *v2.ResourceType {
2424
return userResourceType
2525
}
2626

27-
func userResource(user gitlabSDK.User, parentResourceID *v2.ResourceId) (*v2.Resource, error) {
28-
profile := map[string]interface{}{
29-
"id": user.ID,
30-
"first_name": user.Name,
31-
"username": user.Username,
32-
"email": user.Email,
33-
"state": user.State,
34-
}
35-
36-
userTraitOptions := []resourceSdk.UserTraitOption{
37-
resourceSdk.WithEmail(user.Email, true),
38-
resourceSdk.WithStatus(v2.UserTrait_Status_STATUS_ENABLED),
39-
resourceSdk.WithUserProfile(profile),
40-
resourceSdk.WithUserLogin(user.Email),
41-
}
42-
43-
return resourceSdk.NewUserResource(
44-
user.Name,
45-
userResourceType,
46-
user.ID,
47-
userTraitOptions,
48-
resourceSdk.WithParentResourceID(parentResourceID),
49-
)
50-
}
51-
5227
func (o *userBuilder) setEmailsGroupMembers(ctx context.Context, users []*gitlabSDK.GroupMember) []*gitlabSDK.GroupMember {
5328
for i, user := range users {
5429
details, _, err := o.Users.GetUser(user.ID, gitlabSDK.GetUsersOptions{}, gitlabSDK.WithContext(ctx))
@@ -82,35 +57,67 @@ func (o *userBuilder) setEmailsProjectMembers(ctx context.Context, users []*gitl
8257
// List returns all the users from the database as resource objects.
8358
// Users include a UserTrait because they are the 'shape' of a standard user.
8459
func (o *userBuilder) List(ctx context.Context, parentResourceID *v2.ResourceId, pToken *pagination.Token) ([]*v2.Resource, string, annotations.Annotations, error) {
85-
var (
86-
users []gitlabSDK.User
87-
res *gitlabSDK.Response
88-
pageToken string
89-
err error
90-
)
60+
var users []any
61+
var res *gitlabSDK.Response
62+
var groupId string
63+
var err error
64+
65+
// If the parent resource is nil, this function will exit, the users will be request based on the Groups and Projects received on the arguments.
66+
if parentResourceID == nil {
67+
return nil, "", nil, nil
68+
}
9169

92-
if pToken != nil {
93-
pageToken = pToken.Token
70+
var groupMembers []*gitlabSDK.GroupMember
71+
if parentResourceID.ResourceType == groupResourceType.Id {
72+
groupId, _, err = fromGroupResourceId(parentResourceID.Resource)
73+
if err != nil {
74+
return nil, "", nil, fmt.Errorf("error parsing group resource id: %w", err)
75+
}
76+
if pToken.Token == "" {
77+
groupMembers, res, err = o.ListGroupMembers(ctx, groupId)
78+
} else {
79+
groupMembers, res, err = o.ListGroupMembersPaginate(ctx, groupId, pToken.Token)
80+
}
81+
}
82+
if err != nil {
83+
return nil, "", nil, err
84+
}
85+
86+
groupMembers = o.setEmailsGroupMembers(ctx, groupMembers)
87+
for _, member := range groupMembers {
88+
users = append(users, member)
89+
}
90+
91+
var projectMembers []*gitlabSDK.ProjectMember
92+
if parentResourceID.ResourceType == projectResourceType.Id {
93+
if pToken.Token == "" {
94+
projectMembers, res, err = o.ListProjectMembers(ctx, parentResourceID.Resource)
95+
} else {
96+
projectMembers, res, err = o.ListProjectMembersPaginate(ctx, parentResourceID.Resource, pToken.Token)
97+
}
9498
}
95-
users, res, err = o.Client.GetAllUsers(ctx, pageToken)
9699
if err != nil {
97100
return nil, "", nil, err
98101
}
99102

103+
projectMembers = o.setEmailsProjectMembers(ctx, projectMembers)
104+
for _, member := range projectMembers {
105+
users = append(users, member)
106+
}
107+
100108
outResources := make([]*v2.Resource, 0, len(users))
101109
for _, user := range users {
102-
resource, err := userResource(user, parentResourceID)
110+
resource, err := userResource(user)
103111
if err != nil {
104112
return nil, "", nil, err
105113
}
106114
outResources = append(outResources, resource)
107115
}
108116

109117
var nextPage string
110-
if res.NextPage != 0 {
118+
if res != nil && res.NextPage != 0 {
111119
nextPage = strconv.Itoa(res.NextPage)
112120
}
113-
114121
return outResources, nextPage, nil, nil
115122
}
116123

@@ -149,12 +156,23 @@ func (o *userBuilder) CreateAccount(
149156
return nil, nil, nil, err
150157
}
151158

159+
groupID, err := o.validateUserGroup(ctx, accountInfo)
160+
if err != nil {
161+
return nil, nil, nil, err
162+
}
163+
152164
user, _, err := o.Users.CreateUser(createUserOpts)
153165
if err != nil {
154166
return nil, nil, nil, err
155167
}
156168

157-
userResource, err := userResource(*user, nil)
169+
accessLevelValue := AccessLevel("Guest")
170+
err = o.AddGroupMember(ctx, groupID, user.ID, accessLevelValue)
171+
if err != nil {
172+
return nil, nil, nil, err
173+
}
174+
175+
userResource, err := userResource(user)
158176
if err != nil {
159177
return nil, nil, nil, err
160178
}
@@ -236,3 +254,87 @@ func newUserBuilder(client *gitlab.Client) *userBuilder {
236254
Client: client,
237255
}
238256
}
257+
258+
func (o *userBuilder) validateUserGroup(ctx context.Context, accountInfo *v2.AccountInfo) (string, error) {
259+
pMap := accountInfo.Profile.AsMap()
260+
groupName, ok := pMap["group_name"].(string)
261+
if !ok || groupName == "" {
262+
return "", fmt.Errorf("group_name is required")
263+
}
264+
265+
groups, _, err := o.Groups.ListGroups(&gitlabSDK.ListGroupsOptions{
266+
Search: &groupName,
267+
},
268+
gitlabSDK.WithContext(ctx),
269+
)
270+
if err != nil {
271+
return "", err
272+
}
273+
274+
for _, group := range groups {
275+
if group.Name == groupName {
276+
return strconv.Itoa(group.ID), nil
277+
}
278+
}
279+
280+
return "", fmt.Errorf("group name %s not found", groupName)
281+
}
282+
283+
func userResource(user any) (*v2.Resource, error) {
284+
var id int
285+
// NOTE: The email attribute is only visible to group owners for enterprise users of the group when an API request is sent to the group itself, or that group's subgroups or projects.
286+
// https://docs.gitlab.com/ee/api/members.html#known-issues
287+
var email string
288+
var username string
289+
var name string
290+
var state string
291+
var accessLevel int
292+
293+
switch user := user.(type) {
294+
case *gitlabSDK.GroupMember:
295+
id = user.ID
296+
email = user.Email
297+
state = user.State
298+
name = user.Name
299+
username = user.Username
300+
accessLevel = int(user.AccessLevel)
301+
case *gitlabSDK.ProjectMember:
302+
id = user.ID
303+
email = user.Email
304+
state = user.State
305+
name = user.Name
306+
username = user.Username
307+
accessLevel = int(user.AccessLevel)
308+
case *gitlabSDK.User:
309+
id = user.ID
310+
email = user.Email
311+
state = user.State
312+
name = user.Name
313+
username = user.Username
314+
default:
315+
return nil, fmt.Errorf("unknown user type: %T", user)
316+
}
317+
318+
profile := map[string]interface{}{
319+
"first_name": name,
320+
"username": username,
321+
"email": email,
322+
"state": state,
323+
"access_level": accessLevel,
324+
"id": id,
325+
}
326+
327+
userTraitOptions := []resourceSdk.UserTraitOption{
328+
resourceSdk.WithEmail(email, true),
329+
resourceSdk.WithStatus(v2.UserTrait_Status_STATUS_ENABLED),
330+
resourceSdk.WithUserProfile(profile),
331+
resourceSdk.WithUserLogin(email),
332+
}
333+
334+
return resourceSdk.NewUserResource(
335+
name,
336+
userResourceType,
337+
id,
338+
userTraitOptions,
339+
)
340+
}

0 commit comments

Comments
 (0)