Skip to content

ogar-dir-core / ogar-ad / ogar-az: directory observation PoC - #313

Merged
AdaWorldAPI merged 2 commits into
mainfrom
ccr-0455e606-wmtsor
Oct 3, 2026
Merged

AdaWorldAPI merged 2 commits into
mainfrom
ccr-0455e606-wmtsor

Conversation

@AdaWorldAPI

@AdaWorldAPI AdaWorldAPI commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner

Read-only PoC that encodes observed Active Directory and Entra ID objects as versioned, source-native, fixed-size records. It is not an IAM: there is no provisioning, no reconciliation, no business rules and nothing writes to Graph.

The full design is in docs/DIRECTORY-ADAPTERS-POC.md: the exact 0x000..0x1FF layout, the OU dictionary strategy, the attribute selection and the conflicts.

Crates

  • ogar-dir-core (no dependencies). It holds:
    • Guid128 (textual byte order, with a Microsoft mixed-endian conversion)
    • an RFC 4514 DN parser
    • OuHhtl (8 × u16) with a per-parent OuDictionary that allocates ids explicitly instead of hashing them
    • ValuePool, SchemaFamily/SchemaId/AttrDef
    • DirRecord (512 bytes, following NodeRow's 16/16/480 split)
    • DirEdge (64 bytes)
  • ogar-ad: AD schema v1, an LDIF reader and encode(entry).
  • ogar-az: Graph schema v1, a $select derived from that schema, ingest_page, sync_edges (built from onPremisesImmutableId), and a read-only example az_ingest.

Conflicts with canon (flagged, nothing changed)

  • C1: Canon NodeGuid is a minted address. The source GUID therefore lives in the value slab, and the canonical key slot is left as zero.
  • C2: OuHhtl (8×16) is a separate tree from HEEL/HIP/TWIG and NiblePath.
  • C3: No classid was minted. Auth domain 0x0B is the natural home, but that is an operator decision.
  • C4: The NodeRow mirror is not yet guarded by a test.

Verification

  • 22 tests pass, covering invariants 1–12.
  • clippy -D warnings and fmt are clean.
  • I disabled five guards one at a time; each made its test fail, and the tests pass again with the guards restored.
  • This has not been run against a real tenant. It is verified only on synthetic LDIF and Graph fixtures.

🤖 Generated with Claude Code

https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg


Generated by Claude Code

Summary by CodeRabbit

  • New Features
    • Added read-only support for importing Active Directory data from LDIF files and Microsoft Entra ID user data from Microsoft Graph pages.
    • Directory records now capture identifiers, selected attributes, organizational-unit paths, and observation times in a shared format.
    • Imports report unrecognized attributes, and matching immutable IDs can identify synchronization links between directory records.
    • Added an example workflow for processing a Graph users page without making network requests.

Read-only encoding of observed Active Directory and Entra ID objects into a
fixed 512-byte, NodeRow-shaped record: 128-bit source GUID (textual byte
order, MS mixed-endian converted), scope GUID, an 8x16-bit OU HHTL with an
explicit per-parent dictionary (no hashing, collision-free, reversible),
schema family/version separate from the ABI version, out-of-line value pool,
and a 64-byte GUID-pair edge record (SynchronizesTo, evidenced by
onPremisesImmutableId). AD ingest via LDIF; Graph ingest via a page body with
a schema-derived $select. No classid mint, no IAM semantics, no writes.

Design and conflicts with the canonical NodeGuid/HHTL: docs/DIRECTORY-ADAPTERS-POC.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
@coderabbitai

coderabbitai Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: ffa70dfe-b4ab-4160-a9f2-6efd09b36165
📥 Commits

Reviewing files that changed from the base of the PR and between b6b4628 and 8f8158a.

📒 Files selected for processing (23)
  • Cargo.toml
  • crates/ogar-ad/Cargo.toml
  • crates/ogar-ad/src/ldif.rs
  • crates/ogar-ad/src/lib.rs
  • crates/ogar-ad/tests/fixtures/lab.ldif
  • crates/ogar-ad/tests/main.rs
  • crates/ogar-az/Cargo.toml
  • crates/ogar-az/examples/az_ingest.rs
  • crates/ogar-az/src/lib.rs
  • crates/ogar-az/tests/fixtures/users_page.json
  • crates/ogar-az/tests/main.rs
  • crates/ogar-dir-core/Cargo.toml
  • crates/ogar-dir-core/src/base64.rs
  • crates/ogar-dir-core/src/dn.rs
  • crates/ogar-dir-core/src/edge.rs
  • crates/ogar-dir-core/src/guid.rs
  • crates/ogar-dir-core/src/hhtl.rs
  • crates/ogar-dir-core/src/lib.rs
  • crates/ogar-dir-core/src/pool.rs
  • crates/ogar-dir-core/src/record.rs
  • crates/ogar-dir-core/src/schema.rs
  • crates/ogar-dir-core/tests/main.rs
  • docs/DIRECTORY-ADAPTERS-POC.md
 __________________________________________
< My GPUs are ready. Let's find some bugs. >
 ------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


Comment @coderabbitai help to get the list of available commands.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f464dfc. Configure here.

Comment thread crates/ogar-ad/src/ldif.rs
Comment thread crates/ogar-az/src/lib.rs
…i-values

Bugbot on #313:
- ldifde -f writes 'changetype: add' after every dn; it is the export marker,
  not a change record. Accept it; still refuse modify/delete/modrdn.
- attr_str matched any pooled kind, so a MultiStr slot came back as its
  length-prefixed blob. Restrict it to AttrKind::Str and add attr_multi.
Both covered by new tests that failed before the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review October 3, 2026 06:44
@AdaWorldAPI
AdaWorldAPI merged commit ff47ffe into main Oct 3, 2026
3 of 4 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8f8158a1d7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/ogar-az/src/lib.rs
if dn.has_non_ou_container() {
rec.add_flags(FLAG_NON_OU_CONTAINER);
}
match onprem_dict.intern(&dn.ou_path_root_first()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Choose the OU dictionary by on-premises domain

When one Entra tenant contains users synchronized from multiple AD domains, ingest_page passes this same dictionary to every user even though OuDictionary IDs are defined per domain and onPremisesDomainName is available on each object. Consequently, allocation order from domain A changes domain B's HHTL IDs, so domain B's Entra records no longer align with records encoded using domain B's AD-side dictionary; dispatch to a domain-specific dictionary before interning the DN.

Useful? React with 👍 / 👎.

Comment on lines +7 to +9
let s = s.trim_end_matches('=');
if s.len() % 4 == 1 {
return None;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject malformed base64 padding and trailing bits

For padded or unpadded inputs, stripping every trailing = without validating its count or the discarded bits accepts malformed encodings: for example, ==== becomes an empty value, and nonzero unused bits in the final sextet are silently ignored. In sync_edges, this means a corrupted onPremisesImmutableId can decode to the same 16 bytes as a valid anchor and produce a synchronization edge instead of being rejected; enforce legal padding and zero unused trailing bits.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants