Conversation
BundleMaker assumed every policy module is a rego file and tried to extract a rego package from it. Policy stores such as OpenFGA use other policy formats (e.g. ".fga" authorization models), which have no rego package - non-rego policy modules now get an empty package name instead of failing rego parsing, so they can be synced from git like any other policy module.
Adds OpenFGA (https://openfga.dev) as a policy store alongside OPA and Cedar (POLICY_STORE_TYPE=OPENFGA). OpenFGA does not store independently addressable policy files: a store holds one immutable, versioned authorization model plus relationship tuples. The new OpenFGAClient maps OPAL's concepts onto OpenFGA: - policy updates: every policy module in a bundle (.fga DSL file or a JSON model fragment) is compiled into a single combined authorization model which is written to POST /stores/{id}/authorization-models on every policy update (modules are tracked in memory so delta bundles, set_policy and delete_policy all modify the combined model). - data updates: OPAL data updates are converted into relationship tuples (openfga_tuples.convert_to_tuples accepts tuple-native payloads and a convenient {object: {relation: [users]}} nested form) and written via POST /stores/{id}/write, chunked to OpenFGA's 100-tuples-per-write limit and idempotent via on_duplicate: ignore. Deletions read the affected tuples first and delete them explicitly. - get_data_with_input maps onto OpenFGA's Check API. Also includes an OpenFGA DSL (.fga) -> JSON transpiler (openfga_dsl), validated against the official openfga/language transformer test cases, plus a merge/extend mechanism for combining multiple .fga modules into one authorization model. Client configuration follows the existing policy-store patterns: new OPAL_POLICY_STORE_OPENFGA_* confi options (store id/name, auto-create, authorization model pinning, max tuples per write, duplicate handling), bearer-token auth support, transaction-log state, and a liveness probe against /healthz (falling back to GET /stores) via the shared LivenessProbeMixin.
- openfga_client_test.py: an in-process, stateful fake of the OpenFGA HTTP API (stores, authorization models, tuple write/read with pagination, check with a small Zanzibar-style resolver) exercises the full OPAL client <-> OpenFGA flow over real HTTP: store provisioning and reuse, bundle/delta application, data-updates-to-tuples, chunked writes, deletion, backup/restore, error and connection-failure paths. - openfga_dsl_test.py: conformance tests transpiling the official openfga/language dsl->json transformer test cases (vendored under tests/fixtures/openfga_transformer, Apache-2.0) plus error paths. - openfga_tuples_test.py: data-update -> tuple conversion cases. - openfga_client_liveness_test.py: liveness probe behavior (healthz, fallback to /stores, recovery), mirroring the OPA/Cedar suites. Covers 100% of lines of openfga_client.py, openfga_dsl.py and openfga_tuples.py.
- docker/docker-compose-example-openfga.yml: OpenFGA + opal-server + opal-client demo. The policy repo (an authorization model in the .fga DSL plus static data) is committed into a local git repo that the server watches; the client (POLICY_STORE_TYPE=OPENFGA) transpiles the model into an OpenFGA authorization model and converts data fetched from a mock external data source (nginx-served tuples.json) into relationship tuples. - docker/docker-compose-example-openfga-multi.yml: the same demo with two opal-clients, each keeping its own OpenFGA instance in sync. - documentation/docs/tutorials/openfga.mdx: a walkthrough (how OpenFGA maps to OPAL concepts, running the demo, verifying with the Check API, live policy updates, and all OpenFGA-specific config options). - documents the new OPAL_POLICY_STORE_OPENFGA_* options in documentation/docs/getting-started/configuration.mdx.
✅ Deploy Preview for opal-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes Issue
Closes #661
/claim #661
Changes proposed
This PR adds OpenFGA as a policy store in OPAL, alongside OPA and Cedar, so OPAL can manage OpenFGA services end-to-end: it syncs OpenFGA authorization models from a git policy repo, syncs relationship tuples from external data sources, and reports transaction health/liveness, with working single- and multi-client docker-compose demos.
Design note: OpenFGA does not store independently addressable "policy files" - a store holds one immutable, versioned authorization model plus relationship tuples.
OpenFGAClienttherefore keeps the bundle's policy modules in memory, compiles them into a single combined authorization model, and writes a new model version on every policy update (including deltas and deletions). Data updates are translated into relationship tuples and written through OpenFGA's write API.Acceptance criteria (issue #661)
1. Ability to configure OpenFGA as a policy store in OPAL
PolicyStoreTypes.OPENFGA+ factory wiring:packages/opal-client/opal_client/policy_store/schemas.py,packages/opal-client/opal_client/policy_store/policy_store_client_factory.pyOpenFGAClientimplementing OPAL's full policy-store interface (policy/data updates, transaction log, liveness probe, backup/restore):packages/opal-client/opal_client/policy_store/openfga_client.pyOPAL_POLICY_STORE_OPENFGA_*confi options inpackages/opal-client/opal_client/config.py(store id/name, auto-provisioning by name, authorization-model pinning, max tuples per write, duplicate-tuple handling), documented indocumentation/docs/getting-started/configuration.mdx; bearer-token auth via the existingOPAL_POLICY_STORE_AUTH_TYPE=token.2. OpenFGA models/policies are auto-synced from git
BundleMakernow accepts non-rego policy modules, so.fgafiles flow through OPAL's existing git-watch -> bundle -> pub/sub -> client pipeline:packages/opal-common/opal_common/git_utils/bundle_maker.py.fgaDSL -> JSON transpiler (union/intersection/exclusion, computed usersets, tuple-to-usersets, type restrictions incl. wildcards and conditions,extend type, modules, CEL conditions):packages/opal-client/opal_client/policy_store/openfga_dsl.py.fgarepos are enabled with the pre-existingOPAL_FILTER_FILE_EXTENSIONS/OPAL_POLICY_REPO_POLICY_EXTENSIONSoptions (documented in the tutorial and set in the demo compose files).3. OpenFGA supports the data fetching pattern and syncing data from external data sources
set_policy_data/patch_policy_data) is wired to OpenFGA tuples:packages/opal-client/opal_client/policy_store/openfga_tuples.pyconverts tuple-native payloads ({"tuples": [...]}/ lists / single tuples) and a convenient nested{object: {relation: [users]}}form (with optional OpenFGA relationship conditions) into tuples; writes are chunked to OpenFGA's 100-tuples-per-write limit and idempotent (on_duplicate: ignore);delete_policy_datareads then deletes the affected tuples;get_data_with_inputmaps onto OpenFGA's Check API.4. A working end-to-end demo with example ReBAC policies and mock data
.fgaDSL + static data):docker/docker_files/openfga_policy_repo/docker/docker_files/openfga_data/tuples.jsondocumentation/docs/tutorials/openfga.mdx5. Docker-compose examples of running OPAL with single or multiple OpenFGA clients
docker/docker-compose-example-openfga.ymldocker/docker-compose-example-openfga-multi.yml6. 100% UT coverage on the code and at least one integration test
openfga_client.py: 402 stmts,openfga_dsl.py: 414 stmts,openfga_tuples.py: 80 stmts - verified with coverage.py)packages/opal-client/opal_client/tests/openfga_client_test.pyruns the full OPAL client <-> OpenFGA flow over real HTTP against a stateful, in-process fake of the OpenFGA API (store provisioning/reuse, authorization-model writes, tuple write/read with pagination, Check with a small Zanzibar-style resolver), plusopenfga_client_liveness_test.pyfor the liveness probe.openfga_dsl_test.pyis a conformance suite that transpiles the officialopenfga/languagedsl->json transformer test cases (vendored undertests/fixtures/openfga_transformer, Apache-2.0).Check List (Check all the applicable boxes)
Screenshots
Not applicable (no UI changes).
Note to reviewers
opal-clientsuite passes (127 tests, including the pre-existing OPA/Cedar suites, proving no regressions). Inopal-common, the suites around the touched code pass (68 tests, e.g.paths_test.py); 8bundle_maker_test.pycases fail on this Windows machine for environmental reasons (local-git test setup) and were verified to fail identically on unmodifiedmaster;data_updater_test.py/server_to_client_intergation_test.pydo not collect on Windows (POSIX-onlyfcntlimport) - also pre-existing.openfga/languagetransformer fixtures.flake8 --select=E9,F63,F7,F82.