1
0
mirror of https://github.com/bitwarden/directory-connector synced 2025-12-17 00:33:35 +00:00

Compare commits

..

34 Commits

Author SHA1 Message Date
Kyle Spearrin
7cb2147569 update jslib 2018-10-16 08:57:14 -04:00
Kyle Spearrin
327de4d714 update jslib 2018-10-13 23:29:54 -04:00
Kyle Spearrin
9b57955d72 update jslib 2018-10-11 21:27:04 -04:00
Kyle Spearrin
e63198e130 update electron 2018-10-09 17:56:23 -04:00
Kyle Spearrin
03eac9006f update jslib 2018-10-09 15:32:23 -04:00
Kyle Spearrin
d3402cca5e update jslib 2018-10-05 13:57:36 -04:00
Kyle Spearrin
076b320e70 dont await void methods 2018-10-04 12:05:47 -04:00
Kyle Spearrin
44180f95a5 convert toaster and analytics to platform utils 2018-10-03 10:15:00 -04:00
Kyle Spearrin
e23ebf13b7 update jslib 2018-10-02 09:22:53 -04:00
Kyle Spearrin
e0fd1a2e93 fix null ref on trim, resolves #8 2018-09-20 08:17:21 -04:00
Kyle Spearrin
c39986b37c import webfonts 2018-09-18 15:49:48 -04:00
Kyle Spearrin
1e0029dc15 bump version 2018-09-18 11:52:32 -04:00
Kyle Spearrin
63bc5e4161 ssl options for ldap 2018-09-18 11:31:11 -04:00
Kyle Spearrin
8c8b4da595 no minimize 2018-09-14 15:48:50 -04:00
Kyle Spearrin
1a7536270a bump version 2018-09-14 12:19:16 -04:00
Kyle Spearrin
d92e3c8d7b preserveWhitespaces 2018-09-13 12:04:13 -04:00
Kyle Spearrin
c30aedf491 update jslib 2018-09-12 13:22:54 -04:00
Kyle Spearrin
0e96a462ee update libs and move to webpack 4 2018-09-12 10:17:06 -04:00
Kyle Spearrin
3ef60cb3a0 update jslib 2018-09-11 08:45:46 -04:00
Kyle Spearrin
34b7638e11 trim email also 2018-09-08 08:13:40 -04:00
Kyle Spearrin
63ef932469 dont use group deltas since they do not update on membership changes 2018-09-07 08:41:55 -04:00
Kyle Spearrin
e09163fc72 update lunr 2018-09-03 21:51:37 -04:00
Kyle Spearrin
6a737f6d7d Fix !== null checks 2018-08-30 21:48:17 -04:00
Kyle Spearrin
ccc7b7b213 update jslib 2018-08-29 09:22:37 -04:00
Kyle Spearrin
1e98bc6430 fall back to userPrincipalName in other conditions 2018-08-29 08:49:59 -04:00
Kyle Spearrin
c63a945057 update jslib 2018-08-28 23:17:53 -04:00
Kyle Spearrin
a13d9fa00f add csp 2018-08-28 12:15:12 -04:00
Kyle Spearrin
1518d5eafc update jslib 2018-08-27 23:05:39 -04:00
Kyle Spearrin
c74800fd8c update bootstrap 2018-08-21 15:28:12 -04:00
Kyle Spearrin
9e282efe28 npm audit fix 2018-08-21 15:18:55 -04:00
Kyle Spearrin
26efc9158d ignore notifications service 2018-08-20 22:22:48 -04:00
Kyle Spearrin
e2d3d35d71 update jslib 2018-08-20 17:08:27 -04:00
Kyle Spearrin
c115bf84f0 setUrlsFromStorage on init factory 2018-08-20 17:08:07 -04:00
Kyle Spearrin
5b56d32492 update electron 2018-08-18 21:44:10 -04:00
30 changed files with 2440 additions and 2018 deletions

View File

@@ -18,8 +18,14 @@ function webfonts() {
format: 'woff', format: 'woff',
})) }))
.pipe(gulp.dest(paths.cssDir)); .pipe(gulp.dest(paths.cssDir));
}; }
// ref: https://github.com/angular/angular/issues/22524
function cleanupAotIssue() {
return del(['./node_modules/@types/uglify-js/node_modules/source-map/source-map.d.ts']);
}
gulp.task('clean', clean); gulp.task('clean', clean);
gulp.task('cleanupAotIssue', cleanupAotIssue);
gulp.task('webfonts', ['clean'], webfonts); gulp.task('webfonts', ['clean'], webfonts);
gulp.task('prebuild:renderer', ['webfonts']); gulp.task('prebuild:renderer', ['webfonts', 'cleanupAotIssue']);

2
jslib

Submodule jslib updated: 9ba3c17626...2f6426deb4

