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

gsuite directory query logic to make entries result

This commit is contained in:
Kyle Spearrin
2018-04-28 00:13:04 -04:00
parent 39f760e135
commit 5556a57685
6 changed files with 226 additions and 31 deletions

View File

@@ -71,7 +71,7 @@ const containerService = new ContainerService(cryptoService, platformUtilsServic
const authService = new AuthService(cryptoService, apiService, userService, tokenService, appIdService,
i18nService, platformUtilsService, messagingService, false);
const configurationService = new ConfigurationService(storageService, secureStorageService);
const syncSevrice = new SyncService(configurationService);
const syncSevrice = new SyncService(configurationService, logService);
const analytics = new Analytics(window, () => true, platformUtilsService, storageService, appIdService);
containerService.attachToWindow(window);

View File

@@ -5,7 +5,9 @@ import * as querystring from 'querystring';
import { DirectoryType } from '../enums/directoryType';
import { AzureConfiguration } from '../models/azureConfiguration';
import { GroupEntry } from '../models/groupEntry';
import { SyncConfiguration } from '../models/syncConfiguration';
import { UserEntry } from '../models/userEntry';
import { ConfigurationService } from './configuration.service';
import { DirectoryService } from './directory.service';
@@ -55,7 +57,7 @@ export class AzureDirectoryService implements DirectoryService {
});
}
async getEntries(force = false) {
async getEntries(force = false): Promise<[GroupEntry[], UserEntry[]]> {
const type = await this.configurationService.getDirectoryType();
if (type !== DirectoryType.AzureActiveDirectory) {
return;
@@ -74,6 +76,8 @@ export class AzureDirectoryService implements DirectoryService {
await this.getUsers();
await this.getGroups();
return null;
}
private async getUsers() {

View File

@@ -1,3 +1,6 @@
import { GroupEntry } from '../models/groupEntry';
import { UserEntry } from '../models/userEntry';
export interface DirectoryService {
getEntries(force?: boolean): any;
getEntries(force?: boolean): Promise<[GroupEntry[], UserEntry[]]>;
}

View File

@@ -1,15 +1,26 @@
import { JWT } from 'google-auth-library';
import { google, GoogleApis } from 'googleapis';
import { Admin } from 'googleapis/build/src/apis/admin/directory_v1';
import {
google,
GoogleApis,
} from 'googleapis';
import {
Admin,
Schema$Group,
Schema$User,
} from 'googleapis/build/src/apis/admin/directory_v1';
import { DirectoryType } from '../enums/directoryType';
import { GroupEntry } from '../models/groupEntry';
import { GSuiteConfiguration } from '../models/gsuiteConfiguration';
import { SyncConfiguration } from '../models/syncConfiguration';
import { UserEntry } from '../models/userEntry';
import { ConfigurationService } from './configuration.service';
import { DirectoryService } from './directory.service';
import { LogService } from 'jslib/abstractions/log.service';
export class GSuiteDirectoryService implements DirectoryService {
private client: JWT;
private service: Admin;
@@ -17,11 +28,11 @@ export class GSuiteDirectoryService implements DirectoryService {
private dirConfig: GSuiteConfiguration;
private syncConfig: SyncConfiguration;
constructor(private configurationService: ConfigurationService) {
constructor(private configurationService: ConfigurationService, private logService: LogService) {
this.service = google.admin<Admin>('directory_v1');
}
async getEntries(force = false) {
async getEntries(force = false): Promise<[GroupEntry[], UserEntry[]]> {
const type = await this.configurationService.getDirectoryType();
if (type !== DirectoryType.GSuite) {
return;
@@ -38,30 +49,201 @@ export class GSuiteDirectoryService implements DirectoryService {
}
await this.auth();
await this.getUsers();
await this.getGroups();
}
private async getUsers() {
const response = await this.service.users.list(this.authParams);
console.log(response);
}
private async getGroups() {
const response = await this.service.groups.list(this.authParams);
console.log(response);
if (response.data.groups.length === 0) {
return;
let users: UserEntry[];
if (this.syncConfig.users) {
users = await this.getUsers();
}
response.data.groups.forEach(async (g) => {
const params: any = Object.assign({
groupKey: g.id,
}, this.authParams);
const members = await this.service.members.list(params);
console.log(members);
let groups: GroupEntry[];
if (this.syncConfig.groups) {
groups = await this.getGroups();
}
return [groups, users];
}
private async getUsers(): Promise<UserEntry[]> {
const entries: UserEntry[] = [];
const query = this.createQuery(this.syncConfig.userFilter);
this.logService.info('Querying users.');
let p = Object.assign({ query: query }, this.authParams);
const res = await this.service.users.list(p);
if (res.status !== 200) {
throw new Error('User list API failed: ' + res.statusText);
}
const filter = this.createSet(this.syncConfig.userFilter);
if (res.data.users != null) {
res.data.users.forEach((user) => {
if (this.filterOutResult(filter, user.primaryEmail)) {
return;
}
const entry = this.buildUser(user, false);
if (entry != null) {
entries.push(entry);
}
});
}
this.logService.info('Querying deleted users.');
p = Object.assign({ showDeleted: true, query: query }, this.authParams);
const delRes = await this.service.users.list(p);
if (delRes.status !== 200) {
throw new Error('Deleted user list API failed: ' + delRes.statusText);
}
if (delRes.data.users != null) {
delRes.data.users.forEach((user) => {
if (this.filterOutResult(filter, user.primaryEmail)) {
return;
}
const entry = this.buildUser(user, true);
if (entry != null) {
entries.push(entry);
}
});
}
return entries;
}
private buildUser(user: Schema$User, deleted: boolean) {
if ((user.emails == null || user.emails === '') && !deleted) {
return null;
}
const entry = new UserEntry();
entry.referenceId = user.id;
entry.externalId = user.id;
entry.email = user.primaryEmail;
entry.disabled = user.suspended || false;
entry.deleted = deleted;
// entry.creationDate = user.creationTime; // TODO: string to date conversion
return entry;
}
private async getGroups(): Promise<GroupEntry[]> {
const entries: GroupEntry[] = [];
this.logService.info('Querying groups.');
const res = await this.service.groups.list(this.authParams);
if (res.status !== 200) {
throw new Error('Group list API failed: ' + res.statusText);
}
const filter = this.createSet(this.syncConfig.groupFilter);
if (res.data.groups != null) {
res.data.groups.forEach(async (group) => {
if (this.filterOutResult(filter, group.name)) {
return;
}
const entry = await this.buildGroup(group);
entries.push(entry);
});
}
return entries;
}
private async buildGroup(group: Schema$Group) {
const entry = new GroupEntry();
entry.referenceId = group.id;
entry.externalId = group.id;
entry.name = group.name;
const p = Object.assign({ groupKey: group.id }, this.authParams);
const memRes = await this.service.members.list(p);
if (memRes.status !== 200) {
this.logService.warning('Group member list API failed: ' + memRes.statusText);
return entry;
}
if (memRes.data.members != null) {
memRes.data.members.forEach((member) => {
if (member.role.toLowerCase() !== 'member') {
return;
}
if (member.status.toLowerCase() !== 'active') {
return;
}
if (member.type.toLowerCase() === 'user') {
entry.userMemberExternalIds.add(member.id);
} else if (member.type.toLowerCase() === 'group') {
entry.groupMemberReferenceIds.add(member.id);
}
});
}
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(',');
pieces.forEach((p) => {
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() {

View File

@@ -2,8 +2,10 @@ import * as ldap from 'ldapjs';
import { DirectoryType } from '../enums/directoryType';
import { GroupEntry } from '../models/groupEntry';
import { LdapConfiguration } from '../models/ldapConfiguration';
import { SyncConfiguration } from '../models/syncConfiguration';
import { UserEntry } from '../models/userEntry';
import { ConfigurationService } from './configuration.service';
import { DirectoryService } from './directory.service';
@@ -15,7 +17,7 @@ export class LdapDirectoryService implements DirectoryService {
constructor(private configurationService: ConfigurationService) { }
async getEntries(force = false) {
async getEntries(force = false): Promise<[GroupEntry[], UserEntry[]]> {
const type = await this.configurationService.getDirectoryType();
if (type !== DirectoryType.Ldap) {
return;
@@ -33,6 +35,8 @@ export class LdapDirectoryService implements DirectoryService {
await this.auth();
await this.getUsers();
return null;
}
private getUsers() {

View File

@@ -1,5 +1,6 @@
import { DirectoryType } from '../enums/directoryType';
import { LogService } from 'jslib/abstractions/log.service';
import { StorageService } from 'jslib/abstractions/storage.service';
import { AzureDirectoryService } from './azure-directory.service';
@@ -14,7 +15,7 @@ const Keys = {
export class SyncService {
private dirType: DirectoryType;
constructor(private configurationService: ConfigurationService) { }
constructor(private configurationService: ConfigurationService, private logService: LogService) { }
async sync(force = true, sendToServer = true): Promise<any> {
this.dirType = await this.configurationService.getDirectoryType();
@@ -27,13 +28,14 @@ export class SyncService {
return;
}
directoryService.getEntries(force);
const entries = await directoryService.getEntries(force);
console.log(entries);
}
private getDirectoryService(): DirectoryService {
switch (this.dirType) {
case DirectoryType.GSuite:
return new GSuiteDirectoryService(this.configurationService);
return new GSuiteDirectoryService(this.configurationService, this.logService);
case DirectoryType.AzureActiveDirectory:
return new AzureDirectoryService(this.configurationService);
case DirectoryType.Ldap: