From 9d6cb6e044ac0178a53ed04010f990d8fccb6464 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Thu, 3 May 2018 11:09:28 -0400 Subject: [PATCH] centralize custom filters into base directory service --- src/services/azure-directory.service.ts | 65 ++----------------- src/services/baseDirectory.service.ts | 82 ++++++++++++++++++++++++ src/services/gsuite-directory.service.ts | 73 ++------------------- src/services/okta-directory.service.ts | 33 ++++++---- 4 files changed, 117 insertions(+), 136 deletions(-) create mode 100644 src/services/baseDirectory.service.ts diff --git a/src/services/azure-directory.service.ts b/src/services/azure-directory.service.ts index aacb784f..e38d2302 100644 --- a/src/services/azure-directory.service.ts +++ b/src/services/azure-directory.service.ts @@ -10,6 +10,7 @@ import { GroupEntry } from '../models/groupEntry'; import { SyncConfiguration } from '../models/syncConfiguration'; import { UserEntry } from '../models/userEntry'; +import { BaseDirectoryService } from './baseDirectory.service'; import { ConfigurationService } from './configuration.service'; import { DirectoryService } from './directory.service'; @@ -17,7 +18,7 @@ const NextLink = '@odata.nextLink'; const DeltaLink = '@odata.deltaLink'; const ObjectType = '@odata.type'; -export class AzureDirectoryService implements DirectoryService { +export class AzureDirectoryService extends BaseDirectoryService implements DirectoryService { private client: graph.Client; private dirConfig: AzureConfiguration; private syncConfig: SyncConfiguration; @@ -25,6 +26,7 @@ export class AzureDirectoryService implements DirectoryService { private accessTokenExpiration: Date; constructor(private configurationService: ConfigurationService) { + super(); this.init(); } @@ -52,21 +54,13 @@ export class AzureDirectoryService implements DirectoryService { let groups: GroupEntry[]; if (this.syncConfig.groups) { - const setFilter = this.createSet(this.syncConfig.groupFilter); + const setFilter = this.createCustomSet(this.syncConfig.groupFilter); const groupForce = force || (users != null && users.filter((u) => !u.deleted && !u.disabled).length > 0); groups = await this.getGroups(groupForce, !test, setFilter); - if (setFilter != null && users != null) { - users = users.filter((u) => { - if (u.disabled || u.deleted) { - return true; - } - - return groups.filter((g) => g.userMemberExternalIds.has(u.externalId)).length > 0; - }); - } + users = this.filterUsersFromGroupsSet(users, groups, setFilter); } return [groups, users]; @@ -91,13 +85,13 @@ export class AzureDirectoryService implements DirectoryService { res = await userReq.get(); } - const filter = this.createSet(this.syncConfig.userFilter); + const setFilter = this.createCustomSet(this.syncConfig.userFilter); while (true) { const users: graphType.User[] = res.value; if (users != null) { for (const user of users) { const entry = this.buildUser(user); - if (this.filterOutResult(filter, entry.email)) { + if (this.filterOutResult(setFilter, entry.email)) { return; } @@ -245,51 +239,6 @@ export class AzureDirectoryService implements DirectoryService { return entry; } - private createSet(filter: string): [boolean, Set] { - if (filter == null || filter === '') { - return null; - } - - const parts = filter.split(':'); - if (parts.length !== 2) { - return null; - } - - const keyword = parts[0].trim().toLowerCase(); - let exclude = true; - if (keyword === 'include') { - exclude = false; - } else if (keyword === 'exclude') { - exclude = true; - } else { - return null; - } - - const set = new Set(); - const pieces = parts[1].split(','); - for (const p of pieces) { - set.add(p.trim().toLowerCase()); - } - - return [exclude, set]; - } - - private filterOutResult(filter: [boolean, Set], result: string) { - if (filter != null) { - result = result.trim().toLowerCase(); - const excluded = filter[0]; - const set = filter[1]; - - if (excluded && set.has(result)) { - return true; - } else if (!excluded && !set.has(result)) { - return true; - } - } - - return false; - } - private init() { this.client = graph.Client.init({ authProvider: (done) => { diff --git a/src/services/baseDirectory.service.ts b/src/services/baseDirectory.service.ts new file mode 100644 index 00000000..9e9f64e1 --- /dev/null +++ b/src/services/baseDirectory.service.ts @@ -0,0 +1,82 @@ +import { GroupEntry } from '../models/groupEntry'; +import { UserEntry } from '../models/userEntry'; + +export abstract class BaseDirectoryService { + protected createDirectoryQuery(filter: string) { + if (filter == null || filter === '') { + return null; + } + + const mainParts = filter.split('|'); + if (mainParts.length < 2 || mainParts[1] == null || mainParts[1].trim() === '') { + return null; + } + + return mainParts[1].trim(); + } + + protected createCustomSet(filter: string): [boolean, Set] { + if (filter == null || filter === '') { + return null; + } + + const mainParts = filter.split('|'); + if (mainParts.length < 1 || mainParts[0] == null || mainParts[0].trim() === '') { + return null; + } + + const parts = mainParts[0].split(':'); + if (parts.length !== 2) { + return null; + } + + const keyword = parts[0].trim().toLowerCase(); + let exclude = true; + if (keyword === 'include') { + exclude = false; + } else if (keyword === 'exclude') { + exclude = true; + } else { + return null; + } + + const set = new Set(); + const pieces = parts[1].split(','); + for (const p of pieces) { + set.add(p.trim().toLowerCase()); + } + + return [exclude, set]; + } + + protected filterOutResult(setFilter: [boolean, Set], result: string) { + if (setFilter != null) { + result = result.trim().toLowerCase(); + const excluded = setFilter[0]; + const set = setFilter[1]; + + if (excluded && set.has(result)) { + return true; + } else if (!excluded && !set.has(result)) { + return true; + } + } + + return false; + } + + protected filterUsersFromGroupsSet(users: UserEntry[], groups: GroupEntry[], + setFilter: [boolean, Set]): UserEntry[] { + if (setFilter == null || users == null) { + return users; + } + + return users.filter((u) => { + if (u.disabled || u.deleted) { + return true; + } + + return groups.filter((g) => g.userMemberExternalIds.has(u.externalId)).length > 0; + }); + } +} diff --git a/src/services/gsuite-directory.service.ts b/src/services/gsuite-directory.service.ts index 97ac08a7..193f89bd 100644 --- a/src/services/gsuite-directory.service.ts +++ b/src/services/gsuite-directory.service.ts @@ -16,12 +16,13 @@ import { GSuiteConfiguration } from '../models/gsuiteConfiguration'; import { SyncConfiguration } from '../models/syncConfiguration'; import { UserEntry } from '../models/userEntry'; +import { BaseDirectoryService } from './baseDirectory.service'; import { ConfigurationService } from './configuration.service'; import { DirectoryService } from './directory.service'; import { LogService } from 'jslib/abstractions/log.service'; -export class GSuiteDirectoryService implements DirectoryService { +export class GSuiteDirectoryService extends BaseDirectoryService implements DirectoryService { private client: JWT; private service: Admin; private authParams: any; @@ -29,6 +30,7 @@ export class GSuiteDirectoryService implements DirectoryService { private syncConfig: SyncConfiguration; constructor(private configurationService: ConfigurationService, private logService: LogService) { + super(); this.service = google.admin('directory_v1'); } @@ -65,7 +67,7 @@ export class GSuiteDirectoryService implements DirectoryService { private async getUsers(): Promise { const entries: UserEntry[] = []; - const query = this.createQuery(this.syncConfig.userFilter); + const query = this.createDirectoryQuery(this.syncConfig.userFilter); this.logService.info('Querying users.'); let p = Object.assign({ query: query }, this.authParams); @@ -74,7 +76,7 @@ export class GSuiteDirectoryService implements DirectoryService { throw new Error('User list API failed: ' + res.statusText); } - const filter = this.createSet(this.syncConfig.userFilter); + const filter = this.createCustomSet(this.syncConfig.userFilter); if (res.data.users != null) { for (const user of res.data.users) { if (this.filterOutResult(filter, user.primaryEmail)) { @@ -134,7 +136,7 @@ export class GSuiteDirectoryService implements DirectoryService { throw new Error('Group list API failed: ' + res.statusText); } - const filter = this.createSet(this.syncConfig.groupFilter); + const filter = this.createCustomSet(this.syncConfig.groupFilter); if (res.data.groups != null) { for (const group of res.data.groups) { if (this.filterOutResult(filter, group.name)) { @@ -182,69 +184,6 @@ export class GSuiteDirectoryService implements DirectoryService { return entry; } - private createQuery(filter: string) { - if (filter == null || filter === '') { - return null; - } - - const mainParts = filter.split('|'); - if (mainParts.length < 2 || mainParts[1] == null || mainParts[1].trim() === '') { - return null; - } - - return mainParts[1].trim(); - } - - private createSet(filter: string): [boolean, Set] { - if (filter == null || filter === '') { - return null; - } - - const mainParts = filter.split('|'); - if (mainParts.length < 1 || mainParts[0] == null || mainParts[0].trim() === '') { - return null; - } - - const parts = mainParts[0].split(':'); - if (parts.length !== 2) { - return null; - } - - const keyword = parts[0].trim().toLowerCase(); - let exclude = true; - if (keyword === 'include') { - exclude = false; - } else if (keyword === 'exclude') { - exclude = true; - } else { - return null; - } - - const set = new Set(); - const pieces = parts[1].split(','); - for (const p of pieces) { - set.add(p.trim().toLowerCase()); - } - - return [exclude, set]; - } - - private filterOutResult(filter: [boolean, Set], result: string) { - if (filter != null) { - result = result.trim().toLowerCase(); - const excluded = filter[0]; - const set = filter[1]; - - if (excluded && set.has(result)) { - return true; - } else if (!excluded && !set.has(result)) { - return true; - } - } - - return false; - } - private async auth() { this.client = new google.auth.JWT({ email: this.dirConfig.clientEmail, diff --git a/src/services/okta-directory.service.ts b/src/services/okta-directory.service.ts index db9e2f4f..4623bd96 100644 --- a/src/services/okta-directory.service.ts +++ b/src/services/okta-directory.service.ts @@ -5,6 +5,7 @@ import { OktaConfiguration } from '../models/oktaConfiguration'; import { SyncConfiguration } from '../models/syncConfiguration'; import { UserEntry } from '../models/userEntry'; +import { BaseDirectoryService } from './baseDirectory.service'; import { ConfigurationService } from './configuration.service'; import { DirectoryService } from './directory.service'; @@ -13,12 +14,14 @@ import { LogService } from 'jslib/abstractions/log.service'; // tslint:disable-next-line const okta = require('@okta/okta-sdk-nodejs'); -export class OktaDirectoryService implements DirectoryService { +export class OktaDirectoryService extends BaseDirectoryService implements DirectoryService { private dirConfig: OktaConfiguration; private syncConfig: SyncConfiguration; private client: any; - constructor(private configurationService: ConfigurationService, private logService: LogService) { } + constructor(private configurationService: ConfigurationService, private logService: LogService) { + super(); + } async getEntries(force: boolean, test: boolean): Promise<[GroupEntry[], UserEntry[]]> { const type = await this.configurationService.getDirectoryType(); @@ -48,7 +51,13 @@ export class OktaDirectoryService implements DirectoryService { let groups: GroupEntry[]; if (this.syncConfig.groups) { - groups = await this.getGroups(force); + const setFilter = this.createCustomSet(this.syncConfig.groupFilter); + + const groupForce = force || + (users != null && users.filter((u) => !u.deleted && !u.disabled).length > 0); + + groups = await this.getGroups(groupForce, setFilter); + users = this.filterUsersFromGroupsSet(users, groups, setFilter); } return [groups, users]; @@ -57,12 +66,13 @@ export class OktaDirectoryService implements DirectoryService { private async getUsers(force: boolean): Promise { const entries: UserEntry[] = []; const lastSync = await this.configurationService.getLastUserSyncDate(); - const filter = this.buildFilter(this.syncConfig.userFilter, force, lastSync); + const oktaFilter = this.buildOktaFilter(this.syncConfig.userFilter, force, lastSync); + const setFilter = this.createCustomSet(this.syncConfig.userFilter); this.logService.info('Querying users.'); - await this.client.listUsers({ filter: filter }).each((user: any) => { + await this.client.listUsers({ filter: oktaFilter }).each((user: any) => { const entry = this.buildUser(user); - if (entry != null) { + if (entry != null && !this.filterOutResult(setFilter, entry.email)) { entries.push(entry); } }); @@ -80,15 +90,15 @@ export class OktaDirectoryService implements DirectoryService { return entry; } - private async getGroups(force: boolean): Promise { + private async getGroups(force: boolean, setFilter: [boolean, Set]): Promise { const entries: GroupEntry[] = []; const lastSync = await this.configurationService.getLastGroupSyncDate(); - const filter = this.buildFilter(this.syncConfig.groupFilter, force, lastSync); + const oktaFilter = this.buildOktaFilter(this.syncConfig.groupFilter, force, lastSync); this.logService.info('Querying groups.'); - await this.client.listGroups({ filter: filter }).each((group: any) => { + await this.client.listGroups({ filter: oktaFilter }).each((group: any) => { const entry = this.buildGroup(group); - if (entry != null) { + if (entry != null && !this.filterOutResult(setFilter, entry.name)) { entries.push(entry); } }); @@ -103,7 +113,8 @@ export class OktaDirectoryService implements DirectoryService { return entry; } - private buildFilter(baseFilter: string, force: boolean, lastSync: Date) { + private buildOktaFilter(baseFilter: string, force: boolean, lastSync: Date) { + baseFilter = this.createDirectoryQuery(baseFilter); baseFilter = baseFilter == null || baseFilter.trim() === '' ? null : baseFilter; if (force || lastSync == null) { return baseFilter;