From 7759ad9095dd481ab53e2654de8aec1ae453024b Mon Sep 17 00:00:00 2001 From: Matt Hodgson Date: Tue, 16 Aug 2022 12:14:26 -0400 Subject: [PATCH 1/3] Add Group SAML Links --- groups.go | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++ groups_test.go | 72 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+) diff --git a/groups.go b/groups.go index cfd9ef5f4..e7eb48c3f 100644 --- a/groups.go +++ b/groups.go @@ -75,6 +75,7 @@ type Group struct { LDAPCN string `json:"ldap_cn"` LDAPAccess AccessLevelValue `json:"ldap_access"` LDAPGroupLinks []*LDAPGroupLink `json:"ldap_group_links"` + SAMLGroupLinks []*SAMLGroupLink `json:"saml_group_links"` SharedRunnersMinutesLimit int `json:"shared_runners_minutes_limit"` ExtraSharedRunnersMinutesLimit int `json:"extra_shared_runners_minutes_limit"` PreventForkingOutsideGroup bool `json:"prevent_forking_outside_group"` @@ -100,6 +101,14 @@ type LDAPGroupLink struct { Provider string `json:"provider"` } +// SAMLGroupLink represents a GitLab SAML group link. +// +// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#saml-group-links +type SAMLGroupLink struct { + AccessLevel string `json:"access_level"` + Name string `json:"name"` +} + // ListGroupsOptions represents the available ListGroups() options. // // GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#list-project-groups @@ -744,6 +753,92 @@ func (s *GroupsService) DeleteGroupLDAPLinkForProvider(gid interface{}, provider return s.client.Do(req, nil) } +// ListGroupSAMLLinks lists the group's SAML links. Available only for users who +// can edit groups. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/groups.html#list-saml-group-links +func (s *GroupsService) ListGroupSAMLLinks(gid interface{}, options ...RequestOptionFunc) ([]*SAMLGroupLink, *Response, error) { + group, err := parseID(gid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("groups/%s/saml_group_links", PathEscape(group)) + + req, err := s.client.NewRequest(http.MethodGet, u, nil, options) + if err != nil { + return nil, nil, err + } + + var gls []*SAMLGroupLink + resp, err := s.client.Do(req, &gls) + if err != nil { + return nil, resp, err + } + + return gls, resp, nil +} + +// AddGroupSAMLLinkOptions represents the available AddGroupSAMLLink() options. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/groups.html#add-saml-group-link +type AddGroupSAMLLinkOptions struct { + AccessLevel *string `url:"access_level,omitempty" json:"access_level,omitempty"` + SamlGroupName *string `url:"saml_group_name,omitempty" json:"saml_group_name,omitempty"` +} + +// AddGroupSAMLLink creates a new group SAML link. Available only for users who +// can edit groups. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/groups.html#add-saml-group-link +func (s *GroupsService) AddGroupSAMLLink(gid interface{}, opt *AddGroupSAMLLinkOptions, options ...RequestOptionFunc) (*SAMLGroupLink, *Response, error) { + group, err := parseID(gid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("groups/%s/saml_group_links", PathEscape(group)) + + req, err := s.client.NewRequest(http.MethodPost, u, opt, options) + if err != nil { + return nil, nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return nil, resp, err + } + + // The API returns a null response for new created objects, so we construct the result for a successfull post from the inputs. + gl := SAMLGroupLink{ + AccessLevel: AddGroupSAMLLinkOptions.AccessLevel, + Name: AddGroupSAMLLinkOptions.SamlGroupName + } + + return gl, resp, err +} + +// DeleteGroupSAMLLink deletes a group SAML link. Available only for users who +// can edit groups. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/groups.html#delete-saml-group-link +func (s *GroupsService) DeleteGroupSAMLLink(gid interface{}, saml_group_name string, options ...RequestOptionFunc) (*Response, error) { + group, err := parseID(gid) + if err != nil { + return nil, err + } + u := fmt.Sprintf("groups/%s/saml_group_links/%s", PathEscape(group), PathEscape(saml_group_name)) + + req, err := s.client.NewRequest(http.MethodDelete, u, nil, options) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + // ShareGroupWithGroupOptions represents the available ShareGroupWithGroup() options. // // GitLab API docs: diff --git a/groups_test.go b/groups_test.go index 8a37a43d4..9752d127f 100644 --- a/groups_test.go +++ b/groups_test.go @@ -364,6 +364,78 @@ func TestAddGroupLDAPLinkFilter(t *testing.T) { } } +func TestListGroupSAMLLinks(t *testing.T) { + mux, server, client := setup(t) + defer teardown(server) + + mux.HandleFunc("/api/v4/groups/1/saml_group_links", + func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprint(w, `[ + { + "access_level":"Developer", + "name":"gitlab_group_example_developer" + }, + { + "access_level":"Maintainer", + "name":"gitlab_group_example_maintainer" + } +]`) + }) + + links, _, err := client.Groups.ListGroupSAMLLinks(1) + if err != nil { + t.Errorf("Groups.ListGroupSAMLLinks returned error: %v", err) + } + + want := []*SAMLGroupLink{ + { + AccessLevel: "Developer", + Name: "gitlab_group_example_developer", + }, + { + AccessLevel: "Maintainer", + Name: "gitlab_group_example_maintainer", + }, + } + if !reflect.DeepEqual(want, links) { + t.Errorf("Groups.ListGroupSAMLLinks returned %+v, want %+v", links, want) + } +} + +func TestAddGroupSAMLLink(t *testing.T) { + mux, server, client := setup(t) + defer teardown(server) + + mux.HandleFunc("/api/v4/groups/1/saml_group_links", + func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodPost) + fmt.Fprint(w, ` +{ + "access_level":"Developer", + "saml_group_name":"gitlab_group_example_developer" +}`) + }) + + opt := &AddGroupSAMLLinkOptions{ + AccessLevel: String("developer"), + SamlGroupName: String("gitlab_group_example_developer"), + } + + link, _, err := client.Groups.AddGroupSAMLLink(1, opt) + if err != nil { + t.Errorf("Groups.AddGroupSAMLLink returned error: %v", err) + } + + want := &SAMLGroupLink{ + AccessLevel: "Developer", + Name: "gitlab_group_example_developer", + } + if !reflect.DeepEqual(want, link) { + t.Errorf("Groups.AddGroupSAMLLink returned %+v, want %+v", link, want) + } +} + func TestRestoreGroup(t *testing.T) { mux, server, client := setup(t) defer teardown(server) From d9e4486f157c9016e3eb6a0fd31f34a4e4120ea5 Mon Sep 17 00:00:00 2001 From: Matt Hodgson Date: Tue, 16 Aug 2022 17:57:18 +0000 Subject: [PATCH 2/3] Fix compile and test errors --- groups.go | 6 +++--- groups_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/groups.go b/groups.go index e7eb48c3f..a53bb7bdc 100644 --- a/groups.go +++ b/groups.go @@ -812,11 +812,11 @@ func (s *GroupsService) AddGroupSAMLLink(gid interface{}, opt *AddGroupSAMLLinkO // The API returns a null response for new created objects, so we construct the result for a successfull post from the inputs. gl := SAMLGroupLink{ - AccessLevel: AddGroupSAMLLinkOptions.AccessLevel, - Name: AddGroupSAMLLinkOptions.SamlGroupName + AccessLevel: *opt.AccessLevel, + Name: *opt.SamlGroupName, } - return gl, resp, err + return &gl, resp, err } // DeleteGroupSAMLLink deletes a group SAML link. Available only for users who diff --git a/groups_test.go b/groups_test.go index 9752d127f..78d06d1ec 100644 --- a/groups_test.go +++ b/groups_test.go @@ -418,7 +418,7 @@ func TestAddGroupSAMLLink(t *testing.T) { }) opt := &AddGroupSAMLLinkOptions{ - AccessLevel: String("developer"), + AccessLevel: String("Developer"), SamlGroupName: String("gitlab_group_example_developer"), } From 54f06ed5f10c531b9d31bf7c930ad3ca87770df0 Mon Sep 17 00:00:00 2001 From: Matt Hodgson Date: Wed, 17 Aug 2022 13:23:44 +0000 Subject: [PATCH 3/3] Update to match API in master (15.3) --- groups.go | 37 +++++++++++++++++++++++++++++-------- groups_test.go | 30 +++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/groups.go b/groups.go index a53bb7bdc..3535b83d6 100644 --- a/groups.go +++ b/groups.go @@ -779,6 +779,32 @@ func (s *GroupsService) ListGroupSAMLLinks(gid interface{}, options ...RequestOp return gls, resp, nil } +// GetGroupSAMLLink get a specific group SAML link. Available only for users who +// can edit groups. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/groups.html#get-saml-group-link +func (s *GroupsService) GetGroupSAMLLink(gid interface{}, saml_group_name string, options ...RequestOptionFunc) (*SAMLGroupLink, *Response, error) { + group, err := parseID(gid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf("groups/%s/saml_group_links/%s", PathEscape(group), PathEscape(saml_group_name)) + + req, err := s.client.NewRequest(http.MethodGet, u, nil, options) + if err != nil { + return nil, nil, err + } + + gl := new(SAMLGroupLink) + resp, err := s.client.Do(req, &gl) + if err != nil { + return nil, resp, err + } + + return gl, resp, nil +} + // AddGroupSAMLLinkOptions represents the available AddGroupSAMLLink() options. // // GitLab API docs: @@ -805,18 +831,13 @@ func (s *GroupsService) AddGroupSAMLLink(gid interface{}, opt *AddGroupSAMLLinkO return nil, nil, err } - resp, err := s.client.Do(req, nil) + gl := new(SAMLGroupLink) + resp, err := s.client.Do(req, &gl) if err != nil { return nil, resp, err } - // The API returns a null response for new created objects, so we construct the result for a successfull post from the inputs. - gl := SAMLGroupLink{ - AccessLevel: *opt.AccessLevel, - Name: *opt.SamlGroupName, - } - - return &gl, resp, err + return gl, resp, err } // DeleteGroupSAMLLink deletes a group SAML link. Available only for users who diff --git a/groups_test.go b/groups_test.go index 78d06d1ec..1a015803e 100644 --- a/groups_test.go +++ b/groups_test.go @@ -403,6 +403,34 @@ func TestListGroupSAMLLinks(t *testing.T) { } } +func TestGetGroupSAMLLink(t *testing.T) { + mux, server, client := setup(t) + defer teardown(server) + + mux.HandleFunc("/api/v4/groups/1/saml_group_links/gitlab_group_example_developer", + func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + fmt.Fprint(w, ` +{ + "access_level":"Developer", + "name":"gitlab_group_example_developer" +}`) + }) + + links, _, err := client.Groups.GetGroupSAMLLink(1, "gitlab_group_example_developer") + if err != nil { + t.Errorf("Groups.GetGroupSAMLLinks returned error: %v", err) + } + + want := &SAMLGroupLink{ + AccessLevel: "Developer", + Name: "gitlab_group_example_developer", + } + if !reflect.DeepEqual(want, links) { + t.Errorf("Groups.GetGroupSAMLLink returned %+v, want %+v", links, want) + } +} + func TestAddGroupSAMLLink(t *testing.T) { mux, server, client := setup(t) defer teardown(server) @@ -413,7 +441,7 @@ func TestAddGroupSAMLLink(t *testing.T) { fmt.Fprint(w, ` { "access_level":"Developer", - "saml_group_name":"gitlab_group_example_developer" + "name":"gitlab_group_example_developer" }`) })