3890
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -107,71 +107,73 @@
} }
}, },
"devDependencies": { "devDependencies": {
"@angular/compiler-cli": "5.2.0", "@angular/compiler-cli": "^6.1.7",
"@microsoft/microsoft-graph-types": "^1.2.0", "@microsoft/microsoft-graph-types": "^1.4.0",
"@ngtools/webpack": "1.10.2", "@ngtools/webpack": "^6.2.1",
"@types/ldapjs": "^1.0.3", "@types/ldapjs": "^1.0.3",
"@types/lowdb": "^1.0.1", "@types/lowdb": "^1.0.5",
"@types/lunr": "2.1.5", "@types/lunr": "^2.1.6",
"@types/node": "8.0.19", "@types/node-forge": "^0.7.5",
"@types/node-forge": "0.7.1", "@types/papaparse": "^4.5.3",
"@types/papaparse": "4.1.31", "@types/semver": "^5.5.0",
"@types/semver": "5.5.0", "@types/source-map": "0.5.2",
"@types/webcrypto": "0.0.28", "@types/webcrypto": "^0.0.28",
"clean-webpack-plugin": "^0.1.17", "@types/webpack": "^4.4.11",
"concurrently": "3.5.1", "clean-webpack-plugin": "^0.1.19",
"copy-webpack-plugin": "^4.2.0", "concurrently": "^4.0.1",
"css-loader": "^0.28.7", "copy-webpack-plugin": "^4.5.2",
"css-loader": "^1.0.0",
"del": "^3.0.0", "del": "^3.0.0",
"electron": "2.0.5", "electron": "2.0.11",
"electron-builder": "^20.25.0", "electron-builder": "^20.25.0",
"electron-rebuild": "1.8.1", "electron-rebuild": "1.8.1",
"electron-reload": "1.2.5", "electron-reload": "1.2.5",
"extract-text-webpack-plugin": "^3.0.1", "extract-text-webpack-plugin": "next",
"file-loader": "^1.1.5", "file-loader": "^2.0.0",
"font-awesome": "4.7.0", "font-awesome": "4.7.0",
"gulp": "^3.9.1", "gulp": "^3.9.1",
"gulp-google-webfonts": "^2.0.0", "gulp-google-webfonts": "^2.0.0",
"html-loader": "^0.5.1", "html-loader": "^0.5.5",
"html-webpack-plugin": "^2.30.1", "html-webpack-plugin": "^3.2.0",
"node-loader": "^0.6.0", "node-loader": "^0.6.0",
"node-sass": "^4.9.2", "node-sass": "^4.9.3",
"rimraf": "^2.6.2", "rimraf": "^2.6.2",
"sass-loader": "^6.0.6", "sass-loader": "^7.1.0",
"ts-loader": "^3.5.0", "ts-loader": "^5.1.0",
"tslint": "^5.9.1", "tslint": "^5.11.0",
"tslint-loader": "^3.5.3", "tslint-loader": "^3.6.0",
"typescript": "^2.7.1", "typescript": "^2.7.2",
"webpack": "^3.10.0", "webpack": "^4.18.0",
"webpack-merge": "^4.1.0", "webpack-cli": "^3.1.0",
"webpack-node-externals": "^1.6.0" "webpack-merge": "^4.1.4",
"webpack-node-externals": "^1.7.2"
}, },
"dependencies": { "dependencies": {
"@angular/animations": "5.2.0", "@angular/animations": "6.1.7",
"@angular/common": "5.2.0", "@angular/common": "6.1.7",
"@angular/compiler": "5.2.0", "@angular/compiler": "6.1.7",
"@angular/core": "5.2.0", "@angular/core": "6.1.7",
"@angular/forms": "5.2.0", "@angular/forms": "6.1.7",
"@angular/http": "5.2.0", "@angular/http": "6.1.7",
"@angular/platform-browser": "5.2.0", "@angular/platform-browser": "6.1.7",
"@angular/platform-browser-dynamic": "5.2.0", "@angular/platform-browser-dynamic": "6.1.7",
"@angular/router": "5.2.0", "@angular/router": "6.1.7",
"@angular/upgrade": "5.2.0", "@angular/upgrade": "6.1.7",
"@microsoft/microsoft-graph-client": "1.0.0", "@microsoft/microsoft-graph-client": "1.2.0",
"@okta/okta-sdk-nodejs": "1.1.0", "@okta/okta-sdk-nodejs": "1.2.0",
"angular2-toaster": "4.0.2", "angular2-toaster": "6.1.0",
"angulartics2": "5.0.1", "angulartics2": "6.3.0",
"bootstrap": "4.1.0", "bootstrap": "4.1.3",
"core-js": "2.4.1", "core-js": "2.5.7",
"electron-log": "2.2.14", "electron-log": "2.2.14",
"electron-updater": "3.0.3", "electron-updater": "3.0.3",
"googleapis": "29.0.0", "googleapis": "33.0.0",
"keytar": "4.2.1", "keytar": "4.2.1",
"ldapjs": "1.0.2", "ldapjs": "1.0.2",
"lowdb": "1.0.0", "lowdb": "1.0.0",
"lunr": "2.1.6", "lunr": "2.3.3",
"node-forge": "0.7.1", "node-forge": "0.7.6",
"rxjs": "5.5.6", "rxjs": "6.3.2",
"zone.js": "0.8.19" "zone.js": "0.8.26"
} }
} }

View File

@@ -1,10 +1,8 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { EnvironmentService } from 'jslib/abstractions/environment.service'; import { EnvironmentService } from 'jslib/abstractions/environment.service';
import { I18nService } from 'jslib/abstractions/i18n.service'; import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { EnvironmentComponent as BaseEnvironmentComponent } from 'jslib/angular/components/environment.component'; import { EnvironmentComponent as BaseEnvironmentComponent } from 'jslib/angular/components/environment.component';
@@ -13,8 +11,8 @@ import { EnvironmentComponent as BaseEnvironmentComponent } from 'jslib/angular/
templateUrl: 'environment.component.html', templateUrl: 'environment.component.html',
}) })
export class EnvironmentComponent extends BaseEnvironmentComponent { export class EnvironmentComponent extends BaseEnvironmentComponent {
constructor(analytics: Angulartics2, toasterService: ToasterService, constructor(environmentService: EnvironmentService, i18nService: I18nService,
environmentService: EnvironmentService, i18nService: I18nService) { platformUtilsService: PlatformUtilsService) {
super(analytics, toasterService, environmentService, i18nService); super(platformUtilsService, environmentService, i18nService);
} }
} }

View File

@@ -6,13 +6,11 @@ import {
} from '@angular/core'; } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { EnvironmentComponent } from './environment.component'; import { EnvironmentComponent } from './environment.component';
import { AuthService } from 'jslib/abstractions/auth.service'; import { AuthService } from 'jslib/abstractions/auth.service';
import { I18nService } from 'jslib/abstractions/i18n.service'; import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { StorageService } from 'jslib/abstractions/storage.service'; import { StorageService } from 'jslib/abstractions/storage.service';
import { LoginComponent as BaseLoginComponent } from 'jslib/angular/components/login.component'; import { LoginComponent as BaseLoginComponent } from 'jslib/angular/components/login.component';
@@ -26,10 +24,9 @@ export class LoginComponent extends BaseLoginComponent {
@ViewChild('environment', { read: ViewContainerRef }) environmentModal: ViewContainerRef; @ViewChild('environment', { read: ViewContainerRef }) environmentModal: ViewContainerRef;
constructor(authService: AuthService, router: Router, constructor(authService: AuthService, router: Router,
analytics: Angulartics2, toasterService: ToasterService,
i18nService: I18nService, private componentFactoryResolver: ComponentFactoryResolver, i18nService: I18nService, private componentFactoryResolver: ComponentFactoryResolver,
storageService: StorageService) { storageService: StorageService, platformUtilsService: PlatformUtilsService) {
super(authService, router, analytics, toasterService, i18nService, storageService); super(authService, router, platformUtilsService, i18nService, storageService);
super.successRoute = '/tabs/dashboard'; super.successRoute = '/tabs/dashboard';
} }

View File

@@ -1,9 +1,6 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { AuthService } from 'jslib/abstractions/auth.service'; import { AuthService } from 'jslib/abstractions/auth.service';
import { I18nService } from 'jslib/abstractions/i18n.service'; import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
@@ -18,8 +15,7 @@ import {
}) })
export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent { export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent {
constructor(authService: AuthService, router: Router, constructor(authService: AuthService, router: Router,
analytics: Angulartics2, toasterService: ToasterService,
i18nService: I18nService, platformUtilsService: PlatformUtilsService) { i18nService: I18nService, platformUtilsService: PlatformUtilsService) {
super(authService, router, analytics, toasterService, i18nService, platformUtilsService, window); super(authService, router, i18nService, platformUtilsService, window);
} }
} }

