From 7c17fafb683b9e6b27fad2839590881c7538637e Mon Sep 17 00:00:00 2001 From: Benjamin Binier Date: Sat, 9 Mar 2024 08:43:15 +0100 Subject: [PATCH] Add support for groups job token --- job_token_scope.go | 98 +++++++++++++++++++++++++++++++++++++++++ job_token_scope_test.go | 97 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) diff --git a/job_token_scope.go b/job_token_scope.go index ff96ba698..16ca62b56 100644 --- a/job_token_scope.go +++ b/job_token_scope.go @@ -184,3 +184,101 @@ func (j *JobTokenScopeService) RemoveProjectFromJobScopeAllowList(pid interface{ return j.client.Do(req, nil) } + +// JobTokenInboundGroupsAllowItem represents a single job token inbound group allowlist item. +// +// GitLab API docs: https://docs.gitlab.com/ee/api/project_job_token_scopes.html +type JobTokenInboundGroupsAllowItem struct { + SourceProjectID int `json:"source_project_id"` + TargetGroupID int `json:"target_group_id"` +} + +// GetJobTokenInboundGroupsAllowListOptions represents the available +// GetJobTokenInboundGroupsAllowList() options. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/project_job_token_scopes.html#get-a-projects-cicd-job-token-inbound-allowlist +type GetJobTokenInboundGroupsAllowListOptions struct { + ListOptions +} + +// GetProjectJobTokenInboundGroupsAllowList fetches the CI/CD job token inbound +// groups allowlist (job token scope) of a project. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/project_job_token_scopes.html#get-a-projects-cicd-job-token-allowlist-of-groups +func (j *JobTokenScopeService) GetProjectJobTokenInboundGroupsAllowList(pid interface{}, opt *GetJobTokenInboundGroupsAllowListOptions, options ...RequestOptionFunc) ([]*Group, *Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf(`projects/%s/job_token_scope/groups_allowlist`, PathEscape(project)) + + req, err := j.client.NewRequest(http.MethodGet, u, opt, options) + if err != nil { + return nil, nil, err + } + + var ps []*Group + resp, err := j.client.Do(req, &ps) + if err != nil { + return nil, resp, err + } + + return ps, resp, nil +} + +// AddProjectToJobScopeGroupsAllowListOptions represents the available +// AddProjectToJobScopeGroupsAllowList() options. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/project_job_token_scopes.html#add-a-group-to-a-cicd-job-token-allowlist +type JobTokenInboundGroupsAllowOptions struct { + TargetGroupID *int `url:"target_group_id,omitempty" json:"target_group_id,omitempty"` +} + +// AddProjectToJobScopeGroupsAllowList adds a new group to a project's job token +// inbound groups allow list. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/project_job_token_scopes.html#add-a-group-to-a-cicd-job-token-allowlist +func (j *JobTokenScopeService) AddGroupToJobScopeGroupsAllowList(pid interface{}, opt *JobTokenInboundGroupsAllowOptions, options ...RequestOptionFunc) (*JobTokenInboundGroupsAllowItem, *Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, nil, err + } + u := fmt.Sprintf(`projects/%s/job_token_scope/groups_allowlist`, PathEscape(project)) + + req, err := j.client.NewRequest(http.MethodPost, u, opt, options) + if err != nil { + return nil, nil, err + } + + jt := new(JobTokenInboundGroupsAllowItem) + resp, err := j.client.Do(req, jt) + if err != nil { + return nil, resp, err + } + + return jt, resp, nil +} + +// RemoveGroupFromJobScopeAllowList removes a group from a project's job +// token inbound groups allow list. +// +// GitLab API docs: +// https://docs.gitlab.com/ee/api/project_job_token_scopes.html#remove-a-group-from-a-cicd-job-token-allowlist +func (j *JobTokenScopeService) RemoveGroupFromJobScopeGroupsAllowList(pid interface{}, targetGroup int, options ...RequestOptionFunc) (*Response, error) { + project, err := parseID(pid) + if err != nil { + return nil, err + } + u := fmt.Sprintf(`projects/%s/job_token_scope/groups_allowlist/%d`, PathEscape(project), targetGroup) + + req, err := j.client.NewRequest(http.MethodDelete, u, nil, options) + if err != nil { + return nil, err + } + + return j.client.Do(req, nil) +} diff --git a/job_token_scope_test.go b/job_token_scope_test.go index 4300f21cd..4798f2e87 100644 --- a/job_token_scope_test.go +++ b/job_token_scope_test.go @@ -177,3 +177,100 @@ func TestRemoveProjectFromJobScopeAllowList(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 204, resp.StatusCode) } + +// This tests that when calling the GetProjectJobTokenInboundGroupsAllowList, we +// get a list of groups back properly. There isn't a "deep" test with every +// attribute specified, because the object returned is a *Group object, which +// is already tested in groups.go. +func TestGetProjectJobTokenInboundGroupsAllowList(t *testing.T) { + mux, client := setup(t) + + // Handle project ID 1, and print a result of two groups + mux.HandleFunc("/api/v4/projects/1/job_token_scope/groups_allowlist", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodGet) + + // Print on the response + fmt.Fprint(w, `[{"id":1},{"id":2}]`) + }) + + want := []*Group{{ID: 1}, {ID: 2}} + groups, _, err := client.JobTokenScope.GetProjectJobTokenInboundGroupsAllowList( + 1, + &GetJobTokenInboundGroupsAllowListOptions{}, + ) + + assert.NoError(t, err) + assert.Equal(t, want, groups) +} + +func TestAddGroupToJobScopeGroupsAllowList(t *testing.T) { + mux, client := setup(t) + + mux.HandleFunc("/api/v4/projects/1/job_token_scope/groups_allowlist", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodPost) + + // Read the request to determine which target group is passed in + body, err := io.ReadAll(r.Body) + if err != nil { + t.Fatalf("JobTokenScope.AddGroupToJobScopeGroupsAllowList failed to read body") + } + + // Parse to object to ensure it's sent on the request appropriately. + var createTokenRequest JobTokenInboundGroupsAllowOptions + err = json.Unmarshal(body, &createTokenRequest) + if err != nil { + t.Fatalf("JobTokenScope.AddGroupToJobScopeGroupsAllowList failed to unmarshal body: %v", err) + } + + // Ensure we provide the proper response + w.WriteHeader(http.StatusCreated) + + // Print on the response with the proper target group + fmt.Fprintf(w, `{ + "source_project_id": 1, + "target_group_id": %d + }`, *createTokenRequest.TargetGroupID) + }) + + want := &JobTokenInboundGroupsAllowItem{ + SourceProjectID: 1, + TargetGroupID: 2, + } + + addTokenResponse, resp, err := client.JobTokenScope.AddGroupToJobScopeGroupsAllowList( + 1, + &JobTokenInboundGroupsAllowOptions{TargetGroupID: Ptr(2)}, + ) + assert.NoError(t, err) + assert.Equal(t, want, addTokenResponse) + assert.Equal(t, 201, resp.StatusCode) +} + +func TestRemoveGroupFromJobScopeGroupsAllowList(t *testing.T) { + mux, client := setup(t) + + mux.HandleFunc("/api/v4/projects/1/job_token_scope/groups_allowlist/2", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, http.MethodDelete) + + // Read the request to determine which target group is passed in + body, err := io.ReadAll(r.Body) + if err != nil { + t.Fatalf("JobTokenScope.RemoveGroupFromJobScopeGroupsAllowList failed to read body") + } + + // The body should be empty since all attributes are passed in the path + if body != nil && string(body) != "" { + t.Fatalf("JobTokenScope.RemoveGroupFromJobScopeGroupsAllowList failed to unmarshal body: %v", err) + } + + // Ensure we provide the proper response + w.WriteHeader(http.StatusNoContent) + + // Print an empty body, since that's what the API provides. + fmt.Fprint(w, "") + }) + + resp, err := client.JobTokenScope.RemoveGroupFromJobScopeGroupsAllowList(1, 2) + assert.NoError(t, err) + assert.Equal(t, 204, resp.StatusCode) +}