Skip to content

Commit 02a3ac4

Browse files
feat(agent): add group-based SCM tools access control
1 parent 7bb82f9 commit 02a3ac4

File tree

8 files changed

+122
-17
lines changed

8 files changed

+122
-17
lines changed

.stats.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
configured_endpoints: 159
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-d62ef4b9187c1f3d36f428abc4b31d8a09ffd36e93d39b8136c60c8f463c838e.yml
3-
openapi_spec_hash: d7f01b6f24e88eb46d744ecd28061f26
4-
config_hash: 26e4a10dfc6ec809322e60d889d15414
1+
configured_endpoints: 160
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-a19818e87979929d5484f97ec50318899c659c73733b4a700a41f28687ee2632.yml
3+
openapi_spec_hash: f2d83905d1ed19d50c2f4641ecf29204
4+
config_hash: e84bdcd3fab4b185dd3dd79f70ea527d

api.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,11 +316,13 @@ Response Types:
316316

317317
- <a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go">gitpod</a>.<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go#GroupMembership">GroupMembership</a>
318318
- <a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go">gitpod</a>.<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go#GroupMembershipNewResponse">GroupMembershipNewResponse</a>
319+
- <a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go">gitpod</a>.<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go#GroupMembershipGetResponse">GroupMembershipGetResponse</a>
319320
- <a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go">gitpod</a>.<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go#GroupMembershipDeleteResponse">GroupMembershipDeleteResponse</a>
320321

321322
Methods:
322323

323324
- <code title="post /gitpod.v1.GroupService/CreateMembership">client.Groups.Memberships.<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go#GroupMembershipService.New">New</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, body <a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go">gitpod</a>.<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go#GroupMembershipNewParams">GroupMembershipNewParams</a>) (<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go">gitpod</a>.<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go#GroupMembershipNewResponse">GroupMembershipNewResponse</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
325+
- <code title="post /gitpod.v1.GroupService/GetMembership">client.Groups.Memberships.<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go#GroupMembershipService.Get">Get</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, body <a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go">gitpod</a>.<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go#GroupMembershipGetParams">GroupMembershipGetParams</a>) (<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go">gitpod</a>.<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go#GroupMembershipGetResponse">GroupMembershipGetResponse</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
324326
- <code title="post /gitpod.v1.GroupService/ListMemberships">client.Groups.Memberships.<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go#GroupMembershipService.List">List</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, params <a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go">gitpod</a>.<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go#GroupMembershipListParams">GroupMembershipListParams</a>) (<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go/packages/pagination">pagination</a>.<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go/packages/pagination#MembersPage">MembersPage</a>[<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go">gitpod</a>.<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go#GroupMembership">GroupMembership</a>], <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
325327
- <code title="post /gitpod.v1.GroupService/DeleteMembership">client.Groups.Memberships.<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go#GroupMembershipService.Delete">Delete</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, body <a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go">gitpod</a>.<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go#GroupMembershipDeleteParams">GroupMembershipDeleteParams</a>) (<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go">gitpod</a>.<a href="https://pkg.go.dev/github.com/gitpod-io/gitpod-sdk-go#GroupMembershipDeleteResponse">GroupMembershipDeleteResponse</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
326328

groupmembership.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,36 @@ func (r *GroupMembershipService) New(ctx context.Context, body GroupMembershipNe
6767
return
6868
}
6969

70+
// Gets a specific membership by group ID and subject.
71+
//
72+
// Use this method to:
73+
//
74+
// - Check if a user or service account is a member of a group
75+
// - Verify group membership for access control
76+
//
77+
// ### Examples
78+
//
79+
// - Check user membership:
80+
//
81+
// Checks if a user is a member of a specific group.
82+
//
83+
// ```yaml
84+
// groupId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
85+
// subject:
86+
// id: "f53d2330-3795-4c5d-a1f3-453121af9c60"
87+
// principal: PRINCIPAL_USER
88+
// ```
89+
//
90+
// ### Authorization
91+
//
92+
// All organization members can check group membership (transparency model).
93+
func (r *GroupMembershipService) Get(ctx context.Context, body GroupMembershipGetParams, opts ...option.RequestOption) (res *GroupMembershipGetResponse, err error) {
94+
opts = slices.Concat(r.Options, opts)
95+
path := "gitpod.v1.GroupService/GetMembership"
96+
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...)
97+
return
98+
}
99+
70100
// Lists all memberships of a group.
71101
//
72102
// Use this method to:
@@ -216,6 +246,28 @@ func (r groupMembershipNewResponseJSON) RawJSON() string {
216246
return r.raw
217247
}
218248

