mirror of
https://github.com/bitwarden/directory-connector
synced 2025-12-16 00:04:34 +00:00
Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cb2147569 | ||
|
|
327de4d714 | ||
|
|
9b57955d72 | ||
|
|
e63198e130 | ||
|
|
03eac9006f | ||
|
|
d3402cca5e | ||
|
|
076b320e70 | ||
|
|
44180f95a5 | ||
|
|
e23ebf13b7 | ||
|
|
e0fd1a2e93 | ||
|
|
c39986b37c | ||
|
|
1e0029dc15 | ||
|
|
63bc5e4161 | ||
|
|
8c8b4da595 | ||
|
|
1a7536270a | ||
|
|
d92e3c8d7b | ||
|
|
c30aedf491 | ||
|
|
0e96a462ee | ||
|
|
3ef60cb3a0 | ||
|
|
34b7638e11 | ||
|
|
63ef932469 | ||
|
|
e09163fc72 | ||
|
|
6a737f6d7d | ||
|
|
ccc7b7b213 | ||
|
|
1e98bc6430 | ||
|
|
c63a945057 | ||
|
|
a13d9fa00f | ||
|
|
1518d5eafc | ||
|
|
c74800fd8c | ||
|
|
9e282efe28 | ||
|
|
26efc9158d | ||
|
|
e2d3d35d71 | ||
|
|
c115bf84f0 | ||
|
|
5b56d32492 | ||
|
|
2fe294ad8a | ||
|
|
1343897fa9 | ||
|
|
9b2b0e4ea6 | ||
|
|
489e775b08 | ||
|
|
0d76a45181 | ||
|
|
4787f9a462 | ||
|
|
b228e12c81 | ||
|
|
0e28100ec6 | ||
|
|
d2c1697144 | ||
|
|
b924ea8dd1 | ||
|
|
30936d3ad1 | ||
|
|
289589dce9 | ||
|
|
48734b8109 | ||
|
|
2e69a41943 | ||
|
|
f414a3ac0a | ||
|
|
81833b134c | ||
|
|
5a5b74a0d5 | ||
|
|
5a7a230193 | ||
|
|
bc5ab6252e | ||
|
|
0e4d5e0973 | ||
|
|
08e254e34c | ||
|
|
2c20796ea1 | ||
|
|
8b9d7a7e2e | ||
|
|
1a0709b02c | ||
|
|
0f386ee8d2 | ||
|
|
c0b41155e9 | ||
|
|
ddd1165728 | ||
|
|
651dbe59c8 | ||
|
|
aeb6f28f9a | ||
|
|
012eefad4e | ||
|
|
b05346d4a3 | ||
|
|
854e6e84fe | ||
|
|
20436bf07d | ||
|
|
4110e4b8cb | ||
|
|
e179edc2e0 | ||
|
|
133ef7eb16 | ||
|
|
5a98982fc2 | ||
|
|
f6e92bf597 | ||
|
|
af4a8d5ae8 | ||
|
|
c3704bbb67 | ||
|
|
8620f2f80d | ||
|
|
e0124474cf | ||
|
|
ac21b78225 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@ node_modules
|
|||||||
npm-debug.log
|
npm-debug.log
|
||||||
vwd.webinfo
|
vwd.webinfo
|
||||||
dist/
|
dist/
|
||||||
|
css/
|
||||||
*.crx
|
*.crx
|
||||||
*.pem
|
*.pem
|
||||||
build/
|
build/
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ Supported directories:
|
|||||||
|
|
||||||
The application is written using Electron with Angular and installs on Windows, macOS, and Linux distributions.
|
The application is written using Electron with Angular and installs on Windows, macOS, and Linux distributions.
|
||||||
|
|
||||||
<a href="https://help.bitwarden.com/article/directory-sync/#download-and-install"><img src="https://imgur.com/SLv9paA.png" width="500" height="113"></a>
|
[](https://help.bitwarden.com/article/directory-sync/#download-and-install)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
# Build/Run
|
# Build/Run
|
||||||
|
|
||||||
|
|||||||
31
gulpfile.js
Normal file
31
gulpfile.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
const gulp = require('gulp');
|
||||||
|
const googleWebFonts = require('gulp-google-webfonts');
|
||||||
|
const del = require('del');
|
||||||
|
|
||||||
|
const paths = {
|
||||||
|
cssDir: './src/css/',
|
||||||
|
};
|
||||||
|
|
||||||
|
function clean() {
|
||||||
|
return del([paths.cssDir]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function webfonts() {
|
||||||
|
return gulp.src('./webfonts.list')
|
||||||
|
.pipe(googleWebFonts({
|
||||||
|
fontsDir: 'webfonts',
|
||||||
|
cssFilename: 'webfonts.css',
|
||||||
|
format: 'woff',
|
||||||
|
}))
|
||||||
|
.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('cleanupAotIssue', cleanupAotIssue);
|
||||||
|
gulp.task('webfonts', ['clean'], webfonts);
|
||||||
|
gulp.task('prebuild:renderer', ['webfonts', 'cleanupAotIssue']);
|
||||||
2
jslib
2
jslib
Submodule jslib updated: e0d5a4d8b7...2f6426deb4
11097
package-lock.json
generated
11097
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
117
package.json
117
package.json
@@ -26,8 +26,8 @@
|
|||||||
"lint:fix": "tslint src/**/*.ts --fix",
|
"lint:fix": "tslint src/**/*.ts --fix",
|
||||||
"build": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main\" \"npm run build:renderer\"",
|
"build": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main\" \"npm run build:renderer\"",
|
||||||
"build:main": "webpack --config webpack.main.js",
|
"build:main": "webpack --config webpack.main.js",
|
||||||
"build:renderer": "webpack --config webpack.renderer.js",
|
"build:renderer": "gulp prebuild:renderer && webpack --config webpack.renderer.js",
|
||||||
"build:renderer:watch": "webpack --config webpack.renderer.js --watch",
|
"build:renderer:watch": "gulp prebuild:renderer && webpack --config webpack.renderer.js --watch",
|
||||||
"electron": "npm run build:main && concurrently -k -n Main,Rend -c yellow,cyan \"electron --inspect=5858 ./build --watch\" \"npm run build:renderer:watch\"",
|
"electron": "npm run build:main && concurrently -k -n Main,Rend -c yellow,cyan \"electron --inspect=5858 ./build --watch\" \"npm run build:renderer:watch\"",
|
||||||
"clean:dist": "rimraf ./dist/*",
|
"clean:dist": "rimraf ./dist/*",
|
||||||
"pack:lin": "npm run clean:dist && build --linux --x64 -p never",
|
"pack:lin": "npm run clean:dist && build --linux --x64 -p never",
|
||||||
@@ -107,68 +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/keytar": "^4.0.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/webcrypto": "0.0.28",
|
"@types/semver": "^5.5.0",
|
||||||
"clean-webpack-plugin": "^0.1.17",
|
"@types/source-map": "0.5.2",
|
||||||
"concurrently": "3.5.1",
|
"@types/webcrypto": "^0.0.28",
|
||||||
"copy-webpack-plugin": "^4.2.0",
|
"@types/webpack": "^4.4.11",
|
||||||
"css-loader": "^0.28.7",
|
"clean-webpack-plugin": "^0.1.19",
|
||||||
"electron": "2.0.2",
|
"concurrently": "^4.0.1",
|
||||||
"electron-builder": "^20.8.1",
|
"copy-webpack-plugin": "^4.5.2",
|
||||||
"electron-rebuild": "1.7.3",
|
"css-loader": "^1.0.0",
|
||||||
"electron-reload": "1.2.2",
|
"del": "^3.0.0",
|
||||||
"extract-text-webpack-plugin": "^3.0.1",
|
"electron": "2.0.11",
|
||||||
"file-loader": "^1.1.5",
|
"electron-builder": "^20.25.0",
|
||||||
|
"electron-rebuild": "1.8.1",
|
||||||
|
"electron-reload": "1.2.5",
|
||||||
|
"extract-text-webpack-plugin": "next",
|
||||||
|
"file-loader": "^2.0.0",
|
||||||
"font-awesome": "4.7.0",
|
"font-awesome": "4.7.0",
|
||||||
"google-fonts-webpack-plugin": "^0.4.4",
|
"gulp": "^3.9.1",
|
||||||
"html-loader": "^0.5.1",
|
"gulp-google-webfonts": "^2.0.0",
|
||||||
"html-webpack-plugin": "^2.30.1",
|
"html-loader": "^0.5.5",
|
||||||
|
"html-webpack-plugin": "^3.2.0",
|
||||||
"node-loader": "^0.6.0",
|
"node-loader": "^0.6.0",
|
||||||
"node-sass": "^4.7.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": "2.21.4",
|
"electron-updater": "3.0.3",
|
||||||
"googleapis": "29.0.0",
|
"googleapis": "33.0.0",
|
||||||
"keytar": "4.1.0",
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="email">{{'emailAddress' | i18n}}</label>
|
<label for="email">{{'emailAddress' | i18n}}</label>
|
||||||
<input id="email" type="text" name="Email" [(ngModel)]="email" class="form-control" appAutoFocus>
|
<input id="email" type="text" name="Email" [(ngModel)]="email" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
|
|||||||
@@ -6,14 +6,12 @@ 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 { SyncService } from 'jslib/abstractions/sync.service';
|
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.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';
|
||||||
import { ModalComponent } from 'jslib/angular/components/modal.component';
|
import { ModalComponent } from 'jslib/angular/components/modal.component';
|
||||||
@@ -26,9 +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, platformUtilsService: PlatformUtilsService) {
|
||||||
super(authService, router, analytics, toasterService, i18nService);
|
super(authService, router, platformUtilsService, i18nService, storageService);
|
||||||
super.successRoute = '/tabs/dashboard';
|
super.successRoute = '/tabs/dashboard';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p *ngFor="let p of providers">
|
<p *ngFor="let p of providers">
|
||||||
<a href="#" (click)="choose(p)">
|
<a href="#" appStopClick (click)="choose(p)">
|
||||||
<strong>{{p.name}}</strong>
|
<strong>{{p.name}}</strong>
|
||||||
</a>
|
</a>
|
||||||
<br /> {{p.description}}
|
<br /> {{p.description}}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="selectedProviderType === providerType.Yubikey">
|
<ng-container *ngIf="selectedProviderType === providerType.Yubikey">
|
||||||
<p>{{'insertYubiKey' | i18n}}</p>
|
<p>{{'insertYubiKey' | i18n}}</p>
|
||||||
<img src="../../images/yubikey.jpg" alt="">
|
<p><img src="../../images/yubikey.jpg" class="img-fluid rounded" alt=""></p>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="code">{{'verificationCode' | i18n}}</label>
|
<label for="code">{{'verificationCode' | i18n}}</label>
|
||||||
<input id="code" type="password" name="Code" [(ngModel)]="token" appAutofocus class="form-control">
|
<input id="code" type="password" name="Code" [(ngModel)]="token" appAutofocus class="form-control">
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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';
|
||||||
@@ -19,7 +16,6 @@ import { AuthService } from 'jslib/abstractions/auth.service';
|
|||||||
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 { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||||
import { SyncService } from 'jslib/abstractions/sync.service';
|
|
||||||
|
|
||||||
import { ModalComponent } from 'jslib/angular/components/modal.component';
|
import { ModalComponent } from 'jslib/angular/components/modal.component';
|
||||||
import { TwoFactorComponent as BaseTwoFactorComponent } from 'jslib/angular/components/two-factor.component';
|
import { TwoFactorComponent as BaseTwoFactorComponent } from 'jslib/angular/components/two-factor.component';
|
||||||
@@ -32,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';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ import { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe';
|
|||||||
clearQueryParams: true,
|
clearQueryParams: true,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
ToasterModule,
|
ToasterModule.forRoot(),
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
ApiActionDirective,
|
ApiActionDirective,
|
||||||
|
|||||||
16
src/app/dummy.module.ts
Normal file
16
src/app/dummy.module.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { InputVerbatimDirective } from 'jslib/angular/directives/input-verbatim.directive';
|
||||||
|
import { TrueFalseValueDirective } from 'jslib/angular/directives/true-false-value.directive';
|
||||||
|
import { SearchPipe } from 'jslib/angular/pipes/search.pipe';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [],
|
||||||
|
declarations: [
|
||||||
|
InputVerbatimDirective,
|
||||||
|
TrueFalseValueDirective,
|
||||||
|
SearchPipe,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class DummyModule {
|
||||||
|
}
|
||||||
@@ -5,8 +5,6 @@ import { isDev } from 'jslib/electron/utils';
|
|||||||
|
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
require('../scss/styles.scss');
|
require('../scss/styles.scss');
|
||||||
// tslint:disable-next-line
|
|
||||||
require('../../jslib/src/misc/duo.js');
|
|
||||||
|
|
||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
@@ -14,4 +12,4 @@ if (!isDev()) {
|
|||||||
enableProdMode();
|
enableProdMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
platformBrowserDynamic().bootstrapModule(AppModule, { preserveWhitespaces: true });
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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 = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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="">
|
||||||
|
|||||||
@@ -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"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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.0.2",
|
"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",
|
||||||
@@ -13,8 +13,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"electron-log": "2.2.14",
|
"electron-log": "2.2.14",
|
||||||
"electron-updater": "2.21.4",
|
"electron-updater": "3.0.3",
|
||||||
"keytar": "4.1.0",
|
"keytar": "4.2.1",
|
||||||
"lowdb": "1.0.0"
|
"lowdb": "1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,16 @@ $fa-font-path: "~font-awesome/fonts";
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toast-message {
|
||||||
|
p {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.toast-danger, &.toast-error {
|
&.toast-danger, &.toast-error {
|
||||||
background-image: none !important;
|
background-image: none !important;
|
||||||
background-color: $danger;
|
background-color: $danger;
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,6 +67,7 @@ export class AzureDirectoryService extends BaseDirectoryService implements Direc
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getUsers(force: boolean, saveDelta: boolean): Promise<UserEntry[]> {
|
private async getUsers(force: boolean, saveDelta: boolean): Promise<UserEntry[]> {
|
||||||
|
const entryIds = new Set<string>();
|
||||||
const entries: UserEntry[] = [];
|
const entries: UserEntry[] = [];
|
||||||
|
|
||||||
let res: any = null;
|
let res: any = null;
|
||||||
@@ -90,6 +91,9 @@ export class AzureDirectoryService extends BaseDirectoryService implements Direc
|
|||||||
const users: graphType.User[] = res.value;
|
const users: graphType.User[] = res.value;
|
||||||
if (users != null) {
|
if (users != null) {
|
||||||
for (const user of users) {
|
for (const user of users) {
|
||||||
|
if (user.id == null || entryIds.has(user.id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const entry = this.buildUser(user);
|
const entry = this.buildUser(user);
|
||||||
if (this.filterOutResult(setFilter, entry.email)) {
|
if (this.filterOutResult(setFilter, entry.email)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -101,6 +105,7 @@ export class AzureDirectoryService extends BaseDirectoryService implements Direc
|
|||||||
}
|
}
|
||||||
|
|
||||||
entries.push(entry);
|
entries.push(entry);
|
||||||
|
entryIds.add(user.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +127,17 @@ 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) {
|
||||||
|
entry.email = entry.email.trim().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
entry.disabled = user.accountEnabled == null ? false : !user.accountEnabled;
|
entry.disabled = user.accountEnabled == null ? false : !user.accountEnabled;
|
||||||
|
|
||||||
if ((user as any)['@removed'] != null && (user as any)['@removed'].reason === 'changed') {
|
if ((user as any)['@removed'] != null && (user as any)['@removed'].reason === 'changed') {
|
||||||
@@ -132,77 +147,25 @@ 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 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 (this.filterOutResult(setFilter, group.displayName)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const entry = await this.buildGroup(group);
|
|
||||||
entries.push(entry);
|
|
||||||
} 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)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (this.filterOutResult(setFilter, group.displayName)) {
|
if (this.filterOutResult(setFilter, group.displayName)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const entry = await this.buildGroup(group);
|
const entry = await this.buildGroup(group);
|
||||||
entries.push(entry);
|
entries.push(entry);
|
||||||
|
entryIds.add(group.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
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;
|
||||||
|
|||||||
@@ -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';
|
||||||
@@ -108,10 +109,14 @@ export class LdapDirectoryService implements DirectoryService {
|
|||||||
this.syncConfig.emailPrefixAttribute != null && this.syncConfig.emailSuffix != null) {
|
this.syncConfig.emailPrefixAttribute != null && this.syncConfig.emailSuffix != null) {
|
||||||
const prefixAttr = this.getAttr(searchEntry, this.syncConfig.emailPrefixAttribute);
|
const prefixAttr = this.getAttr(searchEntry, this.syncConfig.emailPrefixAttribute);
|
||||||
if (prefixAttr != null) {
|
if (prefixAttr != null) {
|
||||||
user.email = (prefixAttr + this.syncConfig.emailSuffix).toLowerCase();
|
user.email = prefixAttr + this.syncConfig.emailSuffix;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user.email != null) {
|
||||||
|
user.email = user.email.trim().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
if (!user.deleted && (user.email == null || user.email.trim() === '')) {
|
if (!user.deleted && (user.email == null || user.email.trim() === '')) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -322,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;
|
||||||
|
|||||||
@@ -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;
|
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;
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import { CryptoFunctionService } from 'jslib/abstractions/cryptoFunction.service
|
|||||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||||
import { LogService } from 'jslib/abstractions/log.service';
|
import { LogService } from 'jslib/abstractions/log.service';
|
||||||
import { MessagingService } from 'jslib/abstractions/messaging.service';
|
import { MessagingService } from 'jslib/abstractions/messaging.service';
|
||||||
import { StorageService } from 'jslib/abstractions/storage.service';
|
|
||||||
|
|
||||||
import { Utils } from 'jslib/misc/utils';
|
import { Utils } from 'jslib/misc/utils';
|
||||||
|
|
||||||
@@ -24,9 +23,6 @@ import { GSuiteDirectoryService } from './gsuite-directory.service';
|
|||||||
import { LdapDirectoryService } from './ldap-directory.service';
|
import { LdapDirectoryService } from './ldap-directory.service';
|
||||||
import { OktaDirectoryService } from './okta-directory.service';
|
import { OktaDirectoryService } from './okta-directory.service';
|
||||||
|
|
||||||
const Keys = {
|
|
||||||
};
|
|
||||||
|
|
||||||
export class SyncService {
|
export class SyncService {
|
||||||
private dirType: DirectoryType;
|
private dirType: DirectoryType;
|
||||||
|
|
||||||
@@ -73,7 +69,7 @@ export class SyncService {
|
|||||||
const reqJson = JSON.stringify(req);
|
const reqJson = JSON.stringify(req);
|
||||||
|
|
||||||
let hash: string = null;
|
let hash: string = null;
|
||||||
const hashBuf = await this.cryptoFunctionService.hash(this.apiService.baseUrl + reqJson, 'sha256');
|
const hashBuf = await this.cryptoFunctionService.hash(this.apiService.apiBaseUrl + reqJson, 'sha256');
|
||||||
if (hashBuf != null) {
|
if (hashBuf != null) {
|
||||||
hash = Utils.fromBufferToB64(hashBuf);
|
hash = Utils.fromBufferToB64(hashBuf);
|
||||||
}
|
}
|
||||||
@@ -149,6 +145,9 @@ export class SyncService {
|
|||||||
for (const u of users) {
|
for (const u of users) {
|
||||||
const iu = new ImportDirectoryRequestUser();
|
const iu = new ImportDirectoryRequestUser();
|
||||||
iu.email = u.email;
|
iu.email = u.email;
|
||||||
|
if (iu.email != null) {
|
||||||
|
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);
|
||||||
model.users.push(iu);
|
model.users.push(iu);
|
||||||
|
|||||||
@@ -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,7 +38,9 @@
|
|||||||
"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",
|
||||||
"dist",
|
"dist",
|
||||||
"jslib/dist",
|
"jslib/dist",
|
||||||
"build",
|
"build",
|
||||||
|
|||||||
@@ -48,6 +48,7 @@
|
|||||||
"check-preblock",
|
"check-preblock",
|
||||||
"check-separator",
|
"check-separator",
|
||||||
"check-type"
|
"check-type"
|
||||||
]
|
],
|
||||||
|
"max-classes-per-file": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
webfonts.list
Normal file
1
webfonts.list
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Open+Sans:300,300i,400,400i,600,600i,700,700i,800,800i&subset=cyrillic,cyrillic-ext,greek,greek-ext,latin-ext
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -2,21 +2,13 @@ const path = require('path');
|
|||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const merge = require('webpack-merge');
|
const merge = require('webpack-merge');
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
const GoogleFontsPlugin = require("google-fonts-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 = {
|
||||||
@@ -25,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,
|
||||||
@@ -39,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]+)?$/,
|
||||||
@@ -80,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$/,
|
||||||
@@ -93,40 +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 GoogleFontsPlugin({
|
|
||||||
fonts: [
|
|
||||||
{
|
|
||||||
family: 'Open Sans',
|
|
||||||
variants: ['300', '300italic', '400', '400italic', '600', '600italic',
|
|
||||||
'700', '700italic', '800', '800italic'],
|
|
||||||
subsets: ['cyrillic', 'cyrillic-ext', 'greek', 'greek-ext', 'latin', 'latin-ext']
|
|
||||||
}
|
|
||||||
],
|
|
||||||
formats: ['woff2'],
|
|
||||||
path: 'fonts/',
|
|
||||||
filename: 'css/fonts.css'
|
|
||||||
}),
|
|
||||||
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',
|
||||||
@@ -137,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);
|
||||||
|
|||||||
Reference in New Issue
Block a user