Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for directory role #30

Merged
merged 1 commit into from
Apr 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions msgraph/directory_role_templates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ func testDirectoryRoleTemplatesClient_List(t *testing.T, c DirectoryRoleTemplate
}

func testDirectoryRoleTemplatesClient_Get(t *testing.T, c DirectoryRoleTemplatesClientTest, id string) (directoryRoleTemplate *msgraph.DirectoryRoleTemplate) {
domain, status, err := c.client.Get(c.connection.Context, id)
directoryRoleTemplate, status, err := c.client.Get(c.connection.Context, id)
if err != nil {
t.Fatalf("DirectoryRoleTemplatesClient.Get(): %v", err)
}
if status < 200 || status >= 300 {
t.Fatalf("DirectoryRoleTemplatesClient.Get(): invalid status: %d", status)
}
if domain == nil {
t.Fatal("DirectoryRoleTemplatesClient.Get(): domain was nil")
if directoryRoleTemplate == nil {
t.Fatal("DirectoryRoleTemplatesClient.Get(): directoryRoleTemplate was nil")
}
return
}
222 changes: 222 additions & 0 deletions msgraph/directory_roles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
package msgraph

import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"regexp"

"github.com/manicminer/hamilton/odata"
)

// DirectoryRolesClient performs operations on DirectoryRoles.
type DirectoryRolesClient struct {
BaseClient Client
}

// NewDirectoryRolesClient returns a new DirectoryRolesClient
func NewDirectoryRolesClient(tenantId string) *DirectoryRolesClient {
return &DirectoryRolesClient{
BaseClient: NewClient(Version10, tenantId),
}
}

// List returns a list of DirectoryRoles.
func (c *DirectoryRolesClient) List(ctx context.Context) (*[]DirectoryRole, int, error) {
resp, status, _, err := c.BaseClient.Get(ctx, GetHttpRequestInput{
ValidStatusCodes: []int{http.StatusOK},
Uri: Uri{
Entity: "/directoryRoles",
HasTenantId: true,
},
})
if err != nil {
return nil, status, fmt.Errorf("DirectoryRolesClient.BaseClient.Get(): %v", err)
}
defer resp.Body.Close()
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, status, fmt.Errorf("ioutil.ReadAll(): %v", err)
}
var data struct {
DirectoryRoles []DirectoryRole `json:"value"`
}
if err := json.Unmarshal(respBody, &data); err != nil {
return nil, status, fmt.Errorf("json.Unmarshal(): %v", err)
}
return &data.DirectoryRoles, status, nil
}

// Get retrieves an DirectoryRoles manifest.
func (c *DirectoryRolesClient) Get(ctx context.Context, id string) (*DirectoryRole, int, error) {
resp, status, _, err := c.BaseClient.Get(ctx, GetHttpRequestInput{
ValidStatusCodes: []int{http.StatusOK},
Uri: Uri{
Entity: fmt.Sprintf("/directoryRoles/%s", id),
HasTenantId: true,
},
})
if err != nil {
return nil, status, fmt.Errorf("DirectoryRolesClient.BaseClient.Get(): %v", err)
}
defer resp.Body.Close()
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, status, fmt.Errorf("ioutil.ReadAll(): %v", err)
}
var dirRole DirectoryRole
if err := json.Unmarshal(respBody, &dirRole); err != nil {
return nil, status, fmt.Errorf("json.Unmarshal(): %v", err)
}
return &dirRole, status, nil
}

// ListMembers retrieves the members of the specified directory role.
// id is the object ID of the directory role.
func (c *DirectoryRolesClient) ListMembers(ctx context.Context, id string) (*[]string, int, error) {
resp, status, _, err := c.BaseClient.Get(ctx, GetHttpRequestInput{
ValidStatusCodes: []int{http.StatusOK},
Uri: Uri{
Entity: fmt.Sprintf("/directoryRoles/%s/members", id),
Params: url.Values{"$select": []string{"id"}},
HasTenantId: true,
},
})
if err != nil {
return nil, status, fmt.Errorf("DirectoryRolesClient.BaseClient.Get(): %v", err)
}
defer resp.Body.Close()
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, status, fmt.Errorf("ioutil.ReadAll(): %v", err)
}
var data struct {
Members []struct {
Type string `json:"@odata.type"`
Id string `json:"id"`
} `json:"value"`
}
if err := json.Unmarshal(respBody, &data); err != nil {
return nil, status, fmt.Errorf("json.Unmarshal(): %v", err)
}
ret := make([]string, len(data.Members))
for i, v := range data.Members {
ret[i] = v.Id
}
return &ret, status, nil
}

