Skip to content

Commit 9f8d2f9

Browse files
feat(agent): add group-based SCM tools access control
1 parent 892ffd2 commit 9f8d2f9

File tree

9 files changed

+117
-5
lines changed

9 files changed

+117
-5
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
@@ -275,11 +275,13 @@ Types:
275275

276276
- <code><a href="./src/resources/groups/memberships.ts">GroupMembership</a></code>
277277
- <code><a href="./src/resources/groups/memberships.ts">MembershipCreateResponse</a></code>
278+
- <code><a href="./src/resources/groups/memberships.ts">MembershipRetrieveResponse</a></code>
278279
- <code><a href="./src/resources/groups/memberships.ts">MembershipDeleteResponse</a></code>
279280

280281
Methods:
281282

282283
- <code title="post /gitpod.v1.GroupService/CreateMembership">client.groups.memberships.<a href="./src/resources/groups/memberships.ts">create</a>({ ...params }) -> MembershipCreateResponse</code>
284+
- <code title="post /gitpod.v1.GroupService/GetMembership">client.groups.memberships.<a href="./src/resources/groups/memberships.ts">retrieve</a>({ ...params }) -> MembershipRetrieveResponse</code>
283285
- <code title="post /gitpod.v1.GroupService/ListMemberships">client.groups.memberships.<a href="./src/resources/groups/memberships.ts">list</a>({ ...params }) -> GroupMembershipsMembersPage</code>
284286
- <code title="post /gitpod.v1.GroupService/DeleteMembership">client.groups.memberships.<a href="./src/resources/groups/memberships.ts">delete</a>({ ...params }) -> unknown</code>
285287

src/resources/groups/groups.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
MembershipDeleteParams,
1111
MembershipDeleteResponse,
1212
MembershipListParams,
13+
MembershipRetrieveParams,
14+
MembershipRetrieveResponse,
1315
Memberships,
1416
} from './memberships';
1517
import * as RoleAssignmentsAPI from './role-assignments';
@@ -528,9 +530,11 @@ export declare namespace Groups {
528530
Memberships as Memberships,
529531
type GroupMembership as GroupMembership,
530532
type MembershipCreateResponse as MembershipCreateResponse,
533+
type MembershipRetrieveResponse as MembershipRetrieveResponse,
531534
type MembershipDeleteResponse as MembershipDeleteResponse,
532535
type GroupMembershipsMembersPage as GroupMembershipsMembersPage,
533536
type MembershipCreateParams as MembershipCreateParams,
537+
type MembershipRetrieveParams as MembershipRetrieveParams,
534538
type MembershipListParams as MembershipListParams,
535539
type MembershipDeleteParams as MembershipDeleteParams,
536540
};

src/resources/groups/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ export {
1818
Memberships,
1919
type GroupMembership,
2020
type MembershipCreateResponse,
21+
type MembershipRetrieveResponse,
2122
type MembershipDeleteResponse,
2223
type MembershipCreateParams,
24+
type MembershipRetrieveParams,
2325
type MembershipListParams,
2426
type MembershipDeleteParams,
2527
type GroupMembershipsMembersPage,

src/resources/groups/memberships.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,48 @@ export class Memberships extends APIResource {
4848
return this._client.post('/gitpod.v1.GroupService/CreateMembership', { body, ...options });
4949
}
5050

51+
/**
52+
* Gets a specific membership by group ID and subject.
53+
*
54+
* Use this method to:
55+
*
56+
* - Check if a user or service account is a member of a group
57+
* - Verify group membership for access control
58+
*
59+
* ### Examples
60+
*
61+
* - Check user membership:
62+
*
63+
* Checks if a user is a member of a specific group.
64+
*
65+
* ```yaml
66+
* groupId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
67+
* subject:
68+
* id: "f53d2330-3795-4c5d-a1f3-453121af9c60"
69+
* principal: PRINCIPAL_USER
70+
* ```
71+
*
72+
* ### Authorization
73+
*
74+
* All organization members can check group membership (transparency model).
75+
*
76+
* @example
77+
* ```ts
78+
* const membership = await client.groups.memberships.retrieve(
79+
* {
80+
* subject: {
81+
* id: 'f53d2330-3795-4c5d-a1f3-453121af9c60',
82+
* principal: 'PRINCIPAL_USER',
83+
* },
84+
* groupId: 'd2c94c27-3b76-4a42-b88c-95a85e392c68',
85+
* },
86+
* );
87+
* ```
88+
*/
89+
retrieve(body: MembershipRetrieveParams, options?: RequestOptions): APIPromise<MembershipRetrieveResponse> {
90+
return this._client.post('/gitpod.v1.GroupService/GetMembership', { body, ...options });
91+
}
92+
5193
/**
5294
* Lists all memberships of a group.
5395
*
@@ -172,6 +214,13 @@ export interface MembershipCreateResponse {
172214
member?: GroupMembership;
173215
}
174216

217+
export interface MembershipRetrieveResponse {
218+
/**
219+
* The membership if found, nil if subject is not a member
220+
*/
221+
member?: GroupMembership;
222+
}
223+
175224
/**
176225
* Empty response
177226
*/
@@ -186,6 +235,15 @@ export interface MembershipCreateParams {
186235
subject?: Shared.Subject;
187236
}
188237

