1
0
mirror of https://github.com/bitwarden/directory-connector synced 2025-12-05 23:53:21 +00:00

Lock lowdb file (#95)

* Lock lowdb file when using. Do not allow caching

* Linter fixes

* Move to non-jslib lowdbstorage to allow for lockfile

* update jslib

* Must ensure db file exists prior to initialization

proper-lockfile throws if the file its locking does not exist

* update jslib

* Let base handle file initialization
This commit is contained in:
Matt Gibson
2021-02-17 10:33:05 -06:00
committed by GitHub
parent e5d0b3a372
commit 2583068dbd
16 changed files with 107 additions and 96 deletions

13
.vscode/launch.json vendored
View File

@@ -23,7 +23,18 @@
"port": 9223,
"webRoot": "${workspaceFolder}/build",
"sourceMaps": true
}
},
{
"type": "node",
"request": "launch",
"name": "Debug CLI",
"protocol": "inspector",
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/build-cli/bwdc.js",
"args": [
"sync"
]
}
],
"compounds": [
{

2
jslib

Submodule jslib updated: 6183a30a52...fc1275aeb2

97
package-lock.json generated
View File

@@ -714,6 +714,15 @@
"@types/node": "*"
}
},
"@types/proper-lockfile": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/@types/proper-lockfile/-/proper-lockfile-4.1.1.tgz",
"integrity": "sha512-HAjVfDa73pFgivViHyDu8HHHcds+W4MgOuZZAdyFJrHS8ngtCXmhl4hc2YXqSOwO6Bsa+iF2Sgxb2+gv874VOQ==",
"dev": true,
"requires": {
"@types/retry": "*"
}
},
"@types/responselike": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
@@ -723,6 +732,12 @@
"@types/node": "*"
}
},
"@types/retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
"dev": true
},
"@types/rx": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/@types/rx/-/rx-4.1.1.tgz",
@@ -1366,12 +1381,6 @@
"universalify": "^1.0.0"
}
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"hosted-git-info": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.7.tgz",
@@ -2468,12 +2477,6 @@
"universalify": "^1.0.0"
}
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -4297,12 +4300,6 @@
"universalify": "^1.0.0"
}
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"iconv-lite": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
@@ -4638,12 +4635,6 @@
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -4791,12 +4782,6 @@
"universalify": "^1.0.0"
}
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
@@ -4910,12 +4895,6 @@
"universalify": "^1.0.0"
}
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -5189,12 +5168,6 @@
"responselike": "^2.0.0"
}
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"har-validator": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
@@ -5690,11 +5663,6 @@
"universalify": "^1.0.0"
}
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw=="
},
"js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
@@ -7145,14 +7113,6 @@
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
},
"dependencies": {
"graceful-fs": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
"dev": true
}
}
},
"fs-minipass": {
@@ -8749,9 +8709,9 @@
}
},
"graceful-fs": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
"version": "4.2.6",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
"integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ=="
},
"gtoken": {
"version": "4.1.0",
@@ -13022,6 +12982,16 @@
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
"dev": true
},
"proper-lockfile": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
"integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==",
"requires": {
"graceful-fs": "^4.2.4",
"retry": "^0.12.0",
"signal-exit": "^3.0.2"
}
},
"proto-list": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
@@ -13618,6 +13588,11 @@
"integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
"dev": true
},
"retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
"integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs="
},
"rimraf": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
@@ -14719,12 +14694,6 @@
"path-is-absolute": "^1.0.0"
}
},
"graceful-fs": {
"version": "4.1.15",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
"integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
"dev": true
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",

View File

@@ -27,8 +27,8 @@
"symlink:lin": "rm -rf ./jslib && ln -s ../jslib ./jslib",
"rebuild": "./node_modules/.bin/electron-rebuild",
"reset": "rimraf ./node_modules/keytar/* && npm install",
"lint": "tslint src/**/*.ts || true",
"lint:fix": "tslint src/**/*.ts --fix",
"lint": "tslint 'src/**/*.ts' || true",
"lint:fix": "tslint 'src/**/*.ts' --fix",
"build": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main\" \"npm run build:renderer\"",
"build:main": "webpack --config webpack.main.js",
"build:renderer": "gulp prebuild:renderer && webpack --config webpack.renderer.js",
@@ -145,6 +145,7 @@
"@types/node-fetch": "^2.1.2",
"@types/node-forge": "^0.7.5",
"@types/papaparse": "^4.5.3",
"@types/proper-lockfile": "^4.1.1",
"@types/semver": "^5.5.0",
"@types/source-map": "0.5.2",
"@types/webcrypto": "^0.0.28",
@@ -217,6 +218,7 @@
"node-fetch": "2.2.0",
"node-forge": "0.7.6",
"open": "7.1.0",
"proper-lockfile": "^4.1.2",
"rxjs": "6.6.2",
"tslib": "^2.0.1",
"zone.js": "0.10.3",

View File

@@ -105,7 +105,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
const reader = new FileReader();
reader.readAsText(filePicker.files[0], 'utf-8');
reader.onload = (evt) => {
reader.onload = evt => {
this.ngZone.run(async () => {
try {
const result = JSON.parse((evt.target as FileReader).result as string);

View File

@@ -8,6 +8,7 @@ import { AuthService } from 'jslib/services/auth.service';
import { ConfigurationService } from './services/configuration.service';
import { I18nService } from './services/i18n.service';
import { KeytarSecureStorageService } from './services/keytarSecureStorage.service';
import { LowdbStorageService } from './services/lowdbStorage.service';
import { SyncService } from './services/sync.service';
import { CliPlatformUtilsService } from 'jslib/cli/services/cliPlatformUtils.service';
@@ -18,7 +19,6 @@ import { ConstantsService } from 'jslib/services/constants.service';
import { ContainerService } from 'jslib/services/container.service';
import { CryptoService } from 'jslib/services/crypto.service';
import { EnvironmentService } from 'jslib/services/environment.service';
import { LowdbStorageService } from 'jslib/services/lowdbStorage.service';
import { NodeApiService } from 'jslib/services/nodeApi.service';
import { NodeCryptoFunctionService } from 'jslib/services/nodeCryptoFunction.service';
import { NoopMessagingService } from 'jslib/services/noopMessaging.service';
@@ -78,9 +78,9 @@ export class Main {
this.i18nService = new I18nService('en', './locales');
this.platformUtilsService = new CliPlatformUtilsService('connector', packageJson);
this.logService = new ConsoleLogService(this.platformUtilsService.isDev(),
(level) => process.env.BITWARDENCLI_CONNECTOR_DEBUG !== 'true' && level <= LogLevelType.Info);
level => process.env.BITWARDENCLI_CONNECTOR_DEBUG !== 'true' && level <= LogLevelType.Info);
this.cryptoFunctionService = new NodeCryptoFunctionService();
this.storageService = new LowdbStorageService(this.logService, null, this.dataFilePath, true);
this.storageService = new LowdbStorageService(this.logService, null, this.dataFilePath, false, true);
this.secureStorageService = plaintextSecrets ?
this.storageService : new KeytarSecureStorageService(applicationName);
this.cryptoService = new CryptoService(this.storageService, this.secureStorageService,
@@ -117,7 +117,7 @@ export class Main {
}
private async init() {
this.storageService.init();
await this.storageService.init();
this.containerService.attachToWindow(global);
await this.environmentService.setUrlsFromStorage();
// Dev Server URLs. Comment out the line above.

View File

@@ -41,7 +41,7 @@ export class Main {
app.setPath('logs', path.join(app.getPath('userData'), 'logs'));
const args = process.argv.slice(1);
const watch = args.some((val) => val === '--watch');
const watch = args.some(val => val === '--watch');
if (watch) {
// tslint:disable-next-line
@@ -52,8 +52,7 @@ export class Main {
this.i18nService = new I18nService('en', './locales/');
this.storageService = new ElectronStorageService(app.getPath('userData'));
this.windowMain = new WindowMain(this.storageService, false, 800, 600,
(arg) => this.processDeepLink(arg), null);
this.windowMain = new WindowMain(this.storageService, false, 800, 600, arg => this.processDeepLink(arg), null);
this.menuMain = new MenuMain(this);
this.updaterMain = new UpdaterMain(this.i18nService, this.windowMain, 'directory-connector', () => {
this.messagingService.send('checkingForUpdate');
@@ -64,7 +63,7 @@ export class Main {
}, 'bitwardenDirectoryConnector');
this.trayMain = new TrayMain(this.windowMain, this.i18nService, this.storageService);
this.messagingMain = new MessagingMain(this.windowMain, this.menuMain, this.updaterMain, this.trayMain);
this.messagingService = new ElectronMainMessagingService(this.windowMain, (message) => {
this.messagingService = new ElectronMainMessagingService(this.windowMain, message => {
this.messagingMain.onMessage(message);
});
@@ -96,7 +95,7 @@ export class Main {
}
private processDeepLink(argv: string[]): void {
argv.filter((s) => s.indexOf('bwdc://') === 0).forEach((s) => {
argv.filter(s => s.indexOf('bwdc://') === 0).forEach(s => {
const url = new URL(s);
const code = url.searchParams.get('code');
const receivedState = url.searchParams.get('state');

View File

@@ -14,9 +14,9 @@ export class TestResponse implements BaseResponse {
constructor(result: SimResult) {
this.object = 'test';
this.groups = result.groups != null ? result.groups.map((g) => new GroupResponse(g)) : [];
this.enabledUsers = result.enabledUsers != null ? result.enabledUsers.map((u) => new UserResponse(u)) : [];
this.disabledUsers = result.disabledUsers != null ? result.disabledUsers.map((u) => new UserResponse(u)) : [];
this.deletedUsers = result.deletedUsers != null ? result.deletedUsers.map((u) => new UserResponse(u)) : [];
this.groups = result.groups != null ? result.groups.map(g => new GroupResponse(g)) : [];
this.enabledUsers = result.enabledUsers != null ? result.enabledUsers.map(u => new UserResponse(u)) : [];
this.disabledUsers = result.disabledUsers != null ? result.disabledUsers.map(u => new UserResponse(u)) : [];
this.deletedUsers = result.deletedUsers != null ? result.deletedUsers.map(u => new UserResponse(u)) : [];
}
}

View File

@@ -12,7 +12,7 @@ import { UserEntry } from '../models/userEntry';
import { BaseDirectoryService } from './baseDirectory.service';
import { ConfigurationService } from './configuration.service';
import { DirectoryService } from './directory.service';
import { IDirectoryService } from './directory.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { LogService } from 'jslib/abstractions/log.service';
@@ -29,7 +29,7 @@ enum UserSetType {
ExcludeGroup,
}
export class AzureDirectoryService extends BaseDirectoryService implements DirectoryService {
export class AzureDirectoryService extends BaseDirectoryService implements IDirectoryService {
private client: graph.Client;
private dirConfig: AzureConfiguration;
private syncConfig: SyncConfiguration;
@@ -203,7 +203,8 @@ export class AzureDirectoryService extends BaseDirectoryService implements Direc
const pieces = parts[1].split(',');
if (keyword === 'excludeadministrativeunit' || keyword === 'includeadministrativeunit') {
for (const p of pieces) {
const auMembers = await this.client.api(`https://graph.microsoft.com/beta/administrativeUnits/${p}/members`).get();
const auMembers = await this.client
.api(`https://graph.microsoft.com/beta/administrativeUnits/${p}/members`).get();
for (const auMember of auMembers.value) {
if (auMember['@odata.type'] === '#microsoft.graph.group') {
set.add(auMember.displayName.toLowerCase());

View File

@@ -1,6 +1,6 @@
import { GroupEntry } from '../models/groupEntry';
import { UserEntry } from '../models/userEntry';
export interface DirectoryService {
export interface IDirectoryService {
getEntries(force: boolean, test: boolean): Promise<[GroupEntry[], UserEntry[]]>;
}

View File

@@ -13,12 +13,12 @@ import { UserEntry } from '../models/userEntry';
import { BaseDirectoryService } from './baseDirectory.service';
import { ConfigurationService } from './configuration.service';
import { DirectoryService } from './directory.service';
import { IDirectoryService } from './directory.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { LogService } from 'jslib/abstractions/log.service';
export class GSuiteDirectoryService extends BaseDirectoryService implements DirectoryService {
export class GSuiteDirectoryService extends BaseDirectoryService implements IDirectoryService {
private client: JWT;
private service: admin_directory_v1.Admin;
private authParams: any;

View File

@@ -9,7 +9,7 @@ import { SyncConfiguration } from '../models/syncConfiguration';
import { UserEntry } from '../models/userEntry';
import { ConfigurationService } from './configuration.service';
import { DirectoryService } from './directory.service';
import { IDirectoryService } from './directory.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { LogService } from 'jslib/abstractions/log.service';
@@ -18,7 +18,7 @@ import { Utils } from 'jslib/misc/utils';
const UserControlAccountDisabled = 2;
export class LdapDirectoryService implements DirectoryService {
export class LdapDirectoryService implements IDirectoryService {
private client: ldap.Client;
private dirConfig: LdapConfiguration;
private syncConfig: SyncConfiguration;

View File

@@ -0,0 +1,29 @@
import * as fs from 'fs';
import * as lock from 'proper-lockfile';
import { LogService } from 'jslib/abstractions/log.service';
import { LowdbStorageService as LowdbStorageServiceBase } from 'jslib/services/lowdbStorage.service';
import { Utils } from 'jslib/misc/utils';
export class LowdbStorageService extends LowdbStorageServiceBase {
constructor(logService: LogService, defaults?: any, dir?: string, allowCache = false, private requireLock = false) {
super(logService, defaults, dir, allowCache);
}
protected async lockDbFile<T>(action: () => T): Promise<T> {
if (this.requireLock && !Utils.isNullOrWhitespace(this.dataFilePath)) {
this.logService.info('acquiring db file lock');
return await lock.lock(this.dataFilePath, { retries: 3 }).then(release => {
try {
return action();
} finally {
release();
}
});
} else {
return action();
}
}
}

View File

@@ -7,14 +7,14 @@ import { UserEntry } from '../models/userEntry';
import { BaseDirectoryService } from './baseDirectory.service';
import { ConfigurationService } from './configuration.service';
import { DirectoryService } from './directory.service';
import { IDirectoryService } from './directory.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { LogService } from 'jslib/abstractions/log.service';
import * as https from 'https';
export class OktaDirectoryService extends BaseDirectoryService implements DirectoryService {
export class OktaDirectoryService extends BaseDirectoryService implements IDirectoryService {
private dirConfig: OktaConfiguration;
private syncConfig: SyncConfiguration;

View File

@@ -7,7 +7,7 @@ import { UserEntry } from '../models/userEntry';
import { BaseDirectoryService } from './baseDirectory.service';
import { ConfigurationService } from './configuration.service';
import { DirectoryService } from './directory.service';
import { IDirectoryService } from './directory.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { LogService } from 'jslib/abstractions/log.service';
@@ -15,7 +15,7 @@ import { LogService } from 'jslib/abstractions/log.service';
// Basic email validation: something@something.something
const ValidEmailRegex = /^\S+@\S+\.\S+$/;
export class OneLoginDirectoryService extends BaseDirectoryService implements DirectoryService {
export class OneLoginDirectoryService extends BaseDirectoryService implements IDirectoryService {
private dirConfig: OneLoginConfiguration;
private syncConfig: SyncConfiguration;
private accessToken: string;

View File

@@ -18,7 +18,7 @@ import { Utils } from 'jslib/misc/utils';
import { AzureDirectoryService } from './azure-directory.service';
import { ConfigurationService } from './configuration.service';
import { DirectoryService } from './directory.service';
import { IDirectoryService } from './directory.service';
import { GSuiteDirectoryService } from './gsuite-directory.service';
import { LdapDirectoryService } from './ldap-directory.service';
import { OktaDirectoryService } from './okta-directory.service';
@@ -121,7 +121,7 @@ export class SyncService {
return allUsers;
}
private getDirectoryService(): DirectoryService {
private getDirectoryService(): IDirectoryService {
switch (this.dirType) {
case DirectoryType.GSuite:
return new GSuiteDirectoryService(this.configurationService, this.logService, this.i18nService);