1
0
mirror of https://github.com/bitwarden/directory-connector synced 2026-02-04 02:33:47 +00:00

Compare commits

..

2 Commits

Author SHA1 Message Date
jrmccannon
79696a4889 update package-lock.json 2025-07-22 14:10:49 -05:00
jrmccannon
e1ba811306 testing for Ac 2025-07-22 11:44:01 -05:00
33 changed files with 3862 additions and 8316 deletions

18
.github/renovate.json vendored Normal file
View File

@@ -0,0 +1,18 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["github>bitwarden/renovate-config"],
"enabledManagers": ["github-actions", "npm"],
"packageRules": [
{
"groupName": "gh minor",
"matchManagers": ["github-actions"],
"matchUpdateTypes": ["minor", "patch"]
},
{
"groupName": "Google Libraries",
"matchPackagePatterns": ["google-auth-library", "googleapis"],
"matchManagers": ["npm"],
"groupSlug": "google-libraries"
}
]
}

View File

@@ -1,25 +0,0 @@
{
$schema: "https://docs.renovatebot.com/renovate-schema.json",
extends: ["github>bitwarden/renovate-config"],
enabledManagers: ["github-actions", "npm"],
packageRules: [
{
groupName: "gh minor",
matchManagers: ["github-actions"],
matchUpdateTypes: ["minor", "patch"],
},
{
groupName: "Google Libraries",
matchPackagePatterns: ["google-auth-library", "googleapis"],
matchManagers: ["npm"],
groupSlug: "google-libraries",
},
],
ignoreDeps: [
// yao-pkg is used to create a single executable application bundle for the CLI.
// It is a third party build of node which carries a high supply chain risk.
// This must be manually vetted by our appsec team before upgrading.
// It is excluded from renovate to avoid accidentally upgrading to a non-vetted version.
"@yao-pkg/pkg",
],
}

View File

@@ -52,7 +52,7 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Node
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -128,7 +128,7 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Node
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -201,7 +201,7 @@ jobs:
choco install checksum --no-progress
- name: Set up Node
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -267,18 +267,17 @@ jobs:
env:
NODE_OPTIONS: --max_old_space_size=4096
_PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }}
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
HUSKY: 0
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Node
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: ${{ env._NODE_VERSION }}
node-version: '18'
- name: Update NPM
run: |
@@ -365,18 +364,17 @@ jobs:
env:
NODE_OPTIONS: --max_old_space_size=4096
_PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }}
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
HUSKY: 0
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Node
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: ${{ env._NODE_VERSION }}
node-version: '18'
- name: Update NPM
run: |
@@ -423,18 +421,17 @@ jobs:
env:
NODE_OPTIONS: --max_old_space_size=4096
_PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }}
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
HUSKY: 0
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Node
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: ${{ env._NODE_VERSION }}
node-version: '18'
- name: Update NPM
run: |

View File

@@ -10,14 +10,12 @@ on:
- "src/services/ldap-directory.service*" # we only have integration for LDAP testing at the moment
- "./openldap/**/*" # any change to test fixtures
- "./docker-compose.yml" # any change to Docker configuration
- "./package.json" # dependencies
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
- "./openldap/**/*" # any change to test fixtures
- "./docker-compose.yml" # any change to Docker configuration
- "./package.json" # dependencies
permissions:
contents: read
checks: write # required by dorny/test-reporter to upload its results
@@ -39,7 +37,7 @@ jobs:
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
- name: Set up Node
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -60,7 +58,7 @@ jobs:
run: npm run test:integration --coverage
- name: Report test results
uses: dorny/test-reporter@dc3a92680fcc15842eef52e8c4606ea7ce6bd3f3 # v2.1.1
uses: dorny/test-reporter@31a54ee7ebcacc03a09ea97a7e5465a47b84aea5 # v1.9.1
if: ${{ github.event.pull_request.head.repo.full_name == github.repository && !cancelled() }}
with:
name: Test Results

View File

@@ -32,7 +32,7 @@ jobs:
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
- name: Set up Node
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -51,7 +51,7 @@ jobs:
run: npm run test --coverage
- name: Report test results
uses: dorny/test-reporter@dc3a92680fcc15842eef52e8c4606ea7ce6bd3f3 # v2.1.1
uses: dorny/test-reporter@31a54ee7ebcacc03a09ea97a7e5465a47b84aea5 # v1.9.1
if: ${{ github.event.pull_request.head.repo.full_name == github.repository && !cancelled() }}
with:
name: Test Results

View File

@@ -42,7 +42,7 @@ jobs:
uses: bitwarden/gh-actions/azure-logout@main
- name: Generate GH App token
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
id: app-token
with:
app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }}

View File

@@ -60,7 +60,6 @@ import {
]),
],
preserveWhitespaces: false,
standalone: false,
})
export class BitwardenToast extends BaseToast {
constructor(

View File

@@ -2,7 +2,6 @@ import { Directive, ElementRef, Input, Renderer2 } from "@angular/core";
@Directive({
selector: "[appA11yTitle]",
standalone: false,
})
export class A11yTitleDirective {
@Input() set appA11yTitle(title: string) {

View File

@@ -13,7 +13,6 @@ import { ValidationService } from "../services/validation.service";
*/
@Directive({
selector: "[appApiAction]",
standalone: false,
})
export class ApiActionDirective implements OnChanges {
@Input() appApiAction: Promise<any>;

View File

@@ -5,7 +5,6 @@ import { Utils } from "@/jslib/common/src/misc/utils";
@Directive({
selector: "[appAutofocus]",
standalone: false,
})
export class AutofocusDirective {
@Input() set appAutofocus(condition: boolean | string) {

View File

@@ -2,7 +2,6 @@ import { Directive, ElementRef, HostListener } from "@angular/core";
@Directive({
selector: "[appBlurClick]",
standalone: false,
})
export class BlurClickDirective {
constructor(private el: ElementRef) {}

View File

@@ -2,7 +2,6 @@ import { Directive, ElementRef, HostListener, OnInit } from "@angular/core";
@Directive({
selector: "[appBoxRow]",
standalone: false,
})
export class BoxRowDirective implements OnInit {
el: HTMLElement = null;

View File

@@ -2,7 +2,6 @@ import { Directive, ElementRef, HostListener, Input } from "@angular/core";
@Directive({
selector: "[appFallbackSrc]",
standalone: false,
})
export class FallbackSrcDirective {
@Input("appFallbackSrc") appFallbackSrc: string;

View File

@@ -2,7 +2,6 @@ import { Directive, HostListener } from "@angular/core";
@Directive({
selector: "[appStopClick]",
standalone: false,
})
export class StopClickDirective {
@HostListener("click", ["$event"]) onClick($event: MouseEvent) {

View File

@@ -2,7 +2,6 @@ import { Directive, HostListener } from "@angular/core";
@Directive({
selector: "[appStopProp]",
standalone: false,
})
export class StopPropDirective {
@HostListener("click", ["$event"]) onClick($event: MouseEvent) {

View File

@@ -4,7 +4,6 @@ import { I18nService } from "@/jslib/common/src/abstractions/i18n.service";
@Pipe({
name: "i18n",
standalone: false,
})
export class I18nPipe implements PipeTransform {
constructor(private i18nService: I18nService) {}

View File

@@ -31,4 +31,7 @@ export class PasswordTokenRequest extends TokenRequest implements CaptchaProtect
return obj;
}
alterIdentityTokenHeaders(headers: Headers) {
headers.set("Auth-Email", Utils.fromUtf8ToUrlB64(this.email));
}
}

View File

@@ -35,29 +35,6 @@ const data: Jsonify<GroupEntry>[] = [
externalId: "cn=Cleaners,ou=Janitorial,dc=bitwarden,dc=com",
name: "Cleaners",
},
{
userMemberExternalIds: [
"cn=Painterson Miki,ou=Product Development,dc=bitwarden,dc=com",
"cn=Virgina Pichocki,ou=Product Development,dc=bitwarden,dc=com",
"cn=Steffen Carsten,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=Angus Merizzi,ou=Management,dc=bitwarden,dc=com",
"cn=Grissel Currer,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));

View File

@@ -688,27 +688,4 @@ 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
# DevOps Team and Security Team identify their members by the member uid attribute,
# instead of the member Dn attribute.
# These test that group membership by uid works correctly.
dn: cn=DevOps Team,dc=bitwarden,dc=com
changetype: add
cn: DevOps Team
gidnumber: 800
memberuid: mikip
memberuid: pichockv
memberuid: carstens
objectclass: posixGroup
objectclass: top
dn: cn=Security Team,dc=bitwarden,dc=com
changetype: add
cn: Security Team
gidnumber: 900
memberuid: merizzia
memberuid: currerg
objectclass: posixGroup
objectclass: top
secretary: cn=Keven Gilleland,ou=Administrative,dc=bitwarden, dc=com

11877
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
"name": "@bitwarden/directory-connector",
"productName": "Bitwarden Directory Connector",
"description": "Sync your user directory to your Bitwarden organization.",
"version": "2025.9.0",
"version": "2025.6.1",
"keywords": [
"bitwarden",
"password",
@@ -73,33 +73,33 @@
"test:types": "npx tsc --noEmit"
},
"devDependencies": {
"@angular-devkit/build-angular": "19.2.15",
"@angular-eslint/eslint-plugin-template": "19.8.0",
"@angular-eslint/template-parser": "19.8.0",
"@angular/compiler-cli": "19.2.14",
"@angular-devkit/build-angular": "17.3.17",
"@angular-eslint/eslint-plugin-template": "17.5.3",
"@angular-eslint/template-parser": "17.5.3",
"@angular/compiler-cli": "17.3.12",
"@electron/notarize": "2.5.0",
"@electron/rebuild": "4.0.1",
"@electron/rebuild": "3.7.2",
"@fluffy-spoon/substitute": "1.208.0",
"@microsoft/microsoft-graph-types": "2.40.0",
"@ngtools/webpack": "19.2.14",
"@ngtools/webpack": "17.3.17",
"@types/inquirer": "8.2.10",
"@types/jest": "29.5.14",
"@types/lowdb": "1.0.15",
"@types/node": "22.18.1",
"@types/node": "20.14.8",
"@types/node-fetch": "2.6.12",
"@types/node-forge": "1.3.11",
"@types/proper-lockfile": "4.1.4",
"@types/tldjs": "2.3.4",
"@typescript-eslint/eslint-plugin": "8.43.0",
"@typescript-eslint/parser": "8.43.0",
"@typescript-eslint/eslint-plugin": "8.35.0",
"@typescript-eslint/parser": "8.35.0",
"@yao-pkg/pkg": "5.16.1",
"clean-webpack-plugin": "4.0.0",
"concurrently": "9.2.0",
"concurrently": "9.1.2",
"copy-webpack-plugin": "13.0.0",
"cross-env": "7.0.3",
"css-loader": "7.1.2",
"dotenv": "17.2.0",
"electron": "38.1.0",
"dotenv": "16.5.0",
"electron": "34.5.8",
"electron-builder": "24.13.3",
"electron-log": "5.4.1",
"electron-reload": "2.0.0-alpha.1",
@@ -107,59 +107,59 @@
"electron-updater": "6.6.2",
"eslint": "8.57.1",
"eslint-config-prettier": "10.1.5",
"eslint-import-resolver-typescript": "4.4.4",
"eslint-plugin-import": "2.32.0",
"eslint-import-resolver-typescript": "3.7.0",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-rxjs": "5.0.3",
"eslint-plugin-rxjs-angular": "2.0.1",
"form-data": "4.0.4",
"form-data": "4.0.3",
"html-loader": "5.1.0",
"html-webpack-plugin": "5.6.3",
"husky": "9.1.7",
"jest": "29.7.0",
"jest-junit": "16.0.0",
"jest-mock-extended": "3.0.7",
"jest-mock-extended": "4.0.0",
"jest-preset-angular": "14.6.0",
"lint-staged": "16.1.2",
"mini-css-extract-plugin": "2.9.2",
"node-abi": "3.77.0",
"node-abi": "3.75.0",
"node-forge": "1.3.1",
"node-loader": "2.1.0",
"prettier": "3.6.2",
"prettier": "3.5.3",
"rimraf": "6.0.1",
"rxjs": "7.8.2",
"sass": "1.92.1",
"sass": "1.89.2",
"sass-loader": "16.0.5",
"ts-jest": "29.4.1",
"ts-jest": "29.4.0",
"ts-loader": "9.5.2",
"tsconfig-paths-webpack-plugin": "4.2.0",
"type-fest": "4.41.0",
"typescript": "5.8.3",
"webpack": "5.101.0",
"typescript": "5.4.5",
"webpack": "5.99.9",
"webpack-cli": "6.0.1",
"webpack-merge": "6.0.1",
"webpack-node-externals": "3.0.0",
"zone.js": "0.15.1"
"zone.js": "0.14.10"
},
"dependencies": {
"@angular/animations": "19.2.14",
"@angular/cdk": "19.2.14",
"@angular/cli": "19.2.14",
"@angular/common": "19.2.14",
"@angular/compiler": "19.2.14",
"@angular/core": "19.2.14",
"@angular/forms": "19.2.14",
"@angular/platform-browser": "19.2.14",
"@angular/platform-browser-dynamic": "19.2.14",
"@angular/router": "19.2.14",
"@angular/animations": "17.3.12",
"@angular/cdk": "17.3.10",
"@angular/common": "17.3.12",
"@angular/compiler": "17.3.12",
"@angular/core": "17.3.12",
"@angular/forms": "17.3.12",
"@angular/platform-browser": "17.3.12",
"@angular/platform-browser-dynamic": "17.3.12",
"@angular/router": "17.3.12",
"@microsoft/microsoft-graph-client": "3.0.7",
"big-integer": "1.6.52",
"bootstrap": "5.3.7",
"browser-hrtime": "1.1.8",
"chalk": "4.1.2",
"commander": "14.0.0",
"form-data": "4.0.4",
"google-auth-library": "10.3.0",
"googleapis": "153.0.0",
"core-js": "3.44.0",
"form-data": "4.0.3",
"google-auth-library": "10.1.0",
"googleapis": "152.0.0",
"googleapis-common": "8.0.0",
"https-proxy-agent": "7.0.6",
"inquirer": "8.2.6",
@@ -168,12 +168,12 @@
"lowdb": "1.0.0",
"ngx-toastr": "19.0.0",
"node-fetch": "2.7.0",
"parse5": "8.0.0",
"parse5": "7.3.0",
"proper-lockfile": "4.1.2",
"rxjs": "7.8.2",
"tldjs": "2.3.1",
"uuid": "11.1.0",
"zone.js": "0.15.1"
"zone.js": "0.14.10"
},
"engines": {
"node": "~20",

0
run-ci
View File

View File

@@ -16,7 +16,6 @@ import { EnvironmentComponent } from "./environment.component";
@Component({
selector: "app-apiKey",
templateUrl: "apiKey.component.html",
standalone: false,
})
// There is an eslint exception made here due to semantics.
// The eslint rule expects a typical takeUntil() pattern involving component destruction.

View File

@@ -8,7 +8,6 @@ import { PlatformUtilsService } from "@/jslib/common/src/abstractions/platformUt
@Component({
selector: "app-environment",
templateUrl: "environment.component.html",
standalone: false,
})
export class EnvironmentComponent extends BaseEnvironmentComponent {
constructor(

View File

@@ -28,7 +28,6 @@ const BroadcasterSubscriptionId = "AppComponent";
styles: [],
template: ` <ng-template #settings></ng-template>
<router-outlet></router-outlet>`,
standalone: false,
})
export class AppComponent implements OnInit {
@ViewChild("settings", { read: ViewContainerRef, static: true }) settingsRef: ViewContainerRef;

View File

@@ -1,3 +1,4 @@
import "core-js/stable";
import "zone.js";
import { NgModule } from "@angular/core";

View File

@@ -17,7 +17,6 @@ const BroadcasterSubscriptionId = "DashboardComponent";
@Component({
selector: "app-dashboard",
templateUrl: "dashboard.component.html",
standalone: false,
})
export class DashboardComponent implements OnInit, OnDestroy {
simGroups: GroupEntry[];
@@ -112,7 +111,7 @@ export class DashboardComponent implements OnInit, OnDestroy {
this.simEnabledUsers = result.enabledUsers;
this.simDisabledUsers = result.disabledUsers;
this.simDeletedUsers = result.deletedUsers;
} catch {
} catch (e) {
this.simGroups = null;
this.simUsers = null;
}

View File

@@ -12,7 +12,6 @@ const BroadcasterSubscriptionId = "MoreComponent";
@Component({
selector: "app-more",
templateUrl: "more.component.html",
standalone: false,
})
export class MoreComponent implements OnInit {
version: string;

View File

@@ -1,5 +1,4 @@
import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit } from "@angular/core";
import { webUtils } from "electron";
import { I18nService } from "@/jslib/common/src/abstractions/i18n.service";
import { LogService } from "@/jslib/common/src/abstractions/log.service";
@@ -17,7 +16,6 @@ import { ConnectorUtils } from "../../utils";
@Component({
selector: "app-settings",
templateUrl: "settings.component.html",
standalone: false,
})
export class SettingsComponent implements OnInit, OnDestroy {
directory: DirectoryType;
@@ -123,7 +121,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
return;
}
(this.ldap as any)[id] = webUtils.getPathForFile(filePicker.files[0]);
(this.ldap as any)[id] = filePicker.files[0].path;
// reset file input
// ref: https://stackoverflow.com/a/20552042
filePicker.type = "";

View File

@@ -3,6 +3,5 @@ import { Component } from "@angular/core";
@Component({
selector: "app-tabs",
templateUrl: "tabs.component.html",
standalone: false,
})
export class TabsComponent {}

View File

@@ -8,9 +8,8 @@ $theme-colors: (
"secondary": #ced4da,
"secondary-alt": #1a3b66,
);
$font-family-sans-serif:
"Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol";
$font-family-sans-serif: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
$h1-font-size: 2rem;
$h2-font-size: 1.3rem;

View File

@@ -118,7 +118,7 @@ export class LdapDirectoryService implements IDirectoryService {
[delControl],
);
return regularUsers.concat(deletedUsers);
} catch {
} catch (e) {
this.logService.warning("Cannot query deleted users.");
return regularUsers;
}
@@ -192,21 +192,14 @@ export class LdapDirectoryService implements IDirectoryService {
this.syncConfig.userFilter,
);
const userPath = this.makeSearchPath(this.syncConfig.userPath);
const userDnMap = new Map<string, string>();
const userUidMap = new Map<string, string>();
const userIdMap = 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);
userDnMap.set(dn, externalId);
if (uid != null) {
userUidMap.set(uid.toLowerCase(), externalId);
}
userIdMap.set(this.getReferenceId(se), this.getExternalId(se, this.getReferenceId(se)));
return se;
});
for (const se of groupSearchEntries) {
const group = this.buildGroup(se, userDnMap, userUidMap);
const group = this.buildGroup(se, userIdMap);
if (group != null) {
entries.push(group);
}
@@ -215,20 +208,7 @@ export class LdapDirectoryService implements IDirectoryService {
return entries;
}
/**
* Builds a GroupEntry from LDAP search results, including membership.
* Supports user membership by DN or UID and nested group membership by DN.
*
* @param searchEntry - The LDAP search entry containing group data
* @param userDnMap - Map of user DNs to their external IDs
* @param userUidMap - Map of user UIDs to their external IDs
* @returns A populated GroupEntry object, or null if the group lacks required properties
*/
private buildGroup(
searchEntry: any,
userDnMap: Map<string, string>,
userUidMap: Map<string, string>,
) {
private buildGroup(searchEntry: any, userMap: Map<string, string>) {
const group = new GroupEntry();
group.referenceId = this.getReferenceId(searchEntry);
if (group.referenceId == null) {
@@ -248,34 +228,11 @@ export class LdapDirectoryService implements IDirectoryService {
const members = this.getAttrVals<string>(searchEntry, this.syncConfig.memberAttribute);
if (members != null) {
// Parses a group member attribute and identifies it as a member DN, member Uid, or a group Dn
const getMemberAttributeType = (member: string): "memberDn" | "memberUid" | "groupDn" => {
const isDnLike = member.includes("=") && member.includes(",");
if (isDnLike) {
return userDnMap.has(member) ? "memberDn" : "groupDn";
}
return "memberUid";
};
for (const member of members) {
switch (getMemberAttributeType(member)) {
case "memberDn": {
const externalId = userDnMap.get(member);
if (externalId != null) {
group.userMemberExternalIds.add(externalId);
}
break;
}
case "memberUid": {
const externalId = userUidMap.get(member.toLowerCase());
if (externalId != null) {
group.userMemberExternalIds.add(externalId);
}
break;
}
case "groupDn":
group.groupMemberReferenceIds.add(member);
break;
for (const memDn of members) {
if (userMap.has(memDn) && !group.userMemberExternalIds.has(userMap.get(memDn))) {
group.userMemberExternalIds.add(userMap.get(memDn));
} else if (!group.groupMemberReferenceIds.has(memDn)) {
group.groupMemberReferenceIds.add(memDn);
}
}
}

View File

@@ -123,10 +123,7 @@ describe("SyncService", () => {
expect(apiService.postPublicImportDirectory).toHaveBeenCalledWith(
expect.objectContaining({ overwriteExisting: false }),
);
// The expected number of calls may change if more data is added to the ldif
// Make sure it equals (number of users / 4) + (number of groups / 4)
expect(apiService.postPublicImportDirectory).toHaveBeenCalledTimes(7);
expect(apiService.postPublicImportDirectory).toHaveBeenCalledTimes(6);
// @ts-expect-error Reset batch size to original state.
constants.batchSize = originalBatchSize;