Skip to content

feat(auth): add config-driven domain policy layer#152

Open
ironcommit wants to merge 5 commits into
mainfrom
auth-domain/rsadler
Open

feat(auth): add config-driven domain policy layer#152
ironcommit wants to merge 5 commits into
mainfrom
auth-domain/rsadler

Conversation

@ironcommit
Copy link
Copy Markdown
Contributor

@ironcommit ironcommit commented Jun 3, 2026

Summary

Adds a domain metadata layer to the auth service. API domains (e.g. models, agents) are automatically discovered from registered endpoint paths and extension plugin manifests, then injected into the OPA authorization data bundle as a unified registry. This gives policy authors a structured current_domain rule and get_domain_metadata helper to key on when writing domain-specific authorization logic.

Changes

OPA policies

  • New domain.rego — exposes current_domain by resolving the request path's API domain against the registry
  • extract.rego — added extract_domain_name(path) to pull the domain segment from /apis/<domain>/... paths
  • common.rego — added get_domain_metadata(path) helper to look up domain name, version, and kind from data.authz.domains

Bundle builder (bundle.py)

  • _extract_domain_names_from_endpoints discovers core API domain names from endpoint paths
  • _build_domain_registry merges core domains with extension plugin manifests (via discover_manifests)
  • Rejects collisions: raises ValueError if an extension manifest name conflicts with a core API domain
  • Injects the domains registry into the authorization data bundle under authz.domains

Tests

  • Rego unit tests for domain name extraction, metadata lookup, and core/extension domain resolution
  • Python unit tests verifying core domains appear in the bundle, extension manifests merge correctly, and collisions are rejected
  • Integration test confirming existing allow authorization is unaffected when domain metadata is present

Summary by CodeRabbit

  • Chores

    • Synthesize and embed domain metadata for core and extension API domains into authorization bundles
    • Add domain extraction and metadata lookup used by authorization policies, plus validation to prevent domain-name conflicts
    • Expose current-domain metadata to policy evaluation
  • Tests

    • Add tests for domain extraction, metadata retrieval, policy behavior, bundle generation, extension merging, conflict detection, and non-service name-overlap safeguards

Signed-off-by: Ryan Sadler <267728323+ironcommit@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 02524d02-d0d6-4c50-81f0-ff54c2040efd

📥 Commits

Reviewing files that changed from the base of the PR and between 14da8d6 and 1070de0.

📒 Files selected for processing (1)
  • services/core/auth/src/nmp/core/auth/app/bundle.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • services/core/auth/src/nmp/core/auth/app/bundle.py

📝 Walkthrough

Walkthrough

Authorization now extracts domain names from request paths, exposes domain metadata to Rego via get_domain_metadata/current_domain, synthesizes an authz domain registry at bundle time from endpoints plus discovered manifests, and adds tests for metadata wiring and collision handling.

Changes

Domain metadata infrastructure for authorization

