Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial implementation #703

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions jslib/angular/src/services/jslib-services.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { AppIdService as AppIdServiceAbstraction } from "@/jslib/common/src/abst
import { BroadcasterService as BroadcasterServiceAbstraction } from "@/jslib/common/src/abstractions/broadcaster.service";
import { CryptoService as CryptoServiceAbstraction } from "@/jslib/common/src/abstractions/crypto.service";
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@/jslib/common/src/abstractions/cryptoFunction.service";
import { DirectoryFactoryAbstraction } from "@/jslib/common/src/abstractions/directory-factory.service";
import { EnvironmentService as EnvironmentServiceAbstraction } from "@/jslib/common/src/abstractions/environment.service";
import { I18nService as I18nServiceAbstraction } from "@/jslib/common/src/abstractions/i18n.service";
import { LogService } from "@/jslib/common/src/abstractions/log.service";
Expand All @@ -19,13 +20,17 @@ import { Account } from "@/jslib/common/src/models/domain/account";
import { GlobalState } from "@/jslib/common/src/models/domain/globalState";
import { ApiService } from "@/jslib/common/src/services/api.service";
import { AppIdService } from "@/jslib/common/src/services/appId.service";
import { BatchRequestBuilder } from "@/jslib/common/src/services/batch-requests.service";
import { ConsoleLogService } from "@/jslib/common/src/services/consoleLog.service";
import { CryptoService } from "@/jslib/common/src/services/crypto.service";
import { EnvironmentService } from "@/jslib/common/src/services/environment.service";
import { SingleRequestBuilder } from "@/jslib/common/src/services/single-request.service";
import { StateService } from "@/jslib/common/src/services/state.service";
import { StateMigrationService } from "@/jslib/common/src/services/stateMigration.service";
import { TokenService } from "@/jslib/common/src/services/token.service";

import { DirectoryFactoryService } from "@/src/services/directory-factory.service";

import {
SafeInjectionToken,
SECURE_STORAGE,
Expand Down Expand Up @@ -138,6 +143,21 @@ import { ValidationService } from "./validation.service";
),
deps: [StorageServiceAbstraction, SECURE_STORAGE],
}),
safeProvider({
provide: BatchRequestBuilder,
useClass: BatchRequestBuilder,
useAngularDecorators: true,
}),
safeProvider({
provide: SingleRequestBuilder,
useClass: SingleRequestBuilder,
useAngularDecorators: true,
}),
safeProvider({
provide: DirectoryFactoryAbstraction,
useClass: DirectoryFactoryService,
useAngularDecorators: true,
}),
] satisfies SafeProvider[],
})
export class JslibServicesModule {}
6 changes: 3 additions & 3 deletions jslib/common/src/abstractions/api.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { ApiTokenRequest } from "../models/request/identityToken/apiTokenRequest
import { PasswordTokenRequest } from "../models/request/identityToken/passwordTokenRequest";
import { SsoTokenRequest } from "../models/request/identityToken/ssoTokenRequest";
import { OrganizationImportRequest } from "../models/request/organizationImportRequest";
import { IdentityCaptchaResponse } from '../models/response/identityCaptchaResponse';
import { IdentityTokenResponse } from '../models/response/identityTokenResponse';
import { IdentityTwoFactorResponse } from '../models/response/identityTwoFactorResponse';
import { IdentityCaptchaResponse } from "../models/response/identityCaptchaResponse";
import { IdentityTokenResponse } from "../models/response/identityTokenResponse";
import { IdentityTwoFactorResponse } from "../models/response/identityTwoFactorResponse";

