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

[PM-13008] Add ldap integration tests (#637)

This commit is contained in:
Thomas Rittson
2024-10-14 08:17:00 +10:00
committed by GitHub
parent 743b4b44cb
commit d65f42684e
10 changed files with 1277 additions and 3 deletions

96
.github/workflows/integration-test.yml vendored Normal file
View File

@@ -0,0 +1,96 @@
name: Integration Testing
on:
workflow_dispatch:
push:
branches:
- "main"
paths:
- ".github/workflows/integration-test.yml" # this file
- "src/services/ldap-directory.service*" # we only have integration for LDAP testing at the moment
pull_request:
paths:
- ".github/workflows/integration-test.yml" # this file
- "src/services/ldap-directory.service*" # we only have integration for LDAP testing at the moment
jobs:
check-test-secrets:
name: Check for test secrets
runs-on: ubuntu-22.04
outputs:
available: ${{ steps.check-test-secrets.outputs.available }}
permissions:
contents: read
steps:
- name: Check
id: check-test-secrets
run: |
if [ "${{ secrets.CODECOV_TOKEN }}" != '' ]; then
echo "available=true" >> $GITHUB_OUTPUT;
else
echo "available=false" >> $GITHUB_OUTPUT;
fi
testing:
name: Run tests
if: ${{ startsWith(github.head_ref, 'version_bump_') == false }}
runs-on: ubuntu-22.04
needs: check-test-secrets
permissions:
checks: write
contents: read
pull-requests: write
steps:
- name: Check out repo
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Get Node version
id: retrieve-node-version
run: |
NODE_NVMRC=$(cat .nvmrc)
NODE_VERSION=${NODE_NVMRC/v/''}
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: ${{ steps.retrieve-node-version.outputs.node_version }}
- name: Install Node dependencies
run: npm ci
- name: Install mkcert
run: |
sudo apt-get update
sudo apt-get -y install mkcert
- name: Setup integration tests
run: npm run test:integration:setup
- name: Run integration tests
run: npm run test:integration --coverage
- name: Report test results
uses: dorny/test-reporter@31a54ee7ebcacc03a09ea97a7e5465a47b84aea5 # v1.9.1
if: ${{ needs.check-test-secrets.outputs.available == 'true' && !cancelled() }}
with:
name: Test Results
path: "junit.xml"
reporter: jest-junit
fail-on-error: true
- name: Upload coverage to codecov.io
uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0
if: ${{ needs.check-test-secrets.outputs.available == 'true' }}
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- name: Upload results to codecov.io
uses: codecov/test-results-action@1b5b448b98e58ba90d1a1a1d9fcb72ca2263be46 # v1.0.0
if: ${{ needs.check-test-secrets.outputs.available == 'true' }}
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

18
docker-compose.yml Normal file
View File

@@ -0,0 +1,18 @@
services:
open-ldap:
image: bitnami/openldap:latest
hostname: openldap
environment:
- LDAP_ADMIN_USERNAME=admin
- LDAP_ADMIN_PASSWORD=admin
- LDAP_ROOT=dc=bitwarden,dc=com
- LDAP_ENABLE_TLS=yes
- LDAP_TLS_CERT_FILE=/certs/openldap.pem
- LDAP_TLS_KEY_FILE=/certs/openldap-key.pem
- LDAP_TLS_CA_FILE=/certs/rootCA.pem
volumes:
- "./openldap/ldifs:/ldifs"
- "./openldap/certs:/certs"
ports:
- "1389:1389"
- "1636:1636"

View File

@@ -0,0 +1,40 @@
import { Jsonify } from "type-fest";
import { GroupEntry } from "../src/models/groupEntry";
// These must match the ldap server seed data in directory.ldif
const data: Jsonify<GroupEntry>[] = [
{
userMemberExternalIds: [
"cn=Loella Mak,ou=Payroll,dc=bitwarden,dc=com",
"cn=Painterson Miki,ou=Product Development,dc=bitwarden,dc=com",
"cn=Roland Dyke,ou=Human Resources,dc=bitwarden,dc=com",
],
groupMemberReferenceIds: [],
users: [],
referenceId: "cn=Blue Team,dc=bitwarden,dc=com",
externalId: "cn=Blue Team,dc=bitwarden,dc=com",
name: "Blue Team",
},
{
userMemberExternalIds: [
"cn=Shiela Harada,ou=Peons,dc=bitwarden,dc=com",
"cn=Micaela Doud,ou=Janitorial,dc=bitwarden,dc=com",
],
groupMemberReferenceIds: [],
users: [],
referenceId: "cn=Red Team,dc=bitwarden,dc=com",
externalId: "cn=Red Team,dc=bitwarden,dc=com",
name: "Red Team",
},
{
userMemberExternalIds: [],
groupMemberReferenceIds: [],
users: [],
referenceId: "cn=Cleaners,ou=Janitorial,dc=bitwarden,dc=com",
externalId: "cn=Cleaners,ou=Janitorial,dc=bitwarden,dc=com",
name: "Cleaners",
},
];
export const groupFixtures = data.map((g) => GroupEntry.fromJSON(g));

View File

@@ -0,0 +1,691 @@
version: 1
dn: dc=bitwarden,dc=com
dc: bitwarden
objectClass: dcObject
objectClass: organization
o: Bitwarden
dn: ou=Accounting,dc=bitwarden,dc=com
changetype: add
ou: Accounting
objectClass: top
objectClass: organizationalUnit
dn: ou=Product Development,dc=bitwarden, dc=com
changetype: add
ou: Product Development
objectClass: top
objectClass: organizationalUnit
dn: ou=Product Testing,dc=bitwarden, dc=com
changetype: add
ou: Product Testing
objectClass: top
objectClass: organizationalUnit
dn: ou=Human Resources,dc=bitwarden, dc=com
changetype: add
ou: Human Resources
objectClass: top
objectClass: organizationalUnit
dn: ou=Payroll,dc=bitwarden, dc=com
changetype: add
ou: Payroll
objectClass: top
objectClass: organizationalUnit
dn: ou=Janitorial,dc=bitwarden, dc=com
changetype: add
ou: Janitorial
objectClass: top
objectClass: organizationalUnit
dn: ou=Management,dc=bitwarden, dc=com
changetype: add
ou: Management
objectClass: top
objectClass: organizationalUnit
dn: ou=Administrative,dc=bitwarden, dc=com
changetype: add
ou: Administrative
objectClass: top
objectClass: organizationalUnit
dn: ou=Peons,dc=bitwarden, dc=com
changetype: add
ou: Peons
objectClass: top
objectClass: organizationalUnit
dn: ou=Planning,dc=bitwarden, dc=com
changetype: add
ou: Planning
objectClass: top
objectClass: organizationalUnit
dn: cn=Blue Team,dc=bitwarden,dc=com
changetype: add
cn: Blue Team
gidnumber: 500
memberuid: cn=Loella Mak,ou=Payroll,dc=bitwarden,dc=com
memberuid: cn=Painterson Miki,ou=Product Development,dc=bitwarden,dc=com
memberuid: cn=Roland Dyke,ou=Human Resources,dc=bitwarden,dc=com
objectclass: posixGroup
objectclass: top
dn: cn=Red Team,dc=bitwarden,dc=com
changetype: add
cn: Red Team
gidnumber: 600
memberuid: cn=Shiela Harada,ou=Peons,dc=bitwarden,dc=com
memberuid: cn=Micaela Doud,ou=Janitorial,dc=bitwarden,dc=com
objectclass: posixGroup
objectclass: top
dn: cn=Cleaners,ou=Janitorial,dc=bitwarden,dc=com
changetype: add
cn: Cleaners
gidnumber: 700
objectclass: posixGroup
objectclass: top
dn: cn=Roland Dyke,ou=Human Resources,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Roland Dyke
sn: Dyke
description: This is Roland Dyke's description
facsimileTelephoneNumber: +1 804 674-5794
l: San Francisco
ou: Human Resources
postalAddress: Human Resources$San Francisco
telephoneNumber: +1 804 831-5121
title: Supreme Human Resources Writer
userPassword: Password1
uid: DykeR
givenName: Roland
mail: DykeR@220af87272f04218bb8dd81d50fb19f5.bitwarden.com
carLicense: 4CMGOJ
departmentNumber: 2838
employeeType: Contract
homePhone: +1 804 936-4965
initials: R. D.
mobile: +1 804 592-3734
pager: +1 804 285-2962
roomNumber: 9890
secretary: cn=Keven Gilleland,ou=Administrative,dc=bitwarden, dc=com
manager: cn=Linnell Kinstley,ou=Product Development,dc=bitwarden, dc=com
dn: cn=Charin Goulfine,ou=Human Resources,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Charin Goulfine
sn: Goulfine
description: This is Charin Goulfine's description
facsimileTelephoneNumber: +1 408 916-3906
l: Redmond
ou: Human Resources
postalAddress: Human Resources$Redmond
telephoneNumber: +1 408 551-2393
title: Junior Human Resources Punk
userPassword: Password1
uid: GoulfinC
givenName: Charin
mail: GoulfinC@5fa9a69302a9422abedbd51aa69f472b.bitwarden.com
carLicense: FVUOG5
departmentNumber: 9411
employeeType: Contract
homePhone: +1 408 606-3616
initials: C. G.
mobile: +1 408 369-8824
pager: +1 408 930-6584
roomNumber: 9793
dn: cn=Margit Peters,ou=Product Testing,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Margit Peters
sn: Peters
description: This is Margit Peters's description
facsimileTelephoneNumber: +1 804 822-1892
l: Orem
ou: Product Testing
postalAddress: Product Testing$Orem
telephoneNumber: +1 804 930-5577
title: Supreme Product Testing Dictator
userPassword: Password1
uid: PetersM
givenName: Margit
mail: PetersM@909269ac94be4e5b9ff6809f52b1dda3.bitwarden.com
carLicense: IKKDEY
departmentNumber: 5202
employeeType: Employee
homePhone: +1 804 405-5226
initials: M. P.
mobile: +1 804 210-8735
pager: +1 804 533-8191
roomNumber: 9814
manager: cn=Inga Schnirer,ou=Product Testing,dc=bitwarden, dc=com
secretary: cn=Keven Gilleland,ou=Administrative,dc=bitwarden, dc=com
dn: cn=Shiela Harada,ou=Peons,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Shiela Harada
sn: Harada
description: This is Shiela Harada's description
facsimileTelephoneNumber: +1 510 773-7858
l: Cambridge
ou: Peons
postalAddress: Peons$Cambridge
telephoneNumber: +1 510 358-8117
title: Supreme Peons Technician
userPassword: Password1
uid: HaradaS
givenName: Shiela
mail: HaradaS@2782a7fada1240d682fc754affb31519.bitwarden.com
carLicense: 9KNKLC
departmentNumber: 8729
employeeType: Normal
homePhone: +1 510 412-1076
initials: S. H.
mobile: +1 510 458-4453
pager: +1 510 554-4842
roomNumber: 9387
manager: cn=Roland Dyke,ou=Human Resources,dc=bitwarden, dc=com
secretary: cn=Keven Gilleland,ou=Administrative,dc=bitwarden, dc=com
dn: cn=Angelle Guarino,ou=Human Resources,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Angelle Guarino
sn: Guarino
description: This is Angelle Guarino's description
facsimileTelephoneNumber: +1 510 783-6849
l: Armonk
ou: Human Resources
postalAddress: Human Resources$Armonk
telephoneNumber: +1 510 560-4384
title: Junior Human Resources Vice President
userPassword: Password1
uid: GuarinoA
givenName: Angelle
mail: GuarinoA@720ab4266da34c8e9ccf5ef3370b892b.bitwarden.com
carLicense: K24T4G
departmentNumber: 5102
employeeType: Normal
homePhone: +1 510 712-7834
initials: A. G.
mobile: +1 510 528-1331
pager: +1 510 991-3515
roomNumber: 8053
manager: cn=Roland Dyke,ou=Human Resources,dc=bitwarden, dc=com
secretary: cn=Lynnea Dasilva,ou=Janitorial,dc=bitwarden, dc=com
dn: cn=Shela Khoury,ou=Peons,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Shela Khoury
sn: Khoury
description: This is Shela Khoury's description
facsimileTelephoneNumber: +1 415 829-6000
l: San Mateo
ou: Peons
postalAddress: Peons$San Mateo
telephoneNumber: +1 415 518-4568
title: Supreme Peons Sales Rep
userPassword: Password1
uid: KhouryS
givenName: Shela
mail: KhouryS@4dc1a2d970bb4c23aef0d860b6018ed6.bitwarden.com
carLicense: VX8Y9Q
departmentNumber: 5125
employeeType: Contract
homePhone: +1 415 368-5121
initials: S. K.
mobile: +1 415 224-9289
pager: +1 415 722-7004
roomNumber: 9511
manager: cn=Tilmon Kuzbary,ou=Human Resources,dc=bitwarden, dc=com
secretary: cn=Vicuong Dyba,ou=Product Development,dc=bitwarden, dc=com
dn: cn=Micaela Doud,ou=Janitorial,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Micaela Doud
sn: Doud
description: This is Micaela Doud's description
facsimileTelephoneNumber: +1 804 989-1007
l: Sunnyvale
ou: Janitorial
postalAddress: Janitorial$Sunnyvale
telephoneNumber: +1 804 763-6986
title: Associate Janitorial Warrior
userPassword: Password1
uid: DoudM
givenName: Micaela
mail: DoudM@b2de0606c7904578b184a63046aa1a59.bitwarden.com
carLicense: U51VUR
departmentNumber: 3449
employeeType: Employee
homePhone: +1 804 833-8174
initials: M. D.
mobile: +1 804 255-5188
pager: +1 804 908-3999
roomNumber: 9336
manager: cn=Roland Dyke,ou=Human Resources,dc=bitwarden, dc=com
secretary: cn=Lynnea Dasilva,ou=Janitorial,dc=bitwarden, dc=com
dn: cn=Marthe Kenik,ou=Product Testing,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Marthe Kenik
sn: Kenik
description: This is Marthe Kenik's description
facsimileTelephoneNumber: +1 510 622-1104
l: San Jose
ou: Product Testing
postalAddress: Product Testing$San Jose
telephoneNumber: +1 510 752-7722
title: Master Product Testing Director
userPassword: Password1
uid: KenikM
givenName: Marthe
mail: KenikM@5ce7099e829941498f32f6630dda9440.bitwarden.com
carLicense: R07O7A
departmentNumber: 4904
employeeType: Employee
homePhone: +1 510 326-5669
initials: M. K.
mobile: +1 510 270-2177
pager: +1 510 375-6349
roomNumber: 9653
manager: cn=Roland Dyke,ou=Human Resources,dc=bitwarden, dc=com
secretary: cn=Keven Gilleland,ou=Administrative,dc=bitwarden, dc=com
dn: cn=Angus Merizzi,ou=Management,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Angus Merizzi
sn: Merizzi
description: This is Angus Merizzi's description
facsimileTelephoneNumber: +1 818 188-3223
l: Palo Alto
ou: Management
postalAddress: Management$Palo Alto
telephoneNumber: +1 818 210-9559
title: Master Management Technician
userPassword: Password1
uid: MerizziA
givenName: Angus
mail: MerizziA@7f1912f54e7a4efa8a33a6ba82fc7102.bitwarden.com
carLicense: W89F9T
departmentNumber: 9737
employeeType: Normal
homePhone: +1 818 529-9985
initials: A. M.
mobile: +1 818 317-4882
pager: +1 818 942-6439
roomNumber: 9060
manager: cn=Inga Schnirer,ou=Product Testing,dc=bitwarden, dc=com
secretary: cn=Keven Gilleland,ou=Administrative,dc=bitwarden, dc=com
dn: cn=Edmund Kardos,ou=Product Testing,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Edmund Kardos
sn: Kardos
description: This is Edmund Kardos's description
facsimileTelephoneNumber: +1 804 374-4959
l: Redwood Shores
ou: Product Testing
postalAddress: Product Testing$Redwood Shores
telephoneNumber: +1 804 624-9693
title: Supreme Product Testing Janitor
userPassword: Password1
uid: KardosE
givenName: Edmund
mail: KardosE@5f41ae90bad64d5e97b0ef849f0cfd8f.bitwarden.com
carLicense: 7N9K02
departmentNumber: 2240
employeeType: Normal
homePhone: +1 804 100-4518
initials: E. K.
mobile: +1 804 604-1454
pager: +1 804 472-8093
roomNumber: 9668
manager: cn=Tilmon Kuzbary,ou=Human Resources,dc=bitwarden, dc=com
secretary: cn=Lynnea Dasilva,ou=Janitorial,dc=bitwarden, dc=com
dn: cn=Joyann Frucci,ou=Administrative,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Joyann Frucci
sn: Frucci
description: This is Joyann Frucci's description
facsimileTelephoneNumber: +1 804 209-8112
l: San Jose
ou: Administrative
postalAddress: Administrative$San Jose
telephoneNumber: +1 804 384-8334
title: Supreme Administrative Manager
userPassword: Password1
uid: FrucciJ
givenName: Joyann
mail: FrucciJ@55f85638346c4f81b665496e3fee8d10.bitwarden.com
carLicense: 7HRDG5
departmentNumber: 8188
employeeType: Contract
homePhone: +1 804 364-9627
initials: J. F.
mobile: +1 804 578-9507
pager: +1 804 336-2260
roomNumber: 9547
manager: cn=Tilmon Kuzbary,ou=Human Resources,dc=bitwarden, dc=com
secretary: cn=Lynnea Dasilva,ou=Janitorial,dc=bitwarden, dc=com
dn: cn=Painterson Miki,ou=Product Development,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Painterson Miki
sn: Miki
description: This is Painterson Miki's description
facsimileTelephoneNumber: +1 206 873-3690
l: Alameda
ou: Product Development
postalAddress: Product Development$Alameda
telephoneNumber: +1 206 715-5086
title: Supreme Product Development Dictator
userPassword: Password1
uid: MikiP
givenName: Painterson
mail: MikiP@2c4fec4ef77046e1b1e4b34fd50dd6a9.bitwarden.com
carLicense: 194N6J
departmentNumber: 8374
employeeType: Normal
homePhone: +1 206 983-4256
initials: P. M.
mobile: +1 206 124-4934
pager: +1 206 554-6019
roomNumber: 9715
manager: cn=Roland Dyke,ou=Human Resources,dc=bitwarden, dc=com
secretary: cn=Keven Gilleland,ou=Administrative,dc=bitwarden, dc=com
dn: cn=Jammie De Los,ou=Peons,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Jammie De Los
sn: De Los
description: This is Jammie De Los's description
facsimileTelephoneNumber: +1 818 184-1742
l: San Mateo
ou: Peons
postalAddress: Peons$San Mateo
telephoneNumber: +1 818 390-3544
title: Chief Peons Visionary
userPassword: Password1
uid: De LosJ
givenName: Jammie
mail: DeLosJ@8575686b747a4c5ab6b0a7ac30503d95.bitwarden.com
carLicense: 0GKM0H
departmentNumber: 2547
employeeType: Normal
homePhone: +1 818 277-5492
initials: J. D.
mobile: +1 818 560-9044
pager: +1 818 390-1519
roomNumber: 9392
manager: cn=Roland Dyke,ou=Human Resources,dc=bitwarden, dc=com
secretary: cn=Vicuong Dyba,ou=Product Development,dc=bitwarden, dc=com
dn: cn=Virgina Pichocki,ou=Product Development,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Virgina Pichocki
sn: Pichocki
description: This is Virgina Pichocki's description
facsimileTelephoneNumber: +1 415 921-7139
l: San Francisco
ou: Product Development
postalAddress: Product Development$San Francisco
telephoneNumber: +1 415 400-5576
title: Master Product Development Evangelist
userPassword: Password1
uid: PichockV
givenName: Virgina
mail: PichockV@2b7d385172624c81935f26cfb5f852c0.bitwarden.com
carLicense: N25ABP
departmentNumber: 2635
employeeType: Normal
homePhone: +1 415 736-3487
initials: V. P.
mobile: +1 415 122-7774
pager: +1 415 305-4401
roomNumber: 8444
manager: cn=Inga Schnirer,ou=Product Testing,dc=bitwarden, dc=com
secretary: cn=Vicuong Dyba,ou=Product Development,dc=bitwarden, dc=com
dn: cn=Steffen Carsten,ou=Product Development,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Steffen Carsten
sn: Carsten
description: This is Steffen Carsten's description
facsimileTelephoneNumber: +1 206 207-4197
l: Orem
ou: Product Development
postalAddress: Product Development$Orem
telephoneNumber: +1 206 973-4002
title: Associate Product Development Czar
userPassword: Password1
uid: CarstenS
givenName: Steffen
mail: CarstenS@c8b8d0d540194a31b14e399b8e0ac7ff.bitwarden.com
carLicense: URONSV
departmentNumber: 3796
employeeType: Employee
homePhone: +1 206 494-8029
initials: S. C.
mobile: +1 206 667-8663
pager: +1 206 183-2075
roomNumber: 9917
manager: cn=Inga Schnirer,ou=Product Testing,dc=bitwarden, dc=com
secretary: cn=Keven Gilleland,ou=Administrative,dc=bitwarden, dc=com
dn: cn=Elna Drescher,ou=Administrative,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Elna Drescher
sn: Drescher
description: This is Elna Drescher's description
facsimileTelephoneNumber: +1 408 792-9983
l: Cambridge
ou: Administrative
postalAddress: Administrative$Cambridge
telephoneNumber: +1 408 697-1740
title: Master Administrative Writer
userPassword: Password1
uid: DrescheE
givenName: Elna
mail: DrescheE@4ab96258683b45799b4cb34e5e13e2ee.bitwarden.com
carLicense: NYIO0U
departmentNumber: 4493
employeeType: Contract
homePhone: +1 408 947-7311
initials: E. D.
mobile: +1 408 266-4623
pager: +1 408 793-5306
roomNumber: 8652
manager: cn=Tilmon Kuzbary,ou=Human Resources,dc=bitwarden, dc=com
secretary: cn=Lynnea Dasilva,ou=Janitorial,dc=bitwarden, dc=com
dn: cn=Gwen Kardomateas,ou=Product Development,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Gwen Kardomateas
sn: Kardomateas
description: This is Gwen Kardomateas's description
facsimileTelephoneNumber: +1 415 281-2558
l: Alameda
ou: Product Development
postalAddress: Product Development$Alameda
telephoneNumber: +1 415 920-9857
title: Master Product Development Punk
userPassword: Password1
uid: KardomaG
givenName: Gwen
mail: KardomaG@7bc2a22aa7d345f1bef866ce890bec49.bitwarden.com
carLicense: Q9JPFV
departmentNumber: 7746
employeeType: Contract
homePhone: +1 415 256-9106
initials: G. K.
mobile: +1 415 649-9091
pager: +1 415 896-8495
roomNumber: 8294
manager: cn=Roland Dyke,ou=Human Resources,dc=bitwarden, dc=com
secretary: cn=Vicuong Dyba,ou=Product Development,dc=bitwarden, dc=com
dn: cn=Reeta Roldan,ou=Administrative,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Reeta Roldan
sn: Roldan
description: This is Reeta Roldan's description
facsimileTelephoneNumber: +1 510 354-2580
l: Santa Clara
ou: Administrative
postalAddress: Administrative$Santa Clara
telephoneNumber: +1 510 144-9610
title: Chief Administrative Fellow
userPassword: Password1
uid: RoldanR
givenName: Reeta
mail: RoldanR@108c2ffc1189457d80b27e9b862163f4.bitwarden.com
carLicense: WD7J8D
departmentNumber: 1219
employeeType: Contract
homePhone: +1 510 749-9885
initials: R. R.
mobile: +1 510 356-5695
pager: +1 510 216-4863
roomNumber: 8243
manager: cn=Roland Dyke,ou=Human Resources,dc=bitwarden, dc=com
secretary: cn=Vicuong Dyba,ou=Product Development,dc=bitwarden, dc=com
dn: cn=Grissel Currer,ou=Management,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Grissel Currer
sn: Currer
description: This is Grissel Currer's description
facsimileTelephoneNumber: +1 818 393-4507
l: Sunnyvale
ou: Management
postalAddress: Management$Sunnyvale
telephoneNumber: +1 818 522-3933
title: Junior Management Artist
userPassword: Password1
uid: CurrerG
givenName: Grissel
mail: CurrerG@248d38bb7c664c8f9d2a64525819610e.bitwarden.com
carLicense: M8TKUS
departmentNumber: 1814
employeeType: Normal
homePhone: +1 818 803-8000
initials: G. C.
mobile: +1 818 714-9944
pager: +1 818 100-5018
roomNumber: 9199
manager: cn=Inga Schnirer,ou=Product Testing,dc=bitwarden, dc=com
secretary: cn=Vicuong Dyba,ou=Product Development,dc=bitwarden, dc=com
dn: cn=Loella Mak,ou=Payroll,dc=bitwarden, dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Loella Mak
sn: Mak
description: This is Loella Mak's description
facsimileTelephoneNumber: +1 804 152-4298
l: Menlo Park
ou: Payroll
postalAddress: Payroll$Menlo Park
telephoneNumber: +1 804 289-8864
title: Junior Payroll Punk
userPassword: Password1
uid: MakL
givenName: Loella
mail: MakL@6ab3e25ca49d4d64aaf44844288a8ef7.bitwarden.com
carLicense: YVN4UL
departmentNumber: 5904
employeeType: Employee
homePhone: +1 804 749-7856
initials: L. M.
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

10
openldap/mkcert.sh Executable file
View File

@@ -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 ./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

149
openldap/user-fixtures.ts Normal file
View File

@@ -0,0 +1,149 @@
import { Jsonify } from "type-fest";
import { UserEntry } from "../src/models/userEntry";
// These must match the ldap server seed data in directory.ldif
const data: Jsonify<UserEntry>[] = [
{
disabled: false,
deleted: false,
referenceId: "cn=Roland Dyke,ou=Human Resources,dc=bitwarden,dc=com",
externalId: "cn=Roland Dyke,ou=Human Resources,dc=bitwarden,dc=com",
email: "dyker@220af87272f04218bb8dd81d50fb19f5.bitwarden.com",
},
{
disabled: false,
deleted: false,
referenceId: "cn=Charin Goulfine,ou=Human Resources,dc=bitwarden,dc=com",
externalId: "cn=Charin Goulfine,ou=Human Resources,dc=bitwarden,dc=com",
email: "goulfinc@5fa9a69302a9422abedbd51aa69f472b.bitwarden.com",
},
{
disabled: false,
deleted: false,
referenceId: "cn=Margit Peters,ou=Product Testing,dc=bitwarden,dc=com",
externalId: "cn=Margit Peters,ou=Product Testing,dc=bitwarden,dc=com",
email: "petersm@909269ac94be4e5b9ff6809f52b1dda3.bitwarden.com",
},
{
disabled: false,
deleted: false,
referenceId: "cn=Shiela Harada,ou=Peons,dc=bitwarden,dc=com",
externalId: "cn=Shiela Harada,ou=Peons,dc=bitwarden,dc=com",
email: "haradas@2782a7fada1240d682fc754affb31519.bitwarden.com",
},
{
disabled: false,
deleted: false,
referenceId: "cn=Angelle Guarino,ou=Human Resources,dc=bitwarden,dc=com",
externalId: "cn=Angelle Guarino,ou=Human Resources,dc=bitwarden,dc=com",
email: "guarinoa@720ab4266da34c8e9ccf5ef3370b892b.bitwarden.com",
},
{
disabled: false,
deleted: false,
referenceId: "cn=Shela Khoury,ou=Peons,dc=bitwarden,dc=com",
externalId: "cn=Shela Khoury,ou=Peons,dc=bitwarden,dc=com",
email: "khourys@4dc1a2d970bb4c23aef0d860b6018ed6.bitwarden.com",
},
{
disabled: false,
deleted: false,
referenceId: "cn=Micaela Doud,ou=Janitorial,dc=bitwarden,dc=com",
externalId: "cn=Micaela Doud,ou=Janitorial,dc=bitwarden,dc=com",
email: "doudm@b2de0606c7904578b184a63046aa1a59.bitwarden.com",
},
{
disabled: false,
deleted: false,
referenceId: "cn=Marthe Kenik,ou=Product Testing,dc=bitwarden,dc=com",
externalId: "cn=Marthe Kenik,ou=Product Testing,dc=bitwarden,dc=com",
email: "kenikm@5ce7099e829941498f32f6630dda9440.bitwarden.com",
},
{
disabled: false,
deleted: false,
referenceId: "cn=Angus Merizzi,ou=Management,dc=bitwarden,dc=com",
externalId: "cn=Angus Merizzi,ou=Management,dc=bitwarden,dc=com",
email: "merizzia@7f1912f54e7a4efa8a33a6ba82fc7102.bitwarden.com",
},
{
disabled: false,
deleted: false,
referenceId: "cn=Edmund Kardos,ou=Product Testing,dc=bitwarden,dc=com",
externalId: "cn=Edmund Kardos,ou=Product Testing,dc=bitwarden,dc=com",
email: "kardose@5f41ae90bad64d5e97b0ef849f0cfd8f.bitwarden.com",
},
{
disabled: false,
deleted: false,
referenceId: "cn=Joyann Frucci,ou=Administrative,dc=bitwarden,dc=com",
externalId: "cn=Joyann Frucci,ou=Administrative,dc=bitwarden,dc=com",
email: "fruccij@55f85638346c4f81b665496e3fee8d10.bitwarden.com",
},
{
disabled: false,
deleted: false,
referenceId: "cn=Painterson Miki,ou=Product Development,dc=bitwarden,dc=com",
externalId: "cn=Painterson Miki,ou=Product Development,dc=bitwarden,dc=com",
email: "mikip@2c4fec4ef77046e1b1e4b34fd50dd6a9.bitwarden.com",
},
{
disabled: false,
deleted: false,
referenceId: "cn=Jammie De Los,ou=Peons,dc=bitwarden,dc=com",
externalId: "cn=Jammie De Los,ou=Peons,dc=bitwarden,dc=com",
email: "delosj@8575686b747a4c5ab6b0a7ac30503d95.bitwarden.com",
},
{
disabled: false,
deleted: false,
referenceId: "cn=Virgina Pichocki,ou=Product Development,dc=bitwarden,dc=com",
externalId: "cn=Virgina Pichocki,ou=Product Development,dc=bitwarden,dc=com",
email: "pichockv@2b7d385172624c81935f26cfb5f852c0.bitwarden.com",
},
{
disabled: false,
deleted: false,
referenceId: "cn=Steffen Carsten,ou=Product Development,dc=bitwarden,dc=com",
externalId: "cn=Steffen Carsten,ou=Product Development,dc=bitwarden,dc=com",
email: "carstens@c8b8d0d540194a31b14e399b8e0ac7ff.bitwarden.com",
},
{
disabled: false,
deleted: false,
referenceId: "cn=Elna Drescher,ou=Administrative,dc=bitwarden,dc=com",
externalId: "cn=Elna Drescher,ou=Administrative,dc=bitwarden,dc=com",
email: "dreschee@4ab96258683b45799b4cb34e5e13e2ee.bitwarden.com",
},
{
disabled: false,
deleted: false,
referenceId: "cn=Gwen Kardomateas,ou=Product Development,dc=bitwarden,dc=com",
externalId: "cn=Gwen Kardomateas,ou=Product Development,dc=bitwarden,dc=com",
email: "kardomag@7bc2a22aa7d345f1bef866ce890bec49.bitwarden.com",
},
{
disabled: false,
deleted: false,
referenceId: "cn=Reeta Roldan,ou=Administrative,dc=bitwarden,dc=com",
externalId: "cn=Reeta Roldan,ou=Administrative,dc=bitwarden,dc=com",
email: "roldanr@108c2ffc1189457d80b27e9b862163f4.bitwarden.com",
},
{
disabled: false,
deleted: false,
referenceId: "cn=Grissel Currer,ou=Management,dc=bitwarden,dc=com",
externalId: "cn=Grissel Currer,ou=Management,dc=bitwarden,dc=com",
email: "currerg@248d38bb7c664c8f9d2a64525819610e.bitwarden.com",
},
{
disabled: false,
deleted: false,
referenceId: "cn=Loella Mak,ou=Payroll,dc=bitwarden,dc=com",
externalId: "cn=Loella Mak,ou=Payroll,dc=bitwarden,dc=com",
email: "makl@6ab3e25ca49d4d64aaf44844288a8ef7.bitwarden.com",
},
];
export const userFixtures = data.map((v) => UserEntry.fromJSON(v));

View File

@@ -64,9 +64,12 @@
"publish:win": "npm run build:dist && npm run clean:dist && electron-builder --win --x64 --ia32 -p always -c.win.certificateSubjectName=\"8bit Solutions LLC\"", "publish:win": "npm run build:dist && npm run clean:dist && electron-builder --win --x64 --ia32 -p always -c.win.certificateSubjectName=\"8bit Solutions LLC\"",
"prettier": "prettier --write .", "prettier": "prettier --write .",
"prepare": "husky install", "prepare": "husky install",
"test": "jest", "test": "jest --testPathIgnorePatterns=.integration.spec.ts",
"test:watch": "jest --watch", "test:watch": "jest --watch --testPathIgnorePatterns=.integration.spec.ts",
"test:watch:all": "jest --watchAll", "test:watch:all": "jest --watchAll --testPathIgnorePatterns=.integration.spec.ts",
"test:integration": "jest .integration.spec.ts",
"test:integration:watch": "jest .integration.spec.ts --watch",
"test:integration:setup": "sh ./openldap/mkcert.sh && docker compose up -d",
"test:types": "npx tsc --noEmit" "test:types": "npx tsc --noEmit"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -1,3 +1,5 @@
import { Jsonify } from "type-fest";
import { Entry } from "./entry"; import { Entry } from "./entry";
import { UserEntry } from "./userEntry"; import { UserEntry } from "./userEntry";
@@ -14,4 +16,38 @@ export class GroupEntry extends Entry {
return this.name; return this.name;
} }
toJSON() {
return {
name: this.name,
referenceId: this.referenceId,
externalId: this.externalId,
userMemberExternalIds:
this.userMemberExternalIds == null ? null : [...this.userMemberExternalIds],
groupMemberReferenceIds:
this.groupMemberReferenceIds == null ? null : [...this.groupMemberReferenceIds],
users: this.users?.map((u) => u.toJSON()),
};
}
static fromJSON(data: Jsonify<GroupEntry>) {
const result = new GroupEntry();
result.referenceId = data.referenceId;
result.externalId = data.externalId;
result.name = data.name;
if (data.userMemberExternalIds != null) {
result.userMemberExternalIds = new Set(data.userMemberExternalIds);
}
if (data.groupMemberReferenceIds != null) {
result.groupMemberReferenceIds = new Set(data.groupMemberReferenceIds);
}
if (data.users != null) {
result.users = data.users.map((u) => UserEntry.fromJSON(u));
}
return result;
}
} }

