Skip to content

go: reimplement schema-match headers as an authorizer #1127

Description

@derekperkins

Summary

Reimplement --schema-match-headers as an opt-in authorizer built on #1126, then remove schema-specific state and branching from the generic server handler.

This is the second authorization step. It must preserve the current CLI and wire behavior while proving parity across HTTP and WebSockets before the old path is removed.

Design goals

  • Provide one small reusable constructor/helper for the schema-header policy; keep header parsing and DuckDB AST implementation details private.
  • Resolve configured trusted headers at request/session creation, then authorize the exact SQL of every command.
  • Treat header values as a union of allowed schema names, matching current documented behavior.
  • Compose independently with function restrictions and other authorizers.
  • Remove schemaMatchHeaders, getAllowedSchemas, positional schema configuration, and schema-specific branches from the generic server only after parity tests pass.
  • Remove schema-specific allowedSchemas []string plumbing from the general public query API when the new policy boundary makes that safe.

Required behavior

  • Trim and validate configured header names, ignore empty configured entries, and deduplicate names and resolved schema values.
  • If schema authorization is configured but no non-empty trusted header value resolves, return 401 before WebSocket upgrade or query execution.
  • For JSON/Arrow commands, validate the complete supported statement batch before cache lookup or execution using the existing fail-closed SQL validator.
  • Unauthorized schemas, catalogs, or unqualified table references return 403.
  • Parser failures and unsupported statements under the policy return 400 and never become authorization success.
  • Restricted exec remains disabled, including SELECT, DDL, DML, COPY, ATTACH, and metadata statements, with no side effects.
  • Function blocklist policy continues to apply independently and composes with schema authorization.
  • HTTP and WebSocket decisions are equivalent, and every WebSocket message is checked.
  • Do not represent a denied/missing policy as an empty schema slice: the current query code interprets an empty slice as unrestricted.

Security constraints

Do not weaken the fail-closed behavior delivered for #958. The existing JSON AST validator remains authoritative for supported SELECT, narrow SHOW, and DESCRIBE forms.

In particular:

  • Do not relax restricted exec based on parser_tools while go: replace custom table parser #947 remains unresolved; its current API does not provide complete typed references for DDL, DML, COPY, ATTACH, metadata, catalogs, functions, or mixed batches.
  • Do not use regular expressions over normalized SQL as an authorization boundary.
  • Keep go: add function allowlist #959 function-policy work independent from tenant-schema identity.
  • Document that upstream middleware/gateways must strip client-supplied copies of trusted headers and inject only verified values. The headers are authorization claims, not authentication.
  • Retain the documented single-catalog and process/filesystem/network/credential limitations.

Acceptance criteria

  • Existing --schema-match-headers invocations require no CLI changes.
  • Regression tests cover allowed and denied schemas, catalogs, unqualified tables, missing headers, multiple headers, whitespace/duplicates, parser failures, unsupported statements, cache hits, and function-policy composition.
  • Side-effect tests prove restricted SELECT/DDL/DML/COPY/ATTACH/metadata exec commands remain denied.
  • The same allow/deny matrix passes over POST, GET, and multiple messages on one WebSocket.
  • No schema-specific state or branch remains in the generic server after the adapter is enabled.
  • No raw AST node maps or stateful validator objects are added to the supported public API.
  • README examples and limitations describe the adapter as one optional example policy, not a Mosaic standard or complete sandbox.
  • Normal, race, and lint checks pass from the nested Go module with duckdb_arrow.

Related work

Sequencing

Depends on #1126. It can be developed while the query API and extension tracks proceed, but should merge only after the generic authorizer contract is available and its HTTP/WebSocket tests pass.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions