Skip to content

v0.22.0

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 09 Nov 07:57
· 8 commits to dev since this release
Immutable release. Only release title and notes can be modified.
v0.22.0
eb17893

FOCA 0.22.0 prepares apps for FOCA's ASGI release, which moves to Connexion 3 AsyncApp and removes Flask and Flask-Cors. Nothing is removed in 0.22.0: FOCA warns about what the ASGI release removes where it can, and adds replacements that already work on Connexion 2: request and claims accessors (foca.context), framework-neutral errors (foca.errors), a test client (foca.testing) and an ast-grep rule that finds Flask imports. The functions that take a Flask app (get_app_config(), set_app_config(), enable_cors() and foca.factories.celery_app.create_celery_app()) emit a DeprecationWarning, and FOCA logs each deprecation once per process; exception maps with raw Werkzeug classes are logged as deprecated and stop working with the ASGI release. Some defaults change, which clients, tests and log checks may notice: the detail of responses to invalid requests and the INFO logs no longer quote values of clients, and routes that are not operations of the specifications are treated differently (see Breaking changes). Move your app now: see Preparing for the ASGI release and the migration guide. When work on the ASGI release starts, a 0.22.x branch is cut from the latest 0.22 release, for dependency and security fixes while apps migrate.

Breaking changes

  • Deprecated, removed in the ASGI release: get_app_config(), set_app_config(), enable_cors() and foca.factories.celery_app.create_celery_app() emit a DeprecationWarning and are logged once per process; use foca.get_config() (in scripts, with Foca.use_config()), the security.cors configuration and Foca.create_celery_app() instead (#637, #731, #741, #742)
  • Also deprecated, removed in the ASGI release: reading, setting and deleting app.config.foca (#716, #721), www_authenticate of foca.errors.Unauthorized, whose challenges are now in headers (#710, #714), exception maps with raw Werkzeug classes instead of those of foca.errors (#633, #709), and x-body-name: permission in specifications that route operations to the controllers of the permissions API (#717, #722)
  • FOCA finds routes in its own route table of the operations of the specifications instead of Flask's url_map; for routes that aren't operations (e.g., the Swagger UI, openapi.json, routes added to Flask directly), is_permitted_for() is now False, me_path is no longer checked, and ownership controllers refuse requests; the log field operation_id is the specification's operationId, or the one Connexion resolves if the specification has none (see Routes that aren't operations) (#632, #707; #718, #726)
  • By default, the detail of responses to requests rejected by request validation names the invalid fields by location and code (e.g., Invalid fields: #/name (too-long)), or, if none are listed, is the mapped detail or The request is invalid., instead of the validator's reason; this also replaces the detail of BadRequestProblem (RequestValidationProblem) that apps raise, so raise foca.errors.InvalidFieldsProblem to name the invalid fields of checks of your own. Clients and tests that check detail should check errors instead, or set exceptions.validation_reasons: true to keep the reasons (see Validation reasons without request values) (#732, #733, #736, #738)
  • Logs at INFO: request logs (log_traffic) and denials of access control name the route (the path template of the operation, e.g., GET /ga4gh/tes/v1/tasks/{id}, or <unknown route>) instead of the path, and requests rejected by request validation without invalid fields are logged with the mapped detail or the title instead of the validator's reason; paths and reasons are logged at DEBUG, so log at DEBUG (e.g., for the logger foca) to keep them (see Validation reasons without request values) (#748, #750)

Features

  • Request and claims accessors that keep their meaning on ASGI: foca.context.current_request(), a read-only snapshot of the request, and current_token_info() and current_claims() of foca.security.auth; FOCA itself reads the request only through them (#634, #715, #720; #717, #722; #718, #726)
  • foca.errors: framework-neutral error classes and stable names of Connexion's problems, which FOCA raises everywhere; challenges of Unauthorized go in its headers (#633, #709; #710, #714)
  • foca.api.routes, a route table of the operations of the registered specifications, with get_routes() and match() (#632, #707)
  • foca.testing.Client, a test client with the API of httpx and Starlette's TestClient, foca.testing.request_context() for unit tests of code that reads the accessors, and the pytest fixtures foca_app and foca_client (#635, #729)
  • FOCA logs each of its deprecations once per process at WARNING, on the logger foca.deprecation, so that they show under Gunicorn, where Python hides DeprecationWarning; apps can use foca.deprecation.warn_deprecated() (#727, #728)
  • The ast-grep rule flask-import in FOCA's repository finds the imports of Flask, Werkzeug and Connexion's Flask modules that apps still have to move before the ASGI release, and names the replacements (#636, #730)
  • Responses to invalid requests list the invalid fields (up to 100), of the body and the parameters together, in the new member errors of their problem details, with the problem type Invalid request; title and status stay as before, and detail changes as described under Breaking changes. Raise foca.errors.InvalidFieldsProblem to list the invalid fields of checks of your own the same way (#586, #711, #712, #734)
  • Foca.use_config() lets scripts use a configuration and its collections (get_config(), get_collection()) without creating an app, without Flask and without writing to the database; it replaces set_app_config() for scripts (see Preparing for the ASGI release) (#741, #742)

Security

  • Responses to invalid requests no longer quote the rejected values (and, for parameters, the schema) in detail, which now names the invalid fields by location and code, e.g., Invalid fields: #/name (too-long); they stay listed in errors. Set the new option exceptions.validation_reasons: true to keep the validator's reasons, e.g., if clients depend on them. Names that clients choose are still named in detail: those of unknown query and form parameters and unknown keys of the body (#732, #733, #736, #738)
  • The plugin gateway's refusals of per-request pipelines and backends no longer quote the client's values; they name fields, settings and the positions of steps, e.g., Plugin not available in step 1 (see Plugin gateway messages) (#739, #740)
  • Error messages and logs quote fewer client values: GET /plugins/{plugin_id} names the parameter instead of the identifier, errors of the JSON parser on clients' pipelines get a fixed message unless exceptions.validation_reasons is on, an invalid user acted for in the auth.on_behalf_of_header is listed as an invalid field of the header, so that clients learn what to fix, and rejected users acted for, invalid identifiers and values of policy rules and the roles and subjects of refused role grants are no longer logged at INFO or above (#743, #745)
  • Request logs, denials of access control and rejections by request validation no longer log values of clients at INFO: request logs and denials name the route (the path template of the operation, e.g., GET /ga4gh/tes/v1/tasks/{id}) instead of the path, and rejections without invalid fields the title or mapped detail instead of the validator's reason; paths and reasons are logged at DEBUG; the INFO record Request rejected still names unknown parameters and body keys, whose names clients choose (see Migrating to 0.22.0) (#748, #750)
  • Errors for malformed MongoDB connection strings have a fixed message instead of PyMongo's, which could quote parts of the password (#701, #703)
  • The FOCA images install the Debian updates available when they are built, instead of upgrading single packages, and so get the fixes Debian has released, e.g., for zlib1g (CVE-2026-76844, CVE-2026-85091, CVE-2026-27171), which their base images still lack; images built on FOCA's image get the fixes, too (#747, #749)

Fixes

  • Apps are released when they are no longer used, also while objects of their APIs live on, e.g., in log records that a handler keeps; before, such objects kept the Flask app alive, which mattered for processes that build many apps, such as test suites (#735, #737)
  • The database configurations and MongoDB clients of apps with background jobs are released with their apps; before, Celery's worker signals kept them alive, which mattered for processes that create many apps, such as test suites or scripts (#744, #746)
  • foca check-config, sync-policies and export-spec no longer set up the plugin store of gateway apps without a db section (#705, #706)
  • Query strings that aren't UTF-8 no longer fail pagination links or the check for unexpected query parameters; such bytes stay percent-encoded (#717, #722; #718, #726)
  • Users acted for that can't be stored in policy rules (e.g., with a comma) are rejected as invalid fields of the auth.on_behalf_of_header with 400 Bad Request instead of failing with 500 Internal Server Error when the app makes them owners; grants of roles to groups that don't apply (group_mapping without keep_group_subjects) are warned about without the role, group and resource, which are logged at DEBUG; the image build also fails if the check for kept-back Debian updates fails itself; and the built-in access control and /me APIs have the version 1.1.0, as their Error schema gained code, errors and FieldError (#752, #755)

Documentation

  • New page Preparing for the ASGI release: what the ASGI release removes, what to use now, with examples, and how to find what is left (#637, #731)
  • New page Writing controllers: inputs, accessors, return values and the differences between Connexion 2 and 3 (#719, #723)
  • The migration guide covers every change that apps, clients and log checks may have to act on, with the deprecations, the new defaults of detail and the logs and the routes that aren't operations (#633, #709; #751, #754)
  • New page Invalid request, the problem type of responses to invalid requests, with their invalid fields (#586, #711)
  • The decisions not to coalesce concurrent fetches of user info and to keep check-config on the app are documented (#702, #704)

Maintenance

  • FOCA's route checks, security code and helpers no longer use Flask's url_map and request objects, but the route table and the accessors, so that they move to ASGI unchanged (#632, #707; #717, #722; #718, #726)
  • Unused test files and constants removed (#724, #725)
  • Dependency updates (#713)
  • Release preparation for 0.22.0: citation metadata, release notes and migration guide (#751, #754)

Commits

  • fix(db): don't quote URIs in MongoDB errors by @uniqueg in #703
  • docs: record userinfo, check-config decisions by @uniqueg in #704
  • fix(cli): don't set up plugin store in checks by @uniqueg in #706
  • refactor(api): check routes from specs by @uniqueg in #707
  • refactor(errors): make errors framework-neutral by @uniqueg in #709
  • feat: report invalid fields in 400 responses by @uniqueg in #711
  • build(deps): Bump ast-grep-cli from 0.51.0 to 0.51.1 in the python-dependencies group across 1 directory by @dependabot[bot] in #713
  • refactor(errors): move the challenge to headers by @uniqueg in #714
  • feat: add request and claims accessors by @uniqueg in #720
  • refactor(config): deprecate config.foca accessors by @uniqueg in #721
  • refactor(security): read requests via accessors by @uniqueg in #722
  • docs: add the page Writing controllers by @uniqueg in #723
  • test: remove unused test file and constants by @uniqueg in #725
  • refactor(utils): read the request via accessors by @uniqueg in #726
  • feat: log deprecations once per process by @uniqueg in #728
  • feat(testing): add an httpx-style test client by @uniqueg in #729
  • ci: flag Flask imports with ast-grep by @uniqueg in #730
  • docs: add "Preparing for the ASGI release" by @uniqueg in #731
  • fix: stop echoing values in validation errors by @uniqueg in #733
  • feat: list body and parameter errors together by @uniqueg in #734
  • fix: release apps after installing the route table by @uniqueg in #737
  • feat: don't quote rejected values by default by @uniqueg in #738
  • fix: quote no client values in gateway errors by @uniqueg in #740
  • feat: use a configuration without an app by @uniqueg in #742
  • fix: keep client values out of error messages by @uniqueg in #745
  • fix: release configs held by Celery signals by @uniqueg in #746
  • build: pick up OS security updates in images by @uniqueg in #749
  • fix: log route templates instead of paths by @uniqueg in #750
  • build: prepare the 0.22.0 release by @uniqueg in #754
  • fix: small fixes from the 0.22.0 review by @uniqueg in #755

Full Changelog: v0.21.0...v0.22.0