mirror of
https://github.com/bitwarden/directory-connector
synced 2025-12-05 23:53:21 +00:00
Compare commits
4 Commits
jmccannon/
...
v2.9.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b90694d17a | ||
|
|
b1b0d858ca | ||
|
|
326f11be19 | ||
|
|
996364f2dd |
2
jslib
2
jslib
Submodule jslib updated: d7682cde3b...55a9ea9e18
@@ -21,7 +21,6 @@ import { SyncService } from '../../services/sync.service';
|
|||||||
import { BroadcasterService } from 'jslib-angular/services/broadcaster.service';
|
import { BroadcasterService } from 'jslib-angular/services/broadcaster.service';
|
||||||
import { ValidationService } from 'jslib-angular/services/validation.service';
|
import { ValidationService } from 'jslib-angular/services/validation.service';
|
||||||
|
|
||||||
import { ApiService } from 'jslib-common/services/api.service';
|
|
||||||
import { ApiKeyService } from 'jslib-common/services/apiKey.service';
|
import { ApiKeyService } from 'jslib-common/services/apiKey.service';
|
||||||
import { AppIdService } from 'jslib-common/services/appId.service';
|
import { AppIdService } from 'jslib-common/services/appId.service';
|
||||||
import { ConstantsService } from 'jslib-common/services/constants.service';
|
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||||
@@ -55,6 +54,7 @@ import { StorageService as StorageServiceAbstraction } from 'jslib-common/abstra
|
|||||||
import { TokenService as TokenServiceAbstraction } from 'jslib-common/abstractions/token.service';
|
import { TokenService as TokenServiceAbstraction } from 'jslib-common/abstractions/token.service';
|
||||||
import { UserService as UserServiceAbstraction } from 'jslib-common/abstractions/user.service';
|
import { UserService as UserServiceAbstraction } from 'jslib-common/abstractions/user.service';
|
||||||
|
|
||||||
|
import { ApiService, refreshToken } from '../../services/api.service';
|
||||||
import { AuthService } from '../../services/auth.service';
|
import { AuthService } from '../../services/auth.service';
|
||||||
|
|
||||||
const logService = new ElectronLogService();
|
const logService = new ElectronLogService();
|
||||||
@@ -70,7 +70,7 @@ const cryptoService = new CryptoService(storageService, secureStorageService, cr
|
|||||||
platformUtilsService, logService);
|
platformUtilsService, logService);
|
||||||
const appIdService = new AppIdService(storageService);
|
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, refreshTokenCallback,
|
||||||
async (expired: boolean) => messagingService.send('logout', { expired: expired }));
|
async (expired: boolean) => messagingService.send('logout', { expired: expired }));
|
||||||
const environmentService = new EnvironmentService(apiService, storageService, null);
|
const environmentService = new EnvironmentService(apiService, storageService, null);
|
||||||
const userService = new UserService(tokenService, storageService);
|
const userService = new UserService(tokenService, storageService);
|
||||||
@@ -86,6 +86,10 @@ const policyService = new PolicyService(userService, storageService);
|
|||||||
|
|
||||||
containerService.attachToWindow(window);
|
containerService.attachToWindow(window);
|
||||||
|
|
||||||
|
function refreshTokenCallback(): Promise<any> {
|
||||||
|
return refreshToken(apiKeyService, authService);
|
||||||
|
}
|
||||||
|
|
||||||
export function initFactory(): Function {
|
export function initFactory(): Function {
|
||||||
return async () => {
|
return async () => {
|
||||||
await environmentService.setUrlsFromStorage();
|
await environmentService.setUrlsFromStorage();
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { ConfigurationService } from './services/configuration.service';
|
|||||||
import { I18nService } from './services/i18n.service';
|
import { I18nService } from './services/i18n.service';
|
||||||
import { KeytarSecureStorageService } from './services/keytarSecureStorage.service';
|
import { KeytarSecureStorageService } from './services/keytarSecureStorage.service';
|
||||||
import { LowdbStorageService } from './services/lowdbStorage.service';
|
import { LowdbStorageService } from './services/lowdbStorage.service';
|
||||||
|
import { NodeApiService } from './services/nodeApi.service';
|
||||||
import { SyncService } from './services/sync.service';
|
import { SyncService } from './services/sync.service';
|
||||||
|
|
||||||
import { CliPlatformUtilsService } from 'jslib-node/cli/services/cliPlatformUtils.service';
|
import { CliPlatformUtilsService } from 'jslib-node/cli/services/cliPlatformUtils.service';
|
||||||
@@ -25,11 +26,11 @@ import { NoopMessagingService } from 'jslib-common/services/noopMessaging.servic
|
|||||||
import { PasswordGenerationService } from 'jslib-common/services/passwordGeneration.service';
|
import { PasswordGenerationService } from 'jslib-common/services/passwordGeneration.service';
|
||||||
import { TokenService } from 'jslib-common/services/token.service';
|
import { TokenService } from 'jslib-common/services/token.service';
|
||||||
import { UserService } from 'jslib-common/services/user.service';
|
import { UserService } from 'jslib-common/services/user.service';
|
||||||
import { NodeApiService } from 'jslib-node/services/nodeApi.service';
|
|
||||||
|
|
||||||
import { StorageService as StorageServiceAbstraction } from 'jslib-common/abstractions/storage.service';
|
import { StorageService as StorageServiceAbstraction } from 'jslib-common/abstractions/storage.service';
|
||||||
|
|
||||||
import { Program } from './program';
|
import { Program } from './program';
|
||||||
|
import { refreshToken } from './services/api.service';
|
||||||
|
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
const packageJson = require('./package.json');
|
const packageJson = require('./package.json');
|
||||||
@@ -90,7 +91,7 @@ export class Main {
|
|||||||
this.appIdService = new AppIdService(this.storageService);
|
this.appIdService = new AppIdService(this.storageService);
|
||||||
this.tokenService = new TokenService(this.storageService);
|
this.tokenService = new TokenService(this.storageService);
|
||||||
this.messagingService = new NoopMessagingService();
|
this.messagingService = new NoopMessagingService();
|
||||||
this.apiService = new NodeApiService(this.tokenService, this.platformUtilsService,
|
this.apiService = new NodeApiService(this.tokenService, this.platformUtilsService, this.refreshTokenCallback,
|
||||||
async (expired: boolean) => await this.logout());
|
async (expired: boolean) => await this.logout());
|
||||||
this.environmentService = new EnvironmentService(this.apiService, this.storageService, null);
|
this.environmentService = new EnvironmentService(this.apiService, this.storageService, null);
|
||||||
this.apiKeyService = new ApiKeyService(this.tokenService, this.storageService);
|
this.apiKeyService = new ApiKeyService(this.tokenService, this.storageService);
|
||||||
@@ -117,6 +118,10 @@ export class Main {
|
|||||||
await this.apiKeyService.clear();
|
await this.apiKeyService.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshTokenCallback() {
|
||||||
|
return refreshToken(this.apiKeyService, this.authService);
|
||||||
|
}
|
||||||
|
|
||||||
private async init() {
|
private async init() {
|
||||||
await this.storageService.init();
|
await this.storageService.init();
|
||||||
this.containerService.attachToWindow(global);
|
this.containerService.attachToWindow(global);
|
||||||
|
|||||||
@@ -441,6 +441,19 @@
|
|||||||
"sync": {
|
"sync": {
|
||||||
"message": "Sync"
|
"message": "Sync"
|
||||||
},
|
},
|
||||||
|
"duplicateEmails": {
|
||||||
|
"message": "Emails must be unique. Multiple entries pulled with the following emails:",
|
||||||
|
"desription": "Error message displayed when duplicate email addresses are synced. Followed by a list of duplicate emails."
|
||||||
|
},
|
||||||
|
"andMore": {
|
||||||
|
"message": "and $NUMBER$ more...",
|
||||||
|
"placeholders": {
|
||||||
|
"NUMBER": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "10"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"ldapEncrypted": {
|
"ldapEncrypted": {
|
||||||
"message": "This server uses an encrypted connection"
|
"message": "This server uses an encrypted connection"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export class Main {
|
|||||||
this.messagingMain.onMessage(message);
|
this.messagingMain.onMessage(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.keytarStorageListener = new KeytarStorageListener('Bitwarden Directory Connector');
|
this.keytarStorageListener = new KeytarStorageListener('Bitwarden Directory Connector', null);
|
||||||
}
|
}
|
||||||
|
|
||||||
bootstrap() {
|
bootstrap() {
|
||||||
|
|||||||
@@ -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.9.2",
|
"version": "2.9.3",
|
||||||
"author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)",
|
"author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)",
|
||||||
"homepage": "https://bitwarden.com",
|
"homepage": "https://bitwarden.com",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
|
|||||||
30
src/services/api.service.ts
Normal file
30
src/services/api.service.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { ApiKeyService } from 'jslib-common/abstractions/apiKey.service';
|
||||||
|
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
||||||
|
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||||
|
import { TokenService } from 'jslib-common/abstractions/token.service';
|
||||||
|
|
||||||
|
import { ApiService as ApiServiceBase } from 'jslib-common/services/api.service';
|
||||||
|
|
||||||
|
export async function refreshToken(apiKeyService: ApiKeyService, authService: AuthService) {
|
||||||
|
try {
|
||||||
|
const clientId = await apiKeyService.getClientId();
|
||||||
|
const clientSecret = await apiKeyService.getClientSecret();
|
||||||
|
if (clientId != null && clientSecret != null) {
|
||||||
|
await authService.logInApiKey(clientId, clientSecret);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return Promise.reject(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ApiService extends ApiServiceBase {
|
||||||
|
constructor(tokenService: TokenService, platformUtilsService: PlatformUtilsService,
|
||||||
|
private refreshTokenCallback: () => Promise<void>, logoutCallback: (expired: boolean) => Promise<void>,
|
||||||
|
customUserAgent: string = null) {
|
||||||
|
super(tokenService, platformUtilsService, logoutCallback, customUserAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
doRefreshToken(): Promise<void> {
|
||||||
|
return this.refreshTokenCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,6 +36,11 @@ export class AuthService extends AuthServiceBase {
|
|||||||
return await super.logInApiKey(clientId, clientSecret);
|
return await super.logInApiKey(clientId, clientSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async logOut(callback: Function) {
|
||||||
|
this.apiKeyService.clear();
|
||||||
|
super.logOut(callback);
|
||||||
|
}
|
||||||
|
|
||||||
private async organizationLogInHelper(clientId: string, clientSecret: string) {
|
private async organizationLogInHelper(clientId: string, clientSecret: string) {
|
||||||
const appId = await this.appIdService.getAppId();
|
const appId = await this.appIdService.getAppId();
|
||||||
const deviceRequest = new DeviceRequest(appId, this.platformUtilsService);
|
const deviceRequest = new DeviceRequest(appId, this.platformUtilsService);
|
||||||
@@ -49,7 +54,7 @@ export class AuthService extends AuthServiceBase {
|
|||||||
const tokenResponse = response as IdentityTokenResponse;
|
const tokenResponse = response as IdentityTokenResponse;
|
||||||
result.resetMasterPassword = tokenResponse.resetMasterPassword;
|
result.resetMasterPassword = tokenResponse.resetMasterPassword;
|
||||||
await this.tokenService.setToken(tokenResponse.accessToken);
|
await this.tokenService.setToken(tokenResponse.accessToken);
|
||||||
await this.apiKeyService.setInformation(clientId);
|
await this.apiKeyService.setInformation(clientId, clientSecret);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ export class KeytarSecureStorageService implements StorageService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async has(key: string): Promise<boolean> {
|
||||||
|
return (await this.get(key)) != null;
|
||||||
|
}
|
||||||
|
|
||||||
save(key: string, obj: any): Promise<any> {
|
save(key: string, obj: any): Promise<any> {
|
||||||
return setPassword(this.serviceName, key, JSON.stringify(obj));
|
return setPassword(this.serviceName, key, JSON.stringify(obj));
|
||||||
}
|
}
|
||||||
|
|||||||
16
src/services/nodeApi.service.ts
Normal file
16
src/services/nodeApi.service.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||||
|
import { TokenService } from 'jslib-common/abstractions/token.service';
|
||||||
|
|
||||||
|
import { NodeApiService as NodeApiServiceBase } from 'jslib-node/services/nodeApi.service';
|
||||||
|
|
||||||
|
export class NodeApiService extends NodeApiServiceBase {
|
||||||
|
constructor(tokenService: TokenService, platformUtilsService: PlatformUtilsService,
|
||||||
|
private refreshTokenCallback: () => Promise<void>, logoutCallback: (expired: boolean) => Promise<void>,
|
||||||
|
customUserAgent: string = null) {
|
||||||
|
super(tokenService, platformUtilsService, logoutCallback, customUserAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
doRefreshToken(): Promise<void> {
|
||||||
|
return this.refreshTokenCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -55,6 +55,14 @@ export class SyncService {
|
|||||||
this.flattenUsersToGroups(groups, groups);
|
this.flattenUsersToGroups(groups, groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const duplicateEmails = this.findDuplicateUserEmails(users);
|
||||||
|
if (duplicateEmails.length > 0) {
|
||||||
|
const emailsMessage = duplicateEmails.length < 4 ?
|
||||||
|
duplicateEmails.join('\n') :
|
||||||
|
duplicateEmails.slice(0, 3).join('\n') + '\n' + this.i18nService.t('andMore', `${duplicateEmails.length - 3}`);
|
||||||
|
throw new Error(this.i18nService.t('duplicateEmails') + '\n' + emailsMessage);
|
||||||
|
}
|
||||||
|
|
||||||
if (test || (!syncConfig.overwriteExisting &&
|
if (test || (!syncConfig.overwriteExisting &&
|
||||||
(groups == null || groups.length === 0) && (users == null || users.length === 0))) {
|
(groups == null || groups.length === 0) && (users == null || users.length === 0))) {
|
||||||
if (!test) {
|
if (!test) {
|
||||||
@@ -108,6 +116,19 @@ export class SyncService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private findDuplicateUserEmails(users: UserEntry[]) {
|
||||||
|
const duplicatedEmails = new Array<string>();
|
||||||
|
users.reduce((agg, user) => {
|
||||||
|
if (agg.includes(user.email) && !duplicatedEmails.includes(user.email)) {
|
||||||
|
duplicatedEmails.push(user.email);
|
||||||
|
} else {
|
||||||
|
agg.push(user.email);
|
||||||
|
}
|
||||||
|
return agg;
|
||||||
|
}, new Array<string>());
|
||||||
|
return duplicatedEmails;
|
||||||
|
}
|
||||||
|
|
||||||
private filterUnsupportedUsers(users: UserEntry[]): UserEntry[] {
|
private filterUnsupportedUsers(users: UserEntry[]): UserEntry[] {
|
||||||
return users == null ? null : users.filter(u => u.email?.length <= 256);
|
return users == null ? null : users.filter(u => u.email?.length <= 256);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user