1
0
mirror of https://github.com/bitwarden/directory-connector synced 2025-12-11 13:53:22 +00:00

settings configuration

This commit is contained in:
Kyle Spearrin
2018-04-27 17:16:54 -04:00
parent e7787ea95c
commit 2800c2f077
11 changed files with 472 additions and 7 deletions

View File

@@ -7,6 +7,7 @@ root = true
[*] [*]
end_of_line = lf end_of_line = lf
insert_final_newline = true insert_final_newline = true
quote_type = single
# Set default charset # Set default charset
[*.{js,ts,scss,html}] [*.{js,ts,scss,html}]

View File

@@ -15,6 +15,7 @@ import { isDev } from 'jslib/electron/utils';
import { AuthGuardService } from './auth-guard.service'; import { AuthGuardService } from './auth-guard.service';
import { LaunchGuardService } from './launch-guard.service'; import { LaunchGuardService } from './launch-guard.service';
import { ConfigurationService } from '../../services/configuration.service';
import { I18nService } from '../../services/i18n.service'; import { I18nService } from '../../services/i18n.service';
import { BroadcasterService } from 'jslib/angular/services/broadcaster.service'; import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
@@ -68,6 +69,7 @@ const userService = new UserService(tokenService, storageService);
const containerService = new ContainerService(cryptoService, platformUtilsService); const containerService = new ContainerService(cryptoService, platformUtilsService);
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 analytics = new Analytics(window, () => true, platformUtilsService, storageService, appIdService); const analytics = new Analytics(window, () => true, platformUtilsService, storageService, appIdService);
containerService.attachToWindow(window); containerService.attachToWindow(window);
@@ -120,6 +122,7 @@ export function initFactory(): Function {
{ provide: StorageServiceAbstraction, useValue: storageService }, { provide: StorageServiceAbstraction, useValue: storageService },
{ provide: StateServiceAbstraction, useValue: stateService }, { provide: StateServiceAbstraction, useValue: stateService },
{ provide: LogServiceAbstraction, useValue: logService }, { provide: LogServiceAbstraction, useValue: logService },
{ provide: ConfigurationService, useValue: configurationService },
{ {
provide: APP_INITIALIZER, provide: APP_INITIALIZER,
useFactory: initFactory, useFactory: initFactory,

View File

@@ -1,3 +1,194 @@
<i class="fa fa-rocket"></i> <form (ngSubmit)="submit()">
<h2>Directory</h2>
<div class="form-group">
<label for="directory">{{'type' | i18n}}</label>
<select class="form-control" id="directory" name="Directory" [(ngModel)]="directory">
<option *ngFor="let o of directoryOptions" [ngValue]="o.value">{{o.name}}</option>
</select>
</div>
<div [hidden]="directory != directoryType.Ldap">
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="ssl" [(ngModel)]="ldap.ssl" name="SSL">
<label class="form-check-label" for="ssl">SSL (LDAPS)</label>
</div>
</div>
<div class="form-group">
<label for="hostname">{{'serverHostname' | i18n}}</label>
<input type="text" class="form-control" id="hostname" name="Hostname"
placeholder="{{'ex' | i18n}} ad.company.com" [(ngModel)]="ldap.hostname">
</div>
<div class="form-group">
<label for="port">{{'port' | i18n}}</label>
<input type="text" class="form-control" id="port" name="Port" placeholder="{{'ex' | i18n}} 389"
[(ngModel)]="ldap.port">
</div>
<div class="form-group">
<label for="rootPath">{{'rootPath' | i18n}}</label>
<input type="text" class="form-control" id="rootPath" name="RootPath" [(ngModel)]="ldap.rootPath"
placeholder="{{'ex' | i18n}} DC=ad,DC=company,DC=com">
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="currentUser" [(ngModel)]="ldap.currentUser"
name="CurrentUser">
<label class="form-check-label" for="currentUser">{{'currentUser' | i18n}}</label>
</div>
</div>
<div [hidden]="ldap.currentUser">
<div class="form-group">
<label for="username">{{'username' | i18n}}</label>
<input type="text" class="form-control" id="username" name="Username" [(ngModel)]="ldap.username"
placeholder="{{'ex' | i18n}} admin@company.com">
</div>
<div class="form-group">
<label for="password">{{'password' | i18n}}</label>
<input type="password" class="form-control" id="password" name="Password" [(ngModel)]="ldap.password">
</div>
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="ad" [(ngModel)]="ldap.ad" name="AD">
<label class="form-check-label" for="ad">Active Directory</label>
</div>
</div>
</div>
<div [hidden]="directory != directoryType.AzureActiveDirectory">
<div class="form-group">
<label for="tenant">{{'tenant' | i18n}}</label>
<input type="text" class="form-control" id="tenant" name="Tenant" [(ngModel)]="azure.tenant"
placeholder="{{'ex' | i18n}} companyad.onmicrosoft.com">
</div>
<div class="form-group">
<label for="applicationId">{{'applicationId' | i18n}}</label>
<input type="text" class="form-control" id="applicationId" name="ApplicationId"
[(ngModel)]="azure.applicationId">
</div>
<div class="form-group">
<label for="secretKey">{{'secretKey' | i18n}}</label>
<input type="password" class="form-control" id="secretKey" name="SecretKey" [(ngModel)]="azure.key">
</div>
</div>
<div [hidden]="directory != directoryType.GSuite">
<div class="form-group">
<label for="domain">{{'domain' | i18n}}</label>
<input type="text" class="form-control" id="domain" name="Domain" [(ngModel)]="gsuite.domain"
placeholder="{{'ex' | i18n}} company.com">
</div>
<div class="form-group">
<label for="adminUser">{{'adminUser' | i18n}}</label>
<input type="text" class="form-control" id="adminUser" name="AdminUser" [(ngModel)]="gsuite.adminUser"
placeholder="{{'ex' | i18n}} admin@company.com">
</div>
<div class="form-group">
<label for="customerId">{{'customerId' | i18n}}</label>
<input type="text" class="form-control" id="customerId" name="CustomerId" [(ngModel)]="gsuite.customer"
placeholder="{{'ex' | i18n}} 39204722352">
</div>
<div class="form-group">
<label for="clientEmail">{{'clientEmail' | i18n}}</label>
<input type="text" class="form-control" id="clientEmail" name="ClientEmail"
[(ngModel)]="gsuite.clientEmail">
</div>
<div class="form-group">
<label for="privateKey">{{'privateKey' | i18n}}</label>
<textarea class="form-control" id="privateKey" name="PrivateKey" [(ngModel)]="gsuite.privateKey">
</textarea>
</div>
</div>
The settings!!! <h2>Sync</h2>
<div class="form-group">
<label for="interval">{{'interval' | i18n}}</label>
<input type="number" min="5" class="form-control" id="interval" name="Interval" [(ngModel)]="sync.interval">
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="removeDisabled" [(ngModel)]="sync.removeDisabled"
name="RemoveDisabled">
<label class="form-check-label" for="removeDisabled">{{'removeDisabled' | i18n}}</label>
</div>
</div>
<div class="form-group">
<label for="memberAttribute">{{'memberAttribute' | i18n}}</label>
<input type="text" class="form-control" id="memberAttribute" name="MemberAttribute" [(ngModel)]="sync.memberAttribute">
</div>
<div class="form-group">
<label for="creationDateAttribute">{{'creationDateAttribute' | i18n}}</label>
<input type="text" class="form-control" id="creationDateAttribute" name="CreationDateAttribute" [(ngModel)]="sync.creationDateAttribute">
</div>
<div class="form-group">
<label for="revisionDateAttribute">{{'revisionDateAttribute' | i18n}}</label>
<input type="text" class="form-control" id="revisionDateAttribute" name="RevisionDateAttribute" [(ngModel)]="sync.revisionDateAttribute">
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="useEmailPrefixSuffix" [(ngModel)]="sync.useEmailPrefixSuffix" name="UseEmailPrefixSuffix">
<label class="form-check-label" for="useEmailPrefixSuffix">{{'useEmailPrefixSuffix' | i18n}}</label>
</div>
</div>
<div [hidden]="!sync.useEmailPrefixSuffix">
<div class="form-group">
<label for="emailPrefixAttribute">{{'emailPrefixAttribute' | i18n}}</label>
<input type="text" class="form-control" id="emailPrefixAttribute" name="EmailPrefixAttribute" [(ngModel)]="sync.emailPrefixAttribute">
</div>
<div class="form-group">
<label for="emailSuffix">{{'emailSuffix' | i18n}}</label>
<input type="text" class="form-control" id="emailSuffix" name="EmailSuffix" [(ngModel)]="sync.emailSuffix">
</div>
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="syncUsers" [(ngModel)]="sync.users" name="SyncUsers">
<label class="form-check-label" for="syncUsers">{{'syncUsers' | i18n}}</label>
</div>
</div>
<div [hidden]="!sync.users">
<div class="form-group">
<label for="userFilter">{{'userFilter' | i18n}}</label>
<textarea class="form-control" id="userFilter" name="UserFilter" [(ngModel)]="sync.userFilter">
</textarea>
</div>
<div class="form-group">
<label for="userObjectClass">{{'userObjectClass' | i18n}}</label>
<input type="text" class="form-control" id="userObjectClass" name="UserObjectClass" [(ngModel)]="sync.userObjectClass">
</div>
<div class="form-group">
<label for="userPath">{{'userPath' | i18n}}</label>
<input type="text" class="form-control" id="userPath" name="UserPath" [(ngModel)]="sync.userPath">
</div>
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="syncGroups" [(ngModel)]="sync.groups" name="SyncGroups">
<label class="form-check-label" for="syncGroups">{{'syncGroups' | i18n}}</label>
</div>
</div>
<div [hidden]="!sync.groups">
<div class="form-group">
<label for="groupFilter">{{'groupFilter' | i18n}}</label>
<textarea class="form-control" id="groupFilter" name="GroupFilter" [(ngModel)]="sync.groupFilter">
</textarea>
</div>
<div class="form-group">
<label for="groupObjectClass">{{'groupObjectClass' | i18n}}</label>
<input type="text" class="form-control" id="groupObjectClass" name="GroupObjectClass" [(ngModel)]="sync.groupObjectClass">
</div>
<div class="form-group">
<label for="groupPath">{{'groupPath' | i18n}}</label>
<input type="text" class="form-control" id="groupPath" name="GroupPath" [(ngModel)]="sync.groupPath">
</div>
<div class="form-group">
<label for="groupNameAttribute">{{'groupNameAttribute' | i18n}}</label>
<input type="text" class="form-control" id="groupNameAttribute" name="GroupNameAttribute" [(ngModel)]="sync.groupNameAttribute">
</div>
</div>
<button appBlurClick type="submit" class="btn btn-primary">
{{'save' | i18n}}
</button>
</form>

View File

@@ -1,6 +1,7 @@
import { import {
Component, Component,
ComponentFactoryResolver, ComponentFactoryResolver,
OnInit,
ViewChild, ViewChild,
ViewContainerRef, ViewContainerRef,
} from '@angular/core'; } from '@angular/core';
@@ -9,12 +10,57 @@ import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2'; import { Angulartics2 } from 'angulartics2';
import { I18nService } from 'jslib/abstractions/i18n.service'; import { I18nService } from 'jslib/abstractions/i18n.service';
import { StorageService } from 'jslib/abstractions/storage.service';
import { ConfigurationService } from '../../services/configuration.service';
import { DirectoryType } from '../../enums/directoryType';
import { AzureConfiguration } from '../../models/azureConfiguration';
import { GSuiteConfiguration } from '../../models/gsuiteConfiguration';
import { LdapConfiguration } from '../../models/ldapConfiguration';
import { SyncConfiguration } from '../../models/syncConfiguration';
@Component({ @Component({
selector: 'app-settings', selector: 'app-settings',
templateUrl: 'settings.component.html', templateUrl: 'settings.component.html',
}) })
export class SettingsComponent { export class SettingsComponent implements OnInit {
directory: DirectoryType;
directoryType = DirectoryType;
ldap = new LdapConfiguration();
gsuite = new GSuiteConfiguration();
azure = new AzureConfiguration();
sync = new SyncConfiguration();
directoryOptions: any[];
constructor(analytics: Angulartics2, toasterService: ToasterService, constructor(analytics: Angulartics2, toasterService: ToasterService,
i18nService: I18nService, private componentFactoryResolver: ComponentFactoryResolver) {} i18nService: I18nService, private componentFactoryResolver: ComponentFactoryResolver,
private configurationService: ConfigurationService, private storageService: StorageService) {
this.directoryOptions = [
{ name: i18nService.t('select'), value: null },
{ name: 'Active Directory / LDAP', value: DirectoryType.Ldap },
{ name: 'Azure Active Directory', value: DirectoryType.AzureActiveDirectory },
{ name: 'G Suite (Google)', value: DirectoryType.GSuite },
];
}
async ngOnInit() {
this.directory = await this.storageService.get<DirectoryType>('directory');
this.ldap = (await this.configurationService.get<LdapConfiguration>(DirectoryType.Ldap)) ||
this.ldap;
this.gsuite = (await this.configurationService.get<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;
}
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);
}
} }

View File

@@ -0,0 +1,5 @@
export enum DirectoryType {
Ldap = 0,
AzureActiveDirectory = 1,
GSuite = 2,
}

View File

@@ -229,13 +229,13 @@
"message": "Unknown" "message": "Unknown"
}, },
"view": { "view": {
"message": "View" "message": "View"
}, },
"file": { "file": {
"message": "File" "message": "File"
}, },
"edit": { "edit": {
"message": "Edit" "message": "Edit"
}, },
"undo": { "undo": {
"message": "Undo" "message": "Undo"
@@ -303,5 +303,112 @@
}, },
"window": { "window": {
"message": "Window" "message": "Window"
},
"ex": {
"message": "ex.",
"description": "Short abbreviation for 'example'."
},
"serverHostname": {
"message": "Server Hostname"
},
"port": {
"message": "Port",
"description": "A server's port number. ex. google.com:8080"
},
"username": {
"message": "Username"
},
"password": {
"message": "Password"
},
"thisIsActiveDirectory": {
"message": "This is an Active Directory LDAP server."
},
"type": {
"message": "Type"
},
"directory": {
"message": "Directory"
},
"currentUser": {
"message": "Current user"
},
"rootPath": {
"message": "Root Path"
},
"tenant": {
"message": "Tenant"
},
"applicationId": {
"message": "Application Id"
},
"secretKey": {
"message": "Secret Key"
},
"adminUser": {
"message": "Admin User"
},
"domain": {
"message": "Domain"
},
"customerId": {
"message": "Customer Id"
},
"privateKey": {
"message": "Private Key (from key file)"
},
"clientEmail": {
"message": "Client Email (from key file)"
},
"interval": {
"message": "Interval (in minutes)"
},
"removeDisabled": {
"message": "Remove disabled users"
},
"memberAttribute": {
"message": "Member Attribute"
},
"creationDateAttribute": {
"message": "Creation Date Attribute"
},
"revisionDateAttribute": {
"message": "Revision Date Attribute"
},
"useEmailPrefixSuffix": {
"message": "Use email prefix/suffix"
},
"emailPrefixAttribute": {
"message": "Email Prefix Attribute"
},
"emailSuffix": {
"message": "Email Suffix"
},
"syncUsers": {
"message": "Sync users"
},
"userFilter": {
"message": "User Filter"
},
"userObjectClass": {
"message": "User Object Class"
},
"userPath": {
"message": "User Path"
},
"syncGroups": {
"message": "Sync groups"
},
"groupFilter": {
"message": "Group Filter"
},
"groupObjectClass": {
"message": "Group Object Class"
},
"groupPath": {
"message": "Group Path"
},
"groupNameAttribute": {
"message": "Group Name Attribute"
} }
} }

