mirror of
https://github.com/bitwarden/directory-connector
synced 2025-12-15 07:43:27 +00:00
wire up directory services to settings
This commit is contained in:
@@ -17,6 +17,7 @@ import { LaunchGuardService } from './launch-guard.service';
|
||||
|
||||
import { ConfigurationService } from '../../services/configuration.service';
|
||||
import { I18nService } from '../../services/i18n.service';
|
||||
import { SyncService } from '../../services/sync.service';
|
||||
|
||||
import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
|
||||
import { ValidationService } from 'jslib/angular/services/validation.service';
|
||||
@@ -70,6 +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 analytics = new Analytics(window, () => true, platformUtilsService, storageService, appIdService);
|
||||
containerService.attachToWindow(window);
|
||||
@@ -123,6 +125,7 @@ export function initFactory(): Function {
|
||||
{ provide: StateServiceAbstraction, useValue: stateService },
|
||||
{ provide: LogServiceAbstraction, useValue: logService },
|
||||
{ provide: ConfigurationService, useValue: configurationService },
|
||||
{ provide: SyncService, useValue: syncSevrice },
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: initFactory,
|
||||
|
||||
@@ -1,7 +1,2 @@
|
||||
<i class="fa fa-rocket"></i>
|
||||
|
||||
The dashboard!!
|
||||
|
||||
<button (click)="gsuite()">G Suite</button>
|
||||
<button (click)="ldap()">LDAP</button>
|
||||
<button (click)="azuread()">Azure AD</button>
|
||||
<button (click)="sync()">Sync</button>
|
||||
<button (click)="simulate()">Simulate</button>
|
||||
|
||||
@@ -15,6 +15,7 @@ import { ModalComponent } from 'jslib/angular/components/modal.component';
|
||||
import { AzureDirectoryService } from '../../services/azure-directory.service';
|
||||
import { GSuiteDirectoryService } from '../../services/gsuite-directory.service';
|
||||
import { LdapDirectoryService } from '../../services/ldap-directory.service';
|
||||
import { SyncService } from '../../services/sync.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
@@ -24,20 +25,14 @@ export class DashboardComponent {
|
||||
@ViewChild('settings', { read: ViewContainerRef }) settingsModal: ViewContainerRef;
|
||||
|
||||
constructor(analytics: Angulartics2, toasterService: ToasterService,
|
||||
i18nService: I18nService, private componentFactoryResolver: ComponentFactoryResolver) { }
|
||||
i18nService: I18nService, private componentFactoryResolver: ComponentFactoryResolver,
|
||||
private syncService: SyncService) { }
|
||||
|
||||
gsuite() {
|
||||
const gsuite = new GSuiteDirectoryService();
|
||||
const entries = gsuite.getEntries();
|
||||
async sync() {
|
||||
await this.syncService.sync(true, true);
|
||||
}
|
||||
|
||||
ldap() {
|
||||
const gsuite = new LdapDirectoryService();
|
||||
const entries = gsuite.getEntries();
|
||||
}
|
||||
|
||||
azuread() {
|
||||
const gsuite = new AzureDirectoryService();
|
||||
const entries = gsuite.getEntries();
|
||||
async simulate() {
|
||||
await this.syncService.sync(true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="privateKey">{{'privateKey' | i18n}}</label>
|
||||
<textarea class="form-control" id="privateKey" name="PrivateKey" [(ngModel)]="gsuite.privateKey">
|
||||
<textarea class="form-control text-monospace" id="privateKey" name="PrivateKey" [(ngModel)]="gsuite.privateKey">
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,6 @@ import { ToasterService } from 'angular2-toaster';
|
||||
import { Angulartics2 } from 'angulartics2';
|
||||
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { StorageService } from 'jslib/abstractions/storage.service';
|
||||
|
||||
import { ConfigurationService } from '../../services/configuration.service';
|
||||
|
||||
@@ -36,7 +35,7 @@ export class SettingsComponent implements OnInit {
|
||||
|
||||
constructor(analytics: Angulartics2, toasterService: ToasterService,
|
||||
i18nService: I18nService, private componentFactoryResolver: ComponentFactoryResolver,
|
||||
private configurationService: ConfigurationService, private storageService: StorageService) {
|
||||
private configurationService: ConfigurationService) {
|
||||
this.directoryOptions = [
|
||||
{ name: i18nService.t('select'), value: null },
|
||||
{ name: 'Active Directory / LDAP', value: DirectoryType.Ldap },
|
||||
@@ -46,21 +45,21 @@ export class SettingsComponent implements OnInit {
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.directory = await this.storageService.get<DirectoryType>('directory');
|
||||
this.ldap = (await this.configurationService.get<LdapConfiguration>(DirectoryType.Ldap)) ||
|
||||
this.directory = await this.configurationService.getDirectoryType();
|
||||
this.ldap = (await this.configurationService.getDirectory<LdapConfiguration>(DirectoryType.Ldap)) ||
|
||||
this.ldap;
|
||||
this.gsuite = (await this.configurationService.get<GSuiteConfiguration>(DirectoryType.GSuite)) ||
|
||||
this.gsuite = (await this.configurationService.getDirectory<GSuiteConfiguration>(DirectoryType.GSuite)) ||
|
||||
this.gsuite;
|
||||
this.azure = (await this.configurationService.get<AzureConfiguration>(DirectoryType.AzureActiveDirectory)) ||
|
||||
this.azure;
|
||||
this.sync = (await this.storageService.get<SyncConfiguration>('syncConfig')) || this.sync;
|
||||
this.azure = (await this.configurationService.getDirectory<AzureConfiguration>(
|
||||
DirectoryType.AzureActiveDirectory)) || this.azure;
|
||||
this.sync = (await this.configurationService.getSync()) || this.sync;
|
||||
}
|
||||
|
||||
async submit() {
|
||||
await this.storageService.save('directory', this.directory);
|
||||
await this.configurationService.save(DirectoryType.Ldap, this.ldap);
|
||||
await this.configurationService.save(DirectoryType.GSuite, this.gsuite);
|
||||
await this.configurationService.save(DirectoryType.AzureActiveDirectory, this.azure);
|
||||
await this.storageService.save('syncConfig', this.sync);
|
||||
await this.configurationService.saveDirectoryType(this.directory);
|
||||
await this.configurationService.saveDirectory(DirectoryType.Ldap, this.ldap);
|
||||
await this.configurationService.saveDirectory(DirectoryType.GSuite, this.gsuite);
|
||||
await this.configurationService.saveDirectory(DirectoryType.AzureActiveDirectory, this.azure);
|
||||
await this.configurationService.saveSync(this.sync);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,28 +2,32 @@ import * as graph from '@microsoft/microsoft-graph-client';
|
||||
import * as https from 'https';
|
||||
import * as querystring from 'querystring';
|
||||
|
||||
import { DirectoryService } from 'src/services/directory.service';
|
||||
import { DirectoryType } from '../enums/directoryType';
|
||||
|
||||
const Key = '';
|
||||
const ApplicationId = '';
|
||||
const Tenant = '';
|
||||
import { AzureConfiguration } from '../models/azureConfiguration';
|
||||
import { SyncConfiguration } from '../models/syncConfiguration';
|
||||
|
||||
import { ConfigurationService } from './configuration.service';
|
||||
import { DirectoryService } from './directory.service';
|
||||
|
||||
export class AzureDirectoryService implements DirectoryService {
|
||||
private client: graph.Client;
|
||||
private dirConfig: AzureConfiguration;
|
||||
private syncConfig: SyncConfiguration;
|
||||
|
||||
async getEntries(force = false) {
|
||||
constructor(private configurationService: ConfigurationService) {
|
||||
this.client = graph.Client.init({
|
||||
authProvider: (done) => {
|
||||
const data = querystring.stringify({
|
||||
client_id: ApplicationId,
|
||||
client_secret: Key,
|
||||
client_id: this.dirConfig.applicationId,
|
||||
client_secret: this.dirConfig.key,
|
||||
grant_type: 'client_credentials',
|
||||
scope: 'https://graph.microsoft.com/.default',
|
||||
});
|
||||
|
||||
const req = https.request({
|
||||
host: 'login.microsoftonline.com',
|
||||
path: '/' + Tenant + '/oauth2/v2.0/token',
|
||||
path: '/' + this.dirConfig.tenant + '/oauth2/v2.0/token',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
@@ -49,6 +53,24 @@ export class AzureDirectoryService implements DirectoryService {
|
||||
req.end();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async getEntries(force = false) {
|
||||
const type = await this.configurationService.getDirectoryType();
|
||||
if (type !== DirectoryType.AzureActiveDirectory) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.dirConfig = await this.configurationService.getDirectory<AzureConfiguration>(
|
||||
DirectoryType.AzureActiveDirectory);
|
||||
if (this.dirConfig == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.syncConfig = await this.configurationService.getSync();
|
||||
if (this.syncConfig == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.getUsers();
|
||||
await this.getGroups();
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { DirectoryType } from '../enums/directoryType';
|
||||
|
||||
import { StorageService } from 'jslib/abstractions/storage.service';
|
||||
import { AzureConfiguration } from '../models/azureConfiguration';
|
||||
import { GSuiteConfiguration } from '../models/gsuiteConfiguration';
|
||||
import { LdapConfiguration } from '../models/ldapConfiguration';
|
||||
import { SyncConfiguration } from '../models/syncConfiguration';
|
||||
|
||||
const StoredSecurely = '[STORED SECURELY]';
|
||||
const Keys = {
|
||||
@@ -8,12 +12,14 @@ const Keys = {
|
||||
gsuite: 'gsuitePrivateKey',
|
||||
azure: 'azureKey',
|
||||
directoryConfigPrefix: 'directoryConfig_',
|
||||
sync: 'syncConfig',
|
||||
directoryType: 'directoryType',
|
||||
};
|
||||
|
||||
export class ConfigurationService {
|
||||
constructor(private storageService: StorageService, private secureStorageService: StorageService) { }
|
||||
|
||||
async get<T>(type: DirectoryType): Promise<T> {
|
||||
async getDirectory<T>(type: DirectoryType): Promise<T> {
|
||||
const config = await this.storageService.get<T>(Keys.directoryConfigPrefix + type);
|
||||
if (config == null) {
|
||||
return config;
|
||||
@@ -33,7 +39,8 @@ export class ConfigurationService {
|
||||
return config;
|
||||
}
|
||||
|
||||
async save<T>(type: DirectoryType, config: T): Promise<any> {
|
||||
async saveDirectory(type: DirectoryType,
|
||||
config: LdapConfiguration | GSuiteConfiguration | AzureConfiguration): Promise<any> {
|
||||
const savedConfig: any = Object.assign({}, config);
|
||||
switch (type) {
|
||||
case DirectoryType.Ldap:
|
||||
@@ -56,6 +63,8 @@ export class ConfigurationService {
|
||||
if (savedConfig.privateKey == null) {
|
||||
await this.secureStorageService.remove(Keys.gsuite);
|
||||
} else {
|
||||
(config as any).privateKey = savedConfig.privateKey =
|
||||
savedConfig.privateKey.replace(/\\n/g, '\n');
|
||||
await this.secureStorageService.save(Keys.gsuite, savedConfig.privateKey);
|
||||
savedConfig.privateKey = StoredSecurely;
|
||||
}
|
||||
@@ -63,4 +72,20 @@ export class ConfigurationService {
|
||||
}
|
||||
await this.storageService.save(Keys.directoryConfigPrefix + type, savedConfig);
|
||||
}
|
||||
|
||||
async getSync(): Promise<SyncConfiguration> {
|
||||
return this.storageService.get<SyncConfiguration>(Keys.sync);
|
||||
}
|
||||
|
||||
async saveSync(config: SyncConfiguration) {
|
||||
return this.storageService.save(Keys.sync, config);
|
||||
}
|
||||
|
||||
async getDirectoryType(): Promise<DirectoryType> {
|
||||
return this.storageService.get<DirectoryType>(Keys.directoryType);
|
||||
}
|
||||
|
||||
async saveDirectoryType(type: DirectoryType) {
|
||||
return this.storageService.save(Keys.directoryType, type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,41 @@ import { JWT } from 'google-auth-library';
|
||||
import { google, GoogleApis } from 'googleapis';
|
||||
import { Admin } from 'googleapis/build/src/apis/admin/directory_v1';
|
||||
|
||||
import { DirectoryService } from 'src/services/directory.service';
|
||||
import { DirectoryType } from '../enums/directoryType';
|
||||
|
||||
const PrivateKey = '';
|
||||
const ClientEmail = '';
|
||||
const AdminEmail = '';
|
||||
const Domain = '';
|
||||
import { GSuiteConfiguration } from '../models/gsuiteConfiguration';
|
||||
import { SyncConfiguration } from '../models/syncConfiguration';
|
||||
|
||||
import { ConfigurationService } from './configuration.service';
|
||||
import { DirectoryService } from './directory.service';
|
||||
|
||||
export class GSuiteDirectoryService implements DirectoryService {
|
||||
private client: JWT;
|
||||
private service: Admin;
|
||||
private authParams: any;
|
||||
private dirConfig: GSuiteConfiguration;
|
||||
private syncConfig: SyncConfiguration;
|
||||
|
||||
constructor() {
|
||||
constructor(private configurationService: ConfigurationService) {
|
||||
this.service = google.admin<Admin>('directory_v1');
|
||||
}
|
||||
|
||||
async getEntries(force = false) {
|
||||
const type = await this.configurationService.getDirectoryType();
|
||||
if (type !== DirectoryType.GSuite) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.dirConfig = await this.configurationService.getDirectory<GSuiteConfiguration>(DirectoryType.GSuite);
|
||||
if (this.dirConfig == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.syncConfig = await this.configurationService.getSync();
|
||||
if (this.syncConfig == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.auth();
|
||||
await this.getUsers();
|
||||
await this.getGroups();
|
||||
@@ -48,9 +66,9 @@ export class GSuiteDirectoryService implements DirectoryService {
|
||||
|
||||
private async auth() {
|
||||
this.client = new google.auth.JWT({
|
||||
email: ClientEmail,
|
||||
key: PrivateKey,
|
||||
subject: AdminEmail,
|
||||
email: this.dirConfig.clientEmail,
|
||||
key: this.dirConfig.privateKey,
|
||||
subject: this.dirConfig.adminUser,
|
||||
scopes: [
|
||||
'https://www.googleapis.com/auth/admin.directory.user.readonly',
|
||||
'https://www.googleapis.com/auth/admin.directory.group.readonly',
|
||||
@@ -59,11 +77,13 @@ export class GSuiteDirectoryService implements DirectoryService {
|
||||
});
|
||||
|
||||
await this.client.authorize();
|
||||
|
||||
this.authParams = {
|
||||
auth: this.client,
|
||||
domain: Domain,
|
||||
domain: this.dirConfig.domain,
|
||||
};
|
||||
|
||||
// TODO: add customer?
|
||||
if (this.dirConfig.customer != null) {
|
||||
this.authParams.customer = this.dirConfig.customer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,36 @@
|
||||
import * as ldap from 'ldapjs';
|
||||
|
||||
import { DirectoryService } from 'src/services/directory.service';
|
||||
import { DirectoryType } from '../enums/directoryType';
|
||||
|
||||
const Url = 'ldap://ldap.forumsys.com:389';
|
||||
const Username = 'cn=read-only-admin,dc=example,dc=com';
|
||||
const Password = 'password';
|
||||
import { LdapConfiguration } from '../models/ldapConfiguration';
|
||||
import { SyncConfiguration } from '../models/syncConfiguration';
|
||||
|
||||
import { ConfigurationService } from './configuration.service';
|
||||
import { DirectoryService } from './directory.service';
|
||||
|
||||
export class LdapDirectoryService implements DirectoryService {
|
||||
private client: ldap.Client;
|
||||
private dirConfig: LdapConfiguration;
|
||||
private syncConfig: SyncConfiguration;
|
||||
|
||||
constructor(private configurationService: ConfigurationService) { }
|
||||
|
||||
async getEntries(force = false) {
|
||||
const type = await this.configurationService.getDirectoryType();
|
||||
if (type !== DirectoryType.Ldap) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.dirConfig = await this.configurationService.getDirectory<LdapConfiguration>(DirectoryType.Ldap);
|
||||
if (this.dirConfig == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.syncConfig = await this.configurationService.getSync();
|
||||
if (this.syncConfig == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.auth();
|
||||
await this.getUsers();
|
||||
}
|
||||
@@ -49,11 +70,13 @@ export class LdapDirectoryService implements DirectoryService {
|
||||
|
||||
private async auth() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const url = 'ldap' + (this.dirConfig.ssl ? 's' : '') + '://' + this.dirConfig.hostname +
|
||||
':' + this.dirConfig.port;
|
||||
this.client = ldap.createClient({
|
||||
url: Url,
|
||||
url: url,
|
||||
});
|
||||
|
||||
this.client.bind(Username, Password, (err) => {
|
||||
this.client.bind(this.dirConfig.username, this.dirConfig.password, (err) => {
|
||||
if (err != null) {
|
||||
reject(err);
|
||||
} else {
|
||||
|
||||
45
src/services/sync.service.ts
Normal file
45
src/services/sync.service.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { DirectoryType } from '../enums/directoryType';
|
||||
|
||||
import { StorageService } from 'jslib/abstractions/storage.service';
|
||||
|
||||
import { AzureDirectoryService } from './azure-directory.service';
|
||||
import { ConfigurationService } from './configuration.service';
|
||||
import { DirectoryService } from './directory.service';
|
||||
import { GSuiteDirectoryService } from './gsuite-directory.service';
|
||||
import { LdapDirectoryService } from './ldap-directory.service';
|
||||
|
||||
const Keys = {
|
||||
};
|
||||
|
||||
export class SyncService {
|
||||
private dirType: DirectoryType;
|
||||
|
||||
constructor(private configurationService: ConfigurationService) { }
|
||||
|
||||
async sync(force = true, sendToServer = true): Promise<any> {
|
||||
this.dirType = await this.configurationService.getDirectoryType();
|
||||
if (this.dirType == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const directoryService = this.getDirectoryService();
|
||||
if (directoryService == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
directoryService.getEntries(force);
|
||||
}
|
||||
|
||||
private getDirectoryService(): DirectoryService {
|
||||
switch (this.dirType) {
|
||||
case DirectoryType.GSuite:
|
||||
return new GSuiteDirectoryService(this.configurationService);
|
||||
case DirectoryType.AzureActiveDirectory:
|
||||
return new AzureDirectoryService(this.configurationService);
|
||||
case DirectoryType.Ldap:
|
||||
return new LdapDirectoryService(this.configurationService);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user