// AddMembers adds a new member to a Directory Role.
// First populate the Members field of the DirectoryRole using the AppendMember method of the model, then call this method.
func (c *DirectoryRolesClient) AddMembers(ctx context.Context, directoryRole *DirectoryRole) (int, error) {
var status int
if directoryRole.ID == nil {
return status, errors.New("cannot update directory role with nil ID")
}
if directoryRole.Members == nil {
return status, errors.New("cannot update directory role with nil Owners")
}
for _, member := range *directoryRole.Members {
// don't fail if an member already exists
checkMemberAlreadyExists := func(resp *http.Response, o *odata.OData) bool {
if resp.StatusCode == http.StatusBadRequest {
if o.Error != nil {
re := regexp.MustCompile("One or more added object references already exist")
if re.MatchString(o.Error.String()) {
return true
}
}
}
return false
}
data := struct {
Member string `json:"@odata.id"`
}{
Member: member,
}
body, err := json.Marshal(data)
if err != nil {
return status, fmt.Errorf("json.Marshal(): %v", err)
}
_, status, _, err = c.BaseClient.Post(ctx, PostHttpRequestInput{
Body: body,
ValidStatusCodes: []int{http.StatusNoContent},
ValidStatusFunc: checkMemberAlreadyExists,
Uri: Uri{
Entity: fmt.Sprintf("/directoryRoles/%s/members/$ref", *directoryRole.ID),
HasTenantId: true,
},
})
if err != nil {
return status, fmt.Errorf("DirectoryRolesClient.BaseClient.Post(): %v", err)
}
}
return status, nil
}

// RemoveMembers removes members from a Directory Role.
// id is the object ID of the Directory Role.
// memberIds is a *[]string containing object IDs of members to remove.
func (c *DirectoryRolesClient) RemoveMembers(ctx context.Context, directoryRoleId string, memberIds *[]string) (int, error) {
var status int
if memberIds == nil {
return status, errors.New("cannot remove, nil memberIds")
}
for _, memberId := range *memberIds {
// check for membership before attempting deletion
if _, status, err := c.GetMember(ctx, directoryRoleId, memberId); err != nil {
if status == http.StatusNotFound {
continue
}
return status, err
}
var err error
_, status, _, err = c.BaseClient.Delete(ctx, DeleteHttpRequestInput{
ValidStatusCodes: []int{http.StatusNoContent},
Uri: Uri{
Entity: fmt.Sprintf("/directoryRoles/%s/members/%s/$ref", directoryRoleId, memberId),
HasTenantId: true,
},
})
if err != nil {
return status, fmt.Errorf("DirectoryRolesClient.BaseClient.Delete(): %v", err)
}
}
return status, nil
}

// GetMember retrieves a single member of the specified DirectoryRole.
// directoryRoleId is the object ID of the directory role.
// memberId is the object ID of the member object.
func (c *DirectoryRolesClient) GetMember(ctx context.Context, directoryRoleId, memberId string) (*string, int, error) {
resp, status, _, err := c.BaseClient.Get(ctx, GetHttpRequestInput{
ValidStatusCodes: []int{http.StatusOK},
Uri: Uri{
Entity: fmt.Sprintf("/directoryRoles/%s/members/%s/$ref", directoryRoleId, memberId),
Params: url.Values{"$select": []string{"id,url"}},
HasTenantId: true,
},
})
if err != nil {
return nil, status, fmt.Errorf("DirectoryRolesClient.BaseClient.Get(): %v", err)
}
defer resp.Body.Close()
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, status, fmt.Errorf("ioutil.ReadAll(): %v", err)
}
var data struct {
Context string `json:"@odata.context"`
Type string `json:"@odata.type"`
Id string `json:"id"`
Url string `json:"url"`
}
if err := json.Unmarshal(respBody, &data); err != nil {
return nil, status, fmt.Errorf("json.Unmarshal(): %v", err)
}
return &data.Id, status, nil
}
141 changes: 141 additions & 0 deletions msgraph/directory_roles_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package msgraph_test

import (
"fmt"
"testing"

"github.com/manicminer/hamilton/auth"
"github.com/manicminer/hamilton/internal/test"
"github.com/manicminer/hamilton/internal/utils"
"github.com/manicminer/hamilton/msgraph"
)

type DirectoryRolesClientTest struct {
connection *test.Connection
client *msgraph.DirectoryRolesClient
randomString string
}

