1
0
mirror of https://github.com/bitwarden/directory-connector synced 2025-12-05 23:53:21 +00:00

centralize custom filters into base directory service

This commit is contained in:
Kyle Spearrin
2018-05-03 11:09:28 -04:00
parent 27c5909d7f
commit 9d6cb6e044
4 changed files with 117 additions and 136 deletions

View File

@@ -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<string>] {
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<string>();
const pieces = parts[1].split(',');
for (const p of pieces) {
set.add(p.trim().toLowerCase());
}
return [exclude, set];
}
private filterOutResult(filter: [boolean, Set<string>], 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) => {

View File

@@ -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<string>] {
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<string>();
const pieces = parts[1].split(',');
for (const p of pieces) {
set.add(p.trim().toLowerCase());
}
return [exclude, set];
}
protected filterOutResult(setFilter: [boolean, Set<string>], 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<string>]): 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;
});
}
}

View File

@@ -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<Admin>('directory_v1');
}
@@ -65,7 +67,7 @@ export class GSuiteDirectoryService implements DirectoryService {
private async getUsers(): Promise<UserEntry[]> {
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<string>] {
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<string>();
const pieces = parts[1].split(',');
for (const p of pieces) {
set.add(p.trim().toLowerCase());
}
return [exclude, set];
}
private filterOutResult(filter: [boolean, Set<string>], 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,

View File

@@ -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<UserEntry[]> {
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<GroupEntry[]> {
private async getGroups(force: boolean, setFilter: [boolean, Set<string>]): Promise<GroupEntry[]> {
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;