diff --git a/docs-mintlify/docs.json b/docs-mintlify/docs.json
index ab131e9bacec3..7348d54411761 100644
--- a/docs-mintlify/docs.json
+++ b/docs-mintlify/docs.json
@@ -648,6 +648,12 @@
"recipes/configuration/custom-data-model-per-tenant"
]
},
+ {
+ "group": "Access Control",
+ "pages": [
+ "recipes/access-control/conditional-row-level-access"
+ ]
+ },
{
"group": "Core Data API",
"pages": [
diff --git a/docs-mintlify/docs/data-modeling/data-access-policies.mdx b/docs-mintlify/docs/data-modeling/data-access-policies.mdx
index cf65f076bfa3d..9cd3816bc63f1 100644
--- a/docs-mintlify/docs/data-modeling/data-access-policies.mdx
+++ b/docs-mintlify/docs/data-modeling/data-access-policies.mdx
@@ -492,6 +492,14 @@ view(`orders_view`, {
+### Conditional row-level filters
+
+You can switch which `row_level` filter applies based on the
+[security context][ref-sec-ctx] — for example, to scope a group to its region
+or to let admins bypass row filters with `row_level.allow_all: true`. See the
+[conditional row-level access recipe][ref-recipe-conditional-row-level] for
+worked examples in YAML and JavaScript.
+
### Mandatory filters
You can apply mandatory row-level filters to specific groups to ensure they only see data matching certain criteria:
@@ -621,4 +629,5 @@ cube(`orders`, {
[ref-ref-dap-role]: /reference/data-modeling/data-access-policies#role
[ref-ref-dap-masking]: /reference/data-modeling/data-access-policies#member-masking
[ref-ref-mask-dim]: /reference/data-modeling/dimensions#mask
-[ref-core-data-apis]: /reference/core-data-apis
\ No newline at end of file
+[ref-core-data-apis]: /reference/core-data-apis
+[ref-recipe-conditional-row-level]: /recipes/access-control/conditional-row-level-access
\ No newline at end of file
diff --git a/docs-mintlify/recipes/access-control/conditional-row-level-access.mdx b/docs-mintlify/recipes/access-control/conditional-row-level-access.mdx
new file mode 100644
index 0000000000000..9c51d741bcf70
--- /dev/null
+++ b/docs-mintlify/recipes/access-control/conditional-row-level-access.mdx
@@ -0,0 +1,280 @@
+---
+title: Conditional row-level access
+description: Switch row-level filters based on the security context, and let an admin policy grant unrestricted row access on top of more restrictive policies.
+---
+
+## Use case
+
+You want different row-level filters to apply to the same group depending on
+who the user is. For example:
+
+- Users in different regional groups should see only rows for their region.
+- Admins should bypass row filters entirely, even when other policies restrict
+the same role.
+
+You can express both patterns with [`access_policy`][ref-ref-dap] by combining
+[`conditions`][ref-ref-dap-conditions], [`row_level.allow_all`][ref-ref-dap-row-level],
+and the [_OR_ semantics across policies][ref-dap-rls] that match the same group.
+
+## Data modeling
+
+### Region-based switching
+
+Define one policy per region, and gate each policy with a `conditions` entry
+that checks the user's [security context][ref-sec-ctx]. Only the policy whose
+condition evaluates to `true` contributes its `row_level` filter to the query —
+the others are skipped.
+
+In the following example, users in the `analyst` group see rows for their
+region: members of the `emea` group are restricted to EMEA orders, and members
+of the `amer` group are restricted to AMER orders.
+
+
+
+```yaml title="YAML"
+cubes:
+ - name: orders
+ # ...
+
+ access_policy:
+ - group: analyst
+ conditions:
+ - if: "{ security_context.groups and security_context.groups.includes('emea') }"
+ row_level:
+ filters:
+ - member: region
+ operator: equals
+ values: ["EMEA"]
+
+ - group: analyst
+ conditions:
+ - if: "{ security_context.groups and security_context.groups.includes('amer') }"
+ row_level:
+ filters:
+ - member: region
+ operator: equals
+ values: ["AMER"]
+```
+
+```javascript title="JavaScript"
+cube(`orders`, {
+ // ...
+
+ access_policy: [
+ {
+ group: `analyst`,
+ conditions: [
+ { if: securityContext.groups && securityContext.groups.includes(`emea`) }
+ ],
+ row_level: {
+ filters: [
+ {
+ member: `region`,
+ operator: `equals`,
+ values: [`EMEA`]
+ }
+ ]
+ }
+ },
+ {
+ group: `analyst`,
+ conditions: [
+ { if: securityContext.groups && securityContext.groups.includes(`amer`) }
+ ],
+ row_level: {
+ filters: [
+ {
+ member: `region`,
+ operator: `equals`,
+ values: [`AMER`]
+ }
+ ]
+ }
+ }
+ ]
+})
+```
+
+
+
+In JavaScript, you can collapse the two policies into one by computing the
+filter `values` from `securityContext` at evaluation time — the `values` array
+is evaluated per request, so a ternary can pick the region for the caller:
+
+```javascript title="JavaScript"
+cube(`orders`, {
+ // ...
+
+ access_policy: [
+ {
+ group: `analyst`,
+ row_level: {
+ filters: [
+ {
+ member: `region`,
+ operator: `equals`,
+ values: [
+ securityContext.groups && securityContext.groups.includes(`emea`)
+ ? `EMEA`
+ : `AMER`
+ ]
+ }
+ ]
+ }
+ }
+ ]
+})
+```
+
+
+This single-policy form is JavaScript-only. YAML `{ ... }` expressions don't
+support inline conditionals (Python `if`/`else`) or comparison operators, so in
+YAML use one policy per region as shown above. Note that `row_level` itself is
+always a static object — only the `member` and `values` inside a filter are
+evaluated against `securityContext`; you can't replace `row_level` with a
+function.
+
+
+### Admin override with `allow_all`
+
+To let admins bypass row-level filters that apply to a role, add a second
+policy for the same group that grants [`row_level.allow_all`][ref-ref-dap-row-level]
+when `securityContext.is_admin` is true. Because policies that match the same
+group are combined with _OR_ semantics, the admin policy unlocks every row
+regardless of the more restrictive analyst policy:
+
+
+
+```yaml title="YAML"
+cubes:
+ - name: orders
+ # ...
+
+ access_policy:
+ # Region-restricted access for regular analysts
+ - group: analyst
+ row_level:
+ filters:
+ - member: region
+ operator: equals
+ values: ["{ security_context.region }"]
+
+ # Admin override: full row access when the user is an admin
+ - group: analyst
+ conditions:
+ - if: "{ security_context.is_admin }"
+ row_level:
+ allow_all: true
+```
+
+```javascript title="JavaScript"
+cube(`orders`, {
+ // ...
+
+ access_policy: [
+ {
+ // Region-restricted access for regular analysts
+ group: `analyst`,
+ row_level: {
+ filters: [
+ {
+ member: `region`,
+ operator: `equals`,
+ values: [securityContext.region]
+ }
+ ]
+ }
+ },
+ {
+ // Admin override: full row access when the user is an admin
+ group: `analyst`,
+ conditions: [
+ { if: securityContext.is_admin }
+ ],
+ row_level: {
+ allow_all: true
+ }
+ }
+ ]
+})
+```
+
+
+
+### Composing boolean logic with `conditions`
+
+`conditions` accept boolean logic, so you can switch which `row_level`
+applies based on combined checks against the security context and user
+attributes. In YAML, use `and`, `or`, `not`, and parentheses inside
+`{ ... }`. In JavaScript, use `&&`, `||`, and `!`. Multiple `conditions`
+entries on a single policy are combined with _AND_ semantics; multiple
+matching policies are combined with _OR_ semantics.
+
+In the following example, full-time senior analysts who are _either_ admins
+_or_ owners and are _not_ contractors get unrestricted row access; everyone
+else in the `analyst` group falls back to the region-restricted policy above.
+
+
+
+```yaml title="YAML"
+cubes:
+ - name: orders
+ # ...
+
+ access_policy:
+ - group: analyst
+ conditions:
+ - if: "{ user_attributes.is_full_time_employee and user_attributes.is_senior }"
+ - if: "{ user_attributes.is_admin or user_attributes.is_owner }"
+ - if: "{ not (security_context.groups and security_context.groups.includes('contractors')) }"
+ row_level:
+ allow_all: true
+```
+
+```javascript title="JavaScript"
+cube(`orders`, {
+ // ...
+
+ access_policy: [
+ {
+ group: `analyst`,
+ conditions: [
+ { if: userAttributes.is_full_time_employee && userAttributes.is_senior },
+ { if: userAttributes.is_admin || userAttributes.is_owner },
+ { if: !(securityContext.groups && securityContext.groups.includes(`contractors`)) }
+ ],
+ row_level: {
+ allow_all: true
+ }
+ }
+ ]
+})
+```
+
+
+
+
+YAML `{ ... }` expressions are parsed as Python and support `and`, `or`, `not`,
+parentheses, member access, and method calls like `.includes(...)`. They do
+**not** support comparison operators (`==`, `>=`, `in`) or inline `if`/`else`
+conditionals — use boolean [user attributes][ref-sec-ctx] or method calls
+instead. The JavaScript form has no such restriction.
+
+
+## Result
+
+With these policies in place:
+
+- Regional analysts see only rows for the region attached to their security
+context, because only the policy whose `conditions` match contributes its
+`row_level` filter.
+- Admins see all rows, because the admin policy's `row_level.allow_all: true`
+combines with the regional policy via _OR_ semantics.
+- Users without any matching policy are denied access by default.
+
+
+[ref-ref-dap]: /reference/data-modeling/data-access-policies
+[ref-ref-dap-conditions]: /reference/data-modeling/data-access-policies#conditions
+[ref-ref-dap-row-level]: /reference/data-modeling/data-access-policies#row-level
+[ref-dap-rls]: /docs/data-modeling/data-access-policies#row-level-access
+[ref-sec-ctx]: /docs/data-modeling/access-control/context
diff --git a/docs-mintlify/reference/data-modeling/data-access-policies.mdx b/docs-mintlify/reference/data-modeling/data-access-policies.mdx
index 5698a33e556a5..bc4b9130dc421 100644
--- a/docs-mintlify/reference/data-modeling/data-access-policies.mdx
+++ b/docs-mintlify/reference/data-modeling/data-access-policies.mdx
@@ -208,6 +208,65 @@ cube(`orders`, {
+#### Boolean logic in `if` expressions
+
+`if` expressions support boolean logic so you can compose checks against
+the [security context][ref-sec-ctx] and user attributes:
+
+- In YAML, expressions inside `{ ... }` are parsed as Python and support the
+`and`, `or`, and `not` operators, parentheses for grouping, member access, and
+method calls such as `.includes(...)`. Comparison operators (`==`, `>=`, `in`)
+and inline `if`/`else` conditionals are **not** supported — use boolean user
+attributes or method calls instead.
+- In JavaScript, expressions use native operators: `&&`, `||`, `!`, and any
+valid JavaScript expression.
+- Multiple `conditions` entries on a single policy are combined with _AND_
+semantics — every entry must evaluate to `true` for the policy to take effect.
+- Multiple policies that match the same group are combined with _OR_ semantics —
+if any matching policy grants access, the user gets access.
+
+In the following example, a single policy combines three `conditions` entries
+that each use a different boolean operator:
+
+
+
+```yaml title="YAML"
+cubes:
+ - name: orders
+ # ...
+
+ access_policy:
+ - group: manager
+ conditions:
+ - if: "{ user_attributes.is_full_time_employee and user_attributes.is_senior }"
+ - if: "{ user_attributes.is_admin or user_attributes.is_owner }"
+ - if: "{ not (security_context.groups and security_context.groups.includes('contractors')) }"
+ member_level:
+ includes: "*"
+```
+
+```javascript title="JavaScript"
+cube(`orders`, {
+ // ...
+
+ access_policy: [
+ {
+ group: `manager`,
+ conditions: [
+ { if: userAttributes.is_full_time_employee && userAttributes.is_senior },
+ { if: userAttributes.is_admin || userAttributes.is_owner },
+ { if: !(securityContext.groups && securityContext.groups.includes(`contractors`)) }
+ ],
+ member_level: {
+ includes: `*`
+ }
+ }
+ ]
+})
+```
+
+
+
### `member_level`
The optional `member_level` parameter, when present, configures [member-level
@@ -452,6 +511,63 @@ REST (JSON) API][ref-rest-query-filters] queries, allowing to use the same set o
You can also use `and` and `or` parameters to combine multiple filters into
[boolean logical operators][ref-rest-boolean-ops].
+#### `allow_all`
+
+Use `allow_all: true` to make the row-level intent of a policy explicit without
+listing any filters. It grants the policy access to all rows and is equivalent
+to omitting `row_level` from the policy — useful when you want the policy to
+clearly state that no row-level restriction applies, or as an admin override
+that combines with more restrictive policies via [_OR_ semantics][ref-dap-rls].
+
+A `row_level` block must contain either `filters` or `allow_all` — the two are
+mutually exclusive. `allow_all` only accepts `true`; `allow_all: false` is not a
+valid value. To deny a group all rows, don't grant it a matching policy: access
+policies are deny-by-default, so any member not granted by a matching policy is
+inaccessible.
+
+
+
+```yaml title="YAML"
+cubes:
+ - name: orders
+ # ...
+
+ access_policy:
+ - group: admin
+ row_level:
+ allow_all: true
+```
+
+```javascript title="JavaScript"
+cube(`orders`, {
+ // ...
+
+ access_policy: [
+ {
+ group: `admin`,
+ row_level: {
+ allow_all: true
+ }
+ }
+ ]
+})
+```
+
+
+
+#### Conditional `row_level`
+
+You can make `row_level` conditional so that different filters apply depending
+on the [security context][ref-sec-ctx] or user attributes. Attach
+[`conditions`](#conditions) to a policy so its `row_level` is skipped when the
+conditions don't match, and define separate policies for the different filters
+you want to apply. In JavaScript, you can additionally compute a filter's
+`member` or `values` from `securityContext` at evaluation time (for example,
+with a ternary), which lets a single policy vary its filter per request. When a
+policy's `row_level` is skipped, other matching policies still apply; if no
+policy matches, access is denied by default. See the [conditional row-level
+access recipe][ref-recipe-conditional-row-level] for worked examples.
+
Note that access policies also respect [row-level security][ref-rls] restrictions
configured via the `query_rewrite` configuration option. See [row-level access][ref-dap-rls] to
learn more about policy evaluation.
@@ -513,4 +629,5 @@ cube(`orders`, {
[ref-mask-dim]: /reference/data-modeling/dimensions#mask
[ref-rest-query-filters]: /reference/rest-api/query-format#filters-format
[ref-rest-query-ops]: /reference/rest-api/query-format#filters-operators
-[ref-rest-boolean-ops]: /reference/rest-api/query-format#boolean-logical-operators
\ No newline at end of file
+[ref-rest-boolean-ops]: /reference/rest-api/query-format#boolean-logical-operators
+[ref-recipe-conditional-row-level]: /recipes/access-control/conditional-row-level-access
\ No newline at end of file