func TestDirectoryRolesClient(t *testing.T) {
rs := test.RandomString()
// set up groups test client
groupsClient := GroupsClientTest{
connection: test.NewConnection(auth.MsGraph, auth.TokenVersion2),
randomString: rs,
}
groupsClient.client = msgraph.NewGroupsClient(groupsClient.connection.AuthConfig.TenantID)
groupsClient.client.BaseClient.Authorizer = groupsClient.connection.Authorizer

// set up directory roles test client
dirRolesClient := DirectoryRolesClientTest{
connection: test.NewConnection(auth.MsGraph, auth.TokenVersion2),
randomString: rs,
}
dirRolesClient.client = msgraph.NewDirectoryRolesClient(dirRolesClient.connection.AuthConfig.TenantID)
dirRolesClient.client.BaseClient.Authorizer = dirRolesClient.connection.Authorizer

// list directory roles; usually at least few directory roles are activated within a tenant
directoryRoles := testDirectoryRolesClient_List(t, dirRolesClient)
directoryRole := (*directoryRoles)[0]
testDirectoryRolesClient_Get(t, dirRolesClient, *directoryRole.ID)

// create a new test group which can be later assigned as a member of the previously listed directory role
newGroup := msgraph.Group{
DisplayName: utils.StringPtr("Test Group"),
MailEnabled: utils.BoolPtr(false),
MailNickname: utils.StringPtr(fmt.Sprintf("test-group-%s", groupsClient.randomString)),
SecurityEnabled: utils.BoolPtr(true),
// required attribute to set if you plan to assign a directory role to an object (e.g. user, group etc)
// can be only set on a group creation using MS Graph Beta API
IsAssignableToRole: utils.BoolPtr(true),
}
group := testGroupsClient_Create(t, groupsClient, newGroup)

// add the test group as a member of directory role
directoryRole.AppendMember(dirRolesClient.client.BaseClient.Endpoint, dirRolesClient.client.BaseClient.ApiVersion, *group.ID)
testDirectoryRolesClient_AddMembers(t, dirRolesClient, &directoryRole)

// list members of the directory role; then remove all members to clean up
members := testDirectoryRolesClient_ListMembers(t, dirRolesClient, *directoryRole.ID)
testDirectoryRolesClient_GetMember(t, dirRolesClient, *directoryRole.ID, (*members)[0])
testDirectoryRolesClient_RemoveMembers(t, dirRolesClient, *directoryRole.ID, members)

// remove the test group to clean up
testGroupsClient_Delete(t, groupsClient, *group.ID)
}

func testDirectoryRolesClient_List(t *testing.T, c DirectoryRolesClientTest) (directoryRoles *[]msgraph.DirectoryRole) {
directoryRoles, _, err := c.client.List(c.connection.Context)
if err != nil {
t.Fatalf("DirectoryRolesClient.List(): %v", err)
}
if directoryRoles == nil {
t.Fatal("DirectoryRolesClient.List(): directoryRoles was nil")
}
return
}

func testDirectoryRolesClient_Get(t *testing.T, c DirectoryRolesClientTest, id string) (directoryRole *msgraph.DirectoryRole) {
directoryRole, status, err := c.client.Get(c.connection.Context, id)
if err != nil {
t.Fatalf("DirectoryRolesClient.Get(): %v", err)
}
if status < 200 || status >= 300 {
t.Fatalf("DirectoryRolesClient.Get(): invalid status: %d", status)
}
if directoryRole == nil {
t.Fatal("DirectoryRolesClient.Get(): directoryRole was nil")
}
return
}

func testDirectoryRolesClient_ListMembers(t *testing.T, c DirectoryRolesClientTest, id string) (members *[]string) {
members, status, err := c.client.ListMembers(c.connection.Context, id)
if err != nil {
t.Fatalf("DirectoryRolesClient.ListMembers(): %v", err)
}
if status < 200 || status >= 300 {
t.Fatalf("DirectoryRolesClient.ListMembers(): invalid status: %d", status)
}
if members == nil {
t.Fatal("DirectoryRolesClient.ListMembers(): members was nil")
}
if len(*members) == 0 {
t.Fatal("DirectoryRolesClient.ListMembers(): members was empty")
}
return
}

func testDirectoryRolesClient_GetMember(t *testing.T, c DirectoryRolesClientTest, dirRoleId string, memberId string) (member *string) {
member, status, err := c.client.GetMember(c.connection.Context, dirRoleId, memberId)
if err != nil {
t.Fatalf("DirectoryRolesClient.GetMember(): %v", err)
}
if status < 200 || status >= 300 {
t.Fatalf("DirectoryRolesClient.GetMember(): invalid status: %d", status)
}
if member == nil {
t.Fatal("DirectoryRolesClient.GetMember(): member was nil")
}
return
}

func testDirectoryRolesClient_RemoveMembers(t *testing.T, c DirectoryRolesClientTest, dirRoleId string, memberIds *[]string) {
status, err := c.client.RemoveMembers(c.connection.Context, dirRoleId, memberIds)
if err != nil {
t.Fatalf("DirectoryRolesClient.RemoveMembers(): %v", err)
}
if status < 200 || status >= 300 {
t.Fatalf("DirectoryRolesClient.RemoveMembers(): invalid status: %d", status)
}
}

func testDirectoryRolesClient_AddMembers(t *testing.T, c DirectoryRolesClientTest, dirRole *msgraph.DirectoryRole) {
status, err := c.client.AddMembers(c.connection.Context, dirRole)
if err != nil {
t.Fatalf("DirectoryRolesClient.AddMembers(): %v", err)
}
if status < 200 || status >= 300 {
t.Fatalf("DirectoryRolesClient.AddMembers(): invalid status: %d", status)
}
}
Loading