26 changed files with 259 additions and 37 deletions
@ -1,10 +0,0 @@ |
|||||||
if ! [ -x "$(command -v mkcert)" ]; then |
|
||||||
echo 'Error: mkcert is not installed. Install mkcert first and then re-run this script.' |
|
||||||
echo 'e.g. brew install mkcert' |
|
||||||
exit 1 |
|
||||||
fi |
|
||||||
|
|
||||||
mkcert -install |
|
||||||
mkdir -p ./openldap/certs |
|
||||||
cp "$(mkcert -CAROOT)/rootCA.pem" ./openldap/certs/rootCA.pem |
|
||||||
mkcert -key-file ./openldap/certs/openldap-key.pem -cert-file ./openldap/certs/openldap.pem localhost openldap |
|
||||||
@ -0,0 +1,82 @@ |
|||||||
|
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 { |
||||||
|
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"; |
||||||
|
|
||||||
|
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.
|
||||||
|
// Place the .env file attachment in the utils folder.
|
||||||
|
|
||||||
|
// Load .env variables
|
||||||
|
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_GROUP_FILTER = "|name:Integration*"; |
||||||
|
|
||||||
|
describe("gsuiteDirectoryService", () => { |
||||||
|
let logService: MockProxy<LogService>; |
||||||
|
let i18nService: MockProxy<I18nService>; |
||||||
|
let stateService: MockProxy<StateService>; |
||||||
|
|
||||||
|
let directoryService: GSuiteDirectoryService; |
||||||
|
|
||||||
|
beforeEach(() => { |
||||||
|
logService = mock(); |
||||||
|
i18nService = mock(); |
||||||
|
stateService = mock(); |
||||||
|
|
||||||
|
stateService.getDirectoryType.mockResolvedValue(DirectoryType.GSuite); |
||||||
|
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 GSuiteDirectoryService(logService, i18nService, stateService); |
||||||
|
}); |
||||||
|
|
||||||
|
it("syncs without using filters (includes test data)", async () => { |
||||||
|
const directoryConfig = getGSuiteConfiguration(); |
||||||
|
stateService.getDirectory.calledWith(DirectoryType.GSuite).mockResolvedValue(directoryConfig); |
||||||
|
|
||||||
|
const syncConfig = getSyncConfiguration({ |
||||||
|
groups: true, |
||||||
|
users: true, |
||||||
|
}); |
||||||
|
stateService.getSync.mockResolvedValue(syncConfig); |
||||||
|
|
||||||
|
const result = await directoryService.getEntries(true, true); |
||||||
|
|
||||||
|
expect(result[0]).toEqual(expect.arrayContaining(groupFixtures)); |
||||||
|
expect(result[1]).toEqual(expect.arrayContaining(userFixtures)); |
||||||
|
}); |
||||||
|
|
||||||
|
it("syncs using user and group filters (exact match for test data)", async () => { |
||||||
|
const directoryConfig = getGSuiteConfiguration(); |
||||||
|
stateService.getDirectory.calledWith(DirectoryType.GSuite).mockResolvedValue(directoryConfig); |
||||||
|
|
||||||
|
const syncConfig = getSyncConfiguration({ |
||||||
|
groups: true, |
||||||
|
users: true, |
||||||
|
userFilter: INTEGRATION_USER_FILTER, |
||||||
|
groupFilter: INTEGRATION_GROUP_FILTER, |
||||||
|
}); |
||||||
|
stateService.getSync.mockResolvedValue(syncConfig); |
||||||
|
|
||||||
|
const result = await directoryService.getEntries(true, true); |
||||||
|
|
||||||
|
expect(result).toEqual([groupFixtures, userFixtures]); |
||||||
|
}); |
||||||
|
}); |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
GOOGLE_DOMAIN= |
||||||
|
GOOGLE_ADMIN_USER= |
||||||
|
GOOGLE_CLIENT_EMAIL= |
||||||
|
GOOGLE_PRIVATE_KEY= |
||||||
@ -0,0 +1,56 @@ |
|||||||
|
import { GSuiteConfiguration } from "../../src/models/gsuiteConfiguration"; |
||||||
|
import { SyncConfiguration } from "../../src/models/syncConfiguration"; |
||||||
|
|
||||||
|
/** |
||||||
|
* @returns a basic GSuite configuration. Can be overridden by passing in a partial configuration. |
||||||
|
*/ |
||||||
|
export const getGSuiteConfiguration = ( |
||||||
|
config?: Partial<GSuiteConfiguration>, |
||||||
|
): GSuiteConfiguration => { |
||||||
|
const adminUser = process.env.GOOGLE_ADMIN_USER; |
||||||
|
const clientEmail = process.env.GOOGLE_CLIENT_EMAIL; |
||||||
|
const privateKey = process.env.GOOGLE_PRIVATE_KEY; |
||||||
|
const domain = process.env.GOOGLE_DOMAIN; |
||||||
|
|
||||||
|
if (!adminUser || !clientEmail || !privateKey || !domain) { |
||||||
|
throw new Error("Google Workspace integration test credentials not configured."); |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
// TODO
|
||||||
|
adminUser, |
||||||
|
clientEmail, |
||||||
|
privateKey, |
||||||
|
domain: domain, |
||||||
|
customer: "", |
||||||
|
...(config ?? {}), |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* @returns a basic Google Workspace sync configuration. Can be overridden by passing in a partial configuration. |
||||||
|
*/ |
||||||
|
export const getSyncConfiguration = (config?: Partial<SyncConfiguration>): SyncConfiguration => ({ |
||||||
|
users: false, |
||||||
|
groups: false, |
||||||
|
interval: 5, |
||||||
|
userFilter: "", |
||||||
|
groupFilter: "", |
||||||
|
removeDisabled: false, |
||||||
|
overwriteExisting: false, |
||||||
|
largeImport: false, |
||||||
|
// Ldap properties - not optional for some reason
|
||||||
|
groupObjectClass: "", |
||||||
|
userObjectClass: "", |
||||||
|
groupPath: null, |
||||||
|
userPath: null, |
||||||
|
groupNameAttribute: "", |
||||||
|
userEmailAttribute: "", |
||||||
|
memberAttribute: "", |
||||||
|
useEmailPrefixSuffix: false, |
||||||
|
emailPrefixAttribute: "", |
||||||
|
emailSuffix: null, |
||||||
|
creationDateAttribute: "", |
||||||
|
revisionDateAttribute: "", |
||||||
|
...(config ?? {}), |
||||||
|
}); |
||||||
@ -0,0 +1,26 @@ |
|||||||
|
import { Jsonify } from "type-fest"; |
||||||
|
|
||||||
|
import { GroupEntry } from "../../src/models/groupEntry"; |
||||||
|
|
||||||
|
// These must match the Google Workspace seed data
|
||||||
|
|
||||||
|
const data: Jsonify<GroupEntry>[] = [ |
||||||
|
{ |
||||||
|
externalId: "0319y80a3anpxhj", |
||||||
|
groupMemberReferenceIds: [], |
||||||
|
name: "Integration Test Group A", |
||||||
|
referenceId: "0319y80a3anpxhj", |
||||||
|
userMemberExternalIds: ["111605910541641314041", "111147009830456099026"], |
||||||
|
users: [], |
||||||
|
}, |
||||||
|
{ |
||||||
|
externalId: "02afmg28317uyub", |
||||||
|
groupMemberReferenceIds: [], |
||||||
|
name: "Integration Test Group B", |
||||||
|
referenceId: "02afmg28317uyub", |
||||||
|
userMemberExternalIds: ["111147009830456099026", "100150970267699397306"], |
||||||
|
users: [], |
||||||
|
}, |
||||||
|
]; |
||||||
|
|
||||||
|
export const groupFixtures = data.map((g) => GroupEntry.fromJSON(g)); |
||||||
@ -0,0 +1,50 @@ |
|||||||
|
import { Jsonify } from "type-fest"; |
||||||
|
|
||||||
|
import { UserEntry } from "../../src/models/userEntry"; |
||||||
|
|
||||||
|
// These must match the Google Workspace seed data
|
||||||
|
|
||||||
|
const data: Jsonify<UserEntry>[] = [ |
||||||
|
// In Group A
|
||||||
|
{ |
||||||
|
deleted: false, |
||||||
|
disabled: false, |
||||||
|
email: "testuser1@bwrox.dev", |
||||||
|
externalId: "111605910541641314041", |
||||||
|
referenceId: "111605910541641314041", |
||||||
|
}, |
||||||
|
// In Groups A + B
|
||||||
|
{ |
||||||
|
deleted: false, |
||||||
|
disabled: false, |
||||||
|
email: "testuser2@bwrox.dev", |
||||||
|
externalId: "111147009830456099026", |
||||||
|
referenceId: "111147009830456099026", |
||||||
|
}, |
||||||
|
// In Group B
|
||||||
|
{ |
||||||
|
deleted: false, |
||||||
|
disabled: false, |
||||||
|
email: "testuser3@bwrox.dev", |
||||||
|
externalId: "100150970267699397306", |
||||||
|
referenceId: "100150970267699397306", |
||||||
|
}, |
||||||
|
// Not in a group
|
||||||
|
{ |
||||||
|
deleted: false, |
||||||
|
disabled: false, |
||||||
|
email: "testuser4@bwrox.dev", |
||||||
|
externalId: "113764752650306721470", |
||||||
|
referenceId: "113764752650306721470", |
||||||
|
}, |
||||||
|
// Disabled user
|
||||||
|
{ |
||||||
|
deleted: false, |
||||||
|
disabled: true, |
||||||
|
email: "testuser5@bwrox.dev", |
||||||
|
externalId: "110381976819725658200", |
||||||
|
referenceId: "110381976819725658200", |
||||||
|
}, |
||||||
|
]; |
||||||
|
|
||||||
|
export const userFixtures = data.map((g) => UserEntry.fromJSON(g)); |
||||||
@ -1,5 +1,5 @@ |
|||||||
import { LdapConfiguration } from "../models/ldapConfiguration"; |
import { LdapConfiguration } from "../../src/models/ldapConfiguration"; |
||||||
import { SyncConfiguration } from "../models/syncConfiguration"; |
import { SyncConfiguration } from "../../src/models/syncConfiguration"; |
||||||
|
|
||||||
/** |
/** |
||||||
* @returns a basic ldap configuration without TLS/SSL enabled. Can be overridden by passing in a partial configuration. |
* @returns a basic ldap configuration without TLS/SSL enabled. Can be overridden by passing in a partial configuration. |
||||||
@ -1,6 +1,6 @@ |
|||||||
import { Jsonify } from "type-fest"; |
import { Jsonify } from "type-fest"; |
||||||
|
|
||||||
import { GroupEntry } from "../src/models/groupEntry"; |
import { GroupEntry } from "@/src/models/groupEntry"; |
||||||
|
|
||||||
// These must match the ldap server seed data in directory.ldif
|
// These must match the ldap server seed data in directory.ldif
|
||||||
const data: Jsonify<GroupEntry>[] = [ |
const data: Jsonify<GroupEntry>[] = [ |
||||||
@ -0,0 +1,10 @@ |
|||||||
|
if ! [ -x "$(command -v mkcert)" ]; then |
||||||
|
echo 'Error: mkcert is not installed. Install mkcert first and then re-run this script.' |
||||||
|
echo 'e.g. brew install mkcert' |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
mkcert -install |
||||||
|
mkdir -p ./utils/openldap/certs |
||||||
|
cp "$(mkcert -CAROOT)/rootCA.pem" ./utils/openldap/certs/rootCA.pem |
||||||
|
mkcert -key-file ./utils/openldap/certs/openldap-key.pem -cert-file ./utils/openldap/certs/openldap.pem localhost openldap |
||||||
@ -1,6 +1,6 @@ |
|||||||
import { Jsonify } from "type-fest"; |
import { Jsonify } from "type-fest"; |
||||||
|
|
||||||
import { UserEntry } from "../src/models/userEntry"; |
import { UserEntry } from "@/src/models/userEntry"; |
||||||
|
|
||||||
// These must match the ldap server seed data in directory.ldif
|
// These must match the ldap server seed data in directory.ldif
|
||||||
const data: Jsonify<UserEntry>[] = [ |
const data: Jsonify<UserEntry>[] = [ |
||||||
@ -1,7 +1,7 @@ |
|||||||
import { GetUniqueString } from "@/jslib/common/spec/utils"; |
import { GetUniqueString } from "@/jslib/common/spec/utils"; |
||||||
|
|
||||||
import { GroupEntry } from "../models/groupEntry"; |
import { GroupEntry } from "../src/models/groupEntry"; |
||||||
import { UserEntry } from "../models/userEntry"; |
import { UserEntry } from "../src/models/userEntry"; |
||||||
|
|
||||||
export function userSimulator(userCount: number): UserEntry[] { |
export function userSimulator(userCount: number): UserEntry[] { |
||||||
const users: UserEntry[] = []; |
const users: UserEntry[] = []; |
||||||
Loading…
Reference in new issue