Lotus.Preflight (Lotus v1.1.0)

Copy Markdown View Source

Statement preflight authorization for Lotus.

Delegates to the adapter's extract_accessed_resources/2 callback to discover which tables/relations a statement will access, then checks those relations against visibility rules before execution.

This provides defense-in-depth by blocking queries that would access denied tables, even if they're accessed through views or complex subqueries.

When an adapter returns {:unrestricted, reason} the statement is allowed through only if the host application has opted in via config :lotus, :allow_unrestricted_resources; otherwise preflight returns an error. The opted-in case records {:unrestricted, reason} rather than a relation list, so a later consumer can tell "touches no table" apart from "this adapter cannot name its tables".

Summary

Functions

Authorizes a statement and returns what preflight learned about it.

Authorizes a statement by checking all relations it would access.

Functions

analyze(adapter, statement, search_path \\ nil, scope \\ nil)

@spec analyze(
  Lotus.Source.Adapter.t(),
  Lotus.Query.Statement.t(),
  String.t() | nil,
  term()
) ::
  {:ok, Lotus.Preflight.Relations.outcome()} | {:error, String.t()}

Authorizes a statement and returns what preflight learned about it.

The same check as authorize/4, returning the outcome as a value: the list of {schema, table} relations the statement touches — empty when it touches none — or {:unrestricted, reason} when the adapter cannot name them and the host opted in. Lotus.Runner carries this value down the pipeline and into the :before_execute and :after_query payloads.

authorize(adapter, statement, search_path \\ nil, scope \\ nil)

@spec authorize(
  Lotus.Source.Adapter.t(),
  Lotus.Query.Statement.t(),
  String.t() | nil,
  term()
) ::
  :ok | {:error, String.t()}

Authorizes a statement by checking all relations it would access.

Delegates resource extraction to the adapter, then validates each relation against the visibility rules.

scope is the opaque caller-supplied value handed to the visibility resolver, the same one Lotus.list_tables/2 and friends accept. Pass it and a per-scope deny blocks execution; omit it and only the unscoped rules apply. Without it a resolver that hides a table from one tenant would hide it in the explorer while the query still returned its rows.