Feat: AIAC Phase 1 — functional agent, UC-1 service onboarding + integration tests - #710
Feat: AIAC Phase 1 — functional agent, UC-1 service onboarding + integration tests#710oblinder wants to merge 286 commits into
Conversation
Signed-off-by: Oleg Blinder <oblinder@gmail.com> Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
…lidation PDP Configuration Service and PDP Policy Service are co-located in a single PDP Interface Pod, reducing the Kubernetes Pod count from six to four and the manifest file count from six to four. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com> Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
PDP Configuration Service: 7070 → 7071
PDP Policy Service: 7073 → 7072
Matches the port assignments from the PDP Interface Pod redesign.
Updated sources and tests:
- src/aiac/pdp/library/{configuration,policy}.py — fallback URL defaults
- src/aiac/pdp/service/{configuration,policy}/keycloak/Dockerfile — EXPOSE and --port
- test/pdp/library/{test_configuration,test_policy}.py — BASE constant and fallback assertions
- test/pdp/library/show_keycloak_data.py — doc-comment default URL
- src/aiac/pdp/library/.env, test/pdp/library/.env — renamed AC_SERVICE_URL → AIAC_PDP_CONFIG_URL and updated port (gitignored; on-disk only)
Updated deployment docs:
- k8s/pdp-configuration-keycloak-pod.yaml — containerPort updated; dev-only header comment added
- k8s/install-pdp-config-service.md — port references updated; dev-only callout added; title clarified
In production both PDP services run as containers in the PDP Interface Pod
defined in pdp-interface-deployment.yaml (issue 4.2).
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com> Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Add step-by-step event and call flow diagrams for UC-1a (service on-boarding), UC-1b (realm role on-boarding), UC-2a (incremental policy update), and UC-2b (full rebuild) under a new Call Flows subsection in the Technical Addendum. Restructure the Technical Addendum: promote Key Interface Surface to a named subsection (###) and demote the five endpoint description subsections to ####, keeping Call Flows at the same ### level. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com> Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com> Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Incorporated Abstract, Problem Description, Problem Solution, Major Use-Cases, Component Architecture overview, and Kagenti/Keycloak/OPA Interfaces sections from ARCHITECTURE-SUMMARY.md into PRD.md. Restructured PRD layout: - Removed redundant Purpose section; promoted its design content to standalone Problem Description, Problem Solution, and Design Principles sections - Added Major Use-Cases (UC-1 through UC-4) as a top-level section - Added Component Summary table and data-flow diagram to Architecture Overview - Added Kagenti / Keycloak / OPA Interfaces section - Grouped component sections 7.1–7.8 under a single AIAC System Components parent section - Renumbered all sections 1–10 in document order Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com> Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
… semantics Replace generic (read config)/(set policy) labels in both the architecture summary and PRD diagrams with descriptive italic call text matching the PDP interface endpoints. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com> Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Technology-neutral greenfield evaluation of Red Hat AMQ Broker, AMQ Streams, and AMQ Interconnect against the ten functional requirements derived from the AIAC PRD. AMQ Broker satisfies all requirements; selection guidance covers conditions where it is preferred over the PRD's NATS JetStream choice. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com> Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com> Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com> Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
…l Addendum Move Call Flows before Short-Term Objectives for a more logical reading order (structure before goals). Remove the Technical Addendum wrapper, promoting Call Flows to a top-level section. Fix missing blank line before the NATS JetStream interface divider. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com> Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
Reshape models, refactor Configuration and Policy to stateful class APIs based on design decisions from grilling session. Models: - Remove Assignments and Permission - Role: drop clientRole; add childRoles (list[Role]), mappedScopes (list[Scope]) - Subject: add roles (list[Role]) - Service: drop clientId, protocol, publicClient; add type (Agent/Tool), roles, scopes - Scope: drop protocol - Definition order: Subject → Role → Service → Scope with model_rebuild() calls Configuration: refactor from module-level functions to stateful Configuration class with for_realm() factory; drop get_subject_assignments, get_service_permissions, get_role_composites Policy: refactor from module-level functions to stateful Policy class with for_realm() factory; drop all composite/permission functions; rename create_service_scope → create_scope Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com> Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
… PRD Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com> Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
…ients
Reshape aiac.pdp.library to match the redesigned PRD (issues 1.6, 2.9,
2.10). Implementation done via TDD red-green-refactor cycles.
Models (issue 1.6):
- Remove Assignments, Permission; remove clientRole from Role; remove
clientId/protocol/publicClient from Service; remove protocol from Scope
- Add Service.type: Literal["Agent","Tool"] | None
- Add nested roles/scopes lists to Subject, Role, Service
- Add childRoles/mappedScopes to Role; call model_rebuild() for forward refs
Configuration (issue 2.9):
- Rewrite module-level functions as Configuration class with for_realm()
classmethod factory; reads AIAC_PDP_CONFIG_URL env var with fallback
- get_subjects / get_roles / get_services / get_scopes return typed lists
Policy (issue 2.10):
- Rewrite as Policy class with for_realm() factory; reads AIAC_PDP_POLICY_URL
- create_scope POSTs to /services/{id}/scopes and returns Scope
Supporting changes:
- Empty pdp/__init__.py and pdp/library/__init__.py (no re-exports at pkg root)
- Adapt read_api_from_config to new model shapes; stub out-of-scope functions
(get_subject_assignments, get_service_permissions, get_role_composites)
pending issues 3.5 / 3.7–3.11
- Rewrite test_models.py (31 tests), test_configuration.py (15 tests),
test_policy.py (8 tests) — all 54 pass
- Update show_keycloak_data.py to the new class-based API; confirmed green
against live PDP config service in kind-kagenti cluster
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
…dule Scopes are configuration entities, not policy entities. The library split is by domain: configuration (subjects/roles/services/scopes read+write) vs policy (composite role mappings / Rego rules). Move create_scope and its HTTP endpoint from AIAC_PDP_POLICY_URL to AIAC_PDP_CONFIG_URL. Update PRD.md to remove the read-only characterisation of the PDP Configuration Service and the configuration library module. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com> Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
…d}/scopes endpoint
- Add `Configuration.create_scope(service_id, scope_name, description)` to
`aiac.pdp.library.configuration` — POSTs to the config service and returns
a typed `Scope` instance
- Strip `Policy` to a skeleton (`__init__` + `for_realm` only); scope creation
belongs to `Configuration`, not `Policy`
- Add `POST /services/{service_id}/scopes` to the PDP Configuration Keycloak
service: creates a realm-level client scope and assigns it as a default scope
to the given service in a single operation (201 on success, 502 on
KeycloakError)
- Add `pydantic` to the configuration service `requirements.txt`
- Update PRD (`pdp-configuration-service.md`) with the new endpoint spec
- Full TDD coverage: 93 unit tests pass
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Replace the atomic `create_scope(service_id, scope_name, description)`
with two separate methods: `create_scope` (realm-level creation) and
`map_scope_to_service` (assigns existing scope to a service, returns
refreshed Service). Add matching `create_role` / `map_role_to_service`
methods for realm-level roles.
Update the PDP Configuration Service PRD accordingly: remove the old
atomic `POST /services/{id}/scopes`, add `POST /scopes`, `POST /roles`,
`POST /services/{id}/scopes/{scope_id}`, `POST /services/{id}/roles/{role_id}`,
and `GET /services/{id}` endpoints with Keycloak Admin API mappings and
409/502 response semantics.
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Add 5 new write endpoints to the PDP Configuration Service (issue 1.9):
- GET /services/{service_id}
- POST /scopes (creates realm-level scope with protocol openid-connect)
- POST /services/{id}/scopes/{scope_id} (assigns scope as default)
- POST /roles (creates realm-level role)
- POST /services/{id}/roles/{role_id} (assigns role to service scope mappings)
All endpoints return 409 on conflict and 502 on KeycloakError.
Update Configuration library client (issue 2.9) to replace the old
create_scope(service_id, ...) with four new write methods:
- create_scope(scope_name, scope_description) → POST /scopes
- map_scope_to_service(service, scope) → POST + GET re-fetch
- create_role(role_name, role_description) → POST /roles
- map_role_to_service(service, role) → POST + GET re-fetch
Add unit tests for all 5 service endpoints (issue 2.11) and update
library configuration tests to cover the new write methods (issue 3.13).
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
Extract Service Onboarding, Policy Update, and Role Update (formerly Realm Roles) into dedicated sub-PRDs under components/aiac-agent/. Trim aiac-agent.md to shared infrastructure only (NATS Consumer, Controller, Shared Module, Validate Node, Config, Error Handling). Add scoped architecture diagrams to each UC sub-PRD. Rename Realm Roles orchestrator to Role Update across all PRD documents. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com> Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
aiac.apply.build → aiac.apply.policy.build POST /apply/build → POST /apply/policy/build POST /apply/rebuild → POST /apply/policy/rebuild Updated across uc2-policy-update.md, aiac-agent.md, ARCHITECTURE-SUMMARY.md, PRD.md, event-broker.md, and rag-ingest-service.md. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com> Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
All AIAC components (Agent, Event Broker, PDP Interface, RAG) now use
the domain term 'role'. Keycloak-facing services translate to the
Keycloak 'realm role' concept internally at the boundary.
- NATS subject: aiac.apply.realm-role.{id} → aiac.apply.role.{id}
- HTTP path: /apply/realm-role/{id} → /apply/role/{id}
- Python dirs: realm_roles/ → roles/, realm_role/ → role/
- Prose, Mermaid labels, section headings updated across 13 files
- python-keycloak method names and Keycloak Admin API URLs unchanged
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
…o aiac Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
…_subjects() Replaces name-based exclusion sourced from get_service()/get_roles() with ownership-based exclusion (role id / scope.serviceId) sourced from get_services() (correct kind/ownership) plus get_subjects() (membership- derived user roles), aligning the builder with the PCE's worldview. Fixes I1 (self-reference: focus service's own role leaking into its own scope's candidates) and I2 (role-kind erasure: agent-held roles rendering as kind=User instead of kind=Agent). Updates docs/specs/components/aiac-agent.md and docs/specs/components/aiac-agent/uc1-service-onboarding.md to match. Signed-off-by: Oleg Blinder <oblinder@gmail.com>
…ontract Replace get_service/get_roles name-based exclusion mocks with get_services/get_scopes/get_subjects, matching ServicePolicyBuilder.build's ownership-based (role id / scope.serviceId) candidate selection. Add tests for ownership-beats-membership, kind routing (Agent vs User), non-aiac.managed scope drop, and missing-focus 404 handling. Signed-off-by: Oleg Blinder <oblinder@gmail.com>
The builder's focus-service lookup matched the trigger id against
Service.serviceId (the human-readable Keycloak clientId), but the
/apply/service/{id} route is keyed on the internal client UUID
(Service.id) -- a clientId can be a slash-bearing SPIFFE URI the
single-segment route cannot carry. The two id shapes never matched,
so every onboard raised HTTP 404 "service not found in IdP catalog"
(regression introduced when the builder switched from get_service(id)
to scanning get_services()).
Match on Service.id instead. Add a regression test with distinct
id/serviceId values (the prior _service() fixture conflated them,
which is why the mismatch went uncaught); extend the helper with a
`ref` param for the clientId.
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
The Service Policy Builder focus is resolved from get_services() by
id (the Keycloak internal client UUID the /apply/service/{id} route and
Trigger.entity_id carry), not by serviceId/clientId (which may be a
slash-bearing SPIFFE URI). Sync the uc1-service-onboarding spec, the
provision-state comment, and the builder docstring to this contract.
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
The archived onboarding.old/policy subtree is no longer part of the build, so remove its ruff extend-exclude entry and its pytest pythonpath entry. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Add DELETE /policy/services (clear-all) to the Policy Store Service and a clear_service_policies() wrapper in the store library. The store's SQLite lives on a StatefulSet PV that survives image redeploys, and UC-1 onboarding appends with override=False, so pre-fix cruft accumulated across runs and the PCE replayed it into every regenerated Rego (issue 6.3 RC-A). Wire a per-run store clear into the UC-1 integration harness (uc1_onboard.py onboarded_stack), alongside the existing Keycloak cleanup_provisioned, so each run derives its Rego from only the edges it onboarded. The clear is best-effort via a port-forward + DELETE, keeping the harness free of any aiac import. Verified live: clearing the deployed store (53 stale edges -> 204 -> 404) and re-running rung 2 produces clean inbound Rego (I1 view-profile, I2 dups / self-reference, and the git-issue-agent-aud pollution all gone; SPM edge count 53 -> 3+5). Rung 2/3 outbound stays red by design pending the PCE<->UC-1 outbound-gate spec decision (RC-B, deferred). Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com>
…d UC-1 specs Reconcile the UC-1 outbound-gate specs with the policy-model owner's RC-B decision (Handoff 09): outbound access is a per-scope two-gate AND — a requested scope is allowed iff both the user role and the agent's own per-skill operator role reach it — with the capability gate decided by the PRB from descriptions (capability-match). Range is tool union other agent. - policy-computation-engine.md: generalize the outbound-subject gate range to tool-or-agent and clarify the directional-relevance rule is a derivation-layer property, not a claim the user gate is empty. - uc1-onboarding-pipeline.md: replace the degenerate/user-gate-only narrative with the per-scope two-gate AND + capability-matched agent gate; provision one operator role per skill in place of the generic agent role. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Resolve RC-B of issue 6.3: outbound access is now a per-scope AND
where a requested scope (input.function_name) is allowed iff BOTH the
user (subject) role and the agent's own operator role reach that scope.
- provision: per-skill operator roles so the agent's capability gate is
scope-specific rather than a single generic {workload}.agent role
- rego writer: per-scope AND on input.function_name across the subject
and capability gates; generalized rubric
- builder: source other-service scopes from get_services() (owner-bearing
serviceId) instead of the global get_scopes() catalog, whose empty
serviceId broke self-exclusion and routed rules to SPM("") (422)
- rename emitted outbound map outbound_subject_role_scopes ->
subject_role_scopes, paralleling the sibling agent_role_scopes
- un-degenerate the UC-1 integration contract; update probes, specs,
and writer/PCE unit tests to match
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
…ue_operations) Rename the 5.3 policy-pipeline scenario's github-agent client roles from source-operator / issues-operator to source_operations / issue_operations, matching exactly the per-skill operator roles UC-1 provisioning emits from the AgentCard skill ids. - scenario.py: AGENT_ROLES keys + OUTBOUND_PAIRS; rewrite the plural-naming NOTE — the _operations suffix (not the actor-noun operator) sidesteps the same PRB-auditor actor-confusion the old plural issues-operator dodged - policy.explicit.md: the two "Agent roles -> tool operations" grants - policy-pipeline.md spec + demo docs (github-agent.md, github_agent README) The PRB-auditor collision regression (test_auditor_dimension_integration.py, issue 3.20) deliberately keeps the singular issue-operator and is untouched. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Add the authoritative service-offboard path that closes drift "case 3"
(onboard followed by offboard): reconcile's catalog-anchored drift-GC
cannot prune a decommissioned service because it is gone from
get_services(), so its own SPM inbound edges, its outbound footprint on
other SPMs, and — if an agent — its APM/Rego would linger indefinitely.
- engine: decommission(service_id) + _decommission — purge SPM(X), strip
X's outbound edges from every other SPM, delete APM(X) if X was an
agent, and re-derive every still-live agent whose policy changed.
Keyed by clientId (SPM key), not the Keycloak UUID, since an offboarded
client cannot be resolved via get_services(). Factored spm()/is_agent()
into _spm_cache, shared with _run. Preserves the single-get_services()
read and per-agent partial-upsert invariants.
- offboarding UC: thin offboard_service stub + POST /apply/offboard/
{service_id:path} route dispatching to decommission; bypasses
compute_and_apply. Re-export decommission from aiac.policy.computation.
- tests: two case-3 PCE cases (tool offboard strands no edges + rederives
agent; agent offboard deletes APM + purges outbound footprint) and
three offboard-route tests.
- specs: PRD merge-semantics, PCE component spec (Reconcile + Decommission
sections), aiac-agent endpoints/use-case tables.
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
# Conflicts: # .gitignore # aiac/docs/specs/ARCHITECTURE-SUMMARY.md # aiac/docs/specs/PRD.md # aiac/docs/specs/components/aiac-agent.md # aiac/docs/specs/components/aiac-agent/uc1-service-onboarding.md # aiac/docs/specs/components/event-broker.md # aiac/docs/specs/components/idp-configuration-service.md # aiac/docs/specs/components/keycloak-service.md # aiac/docs/specs/components/library-idp.md # aiac/docs/specs/components/library-policy-store.md # aiac/docs/specs/components/pdp-policy-keycloak-service.md # aiac/docs/specs/components/pdp-policy-writer-opa.md # aiac/docs/specs/components/policy-computation-engine.md # aiac/docs/specs/components/policy-model.md # aiac/docs/specs/components/policy-store.md # aiac/docs/specs/demo/github-agent.md # aiac/docs/specs/demo/github-tool.md # aiac/docs/specs/integration-test/pdp-policy-writer.md # aiac/docs/specs/integration-test/policy-pipeline.md # aiac/docs/specs/integration-test/uc1-onboarding-pipeline.md Signed-off-by: Oleg Blinder <oblinder@gmail.com>
- Fix invalid trailing comma in aiac/pyrightconfig.json (check-json) - Trailing whitespace / EOF fixes on PRD.md, github-mcp-tools-summary.json, Dockerfile Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
|
Important Review skippedToo many files! This PR contains 204 files, which is 104 over the limit of 100. To get a review, narrow the scope: Upgrade to a paid plan to raise the limit. This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (204)
You can disable this status message by setting the 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 |
Resolve the security, correctness, test, and doc/spec findings from the CodeRabbit CLI review of the UC-1 service-onboarding Phase 1 branch. Security: - Remove committed keycloak-admin-secret (admin/admin) from pdp-interface-deployment.yaml and idp-configuration-keycloak-pod.yaml; document the Secret + namespace as pre-provisioned preconditions. - .dockerignore the demo agent's .env so secrets are never baked into the image. - Escape all values/keys emitted into Rego string literals via json.dumps (rego.py) to prevent Rego injection / malformed output. Live-source correctness: - Policy Store: 422 guard on path/body service_id mismatch; serialize cache+DB mutations under a single write lock; mkdir the SQLite data dir. - Policy Store library: add request timeouts to all HTTP calls. - IdP config: fail fast (RuntimeError) when the realm env var is unset. - Keycloak IdP service: narrow the over-broad KeycloakError handler and replace the O(scopes x clients) owner scan with a one-pass reverse index. - Onboarding provision nodes: guard missing tool/skill names and empty service ports with actionable HTTP 502s instead of KeyError/IndexError. - Retries (shared/upstream + policy_rules_builder): retry only transient failures; tolerate an unset/non-numeric UPSTREAM_MAX_RETRIES. - PCE engine: fold changed owners into the affected set so policy override revocation propagates (previously left stale APM/SPM state). - Legacy Keycloak writer: correct add_client_default_client_scope call. Tests: - Fix vacuous startswith() outbound filters (substring match, matching rung 2). - Harden the integration harness (opa_dump guard, store-clear failure handling, ConfigMap rollout, port-forward readiness + stderr drain). - Assorted docstring/name/marker fixes. Docs/specs: - Reconcile the PCE exception contract (re-raise), module paths, trigger-id wording (service_id/UUID), concurrency, OPA subject-gate semantics, and analysis-doc inaccuracies to match the implemented behavior. Unit suite green: 466 passed. Integration suite collects (154). Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Resolve the 11 findings from the follow-up CodeRabbit review:
- github_tool Dockerfile: install -r requirements.txt (was hardcoding
mcp[cli]+uvicorn, dropping the pinned bounds and httpx).
- pdp-policy-writer-opa spec: correct the outbound behaviour note
(subject_ok matches subject_role_scopes[role], target_ok matches
target_scopes[input.target]); fix the stale file tree
(aiac/pdp/library/policy.py -> aiac/pdp/policy/library/api.py); add a
note reconciling the client-UUID vs clientId identifiers used at the
onboarding vs writer layers.
- idp-configuration-service spec: reconcile the /services/{id}/roles
summary row, behaviour note, and Assumption 3 with the code, which
returns client roles PLUS aiac.managed realm roles on the service
account.
- ARCHITECTURE-SUMMARY: mark the NATS Event Broker / subscription path as
Phase 2 and the direct Controller /apply/* invocation as the Phase 1
trigger, matching event-broker.md; align the library import path with
the .api submodule convention.
- uc1-service-onboarding spec: make the focus-resolution snippet use a
default + explicit 404 (matches builder.py and the surrounding prose).
- k8s: rename the standalone pod's ConfigMap to aiac-pdp-config-standalone
to avoid clobbering the interface ConfigMap; document the deployment as
a Phase 1 local/Kind dev topology (node-local images).
Docs/specs and build config only; no source changes, unit suite unaffected.
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Address 8 open GitHub Advanced Security (CodeQL) alerts on PR rossoctl#710: - py/stack-trace-exposure (x3, store/service/main.py): SQLite errors were echoed to clients via {"error": str(e)}. Log the exception server-side and return a generic {"error": "database error"} body instead. - py/log-injection (engine.py): strip CR/LF from service_id before logging so a hostile id cannot forge log records. - py/partial-ssrf (pdp/policy/library/api.py): URL-encode the agent_id path segment with quote(..., safe=''); also fixes routing for slash-bearing clientIds so they cannot split into extra path segments. - py/partial-ssrf (x2, store/library via keying.py): assert the base64url encoded service_id matches [A-Za-z0-9_-]+ before it reaches a request URL. - py/path-injection (rego.py slugify): assert the slug is a single [a-z0-9_]+ segment (blocks path traversal, rejects empty slugs) before it becomes a filename in the PDP Policy Writer output dir. No behaviour change for valid inputs; full unit suite (466 tests) green. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Address 8 open GitHub Advanced Security (CodeQL) alerts on PR rossoctl#710: - py/stack-trace-exposure (x3, store/service/main.py): SQLite errors were echoed to clients via {"error": str(e)}. Log the exception server-side and return a generic {"error": "database error"} body instead. - py/log-injection (engine.py): strip CR/LF from service_id before logging so a hostile id cannot forge log records. - py/partial-ssrf (pdp/policy/library/api.py): URL-encode the agent_id path segment with quote(..., safe=''); also fixes routing for slash-bearing clientIds so they cannot split into extra path segments. - py/partial-ssrf (x2, store/library via keying.py): assert the base64url encoded service_id matches [A-Za-z0-9_-]+ before it reaches a request URL. - py/path-injection (rego.py slugify): assert the slug is a single [a-z0-9_]+ segment (blocks path traversal, rejects empty slugs) before it becomes a filename in the PDP Policy Writer output dir. No behaviour change for valid inputs; full unit suite (466 tests) green. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com>
…w findings Two automated checks on PR rossoctl#710 were failing: CodeQL (2 new alerts): - py/partial-ssrf (critical, pdp/policy/library/api.py): the prior quote() fix did not satisfy CodeQL. Add a regex fullmatch barrier on the encoded segment (mirroring policy/store/keying.py, which CodeQL accepts) via a new _agent_id_segment() helper used by apply_agent_policy + delete_agent_policy. - py/path-injection (high, pdp/service/policy/opa/main.py): the /health handler took out_dir as a FastAPI route parameter, which CodeQL treats as request-controlled input flowing into a filesystem path check. Resolve it from get_output_dir() inside the handler instead (it is operator config, never a request). Update TestHealth to drive REGO_OUTPUT_DIR via env. Dependency Review: - chromadb CVE-2026-45829 / GHSA-f4j7-r4q5-qw2c (critical, transitive via crewai in the github_agent demo) has no patched version (affected <= 1.5.9) and is already documented as a mitigated/accepted risk in the demo's pyproject.toml. Add it to the dependency-review-action allowlist. Full unit suite (466 tests) green. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com>
…w findings Two automated checks on PR rossoctl#710 were failing: CodeQL (2 new alerts): - py/partial-ssrf (critical, pdp/policy/library/api.py): the prior quote() fix did not satisfy CodeQL. Add a regex fullmatch barrier on the encoded segment (mirroring policy/store/keying.py, which CodeQL accepts) via a new _agent_id_segment() helper used by apply_agent_policy + delete_agent_policy. - py/path-injection (high, pdp/service/policy/opa/main.py): the /health handler took out_dir as a FastAPI route parameter, which CodeQL treats as request-controlled input flowing into a filesystem path check. Resolve it from get_output_dir() inside the handler instead (it is operator config, never a request). Update TestHealth to drive REGO_OUTPUT_DIR via env. Dependency Review: - chromadb CVE-2026-45829 / GHSA-f4j7-r4q5-qw2c (critical, transitive via crewai in the github_agent demo) has no patched version (affected <= 1.5.9) and is already documented as a mitigated/accepted risk in the demo's pyproject.toml. Add it to the dependency-review-action allowlist. Full unit suite (466 tests) green. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com>
The dependency-review check flagged json-repair@0.25.3 (transitive via crewai in the github_agent demo) for GHSA-xf7x-x43h-rpqh (high, unbounded-CPU DoS via circular JSON Schema $ref). Patched in 0.60.1. Pin it in pyproject.toml following the existing indirect-dep pattern and refresh uv.lock (also pulls minor crewai 1.15.2->1.15.8, mcp, pydantic-settings bumps from re-resolution). chromadb (CVE-2026-45829, no patched version) remains allowlisted via allow-ghsas in security-scans.yaml. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Summary
Delivers Phase 1 of the AI-based Access Control (AIAC) Kagenti extension and the UC-1 (service onboarding) use case, addressing #645.
AIAC translates a natural-language access-control policy into concrete PDP (OPA) rules for AI agents on Kubernetes. This PR lands the full Phase 1 stack plus the integration-test ladder that proves UC-1 end-to-end by evaluating the generated Rego (Phase 1 explicitly defers live enforcement/traffic).
What's included
Core services & library (
aiac/src/aiac/, all Python 3.12,idp/pdp/policynamespace split):aiac.idp.configuration) — Keycloak Admin REST proxy exposing subjects/roles/services/scopes with per-service enrichment; stamps theaiac.managedprovisioning marker.{slug}.inbound.rego+{slug}.outbound.rego(the Phase 1 Interface-Pod deployment).ServicePolicyModel/AgentPolicyModelstate; SPM-centric.aiac.policy.computation) — in-process library; singlecompute_and_apply(rules)entry point, additive merge with drift-GC + authoritativedecommission()offboard.aiac.policy.model) —PolicyRule,ServicePolicyModel,AgentPolicyModel,PolicyModel.AIAC Agent (
aiac.agent) — FastAPI Controller dispatching/apply/*to UC sub-agents:kagenti.io/typelabel, discovers capabilities (AgentCard for agents; MCPtools/listfor tools), provisions roles/scopes into Keycloak, runs the Policy Rules Builder (PRB, LLMtemperature=0), and calls the PCE.Demo scenario workloads (
aiac/demo/) —github-agent(A2A, source + issue skills) and a simplifiedgithub-toolMCP server, with their k8s manifests. These are the workloads onboarded by the integration tests.Kubernetes manifests (
aiac/k8s/) — Interface Pod, Policy Store StatefulSet, Agent deployment (Phase 1).Specs & issue tracking —
aiac/docs/specs/(master PRD + per-component + integration-test specs) andaiac/docs/issues/(local markdown issue tracker + handoffs).Testing
.venv/bin/pytest test/ -m "not integration"(no--ignoreflags needed after the Wave 3 SPM migration).aiac/test/integration/,@pytest.mark.integration,opa-gated) —uc1-onboarding-pipeline: drives the real UC-1 agent (POST /apply/service/{id}) against really-deployed workloads and asserts the generated Rego withopa eval, computing verdicts fromscenario_uc1.py(the oracle):policy-pipelineandpdp-policy-writerstandalone launcher integration tests.Out of scope (Phase 1)
Live trigger path / NATS Event Broker, live enforcement & token exchange, writing Rego to the
AuthorizationPolicyCR, the RAG knowledge base, and UC-2/UC-3 — all deferred to later phases per the PRD.Notes for reviewers
aiac/; the only root change is a newpyrightconfig.json.aiac/docs/specs/PRD.mdandaiac/docs/specs/integration-test/uc1-onboarding-pipeline.md.Addresses #645 (Phase 1 implementation + integration tests; runnable demo flow to follow in a separate PR).
Assisted-By: Claude (Anthropic AI) noreply@anthropic.com