Skip to content

[FEATURE][AUTH]: LDAP / Active Directory integrationΒ #284

@crivetimihai

Description

@crivetimihai

🧭 Epic β€” LDAP / Active-Directory Integration

Field Value
Title Enterprise Directory Sync & LDAP Bind Support
Goal Import users + groups from on-prem AD/LDAP and (optionally) allow direct LDAP-bind login when the IdP is unavailable.
Why now Our SSO rollout (SAML / OIDC) depends on clean group data. Many customers still need just-in-time sync from AD and a fail-safe auth path that bypasses the IdP during outages.
Depends on β€œAdd SSO & IdP-Issued Tokens” epic (for base user / table schema).
Related Feature #220 β€” SSO + IdP Integration

🧭 Type of Feature

  • Security hardening
  • New capability
  • Operations / reliability

πŸ™‹β€β™‚οΈ User Story 1 β€” Directory Sync

As a: Tenant admin
I want: the Gateway to import users, groups and memberOf relations from our AD every hour
So that: RBAC policies always reflect the source-of-truth directory.

βœ… Acceptance Criteria
Scenario: Hourly sync detects new user
  Given "eve" exists in LDAP under "ou=Users,dc=corp,dc=com"
  And "eve" is a member of "cn=ml-team,ou=Groups,dc=corp,dc=com"
  When the scheduled sync job runs
  Then "eve" is present in gateway.users
  And "ml-team" is present in gateway.groups
  And a link row exists in gateway.group_members(user_id=eve, group=ml-team)

πŸ™‹β€β™‚οΈ User Story 2 β€” LDAP Bind Fallback Login (optional flag)

As a: On-call engineer
I want: users to sign-in with AD credentials when the IdP is down
So that: critical dashboards stay reachable during SAML/OIDC outages.

βœ… Acceptance Criteria
Scenario: LDAP bind succeeds
  Given the IdP health-check is failing
  And user "alice" enters AD username/password
  When gateway performs an LDAP simple bind with those creds
  Then it issues a session cookie scoped to alice
  And maps alice’s AD groups to gateway roles

πŸ™‹β€β™‚οΈ User Story 3 β€” Group-Derived RBAC Refresh

As a: Security auditor
I want: group membership changes in AD to propagate within < 1 hour
So that: removed users lose access promptly.

βœ… Acceptance Criteria
Scenario: User removed from AD group
  Given "bob" was a member of "finance-analyst"
  And an admin deletes bob from that group in AD
  When the next sync runs
  Then bob’s gateway session is invalidated
  And subsequent API calls return 403 "forbidden_scope"

πŸ“ Design Sketch

