-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #86 from eli-guidewire/group-role-assignment
Added support for TeamCity Role Assignments for User Groups
- Loading branch information
Showing
4 changed files
with
250 additions
and
13 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package teamcity | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
|
||
"github.com/dghubble/sling" | ||
) | ||
|
||
// GroupRoleAssignment is the model for role assignment for groups in TeamCity | ||
type GroupRoleAssignment struct { | ||
GroupKey string //`json:"groupkey,omitempty" xml:"groupkey"` | ||
RoleID string //`json:"roleid,omitempty" xml:"roleid"` | ||
Scope string //`json:"scope,omitempty" xml:"scope"` | ||
} | ||
|
||
// RoleAssignmentReference represents a response of a request to assign role to a group or a user | ||
type RoleAssignmentReference struct { | ||
RoleID string `json:"roleId,omitempty" xml:"roleId"` | ||
Scope string `json:"scope,omitempty" xml:"scope"` | ||
Href string `json:"href,omitempty" xml:"href"` | ||
} | ||
|
||
type roleAssignmentsJSON struct { | ||
Items []RoleAssignmentReference `json:"role"` | ||
} | ||
|
||
// NewGroupRoleAssignment returns an instance of a GroupRoleAssignment. A non-empty groupKey, roleId, and scope is required. | ||
func NewGroupRoleAssignment(groupKey string, roleID string, scope string) (*GroupRoleAssignment, error) { | ||
if groupKey == "" { | ||
return nil, fmt.Errorf("GroupKey is required") | ||
} | ||
|
||
if roleID == "" { | ||
return nil, fmt.Errorf("RoleId is required") | ||
} | ||
|
||
if scope == "" { | ||
return nil, fmt.Errorf("scope is required. Use \"g\" at the global level for System Administrators, otherwise for other roles, use \"p:_Root\" for the root project, or \"p:<project_id>\" for other projects") | ||
} | ||
|
||
return &GroupRoleAssignment{ | ||
GroupKey: groupKey, | ||
RoleID: roleID, | ||
Scope: scope, | ||
}, nil | ||
} | ||
|
||
// RoleAssignmentService has operations for handling role assignments for groups or users | ||
type RoleAssignmentService struct { | ||
groupSling *sling.Sling | ||
httpClient *http.Client | ||
groupHelper *restHelper | ||
} | ||
|
||
func newRoleAssignmentService(base *sling.Sling, httpClient *http.Client) *RoleAssignmentService { | ||
groupSling := base.New().Path(fmt.Sprintf("userGroups/")) | ||
return &RoleAssignmentService{ | ||
httpClient: httpClient, | ||
groupSling: groupSling, | ||
groupHelper: newRestHelperWithSling(httpClient, groupSling), | ||
} | ||
} | ||
|
||
// AssignToGroup adds a role assignment to a group | ||
func (s *RoleAssignmentService) AssignToGroup(assignment *GroupRoleAssignment) (*RoleAssignmentReference, error) { | ||
var out RoleAssignmentReference | ||
|
||
// URL for assigning role is /app/rest/userGroups/{groupLocator}/roles/{roleId}/{scope} | ||
err := s.groupHelper.post(fmt.Sprintf("%s/roles/%s/%s", assignment.GroupKey, assignment.RoleID, assignment.Scope), nil, &out, "AssignToGroup role to group") | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &out, nil | ||
} | ||
|
||
// GetForGroup get a specific role assignment for a group | ||
func (s *RoleAssignmentService) GetForGroup(assignment *GroupRoleAssignment) (*RoleAssignmentReference, error) { | ||
var out RoleAssignmentReference | ||
|
||
// URL for getting a specific role assignments is /app/rest/userGroups/{groupLocator}/roles/{roleId}/{scope} | ||
err := s.groupHelper.get(fmt.Sprintf("%s/roles/%s/%s", assignment.GroupKey, assignment.RoleID, assignment.Scope), &out, "GetForGroup role assignmens for group") | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &out, nil | ||
} | ||
|
||
// GetAllForGroup gets all the role assignments for a group | ||
func (s *RoleAssignmentService) GetAllForGroup(group *Group) ([]RoleAssignmentReference, error) { | ||
var aux roleAssignmentsJSON | ||
|
||
// URL for getting role assignments is /app/rest/userGroups/{groupLocator}/roles | ||
err := s.groupHelper.get(fmt.Sprintf("%s/roles", group.Key), &aux, "GetForGroup role assignments for group") | ||
if err != nil { | ||
return nil, err | ||
} | ||
return aux.Items, nil | ||
} | ||
|
||
// UnassignFromGroup removes the role assignment from a group | ||
func (s *RoleAssignmentService) UnassignFromGroup(assignment *GroupRoleAssignment) error { | ||
// URL for unassigning role is /app/rest/userGroups/{groupLocator}/roles/{roleId}/{scope} | ||
return s.groupHelper.delete(fmt.Sprintf("%s/roles/%s/%s", assignment.GroupKey, assignment.RoleID, assignment.Scope), "UnassignFromGroup role from group") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
package teamcity_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/cvbarros/go-teamcity/teamcity" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestRoleAssignment_AssignGlobalSysAdminToGroup(t *testing.T) { | ||
client := setup() | ||
|
||
newGroup, _ := teamcity.NewGroup("TESTGROUPKEY", "Test Group Name", "Test Group Description") | ||
newGroupRoleAssignment, _ := teamcity.NewGroupRoleAssignment("TESTGROUPKEY", "SYSTEM_ADMIN", "g") // "g" is for sys admins at the global level | ||
|
||
actualGroup, err := client.Groups.Create(newGroup) | ||
require.NoError(t, err) | ||
require.NotNil(t, actualGroup) | ||
|
||
createdGroupRoleAssignment, err := client.RoleAssignments.AssignToGroup(newGroupRoleAssignment) | ||
require.NoError(t, err) | ||
require.NotNil(t, createdGroupRoleAssignment) | ||
assert.NotEmpty(t, createdGroupRoleAssignment.RoleID) | ||
assert.NotEmpty(t, createdGroupRoleAssignment.Scope) | ||
assert.NotEmpty(t, createdGroupRoleAssignment.Href) | ||
|
||
groupRoleAssignments, err := client.RoleAssignments.GetAllForGroup(newGroup) | ||
require.NoError(t, err) | ||
assert.Equal(t, 1, len(groupRoleAssignments)) | ||
actualGroupRoleAssignmentReference := groupRoleAssignments[0] | ||
assert.Equal(t, createdGroupRoleAssignment.RoleID, actualGroupRoleAssignmentReference.RoleID) | ||
assert.Equal(t, createdGroupRoleAssignment.Scope, actualGroupRoleAssignmentReference.Scope) | ||
assert.Equal(t, createdGroupRoleAssignment.Href, actualGroupRoleAssignmentReference.Href) | ||
|
||
actualGroupRoleAssignmentReference2, err := client.RoleAssignments.GetForGroup(newGroupRoleAssignment) | ||
require.NoError(t, err) | ||
assert.Equal(t, createdGroupRoleAssignment.RoleID, actualGroupRoleAssignmentReference2.RoleID) | ||
assert.Equal(t, createdGroupRoleAssignment.Scope, actualGroupRoleAssignmentReference2.Scope) | ||
assert.Equal(t, createdGroupRoleAssignment.Href, actualGroupRoleAssignmentReference2.Href) | ||
|
||
// Clean up group after test | ||
cleanUpGroup(t, client, actualGroup.Key) | ||
} | ||
|
||
func TestRoleAssignment_AssignToGroup(t *testing.T) { | ||
client := setup() | ||
|
||
parent, _ := teamcity.NewProject("ParentProject", "Parent Project", "") | ||
child, _ := teamcity.NewProject("ChildProject", "Child Project", "ParentProject") | ||
|
||
_, err := client.Projects.Create(parent) | ||
require.NoError(t, err) | ||
created, err := client.Projects.Create(child) | ||
require.NoError(t, err) | ||
|
||
newGroup, _ := teamcity.NewGroup("TESTGROUPKEY", "Test Group Name", "Test Group Description") | ||
newGroupRoleAssignment, _ := teamcity.NewGroupRoleAssignment("TESTGROUPKEY", "PROJECT_DEVELOPER", "p:"+created.ID) | ||
|
||
actualGroup, err := client.Groups.Create(newGroup) | ||
require.NoError(t, err) | ||
require.NotNil(t, actualGroup) | ||
|
||
createdGroupRoleAssignment, err := client.RoleAssignments.AssignToGroup(newGroupRoleAssignment) | ||
require.NoError(t, err) | ||
require.NotNil(t, createdGroupRoleAssignment) | ||
assert.NotEmpty(t, createdGroupRoleAssignment.RoleID) | ||
assert.NotEmpty(t, createdGroupRoleAssignment.Scope) | ||
assert.NotEmpty(t, createdGroupRoleAssignment.Href) | ||
|
||
groupRoleAssignments, err := client.RoleAssignments.GetAllForGroup(newGroup) | ||
require.NoError(t, err) | ||
assert.Equal(t, 1, len(groupRoleAssignments)) | ||
actualGroupRoleAssignmentReference := groupRoleAssignments[0] | ||
assert.Equal(t, createdGroupRoleAssignment.RoleID, actualGroupRoleAssignmentReference.RoleID) | ||
assert.Equal(t, createdGroupRoleAssignment.Scope, actualGroupRoleAssignmentReference.Scope) | ||
assert.Equal(t, createdGroupRoleAssignment.Href, actualGroupRoleAssignmentReference.Href) | ||
|
||
actualGroupRoleAssignmentReference2, err := client.RoleAssignments.GetForGroup(newGroupRoleAssignment) | ||
require.NoError(t, err) | ||
assert.Equal(t, createdGroupRoleAssignment.RoleID, actualGroupRoleAssignmentReference2.RoleID) | ||
assert.Equal(t, createdGroupRoleAssignment.Scope, actualGroupRoleAssignmentReference2.Scope) | ||
assert.Equal(t, createdGroupRoleAssignment.Href, actualGroupRoleAssignmentReference2.Href) | ||
|
||
// Clean up group and projects after test | ||
cleanUpGroup(t, client, actualGroup.Key) | ||
cleanUpProject(t, client, "ParentProject") | ||
} | ||
|
||
func TestRoleAssignment_UnassignFromGroup(t *testing.T) { | ||
client := setup() | ||
|
||
parent, _ := teamcity.NewProject("ParentProject", "Parent Project", "") | ||
child, _ := teamcity.NewProject("ChildProject", "Child Project", "ParentProject") | ||
|
||
_, err := client.Projects.Create(parent) | ||
require.NoError(t, err) | ||
created, err := client.Projects.Create(child) | ||
require.NoError(t, err) | ||
|
||
newGroup, _ := teamcity.NewGroup("TESTGROUPKEY", "Test Group Name", "Test Group Description") | ||
newGroupRoleAssignment, _ := teamcity.NewGroupRoleAssignment("TESTGROUPKEY", "PROJECT_DEVELOPER", "p:"+created.ID) | ||
|
||
actualGroup, err := client.Groups.Create(newGroup) | ||
require.NoError(t, err) | ||
require.NotNil(t, actualGroup) | ||
|
||
createdGroupRoleAssignment, err := client.RoleAssignments.AssignToGroup(newGroupRoleAssignment) | ||
require.NoError(t, err) | ||
require.NotNil(t, createdGroupRoleAssignment) | ||
assert.NotEmpty(t, createdGroupRoleAssignment.RoleID) | ||
assert.NotEmpty(t, createdGroupRoleAssignment.Scope) | ||
assert.NotEmpty(t, createdGroupRoleAssignment.Href) | ||
|
||
err = client.RoleAssignments.UnassignFromGroup(newGroupRoleAssignment) | ||
require.NoError(t, err) | ||
|
||
groupRoleAssignments, err := client.RoleAssignments.GetAllForGroup(newGroup) | ||
require.NoError(t, err) | ||
assert.Equal(t, 0, len(groupRoleAssignments)) | ||
|
||
// The Role has been unassigneded, so expect error, and message to contain 404 (NOT FOUND) | ||
_, err = client.RoleAssignments.GetForGroup(newGroupRoleAssignment) | ||
require.Error(t, err) | ||
assert.Contains(t, err.Error(), "404") | ||
|
||
// Clean up group and projects after test | ||
cleanUpGroup(t, client, actualGroup.Key) | ||
cleanUpProject(t, client, "ParentProject") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters