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

[PM-26672] Add Google Workspace integration tests to CI pipeline (#909)

- reorganize integration test files to allow for future additions
- add Google Workspace integration tests to the Github workflow
- refactor to run tests selective based on changed files and use
  Azure Key Vault
This commit is contained in:
Thomas Rittson
2025-11-12 06:03:37 +10:00
committed by GitHub
parent ab436551de
commit a44eb28be8
15 changed files with 163 additions and 79 deletions

View File

@@ -1,5 +1,5 @@
import { DirectoryType } from "@/src/enums/directoryType";
import { IDirectoryService } from "@/src/services/directory.service";
import { IDirectoryService } from "@/src/services/directory-services/directory.service";
export abstract class DirectoryFactoryService {
abstract createService(type: DirectoryType): IDirectoryService;

View File

@@ -768,5 +768,8 @@
},
"launchWebVault": {
"message": "Launch Web Vault"
},
"authenticationFailed": {
"message": "Authentication failed"
}
}

View File

@@ -5,11 +5,11 @@ import { DirectoryFactoryService } from "../abstractions/directory-factory.servi
import { StateService } from "../abstractions/state.service";
import { DirectoryType } from "../enums/directoryType";
import { EntraIdDirectoryService } from "./entra-id-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";
import { EntraIdDirectoryService } from "./directory-services/entra-id-directory.service";
import { GSuiteDirectoryService } from "./directory-services/gsuite-directory.service";
import { LdapDirectoryService } from "./directory-services/ldap-directory.service";
import { OktaDirectoryService } from "./directory-services/okta-directory.service";
import { OneLoginDirectoryService } from "./directory-services/onelogin-directory.service";
export class DefaultDirectoryFactoryService implements DirectoryFactoryService {
constructor(

View File

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

View File

@@ -7,14 +7,14 @@ import * as graphType from "@microsoft/microsoft-graph-types";
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 { EntraIdConfiguration } from "../models/entraIdConfiguration";
import { GroupEntry } from "../models/groupEntry";
import { SyncConfiguration } from "../models/syncConfiguration";
import { UserEntry } from "../models/userEntry";
import { StateService } from "../../abstractions/state.service";
import { DirectoryType } from "../../enums/directoryType";
import { EntraIdConfiguration } from "../../models/entraIdConfiguration";
import { GroupEntry } from "../../models/groupEntry";
import { SyncConfiguration } from "../../models/syncConfiguration";
import { UserEntry } from "../../models/userEntry";
import { BaseDirectoryService } from "../baseDirectory.service";
import { BaseDirectoryService } from "./baseDirectory.service";
import { IDirectoryService } from "./directory.service";
const EntraIdPublicIdentityAuthority = "login.microsoftonline.com";

View File

@@ -1,18 +1,18 @@
import { config as dotenvConfig } from "dotenv";
import { mock, MockProxy } from "jest-mock-extended";
import { I18nService } from "../../jslib/common/src/abstractions/i18n.service";
import { LogService } from "../../jslib/common/src/abstractions/log.service";
import { I18nService } from "../../../jslib/common/src/abstractions/i18n.service";
import { LogService } from "../../../jslib/common/src/abstractions/log.service";
import {
getGSuiteConfiguration,
getSyncConfiguration,
} from "../../utils/google-workspace/config-fixtures";
import { groupFixtures } from "../../utils/google-workspace/group-fixtures";
import { userFixtures } from "../../utils/google-workspace/user-fixtures";
import { DirectoryType } from "../enums/directoryType";
} from "../../../utils/google-workspace/config-fixtures";
import { groupFixtures } from "../../../utils/google-workspace/group-fixtures";
import { userFixtures } from "../../../utils/google-workspace/user-fixtures";
import { DirectoryType } from "../../enums/directoryType";
import { StateService } from "../state.service";
import { GSuiteDirectoryService } from "./gsuite-directory.service";
import { StateService } from "./state.service";
// These tests integrate with a test Google Workspace instance.
// Credentials are located in the shared Bitwarden collection for Directory Connector testing.
@@ -24,10 +24,13 @@ dotenvConfig({ path: "utils/.env" });
// These filters target integration test data.
// These should return data that matches the user and group fixtures exactly.
// There may be additional data present if not used.
const INTEGRATION_USER_FILTER =
"exclude:integration-user-a@bwrox.dev|orgUnitPath='/Integration testing'";
const INTEGRATION_USER_FILTER = "|orgUnitPath='/Integration testing'";
const INTEGRATION_GROUP_FILTER = "|name:Integration*";
// These tests are slow!
// Increase the default timeout from 5s to 15s
jest.setTimeout(15000);
describe("gsuiteDirectoryService", () => {
let logService: MockProxy<LogService>;
let i18nService: MockProxy<I18nService>;

View File

@@ -4,14 +4,14 @@ import { admin_directory_v1, google } from "googleapis";
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 { GroupEntry } from "../models/groupEntry";
import { GSuiteConfiguration } from "../models/gsuiteConfiguration";
import { SyncConfiguration } from "../models/syncConfiguration";
import { UserEntry } from "../models/userEntry";
import { StateService } from "../../abstractions/state.service";
import { DirectoryType } from "../../enums/directoryType";
import { GroupEntry } from "../../models/groupEntry";
import { GSuiteConfiguration } from "../../models/gsuiteConfiguration";
import { SyncConfiguration } from "../../models/syncConfiguration";
import { UserEntry } from "../../models/userEntry";
import { BaseDirectoryService } from "../baseDirectory.service";
import { BaseDirectoryService } from "./baseDirectory.service";
import { IDirectoryService } from "./directory.service";
export class GSuiteDirectoryService extends BaseDirectoryService implements IDirectoryService {
@@ -253,7 +253,15 @@ export class GSuiteDirectoryService extends BaseDirectoryService implements IDir
],
});
await this.client.authorize();
try {
await this.client.authorize();
} catch (error) {
// Catch and rethrow this to sanitize any sensitive info (e.g. private key) in the error message
this.logService.error(
`Google Workspace authentication failed: ${error?.name || "Unknown error"}`,
);
throw new Error(this.i18nService.t("authenticationFailed"));
}
this.authParams = {
auth: this.client,

View File

@@ -1,14 +1,17 @@
import { mock, MockProxy } from "jest-mock-extended";
import { I18nService } from "../../jslib/common/src/abstractions/i18n.service";
import { LogService } from "../../jslib/common/src/abstractions/log.service";
import { getLdapConfiguration, getSyncConfiguration } from "../../utils/openldap/config-fixtures";
import { groupFixtures } from "../../utils/openldap/group-fixtures";
import { userFixtures } from "../../utils/openldap/user-fixtures";
import { DirectoryType } from "../enums/directoryType";
import { I18nService } from "../../../jslib/common/src/abstractions/i18n.service";
import { LogService } from "../../../jslib/common/src/abstractions/log.service";
import {
getLdapConfiguration,
getSyncConfiguration,
} from "../../../utils/openldap/config-fixtures";
import { groupFixtures } from "../../../utils/openldap/group-fixtures";
import { userFixtures } from "../../../utils/openldap/user-fixtures";
import { DirectoryType } from "../../enums/directoryType";
import { StateService } from "../state.service";
import { LdapDirectoryService } from "./ldap-directory.service";
import { StateService } from "./state.service";
// These tests integrate with the OpenLDAP docker image and seed data located in the openldap folder.
// To run theses tests:

View File

@@ -7,12 +7,12 @@ import { I18nService } from "@/jslib/common/src/abstractions/i18n.service";
import { LogService } from "@/jslib/common/src/abstractions/log.service";
import { Utils } from "@/jslib/common/src/misc/utils";
import { StateService } from "../abstractions/state.service";
import { DirectoryType } from "../enums/directoryType";
import { GroupEntry } from "../models/groupEntry";
import { LdapConfiguration } from "../models/ldapConfiguration";
import { SyncConfiguration } from "../models/syncConfiguration";
import { UserEntry } from "../models/userEntry";
import { StateService } from "../../abstractions/state.service";
import { DirectoryType } from "../../enums/directoryType";
import { GroupEntry } from "../../models/groupEntry";
import { LdapConfiguration } from "../../models/ldapConfiguration";
import { SyncConfiguration } from "../../models/syncConfiguration";
import { UserEntry } from "../../models/userEntry";
import { IDirectoryService } from "./directory.service";

View File

@@ -3,14 +3,14 @@ import * as https from "https";
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 { GroupEntry } from "../models/groupEntry";
import { OktaConfiguration } from "../models/oktaConfiguration";
import { SyncConfiguration } from "../models/syncConfiguration";
import { UserEntry } from "../models/userEntry";
import { StateService } from "../../abstractions/state.service";
import { DirectoryType } from "../../enums/directoryType";
import { GroupEntry } from "../../models/groupEntry";
import { OktaConfiguration } from "../../models/oktaConfiguration";
import { SyncConfiguration } from "../../models/syncConfiguration";
import { UserEntry } from "../../models/userEntry";
import { BaseDirectoryService } from "../baseDirectory.service";
import { BaseDirectoryService } from "./baseDirectory.service";
import { IDirectoryService } from "./directory.service";
const DelayBetweenBuildGroupCallsInMilliseconds = 500;

View File

@@ -1,14 +1,14 @@
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 { GroupEntry } from "../models/groupEntry";
import { OneLoginConfiguration } from "../models/oneLoginConfiguration";
import { SyncConfiguration } from "../models/syncConfiguration";
import { UserEntry } from "../models/userEntry";
import { StateService } from "../../abstractions/state.service";
import { DirectoryType } from "../../enums/directoryType";
import { GroupEntry } from "../../models/groupEntry";
import { OneLoginConfiguration } from "../../models/oneLoginConfiguration";
import { SyncConfiguration } from "../../models/syncConfiguration";
import { UserEntry } from "../../models/userEntry";
import { BaseDirectoryService } from "../baseDirectory.service";
import { BaseDirectoryService } from "./baseDirectory.service";
import { IDirectoryService } from "./directory.service";
// Basic email validation: something@something.something

View File

@@ -12,7 +12,7 @@ import { DirectoryFactoryService } from "../abstractions/directory-factory.servi
import { DirectoryType } from "../enums/directoryType";
import { BatchRequestBuilder } from "./batch-request-builder";
import { LdapDirectoryService } from "./ldap-directory.service";
import { LdapDirectoryService } from "./directory-services/ldap-directory.service";
import { SingleRequestBuilder } from "./single-request-builder";
import { StateService } from "./state.service";
import { SyncService } from "./sync.service";

View File

@@ -11,8 +11,8 @@ import { DirectoryFactoryService } from "../abstractions/directory-factory.servi
import { DirectoryType } from "../enums/directoryType";
import { BatchRequestBuilder } from "./batch-request-builder";
import { LdapDirectoryService } from "./directory-services/ldap-directory.service";
import { I18nService } from "./i18n.service";
import { LdapDirectoryService } from "./ldap-directory.service";
import { SingleRequestBuilder } from "./single-request-builder";
import { StateService } from "./state.service";
import { SyncService } from "./sync.service";