Row-level security

Row-level security (RLS) lets you ensure that the Mumbai sales rep sees only Mumbai orders, the Bangalore rep sees only Bangalore orders, and the CFO sees everything — all from one dataset, one report, and zero duplication.

Declaring a rule

Rules live under security: in the semantic model YAML:

security:
  - name: region_scoped
    applies_to: { role: ["sales_rep"] }
    predicate: "region = {{ user.attributes.region }}"
  - name: cfo_all
    applies_to: { role: ["cfo", "owner"] }
    predicate: "1=1"

Each rule has:

  • applies_to — a set of roles, teams, or specific user IDs
  • predicate — a SQL fragment evaluated with user.* attributes interpolated

At query compile time, matching rules are AND-ed into the WHERE clause of every query that touches the dataset. Users see the filtered data; the sql_preview shows them the effective predicate.

Attribute source

user.attributes comes from one of:

  1. SCIM — pushed from your IdP, see SCIM provisioning.
  2. SAML assertions — attributes in the SAML response, mapped in Settings → SSO.
  3. Manual — Admin sets them per user in the UI (useful for small teams without IdP).

Testing a rule

Click Preview as user in the Model editor, type an email, and OneAnalytics shows you the exact SQL and the resulting rows. A log entry is written to the audit log.

Failure mode

If a user matches zero rules, they get zero rows (default-deny). There is no "no RLS" fallback. To grant unrestricted access, add an explicit 1=1 rule scoped to the right roles, as in cfo_all above.

Sharing grants vs. RLS

RLS filters rows inside a dataset; a sharing grant decides whether the user can open the report at all. A user needs both — a grant and matching RLS — to see any data.

Performance

Predicates are applied as regular WHERE clauses. Indexes on filtered columns (e.g., region) matter. For imported datasets, our columnar engine handles cardinality up to a few thousand distinct values per predicate without trouble.