feat(connector): add on-prem Active Directory (LDAP) HR connector - #1900
Conversation
|
Warning Review limit reached
Next review available in: 48 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds an Airbyte LDAP/LDAPS Active Directory connector with paged Bronze user extraction, normalized identity fields, SCD2 history, ChangesActive Directory HR Connector
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/domain/connector/HR_IDENTITY_CONNECTOR_GUIDE.md`:
- Line 30: Add the `text` language identifier to both fenced code blocks in the
HR identity connector guide, including the blocks around the referenced
locations, so the diagrams satisfy markdownlint MD040.
In
`@src/ingestion/connectors/hr-directory/active-directory/dbt/active_directory__manager_identity_inputs.sql`:
- Around line 41-43: Update the manager change filtering in the active-directory
manager identity inputs query so empty managerDn changes are retained as DELETE
events rather than excluded. Emit DELETE rows for both parent_email and
parent_id relationship value types when managerDn becomes empty, while
preserving existing behavior for non-empty manager updates.
- Around line 38-41: Update the manager identity input query around the history
CTE and manager join to include changes to a manager’s mail or
userPrincipalName, not only managerDn changes. Fan those email-history events
out to every user whose current managerDn references the changed manager, while
preserving the existing parent_email output and direct-report mapping.
In
`@src/ingestion/connectors/hr-directory/active-directory/dbt/active_directory__to_class_people.sql`:
- Around line 37-40: Update the manager mapping around the manager_person_id
projection to leave manager_person_id null instead of selecting mgr.id, since
mgr.id is an AD source identifier. Preserve the existing parent_id and
parent_email identity signals so manager relationships are resolved through
identity resolution.
In
`@src/ingestion/connectors/hr-directory/active-directory/dbt/active_directory__users_snapshot.sql`:
- Around line 12-16: Update the snapshot’s check_cols configuration to include
managerDn alongside the other tracked user fields, ensuring manager-only changes
create a new version for active_directory__users_fields_history and reach
active_directory__manager_identity_inputs.
In `@src/ingestion/connectors/hr-directory/active-directory/pyproject.toml`:
- Around line 1-3: Update the build configuration in pyproject.toml to add the
required cf-studio-path setting with the value .cf-studio, preserving the
existing build-system configuration.
In
`@src/ingestion/connectors/hr-directory/active-directory/source_active_directory/ldap_client.py`:
- Around line 64-65: Update the configuration parsing for ad_use_ssl before
constructing Tls or the LDAP server so string values "true" and "false" are
converted to booleans, while invalid values are rejected. Preserve the default
of true when the setting is absent, and ensure the normalized value controls
LDAPS selection and port 636 behavior.
- Line 65: Update the TLS configuration in the LDAP client’s SSL setup to
require certificate and hostname validation instead of using Tls(validate=0).
Configure the trusted CA bundle through the existing connector configuration or
mounted certificate path so private AD certificates are supported without
falling back to CERT_NONE.
In
`@src/ingestion/connectors/hr-directory/active-directory/source_active_directory/source.py`:
- Around line 50-56: The check_connection() validation must reject empty
normalized insight_source_id and insight_tenant_id values before records are
emitted. In source.py, retrieve and trim both identity fields and return the
existing failure response when either is empty; in spec.json, add minLength: 1
to both identity field definitions so configuration validation enforces the same
requirement.
- Around line 73-91: Update the connection probe in check_connection to avoid
treating expected LDAP size limits as failures: use BASE search scope with an
(objectClass=*) filter, or explicitly handle LDAPSizeLimitExceededResult as
success. Preserve failure handling for other LDAP errors and unreadable search
bases.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: f8f791fe-5560-49c3-b25b-e935068c3750
📒 Files selected for processing (21)
docs/domain/connector/HR_IDENTITY_CONNECTOR_GUIDE.mdsrc/ingestion/connectors/hr-directory/active-directory/Dockerfilesrc/ingestion/connectors/hr-directory/active-directory/README.mdsrc/ingestion/connectors/hr-directory/active-directory/dbt/active_directory__bronze_promoted.sqlsrc/ingestion/connectors/hr-directory/active-directory/dbt/active_directory__identity_inputs.sqlsrc/ingestion/connectors/hr-directory/active-directory/dbt/active_directory__manager_identity_inputs.sqlsrc/ingestion/connectors/hr-directory/active-directory/dbt/active_directory__to_class_people.sqlsrc/ingestion/connectors/hr-directory/active-directory/dbt/active_directory__users_fields_history.sqlsrc/ingestion/connectors/hr-directory/active-directory/dbt/active_directory__users_snapshot.sqlsrc/ingestion/connectors/hr-directory/active-directory/dbt/schema.ymlsrc/ingestion/connectors/hr-directory/active-directory/descriptor.yamlsrc/ingestion/connectors/hr-directory/active-directory/pyproject.tomlsrc/ingestion/connectors/hr-directory/active-directory/source_active_directory/__init__.pysrc/ingestion/connectors/hr-directory/active-directory/source_active_directory/ldap_client.pysrc/ingestion/connectors/hr-directory/active-directory/source_active_directory/source.pysrc/ingestion/connectors/hr-directory/active-directory/source_active_directory/spec.jsonsrc/ingestion/connectors/hr-directory/active-directory/source_active_directory/streams/__init__.pysrc/ingestion/connectors/hr-directory/active-directory/source_active_directory/streams/users.pysrc/ingestion/secrets/connectors/active-directory.yaml.examplesrc/ingestion/silver/_shared/class_people.sqlsrc/ingestion/silver/_shared/identity_inputs.sql
Fixes 10 issues flagged in PR #1900: manager email/removal not propagated to identity_inputs, manager_person_id wrongly set to an AD source id, managerDn untracked in the snapshot, ad_use_ssl string "false" always truthy, LDAPS skipping cert validation (CERT_NONE), empty tenant/source id accepted, check_connection misreading an expected sizeLimitExceeded as an unreadable search base, missing cf-studio-path, and MD040 lint on the guide doc. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> Signed-off-by: Dmitry.Saukh <cyberdima@gmail.com>
e596e60 to
f867ae2
Compare
|
CDK Python connector querying AD over LDAP/LDAPS (ldap3). Sibling of ms-entra — same class_people/identity_inputs Silver contract, different transport. sAMAccountName → sam_account identity signal reconciles on-prem AD ↔ cloud Entra ↔ self-hosted Git. - source_active_directory/: AbstractSource, ldap_client, users stream (paged search, objectGUID-keyed, privacy allowlist) - dbt/: bronze_promoted, snapshot, fields_history, identity_inputs, to_class_people (mirror of ms_entra__* models) - docs/domain/connector/HR_IDENTITY_CONNECTOR_GUIDE.md: agent-facing recipe for HR/identity connector authoring (CDK-for-non-HTTP pattern) - secrets/connectors/active-directory.yaml.example: K8s Secret template Signed-off-by: Dmitry.Saukh <cyberdima@gmail.com> # Conflicts: # src/ingestion/silver/_shared/identity_inputs.sql Signed-off-by: Dmitry Saukh <38005371+cyberdima@users.noreply.github.com>
Fixes 10 issues flagged in PR #1900: manager email/removal not propagated to identity_inputs, manager_person_id wrongly set to an AD source id, managerDn untracked in the snapshot, ad_use_ssl string "false" always truthy, LDAPS skipping cert validation (CERT_NONE), empty tenant/source id accepted, check_connection misreading an expected sizeLimitExceeded as an unreadable search base, missing cf-studio-path, and MD040 lint on the guide doc. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> Signed-off-by: Dmitry.Saukh <cyberdima@gmail.com> Signed-off-by: Dmitry Saukh <38005371+cyberdima@users.noreply.github.com>
15d1eab to
bf34e99
Compare
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
src/ingestion/connectors/hr-directory/active-directory/source_active_directory/streams/users.py (1)
163-195: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low valueConsider requiring
tenant_id/source_idin the schema, matching the stated framework-field contract.The docstring at line 115-117 in
source.py's CDK counterpart states tenant_id/source_id/unique_key are all mandatory framework fields, but onlyunique_keyandidare inrequiredhere. Since these are always populated in_to_record, this is non-blocking, but adding them closes a latent gap if the record-building logic ever changes.♻️ Proposed fix
- "required": ["unique_key", "id"], + "required": ["unique_key", "id", "tenant_id", "source_id"],🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/ingestion/connectors/hr-directory/active-directory/source_active_directory/streams/users.py` around lines 163 - 195, Update the get_json_schema method’s required list to include tenant_id and source_id alongside unique_key and id, matching the framework-field contract and the fields populated by _to_record.src/ingestion/connectors/hr-directory/active-directory/source_active_directory/source.py (1)
101-106: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSilent
except Exception: passon connection cleanup in both files. Both files swallow all unbind exceptions with no logging; the# noqa: BLE001doesn't suppress Ruff'sS110(try-except-pass) hint, so this still surfaces in lint.
src/ingestion/connectors/hr-directory/active-directory/source_active_directory/source.py#L101-L106: log the unbind exception at debug level (or extend the noqa to coverS110) instead of silently passing.src/ingestion/connectors/hr-directory/active-directory/source_active_directory/streams/users.py#L157-L161: apply the same debug-logging fix to theconn.unbind()cleanup inread_records.♻️ Proposed fix (apply to both sites)
- try: - conn.unbind() - except Exception: # noqa: BLE001 — best-effort cleanup - pass + try: + conn.unbind() + except Exception: # noqa: BLE001 — best-effort cleanup + logger.debug("LDAP unbind failed", exc_info=True)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/ingestion/connectors/hr-directory/active-directory/source_active_directory/source.py` around lines 101 - 106, The conn.unbind() cleanup handlers in source.py lines 101-106 and users.py lines 157-161 silently swallow exceptions and trigger S110. Update both cleanup blocks to log unbind failures at debug level while preserving best-effort cleanup and exception suppression; use the surrounding source flow and read_records method as anchors.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/ingestion/connectors/hr-directory/active-directory/dbt/active_directory__manager_identity_inputs.sql`:
- Around line 71-84: Deduplicate the rows produced by manager_email_changes
before they are emitted, grouping or applying distinctness by report,
updated_at, and resolved manager_email so simultaneous mail and
userPrincipalName history events yield one event. Preserve the existing coalesce
resolution and joins, while retaining separate rows when the report, timestamp,
or resolved email differs.
- Around line 71-84: Update manager_email_changes and the downstream
event-generation logic so a manager with both mail and userPrincipalName empty
produces a DELETE event for each affected direct report. Ensure the DELETE
clears the existing parent_email instead of being removed by the final non-empty
filter, while preserving current behavior for non-empty manager emails.
- Around line 52-53: Update all three manager joins in the active-directory
manager identity inputs query, including the joins around current_users and the
referenced location, to require matching tenant_id and source_id in addition to
the existing manager identifier predicate. Preserve the current join behavior
within the same tenant and source while preventing cross-tenant or cross-source
matches.
In `@src/ingestion/connectors/hr-directory/active-directory/README.md`:
- Around line 90-93: Update the privacy statement in the README to remove the
inaccurate “or other PII” claim. Describe that the explicit allowlist still
collects required identity fields, while excluding non-required fields such as
photos, postal addresses, and phone numbers.
---
Nitpick comments:
In
`@src/ingestion/connectors/hr-directory/active-directory/source_active_directory/source.py`:
- Around line 101-106: The conn.unbind() cleanup handlers in source.py lines
101-106 and users.py lines 157-161 silently swallow exceptions and trigger S110.
Update both cleanup blocks to log unbind failures at debug level while
preserving best-effort cleanup and exception suppression; use the surrounding
source flow and read_records method as anchors.
In
`@src/ingestion/connectors/hr-directory/active-directory/source_active_directory/streams/users.py`:
- Around line 163-195: Update the get_json_schema method’s required list to
include tenant_id and source_id alongside unique_key and id, matching the
framework-field contract and the fields populated by _to_record.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: b1e64a49-5c4b-46db-9c91-1d7b7843834a
📒 Files selected for processing (21)
docs/domain/connector/HR_IDENTITY_CONNECTOR_GUIDE.mdsrc/ingestion/connectors/hr-directory/active-directory/Dockerfilesrc/ingestion/connectors/hr-directory/active-directory/README.mdsrc/ingestion/connectors/hr-directory/active-directory/dbt/active_directory__bronze_promoted.sqlsrc/ingestion/connectors/hr-directory/active-directory/dbt/active_directory__identity_inputs.sqlsrc/ingestion/connectors/hr-directory/active-directory/dbt/active_directory__manager_identity_inputs.sqlsrc/ingestion/connectors/hr-directory/active-directory/dbt/active_directory__to_class_people.sqlsrc/ingestion/connectors/hr-directory/active-directory/dbt/active_directory__users_fields_history.sqlsrc/ingestion/connectors/hr-directory/active-directory/dbt/active_directory__users_snapshot.sqlsrc/ingestion/connectors/hr-directory/active-directory/dbt/schema.ymlsrc/ingestion/connectors/hr-directory/active-directory/descriptor.yamlsrc/ingestion/connectors/hr-directory/active-directory/pyproject.tomlsrc/ingestion/connectors/hr-directory/active-directory/source_active_directory/__init__.pysrc/ingestion/connectors/hr-directory/active-directory/source_active_directory/ldap_client.pysrc/ingestion/connectors/hr-directory/active-directory/source_active_directory/source.pysrc/ingestion/connectors/hr-directory/active-directory/source_active_directory/spec.jsonsrc/ingestion/connectors/hr-directory/active-directory/source_active_directory/streams/__init__.pysrc/ingestion/connectors/hr-directory/active-directory/source_active_directory/streams/users.pysrc/ingestion/secrets/connectors/active-directory.yaml.examplesrc/ingestion/silver/_shared/class_people.sqlsrc/ingestion/silver/_shared/identity_inputs.sql
🚧 Files skipped from review as they are similar to previous changes (14)
- src/ingestion/silver/_shared/class_people.sql
- src/ingestion/connectors/hr-directory/active-directory/source_active_directory/init.py
- src/ingestion/connectors/hr-directory/active-directory/Dockerfile
- src/ingestion/secrets/connectors/active-directory.yaml.example
- src/ingestion/connectors/hr-directory/active-directory/dbt/active_directory__bronze_promoted.sql
- src/ingestion/connectors/hr-directory/active-directory/pyproject.toml
- docs/domain/connector/HR_IDENTITY_CONNECTOR_GUIDE.md
- src/ingestion/silver/_shared/identity_inputs.sql
- src/ingestion/connectors/hr-directory/active-directory/dbt/active_directory__users_snapshot.sql
- src/ingestion/connectors/hr-directory/active-directory/dbt/schema.yml
- src/ingestion/connectors/hr-directory/active-directory/dbt/active_directory__identity_inputs.sql
- src/ingestion/connectors/hr-directory/active-directory/source_active_directory/spec.json
- src/ingestion/connectors/hr-directory/active-directory/source_active_directory/ldap_client.py
- src/ingestion/connectors/hr-directory/active-directory/descriptor.yaml
…ail fan-out Address remaining CodeRabbit findings on PR #1900: join manager identity resolution on tenant_id/source_id (not DN value alone) to prevent cross-tenant leakage, dedupe simultaneous mail/userPrincipalName history events, emit a DELETE when a manager loses its last email signal, and correct the README privacy allowlist wording. Signed-off-by: Dmitry Saukh <38005371+cyberdima@users.noreply.github.com>
… the class The connector merged in constructorfabric#1900 was half-landed. Three defects, each of which alone breaks a shared surface (issue constructorfabric#2048): 1. `descriptor.yaml` carried `version: "1.0"` — two components. The CI `bump-descriptors` job feeds image-bearing descriptors to bump-descriptor-version.sh, which hard-fails on non-semver. That job runs only on the push to main, so PR constructorfabric#1900 went green and the job aborted post-merge, leaving `images.cdk.image` empty and the connector undeployable (reconcile WARN+skips an empty ref). Fixed to "1.0.0" and pinned images.cdk.image to the image that run actually published and tagged latest: ghcr.io/constructorfabric/source-active-directory-insight:2026.07.28.06.32-453df2d Set directly rather than by rebuild: discover-image-matrix.py excludes descriptor.yaml from the trigger set, so a descriptor-only commit emits an empty matrix, `any=false`, and bump-descriptors skips itself — verified, no rebuild and no recursion. 2. No entry in scripts/bootstrap-db/connectors-config.yaml, so bootstrap-db never created bronze_active_directory. Both AD models failed `Code: 81 UNKNOWN_DATABASE`, and the hard `depends_on` in silver/_shared/class_people.sql cascaded that into the shared class: `PASS=202 ERROR=2 SKIP=6`, with silver.class_people and insight.metric_entity_cohorts_current absent. dbt's non-zero exit then tripped `set -e` before the gold-view migrations ran, so any regenerated connectors-ddl snapshot would silently lose both relations. Added the entry as a generated fragment (`./generate-connectors-config.sh 'hr-directory/active-directory'`) rather than regenerating the whole file, which would replace the HubSpot and Salesforce `env:` credential references with fake `value:` entries. 3. `manager_person_id` was `CAST(NULL AS Nullable(UUID))` while ms-entra uses `Nullable(String)` and bamboohr/workday project String columns. union_by_tag UNION ALLs all four branches, so fixing (2) alone would have swapped Code 81 for `Code: 386 NO_COMMON_TYPE`. `org_unit_id` is Nullable(UUID) in all four and is left alone. Verified on a fresh ClickHouse 25.7.5: all four bootstrap-db steps run, exit 0, dbt `PASS=210 ERROR=0 SKIP=0`, and silver.class_people, insight.metric_entity_cohorts_current and bronze_active_directory.users all exist. Compiled class_people unions all four HR sources, with manager_person_id resolved to Nullable(String). Prevention: scripts/ci/connector_wiring.py, wired into ci.yml as `connector-wiring-guard`. It checks strict semver on image-bearing descriptors, connectors-config.yaml registration, the class_<X> depends_on edge, and cross-source column-type agreement. Run against the merge commit 453df2d it reports exactly these three errors and exits 1, so this PR would have been blocked. Empty image refs and legacy non-semver versions on image-less descriptors are warnings, not errors: a brand-new CDK connector legitimately ships `image: ""`, and ADR-0015 §"Legacy non-semver values" tolerates 2026.05.04 on ai/openai, collaboration/slack and hr-directory/bamboohr — none of which declares an `images:` block, so none can reach bump-descriptors. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Roman Mitasov <Roman.Mitasov@constructor.tech>
… the class (#2116) The connector merged in #1900 was half-landed. Three defects, each of which alone breaks a shared surface (issue #2048): 1. `descriptor.yaml` carried `version: "1.0"` — two components. The CI `bump-descriptors` job feeds image-bearing descriptors to bump-descriptor-version.sh, which hard-fails on non-semver. That job runs only on the push to main, so PR #1900 went green and the job aborted post-merge, leaving `images.cdk.image` empty and the connector undeployable (reconcile WARN+skips an empty ref). Fixed to "1.0.0" and pinned images.cdk.image to the image that run actually published and tagged latest: ghcr.io/constructorfabric/source-active-directory-insight:2026.07.28.06.32-453df2d Set directly rather than by rebuild: discover-image-matrix.py excludes descriptor.yaml from the trigger set, so a descriptor-only commit emits an empty matrix, `any=false`, and bump-descriptors skips itself — verified, no rebuild and no recursion. 2. No entry in scripts/bootstrap-db/connectors-config.yaml, so bootstrap-db never created bronze_active_directory. Both AD models failed `Code: 81 UNKNOWN_DATABASE`, and the hard `depends_on` in silver/_shared/class_people.sql cascaded that into the shared class: `PASS=202 ERROR=2 SKIP=6`, with silver.class_people and insight.metric_entity_cohorts_current absent. dbt's non-zero exit then tripped `set -e` before the gold-view migrations ran, so any regenerated connectors-ddl snapshot would silently lose both relations. Added the entry as a generated fragment (`./generate-connectors-config.sh 'hr-directory/active-directory'`) rather than regenerating the whole file, which would replace the HubSpot and Salesforce `env:` credential references with fake `value:` entries. 3. `manager_person_id` was `CAST(NULL AS Nullable(UUID))` while ms-entra uses `Nullable(String)` and bamboohr/workday project String columns. union_by_tag UNION ALLs all four branches, so fixing (2) alone would have swapped Code 81 for `Code: 386 NO_COMMON_TYPE`. `org_unit_id` is Nullable(UUID) in all four and is left alone. Verified on a fresh ClickHouse 25.7.5: all four bootstrap-db steps run, exit 0, dbt `PASS=210 ERROR=0 SKIP=0`, and silver.class_people, insight.metric_entity_cohorts_current and bronze_active_directory.users all exist. Compiled class_people unions all four HR sources, with manager_person_id resolved to Nullable(String). Prevention: scripts/ci/connector_wiring.py, wired into ci.yml as `connector-wiring-guard`. It checks strict semver on image-bearing descriptors, connectors-config.yaml registration, the class_<X> depends_on edge, and cross-source column-type agreement. Run against the merge commit 453df2d it reports exactly these three errors and exits 1, so this PR would have been blocked. Empty image refs and legacy non-semver versions on image-less descriptors are warnings, not errors: a brand-new CDK connector legitimately ships `image: ""`, and ADR-0015 §"Legacy non-semver values" tolerates 2026.05.04 on ai/openai, collaboration/slack and hr-directory/bamboohr — none of which declares an `images:` block, so none can reach bump-descriptors. (cherry picked from commit 0ecce4f) Signed-off-by: Roman Mitasov <Roman.Mitasov@constructor.tech> Signed-off-by: Anton Zelenov <antonz@constructor.tech> Co-authored-by: Roman Mitasov <Roman.Mitasov@constructor.tech> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
CDK Python connector querying AD over LDAP/LDAPS (ldap3). Sibling of ms-entra — same class_people/identity_inputs Silver contract, different transport. sAMAccountName → sam_account identity signal reconciles on-prem AD ↔ cloud Entra ↔ self-hosted Git.
Summary by CodeRabbit