flowchart TD
    subgraph "Active Directory / LDAP"
        LDAP[(ldaps://ad.corp.com)]
    end

    SyncJob["LDAP Sync Cron"] -->|hourly search| LDAP
    LDAP --> SyncJob
    SyncJob --> DB[(gateway.users / groups)]

    Browser --> LoginUI
    LoginUI --> AuthService
    AuthService -->|if IdP down| LDAP
    AuthService -->|else| IdP[(SAML / OIDC)]
Loading

πŸ”„ Roll-out Plan

Phase Feature Flag Key Tasks
0 EXPERIMENTAL_LDAP_SYNC off Implement read-only sync job targeting staging AD replica.
1 on (shadow) Sync to prod; no bind login yet; verify RBAC mapping accuracy.
2 EXPERIMENTAL_LDAP_BIND off Add bind-login endpoint behind separate flag; security review & rate-limiting.
3 on (optional) Enable bind login for DR run-books; document rollback.

πŸ”§ Tasks

  1. Connector & Schema

    • Add ldap_connector.py (uses ldap3, paged searches).
    • Extend users, groups, group_members with external_source='ldap'.
    • Store last_sync_at, ad_dn fields.
  2. Sync Scheduler

    • Reusable cron-style job (default 60 min, configurable).
    • Full import β†’ diff β†’ upsert; delete-orphan flag.
  3. Bind Auth Endpoint

    • POST /auth/ldap (username, password).
    • Simple bind over LDAPS, follow referrals, 5 s timeout.
    • Issue same JWT/session-cookie pipeline.
  4. RBAC Cache Invalidation

    • Push β€œgroup-change” events β†’ session store.
    • Middleware denies stale tokens.
  5. DevOps / Secrets

    • Vault path for LDAP_BIND_DN / LDAP_BIND_PW.
    • TLS pinning for LDAPS certificates.
  6. Monitoring & Alerts

    • Prometheus: ldap_sync_success, ldap_bind_latency.
    • Alert if sync fails > 3 Γ— or bind error rate > 5 %.
  7. Docs & Run-books

    • β€œHow to map OUs” guide.
    • Disaster-recovery login procedure.
  8. Test Infrastructure

    • Docker Compose file with OpenLDAP + phpLDAPadmin for local smoke-tests.
    • Optional Helm chart (osixia/openldap-stack-ha) for CI/kind clusters.
    • CI script that seeds users/groups, runs sync, and asserts DB state.

πŸ§ͺ Testing with OpenLDAP (no IdP required)

1. Spin-up

# docker-compose.yml (drop in repo root)
version: "3"
services:
  ldap:
    image: osixia/openldap:1.5.0
    container_name: ldap
    environment:
      LDAP_ORGANISATION: "Example Corp"
      LDAP_DOMAIN: "example.org"
      LDAP_ADMIN_PASSWORD: "admin"
    ports:
      - "389:389"   # LDAP
      - "636:636"   # LDAPS (self-signed cert)

  phpldapadmin:
    image: osixia/phpldapadmin:0.9.0
    environment:
      PHPLDAPADMIN_LDAP_HOSTS: "ldap"
    ports:
      - "8080:80"
    depends_on: [ ldap ]
docker compose up -d
# LDAP β†’ ldap://localhost:389 , admin DN "cn=admin,dc=example,dc=org" / pw "admin"
# GUI  β†’ http://localhost:8080  (login with same DN)

2. Seed demo data

# seed.ldif
dn: ou=Users,dc=example,dc=org
objectClass: organizationalUnit
ou: Users

dn: uid=alice,ou=Users,dc=example,dc=org
objectClass: inetOrgPerson
uid: alice
sn: Liddell
cn: Alice Liddell
userPassword: password

dn: uid=bob,ou=Users,dc=example,dc=org
objectClass: inetOrgPerson
uid: bob
sn: Builder
cn: Bob Builder
userPassword: password

dn: cn=data-science,ou=Users,dc=example,dc=org
objectClass: groupOfNames
cn: data-science
member: uid=alice,ou=Users,dc=example,dc=org
member: uid=bob,ou=Users,dc=example,dc=org
docker cp seed.ldif ldap:/seed.ldif
docker exec ldap \
  ldapadd -x -D "cn=admin,dc=example,dc=org" -w admin -f /seed.ldif

3. Smoke-check

ldapsearch -x -H ldap://localhost:389 \
  -D "cn=admin,dc=example,dc=org" -w admin \
  -b "dc=example,dc=org" "(uid=alice)" uid cn memberOf
# β†’ verify alice appears and is in data-science

4. Wire to Gateway

LDAP_URI=ldap://localhost:389
LDAP_BASE_DN=dc=example,dc=org
LDAP_BIND_DN=cn=admin,dc=example,dc=org
LDAP_BIND_PW=admin

Run the sync job β†’ database now holds alice, bob, data-science.
Call /auth/ldap with alice/password β†’ expect JWT & gateway access.


πŸ”„ Alternatives Considered

Approach Pros Cons Decision
IdP-only (no LDAP) Simpler, fewer ports IdP outage = total lockout ❌
One-time CSV import No LDAP secrets No automatic updates ❌
SCIM 2.0 Standard provisioning AD needs extra tooling πŸ”„ Future

πŸ““ Additional Context

  • Security – Bind DN is least-privilege, read-only; LDAPS :636 only.
  • Performance – Sync of 10 k users / 1 k groups β‰ˆ 15 s.
  • Compliance – Only import sAMAccountName, mail, memberOf.
  • Backwards-compat – If IdP + LDAP both fail, legacy local admin remains.

Metadata

Metadata

Assignees

Labels

MUSTP1: Non-negotiable, critical requirements without which the product is non-functional or unsafedatabaseenhancementNew feature or requestpythonPython / backend development (FastAPI)readyValidated, ready-to-work-on itemssecurityImproves security

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions