1
0
mirror of https://github.com/bitwarden/directory-connector synced 2025-12-16 00:04:34 +00:00

Compare commits

..

77 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
Kyle Spearrin
2fe294ad8a fix click issue on 2fa provider selection 2018-08-17 08:56:31 -04:00
Kyle Spearrin
1343897fa9 update jslib 2018-08-17 08:32:38 -04:00
Kyle Spearrin
9b2b0e4ea6 update jslib 2018-08-16 15:13:34 -04:00
Kyle Spearrin
489e775b08 update jslib 2018-08-15 09:01:30 -04:00
Kyle Spearrin
0d76a45181 bump version 2018-08-14 21:44:34 -04:00
Kyle Spearrin
4787f9a462 prelogin kdf info 2018-08-14 15:13:44 -04:00
Kyle Spearrin
b228e12c81 update jslib 2018-08-13 23:41:14 -04:00
Kyle Spearrin
0e28100ec6 add papaparse types cause whoknowswhy 2018-08-08 17:00:33 -04:00
Kyle Spearrin
d2c1697144 switch to gulp file to get google fonts 2018-08-08 12:53:09 -04:00
Kyle Spearrin
b924ea8dd1 update packages for node 10 2018-08-08 12:28:32 -04:00
Kyle Spearrin
30936d3ad1 update jslib 2018-08-07 09:26:14 -04:00
Kyle Spearrin
289589dce9 toaster message paragraph styles 2018-08-02 08:48:59 -04:00
Kyle Spearrin
48734b8109 update jslib 2018-07-31 23:49:46 -04:00
Kyle Spearrin
2e69a41943 update jslib 2018-07-31 11:26:33 -04:00
Kyle Spearrin
f414a3ac0a update jslib 2018-07-30 17:08:49 -04:00
Kyle Spearrin
81833b134c update jslib 2018-07-25 14:30:40 -04:00
Kyle Spearrin
5a5b74a0d5 bump version 2018-07-24 15:29:48 -04:00
Kyle Spearrin
5a7a230193 update libs 2018-07-24 15:29:00 -04:00
Jacob
bc5ab6252e Fetch image from bitwarden/brand (#7)
That was the last of them, sorry for all the pull request spams :P
2018-07-19 10:51:35 -04:00
Kyle Spearrin
0e4d5e0973 update jslib 2018-07-18 09:56:19 -04:00
Kyle Spearrin
08e254e34c version bump 2018-07-17 08:49:46 -04:00
Kyle Spearrin
2c20796ea1 make sure entry ids are unique 2018-07-17 08:49:15 -04:00
Kyle Spearrin
8b9d7a7e2e remove sync service ref 2018-07-13 10:51:14 -04:00
Kyle Spearrin
1a0709b02c update jslib 2018-07-13 09:31:32 -04:00
Kyle Spearrin
0f386ee8d2 remember email on login 2018-07-13 09:29:12 -04:00
Kyle Spearrin
c0b41155e9 update jslib 2018-07-09 09:14:46 -04:00
Kyle Spearrin
ddd1165728 dummy module update 2018-07-07 23:57:24 -04:00
Kyle Spearrin
651dbe59c8 update jslib 2018-07-07 23:52:19 -04:00
Kyle Spearrin
aeb6f28f9a update jslib 2018-07-05 14:55:06 -04:00
Kyle Spearrin
012eefad4e update jslib 2018-07-05 14:42:58 -04:00
Kyle Spearrin
b05346d4a3 update jslib 2018-06-30 13:51:32 -04:00
Kyle Spearrin
854e6e84fe ignore jslib importers 2018-06-28 08:07:02 -04:00
Kyle Spearrin
20436bf07d tolowercase all the emails 2018-06-28 07:53:58 -04:00
Kyle Spearrin
4110e4b8cb update jslib 2018-06-13 22:14:28 -04:00
Kyle Spearrin
e179edc2e0 update jslib 2018-06-13 17:15:21 -04:00
Kyle Spearrin
133ef7eb16 load DuoWebSDK as a module 2018-06-11 13:33:03 -04:00
Kyle Spearrin
5a98982fc2 move dummy module into project 2018-06-11 08:54:34 -04:00
Kyle Spearrin
f6e92bf597 bump version 2018-06-08 16:08:12 -04:00
Kyle Spearrin
af4a8d5ae8 update jslib 2018-06-04 12:06:04 -04:00
Kyle Spearrin
c3704bbb67 bump version 2018-06-01 16:42:01 -04:00
Kyle Spearrin
8620f2f80d update jslib 2018-06-01 12:34:11 -04:00
Kyle Spearrin
e0124474cf update jslib 2018-06-01 12:00:58 -04:00
Kyle Spearrin
ac21b78225 Update README.md 2018-06-01 09:32:08 -04:00
38 changed files with 6891 additions and 4898 deletions

1
.gitignore vendored
View File

@@ -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/

View File

@@ -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> [![Platforms](https://imgur.com/SLv9paA.png "Windows, macOS, and Linux")](https://help.bitwarden.com/article/directory-sync/#download-and-install)
![Directory Connector](http://imgur.com/I6FjN4j.png "Dashboard") ![Directory Connector](https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/directory-connector-macos.png "Dashboard")
# Build/Run # Build/Run

31
gulpfile.js Normal file
View 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

Submodule jslib updated: e0d5a4d8b7...2f6426deb4

11097
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -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"
} }
} }

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

@@ -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">

View File

@@ -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';
} }

View File

@@ -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}}

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

@@ -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>

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';
@@ -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';
} }

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,

16
src/app/dummy.module.ts Normal file
View 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 {
}

View File

@@ -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 });

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.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"
} }
} }

View File

@@ -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;

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);
} }
@@ -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);
} }
} }

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; 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';
@@ -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;

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; 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

@@ -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);

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,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",

View File

@@ -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
View 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

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

@@ -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);