249+
type GroupMembershipGetResponse struct {
250+
// The membership if found, nil if subject is not a member
251+
Member GroupMembership `json:"member"`
252+
JSON groupMembershipGetResponseJSON `json:"-"`
253+
}
254+
255+
// groupMembershipGetResponseJSON contains the JSON metadata for the struct
256+
// [GroupMembershipGetResponse]
257+
type groupMembershipGetResponseJSON struct {
258+
Member apijson.Field
259+
raw string
260+
ExtraFields map[string]apijson.Field
261+
}
262+
263+
func (r *GroupMembershipGetResponse) UnmarshalJSON(data []byte) (err error) {
264+
return apijson.UnmarshalRoot(data, r)
265+
}
266+
267+
func (r groupMembershipGetResponseJSON) RawJSON() string {
268+
return r.raw
269+
}
270+
219271
type GroupMembershipDeleteResponse = interface{}
220272

221273
type GroupMembershipNewParams struct {
@@ -228,6 +280,16 @@ func (r GroupMembershipNewParams) MarshalJSON() (data []byte, err error) {
228280
return apijson.MarshalRoot(r)
229281
}
230282

283+
type GroupMembershipGetParams struct {
284+
// Subject to check membership for
285+
Subject param.Field[shared.SubjectParam] `json:"subject,required"`
286+
GroupID param.Field[string] `json:"groupId" format:"uuid"`
287+
}
288+
289+
func (r GroupMembershipGetParams) MarshalJSON() (data []byte, err error) {
290+
return apijson.MarshalRoot(r)
291+
}
292+
231293
type GroupMembershipListParams struct {
232294
Token param.Field[string] `query:"token"`
233295
PageSize param.Field[int64] `query:"pageSize"`

groupmembership_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,35 @@ func TestGroupMembershipNewWithOptionalParams(t *testing.T) {
4343
}
4444
}
4545

46+
func TestGroupMembershipGetWithOptionalParams(t *testing.T) {
47+
t.Skip("Prism tests are disabled")
48+
baseURL := "http://localhost:4010"
49+
if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok {
50+
baseURL = envURL
51+
}
52+
if !testutil.CheckTestServer(t, baseURL) {
53+
return
54+
}
55+
client := gitpod.NewClient(
56+
option.WithBaseURL(baseURL),
57+
option.WithBearerToken("My Bearer Token"),
58+
)
59+
_, err := client.Groups.Memberships.Get(context.TODO(), gitpod.GroupMembershipGetParams{
60+
Subject: gitpod.F(shared.SubjectParam{
61+
ID: gitpod.F("f53d2330-3795-4c5d-a1f3-453121af9c60"),
62+
Principal: gitpod.F(shared.PrincipalUser),
63+
}),
64+
GroupID: gitpod.F("d2c94c27-3b76-4a42-b88c-95a85e392c68"),
65+
})
66+
if err != nil {
67+
var apierr *gitpod.Error
68+
if errors.As(err, &apierr) {
69+
t.Log(string(apierr.DumpRequest(true)))
70+
}
71+
t.Fatalf("err should be nil: %s", err.Error())
72+
}
73+
}
74+
4675
func TestGroupMembershipListWithOptionalParams(t *testing.T) {
4776
t.Skip("Prism tests are disabled")
4877
baseURL := "http://localhost:4010"

organizationpolicy.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,17 +106,21 @@ type AgentPolicy struct {
106106
McpDisabled bool `json:"mcpDisabled,required"`
107107
// scm_tools_disabled controls whether SCM (Source Control Management) tools are
108108
// disabled for agents
109-
ScmToolsDisabled bool `json:"scmToolsDisabled,required"`
110-
JSON agentPolicyJSON `json:"-"`
109+
ScmToolsDisabled bool `json:"scmToolsDisabled,required"`
110+
// scm_tools_allowed_group_id restricts SCM tools access to members of this group.
111+
// Empty means no restriction (all users can use SCM tools if not disabled).
112+
ScmToolsAllowedGroupID string `json:"scmToolsAllowedGroupId"`
113+
JSON agentPolicyJSON `json:"-"`
111114
}
112115

113116
// agentPolicyJSON contains the JSON metadata for the struct [AgentPolicy]
114117
type agentPolicyJSON struct {
115-
CommandDenyList apijson.Field
116-
McpDisabled apijson.Field
117-
ScmToolsDisabled apijson.Field
118-
raw string
119-
ExtraFields map[string]apijson.Field
118+
CommandDenyList apijson.Field
119+
McpDisabled apijson.Field
120+
ScmToolsDisabled apijson.Field
121+
ScmToolsAllowedGroupID apijson.Field
122+
raw string
123+
ExtraFields map[string]apijson.Field
120124
}
121125

122126
func (r *AgentPolicy) UnmarshalJSON(data []byte) (err error) {
@@ -401,6 +405,9 @@ type OrganizationPolicyUpdateParamsAgentPolicy struct {
401405
// mcp_disabled controls whether MCP (Model Context Protocol) is disabled for
402406
// agents
403407
McpDisabled param.Field[bool] `json:"mcpDisabled"`
408+
// scm_tools_allowed_group_id restricts SCM tools access to members of this group.
409+
// Empty means no restriction (all users can use SCM tools if not disabled).
410+
ScmToolsAllowedGroupID param.Field[string] `json:"scmToolsAllowedGroupId"`
404411
// scm_tools_disabled controls whether SCM (Source Control Management) tools are
405412
// disabled for agents
406413
ScmToolsDisabled param.Field[bool] `json:"scmToolsDisabled"`

organizationpolicy_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,10 @@ func TestOrganizationPolicyUpdateWithOptionalParams(t *testing.T) {
5454
_, err := client.Organizations.Policies.Update(context.TODO(), gitpod.OrganizationPolicyUpdateParams{
5555
OrganizationID: gitpod.F("b0e12f6c-4c67-429d-a4a6-d9838b5da047"),
5656
AgentPolicy: gitpod.F(gitpod.OrganizationPolicyUpdateParamsAgentPolicy{
57-
CommandDenyList: gitpod.F([]string{"string"}),
58-
McpDisabled: gitpod.F(true),
59-
ScmToolsDisabled: gitpod.F(true),
57+
CommandDenyList: gitpod.F([]string{"string"}),
58+
McpDisabled: gitpod.F(true),
59+
ScmToolsAllowedGroupID: gitpod.F("scmToolsAllowedGroupId"),
60+
ScmToolsDisabled: gitpod.F(true),
6061
}),
6162
AllowedEditorIDs: gitpod.F([]string{"string"}),
6263
AllowLocalRunners: gitpod.F(true),

project.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,9 @@ type ProjectListParamsFilter struct {
10551055
// runner_ids filters the response to only projects that use environment classes
10561056
// from these runners
10571057
RunnerIDs param.Field[[]string] `json:"runnerIds" format:"uuid"`
1058+
// runner_kinds filters the response to only projects that use environment classes
1059+
// from runners of these kinds
1060+
RunnerKinds param.Field[[]RunnerKind] `json:"runnerKinds"`
10581061
// search performs case-insensitive search across project name, project ID, and
10591062
// repository name
10601063
Search param.Field[string] `json:"search"`

project_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,10 @@ func TestProjectListWithOptionalParams(t *testing.T) {
171171
Token: gitpod.F("token"),
172172
PageSize: gitpod.F(int64(0)),
173173
Filter: gitpod.F(gitpod.ProjectListParamsFilter{
174-
ProjectIDs: gitpod.F([]string{"182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"}),
175-
RunnerIDs: gitpod.F([]string{"182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"}),
176-
Search: gitpod.F("search"),
174+
ProjectIDs: gitpod.F([]string{"182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"}),
175+
RunnerIDs: gitpod.F([]string{"182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"}),
176+
RunnerKinds: gitpod.F([]gitpod.RunnerKind{gitpod.RunnerKindUnspecified}),
177+
Search: gitpod.F("search"),
177178
}),
178179
Pagination: gitpod.F(gitpod.ProjectListParamsPagination{
179180
Token: gitpod.F("token"),

0 commit comments

Comments
 (0)