Merge pull request #24936 from hghtwr/master

#24889 - Feature: Fetch Gitlab users for self-hosted Gitlab based on group assignment
This commit is contained in:
Ben Lambert
2024-06-25 10:52:37 +02:00
committed by GitHub
13 changed files with 726 additions and 26 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend-module-gitlab': patch
---
The Gitlab configuration supports an additional optional boolean key `catalog.providers.gitlab.<your-org>.restrictUsersToGroup`. Setting this to `true` will make Backstage only import users from the group defined in the `group` key, instead of all users in the organisation (self-hosted) or of the root group (SaaS). It will default to false, keeping the original implementation intact, when not explicitly set.
+1
View File
@@ -94,6 +94,7 @@ typings/
# dotenv environment variables file
.env
.env.test
.envrc
# parcel-bundler cache (https://parceljs.org/)
.cache
+14 -1
View File
@@ -196,11 +196,24 @@ which contain members will be ingested.
### Users
For self hosted, all `User` entities are ingested from the entire instance.
For self hosted, all `User` entities are ingested from the entire instance by default.
For gitlab.com `User` entities for users who have [direct or inherited membership](https://docs.gitlab.com/ee/user/project/members/index.html#membership-types)
of the top-level group for the configured group path will be ingested.
In both cases (SaaS & self hosted), you can limit the ingested users to users directly assigned to the group defined in your `app-config.yaml` by setting the configuration key `restrictUsersToGroup: true`. This is especially useful when you have a large user base that you don't want to import by default.
```yaml
catalog:
providers:
gitlab:
yourProviderId:
host: gitlab.com ## Could also be self hosted.
orgEnabled: true
group: org/teams # Required for gitlab.com when `orgEnabled: true`. Optional for self managed. Must not end with slash. Accepts only groups under the provided path (which will be stripped)
restrictUsersToGroup: true # Backstage will ingest only users directly assigned to org/teams.
```
### Limiting `User` and `Group` entity ingestion in the provider
Optionally, you can limit the entity types ingested by the provider when using
-1
View File
@@ -21,7 +21,6 @@ const backend = createBackend();
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('./authModuleGithubProvider'));
backend.add(import('@backstage/plugin-auth-backend-module-guest-provider'));
backend.add(import('@backstage/plugin-app-backend/alpha'));
backend.add(import('@backstage/plugin-catalog-backend-module-unprocessed'));
backend.add(
@@ -98,6 +98,7 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider {
export type GitlabProviderConfig = {
host: string;
group: string;
restrictUsersToGroup?: boolean;
id: string;
branch?: string;
fallbackBranch: string;
@@ -122,6 +123,7 @@ export type GitLabUser = {
avatar_url: string;
groups?: GitLabGroup[];
group_saml_identity?: GitLabGroupSamlIdentity;
is_using_seat?: boolean;
};
// @public
@@ -19,6 +19,8 @@ import {
all_groups_response,
all_projects_response,
all_saas_users_response,
all_self_hosted_group1_members,
subgroup_saas_users_response,
all_users_response,
apiBaseUrl,
apiBaseUrlSaas,
@@ -63,11 +65,21 @@ const httpHandlers = [
rest.get(`${apiBaseUrl}/groups/42`, (_, res, ctx) => {
return res(ctx.status(500), ctx.json({ error: 'Internal Server Error' }));
}),
rest.get(`${apiBaseUrl}/groups/group1/members/all`, (_req, res, ctx) => {
return res(ctx.json(all_self_hosted_group1_members));
}),
rest.get(`${apiBaseUrlSaas}/groups/group1/members/all`, (_req, res, ctx) => {
return res(ctx.json(all_saas_users_response));
}),
rest.get(
`${apiBaseUrlSaas}/groups/subgroup1/members/all`,
(_req, res, ctx) => {
return res(ctx.json(subgroup_saas_users_response)); // To-DO change
},
),
/**
* Users REST endpoint mocks
*/
@@ -186,7 +198,34 @@ const graphqlHandlers = [
.link(graphQlBaseUrl)
.query('getGroupMembers', async (req, res, ctx) => {
const { group, relations } = req.variables;
// group is actually full_path
if (group === 'group1/subgroup1' && relations.includes('DIRECT')) {
return res(
ctx.data({
group: {
groupMembers: {
nodes: [
{
user: {
id: 'gid://gitlab/User/1',
username: 'user1',
publicEmail: 'user1@example.com',
name: 'user1',
state: 'active',
webUrl: 'user1.com',
avatarUrl: 'user1',
},
},
],
pageInfo: {
endCursor: 'end',
hasNextPage: false,
},
},
},
}),
);
}
if (group === 'group1' && relations.includes('DIRECT')) {
return res(
ctx.data({
@@ -367,7 +406,31 @@ const graphqlHandlers = [
]),
);
}
if (group === 'group1') {
return res(
ctx.data({
group: {
descendantGroups: {
nodes: [
{
id: 'gid://gitlab/Group/6',
name: 'subgroup1',
description: 'description1',
fullPath: 'group1/subgroup1',
parent: {
id: '123',
},
},
],
pageInfo: {
endCursor: 'end',
hasNextPage: false,
},
},
},
}),
);
}
if (group === 'group-with-parent') {
return res(
ctx.data({
@@ -596,6 +596,127 @@ export const config_org_double_integration: MockObject = {
},
};
export const config_org_group_saas = {
integrations: {
gitlab: [
{
host: 'gitlab.com',
apiBaseUrl: 'https://gitlab.com/api/v4',
token: '1234',
},
],
},
catalog: {
providers: {
gitlab: {
'test-id': {
host: 'gitlab.com',
group: 'group1',
orgEnabled: true,
skipForkedRepos: true,
},
},
},
},
};
export const config_org_group_restrictUsers_false_saas = {
integrations: {
gitlab: [
{
host: 'gitlab.com',
apiBaseUrl: 'https://gitlab.com/api/v4',
token: '1234',
},
],
},
catalog: {
providers: {
gitlab: {
'test-id': {
host: 'gitlab.com',
group: 'group1',
orgEnabled: true,
skipForkedRepos: true,
},
},
},
},
};
export const config_org_group_restrictUsers_true_saas = {
integrations: {
gitlab: [
{
host: 'gitlab.com',
apiBaseUrl: 'https://gitlab.com/api/v4',
token: '1234',
},
],
},
catalog: {
providers: {
gitlab: {
'test-id': {
host: 'gitlab.com',
group: 'group1/subgroup1',
restrictUsersToGroup: true,
orgEnabled: true,
skipForkedRepos: true,
},
},
},
},
};
export const config_org_group_selfHosted = {
integrations: {
gitlab: [
{
host: 'example.com',
apiBaseUrl: 'https://example.com/api/v4',
token: '1234',
},
],
},
catalog: {
providers: {
gitlab: {
'test-id': {
host: 'example.com',
group: 'group1',
orgEnabled: true,
skipForkedRepos: true,
},
},
},
},
};
export const config_org_group_restrictUsers_true_selfHosted = {
integrations: {
gitlab: [
{
host: 'example.com',
apiBaseUrl: 'https://example.com/api/v4',
token: '1234',
},
],
},
catalog: {
providers: {
gitlab: {
'test-id': {
host: 'example.com',
group: 'group1',
orgEnabled: true,
skipForkedRepos: true,
restrictUsersToGroup: true,
},
},
},
},
};
/**
* GitLab API responses
*/
@@ -822,6 +943,12 @@ export const all_groups_response: GitLabGroup[] = [
description: '',
full_path: 'parent1/nonMatchingGroup',
},
{
id: 6,
name: 'subgroup1',
description: '',
full_path: 'group1/subgroup1',
},
];
export const group_with_parent: MockObject[] = [
@@ -1708,7 +1835,7 @@ export const expected_full_org_scan_entities: MockObject[] = [
name: 'JohnDoe',
},
spec: {
memberOf: ['group1'],
memberOf: ['group1', 'group1-subgroup1'],
profile: {
displayName: 'John Doe',
email: 'john.doe@company.com',
@@ -1815,6 +1942,30 @@ export const expected_full_org_scan_entities: MockObject[] = [
},
locationKey: 'GitlabOrgDiscoveryEntityProvider:test-id',
},
{
entity: {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Group',
metadata: {
annotations: {
'backstage.io/managed-by-location':
'url:https://example.com/group1/subgroup1',
'backstage.io/managed-by-origin-location':
'url:https://example.com/group1/subgroup1',
'example.com/team-path': 'group1/subgroup1',
},
name: 'group1-subgroup1',
},
spec: {
children: [],
profile: {
displayName: 'subgroup1',
},
type: 'team',
},
},
locationKey: 'GitlabOrgDiscoveryEntityProvider:test-id',
},
];
export const expected_full_org_scan_entities_saas: MockObject[] = [
@@ -1871,3 +2022,269 @@ export const expected_full_org_scan_entities_saas: MockObject[] = [
locationKey: 'GitlabOrgDiscoveryEntityProvider:test-id',
},
];
export const subgroup_saas_users_response: MockObject[] = [
{
access_level: 30,
created_at: '2023-07-17T08:58:34.984Z',
expires_at: null,
id: 12,
username: 'testuser1',
name: 'Test User 1',
state: 'active',
avatar_url: 'https://secure.gravatar.com/',
web_url: 'https://gitlab.com/testuser1',
email: 'testuser1@example.com',
group_saml_identity: {
provider: 'group_saml',
extern_uid: '51',
saml_provider_id: 1,
},
is_using_seat: true,
membership_state: 'active',
},
{
access_level: 50,
created_at: '2023-07-15T08:58:34.984Z',
expires_at: '2023-10-26',
id: 54,
username: 'group_100_bot_23dc8057bef66e05181f39be4652577c',
name: 'Token Bot',
state: 'active',
avatar_url: 'https://secure.gravatar.com/',
web_url:
'https://gitlab.com/group_100_bot_23dc8057bef66e05181f39be4652577c',
group_saml_identity: null,
is_using_seat: false,
membership_state: 'active',
},
];
export const expected_subgroup_org_scan_entities_saas: MockObject[] = [
{
entity: {
apiVersion: 'backstage.io/v1alpha1',
kind: 'User',
metadata: {
annotations: {
'backstage.io/managed-by-location':
'url:https://gitlab.com/testuser1',
'backstage.io/managed-by-origin-location':
'url:https://gitlab.com/testuser1',
'gitlab.com/user-login': 'https://gitlab.com/testuser1',
'gitlab.com/saml-external-uid': '51',
},
name: 'testuser1',
},
spec: {
memberOf: [],
profile: {
displayName: 'Test User 1',
email: 'testuser1@example.com',
picture: 'https://secure.gravatar.com/',
},
},
},
locationKey: 'GitlabOrgDiscoveryEntityProvider:test-id',
},
];
// Simulate return of all users but only with membership of the descendants of config.group
export const expected_full_members_group_org_scan_entities: MockObject[] = [
{
entity: {
apiVersion: 'backstage.io/v1alpha1',
kind: 'User',
metadata: {
annotations: {
'backstage.io/managed-by-location': 'url:https://example.com/JohnDoe',
'backstage.io/managed-by-origin-location':
'url:https://example.com/JohnDoe',
'example.com/user-login': 'https://gitlab.example/john_doe',
},
name: 'JohnDoe',
},
spec: {
memberOf: ['subgroup1'],
profile: {
displayName: 'John Doe',
email: 'john.doe@company.com',
picture: 'https://secure.gravatar.com/',
},
},
},
locationKey: 'GitlabOrgDiscoveryEntityProvider:test-id',
},
{
entity: {
apiVersion: 'backstage.io/v1alpha1',
kind: 'User',
metadata: {
annotations: {
'backstage.io/managed-by-location': 'url:https://example.com/JaneDoe',
'backstage.io/managed-by-origin-location':
'url:https://example.com/JaneDoe',
'example.com/user-login': 'https://gitlab.example/jane_doe',
},
name: 'JaneDoe',
},
spec: {
memberOf: [],
profile: {
displayName: 'Jane Doe',
email: 'jane.doe@company.com',
picture: 'https://secure.gravatar.com/',
},
},
},
locationKey: 'GitlabOrgDiscoveryEntityProvider:test-id',
},
{
entity: {
apiVersion: 'backstage.io/v1alpha1',
kind: 'User',
metadata: {
annotations: {
'backstage.io/managed-by-location':
'url:https://example.com/MarySmith',
'backstage.io/managed-by-origin-location':
'url:https://example.com/MarySmith',
'example.com/user-login': 'https://gitlab.example/mary_smith',
},
name: 'MarySmith',
},
spec: {
memberOf: [],
profile: {
displayName: 'Mary Smith',
email: 'mary.smith@company.com',
picture: 'https://secure.gravatar.com/',
},
},
},
locationKey: 'GitlabOrgDiscoveryEntityProvider:test-id',
},
{
entity: {
apiVersion: 'backstage.io/v1alpha1',
kind: 'User',
metadata: {
annotations: {
'backstage.io/managed-by-location':
'url:https://example.com/MarioMario',
'backstage.io/managed-by-origin-location':
'url:https://example.com/MarioMario',
'example.com/user-login': 'https://gitlab.example/mario_mario',
},
name: 'MarioMario',
},
spec: {
memberOf: [],
profile: {
displayName: 'Mario Mario',
email: 'mario.mario-company.com',
picture: 'https://secure.gravatar.com/',
},
},
},
locationKey: 'GitlabOrgDiscoveryEntityProvider:test-id',
},
{
entity: {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Group',
metadata: {
annotations: {
'backstage.io/managed-by-location':
'url:https://example.com/group1/subgroup1',
'backstage.io/managed-by-origin-location':
'url:https://example.com/group1/subgroup1',
'example.com/team-path': 'group1/subgroup1',
},
name: 'subgroup1',
},
spec: {
children: [],
profile: {
displayName: 'subgroup1',
},
type: 'team',
},
},
locationKey: 'GitlabOrgDiscoveryEntityProvider:test-id',
},
];
export const expected_group_members_group_org_scan_entities: MockObject[] = [
{
entity: {
apiVersion: 'backstage.io/v1alpha1',
kind: 'User',
metadata: {
annotations: {
'backstage.io/managed-by-location': 'url:https://example.com/JohnDoe',
'backstage.io/managed-by-origin-location':
'url:https://example.com/JohnDoe',
'example.com/user-login': 'https://gitlab.example/john_doe',
},
name: 'JohnDoe',
},
spec: {
memberOf: ['subgroup1'],
profile: {
displayName: 'John Doe',
email: 'john.doe@company.com',
picture: 'https://secure.gravatar.com/',
},
},
},
locationKey: 'GitlabOrgDiscoveryEntityProvider:test-id',
},
{
entity: {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Group',
metadata: {
annotations: {
'backstage.io/managed-by-location':
'url:https://example.com/group1/subgroup1',
'backstage.io/managed-by-origin-location':
'url:https://example.com/group1/subgroup1',
'example.com/team-path': 'group1/subgroup1',
},
name: 'subgroup1',
description: 'description1',
},
spec: {
children: [],
profile: {
displayName: 'subgroup1',
},
type: 'team',
},
},
locationKey: 'GitlabOrgDiscoveryEntityProvider:test-id',
},
];
export const all_self_hosted_group1_members: MockObject[] = [
{
id: 1,
username: 'JohnDoe',
name: 'John Doe',
state: 'active',
email: 'john.doe@company.com',
avatar_url: 'https://secure.gravatar.com/',
web_url: 'https://gitlab.example/john_doe',
},
// inactive
{
id: 5,
username: 'MarioMario',
name: 'Mario Mario',
state: 'inactive',
email: 'mario.mario-company.com',
avatar_url: 'https://secure.gravatar.com/',
web_url: 'https://gitlab.example/mario_mario',
},
];
@@ -114,6 +114,16 @@ export class GitLabClient {
return response;
}
async listGroupMembers(
groupPath: string,
options?: CommonListOptions,
): Promise<PagedResponse<GitLabUser>> {
return this.pagedRequest(
`/groups/${encodeURIComponent(groupPath)}/members/all`,
options,
);
}
async listUsers(
options?: UserListOptions,
): Promise<PagedResponse<GitLabUser>> {
@@ -128,13 +138,10 @@ export class GitLabClient {
groupPath: string,
options?: CommonListOptions,
): Promise<PagedResponse<GitLabUser>> {
return this.pagedRequest(
`/groups/${encodeURIComponent(groupPath)}/members/all`,
{
...options,
show_seat_info: true,
},
).then(resp => {
return this.listGroupMembers(groupPath, {
...options,
show_seat_info: true,
}).then(resp => {
resp.items = resp.items.filter(user => user.is_using_seat);
return resp;
});
@@ -60,6 +60,7 @@ export type GitLabUser = {
avatar_url: string;
groups?: GitLabGroup[];
group_saml_identity?: GitLabGroupSamlIdentity;
is_using_seat?: boolean; // Only available in responses from the group members endpoint
};
/**
@@ -149,6 +150,12 @@ export type GitlabProviderConfig = {
* Accepts only groups under the provided path (which will be stripped)
*/
group: string;
/**
* If true, the provider will only ingest users that are part of the configured group.
*/
restrictUsersToGroup?: boolean;
/**
* ???
*/
@@ -260,15 +260,16 @@ describe('GitlabOrgDiscoveryEntityProvider - refresh', () => {
const taskDef = schedule.getTasks()[0];
await (taskDef.fn as () => Promise<void>)();
const userEntities = mock.expected_full_org_scan_entities.filter(
const entities = mock.expected_full_org_scan_entities.filter(
element => element.entity.metadata.name !== 'MarioMario',
); // filter out user with non matched e-mail
expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1);
expect(userEntities).not.toHaveLength(mock.all_users_response.length);
expect(entities).not.toHaveLength(
mock.expected_full_org_scan_entities.length,
);
expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({
type: 'full',
entities: userEntities,
entities: entities,
});
});
@@ -301,6 +302,168 @@ describe('GitlabOrgDiscoveryEntityProvider - refresh', () => {
entities: mock.expected_full_org_scan_entities_saas,
});
});
// This should return all members of the SaaS Root group (group1) -> expected_full_org_scan_entities_saas
it('SaaS: should get all saas root group users when restrictUsersToGroup is not set', async () => {
const config = new ConfigReader(mock.config_org_group_saas);
const schedule = new PersistingTaskRunner();
const entityProviderConnection: EntityProviderConnection = {
applyMutation: jest.fn(),
refresh: jest.fn(),
};
const provider = GitlabOrgDiscoveryEntityProvider.fromConfig(config, {
logger,
schedule,
})[0];
expect(provider.getProviderName()).toEqual(
'GitlabOrgDiscoveryEntityProvider:test-id',
);
await provider.connect(entityProviderConnection);
const taskDef = schedule.getTasks()[0];
expect(taskDef.id).toEqual(
'GitlabOrgDiscoveryEntityProvider:test-id:refresh',
);
await (taskDef.fn as () => Promise<void>)();
expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1);
expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({
type: 'full',
entities: mock.expected_full_org_scan_entities_saas, //
});
});
// This should return all members of the SaaS Root group (group1) -> expected_full_org_scan_entities_saas
it('SaaS: should get all saas root group users when restrictUsersToGroup is false', async () => {
const config = new ConfigReader(
mock.config_org_group_restrictUsers_false_saas,
);
const schedule = new PersistingTaskRunner();
const entityProviderConnection: EntityProviderConnection = {
applyMutation: jest.fn(),
refresh: jest.fn(),
};
const provider = GitlabOrgDiscoveryEntityProvider.fromConfig(config, {
logger,
schedule,
})[0];
expect(provider.getProviderName()).toEqual(
'GitlabOrgDiscoveryEntityProvider:test-id',
);
await provider.connect(entityProviderConnection);
const taskDef = schedule.getTasks()[0];
expect(taskDef.id).toEqual(
'GitlabOrgDiscoveryEntityProvider:test-id:refresh',
);
await (taskDef.fn as () => Promise<void>)();
expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1);
expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({
type: 'full',
entities: mock.expected_full_org_scan_entities_saas, //
});
});
// This should return only members of the SaaS subgroup (group1/subgroup1) -> expected_subgroup_org_scan_entities_saas
it('SaaS: should get only subgroup users when restrictUsersToGroup is true', async () => {
const config = new ConfigReader(
mock.config_org_group_restrictUsers_true_saas,
);
const schedule = new PersistingTaskRunner();
const entityProviderConnection: EntityProviderConnection = {
applyMutation: jest.fn(),
refresh: jest.fn(),
};
const provider = GitlabOrgDiscoveryEntityProvider.fromConfig(config, {
logger,
schedule,
})[0];
expect(provider.getProviderName()).toEqual(
'GitlabOrgDiscoveryEntityProvider:test-id',
);
await provider.connect(entityProviderConnection);
const taskDef = schedule.getTasks()[0];
expect(taskDef.id).toEqual(
'GitlabOrgDiscoveryEntityProvider:test-id:refresh',
);
await (taskDef.fn as () => Promise<void>)();
expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1);
expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({
type: 'full',
entities: mock.expected_subgroup_org_scan_entities_saas,
});
});
// This should return all members of the self-hosted instance regardless of the group set -> expected_full_members_group_org_scan_entities
// All instance members, but only the group entities below the config.group
it('Self-hosted: should get all instance users when restrictUsersToGroup is not set', async () => {
const config = new ConfigReader(mock.config_org_group_selfHosted);
const schedule = new PersistingTaskRunner();
const entityProviderConnection: EntityProviderConnection = {
applyMutation: jest.fn(),
refresh: jest.fn(),
};
const provider = GitlabOrgDiscoveryEntityProvider.fromConfig(config, {
logger,
schedule,
})[0];
expect(provider.getProviderName()).toEqual(
'GitlabOrgDiscoveryEntityProvider:test-id',
);
await provider.connect(entityProviderConnection);
const taskDef = schedule.getTasks()[0];
expect(taskDef.id).toEqual(
'GitlabOrgDiscoveryEntityProvider:test-id:refresh',
);
await (taskDef.fn as () => Promise<void>)();
expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1);
expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({
type: 'full',
entities: mock.expected_full_members_group_org_scan_entities, // This should deliver all users but only their membership in subgroups of config.group
});
});
// This should return all members of the self-hosted config.group and all group entities of config.group -> expected_full_members_group_org_scan_entities
it('Self-hosted: should get only groups users when restrictUsersToGroup is set', async () => {
const config = new ConfigReader(
mock.config_org_group_restrictUsers_true_selfHosted,
);
const schedule = new PersistingTaskRunner();
const entityProviderConnection: EntityProviderConnection = {
applyMutation: jest.fn(),
refresh: jest.fn(),
};
const provider = GitlabOrgDiscoveryEntityProvider.fromConfig(config, {
logger,
schedule,
})[0];
expect(provider.getProviderName()).toEqual(
'GitlabOrgDiscoveryEntityProvider:test-id',
);
await provider.connect(entityProviderConnection);
const taskDef = schedule.getTasks()[0];
expect(taskDef.id).toEqual(
'GitlabOrgDiscoveryEntityProvider:test-id:refresh',
);
await (taskDef.fn as () => Promise<void>)();
expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1);
expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({
type: 'full',
entities: mock.expected_group_members_group_org_scan_entities, // This should deliver all users but only their membership in subgroups of config.group
});
});
});
describe('GitlabOrgDiscoveryEntityProvider with events support', () => {
@@ -356,7 +356,23 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider {
let groups;
let users;
if (this.gitLabClient.isSelfManaged()) {
// Self-hosted: Fetch the users either from the defined group (restrictUsersToGroup) or fetch all users from the GitLab instance
// SaaS: Fetch the users from the defined group (restrictUsersToGroup) or fetch all users from the root group.
if (this.gitLabClient.isSelfManaged() && this.config.restrictUsersToGroup) {
groups = (await this.gitLabClient.listDescendantGroups(this.config.group))
.items;
users = paginated<GitLabUser>(
options =>
this.gitLabClient.listGroupMembers(this.config.group, options), // calls /groups/<groupId>/members
{
page: 1,
per_page: 100,
},
);
} else if (
this.gitLabClient.isSelfManaged() &&
!this.config.restrictUsersToGroup
) {
groups = paginated<GitLabGroup>(
options => this.gitLabClient.listGroups(options),
{
@@ -365,19 +381,19 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider {
all_available: true,
},
);
users = paginated<GitLabUser>(
options => this.gitLabClient.listUsers(options),
{
page: 1,
per_page: 100,
active: true,
},
options => this.gitLabClient.listUsers(options), // calls /users?
{ page: 1, per_page: 100, active: true },
);
} else {
}
// SaaS: Fetch the users from the defined group (restrictUsersToGroup) or fetch all users from the root group.
else {
groups = (await this.gitLabClient.listDescendantGroups(this.config.group))
.items;
const rootGroup = this.config.group.split('/')[0];
const rootGroupSplit = this.config.group.split('/');
const rootGroup = this.config.restrictUsersToGroup
? rootGroupSplit[rootGroupSplit.length - 1]
: rootGroupSplit[0];
users = paginated<GitLabUser>(
options => this.gitLabClient.listSaaSUsers(rootGroup, options),
{
@@ -60,6 +60,7 @@ describe('config', () => {
allowInherited: false,
schedule: undefined,
skipForkedRepos: false,
restrictUsersToGroup: false,
}),
);
});
@@ -98,6 +99,7 @@ describe('config', () => {
allowInherited: false,
schedule: undefined,
skipForkedRepos: false,
restrictUsersToGroup: false,
}),
);
});
@@ -136,6 +138,7 @@ describe('config', () => {
orgEnabled: false,
allowInherited: false,
schedule: undefined,
restrictUsersToGroup: false,
skipForkedRepos: true,
}),
);
@@ -177,6 +180,7 @@ describe('config', () => {
orgEnabled: false,
allowInherited: false,
skipForkedRepos: false,
restrictUsersToGroup: false,
schedule: {
frequency: { minutes: 30 },
timeout: {
@@ -51,6 +51,8 @@ function readGitlabConfig(id: string, config: Config): GitlabProviderConfig {
const schedule = config.has('schedule')
? readTaskScheduleDefinitionFromConfig(config.getConfig('schedule'))
: undefined;
const restrictUsersToGroup =
config.getOptionalBoolean('restrictUsersToGroup') ?? false;
return {
id,
@@ -66,6 +68,7 @@ function readGitlabConfig(id: string, config: Config): GitlabProviderConfig {
orgEnabled,
allowInherited,
skipForkedRepos,
restrictUsersToGroup,
};
}