View File

@@ -1,3 +1,5 @@
import { Jsonify } from "type-fest";
import { Entry } from "./entry"; import { Entry } from "./entry";
export class UserEntry extends Entry { export class UserEntry extends Entry {
@@ -12,4 +14,26 @@ export class UserEntry extends Entry {
return this.email; return this.email;
} }
toJSON() {
return {
referenceId: this.referenceId,
externalId: this.externalId,
email: this.email,
disabled: this.disabled,
deleted: this.deleted,
};
}
static fromJSON(data: Jsonify<UserEntry>) {
const result = new UserEntry();
result.referenceId = data.referenceId;
result.externalId = data.externalId;
result.email = data.email;
result.disabled = data.disabled;
result.deleted = data.deleted;
return result;
}
} }

View File

@@ -0,0 +1,207 @@
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 { groupFixtures } from "../../openldap/group-fixtures";
import { userFixtures } from "../../openldap/user-fixtures";
import { DirectoryType } from "../enums/directoryType";
import { LdapConfiguration } from "../models/ldapConfiguration";
import { SyncConfiguration } from "../models/syncConfiguration";
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:
// 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: "./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: "./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]);
});
});
});
/**
* @returns a basic ldap configuration without TLS/SSL enabled. Can be overridden by passing in a partial configuration.
*/
const getLdapConfiguration = (config?: Partial<LdapConfiguration>): LdapConfiguration => ({
ssl: false,
startTls: false,
tlsCaPath: null,
sslAllowUnauthorized: false,
sslCertPath: null,
sslKeyPath: null,
sslCaPath: null,
hostname: "localhost",
port: 1389,
domain: null,
rootPath: "dc=bitwarden,dc=com",
currentUser: false,
username: "cn=admin,dc=bitwarden,dc=com",
password: "admin",
ad: false,
pagedSearch: false,
...(config ?? {}),
});
/**
* @returns a basic sync configuration. Can be overridden by passing in a partial configuration.
*/
const getSyncConfiguration = (config?: Partial<SyncConfiguration>): SyncConfiguration => ({
users: false,
groups: false,
interval: 5,
userFilter: null,
groupFilter: null,
removeDisabled: false,
overwriteExisting: false,
largeImport: false,
// Ldap properties
groupObjectClass: "posixGroup",
userObjectClass: "person",
groupPath: null,
userPath: null,
groupNameAttribute: "cn",
userEmailAttribute: "mail",
memberAttribute: "memberUid",
useEmailPrefixSuffix: false,
emailPrefixAttribute: "sAMAccountName",
emailSuffix: null,
creationDateAttribute: "whenCreated",
revisionDateAttribute: "whenChanged",
...(config ?? {}),
});