Skip to main content

Keycloak IdentityResolver for audit-framework — resolve groups/roles/users via the Admin API, with a TTL cache.

Project description

audit-framework-keycloak

The Keycloak IdentityResolver for audit-framework. Broadcast policies target roles, groups and users; this plugin turns those into the concrete user ids — and per-user email/phone contacts — the dispatcher delivers to, by querying the Keycloak Admin REST API as a service account (AD-7).

Install

pip install audit-framework-keycloak          # bring your own transport
pip install audit-framework-keycloak[httpx]   # + httpx for the default transport

Use

from audit_framework_keycloak import (
    KeycloakIdentityResolver, CachedIdentityResolver, httpx_transport,
)

resolver = KeycloakIdentityResolver(
    base_url="https://kc.internal:8443",
    realm="acme",
    client_id="audit-svc",
    client_secret=os.environ["KEYCLOAK_CLIENT_SECRET"],
    transport=httpx_transport(shared_httpx_client),   # optional; pooled in prod
)

# Memoise the slow-changing group/role lookups (default TTL 300s).
identity = CachedIdentityResolver(resolver, ttl_seconds=300)

await identity.resolve_role("auditor")        # -> ["<user-uuid>", ...]
await identity.resolve_group("legal-team")    # by name (or "/parent/child" path)
await identity.resolve_user("<user-uuid>")    # passthrough: ["<user-uuid>"] or []
await identity.get_user_contact("<user-uuid>", "email")   # -> "alice@acme.io"
await identity.get_user_contact("<user-uuid>", "sms")     # -> phone attribute

It advertises itself as the keycloak provider for the identity_resolver port via the audit_framework.plugins entry point, so it's discoverable through the registry.

End-to-end example (wiring into the pipeline)

The resolver feeds two stages: the broadcast layer (role/group/user → recipient ids) and the dispatcher (recipient id → email/SMS contact). Wire one cached instance into both:

import os
import httpx
from audit_framework.core.dispatcher import Dispatcher
from audit_framework.core.middlewares import BroadcastPolicyMiddleware, DispatchMiddleware
from audit_framework.core.pipeline import Pipeline
from audit_framework_keycloak import (
    KeycloakIdentityResolver, CachedIdentityResolver, httpx_transport,
)

http_client = httpx.AsyncClient(timeout=10.0)   # shared, pooled connections

identity = CachedIdentityResolver(
    KeycloakIdentityResolver(
        base_url="https://kc.internal:8443",
        realm="acme",
        client_id="audit-svc",
        client_secret=os.environ["KEYCLOAK_CLIENT_SECRET"],
        transport=httpx_transport(http_client),
    ),
    ttl_seconds=300,
)

# Same `identity` resolves recipients (broadcast) AND their contacts (dispatch).
dispatcher = Dispatcher(channels, renderer, notification_store, identity=identity)
pipeline = (
    Pipeline()
    # ... AuditPolicy / Redact / Store / SinkFanOut middlewares first ...
    .use(BroadcastPolicyMiddleware(policy_store, identity, throttle_store=throttle))
    .use(DispatchMiddleware(dispatcher))
)

A broadcast policy that targets a Keycloak realm role, delivered over two channels — the resolver turns auditor into the user ids, then each id into an email address:

broadcast_policies:
  - name: notify-auditors-on-delete
    match: { action: [DELETE] }
    targets:
      - { type: role,  value: auditor,    channels: [email, in_app] }
      - { type: group, value: legal-team, channels: [email] }

Service-account permissions

The client uses the OAuth2 client_credentials grant, so enable Service Accounts on it and grant it the realm-management client roles needed to read the directory: view-users, query-users, query-groups. Without them the Admin API returns 403 and resolution fails loudly.

If the service-account client lives in a different realm than the one it administers (e.g. a client in master), pass token_realm=....

No hard HTTP dependency

