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 IDspredicate— a SQL fragment evaluated withuser.*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:
- SCIM — pushed from your IdP, see SCIM provisioning.
- SAML assertions — attributes in the SAML response, mapped in Settings → SSO.
- 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.