Draft: feat(compiler): filter owner-scoped entities to the rows of the caller

What does this MR do and why?

Some GitLab data belongs to one user only. A to-do item is the first example: GitLab shows it to its owner and to nobody else, not even an administrator. Orbit could not express that rule. Without it, anyone who can see a project would also see other people's to-do items on it. This MR lets the ontology name an owner column, and Orbit then returns only the caller's own rows.

How it works

A node declares its owner column in its redaction block:

redaction:
  resource_type: todo
  ability: read_todo
  owner_column: user_id

The compiler then adds user_id = <caller> to every read of that node (security pass). The filter also keeps deleted rows deleted. A token without a user id reads nothing.

Edges need the same rule, or a neighbor query could still reach someone else's to-do item. So an owner-scoped node writes its edges to its own edge table, and that table has an owner column too. The indexer fills it from the node that writes the edge. If an owner-scoped node writes edges anywhere else, the ontology fails to load. That keeps the rule fail-closed.

Orbit question, as a Reporter Before After
How many to-do items are on this project? everyone's only mine
Which to-do items point at this MR? everyone's only mine

Administrators follow the same rule by default. A node can set owner_bypass: admin when GitLab lets admins read the data.

This MR also hardens the security pass: it now finds every table through the query data model, and an unknown table fails the query instead of slipping through (lookup).

No shipped node uses owner_column yet. The Todo MR, stacked on !3196 (closed), is the first.

Related to #1379. Stacked on !3230 (closed).

Testing

  • Five scenarios cover the owner, another user, an admin with and without the bypass, a deleted row, and an edge row with no owner.
  • Compiler tests check the filter for each caller type, and that an unknown table fails.
  • The MR pipeline is green.

Performance Analysis

On production, the owner filter reads the same rows as the read without it: a pinned edge read stays at 3 marks and 5 MiB. Details are in the measurements snippet.

  • This merge request does not introduce any performance regression.
Agent context: file walk

The file walk is in engine-stack-notes.md of the measurements snippet.

Gates: the kept comments are the two-line module doc at the top of security.rs and the clap help for --user-id in the profiler. The seed rows repeat across the five owner-scope scenario files because each file is self-contained. A four-line direction match in the neighbors planner and in lowering predates this MR. The diff is net positive: scenarios, unit tests, and the security code.

Edited by Michael Angelo Rivera

Merge request reports

Loading
Loading