All Admin API access goes through an injected transportasync (method, url, *, params, headers, data, json) -> HttpResult. So the resolver is fully unit-testable without a network or httpx (the test suite is stdlib-only with a fake transport), and you control connection pooling. The bundled httpx_transport() (the httpx extra) is the production default.

Caching (AD-7)

CachedIdentityResolver wraps any resolver and caches group and role membership for a TTL (default 300s) — these are queried on every matching event but change slowly. resolve_user and get_user_contact are not cached: they're cheap passthroughs, and contact changes should take effect immediately.

Token & pagination handling

  • Token: the service-account token is fetched lazily, cached, and refreshed automatically on a 401 (then the call is retried once). Concurrent resolves share a single refresh via an asyncio.Lock.
  • Pagination: members/role-user listings are fetched page-by-page (first/max, Keycloak's default cap is 100) until a short page is seen, so large groups resolve completely.

Development

pip install -e packages/audit-framework
pip install -e "packages/audit-framework-keycloak[dev]"
pytest -q packages/audit-framework-keycloak    # stdlib-only; fake transport, no Keycloak

Live integration test

The unit suite drives a fake transport. For end-to-end coverage against a real server, tests/integration_test.py self-provisions a throwaway realm (client, service account, users, group, role), exercises the resolver, and tears it down. It is gated: skipped unless KEYCLOAK_URL is set and httpx is installed, so the default run and CI stay green.

docker compose -f docker-compose.integration.yml up -d        # boot Keycloak (dev mode)
pip install -e "packages/audit-framework-keycloak[httpx,dev]"
KEYCLOAK_URL=http://localhost:8080 \
KEYCLOAK_ADMIN=admin KEYCLOAK_ADMIN_PASSWORD=admin \
    pytest -q -m integration packages/audit-framework-keycloak
docker compose -f docker-compose.integration.yml down

License

MIT

For AI agents & coding assistants

This package ships its agent guide — AGENTS.mdinside the wheel (installed at <site-packages>/audit_framework_keycloak/AGENTS.md). Read it offline, with no docs site and no network, even from an airgapped Nexus PyPI mirror:

python -m audit_framework_keycloak
python -c "import audit_framework_keycloak; print(audit_framework_keycloak.overview())"

AGENTS.md is the single source of truth: mental model, one runnable quickstart, and the exact public API. audit_framework_keycloak.overview() returns it, and tests/guide_test.py compiles its examples so the guide can't drift from the code.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

audit_framework_keycloak-0.1.1.tar.gz (21.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

audit_framework_keycloak-0.1.1-py3-none-any.whl (15.6 kB view details)

Uploaded Python 3

File details

Details for the file audit_framework_keycloak-0.1.1.tar.gz.

File metadata

  • Download URL: audit_framework_keycloak-0.1.1.tar.gz
  • Upload date:
  • Size: 21.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for audit_framework_keycloak-0.1.1.tar.gz
Algorithm Hash digest
SHA256 bfe0141264627267a84983a60a2fc458ca791620ce5ada03eb63211a42d3ac52
MD5 6377a2c62bff2fc0a56912b87bc690da
BLAKE2b-256 90a1a7d6a86f991848a5ed7e081c27296ecac95d4a2099bb3041bee663690c6c

See more details on using hashes here.

Provenance

The following attestation bundles were made for audit_framework_keycloak-0.1.1.tar.gz:

Publisher: release.yml on vanmarkic/audit-logger

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file audit_framework_keycloak-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for audit_framework_keycloak-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6d60cde556e18ef95ece6254196fa51f641b5142582893144934990a1af944b5
MD5 dfc331ff25fde484c163b11b92ae738e
BLAKE2b-256 3bd4bde4bd0e5eb005872019dd1422e9ad6baa0efd3cd994331335fcbbbd8832

See more details on using hashes here.

Provenance

The following attestation bundles were made for audit_framework_keycloak-0.1.1-py3-none-any.whl:

Publisher: release.yml on vanmarkic/audit-logger

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page