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:
@@ -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) => {
|
||||
|
||||
82
src/services/baseDirectory.service.ts
Normal file
82
src/services/baseDirectory.service.ts
Normal 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user