Make userFilter and userGroupMemberFilter mutually exclusive

Signed-off-by: Mustansar Anwar ul Samad <mustansar.samad@gmail.com>
This commit is contained in:
Mustansar Anwar ul Samad
2021-10-04 17:08:44 +13:00
committed by blam
parent ff7c6cec1a
commit 76599b5429
4 changed files with 161 additions and 24 deletions
@@ -55,15 +55,20 @@ catalog:
# Optional filter for user, see Microsoft Graph API for the syntax
# See https://docs.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0#properties
# and for the syntax https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter
# This and userGroupMemberFilter are mutually exclusive, only one can be specified
userFilter: accountEnabled eq true and userType eq 'member'
# Optional filter for users, use group membership to get users
# This appends the users to the list of users retrieved via userFilter
# Optional filter for users, use group membership to get users.
# This and userFilter are mutually exclusive, only one can be specified
userGroupMemberFilter: "displayName eq 'Backstage Users'"
# Optional filter for group, see Microsoft Graph API for the syntax
# See https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0#properties
groupFilter: securityEnabled eq false and mailEnabled eq true and groupTypes/any(c:c+eq+'Unified')
```
`userFilter` and `userGroupMemberFilter` are mutually exclusive, only one can be provided. If both are provided, an error will be thrown.
By default, all users are loaded. If you want to filter users based on their attributes, use `userFilter`. `userGroupMemberFilter` can be used if you want to load users based on their group membership.
5. Add a location that ingests from Microsoft Graph:
```yaml
@@ -19,6 +19,7 @@ import merge from 'lodash/merge';
import { GroupMember, MicrosoftGraphClient } from './client';
import {
readMicrosoftGraphGroups,
readMicrosoftGraphOrg,
readMicrosoftGraphOrganization,
readMicrosoftGraphUsers,
readMicrosoftGraphUsersInGroups,
@@ -537,4 +538,132 @@ describe('read microsoft graph', () => {
);
});
});
describe('readMicrosoftGraphOrg', () => {
async function* getExampleUsers() {
yield {
id: 'userid',
displayName: 'User Name',
mail: 'user.name@example.com',
};
}
async function* getExampleGroups() {
yield {
id: 'groupid',
displayName: 'Group Name',
description: 'Group Description',
mail: 'group@example.com',
};
}
async function* getExampleGroupMembers(): AsyncIterable<GroupMember> {
yield {
'@odata.type': '#microsoft.graph.group',
id: 'childgroupid',
};
yield {
'@odata.type': '#microsoft.graph.user',
id: 'userid',
};
}
it('should read all users if no filter provided', async () => {
client.getOrganization.mockResolvedValue({
id: 'tenantid',
displayName: 'Organization Name',
});
client.getUsers.mockImplementation(getExampleUsers);
client.getUserPhotoWithSizeLimit.mockResolvedValue(
'data:image/jpeg;base64,...',
);
client.getGroups.mockImplementation(getExampleGroups);
client.getGroupMembers.mockImplementation(getExampleGroupMembers);
client.getGroupPhotoWithSizeLimit.mockResolvedValue(
'data:image/jpeg;base64,...',
);
await readMicrosoftGraphOrg(client, 'tenantid', {
logger: getVoidLogger(),
groupFilter: 'securityEnabled eq false',
});
expect(client.getUsers).toBeCalledTimes(1);
expect(client.getUsers).toBeCalledWith({
filter: undefined,
});
expect(client.getGroups).toBeCalledTimes(1);
expect(client.getGroups).toBeCalledWith({
filter: 'securityEnabled eq false',
});
});
it('should read users using userFilter', async () => {
client.getOrganization.mockResolvedValue({
id: 'tenantid',
displayName: 'Organization Name',
});
client.getUsers.mockImplementation(getExampleUsers);
client.getUserPhotoWithSizeLimit.mockResolvedValue(
'data:image/jpeg;base64,...',
);
client.getGroups.mockImplementation(getExampleGroups);
client.getGroupMembers.mockImplementation(getExampleGroupMembers);
client.getGroupPhotoWithSizeLimit.mockResolvedValue(
'data:image/jpeg;base64,...',
);
await readMicrosoftGraphOrg(client, 'tenantid', {
logger: getVoidLogger(),
userFilter: 'accountEnabled eq true',
groupFilter: 'securityEnabled eq false',
});
expect(client.getUsers).toBeCalledTimes(1);
expect(client.getUsers).toBeCalledWith({
filter: 'accountEnabled eq true',
});
expect(client.getGroups).toBeCalledTimes(1);
expect(client.getGroups).toBeCalledWith({
filter: 'securityEnabled eq false',
});
});
it('should read users using userGroupMemberFilter', async () => {
client.getOrganization.mockResolvedValue({
id: 'tenantid',
displayName: 'Organization Name',
});
client.getUsers.mockImplementation(getExampleUsers);
client.getUserPhotoWithSizeLimit.mockResolvedValue(
'data:image/jpeg;base64,...',
);
client.getGroups.mockImplementation(getExampleGroups);
client.getGroupMembers.mockImplementation(getExampleGroupMembers);
client.getGroupPhotoWithSizeLimit.mockResolvedValue(
'data:image/jpeg;base64,...',
);
await readMicrosoftGraphOrg(client, 'tenantid', {
logger: getVoidLogger(),
userGroupMemberFilter: 'name eq backstage-group',
groupFilter: 'securityEnabled eq false',
});
expect(client.getUsers).toBeCalledTimes(0);
expect(client.getGroups).toBeCalledTimes(2);
expect(client.getGroups).toBeCalledWith({
filter: 'name eq backstage-group',
});
expect(client.getGroups).toBeCalledWith({
filter: 'securityEnabled eq false',
});
});
});
});
@@ -137,10 +137,6 @@ export async function readMicrosoftGraphUsersInGroups(
}> {
const users: UserEntity[] = [];
if (!options.userGroupMemberFilter) {
return { users };
}
const limiter = limiterFactory(10);
const transformer = options?.transformer ?? defaultUserTransformer;
@@ -161,9 +157,7 @@ export async function readMicrosoftGraphUsersInGroups(
}
if (member['@odata.type'] === '#microsoft.graph.user') {
if (!groupMemberUsers.has(member.id)) {
groupMemberUsers.add(member.id);
}
groupMemberUsers.add(member.id);
}
}
}),
@@ -484,23 +478,26 @@ export async function readMicrosoftGraphOrg(
logger: Logger;
},
): Promise<{ users: UserEntity[]; groups: GroupEntity[] }> {
const { users: usersInGroups } = await readMicrosoftGraphUsersInGroups(
client,
{
userGroupMemberFilter: options.userGroupMemberFilter,
const users: UserEntity[] = [];
if (options.userGroupMemberFilter) {
const { users: usersInGroups } = await readMicrosoftGraphUsersInGroups(
client,
{
userGroupMemberFilter: options.userGroupMemberFilter,
transformer: options.userTransformer,
logger: options.logger,
},
);
users.push(...usersInGroups);
} else {
const { users: usersWithFilter } = await readMicrosoftGraphUsers(client, {
userFilter: options.userFilter,
transformer: options.userTransformer,
logger: options.logger,
},
);
const { users: usersWithFilter } = await readMicrosoftGraphUsers(client, {
userFilter: options.userFilter,
transformer: options.userTransformer,
logger: options.logger,
});
const users: UserEntity[] = usersWithFilter.concat(usersInGroups);
});
users.push(...usersWithFilter);
}
const { groups, rootGroup, groupMember, groupMemberOf } =
await readMicrosoftGraphGroups(client, tenantId, {
groupFilter: options?.groupFilter,
@@ -90,6 +90,12 @@ export class MicrosoftGraphOrgReaderProcessor implements CatalogProcessor {
);
}
if (provider.userFilter && provider.userGroupMemberFilter) {
throw new Error(
`userFilter and userGroupMemberFilter are mutually exclusive, only one can be specified.`,
);
}
// Read out all of the raw data
const startTimestamp = Date.now();
this.logger.info('Reading Microsoft Graph users and groups');