238+
export interface MembershipRetrieveParams {
239+
/**
240+
* Subject to check membership for
241+
*/
242+
subject: Shared.Subject;
243+
244+
groupId?: string;
245+
}
246+
189247
export interface MembershipListParams extends MembersPageParams {
190248
/**
191249
* Body param:
@@ -228,9 +286,11 @@ export declare namespace Memberships {
228286
export {
229287
type GroupMembership as GroupMembership,
230288
type MembershipCreateResponse as MembershipCreateResponse,
289+
type MembershipRetrieveResponse as MembershipRetrieveResponse,
231290
type MembershipDeleteResponse as MembershipDeleteResponse,
232291
type GroupMembershipsMembersPage as GroupMembershipsMembersPage,
233292
type MembershipCreateParams as MembershipCreateParams,
293+
type MembershipRetrieveParams as MembershipRetrieveParams,
234294
type MembershipListParams as MembershipListParams,
235295
type MembershipDeleteParams as MembershipDeleteParams,
236296
};

src/resources/organizations/policies.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ export interface AgentPolicy {
111111
* disabled for agents
112112
*/
113113
scmToolsDisabled: boolean;
114+
115+
/**
116+
* scm_tools_allowed_group_id restricts SCM tools access to members of this group.
117+
* Empty means no restriction (all users can use SCM tools if not disabled).
118+
*/
119+
scmToolsAllowedGroupId?: string;
114120
}
115121

116122
/**
@@ -411,6 +417,12 @@ export namespace PolicyUpdateParams {
411417
*/
412418
mcpDisabled?: boolean | null;
413419

420+
/**
421+
* scm_tools_allowed_group_id restricts SCM tools access to members of this group.
422+
* Empty means no restriction (all users can use SCM tools if not disabled).
423+
*/
424+
scmToolsAllowedGroupId?: string | null;
425+
414426
/**
415427
* scm_tools_disabled controls whether SCM (Source Control Management) tools are
416428
* disabled for agents

src/resources/projects/projects.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
ProjectPolicy,
2424
ProjectRole,
2525
} from './policies';
26+
import * as RunnersAPI from '../runners/runners';
2627
import { APIPromise } from '../../core/api-promise';
2728
import { PagePromise, ProjectsPage, type ProjectsPageParams } from '../../core/pagination';
2829
import { RequestOptions } from '../../internal/request-options';
@@ -821,6 +822,12 @@ export namespace ProjectListParams {
821822
*/
822823
runnerIds?: Array<string>;
823824

825+
/**
826+
* runner_kinds filters the response to only projects that use environment classes
827+
* from runners of these kinds
828+
*/
829+
runnerKinds?: Array<RunnersAPI.RunnerKind>;
830+
824831
/**
825832
* search performs case-insensitive search across project name, project ID, and
826833
* repository name

tests/api-resources/groups/memberships.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,26 @@ describe('resource memberships', () => {
2020
expect(dataAndResponse.response).toBe(rawResponse);
2121
});
2222

23+
// Prism tests are disabled
24+
test.skip('retrieve: only required params', async () => {
25+
const responsePromise = client.groups.memberships.retrieve({ subject: {} });
26+
const rawResponse = await responsePromise.asResponse();
27+
expect(rawResponse).toBeInstanceOf(Response);
28+
const response = await responsePromise;
29+
expect(response).not.toBeInstanceOf(Response);
30+
const dataAndResponse = await responsePromise.withResponse();
31+
expect(dataAndResponse.data).toBe(response);
32+
expect(dataAndResponse.response).toBe(rawResponse);
33+
});
34+
35+
// Prism tests are disabled
36+
test.skip('retrieve: required and optional params', async () => {
37+
const response = await client.groups.memberships.retrieve({
38+
subject: { id: 'f53d2330-3795-4c5d-a1f3-453121af9c60', principal: 'PRINCIPAL_USER' },
39+
groupId: 'd2c94c27-3b76-4a42-b88c-95a85e392c68',
40+
});
41+
});
42+
2343
// Prism tests are disabled
2444
test.skip('list', async () => {
2545
const responsePromise = client.groups.memberships.list({});

tests/api-resources/organizations/policies.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@ describe('resource policies', () => {
4747
test.skip('update: required and optional params', async () => {
4848
const response = await client.organizations.policies.update({
4949
organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
50-
agentPolicy: { commandDenyList: ['string'], mcpDisabled: true, scmToolsDisabled: true },
50+
agentPolicy: {
51+
commandDenyList: ['string'],
52+
mcpDisabled: true,
53+
scmToolsAllowedGroupId: 'scmToolsAllowedGroupId',
54+
scmToolsDisabled: true,
55+
},
5156
allowedEditorIds: ['string'],
5257
allowLocalRunners: true,
5358
defaultEditorId: 'defaultEditorId',

0 commit comments

Comments
 (0)