Skip to content

Commit 41f68f9

Browse files
Merge pull request #82 from ConductorOne/bugfix/add_grant_already_exists
[BB-801] add grant already exists for groups
2 parents dbcbf65 + 9577acd commit 41f68f9

File tree

4 files changed

+114
-43
lines changed

4 files changed

+114
-43
lines changed

pkg/client/client.go

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ const (
9292
allGroupsV2 = "rest/api/2/groups/picker"
9393
allPermissionSchemeV2 = "rest/api/2/permissionscheme"
9494
addUserToGroup = "rest/api/2/group/user"
95-
createUserPath = "rest/api/2/user"
95+
baseUserPath = "rest/api/2/user"
9696
NF = -1
9797
)
9898

@@ -331,6 +331,26 @@ func (client *Client) GetUser(ctx context.Context, userName string) (jira.User,
331331
}, err
332332
}
333333

334+
func (client *Client) GetUserByKey(ctx context.Context, userKey string) (*UsersAPIData, error) {
335+
var userAPIData UsersAPIData
336+
req, err := getRequest(ctx, client, baseUserPath, Query{
337+
"key": userKey,
338+
"expand": "groups",
339+
})
340+
if err != nil {
341+
return nil, err
342+
}
343+
344+
resp, err := client.httpClient.Do(req, uhttp.WithJSONResponse(&userAPIData))
345+
if err != nil {
346+
return nil, err
347+
}
348+
349+
defer resp.Body.Close()
350+
351+
return &userAPIData, err
352+
}
353+
334354
// GetProjectRoles
335355
// Returns all roles that are present in specific project.
336356
func (client *Client) GetProjectRoles(ctx context.Context, projectId string) (map[string]string, error) {
@@ -553,12 +573,12 @@ func (client *Client) RemoveActorsFromProjectRole(ctx context.Context, projectId
553573

554574
// AddUserToGroup
555575
// Adds given user to a group in the Jira DC.
556-
// Returns the current state of the group.
576+
// Returns the true is the user was added successfully (status code = 201).
557577
// https://docs.atlassian.com/software/jira/docs/api/REST/9.14.0/#api/2/group-addUserToGroup
558-
func (client *Client) AddUserToGroup(ctx context.Context, groupName, userName string) (int, error) {
578+
func (client *Client) AddUserToGroup(ctx context.Context, groupName, userName string) (bool, error) {
559579
endpointUrl, err := url.JoinPath(addUserToGroup)
560580
if err != nil {
561-
return NF, err
581+
return false, err
562582
}
563583

564584
req, err := getXRequest(ctx, client, http.MethodPost, endpointUrl, Query{
@@ -567,16 +587,16 @@ func (client *Client) AddUserToGroup(ctx context.Context, groupName, userName st
567587
Name: userName,
568588
})
569589
if err != nil {
570-
return NF, err
590+
return false, err
571591
}
572592

573593
resp, err := client.httpClient.Do(req)
574594
if err != nil {
575-
return NF, err
595+
return false, err
576596
}
577597

578598
defer resp.Body.Close()
579-
return resp.StatusCode, err
599+
return resp.StatusCode == http.StatusCreated, err
580600
}
581601

582602
// GetUserName
@@ -600,28 +620,29 @@ func (client *Client) GetUserName(ctx context.Context, userId string) (string, e
600620

601621
// RemoveUserFromGroup
602622
// Removes given user from a group in the Jira DC.
623+
// Returns true if the user was successfully removed.
603624
// https://docs.atlassian.com/software/jira/docs/api/REST/9.14.0/#api/2/group-removeUserFromGroup
604-
func (client *Client) RemoveUserFromGroup(ctx context.Context, groupName, userName string) (int, error) {
625+
func (client *Client) RemoveUserFromGroup(ctx context.Context, groupName, userName string) (bool, error) {
605626
endpointUrl, err := url.JoinPath(addUserToGroup)
606627
if err != nil {
607-
return NF, err
628+
return false, err
608629
}
609630

610631
req, err := getXRequest(ctx, client, http.MethodDelete, endpointUrl, Query{
611632
"groupname": groupName,
612633
"username": userName,
613634
}, BodyActors{})
614635
if err != nil {
615-
return NF, err
636+
return false, err
616637
}
617638

618639
resp, err := client.httpClient.Do(req)
619640
if err != nil {
620-
return NF, err
641+
return false, err
621642
}
622643

623644
defer resp.Body.Close()
624-
return resp.StatusCode, err
645+
return resp.StatusCode == http.StatusOK, err
625646
}
626647

627648
// AddProjectRoleActorsToRole
@@ -693,7 +714,7 @@ func (client *Client) CreateUser(ctx context.Context, userRequest *CreateUserReq
693714
var userData UsersAPIData
694715

695716
// Create the URL
696-
endpointUrl, err := url.JoinPath(client.BaseURL, createUserPath)
717+
endpointUrl, err := url.JoinPath(client.BaseURL, baseUserPath)
697718
if err != nil {
698719
return nil, err
699720
}

pkg/client/internal_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -258,9 +258,9 @@ func TestClientAddUserToGroup(t *testing.T) {
258258
userName, err := client.GetUserName(ctx, "JIRAUSER10103")
259259
assert.Nil(t, err)
260260

261-
statusCode, err := client.AddUserToGroup(ctx, "jira-administrators", userName)
261+
success, err := client.AddUserToGroup(ctx, "jira-administrators", userName)
262262
assert.Nil(t, err)
263-
assert.Equal(t, http.StatusCreated, statusCode)
263+
assert.True(t, success)
264264
}
265265

266266
func TestClientRemoveUserFromGroup(t *testing.T) {
@@ -272,9 +272,9 @@ func TestClientRemoveUserFromGroup(t *testing.T) {
272272
userName, err := client.GetUserName(ctx, "JIRAUSER10103")
273273
assert.Nil(t, err)
274274

275-
statusCode, err := client.RemoveUserFromGroup(ctx, "jira-administrators", userName)
275+
success, err := client.RemoveUserFromGroup(ctx, "jira-administrators", userName)
276276
assert.Nil(t, err)
277-
assert.Equal(t, http.StatusOK, statusCode)
277+
assert.True(t, success)
278278
}
279279

280280
func TestClientAddProjectRoleActorsToRoleWithGroup(t *testing.T) {

pkg/client/model.go

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,27 @@ type Actors struct {
7373
}
7474

7575
type UsersAPIData struct {
76-
Self string `json:"self,omitempty"`
77-
Key string `json:"key,omitempty"`
78-
Name string `json:"name,omitempty"`
79-
EmailAddress string `json:"emailAddress,omitempty"`
80-
AvatarUrls AvatarUrls `json:"avatarUrls,omitempty"`
81-
DisplayName string `json:"displayName,omitempty"`
82-
Active bool `json:"active,omitempty"`
83-
Deleted bool `json:"deleted,omitempty"`
84-
TimeZone string `json:"timeZone,omitempty"`
85-
Locale string `json:"locale,omitempty"`
76+
Self string `json:"self,omitempty"`
77+
Key string `json:"key,omitempty"`
78+
Name string `json:"name,omitempty"`
79+
EmailAddress string `json:"emailAddress,omitempty"`
80+
AvatarUrls AvatarUrls `json:"avatarUrls,omitempty"`
81+
DisplayName string `json:"displayName,omitempty"`
82+
Active bool `json:"active,omitempty"`
83+
Deleted bool `json:"deleted,omitempty"`
84+
TimeZone string `json:"timeZone,omitempty"`
85+
Locale string `json:"locale,omitempty"`
86+
Groups *UserGroupsList `json:"groups,omitempty"`
87+
}
88+
89+
type UserGroupsList struct {
90+
Size int `json:"size,omitempty"`
91+
Items []UserGroup `json:"items,omitempty"`
92+
}
93+
94+
type UserGroup struct {
95+
Name string `json:"name,omitempty"`
96+
Self string `json:"self,omitempty"`
8697
}
8798

8899
type GroupRolesAPIData struct {

pkg/connector/group.go

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"errors"
66
"fmt"
7-
"net/http"
87

98
v2 "github.com/conductorone/baton-sdk/pb/c1/connector/v2"
109
"github.com/conductorone/baton-sdk/pkg/annotations"
@@ -163,25 +162,40 @@ func (g *groupBuilder) Grant(ctx context.Context, principal *v2.Resource, entitl
163162

164163
groupId := entitlement.Resource.Id.Resource
165164
userId := principal.Id.Resource
166-
userName, err := g.client.GetUserName(ctx, userId)
165+
userInfo, err := g.client.GetUserByKey(ctx, userId)
167166
if err != nil {
168-
return nil, err
167+
return nil, fmt.Errorf("jira(dc)-connector: error retrieving user %s: %w", userId, err)
168+
}
169+
170+
// Check user's group membership
171+
if userInfo.Groups != nil && userInfo.Groups.Size > 0 {
172+
for _, group := range userInfo.Groups.Items {
173+
if group.Name == groupId {
174+
l.Debug("Group Membership already exists.",
175+
zap.String("userId", userId),
176+
zap.String("userName", userInfo.Name),
177+
zap.String("group", groupId),
178+
)
179+
return annotations.New(&v2.GrantAlreadyExists{}), nil
180+
}
181+
}
169182
}
170183

171-
statusCode, err := g.client.AddUserToGroup(ctx, groupId, userName)
184+
succeded, err := g.client.AddUserToGroup(ctx, groupId, userInfo.Name)
172185
err = getError(err)
173186
if err != nil {
174187
return nil, err
175188
}
176189

177-
if statusCode == http.StatusCreated {
178-
l.Warn("Group Membership has been created.",
179-
zap.String("userId", userId),
180-
zap.String("userName", userName),
181-
zap.String("group", groupId),
182-
)
190+
if !succeded {
191+
return nil, fmt.Errorf("jira(dc)-connector: failed when granting user to group %s", groupId)
183192
}
184193

194+
l.Warn("Group Membership has been created.",
195+
zap.String("userId", userId),
196+
zap.String("userName", userInfo.Name),
197+
zap.String("group", groupId),
198+
)
185199
return nil, nil
186200
}
187201

@@ -207,25 +221,50 @@ func (g *groupBuilder) Revoke(ctx context.Context, grant *v2.Grant) (annotations
207221

208222
groupId := projectResourceId.Resource
209223
userId := principal.Id.Resource
210-
userName, err := g.client.GetUserName(ctx, userId)
224+
userInfo, err := g.client.GetUserByKey(ctx, userId)
211225
if err != nil {
212-
return nil, err
226+
return nil, fmt.Errorf("jira(dc)-connector: error retrieving user %s: %w", userId, err)
213227
}
214228

215-
statusCode, err := g.client.RemoveUserFromGroup(ctx, groupId, userName)
229+
// Check user's group membership
230+
membershipExists := false
231+
if userInfo.Groups != nil && userInfo.Groups.Size > 0 {
232+
for _, group := range userInfo.Groups.Items {
233+
if group.Name == groupId {
234+
membershipExists = true
235+
break
236+
}
237+
}
238+
}
239+
if !membershipExists {
240+
l.Debug("Group Membership already revoked.",
241+
zap.String("userId", userId),
242+
zap.String("userName", userInfo.Name),
243+
zap.String("group", groupId),
244+
)
245+
return annotations.New(&v2.GrantAlreadyRevoked{}), nil
246+
}
247+
248+
success, err := g.client.RemoveUserFromGroup(ctx, groupId, userInfo.Name)
216249
err = getError(err)
217250
if err != nil {
218251
return nil, err
219252
}
220253

221-
if statusCode == http.StatusOK {
222-
l.Warn("Group Membership has been revoked.",
254+
if !success {
255+
l.Error("Failed to revoke Group Membership.",
223256
zap.String("groupId", groupId),
224257
zap.String("userId", userId),
225-
zap.String("userName", userName),
258+
zap.String("userName", userInfo.Name),
226259
)
260+
return nil, fmt.Errorf("jira(dc)-connector: failed when revoking user from group %s", groupId)
227261
}
228262

263+
l.Warn("Group Membership has been revoked.",
264+
zap.String("groupId", groupId),
265+
zap.String("userId", userId),
266+
zap.String("userName", userInfo.Name),
267+
)
229268
return nil, nil
230269
}
231270
func newGroupBuilder(client *client.Client) *groupBuilder {

0 commit comments

Comments
 (0)