Skip to content

Commit 2384564

Browse files
authored
docs(product): add configuration example for Row-Level Security (#7004)
1 parent 9dfdec5 commit 2384564

File tree

1 file changed

+113
-3
lines changed
  • docs/docs-new/pages/product/apis-integrations/sql-api

1 file changed

+113
-3
lines changed

docs/docs-new/pages/product/apis-integrations/sql-api/security.mdx

Lines changed: 113 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,29 +38,139 @@ additional security.
3838

3939
Cube's SQL API can also use the Security Context for [Dynamic data model
4040
creation][ref-dynamic-schemas] or [`queryRewrite`][ref-config-queryrewrite]
41-
property in your [`cube.js` configuration file][ref-config-js].
41+
property in your `cube.js` configuration file.
4242

4343
By default, the SQL API uses the current user's Security Context, but this
4444
behaviour can be modified so that certain users are allowed to switch. To do
4545
this, we must first define which user is allowed to change Security Context:
4646

47+
### Example
48+
4749
First, you need to define what user is allowed to change security context:
4850

4951
```dotenv
5052
CUBEJS_SQL_SUPER_USER=admin
5153
```
5254

55+
Then configure the [`contextToAppId()`][ref-config-ctx-to-app-id],
56+
[`queryRewrite()`][ref-config-queryrewrite] and
57+
[`checkSqlAuth()`][ref-config-check-sql-auth] properties in your `cube.js`
58+
configuration file:
59+
60+
```javascript filename="cube.js"
61+
module.exports = {
62+
// Create a new appId for each team, this prevents teams from seeing each
63+
// other's data
64+
// https://cube.dev/docs/reference/configuration/config#contexttoappid
65+
contextToAppId: ({ securityContext }) => {
66+
return securityContext.team;
67+
},
68+
69+
// Enforce a default value for `team` if one is not provided
70+
// in the security context
71+
// https://cube.dev/docs/reference/configuration/config#extendcontext
72+
extendContext: ({ securityContext }) => {
73+
if (!securityContext.team) {
74+
securityContext.team = "public";
75+
}
76+
77+
return {
78+
securityContext,
79+
};
80+
},
81+
82+
// Here we create a new security context for each team so that we can
83+
// use it in our data model later
84+
checkSqlAuth: (query, username) => {
85+
const securityContext = {
86+
team: username,
87+
};
88+
89+
return {
90+
password: process.env.CUBEJS_SQL_PASSWORD,
91+
securityContext: securityContext,
92+
};
93+
},
94+
};
95+
```
96+
97+
Now, you can use the `securityContext` in your data model:
98+
99+
```twig filename="model/cubes/users.yml.jinja"
100+
{# Is the current team trusted? #}
101+
{% set trusted_teams = ['cx', 'exec' ] %}
102+
{% set is_trusted_team = COMPILE_CONTEXT.securityContext.team in trusted_teams %}
103+
104+
{# Convenient function to mask values if the current team is not trusted #}
105+
{% macro masked(sql, is_visible) -%}
106+
{{ sql if is_visible else "\"'--- masked ---'\"" }}
107+
{%- endmacro %}
108+
109+
cubes:
110+
- name: users
111+
sql_table: users
112+
public: false
113+
114+
dimensions:
115+
116+
{# This property will be masked unless the requesting user is part of a trusted team #}
117+
- name: first_name
118+
sql: {{ masked('first_name', is_trusted_team) }}
119+
type: string
120+
121+
{# This property will be masked unless the requesting user is part of a trusted team #}
122+
- name: last_name
123+
sql: {{ masked('last_name', is_trusted_team) }}
124+
type: string
125+
126+
- name: state
127+
sql: state
128+
type: string
129+
130+
- name: city
131+
sql: city
132+
type: string
133+
134+
- name: created_at
135+
sql: created_at
136+
type: time
137+
138+
measures:
139+
- name: count
140+
type: count
141+
142+
```
143+
144+
With the above now configured, we can query Cube using SQL with a user that is
145+
part of a trusted team:
146+
147+
```sql
148+
SELECT users_city, users_first_name, users_last_name
149+
FROM users
150+
WHERE __user = 'cx'
151+
```
152+
153+
This pairs well with other security functionality in tools like Preset, which
154+
allows configuring [row-level security to allow
155+
access][preset-docs-rls-inclusive] to data based on the current user's security
156+
context.
157+
53158
If it's not enough for your case, you define your logic for check with
54-
`canSwitchSqlUser` property in your [`cube.js` configuration
55-
file][ref-config-js].
159+
[`canSwitchSqlUser` property][ref-config-can-switch-sql-user] in your `cube.js`
160+
configuration file.
56161

57162
You can change security context for specific query via virtual filter on:
58163

59164
```sql
60165
SELECT * FROM orders WHERE __user = 'anotheruser';
61166
```
62167

168+
[preset-docs-rls-inclusive]:
169+
https://docs.preset.io/docs/row-level-security-rls#define-inclusive-regular-rls-filter
170+
[ref-config-can-switch-sql-user]:
171+
/reference/configuration/config#canswitchsqluser
63172
[ref-config-check-sql-auth]: /reference/configuration/config#checksqlauth
173+
[ref-config-ctx-to-app-id]: /reference/configuration/config#contexttoappid
64174
[ref-config-queryrewrite]: /reference/configuration/config#queryrewrite
65175
[ref-dynamic-schemas]: /schema/dynamic-schema-creation
66176
[ref-config-js]: /reference/configuration/config

0 commit comments

Comments
 (0)