View File

@@ -31,7 +31,7 @@
<iframe id="duo_iframe"></iframe> <iframe id="duo_iframe"></iframe>
</div> </div>
</ng-container> </ng-container>
<div class="form-group" *ngIf="selectedProviderType !== null && selectedProviderType !== providerType.U2f"> <div class="form-group" *ngIf="selectedProviderType != null && selectedProviderType !== providerType.U2f">
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" id="remember" [(ngModel)]="remember" name="Remember"> <input class="form-check-input" type="checkbox" id="remember" [(ngModel)]="remember" name="Remember">
<label class="form-check-label" for="remember">{{'rememberMe' | i18n}}</label> <label class="form-check-label" for="remember">{{'rememberMe' | i18n}}</label>

View File

@@ -7,9 +7,6 @@ import {
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { TwoFactorOptionsComponent } from './two-factor-options.component'; import { TwoFactorOptionsComponent } from './two-factor-options.component';
import { TwoFactorProviderType } from 'jslib/enums/twoFactorProviderType'; import { TwoFactorProviderType } from 'jslib/enums/twoFactorProviderType';
@@ -31,12 +28,10 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
@ViewChild('twoFactorOptions', { read: ViewContainerRef }) twoFactorOptionsModal: ViewContainerRef; @ViewChild('twoFactorOptions', { read: ViewContainerRef }) twoFactorOptionsModal: ViewContainerRef;
constructor(authService: AuthService, router: Router, constructor(authService: AuthService, router: Router,
analytics: Angulartics2, toasterService: ToasterService,
i18nService: I18nService, apiService: ApiService, i18nService: I18nService, apiService: ApiService,
platformUtilsService: PlatformUtilsService, environmentService: EnvironmentService, platformUtilsService: PlatformUtilsService, environmentService: EnvironmentService,
private componentFactoryResolver: ComponentFactoryResolver) { private componentFactoryResolver: ComponentFactoryResolver) {
super(authService, router, analytics, toasterService, i18nService, apiService, super(authService, router, i18nService, apiService, platformUtilsService, window, environmentService);
platformUtilsService, window, environmentService);
super.successRoute = '/tabs/dashboard'; super.successRoute = '/tabs/dashboard';
} }

View File

@@ -1,24 +1,26 @@
import { import {
BodyOutputType,
Toast,
ToasterConfig, ToasterConfig,
ToasterContainerComponent, ToasterContainerComponent,
ToasterService,
} from 'angular2-toaster'; } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
import { import {
Component, Component,
ComponentFactoryResolver, ComponentFactoryResolver,
NgZone, NgZone,
OnDestroy,
OnInit, OnInit,
SecurityContext,
Type, Type,
ViewChild, ViewChild,
ViewContainerRef, ViewContainerRef,
} from '@angular/core'; } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { ModalComponent } from 'jslib/angular/components/modal.component'; import { ModalComponent } from 'jslib/angular/components/modal.component';
import { BroadcasterService } from 'jslib/angular/services/broadcaster.service'; import { BroadcasterService } from 'jslib/angular/services/broadcaster.service';
@@ -27,14 +29,11 @@ import { ApiService } from 'jslib/abstractions/api.service';
import { AuthService } from 'jslib/abstractions/auth.service'; import { AuthService } from 'jslib/abstractions/auth.service';
import { I18nService } from 'jslib/abstractions/i18n.service'; import { I18nService } from 'jslib/abstractions/i18n.service';
import { MessagingService } from 'jslib/abstractions/messaging.service'; import { MessagingService } from 'jslib/abstractions/messaging.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { StateService } from 'jslib/abstractions/state.service'; import { StateService } from 'jslib/abstractions/state.service';
import { StorageService } from 'jslib/abstractions/storage.service'; import { StorageService } from 'jslib/abstractions/storage.service';
import { TokenService } from 'jslib/abstractions/token.service'; import { TokenService } from 'jslib/abstractions/token.service';
import { UserService } from 'jslib/abstractions/user.service'; import { UserService } from 'jslib/abstractions/user.service';
import { ConstantsService } from 'jslib/services/constants.service';
import { ConfigurationService } from '../services/configuration.service'; import { ConfigurationService } from '../services/configuration.service';
import { SyncService } from '../services/sync.service'; import { SyncService } from '../services/sync.service';
@@ -66,12 +65,12 @@ export class AppComponent implements OnInit {
private tokenService: TokenService, private storageService: StorageService, private tokenService: TokenService, private storageService: StorageService,
private authService: AuthService, private router: Router, private analytics: Angulartics2, private authService: AuthService, private router: Router, private analytics: Angulartics2,
private toasterService: ToasterService, private i18nService: I18nService, private toasterService: ToasterService, private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService, private ngZone: NgZone, private sanitizer: DomSanitizer, private ngZone: NgZone,
private componentFactoryResolver: ComponentFactoryResolver, private messagingService: MessagingService, private componentFactoryResolver: ComponentFactoryResolver, private messagingService: MessagingService,
private configurationService: ConfigurationService, private syncService: SyncService, private configurationService: ConfigurationService, private syncService: SyncService,
private stateService: StateService, private apiService: ApiService) { private stateService: StateService, private apiService: ApiService) {
(window as any).BitwardenToasterService = toasterService; (window as any).BitwardenToasterService = toasterService;
} }
ngOnInit() { ngOnInit() {
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => { this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
@@ -125,6 +124,15 @@ export class AppComponent implements OnInit {
this.messagingService.send('scheduleNextDirSync'); this.messagingService.send('scheduleNextDirSync');
break; break;
case 'showToast':
this.showToast(message);
break;
case 'analyticsEventTrack':
this.analytics.eventTrack.next({
action: message.action,
properties: { label: message.label },
});
break;
default: default:
} }
}); });
@@ -166,4 +174,31 @@ export class AppComponent implements OnInit {
this.modal = null; this.modal = null;
}); });
} }
private showToast(msg: any) {
const toast: Toast = {
type: msg.type,
title: msg.title,
};
if (typeof (msg.text) === 'string') {
toast.body = msg.text;
} else if (msg.text.length === 1) {
toast.body = msg.text[0];
} else {
let message = '';
msg.text.forEach((t: string) =>
message += ('<p>' + this.sanitizer.sanitize(SecurityContext.HTML, t) + '</p>'));
toast.body = message;
toast.bodyOutputType = BodyOutputType.TrustedHtml;
}
if (msg.options != null) {
if (msg.options.trustedHtml === true) {
toast.bodyOutputType = BodyOutputType.TrustedHtml;
}
if (msg.options.timeout != null && msg.options.timeout > 0) {
toast.timeout = msg.options.timeout;
}
}
this.toasterService.popAsync(toast);
}
} }

