1
0
mirror of https://github.com/bitwarden/directory-connector synced 2025-12-05 23:53:21 +00:00
Files
directory-connector/src/services/directory-services/ldap-directory.service.integration.spec.ts
Thomas Rittson a44eb28be8 [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
2025-11-12 06:03:37 +10:00

159 lines
5.9 KiB
TypeScript

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 { StateService } from "../state.service";
import { LdapDirectoryService } from "./ldap-directory.service";
// These tests integrate with the OpenLDAP docker image and seed data located in the openldap folder.
// To run theses tests:
// Install mkcert, e.g.: brew install mkcert
// Configure the environment: npm run test:integration:setup
// Run tests: npm run test:integration:watch
describe("ldapDirectoryService", () => {
let logService: MockProxy<LogService>;
let i18nService: MockProxy<I18nService>;
let stateService: MockProxy<StateService>;
let directoryService: LdapDirectoryService;
beforeEach(() => {
logService = mock();
i18nService = mock();
stateService = mock();
stateService.getDirectoryType.mockResolvedValue(DirectoryType.Ldap);
stateService.getLastUserSync.mockResolvedValue(null); // do not filter results by last modified date
i18nService.t.mockImplementation((id) => id); // passthrough implementation for any error messages
directoryService = new LdapDirectoryService(logService, i18nService, stateService);
});
describe("basic sync fetching users and groups", () => {
it("with an unencrypted connection", async () => {
stateService.getDirectory
.calledWith(DirectoryType.Ldap)
.mockResolvedValue(getLdapConfiguration());
stateService.getSync.mockResolvedValue(getSyncConfiguration({ groups: true, users: true }));
const result = await directoryService.getEntries(true, true);
expect(result).toEqual([groupFixtures, userFixtures]);
});
// StartTLS opportunistically encrypts an otherwise unencrypted connection and therefore uses the same port
it("with StartTLS + SSL", async () => {
stateService.getDirectory.calledWith(DirectoryType.Ldap).mockResolvedValue(
getLdapConfiguration({
ssl: true,
startTls: true,
tlsCaPath: "./utils/openldap/certs/rootCA.pem",
}),
);
stateService.getSync.mockResolvedValue(getSyncConfiguration({ groups: true, users: true }));
const result = await directoryService.getEntries(true, true);
expect(result).toEqual([groupFixtures, userFixtures]);
});
// The ldaps protocol requires use of SSL and uses the secure port
it("with SSL using the ldaps protocol", async () => {
stateService.getDirectory.calledWith(DirectoryType.Ldap).mockResolvedValue(
getLdapConfiguration({
port: 1636,
ssl: true,
sslCaPath: "./utils/openldap/certs/rootCA.pem",
}),
);
stateService.getSync.mockResolvedValue(getSyncConfiguration({ groups: true, users: true }));
const result = await directoryService.getEntries(true, true);
expect(result).toEqual([groupFixtures, userFixtures]);
});
});
describe("users", () => {
it("respects the users path", async () => {
stateService.getDirectory
.calledWith(DirectoryType.Ldap)
.mockResolvedValue(getLdapConfiguration());
stateService.getSync.mockResolvedValue(
getSyncConfiguration({
users: true,
userPath: "ou=Human Resources",
}),
);
// These users are in the Human Resources ou
const hrUsers = userFixtures.filter(
(u) =>
u.referenceId === "cn=Roland Dyke,ou=Human Resources,dc=bitwarden,dc=com" ||
u.referenceId === "cn=Charin Goulfine,ou=Human Resources,dc=bitwarden,dc=com" ||
u.referenceId === "cn=Angelle Guarino,ou=Human Resources,dc=bitwarden,dc=com",
);
const result = await directoryService.getEntries(true, true);
expect(result[1]).toEqual(expect.arrayContaining(hrUsers));
expect(result[1].length).toEqual(hrUsers.length);
});
it("filters users", async () => {
stateService.getDirectory
.calledWith(DirectoryType.Ldap)
.mockResolvedValue(getLdapConfiguration());
stateService.getSync.mockResolvedValue(
getSyncConfiguration({ users: true, userFilter: "(cn=Roland Dyke)" }),
);
const roland = userFixtures.find(
(u) => u.referenceId === "cn=Roland Dyke,ou=Human Resources,dc=bitwarden,dc=com",
);
const result = await directoryService.getEntries(true, true);
expect(result).toEqual([undefined, [roland]]);
});
});
describe("groups", () => {
it("respects the groups path", async () => {
stateService.getDirectory
.calledWith(DirectoryType.Ldap)
.mockResolvedValue(getLdapConfiguration());
stateService.getSync.mockResolvedValue(
getSyncConfiguration({
groups: true,
groupPath: "ou=Janitorial",
}),
);
// These groups are in the Janitorial ou
const janitorialGroups = groupFixtures.filter((g) => g.name === "Cleaners");
const result = await directoryService.getEntries(true, true);
expect(result).toEqual([janitorialGroups, undefined]);
});
it("filters groups", async () => {
stateService.getDirectory
.calledWith(DirectoryType.Ldap)
.mockResolvedValue(getLdapConfiguration());
stateService.getSync.mockResolvedValue(
getSyncConfiguration({ groups: true, groupFilter: "(cn=Red Team)" }),
);
const redTeam = groupFixtures.find(
(u) => u.referenceId === "cn=Red Team,dc=bitwarden,dc=com",
);
const result = await directoryService.getEntries(true, true);
expect(result).toEqual([[redTeam], undefined]);
});
});
});