View File

@@ -0,0 +1,5 @@
export class AzureConfiguration {
tenant: string;
applicationId: string;
key: string;
}

View File

@@ -0,0 +1,7 @@
export class GSuiteConfiguration {
clientEmail: string;
privateKey: string;
domain: string;
adminUser: string;
customer: string;
}

View File

@@ -0,0 +1,13 @@
import { DirectoryType } from '../enums/directoryType';
export class LdapConfiguration {
ssl: boolean = false;
hostname: string;
port: number = 389;
domain: string;
rootPath: string;
currentUser: boolean = false;
username: string;
password: string;
ad: boolean = true;
}

View File

@@ -0,0 +1,21 @@
export class SyncConfiguration {
users: boolean = false;
groups: boolean = false;
interval: number = 5;
userFilter: string;
groupFilter: string;
removeDisabled: boolean = false;
// Ldap properties
groupObjectClass: string;
userObjectClass: string;
groupPath: string;
userPath: string;
groupNameAttribute: string;
userEmailAttribute: string;
memberAttribute: string;
useEmailPrefixSuffix: boolean = false;
emailPrefixAttribute: string;
emailSuffix: string;
creationDateAttribute: string;
revisionDateAttribute: string;
}

View File

@@ -0,0 +1,66 @@
import { DirectoryType } from '../enums/directoryType';
import { StorageService } from 'jslib/abstractions/storage.service';
const StoredSecurely = '[STORED SECURELY]';
const Keys = {
ldap: 'ldapPassword',
gsuite: 'gsuitePrivateKey',
azure: 'azureKey',
directoryConfigPrefix: 'directoryConfig_',
};
export class ConfigurationService {
constructor(private storageService: StorageService, private secureStorageService: StorageService) { }
async get<T>(type: DirectoryType): Promise<T> {
const config = await this.storageService.get<T>(Keys.directoryConfigPrefix + type);
if (config == null) {
return config;
}
switch (type) {
case DirectoryType.Ldap:
(config as any).password = await this.secureStorageService.get<string>(Keys.ldap);
break;
case DirectoryType.AzureActiveDirectory:
(config as any).key = await this.secureStorageService.get<string>(Keys.azure);
break;
case DirectoryType.GSuite:
(config as any).privateKey = await this.secureStorageService.get<string>(Keys.gsuite);
break;
}
return config;
}
async save<T>(type: DirectoryType, config: T): Promise<any> {
const savedConfig: any = Object.assign({}, config);
switch (type) {
case DirectoryType.Ldap:
if (savedConfig.password == null) {
await this.secureStorageService.remove(Keys.ldap);
} else {
await this.secureStorageService.save(Keys.ldap, savedConfig.password);
savedConfig.password = StoredSecurely;
}
break;
case DirectoryType.AzureActiveDirectory:
if (savedConfig.key == null) {
await this.secureStorageService.remove(Keys.azure);
} else {
await this.secureStorageService.save(Keys.azure, savedConfig.key);
savedConfig.key = StoredSecurely;
}
break;
case DirectoryType.GSuite:
if (savedConfig.privateKey == null) {
await this.secureStorageService.remove(Keys.gsuite);
} else {
await this.secureStorageService.save(Keys.gsuite, savedConfig.privateKey);
savedConfig.privateKey = StoredSecurely;
}
break;
}
await this.storageService.save(Keys.directoryConfigPrefix + type, savedConfig);
}
}