mirror of
https://github.com/bitwarden/directory-connector
synced 2025-12-05 23:53:21 +00:00
New Data, New Test
This commit is contained in:
committed by
Thomas Rittson
parent
88a1dc7334
commit
2ca8654492
@@ -35,6 +35,29 @@ const data: Jsonify<GroupEntry>[] = [
|
||||
externalId: "cn=Cleaners,ou=Janitorial,dc=bitwarden,dc=com",
|
||||
name: "Cleaners",
|
||||
},
|
||||
{
|
||||
userMemberExternalIds: [
|
||||
"cn=Benjamin Chen,ou=Product Development,dc=bitwarden,dc=com",
|
||||
"cn=Karen Smith,ou=Product Development,dc=bitwarden,dc=com",
|
||||
"cn=Robert Johnson,ou=Product Development,dc=bitwarden,dc=com",
|
||||
],
|
||||
groupMemberReferenceIds: [],
|
||||
users: [],
|
||||
referenceId: "cn=DevOps Team,dc=bitwarden,dc=com",
|
||||
externalId: "cn=DevOps Team,dc=bitwarden,dc=com",
|
||||
name: "DevOps Team",
|
||||
},
|
||||
{
|
||||
userMemberExternalIds: [
|
||||
"cn=Thomas Williams,ou=Management,dc=bitwarden,dc=com",
|
||||
"cn=Michelle Brown,ou=Management,dc=bitwarden,dc=com",
|
||||
],
|
||||
groupMemberReferenceIds: [],
|
||||
users: [],
|
||||
referenceId: "cn=Security Team,dc=bitwarden,dc=com",
|
||||
externalId: "cn=Security Team,dc=bitwarden,dc=com",
|
||||
name: "Security Team",
|
||||
},
|
||||
];
|
||||
|
||||
export const groupFixtures = data.map((g) => GroupEntry.fromJSON(g));
|
||||
|
||||
@@ -688,4 +688,173 @@ mobile: +1 804 319-5569
|
||||
pager: +1 804 815-3661
|
||||
roomNumber: 9273
|
||||
manager: cn=Inga Schnirer,ou=Product Testing,dc=bitwarden, dc=com
|
||||
secretary: cn=Keven Gilleland,ou=Administrative,dc=bitwarden, dc=com
|
||||
|
||||
dn: cn=DevOps Team,dc=bitwarden,dc=com
|
||||
changetype: add
|
||||
cn: DevOps Team
|
||||
gidnumber: 800
|
||||
memberuid: ChenB
|
||||
memberuid: SmithK
|
||||
memberuid: JohnsonR
|
||||
objectclass: posixGroup
|
||||
objectclass: top
|
||||
|
||||
dn: cn=Security Team,dc=bitwarden,dc=com
|
||||
changetype: add
|
||||
cn: Security Team
|
||||
gidnumber: 900
|
||||
memberuid: WilliamsT
|
||||
memberuid: BrownM
|
||||
objectclass: posixGroup
|
||||
objectclass: top
|
||||
|
||||
dn: cn=Benjamin Chen,ou=Product Development,dc=bitwarden, dc=com
|
||||
changetype: add
|
||||
objectClass: top
|
||||
objectClass: person
|
||||
objectClass: organizationalPerson
|
||||
objectClass: inetOrgPerson
|
||||
cn: Benjamin Chen
|
||||
sn: Chen
|
||||
description: This is Benjamin Chen's description
|
||||
facsimileTelephoneNumber: +1 408 555-1234
|
||||
l: San Jose
|
||||
ou: Product Development
|
||||
postalAddress: Product Development$San Jose
|
||||
telephoneNumber: +1 408 555-5678
|
||||
title: Senior DevOps Engineer
|
||||
userPassword: Password1
|
||||
uid: ChenB
|
||||
givenName: Benjamin
|
||||
mail: ChenB@9f8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3.bitwarden.com
|
||||
carLicense: 2K8N9L
|
||||
departmentNumber: 1001
|
||||
employeeType: Employee
|
||||
homePhone: +1 408 555-9876
|
||||
initials: B. C.
|
||||
mobile: +1 408 555-4321
|
||||
pager: +1 408 555-7890
|
||||
roomNumber: 7125
|
||||
manager: cn=Roland Dyke,ou=Human Resources,dc=bitwarden, dc=com
|
||||
secretary: cn=Keven Gilleland,ou=Administrative,dc=bitwarden, dc=com
|
||||
|
||||
dn: cn=Karen Smith,ou=Product Development,dc=bitwarden, dc=com
|
||||
changetype: add
|
||||
objectClass: top
|
||||
objectClass: person
|
||||
objectClass: organizationalPerson
|
||||
objectClass: inetOrgPerson
|
||||
cn: Karen Smith
|
||||
sn: Smith
|
||||
description: This is Karen Smith's description
|
||||
facsimileTelephoneNumber: +1 415 555-2345
|
||||
l: San Francisco
|
||||
ou: Product Development
|
||||
postalAddress: Product Development$San Francisco
|
||||
telephoneNumber: +1 415 555-6789
|
||||
title: Senior Systems Administrator
|
||||
userPassword: Password1
|
||||
uid: SmithK
|
||||
givenName: Karen
|
||||
mail: SmithK@3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9.bitwarden.com
|
||||
carLicense: 5M3P7Q
|
||||
departmentNumber: 1002
|
||||
employeeType: Employee
|
||||
homePhone: +1 415 555-0987
|
||||
initials: K. S.
|
||||
mobile: +1 415 555-5432
|
||||
pager: +1 415 555-8901
|
||||
roomNumber: 7126
|
||||
manager: cn=Roland Dyke,ou=Human Resources,dc=bitwarden, dc=com
|
||||
secretary: cn=Keven Gilleland,ou=Administrative,dc=bitwarden, dc=com
|
||||
|
||||
dn: cn=Robert Johnson,ou=Product Development,dc=bitwarden, dc=com
|
||||
changetype: add
|
||||
objectClass: top
|
||||
objectClass: person
|
||||
objectClass: organizationalPerson
|
||||
objectClass: inetOrgPerson
|
||||
cn: Robert Johnson
|
||||
sn: Johnson
|
||||
description: This is Robert Johnson's description
|
||||
facsimileTelephoneNumber: +1 510 555-3456
|
||||
l: Oakland
|
||||
ou: Product Development
|
||||
postalAddress: Product Development$Oakland
|
||||
telephoneNumber: +1 510 555-7890
|
||||
title: Cloud Infrastructure Engineer
|
||||
userPassword: Password1
|
||||
uid: JohnsonR
|
||||
givenName: Robert
|
||||
mail: JohnsonR@7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3.bitwarden.com
|
||||
carLicense: 8X2Y4Z
|
||||
departmentNumber: 1003
|
||||
employeeType: Employee
|
||||
homePhone: +1 510 555-1098
|
||||
initials: R. J.
|
||||
mobile: +1 510 555-6543
|
||||
pager: +1 510 555-9012
|
||||
roomNumber: 7127
|
||||
manager: cn=Roland Dyke,ou=Human Resources,dc=bitwarden, dc=com
|
||||
secretary: cn=Keven Gilleland,ou=Administrative,dc=bitwarden, dc=com
|
||||
|
||||
dn: cn=Thomas Williams,ou=Management,dc=bitwarden, dc=com
|
||||
changetype: add
|
||||
objectClass: top
|
||||
objectClass: person
|
||||
objectClass: organizationalPerson
|
||||
objectClass: inetOrgPerson
|
||||
cn: Thomas Williams
|
||||
sn: Williams
|
||||
description: This is Thomas Williams's description
|
||||
facsimileTelephoneNumber: +1 650 555-4567
|
||||
l: Palo Alto
|
||||
ou: Management
|
||||
postalAddress: Management$Palo Alto
|
||||
telephoneNumber: +1 650 555-8901
|
||||
title: Chief Security Officer
|
||||
userPassword: Password1
|
||||
uid: WilliamsT
|
||||
givenName: Thomas
|
||||
mail: WilliamsT@1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7.bitwarden.com
|
||||
carLicense: 6H9J2K
|
||||
departmentNumber: 1004
|
||||
employeeType: Employee
|
||||
homePhone: +1 650 555-2109
|
||||
initials: T. W.
|
||||
mobile: +1 650 555-7654
|
||||
pager: +1 650 555-0123
|
||||
roomNumber: 7128
|
||||
manager: cn=Roland Dyke,ou=Human Resources,dc=bitwarden, dc=com
|
||||
secretary: cn=Keven Gilleland,ou=Administrative,dc=bitwarden, dc=com
|
||||
|
||||
dn: cn=Michelle Brown,ou=Management,dc=bitwarden, dc=com
|
||||
changetype: add
|
||||
objectClass: top
|
||||
objectClass: person
|
||||
objectClass: organizationalPerson
|
||||
objectClass: inetOrgPerson
|
||||
cn: Michelle Brown
|
||||
sn: Brown
|
||||
description: This is Michelle Brown's description
|
||||
facsimileTelephoneNumber: +1 408 555-5678
|
||||
l: Santa Clara
|
||||
ou: Management
|
||||
postalAddress: Management$Santa Clara
|
||||
telephoneNumber: +1 408 555-9012
|
||||
title: Security Analyst
|
||||
userPassword: Password1
|
||||
uid: BrownM
|
||||
givenName: Michelle
|
||||
mail: BrownM@5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1.bitwarden.com
|
||||
carLicense: 4T6U8V
|
||||
departmentNumber: 1005
|
||||
employeeType: Employee
|
||||
homePhone: +1 408 555-3210
|
||||
initials: M. B.
|
||||
mobile: +1 408 555-8765
|
||||
pager: +1 408 555-1234
|
||||
roomNumber: 7129
|
||||
manager: cn=Roland Dyke,ou=Human Resources,dc=bitwarden, dc=com
|
||||
secretary: cn=Keven Gilleland,ou=Administrative,dc=bitwarden, dc=com
|
||||
|
||||
@@ -144,6 +144,41 @@ const data: Jsonify<UserEntry>[] = [
|
||||
externalId: "cn=Loella Mak,ou=Payroll,dc=bitwarden,dc=com",
|
||||
email: "makl@6ab3e25ca49d4d64aaf44844288a8ef7.bitwarden.com",
|
||||
},
|
||||
{
|
||||
disabled: false,
|
||||
deleted: false,
|
||||
referenceId: "cn=Benjamin Chen,ou=Product Development,dc=bitwarden,dc=com",
|
||||
externalId: "cn=Benjamin Chen,ou=Product Development,dc=bitwarden,dc=com",
|
||||
email: "chenb@9f8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3.bitwarden.com",
|
||||
},
|
||||
{
|
||||
disabled: false,
|
||||
deleted: false,
|
||||
referenceId: "cn=Karen Smith,ou=Product Development,dc=bitwarden,dc=com",
|
||||
externalId: "cn=Karen Smith,ou=Product Development,dc=bitwarden,dc=com",
|
||||
email: "smithk@3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9.bitwarden.com",
|
||||
},
|
||||
{
|
||||
disabled: false,
|
||||
deleted: false,
|
||||
referenceId: "cn=Robert Johnson,ou=Product Development,dc=bitwarden,dc=com",
|
||||
externalId: "cn=Robert Johnson,ou=Product Development,dc=bitwarden,dc=com",
|
||||
email: "johnsonr@7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3.bitwarden.com",
|
||||
},
|
||||
{
|
||||
disabled: false,
|
||||
deleted: false,
|
||||
referenceId: "cn=Thomas Williams,ou=Management,dc=bitwarden,dc=com",
|
||||
externalId: "cn=Thomas Williams,ou=Management,dc=bitwarden,dc=com",
|
||||
email: "williamst@1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7.bitwarden.com",
|
||||
},
|
||||
{
|
||||
disabled: false,
|
||||
deleted: false,
|
||||
referenceId: "cn=Michelle Brown,ou=Management,dc=bitwarden,dc=com",
|
||||
externalId: "cn=Michelle Brown,ou=Management,dc=bitwarden,dc=com",
|
||||
email: "brownm@5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1.bitwarden.com",
|
||||
},
|
||||
];
|
||||
|
||||
export const userFixtures = data.map((v) => UserEntry.fromJSON(v));
|
||||
|
||||
@@ -152,4 +152,88 @@ describe("ldapDirectoryService", () => {
|
||||
expect(result).toEqual([[redTeam], undefined]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("new groups and users", () => {
|
||||
it("fetches DevOps Team and Security Team groups", async () => {
|
||||
stateService.getDirectory
|
||||
.calledWith(DirectoryType.Ldap)
|
||||
.mockResolvedValue(getLdapConfiguration());
|
||||
stateService.getSync.mockResolvedValue(
|
||||
getSyncConfiguration({
|
||||
groups: true,
|
||||
groupFilter: "(|(cn=DevOps Team)(cn=Security Team))",
|
||||
}),
|
||||
);
|
||||
|
||||
const devOpsTeam = groupFixtures.find(
|
||||
(g) => g.referenceId === "cn=DevOps Team,dc=bitwarden,dc=com",
|
||||
);
|
||||
const securityTeam = groupFixtures.find(
|
||||
(g) => g.referenceId === "cn=Security Team,dc=bitwarden,dc=com",
|
||||
);
|
||||
|
||||
const result = await directoryService.getEntries(true, true);
|
||||
expect(result[0]).toEqual(expect.arrayContaining([devOpsTeam, securityTeam]));
|
||||
expect(result[0].length).toEqual(2);
|
||||
});
|
||||
|
||||
it("fetches new users with correct group memberships", async () => {
|
||||
stateService.getDirectory
|
||||
.calledWith(DirectoryType.Ldap)
|
||||
.mockResolvedValue(getLdapConfiguration());
|
||||
stateService.getSync.mockResolvedValue(
|
||||
getSyncConfiguration({
|
||||
users: true,
|
||||
groups: true,
|
||||
userFilter: "(|(uid=ChenB)(uid=SmithK)(uid=JohnsonR)(uid=WilliamsT)(uid=BrownM))",
|
||||
}),
|
||||
);
|
||||
|
||||
const newUsers = userFixtures.filter(
|
||||
(u) =>
|
||||
u.referenceId === "cn=Benjamin Chen,ou=Product Development,dc=bitwarden,dc=com" ||
|
||||
u.referenceId === "cn=Karen Smith,ou=Product Development,dc=bitwarden,dc=com" ||
|
||||
u.referenceId === "cn=Robert Johnson,ou=Product Development,dc=bitwarden,dc=com" ||
|
||||
u.referenceId === "cn=Thomas Williams,ou=Management,dc=bitwarden,dc=com" ||
|
||||
u.referenceId === "cn=Michelle Brown,ou=Management,dc=bitwarden,dc=com",
|
||||
);
|
||||
|
||||
const devOpsTeam = groupFixtures.find(
|
||||
(g) => g.referenceId === "cn=DevOps Team,dc=bitwarden,dc=com",
|
||||
);
|
||||
const securityTeam = groupFixtures.find(
|
||||
(g) => g.referenceId === "cn=Security Team,dc=bitwarden,dc=com",
|
||||
);
|
||||
|
||||
const result = await directoryService.getEntries(true, true);
|
||||
|
||||
// Verify users are fetched
|
||||
expect(result[1]).toEqual(expect.arrayContaining(newUsers));
|
||||
expect(result[1].length).toEqual(newUsers.length);
|
||||
|
||||
// Verify groups are fetched with correct membership
|
||||
expect(result[0]).toEqual(expect.arrayContaining([devOpsTeam, securityTeam]));
|
||||
|
||||
// Verify DevOps Team has 3 members
|
||||
const fetchedDevOpsTeam = result[0].find((g) => g.name === "DevOps Team");
|
||||
expect(fetchedDevOpsTeam.userMemberExternalIds.size).toEqual(3);
|
||||
expect(Array.from(fetchedDevOpsTeam.userMemberExternalIds)).toEqual(
|
||||
expect.arrayContaining([
|
||||
"cn=Benjamin Chen,ou=Product Development,dc=bitwarden,dc=com",
|
||||
"cn=Karen Smith,ou=Product Development,dc=bitwarden,dc=com",
|
||||
"cn=Robert Johnson,ou=Product Development,dc=bitwarden,dc=com",
|
||||
]),
|
||||
);
|
||||
|
||||
// Verify Security Team has 2 members
|
||||
const fetchedSecurityTeam = result[0].find((g) => g.name === "Security Team");
|
||||
expect(fetchedSecurityTeam.userMemberExternalIds.size).toEqual(2);
|
||||
expect(Array.from(fetchedSecurityTeam.userMemberExternalIds)).toEqual(
|
||||
expect.arrayContaining([
|
||||
"cn=Thomas Williams,ou=Management,dc=bitwarden,dc=com",
|
||||
"cn=Michelle Brown,ou=Management,dc=bitwarden,dc=com",
|
||||
]),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -192,13 +192,13 @@ export class LdapDirectoryService implements IDirectoryService {
|
||||
this.syncConfig.userFilter,
|
||||
);
|
||||
const userPath = this.makeSearchPath(this.syncConfig.userPath);
|
||||
const userIdMap = new Map<string, string>();
|
||||
const userDnMap = new Map<string, string>();
|
||||
const userUidMap = new Map<string, string>();
|
||||
await this.search<string>(userPath, userFilter, (se: any) => {
|
||||
const dn = this.getReferenceId(se);
|
||||
const uid = this.getAttr<string>(se, "uid");
|
||||
const externalId = this.getExternalId(se, dn);
|
||||
userIdMap.set(dn, externalId);
|
||||
userDnMap.set(dn, externalId);
|
||||
if (uid != null) {
|
||||
userUidMap.set(uid, externalId);
|
||||
}
|
||||
@@ -206,7 +206,7 @@ export class LdapDirectoryService implements IDirectoryService {
|
||||
});
|
||||
|
||||
for (const se of groupSearchEntries) {
|
||||
const group = this.buildGroup(se, userIdMap, userUidMap);
|
||||
const group = this.buildGroup(se, userDnMap, userUidMap);
|
||||
if (group != null) {
|
||||
entries.push(group);
|
||||
}
|
||||
@@ -217,7 +217,7 @@ export class LdapDirectoryService implements IDirectoryService {
|
||||
|
||||
private buildGroup(
|
||||
searchEntry: any,
|
||||
userMap: Map<string, string>,
|
||||
userDnMap: Map<string, string>,
|
||||
userUidMap: Map<string, string>,
|
||||
) {
|
||||
const group = new GroupEntry();
|
||||
@@ -239,24 +239,33 @@ export class LdapDirectoryService implements IDirectoryService {
|
||||
|
||||
const members = this.getAttrVals<string>(searchEntry, this.syncConfig.memberAttribute);
|
||||
if (members != null) {
|
||||
for (const member of members) {
|
||||
// Check if member is a DN (contains '=' and ',')
|
||||
const isDn = member.includes("=") && member.includes(",");
|
||||
const getMemberAttributeType = (member: string): "memberDn" | "memberUid" | "groupDn" => {
|
||||
const isDnLike = member.includes("=") && member.includes(",");
|
||||
if (isDnLike) {
|
||||
return userDnMap.has(member) ? "memberDn" : "groupDn";
|
||||
}
|
||||
return "memberUid";
|
||||
};
|
||||
|
||||
if (isDn) {
|
||||
// Member is a DN
|
||||
if (userMap.has(member) && !group.userMemberExternalIds.has(userMap.get(member))) {
|
||||
group.userMemberExternalIds.add(userMap.get(member));
|
||||
} else if (!group.groupMemberReferenceIds.has(member)) {
|
||||
group.groupMemberReferenceIds.add(member);
|
||||
for (const member of members) {
|
||||
switch (getMemberAttributeType(member)) {
|
||||
case "memberDn": {
|
||||
const externalId = userDnMap.get(member);
|
||||
if (externalId != null) {
|
||||
group.userMemberExternalIds.add(externalId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Member is likely a UID
|
||||
if (userUidMap.has(member) && !group.userMemberExternalIds.has(userUidMap.get(member))) {
|
||||
group.userMemberExternalIds.add(userUidMap.get(member));
|
||||
} else if (!group.groupMemberReferenceIds.has(member)) {
|
||||
group.groupMemberReferenceIds.add(member);
|
||||
case "memberUid": {
|
||||
const externalId = userUidMap.get(member);
|
||||
if (externalId != null) {
|
||||
group.userMemberExternalIds.add(externalId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "groupDn":
|
||||
group.groupMemberReferenceIds.add(member);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ describe("SyncService", () => {
|
||||
expect(apiService.postPublicImportDirectory).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ overwriteExisting: false }),
|
||||
);
|
||||
expect(apiService.postPublicImportDirectory).toHaveBeenCalledTimes(6);
|
||||
expect(apiService.postPublicImportDirectory).toHaveBeenCalledTimes(9);
|
||||
|
||||
// @ts-expect-error Reset batch size to original state.
|
||||
constants.batchSize = originalBatchSize;
|
||||
|
||||
Reference in New Issue
Block a user