View File

@@ -50,7 +50,7 @@ import { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe';
clearQueryParams: true, clearQueryParams: true,
}, },
}), }),
ToasterModule, ToasterModule.forRoot(),
], ],
declarations: [ declarations: [
ApiActionDirective, ApiActionDirective,

View File

@@ -12,4 +12,4 @@ if (!isDev()) {
enableProdMode(); enableProdMode();
} }
platformBrowserDynamic().bootstrapModule(AppModule); platformBrowserDynamic().bootstrapModule(AppModule, { preserveWhitespaces: true });

View File

@@ -11,7 +11,6 @@ import { ElectronLogService } from 'jslib/electron/services/electronLog.service'
import { ElectronPlatformUtilsService } from 'jslib/electron/services/electronPlatformUtils.service'; import { ElectronPlatformUtilsService } from 'jslib/electron/services/electronPlatformUtils.service';
import { ElectronRendererMessagingService } from 'jslib/electron/services/electronRendererMessaging.service'; import { ElectronRendererMessagingService } from 'jslib/electron/services/electronRendererMessaging.service';
import { ElectronRendererSecureStorageService } from 'jslib/electron/services/electronRendererSecureStorage.service'; import { ElectronRendererSecureStorageService } from 'jslib/electron/services/electronRendererSecureStorage.service';
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';
@@ -56,9 +55,9 @@ import { UserService as UserServiceAbstraction } from 'jslib/abstractions/user.s
const logService = new ElectronLogService(); const logService = new ElectronLogService();
const i18nService = new I18nService(window.navigator.language, './locales'); const i18nService = new I18nService(window.navigator.language, './locales');
const stateService = new StateService(); const stateService = new StateService();
const platformUtilsService = new ElectronPlatformUtilsService(i18nService, true);
const broadcasterService = new BroadcasterService(); const broadcasterService = new BroadcasterService();
const messagingService = new ElectronRendererMessagingService(broadcasterService); const messagingService = new ElectronRendererMessagingService(broadcasterService);
const platformUtilsService = new ElectronPlatformUtilsService(i18nService, messagingService, true);
const storageService: StorageServiceAbstraction = new LowdbStorageService(null, remote.app.getPath('userData')); const storageService: StorageServiceAbstraction = new LowdbStorageService(null, remote.app.getPath('userData'));
const secureStorageService: StorageServiceAbstraction = new ElectronRendererSecureStorageService(); const secureStorageService: StorageServiceAbstraction = new ElectronRendererSecureStorageService();
const cryptoFunctionService: CryptoFunctionServiceAbstraction = new NodeCryptoFunctionService(); const cryptoFunctionService: CryptoFunctionServiceAbstraction = new NodeCryptoFunctionService();
@@ -67,9 +66,9 @@ const appIdService = new AppIdService(storageService);
const tokenService = new TokenService(storageService); const tokenService = new TokenService(storageService);
const apiService = new ApiService(tokenService, platformUtilsService, const apiService = new ApiService(tokenService, platformUtilsService,
async (expired: boolean) => messagingService.send('logout', { expired: expired })); async (expired: boolean) => messagingService.send('logout', { expired: expired }));
const environmentService = new EnvironmentService(apiService, storageService); const environmentService = new EnvironmentService(apiService, storageService, null);
const userService = new UserService(tokenService, storageService); const userService = new UserService(tokenService, storageService);
const containerService = new ContainerService(cryptoService, platformUtilsService); const containerService = new ContainerService(cryptoService);
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);
@@ -78,14 +77,12 @@ const syncService = new SyncService(configurationService, logService, cryptoFunc
const analytics = new Analytics(window, () => true, platformUtilsService, storageService, appIdService); const analytics = new Analytics(window, () => true, platformUtilsService, storageService, appIdService);
containerService.attachToWindow(window); containerService.attachToWindow(window);
environmentService.setUrlsFromStorage().then(() => {
// Do nothing
});
export function initFactory(): Function { export function initFactory(): Function {
return async () => { return async () => {
await environmentService.setUrlsFromStorage();
await i18nService.init(); await i18nService.init();
await authService.init(); authService.init();
const htmlEl = window.document.documentElement; const htmlEl = window.document.documentElement;
htmlEl.classList.add('os_' + platformUtilsService.getDeviceString()); htmlEl.classList.add('os_' + platformUtilsService.getDeviceString());
htmlEl.classList.add('locale_' + i18nService.translationLocale); htmlEl.classList.add('locale_' + i18nService.translationLocale);

View File

@@ -27,14 +27,38 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" id="ssl" [(ngModel)]="ldap.ssl" name="SSL"> <input class="form-check-input" type="checkbox" id="ad" [(ngModel)]="ldap.ad" name="AD">
<label class="form-check-label" for="ssl">{{'ldapSsl' | i18n}}</label> <label class="form-check-label" for="ad">{{'ldapAd' | i18n}}</label>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" id="ad" [(ngModel)]="ldap.ad" name="AD"> <input class="form-check-input" type="checkbox" id="ssl" [(ngModel)]="ldap.ssl" name="SSL">
<label class="form-check-label" for="ad">{{'ldapAd' | i18n}}</label> <label class="form-check-label" for="ssl">{{'ldapSsl' | i18n}}</label>
</div>
</div>
<div class="ml-4" *ngIf="ldap.ssl">
<p>{{'ldapSslUntrustedDesc' | i18n}}</p>
<div class="form-group">
<label for="sslCertPath">{{'ldapSslCert' | i18n}}</label>
<input type="file" class="form-control-file mb-2" id="sslCertPath_file" (change)="setSslPath('sslCertPath')">
<input type="text" class="form-control" id="sslCertPath" name="SSLCertPath" [(ngModel)]="ldap.sslCertPath">
</div>
<div class="form-group">
<label for="sslKeyPath">{{'ldapSslKey' | i18n}}</label>
<input type="file" class="form-control-file mb-2" id="sslKeyPath_file" (change)="setSslPath('sslKeyPath')">
<input type="text" class="form-control" id="sslKeyPath" name="SSLKeyPath" [(ngModel)]="ldap.sslKeyPath">
</div>
<div class="form-group">
<label for="sslCaPath">{{'ldapSslCa' | i18n}}</label>
<input type="file" class="form-control-file mb-2" id="sslCaPath_file" (change)="setSslPath('sslCaPath')">
<input type="text" class="form-control" id="sslCaPath" name="SSLCaPath" [(ngModel)]="ldap.sslCaPath">
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="sslAllowUnauthorized" [(ngModel)]="ldap.sslAllowUnauthorized" name="SSLAllowUnauthorized">
<label class="form-check-label" for="sslAllowUnauthorized">{{'ldapSslAllowUnauthorized' | i18n}}</label>
</div>
</div> </div>
</div> </div>
<div class="form-group" [hidden]="true"> <div class="form-group" [hidden]="true">

View File

@@ -111,7 +111,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
await this.configurationService.saveSync(this.sync); await this.configurationService.saveSync(this.sync);
} }
async parseKeyFile() { parseKeyFile() {
const filePicker = (document.getElementById('keyFile') as HTMLInputElement); const filePicker = (document.getElementById('keyFile') as HTMLInputElement);
if (filePicker.files == null || filePicker.files.length < 0) { if (filePicker.files == null || filePicker.files.length < 0) {
return; return;
@@ -122,7 +122,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
reader.onload = (evt) => { reader.onload = (evt) => {
this.ngZone.run(async () => { this.ngZone.run(async () => {
try { try {
const result = JSON.parse((evt.target as FileReader).result); const result = JSON.parse((evt.target as FileReader).result as string);
if (result.client_email != null && result.private_key != null) { if (result.client_email != null && result.private_key != null) {
this.gsuite.clientEmail = result.client_email; this.gsuite.clientEmail = result.client_email;
this.gsuite.privateKey = result.private_key; this.gsuite.privateKey = result.private_key;
@@ -138,4 +138,18 @@ export class SettingsComponent implements OnInit, OnDestroy {
filePicker.value = ''; filePicker.value = '';
}; };
} }
setSslPath(id: string) {
const filePicker = (document.getElementById(id + '_file') as HTMLInputElement);
if (filePicker.files == null || filePicker.files.length < 0) {
return;
}
(this.ldap as any)[id] = filePicker.files[0].path;
// reset file input
// ref: https://stackoverflow.com/a/20552042
filePicker.type = '';
filePicker.type = 'file';
filePicker.value = '';
}
} }

View File

@@ -2,6 +2,8 @@
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; style-src 'self' 'unsafe-inline';
img-src 'self' data: *; child-src *; frame-src *; connect-src *;">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bitwarden Directory Connector</title> <title>Bitwarden Directory Connector</title>
<base href=""> <base href="">

View File

@@ -417,6 +417,21 @@
"ldapSsl": { "ldapSsl": {
"message": "This server uses SSL (LDAPS)" "message": "This server uses SSL (LDAPS)"
}, },
"ldapSslUntrustedDesc": {
"message": "If your LDAPS server uses an untrusted certificate you can configure certificate options below."
},
"ldapSslCa": {
"message": "Certificate CA Chain (PEM)"
},
"ldapSslCert": {
"message": "Certificate (PEM)"
},
"ldapSslKey": {
"message": "Certificate Private Key (PEM)"
},
"ldapSslAllowUnauthorized": {
"message": "Allow untrusted SSL connections (not recommended)."
},
"ldapAd": { "ldapAd": {
"message": "This server uses Active Directory" "message": "This server uses Active Directory"
}, },

View File

@@ -1,4 +1,4 @@
import { app, BrowserWindow } from 'electron'; import { app } from 'electron';
import * as path from 'path'; import * as path from 'path';
import { MenuMain } from './main/menu.main'; import { MenuMain } from './main/menu.main';

View File

@@ -1,7 +1,9 @@
import { DirectoryType } from '../enums/directoryType';
export class LdapConfiguration { export class LdapConfiguration {
ssl = false; ssl = false;
sslAllowUnauthorized = false;
sslCertPath: string;
sslKeyPath: string;
sslCaPath: string;
hostname: string; hostname: string;
port = 389; port = 389;
domain: string; domain: string;

View File

@@ -2,7 +2,7 @@
"name": "bitwarden-directory-connector", "name": "bitwarden-directory-connector",
"productName": "Bitwarden Directory Connector", "productName": "Bitwarden Directory Connector",
"description": "Sync your user directory to your Bitwarden organization.", "description": "Sync your user directory to your Bitwarden organization.",
"version": "2.2.0", "version": "2.4.0",
"author": "8bit Solutions LLC <hello@bitwarden.com> (https://bitwarden.com)", "author": "8bit Solutions LLC <hello@bitwarden.com> (https://bitwarden.com)",
"homepage": "https://bitwarden.com", "homepage": "https://bitwarden.com",
"license": "GPL-3.0", "license": "GPL-3.0",

View File

@@ -1,4 +1,5 @@
@import "bootstrap.scss"; @import "../css/webfonts.css";
@import "bootstrap.scss";
@import "pages.scss"; @import "pages.scss";
@import "misc.scss"; @import "misc.scss";
@import "plugins.scss"; @import "plugins.scss";

View File

@@ -59,7 +59,7 @@ export class AzureDirectoryService extends BaseDirectoryService implements Direc
let groups: GroupEntry[]; let groups: GroupEntry[];
if (this.syncConfig.groups) { if (this.syncConfig.groups) {
const setFilter = this.createCustomSet(this.syncConfig.groupFilter); const setFilter = this.createCustomSet(this.syncConfig.groupFilter);
groups = await this.getGroups(this.forceGroup(force, users), !test, setFilter); groups = await this.getGroups(setFilter);
users = this.filterUsersFromGroupsSet(users, groups, setFilter); users = this.filterUsersFromGroupsSet(users, groups, setFilter);
} }
@@ -127,10 +127,15 @@ export class AzureDirectoryService extends BaseDirectoryService implements Direc
const entry = new UserEntry(); const entry = new UserEntry();
entry.referenceId = user.id; entry.referenceId = user.id;
entry.externalId = user.id; entry.externalId = user.id;
entry.email = user.mail || user.userPrincipalName; entry.email = user.mail;
if (user.userPrincipalName && (entry.email == null || entry.email === '' ||
entry.email.indexOf('onmicrosoft.com') > -1)) {
entry.email = user.userPrincipalName;
}
if (entry.email != null) { if (entry.email != null) {
entry.email = entry.email.toLowerCase(); entry.email = entry.email.trim().toLowerCase();
} }
entry.disabled = user.accountEnabled == null ? false : !user.accountEnabled; entry.disabled = user.accountEnabled == null ? false : !user.accountEnabled;
@@ -142,76 +147,15 @@ export class AzureDirectoryService extends BaseDirectoryService implements Direc
return entry; return entry;
} }
private async getGroups(force: boolean, saveDelta: boolean, private async getGroups(setFilter: [boolean, Set<string>]): Promise<GroupEntry[]> {
setFilter: [boolean, Set<string>]): Promise<GroupEntry[]> {
const entryIds = new Set<string>(); const entryIds = new Set<string>();
const entries: GroupEntry[] = []; const entries: GroupEntry[] = [];
const changedGroupIds: string[] = []; const groupsReq = this.client.api('/groups');
const token = await this.configurationService.getGroupDeltaToken(); let res = await groupsReq.get();
const getFullResults = token == null || force;
let res: any = null;
let errored = false;
try {
if (!getFullResults) {
try {
const deltaReq = this.client.api(token);
res = await deltaReq.get();
} catch {
res = null;
}
}
if (res == null) {
const groupReq = this.client.api('/groups/delta');
res = await groupReq.get();
}
while (true) {
const groups: graphType.Group[] = res.value;
if (groups != null) {
for (const group of groups) {
if (getFullResults) {
if (group.id == null || entryIds.has(group.id)) {
continue;
}
if (this.filterOutResult(setFilter, group.displayName)) {
continue;
}
const entry = await this.buildGroup(group);
entries.push(entry);
entryIds.add(group.id);
} else {
changedGroupIds.push(group.id);
}
}
}
if (res[NextLink] == null) {
if (res[DeltaLink] != null && saveDelta) {
await this.configurationService.saveGroupDeltaToken(res[DeltaLink]);
}
break;
} else {
const nextReq = this.client.api(res[NextLink]);
res = await nextReq.get();
}
}
} catch {
errored = true;
}
if (!errored && (getFullResults || changedGroupIds.length === 0)) {
return entries;
}
const allGroupsReq = this.client.api('/groups');
res = await allGroupsReq.get();
while (true) { while (true) {
const allGroups: graphType.Group[] = res.value; const groups: graphType.Group[] = res.value;
if (allGroups != null) { if (groups != null) {
for (const group of allGroups) { for (const group of groups) {
if (group.id == null || entryIds.has(group.id)) { if (group.id == null || entryIds.has(group.id)) {
continue; continue;
} }

View File

@@ -51,13 +51,13 @@ export abstract class BaseDirectoryService {
protected filterOutResult(setFilter: [boolean, Set<string>], result: string) { protected filterOutResult(setFilter: [boolean, Set<string>], result: string) {
if (setFilter != null) { if (setFilter != null) {
result = result.trim().toLowerCase(); const cleanResult = result != null ? result.trim().toLowerCase() : '--';
const excluded = setFilter[0]; const excluded = setFilter[0];
const set = setFilter[1]; const set = setFilter[1];
if (excluded && set.has(result)) { if (excluded && set.has(cleanResult)) {
return true; return true;
} else if (!excluded && !set.has(result)) { } else if (!excluded && !set.has(cleanResult)) {
return true; return true;
} }
} }

View File

@@ -1,13 +1,8 @@
import { JWT } from 'google-auth-library'; import { JWT } from 'google-auth-library';
import { import {
admin_directory_v1,
google, google,
GoogleApis,
} from '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';
@@ -25,7 +20,7 @@ import { LogService } from 'jslib/abstractions/log.service';
export class GSuiteDirectoryService extends BaseDirectoryService implements DirectoryService { export class GSuiteDirectoryService extends BaseDirectoryService implements DirectoryService {
private client: JWT; private client: JWT;
private service: Admin; private service: admin_directory_v1.Admin;
private authParams: any; private authParams: any;
private dirConfig: GSuiteConfiguration; private dirConfig: GSuiteConfiguration;
private syncConfig: SyncConfiguration; private syncConfig: SyncConfiguration;
@@ -33,7 +28,7 @@ export class GSuiteDirectoryService extends BaseDirectoryService implements Dire
constructor(private configurationService: ConfigurationService, private logService: LogService, constructor(private configurationService: ConfigurationService, private logService: LogService,
private i18nService: I18nService) { private i18nService: I18nService) {
super(); super();
this.service = google.admin<Admin>('directory_v1'); this.service = google.admin('directory_v1');
} }
async getEntries(force: boolean, test: boolean): Promise<[GroupEntry[], UserEntry[]]> { async getEntries(force: boolean, test: boolean): Promise<[GroupEntry[], UserEntry[]]> {
@@ -117,7 +112,7 @@ export class GSuiteDirectoryService extends BaseDirectoryService implements Dire
return entries; return entries;
} }
private buildUser(user: Schema$User, deleted: boolean) { private buildUser(user: admin_directory_v1.Schema$User, deleted: boolean) {
if ((user.emails == null || user.emails === '') && !deleted) { if ((user.emails == null || user.emails === '') && !deleted) {
return null; return null;
} }
@@ -125,7 +120,7 @@ export class GSuiteDirectoryService extends BaseDirectoryService implements Dire
const entry = new UserEntry(); const entry = new UserEntry();
entry.referenceId = user.id; entry.referenceId = user.id;
entry.externalId = user.id; entry.externalId = user.id;
entry.email = user.primaryEmail != null ? user.primaryEmail.toLowerCase() : null; entry.email = user.primaryEmail != null ? user.primaryEmail.trim().toLowerCase() : null;
entry.disabled = user.suspended || false; entry.disabled = user.suspended || false;
entry.deleted = deleted; entry.deleted = deleted;
return entry; return entry;
@@ -151,7 +146,7 @@ export class GSuiteDirectoryService extends BaseDirectoryService implements Dire
return entries; return entries;
} }
private async buildGroup(group: Schema$Group) { private async buildGroup(group: admin_directory_v1.Schema$Group) {
const entry = new GroupEntry(); const entry = new GroupEntry();
entry.referenceId = group.id; entry.referenceId = group.id;
entry.externalId = group.id; entry.externalId = group.id;

View File

@@ -1,3 +1,4 @@
import * as fs from 'fs';
import * as ldap from 'ldapjs'; import * as ldap from 'ldapjs';
import { DirectoryType } from '../enums/directoryType'; import { DirectoryType } from '../enums/directoryType';
@@ -113,7 +114,7 @@ export class LdapDirectoryService implements DirectoryService {
} }
if (user.email != null) { if (user.email != null) {
user.email = user.email.toLowerCase(); user.email = user.email.trim().toLowerCase();
} }
if (!user.deleted && (user.email == null || user.email.trim() === '')) { if (!user.deleted && (user.email == null || user.email.trim() === '')) {
@@ -326,10 +327,32 @@ export class LdapDirectoryService implements DirectoryService {
const url = 'ldap' + (this.dirConfig.ssl ? 's' : '') + '://' + this.dirConfig.hostname + const url = 'ldap' + (this.dirConfig.ssl ? 's' : '') + '://' + this.dirConfig.hostname +
':' + this.dirConfig.port; ':' + this.dirConfig.port;
const options: ldap.ClientOptions = {
url: url.trim().toLowerCase(),
};
if (this.dirConfig.ssl) {
const tlsOptions: any = {};
if (this.dirConfig.sslAllowUnauthorized != null) {
tlsOptions.rejectUnauthorized = !this.dirConfig.sslAllowUnauthorized;
}
if (this.dirConfig.sslCaPath != null && this.dirConfig.sslCaPath !== '' &&
fs.existsSync(this.dirConfig.sslCaPath)) {
tlsOptions.ca = [fs.readFileSync(this.dirConfig.sslCaPath)];
}
if (this.dirConfig.sslCertPath != null && this.dirConfig.sslCertPath !== '' &&
fs.existsSync(this.dirConfig.sslCertPath)) {
tlsOptions.cert = fs.readFileSync(this.dirConfig.sslCertPath);
}
if (this.dirConfig.sslKeyPath != null && this.dirConfig.sslKeyPath !== '' &&
fs.existsSync(this.dirConfig.sslKeyPath)) {
tlsOptions.key = fs.readFileSync(this.dirConfig.sslKeyPath);
}
if (Object.keys(tlsOptions).length > 0) {
options.tlsOptions = tlsOptions;
}
}
this.client = ldap.createClient({ this.client = ldap.createClient(options);
url: url.toLowerCase(),
});
const user = this.dirConfig.username == null || this.dirConfig.username.trim() === '' ? null : const user = this.dirConfig.username == null || this.dirConfig.username.trim() === '' ? null :
this.dirConfig.username; this.dirConfig.username;

View File

@@ -104,7 +104,7 @@ export class OktaDirectoryService extends BaseDirectoryService implements Direct
const entry = new UserEntry(); const entry = new UserEntry();
entry.externalId = user.id; entry.externalId = user.id;
entry.referenceId = user.id; entry.referenceId = user.id;
entry.email = user.profile.email != null ? user.profile.email.toLowerCase() : null; entry.email = user.profile.email != null ? user.profile.email.trim().toLowerCase() : null;
entry.deleted = user.status === 'DEPROVISIONED'; entry.deleted = user.status === 'DEPROVISIONED';
entry.disabled = user.status === 'SUSPENDED'; entry.disabled = user.status === 'SUSPENDED';
return entry; return entry;

View File

@@ -146,7 +146,7 @@ export class SyncService {
const iu = new ImportDirectoryRequestUser(); const iu = new ImportDirectoryRequestUser();
iu.email = u.email; iu.email = u.email;
if (iu.email != null) { if (iu.email != null) {
iu.email = iu.email.toLowerCase(); iu.email = iu.email.trim().toLowerCase();
} }
iu.externalId = u.externalId; iu.externalId = u.externalId;
iu.deleted = u.deleted || (removeDisabled && u.disabled); iu.deleted = u.deleted || (removeDisabled && u.disabled);

View File

@@ -11,23 +11,26 @@
"types": [], "types": [],
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"tldjs": [
"jslib/src/misc/tldjs.noop"
],
"jslib/*": [ "jslib/*": [
"jslib/src/*" "jslib/src/*"
], ],
"@angular/*": [ "@angular/*": [
"node_modules/@angular/*" "node_modules/@angular/*"
], ],
"angular2-toaster": [
"node_modules/angular2-toaster"
],
"angulartics2": [
"node_modules/angulartics2"
],
"electron": [ "electron": [
"node_modules/electron" "node_modules/electron"
],
"node": [
"node_modules/@types/node"
] ]
} }
}, },
"angularCompilerOptions": {
"preserveWhitespaces": true
},
"exclude": [ "exclude": [
"node_modules", "node_modules",
"jslib/node_modules", "jslib/node_modules",
@@ -35,6 +38,7 @@
"jslib/src/services/search.service.ts", "jslib/src/services/search.service.ts",
"jslib/src/services/nodeApi.service.ts", "jslib/src/services/nodeApi.service.ts",
"jslib/src/services/export.service.ts", "jslib/src/services/export.service.ts",
"jslib/src/services/notifications.service.ts",
"jslib/src/angular/components/export.component.ts", "jslib/src/angular/components/export.component.ts",
"jslib/src/importers", "jslib/src/importers",
"dist", "dist",

View File

@@ -1,5 +1,4 @@
const path = require('path'); const path = require('path');
const webpack = require('webpack');
const merge = require('webpack-merge'); const merge = require('webpack-merge');
const CopyWebpackPlugin = require('copy-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin');
@@ -11,48 +10,53 @@ const common = {
{ {
test: /\.ts$/, test: /\.ts$/,
enforce: 'pre', enforce: 'pre',
loader: 'tslint-loader' loader: 'tslint-loader',
}, },
{ {
test: /\.tsx?$/, test: /\.tsx?$/,
use: 'ts-loader', use: 'ts-loader',
exclude: /node_modules\/(?!(@bitwarden)\/).*/ exclude: /node_modules\/(?!(@bitwarden)\/).*/,
}, },
] ],
}, },
plugins: [], plugins: [],
resolve: { resolve: {
extensions: ['.tsx', '.ts', '.js'], extensions: ['.tsx', '.ts', '.js'],
alias: { alias: {
jslib: path.join(__dirname, 'jslib/src') jslib: path.join(__dirname, 'jslib/src'),
} tldjs: path.join(__dirname, 'jslib/src/misc/tldjs.noop'),
},
}, },
output: { output: {
filename: '[name].js', filename: '[name].js',
path: path.resolve(__dirname, 'build') path: path.resolve(__dirname, 'build'),
} },
}; };
const main = { const main = {
mode: 'production',
target: 'electron-main', target: 'electron-main',
node: { node: {
__dirname: false, __dirname: false,
__filename: false __filename: false,
}, },
entry: { entry: {
'main': './src/main.ts' 'main': './src/main.ts',
},
optimization: {
minimize: false,
}, },
module: { module: {
rules: [ rules: [
{ {
test: /\.node$/, test: /\.node$/,
loader: 'node-loader' loader: 'node-loader',
}, },
] ],
}, },
plugins: [ plugins: [
new CleanWebpackPlugin([ new CleanWebpackPlugin([
path.resolve(__dirname, 'build/*') path.resolve(__dirname, 'build/*'),
]), ]),
new CopyWebpackPlugin([ new CopyWebpackPlugin([
'./src/package.json', './src/package.json',
@@ -60,7 +64,7 @@ const main = {
{ from: './src/locales', to: 'locales' }, { from: './src/locales', to: 'locales' },
]), ]),
], ],
externals: [nodeExternals()] externals: [nodeExternals()],
}; };
module.exports = merge(common, main); module.exports = merge(common, main);

View File

@@ -5,17 +5,10 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin'); const ExtractTextPlugin = require('extract-text-webpack-plugin');
const AngularCompilerPlugin = require('@ngtools/webpack').AngularCompilerPlugin; const AngularCompilerPlugin = require('@ngtools/webpack').AngularCompilerPlugin;
const isVendorModule = (module) => {
if (!module.context) {
return false;
}
return module.context.indexOf('node_modules') !== -1;
};
const extractCss = new ExtractTextPlugin({ const extractCss = new ExtractTextPlugin({
filename: '[name].css', filename: '[name].css',
disable: false, disable: false,
allChunks: true allChunks: true,
}); });
const common = { const common = {
@@ -24,11 +17,11 @@ const common = {
{ {
test: /\.ts$/, test: /\.ts$/,
enforce: 'pre', enforce: 'pre',
loader: 'tslint-loader' loader: 'tslint-loader',
}, },
{ {
test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/, test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/,
loader: '@ngtools/webpack' loader: '@ngtools/webpack',
}, },
{ {
test: /\.(jpe?g|png|gif|svg)$/i, test: /\.(jpe?g|png|gif|svg)$/i,
@@ -38,39 +31,54 @@ const common = {
options: { options: {
name: '[name].[ext]', name: '[name].[ext]',
outputPath: 'images/', outputPath: 'images/',
} },
}] }],
} },
] ],
}, },
plugins: [], plugins: [],
resolve: { resolve: {
extensions: ['.tsx', '.ts', '.js', '.json'], extensions: ['.tsx', '.ts', '.js', '.json'],
alias: { alias: {
jslib: path.join(__dirname, 'jslib/src') jslib: path.join(__dirname, 'jslib/src'),
}, },
symlinks: false, symlinks: false,
modules: [path.resolve('node_modules')] modules: [path.resolve('node_modules')],
}, },
output: { output: {
filename: '[name].js', filename: '[name].js',
path: path.resolve(__dirname, 'build') path: path.resolve(__dirname, 'build'),
} },
}; };
const renderer = { const renderer = {
mode: 'production',
target: 'electron-renderer', target: 'electron-renderer',
node: { node: {
__dirname: false __dirname: false,
}, },
entry: { entry: {
'app/main': './src/app/main.ts' 'app/main': './src/app/main.ts',
},
optimization: {
minimize: false,
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'app/vendor',
chunks: (chunk) => {
return chunk.name === 'app/main';
},
},
},
},
}, },
module: { module: {
rules: [ rules: [
{ {
test: /\.(html)$/, test: /\.(html)$/,
loader: 'html-loader' loader: 'html-loader',
}, },
{ {
test: /.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/, test: /.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
@@ -79,9 +87,9 @@ const renderer = {
loader: 'file-loader', loader: 'file-loader',
options: { options: {
name: '[name].[ext]', name: '[name].[ext]',
outputPath: 'fonts/' outputPath: 'fonts/',
} },
}] }],
}, },
{ {
test: /\.scss$/, test: /\.scss$/,
@@ -92,27 +100,27 @@ const renderer = {
}, },
{ {
loader: 'sass-loader', loader: 'sass-loader',
} },
], ],
publicPath: '../' publicPath: '../',
}) })
}, },
] // Hide System.import warnings. ref: https://github.com/angular/angular/issues/21560
{
test: /[\/\\]@angular[\/\\].+\.js$/,
parser: { system: true },
},
],
}, },
plugins: [ plugins: [
new AngularCompilerPlugin({ new AngularCompilerPlugin({
tsConfigPath: 'tsconfig.json', tsConfigPath: 'tsconfig.json',
entryModule: 'src/app/app.module#AppModule', entryModule: 'src/app/app.module#AppModule',
sourceMap: true sourceMap: true,
}), }),
// ref: https://github.com/angular/angular/issues/20357 // ref: https://github.com/angular/angular/issues/20357
new webpack.ContextReplacementPlugin(/\@angular(\\|\/)core(\\|\/)esm5/, new webpack.ContextReplacementPlugin(/\@angular(\\|\/)core(\\|\/)fesm5/,
path.resolve(__dirname, './src')), path.resolve(__dirname, './src')),
new webpack.optimize.CommonsChunkPlugin({
name: 'app/vendor',
chunks: ['app/main'],
minChunks: isVendorModule
}),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
template: './src/index.html', template: './src/index.html',
filename: 'index.html', filename: 'index.html',
@@ -123,8 +131,8 @@ const renderer = {
include: ['app/main.js'] include: ['app/main.js']
}), }),
new webpack.DefinePlugin({ 'global.GENTLY': false }), new webpack.DefinePlugin({ 'global.GENTLY': false }),
extractCss extractCss,
] ],
}; };
module.exports = merge(common, renderer); module.exports = merge(common, renderer);