Layer / File(s) Summary
Domain name extraction from paths
services/core/auth/src/nmp/core/auth/app/policies/extract.rego
extract_domain_name(path) parses /apis/<domain>/... by stripping query strings, validating path structure, and returning the domain component.
Domain metadata lookup
services/core/auth/src/nmp/core/auth/app/policies/common.rego
Imports extract_domain_name and adds get_domain_metadata(path) that derives domain name and returns name, version, kind from data.authz.domains.
Current domain derivation
services/core/auth/src/nmp/core/auth/app/policies/domain.rego
New module defines current_domain as the metadata returned by get_domain_metadata(extract_path).
Rego policy helper and domain tests
services/core/auth/src/nmp/core/auth/app/policy_tests/helpers_test.rego, services/core/auth/src/nmp/core/auth/app/policy_tests/authz_simple_test.rego
Tests validate domain extraction for core/extension paths, metadata lookup for both kinds with mocked data.authz.domains, and domain presence in mock authz data.
Bundle domain registry generation
services/core/auth/src/nmp/core/auth/app/bundle.py
Adds imports for entry-point discovery and a manifest type, helpers to extract domain names from authz.endpoints, discover extension manifests, merge core and extension domains (raise on collision), and populate static_data["authz"]["domains"] during bundle construction.
Bundle and embedded PDP integration tests
services/core/auth/tests/test_bundle.py, services/core/auth/tests/test_embedded_pdp.py
Tests verify bundle emits core domain metadata, merges extension manifests (version/kind), rejects manifest collisions, ignores non-service plugin name overlap, and embedded PDP still authorizes when domain metadata exists.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(auth): add config-driven domain policy layer' directly describes the main change: introducing a domain metadata/registry layer to the auth service that enables domain-aware authorization policies.
Docstring Coverage ✅ Passed Docstring coverage is 84.62% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch auth-domain/rsadler

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@services/core/auth/src/nmp/core/auth/app/bundle.py`:
- Around line 47-52: The code currently lets manifests override core-inferred
domains by iterating over sorted(domain_names | manifests.keys()) and writing
into domains; change this to detect collisions first and fail fast: compute the
intersection between domain_names and manifests.keys() (e.g., conflicts =
domain_names & manifests.keys()) and if non-empty raise an exception
(ValueError/RuntimeError) with a clear message listing the conflicting names so
the PR fails rather than silently overwriting; keep the existing
domains-building logic (using manifests.get(name) and setting "extension" vs
"core") but only run it after verifying there are no conflicts so
common.get_domain_metadata(...) is never fed corrupted metadata.

In `@services/core/auth/src/nmp/core/auth/app/policies/domain.rego`:
- Around line 19-35: The rule domain_policy_checks_pass is currently a tautology
because its three branches cover every case (no current_domain, current_domain
without a policy, current_domain with a policy) so the domain layer never
enforces anything; change the third branch (the one matching when
has_domain_policy(domain.name) and binding policy :=
data.authz.domain_policies[domain.name]) to actually evaluate the policy outcome
instead of just returning the policy object — e.g., invoke or test the policy
decision (replace the unconditional "policy" success with a check like
policy.allow or calling a domain-specific decision rule such as
domain_policy_allows_request) so domain_policy_checks_pass only returns true
when there is no domain or when a domain exists and its policy explicitly
permits the request (this will allow the allow_request gating in authz.rego to
enforce domain policies).
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: c7af7318-db68-49ba-9070-542d4decb13f

📥 Commits

Reviewing files that changed from the base of the PR and between 01bd85e and 8ce6c41.

📒 Files selected for processing (9)
  • services/core/auth/src/nmp/core/auth/app/bundle.py
  • services/core/auth/src/nmp/core/auth/app/policies/authz.rego
  • services/core/auth/src/nmp/core/auth/app/policies/common.rego
  • services/core/auth/src/nmp/core/auth/app/policies/domain.rego
  • services/core/auth/src/nmp/core/auth/app/policies/extract.rego
  • services/core/auth/src/nmp/core/auth/app/policy_tests/authz_simple_test.rego
  • services/core/auth/src/nmp/core/auth/app/policy_tests/helpers_test.rego
  • services/core/auth/tests/test_bundle.py
  • services/core/auth/tests/test_embedded_pdp.py

Comment thread services/core/auth/src/nmp/core/auth/app/bundle.py
Comment on lines +19 to +35
domain_policy_checks_pass if {
not current_domain
}

domain_policy_checks_pass if {
current_domain
not has_domain_policy(current_domain.name)
}

domain_policy_checks_pass if {
domain := current_domain
has_domain_policy(domain.name)
policy := data.authz.domain_policies[domain.name]
# First cut: policy presence activates the domain layer without
# adding additional deny conditions yet.
policy
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

domain_policy_checks_pass is a tautology.

One of these three branches matches every request: no current domain, current domain without a policy, or current domain with a policy. That makes domain_policy_checks_pass effectively always true, so the new allow_request gates in services/core/auth/src/nmp/core/auth/app/policies/authz.rego never enforce the domain layer at all.

🤖 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 `@services/core/auth/src/nmp/core/auth/app/policies/domain.rego` around lines
19 - 35, The rule domain_policy_checks_pass is currently a tautology because its
three branches cover every case (no current_domain, current_domain without a
policy, current_domain with a policy) so the domain layer never enforces
anything; change the third branch (the one matching when
has_domain_policy(domain.name) and binding policy :=
data.authz.domain_policies[domain.name]) to actually evaluate the policy outcome
instead of just returning the policy object — e.g., invoke or test the policy
decision (replace the unconditional "policy" success with a check like
policy.allow or calling a domain-specific decision rule such as
domain_policy_allows_request) so domain_policy_checks_pass only returns true
when there is no domain or when a domain exists and its policy explicitly
permits the request (this will allow the allow_request gating in authz.rego to
enforce domain policies).

Signed-off-by: Ryan Sadler <267728323+ironcommit@users.noreply.github.com>
Signed-off-by: Ryan Sadler <267728323+ironcommit@users.noreply.github.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
services/core/auth/tests/test_embedded_pdp.py (1)

194-204: 💤 Low value

Duplicate of test_authenticated_user_with_permission.

Identical input and assertion (Lines 158-168). Since minimal_authz_data now always carries domains, every test in this class already exercises the "domain metadata present" path, so this adds no coverage. Either drop it or make it assert something the existing test doesn't (e.g. that domains is actually in policy data / a domain-policy field).

🤖 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 `@services/core/auth/tests/test_embedded_pdp.py` around lines 194 - 204, The
test test_allow_keeps_working_when_domain_metadata_is_present is a duplicate of
test_authenticated_user_with_permission; either remove it or change it to assert
something unique: after calling set_policy_data(minimal_authz_data) and
evaluate("allow", ...), also assert that the policy data contains the domains
metadata (e.g., check minimal_authz_data or the policy store for a "domains" key
or domain-specific field) so the test verifies domain metadata is present rather
than duplicating the permission check; update the test name and assertions
accordingly (refer to test_allow_keeps_working_when_domain_metadata_is_present
and test_authenticated_user_with_permission).
🤖 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.

Nitpick comments:
In `@services/core/auth/tests/test_embedded_pdp.py`:
- Around line 194-204: The test
test_allow_keeps_working_when_domain_metadata_is_present is a duplicate of
test_authenticated_user_with_permission; either remove it or change it to assert
something unique: after calling set_policy_data(minimal_authz_data) and
evaluate("allow", ...), also assert that the policy data contains the domains
metadata (e.g., check minimal_authz_data or the policy store for a "domains" key
or domain-specific field) so the test verifies domain metadata is present rather
than duplicating the permission check; update the test name and assertions
accordingly (refer to test_allow_keeps_working_when_domain_metadata_is_present
and test_authenticated_user_with_permission).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 73fdc833-4fb7-493e-90c7-17e9cca5fd75

📥 Commits

Reviewing files that changed from the base of the PR and between 8ce6c41 and 4c8c963.

📒 Files selected for processing (4)
  • services/core/auth/src/nmp/core/auth/app/bundle.py
  • services/core/auth/src/nmp/core/auth/app/policies/domain.rego
  • services/core/auth/src/nmp/core/auth/app/policy_tests/authz_simple_test.rego
  • services/core/auth/tests/test_embedded_pdp.py
💤 Files with no reviewable changes (2)
  • services/core/auth/src/nmp/core/auth/app/policies/domain.rego
  • services/core/auth/src/nmp/core/auth/app/bundle.py

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 3, 2026

Suite Lines Covered Line Rate Branch Rate
Unit Tests 18480/24466 75.5% 62.0%
Integration Tests 11888/23243 51.1% 26.4%

Copy link
Copy Markdown
Contributor

@mckornfield mckornfield left a comment

Choose a reason for hiding this comment

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

domain doesn't feel like the right name to me because of priors, but I don't know that my other names are any better

extract_domain_name(path) := domain if {
base_path := split(path, "?")[0]
parts := split(base_path, "/")
count(parts) >= 3
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

does this act as an assert?

path := input.attributes.request.http.path
}

# Extract domain name from /apis/<domain>/... request paths.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

when I think domain I think of DNS like a TLD, is that the right word, versus "service" or "component"?

Signed-off-by: Ryan Sadler <267728323+ironcommit@users.noreply.github.com>
Signed-off-by: Ryan Sadler <267728323+ironcommit@users.noreply.github.com>
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