jrgemignani opened a new pull request, #2561: URL: https://github.com/apache/age/pull/2561
The global graph cache that backs variable-length edge (VLE) and shortest-path traversal is populated with a direct heap scan (table_beginscan + heap_getnext). That scan does not go through the planner or executor, so the row-level security policies and table privileges that MATCH receives automatically are not applied to traversal: it reads every vertex and edge regardless of the current role's policies or grants. Add age.enforce_rls_in_traversal (bool, PGC_SUSET, default on). When on: * SELECT privilege is checked on every label table before it is read (pg_class_aclcheck, with a pg_attribute_aclcheck fallback so column-level grants are honored the same way the executor honors them). * When RLS is active on a label for the current role, that label is loaded through SPI (SELECT ... FROM ONLY <label>) with a batched read-only cursor so the planner applies the policies. Otherwise the direct-scan path is used, now behind the privilege check. Properties are still fetched lazily by TID: only policy-visible rows are loaded, so the later heap_fetch reads only authorized tuples, which covers shortest_path as well. When off, the previous direct-scan behavior is restored, leaving operators a single escape hatch. To match MATCH's inner-join behavior, the edge loaders drop any edge whose start or end vertex was filtered out by a vertex policy rather than leaving a dangling edge in the cache. Cache validity: * A cache that applied RLS is not reused across statements (loaded_with_rls), because policies can depend on session state such as current_setting() that is not covered by the version, snapshot, role, and GUC cache keys. Graphs that do not use RLS are unaffected. * is_ggctx_invalid checks the role and GUC keys before the graph version-counter fast path, so a role or GUC change forces a rebuild even in DSM/SHMEM mode. * The version counter tracks only graph data changes, so an enforced non-RLS cache could otherwise survive a GRANT/REVOKE or policy/RLS DDL on a label table and serve stale permissions. A relcache callback keyed by label-table OID (table/column privileges, policies, RLS flags), plus pg_authid and pg_auth_members syscache callbacks (BYPASSRLS, role membership), now mark such caches stale so they are rebuilt and re-authorized. Re-entrancy and cleanup: * The context is built detached and attached only after a successful load, so a policy expression that re-enters traversal cannot observe a half-built cache, and a failed load frees the partial context instead of leaving it in TopMemoryContext. * On attach, an existing context for the same graph is reused only if it is still valid for the current role and GUC, so a re-entrant traversal reached through a SECURITY DEFINER policy cannot be handed a context loaded as another role. If discarding an incompatible context reports a missing entry, that error is raised after first freeing the freshly built context. * Each loader frees the transient label-name list from get_ag_labels_names() on both the normal and error paths; it is allocated in TopMemoryContext and would otherwise accumulate on every rebuild. Adds regression test rls_vle covering policy and privilege enforcement, the opt-out GUC, per-role cache invalidation, a session current_setting() policy, and privilege-metadata invalidation. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