export abstract class ApiService {
postIdentityToken: (
Expand Down
15 changes: 15 additions & 0 deletions jslib/common/src/abstractions/directory-factory.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { DirectoryType } from "@/src/enums/directoryType";
import { IDirectoryService } from "@/src/services/directory.service";

import { I18nService } from "./i18n.service";
import { LogService } from "./log.service";
import { StateService } from "./state.service";

export abstract class DirectoryFactoryAbstraction {
abstract createService(
type: DirectoryType,
logService: LogService,
i18nService: I18nService,
stateService: StateService,
): IDirectoryService;
}
13 changes: 13 additions & 0 deletions jslib/common/src/abstractions/request-builder.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { GroupEntry } from "@/src/models/groupEntry";
import { UserEntry } from "@/src/models/userEntry";

import { OrganizationImportRequest } from "../models/request/organizationImportRequest";

export abstract class RequestBuilderAbstratction {
buildRequest: (
groups: GroupEntry[],
users: UserEntry[],
removeDisabled: boolean,
overwriteExisting: boolean,
) => OrganizationImportRequest[];
}
55 changes: 55 additions & 0 deletions jslib/common/src/services/batch-requests.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { GroupEntry } from "@/src/models/groupEntry";
import { UserEntry } from "@/src/models/userEntry";

import { BatchRequestBuilder } from "./batch-requests.service";
import { SingleRequestBuilder } from "./single-request.service";

describe("BatchingService", () => {
let batchRequestBuilder: BatchRequestBuilder;
let singleRequestBuilder: SingleRequestBuilder;

function userSimulator(userCount: number) {
const simulatedArray: UserEntry[] = [];
for (let i = 0; i <= userCount; i++) {
simulatedArray.push(new UserEntry());
}
return simulatedArray;
}

function groupSimulator(groupCount: number) {
const simulatedArray: GroupEntry[] = [];
for (let i = 0; i <= groupCount; i++) {
simulatedArray.push(new GroupEntry());
}
return simulatedArray;
}

beforeEach(async () => {
batchRequestBuilder = new BatchRequestBuilder();
singleRequestBuilder = new SingleRequestBuilder();
});

it("BatchRequestBuilder batches requests for > 2000 users", () => {
const mockGroups = groupSimulator(11000);
const mockUsers = userSimulator(11000);

const requests = batchRequestBuilder.buildRequest(mockGroups, mockUsers, true, true);

expect(requests.length).toEqual(12);
});

it("SingleRequestBuilder returns single request for 200 users", () => {
const mockGroups = groupSimulator(200);
const mockUsers = userSimulator(200);

const requests = singleRequestBuilder.buildRequest(mockGroups, mockUsers, true, true);

expect(requests.length).toEqual(1);
});

it("BatchRequestBuilder retuns an empty array when there are no users or groups", () => {
const requests = batchRequestBuilder.buildRequest([], [], true, true);

expect(requests).toEqual([]);
});
});
65 changes: 65 additions & 0 deletions jslib/common/src/services/batch-requests.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { OrganizationImportRequest } from "@/jslib/common/src/models/request/organizationImportRequest";

import { GroupEntry } from "@/src/models/groupEntry";
import { UserEntry } from "@/src/models/userEntry";

import { RequestBuilderAbstratction } from "../abstractions/request-builder.service";

export class BatchRequestBuilder implements RequestBuilderAbstratction {
batchSize = 2000;

buildRequest(
groups: GroupEntry[],
users: UserEntry[],
removeDisabled: boolean,
overwriteExisting: boolean,
): OrganizationImportRequest[] {
const requests: OrganizationImportRequest[] = [];

if (users.length > 0) {
const usersRequest = users.map((u) => {
return {
email: u.email,
externalId: u.externalId,
deleted: u.deleted || (removeDisabled && u.disabled),
};
});

// Partition users
for (let i = 0; i < usersRequest.length; i += this.batchSize) {
const u = usersRequest.slice(i, i + this.batchSize);
const req = new OrganizationImportRequest({
groups: [],
users: u,
largeImport: true,
overwriteExisting,
});
requests.push(req);
}
}

if (groups.length > 0) {
const groupRequest = groups.map((g) => {
return {
name: g.name,
externalId: g.externalId,
memberExternalIds: Array.from(g.userMemberExternalIds),
};
});

// Partition groups
for (let i = 0; i < groupRequest.length; i += this.batchSize) {
const g = groupRequest.slice(i, i + this.batchSize);
const req = new OrganizationImportRequest({
groups: g,
users: [],
largeImport: true,
overwriteExisting,
});
requests.push(req);
}
}

return requests;
}
}
36 changes: 36 additions & 0 deletions jslib/common/src/services/single-request.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { OrganizationImportRequest } from "@/jslib/common/src/models/request/organizationImportRequest";

import { GroupEntry } from "@/src/models/groupEntry";
import { UserEntry } from "@/src/models/userEntry";

import { RequestBuilderAbstratction } from "../abstractions/request-builder.service";

export class SingleRequestBuilder implements RequestBuilderAbstratction {
buildRequest(
groups: GroupEntry[],
users: UserEntry[],
removeDisabled: boolean,
overwriteExisting: boolean,
): OrganizationImportRequest[] {
return [
new OrganizationImportRequest({
groups: (groups ?? []).map((g) => {
return {
name: g.name,
externalId: g.externalId,
memberExternalIds: Array.from(g.userMemberExternalIds),
};
}),
users: (users ?? []).map((u) => {
return {
email: u.email,
externalId: u.externalId,
deleted: u.deleted || (removeDisabled && u.disabled),
};
}),
overwriteExisting: overwriteExisting,
largeImport: false,
}),
];
}
}
6 changes: 6 additions & 0 deletions src/app/services/services.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { AppIdService as AppIdServiceAbstraction } from "@/jslib/common/src/abst
import { BroadcasterService as BroadcasterServiceAbstraction } from "@/jslib/common/src/abstractions/broadcaster.service";
import { CryptoService as CryptoServiceAbstraction } from "@/jslib/common/src/abstractions/crypto.service";
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@/jslib/common/src/abstractions/cryptoFunction.service";
import { DirectoryFactoryAbstraction } from "@/jslib/common/src/abstractions/directory-factory.service";
import { EnvironmentService as EnvironmentServiceAbstraction } from "@/jslib/common/src/abstractions/environment.service";
import { I18nService as I18nServiceAbstraction } from "@/jslib/common/src/abstractions/i18n.service";
import { LogService as LogServiceAbstraction } from "@/jslib/common/src/abstractions/log.service";
Expand All @@ -16,7 +17,9 @@ import { StorageService as StorageServiceAbstraction } from "@/jslib/common/src/
import { TokenService as TokenServiceAbstraction } from "@/jslib/common/src/abstractions/token.service";
import { StateFactory } from "@/jslib/common/src/factories/stateFactory";
import { GlobalState } from "@/jslib/common/src/models/domain/globalState";
import { BatchRequestBuilder as BatchRequestAbstraction } from "@/jslib/common/src/services/batch-requests.service";
import { ContainerService } from "@/jslib/common/src/services/container.service";
import { SingleRequestBuilder as SingleRequestAbstraction } from "@/jslib/common/src/services/single-request.service";
import { ElectronLogService } from "@/jslib/electron/src/services/electronLog.service";
import { ElectronPlatformUtilsService } from "@/jslib/electron/src/services/electronPlatformUtils.service";
import { ElectronRendererMessagingService } from "@/jslib/electron/src/services/electronRendererMessaging.service";
Expand Down Expand Up @@ -175,6 +178,9 @@ export function initFactory(
I18nServiceAbstraction,
EnvironmentServiceAbstraction,
StateServiceAbstraction,
BatchRequestAbstraction,
SingleRequestAbstraction,
DirectoryFactoryAbstraction,
],
}),
safeProvider(AuthGuardService),
Expand Down
9 changes: 9 additions & 0 deletions src/bwdc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import { LogLevelType } from "@/jslib/common/src/enums/logLevelType";
import { StateFactory } from "@/jslib/common/src/factories/stateFactory";
import { GlobalState } from "@/jslib/common/src/models/domain/globalState";
import { AppIdService } from "@/jslib/common/src/services/appId.service";
import { BatchRequestBuilder } from "@/jslib/common/src/services/batch-requests.service";
import { ContainerService } from "@/jslib/common/src/services/container.service";
import { CryptoService } from "@/jslib/common/src/services/crypto.service";
import { EnvironmentService } from "@/jslib/common/src/services/environment.service";
import { NoopMessagingService } from "@/jslib/common/src/services/noopMessaging.service";
import { SingleRequestBuilder } from "@/jslib/common/src/services/single-request.service";
import { TokenService } from "@/jslib/common/src/services/token.service";
import { CliPlatformUtilsService } from "@/jslib/node/src/cli/services/cliPlatformUtils.service";
import { ConsoleLogService } from "@/jslib/node/src/cli/services/consoleLog.service";
Expand All @@ -20,6 +22,7 @@ import { NodeCryptoFunctionService } from "@/jslib/node/src/services/nodeCryptoF
import { Account } from "./models/account";
import { Program } from "./program";
import { AuthService } from "./services/auth.service";
import { DirectoryFactoryService } from "./services/directory-factory.service";
import { I18nService } from "./services/i18n.service";
import { KeytarSecureStorageService } from "./services/keytarSecureStorage.service";
import { LowdbStorageService } from "./services/lowdbStorage.service";
Expand Down Expand Up @@ -51,6 +54,9 @@ export class Main {
syncService: SyncService;
stateService: StateService;
stateMigrationService: StateMigrationService;
directoryFactoryService: DirectoryFactoryService;
batchRequestBuilder: BatchRequestBuilder;
singleRequestBuilder: SingleRequestBuilder;

constructor() {
const applicationName = "Bitwarden Directory Connector";
Expand Down Expand Up @@ -154,6 +160,9 @@ export class Main {
this.i18nService,
this.environmentService,
this.stateService,
this.batchRequestBuilder,
this.singleRequestBuilder,
this.directoryFactoryService,
);

this.program = new Program(this);
Expand Down
4 changes: 4 additions & 0 deletions src/models/hashResult.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export class HashResult {
hash: string;
hashLegacy: string;
}
36 changes: 36 additions & 0 deletions src/services/directory-factory.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { DirectoryFactoryAbstraction } from "@/jslib/common/src/abstractions/directory-factory.service";
import { I18nService } from "@/jslib/common/src/abstractions/i18n.service";
import { LogService } from "@/jslib/common/src/abstractions/log.service";

import { StateService } from "../abstractions/state.service";
import { DirectoryType } from "../enums/directoryType";

import { AzureDirectoryService } from "./azure-directory.service";
import { GSuiteDirectoryService } from "./gsuite-directory.service";
import { LdapDirectoryService } from "./ldap-directory.service";
import { OktaDirectoryService } from "./okta-directory.service";
import { OneLoginDirectoryService } from "./onelogin-directory.service";

export class DirectoryFactoryService implements DirectoryFactoryAbstraction {
createService(
directoryType: DirectoryType,
logService: LogService,
i18nService: I18nService,
stateService: StateService,
) {
switch (directoryType) {
case DirectoryType.GSuite:
return new GSuiteDirectoryService(logService, i18nService, stateService);
case DirectoryType.AzureActiveDirectory:
return new AzureDirectoryService(logService, i18nService, stateService);
case DirectoryType.Ldap:
return new LdapDirectoryService(logService, i18nService, stateService);
case DirectoryType.Okta:
return new OktaDirectoryService(logService, i18nService, stateService);
case DirectoryType.OneLogin:
return new OneLoginDirectoryService(logService, i18nService, stateService);
default:
return null;
}
}
}
Loading
Loading