Extend tests for subgroups

Signed-off-by: Hghtwr <johannes.sonner@outlook.com>
This commit is contained in:
Hghtwr
2024-06-20 16:32:26 +02:00
parent 08b0740812
commit 2537c3f345
5 changed files with 518 additions and 16 deletions
+6 -2
View File
@@ -1,13 +1,17 @@
enableGlobalCache: true
httpProxy: "http://sia-lb.telekom.de:8080"
httpTimeout: 300000
httpsProxy: "http://sia-lb.telekom.de:8080"
nodeLinker: node-modules
npmRegistryServer: 'https://registry.npmjs.org/'
npmRegistryServer: "https://registry.npmjs.org/"
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: '@yarnpkg/plugin-workspace-tools'
spec: "@yarnpkg/plugin-workspace-tools"
yarnPath: .yarn/releases/yarn-3.8.1.cjs
@@ -19,6 +19,7 @@ import {
all_groups_response,
all_projects_response,
all_saas_users_response,
subgroup_saas_users_response,
all_users_response,
apiBaseUrl,
apiBaseUrlSaas,
@@ -68,6 +69,13 @@ const httpHandlers = [
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 +194,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 +402,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({
@@ -822,6 +822,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 +1714,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 +1821,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 +1901,195 @@ 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_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',
},
];
@@ -175,6 +175,220 @@ describe('GitlabOrgDiscoveryEntityProvider - configuration', () => {
});
});
// 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({
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,
},
},
},
},
});
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({
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,
restrictUsersToGroup: false,
},
},
},
},
});
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({
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,
},
},
},
},
});
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_org_scan_entities
it('Self-hosted: should get all instance users when restrictUsersToGroup is not set', async () => {
const config = new ConfigReader({
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,
},
},
},
},
});
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_org_scan_entities, // This should deliver all users but only their membership in subgroups of config.group
});
});
describe('GitlabOrgDiscoveryEntityProvider - refresh', () => {
it('should apply full update on scheduled execution', async () => {
const config = new ConfigReader(mock.config_org_integration);
@@ -260,15 +474,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,
});
});
@@ -107,7 +107,7 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider {
private userTransformer: UserTransformer;
private groupEntitiesTransformer: GroupEntitiesTransformer;
private groupNameTransformer: GroupNameTransformer;
private readonly gitLabClient: GitLabClient;
public readonly gitLabClient: GitLabClient;
static fromConfig(
config: Config,
@@ -356,13 +356,14 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider {
let groups;
let users;
// Doesn't matter if we are on SaaS or not, we can either get the users from the defined group or from the root group.
// 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),
this.gitLabClient.listGroupMembers(this.config.group, options), // calls /groups/<groupId>/members
{
page: 1,
per_page: 100,
@@ -381,17 +382,18 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider {
},
);
users = paginated<GitLabUser>(
options => this.gitLabClient.listUsers(options),
options => this.gitLabClient.listUsers(options), // calls /users?
{ page: 1, per_page: 100, active: true },
);
}
// For SaaS, the only difference is the root group
// 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 rootGroupSplit = this.config.group.split('/');
const rootGroup = this.config.restrictUsersToGroup
? this.config.group
: this.config.group.split('/')[0];
? rootGroupSplit[rootGroupSplit.length - 1]
: rootGroupSplit[0];
users = paginated<GitLabUser>(
options => this.gitLabClient.listSaaSUsers(rootGroup, options),
{