Skip to content

v0.18.0

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 27 Oct 01:31
· 116 commits to dev since this release
Immutable release. Only release title and notes can be modified.
v0.18.0
1098fa6

FOCA 0.18.0 adds a plugin engine for gateway apps such as proTES (plugins installed and pinned by operators, per-request pipelines, failover routing, provenance and a plugin test kit), an HTTP client for untrusted URLs guarded against SSRF, opt-in roles (e.g., for an admin mode), settings per trusted issuer and Python 3.15 support, and keeps one cached Casbin enforcer per app instance, so that requests cost about the same with many policy rules as with few. It contains breaking changes: please read Migrating to 0.18.0 before upgrading.

Breaking changes

  • Access control: each app instance keeps one shared, cached Casbin enforcer, kept up to date via a record of changes (<collection_name>_changes); rules changed in the database directly (e.g., with mongosh) no longer apply from the next request, but only after mark_changed() or a restart; enforcer methods that FOCA's adapter can't store (update_policy(), remove_policies()) raise NotImplementedError; during a rolling update from 0.17, upgrade all instances before changing rules (#450, #454).
  • Access control: get_enforcer() is removed; use the context manager locked_enforcer() (#468, #477).
  • Access control and errors: apps refuse to start if group_mapping maps groups but no group claim is set, or if claim_groups has surrounding whitespace; exceptions.validation_details now defaults to true, so responses to invalid requests carry the validation reason as detail (#467, #475).
  • Auth: with several trusted issuers, subjects are namespaced by issuer (<namespace>|<subject>, by default the issuer URL), also in policies, resource roles, owners and on-behalf-of users; rules and grants stored for bare subjects no longer apply unless the issuer's namespace is set to "" (#509, #512).
  • Auth: trusted issuers can be listed with settings of their own (namespace, audience, claim_groups, userinfo_claims), which replace the global settings for their tokens; security.auth.issuers is typed Sequence[str | IssuerConfig] (use issuer_urls for the URLs) (#503, #510, #514).
  • Configuration: the top-level plugins section is now validated as FOCA's gateway plugin configuration, with unknown keys refused, also in apps that are no gateways; gateway apps refuse the keys of earlier middleware stacks (middlewares, custom.middlewares) (#486, #496).

Features

  • Gateway plugins: a framework-free plugin engine (foca.plugins) for gateway apps, declared with Foca(gateway=GatewayApp(...)): plugins from the entry point group foca.plugins, allowlisted and pinned in plugins.available, run in the phases request, route, forward and response, in steps with fallback alternatives, timeouts and on_failure (#486, #496).
  • Gateway plugins: clients choose presets, own pipelines (off by default) and backends per request in the reserved tags gateway.pipeline and gateway.backends, within limits, roles and an admin frame (prepend, append); experimental (#487, #498).
  • Gateway plugins: provenance records of each run, with hashes of each plugin's changes and no secrets; optional records of failed runs with a TTL (#488, #499).
  • Gateway plugins: typed backend lists for gateway apps (#493, #501), a default failover router foca.routing.ordered (#491, #504), gateway tags that record the pipeline, plugins and backend of each task or run (#494, #505), a plugin listing API for clients (GET /plugins, GET /plugins/{plugin_id}) (#489, #506) and a plugin test kit (foca.plugins.testing) with contract checks and fakes (#492, #507).
  • Utilities: GuardedClient (foca.utils.http) sends requests to untrusted URLs with SSRF guards (public addresses only, also after DNS resolution and on connect; redirects not followed; time and size limits); new dependencies urllib3>=2.6,<3 and packaging>=23 (#490, #500).
  • Access control: opt-in roles (opt_in_roles, header opt_in_header, default X-Request-Roles) that apply only to requests that ask for them, e.g., for an admin mode, with get_available_opt_in_roles() and get_active_opt_in_roles() (#502, #508).
  • Access control: ownership by document field (set_owner_field(), owner_filter()) for resources that are never shared (#455, #456).
  • Configuration: startup checks that warn about settings that likely don't work as intended (e.g., no admins; placeholder service info), and security.access_control.require_admin (#459, #466).
  • Configuration: public URLs behind proxies (server.public_url, get_public_url()) and the typed custom configuration (get_custom_config()) (#460, #465).
  • Errors: request-specific details of validation errors (exceptions.validation_details) (#461, #464).
  • Deployment: default Gunicorn settings for apps in containers (foca.gunicorn_config) (#458, #463).
  • Python 3.15 is supported, with images tagged -py3.15; the unsuffixed tags stay on Python 3.13 (#448, #457).

Security

  • Gateway plugins: client pipelines are off by default and only accepted from authenticated callers, and the app warns on startup about policy steps of the default pipeline that client pipelines could skip (#518, #526).
  • Gateway plugins: the caller (Principal) passed to plugins is identified as by access control: on-behalf-of users, the roles of service clients and role rules (#519, #527).
  • The SSRF guard rejects IPv6 site-local addresses (fec0::/10); the build context of the base image is an allowlist, so local files (e.g., .env) can't end up in published images; the petstore-access-control example adds admins via the permissions API (#516, #522).
  • The .dockerignore files of the examples are allowlists, so that local files stay out of example images (#511, #513).

Fixes

  • Gateway plugins: the plugin listing API works in apps that set a controller for all operations (add_operation_fields with x-openapi-router-controller) (#515, #521).
  • Gateway plugins: gateway settings are bound to their app: a later Foca(...) without gateway= (e.g., in a Celery worker module) no longer clears them (#517, #525).
  • Access control: opt-in roles need no group mapping to them, so that an app's base config can make a role opt-in before its deployments map groups to it; apps warn of opt-in roles that no group is mapped to on startup, and opt-in roles must not be resource roles or start with a subject prefix (#530, #533).
  • Releases are published only after their assets are uploaded (#451, #452).
  • The petstore chart and the docs use a keep-alive of 100 s, longer than the idle timeouts of common proxies (#469, #478).

Documentation

  • Migration guide for 0.18, linked from the README and the docs index; the README lists gateway plugins and the guarded HTTP client (#523, #531, #534, #535).
  • Gateway plugins: the client-facing tags gateway.pipeline and gateway.backends and the settings client_pipelines and client_backends are marked as experimental, as they may change with federated backend selection (#528) (#529, #532).
  • The version is taken from one source (#453, #462), and the chart version in the docs from Chart.yaml (#472, #481).
  • Zenodo DOI and badge (#412, #476); the docs badge uses Shields.io (#473, #474, #479).

Maintenance

  • CI workflow and job names are aligned with the other repositories, with a single required gate CI passed (#480, #482).
  • Releases of the library chart need approval (#449, #520).
  • Sourcery skips rule-test fixtures (#470, #471).
  • Dependency updates (#484, #497).
  • Citation metadata for 0.18.0 (#524, #536).
  • A flaky timing test of the registry backend cache is fixed (#537, #538).

Commits

  • fix: publish releases after uploading assets by @uniqueg in #452
  • perf(access-control): cache the policy enforcer by @uniqueg in #454
  • feat(access-control): add field-based ownership by @uniqueg in #456
  • build: support Python 3.15 by @uniqueg in #457
  • docs: take the version from one source by @uniqueg in #462
  • feat(config): add default Gunicorn settings by @uniqueg in #463
  • feat(errors): serve validation error details by @uniqueg in #464
  • feat: add public URLs and typed custom config by @uniqueg in #465
  • feat: add startup checks of the config by @uniqueg in #466
  • ci: let Sourcery skip rule-test fixtures by @uniqueg in #471
  • docs: fix the docs badge URL by @uniqueg in #474
  • docs: add the Zenodo DOI and badge by @uniqueg in #476
  • feat!: tighten group checks and error details by @uniqueg in #475
  • refactor!: remove get_enforcer() by @uniqueg in #477
  • fix(chart): use a keep-alive of 100 s by @uniqueg in #478
  • docs: use Shields.io for the docs badge by @uniqueg in #479
  • docs: take the chart version from Chart.yaml by @uniqueg in #481
  • ci: align workflow and job names by @uniqueg in #482
  • feat(plugins): add plugin engine by @uniqueg in #496
  • feat(plugins): resolve per-request pipelines by @uniqueg in #498
  • feat(plugins): record provenance by @uniqueg in #499
  • feat(utils): add a guarded HTTP client by @uniqueg in #500
  • feat(plugins): add typed backend lists by @uniqueg in #501
  • feat(plugins): add default failover router by @uniqueg in #504
  • feat(plugins): write gateway tags by @uniqueg in #505
  • feat(plugins): list plugins for clients by @uniqueg in #506
  • feat(plugins): add plugin test kit by @uniqueg in #507
  • feat(access-control): add opt-in roles by @uniqueg in #508
  • fix(auth): namespace subjects of several issuers by @uniqueg in #512
  • fix: keep local files out of example images by @uniqueg in #513
  • feat(auth): add per-issuer settings by @uniqueg in #514
  • build(deps): Bump semgrep from 1.181.0 to 1.182.1 in the semgrep group across 1 directory by @dependabot[bot] in #497
  • fix: close small pre-release review gaps by @uniqueg in #522
  • fix(plugins): set controller of listing operations by @uniqueg in #521
  • build(deps): Bump the github-actions group across 3 directories with 4 updates by @dependabot[bot] in #484
  • ci: approve library chart releases by @uniqueg in #520
  • fix(plugins): bind gateway settings to their app by @uniqueg in #525
  • fix(plugins): fail closed on client pipelines by @uniqueg in #526
  • fix(plugins): align Principal with access control by @uniqueg in #527
  • docs: mark client gateway tags as experimental by @uniqueg in #532
  • docs: complete the 0.18 migration guide by @uniqueg in #531
  • fix: allow opt-in roles without group mapping by @uniqueg in #533
  • docs: align the 0.18 guide on opt-in roles by @uniqueg in #535
  • chore: set citation metadata to 0.18.0 by @uniqueg in #536
  • test: fix flaky backend cache timing by @uniqueg in #538

Full Changelog: v0.17.0...v0.18.0