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:
@@ -71,7 +71,7 @@ const containerService = new ContainerService(cryptoService, platformUtilsServic
|
|||||||
const authService = new AuthService(cryptoService, apiService, userService, tokenService, appIdService,
|
const authService = new AuthService(cryptoService, apiService, userService, tokenService, appIdService,
|
||||||
i18nService, platformUtilsService, messagingService, false);
|
i18nService, platformUtilsService, messagingService, false);
|
||||||
const configurationService = new ConfigurationService(storageService, secureStorageService);
|
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);
|
const analytics = new Analytics(window, () => true, platformUtilsService, storageService, appIdService);
|
||||||
containerService.attachToWindow(window);
|
containerService.attachToWindow(window);
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import * as querystring from 'querystring';
|
|||||||
import { DirectoryType } from '../enums/directoryType';
|
import { DirectoryType } from '../enums/directoryType';
|
||||||
|
|
||||||
import { AzureConfiguration } from '../models/azureConfiguration';
|
import { AzureConfiguration } from '../models/azureConfiguration';
|
||||||
|
import { GroupEntry } from '../models/groupEntry';
|
||||||
import { SyncConfiguration } from '../models/syncConfiguration';
|
import { SyncConfiguration } from '../models/syncConfiguration';
|
||||||
|
import { UserEntry } from '../models/userEntry';
|
||||||
|
|
||||||
import { ConfigurationService } from './configuration.service';
|
import { ConfigurationService } from './configuration.service';
|
||||||
import { DirectoryService } from './directory.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();
|
const type = await this.configurationService.getDirectoryType();
|
||||||
if (type !== DirectoryType.AzureActiveDirectory) {
|
if (type !== DirectoryType.AzureActiveDirectory) {
|
||||||
return;
|
return;
|
||||||
@@ -74,6 +76,8 @@ export class AzureDirectoryService implements DirectoryService {
|
|||||||
|
|
||||||
await this.getUsers();
|
await this.getUsers();
|
||||||
await this.getGroups();
|
await this.getGroups();
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getUsers() {
|
private async getUsers() {
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import { GroupEntry } from '../models/groupEntry';
|
||||||
|
import { UserEntry } from '../models/userEntry';
|
||||||
|
|
||||||
export interface DirectoryService {
|
export interface DirectoryService {
|
||||||
getEntries(force?: boolean): any;
|
getEntries(force?: boolean): Promise<[GroupEntry[], UserEntry[]]>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,26 @@
|
|||||||
import { JWT } from 'google-auth-library';
|
import { JWT } from 'google-auth-library';
|
||||||
import { google, GoogleApis } from 'googleapis';
|
import {
|
||||||
import { Admin } from 'googleapis/build/src/apis/admin/directory_v1';
|
google,
|
||||||
|
GoogleApis,
|
||||||
|
} from 'googleapis';
|
||||||
|
import {
|
||||||
|
Admin,
|
||||||
|
Schema$Group,
|
||||||
|
Schema$User,
|
||||||
|
} from 'googleapis/build/src/apis/admin/directory_v1';
|
||||||
|
|
||||||
import { DirectoryType } from '../enums/directoryType';
|
import { DirectoryType } from '../enums/directoryType';
|
||||||
|
|
||||||
|
import { GroupEntry } from '../models/groupEntry';
|
||||||
import { GSuiteConfiguration } from '../models/gsuiteConfiguration';
|
import { GSuiteConfiguration } from '../models/gsuiteConfiguration';
|
||||||
import { SyncConfiguration } from '../models/syncConfiguration';
|
import { SyncConfiguration } from '../models/syncConfiguration';
|
||||||
|
import { UserEntry } from '../models/userEntry';
|
||||||
|
|
||||||
import { ConfigurationService } from './configuration.service';
|
import { ConfigurationService } from './configuration.service';
|
||||||
import { DirectoryService } from './directory.service';
|
import { DirectoryService } from './directory.service';
|
||||||
|
|
||||||
|
import { LogService } from 'jslib/abstractions/log.service';
|
||||||
|
|
||||||
export class GSuiteDirectoryService implements DirectoryService {
|
export class GSuiteDirectoryService implements DirectoryService {
|
||||||
private client: JWT;
|
private client: JWT;
|
||||||
private service: Admin;
|
private service: Admin;
|
||||||
@@ -17,11 +28,11 @@ export class GSuiteDirectoryService implements DirectoryService {
|
|||||||
private dirConfig: GSuiteConfiguration;
|
private dirConfig: GSuiteConfiguration;
|
||||||
private syncConfig: SyncConfiguration;
|
private syncConfig: SyncConfiguration;
|
||||||
|
|
||||||
constructor(private configurationService: ConfigurationService) {
|
constructor(private configurationService: ConfigurationService, private logService: LogService) {
|
||||||
this.service = google.admin<Admin>('directory_v1');
|
this.service = google.admin<Admin>('directory_v1');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEntries(force = false) {
|
async getEntries(force = false): Promise<[GroupEntry[], UserEntry[]]> {
|
||||||
const type = await this.configurationService.getDirectoryType();
|
const type = await this.configurationService.getDirectoryType();
|
||||||
if (type !== DirectoryType.GSuite) {
|
if (type !== DirectoryType.GSuite) {
|
||||||
return;
|
return;
|
||||||
@@ -38,32 +49,203 @@ export class GSuiteDirectoryService implements DirectoryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await this.auth();
|
await this.auth();
|
||||||
await this.getUsers();
|
|
||||||
await this.getGroups();
|
let users: UserEntry[];
|
||||||
|
if (this.syncConfig.users) {
|
||||||
|
users = await this.getUsers();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getUsers() {
|
let groups: GroupEntry[];
|
||||||
const response = await this.service.users.list(this.authParams);
|
if (this.syncConfig.groups) {
|
||||||
console.log(response);
|
groups = await this.getGroups();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getGroups() {
|
return [groups, users];
|
||||||
const response = await this.service.groups.list(this.authParams);
|
}
|
||||||
console.log(response);
|
|
||||||
|
|
||||||
if (response.data.groups.length === 0) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
response.data.groups.forEach(async (g) => {
|
const entry = this.buildUser(user, false);
|
||||||
const params: any = Object.assign({
|
if (entry != null) {
|
||||||
groupKey: g.id,
|
entries.push(entry);
|
||||||
}, this.authParams);
|
}
|
||||||
const members = await this.service.members.list(params);
|
|
||||||
console.log(members);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() {
|
private async auth() {
|
||||||
this.client = new google.auth.JWT({
|
this.client = new google.auth.JWT({
|
||||||
email: this.dirConfig.clientEmail,
|
email: this.dirConfig.clientEmail,
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ import * as ldap from 'ldapjs';
|
|||||||
|
|
||||||
import { DirectoryType } from '../enums/directoryType';
|
import { DirectoryType } from '../enums/directoryType';
|
||||||
|
|
||||||
|
import { GroupEntry } from '../models/groupEntry';
|
||||||
import { LdapConfiguration } from '../models/ldapConfiguration';
|
import { LdapConfiguration } from '../models/ldapConfiguration';
|
||||||
import { SyncConfiguration } from '../models/syncConfiguration';
|
import { SyncConfiguration } from '../models/syncConfiguration';
|
||||||
|
import { UserEntry } from '../models/userEntry';
|
||||||
|
|
||||||
import { ConfigurationService } from './configuration.service';
|
import { ConfigurationService } from './configuration.service';
|
||||||
import { DirectoryService } from './directory.service';
|
import { DirectoryService } from './directory.service';
|
||||||
@@ -15,7 +17,7 @@ export class LdapDirectoryService implements DirectoryService {
|
|||||||
|
|
||||||
constructor(private configurationService: ConfigurationService) { }
|
constructor(private configurationService: ConfigurationService) { }
|
||||||
|
|
||||||
async getEntries(force = false) {
|
async getEntries(force = false): Promise<[GroupEntry[], UserEntry[]]> {
|
||||||
const type = await this.configurationService.getDirectoryType();
|
const type = await this.configurationService.getDirectoryType();
|
||||||
if (type !== DirectoryType.Ldap) {
|
if (type !== DirectoryType.Ldap) {
|
||||||
return;
|
return;
|
||||||
@@ -33,6 +35,8 @@ export class LdapDirectoryService implements DirectoryService {
|
|||||||
|
|
||||||
await this.auth();
|
await this.auth();
|
||||||
await this.getUsers();
|
await this.getUsers();
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getUsers() {
|
private getUsers() {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { DirectoryType } from '../enums/directoryType';
|
import { DirectoryType } from '../enums/directoryType';
|
||||||
|
|
||||||
|
import { LogService } from 'jslib/abstractions/log.service';
|
||||||
import { StorageService } from 'jslib/abstractions/storage.service';
|
import { StorageService } from 'jslib/abstractions/storage.service';
|
||||||
|
|
||||||
import { AzureDirectoryService } from './azure-directory.service';
|
import { AzureDirectoryService } from './azure-directory.service';
|
||||||
@@ -14,7 +15,7 @@ const Keys = {
|
|||||||
export class SyncService {
|
export class SyncService {
|
||||||
private dirType: DirectoryType;
|
private dirType: DirectoryType;
|
||||||
|
|
||||||
constructor(private configurationService: ConfigurationService) { }
|
constructor(private configurationService: ConfigurationService, private logService: LogService) { }
|
||||||
|
|
||||||
async sync(force = true, sendToServer = true): Promise<any> {
|
async sync(force = true, sendToServer = true): Promise<any> {
|
||||||
this.dirType = await this.configurationService.getDirectoryType();
|
this.dirType = await this.configurationService.getDirectoryType();
|
||||||
@@ -27,13 +28,14 @@ export class SyncService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
directoryService.getEntries(force);
|
const entries = await directoryService.getEntries(force);
|
||||||
|
console.log(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getDirectoryService(): DirectoryService {
|
private getDirectoryService(): DirectoryService {
|
||||||
switch (this.dirType) {
|
switch (this.dirType) {
|
||||||
case DirectoryType.GSuite:
|
case DirectoryType.GSuite:
|
||||||
return new GSuiteDirectoryService(this.configurationService);
|
return new GSuiteDirectoryService(this.configurationService, this.logService);
|
||||||
case DirectoryType.AzureActiveDirectory:
|
case DirectoryType.AzureActiveDirectory:
|
||||||
return new AzureDirectoryService(this.configurationService);
|
return new AzureDirectoryService(this.configurationService);
|
||||||
case DirectoryType.Ldap:
|
case DirectoryType.Ldap:
|
||||||
|
|||||||
Reference in New Issue
Block a user