From e72b5476c621c88546f23827c1e8708e5936392b Mon Sep 17 00:00:00 2001 From: Igor Lukanin Date: Wed, 29 Jan 2025 14:10:02 +0100 Subject: [PATCH 1/3] Rebase changes --- .../custom-data-model-per-tenant.mdx | 14 +- docs/pages/guides/style-guide.mdx | 3 + docs/pages/product/_meta.js | 2 +- docs/pages/product/auth.mdx | 7 +- docs/pages/product/auth/_meta.js | 5 +- .../product/auth/data-access-policies.mdx | 49 +++ .../member-level-security.mdx} | 4 +- .../pages/product/auth/row-level-security.mdx | 3 + docs/pages/product/configuration.mdx | 5 +- docs/pages/product/data-modeling/concepts.mdx | 148 +++++--- .../product/data-modeling/concepts/_meta.js | 1 - .../product/data-modeling/dynamic/jinja.mdx | 4 + docs/pages/reference/configuration/config.mdx | 4 + docs/pages/reference/data-model/_meta.js | 1 + docs/pages/reference/data-model/cube.mdx | 339 ++++++++++-------- .../data-model/data-access-policies.mdx | 174 +++++++++ .../pages/reference/data-model/dimensions.mdx | 7 +- .../reference/data-model/hierarchies.mdx | 5 +- docs/pages/reference/data-model/joins.mdx | 9 +- docs/pages/reference/data-model/measures.mdx | 7 +- .../reference/data-model/pre-aggregations.mdx | 4 +- docs/pages/reference/data-model/segments.mdx | 8 +- docs/pages/reference/data-model/view.mdx | 7 +- docs/redirects.json | 5 + 24 files changed, 581 insertions(+), 234 deletions(-) create mode 100644 docs/pages/product/auth/data-access-policies.mdx rename docs/pages/product/{data-modeling/concepts/publicity.mdx => auth/member-level-security.mdx} (98%) create mode 100644 docs/pages/product/auth/row-level-security.mdx create mode 100644 docs/pages/reference/data-model/data-access-policies.mdx diff --git a/docs/pages/guides/recipes/multitenancy/custom-data-model-per-tenant.mdx b/docs/pages/guides/recipes/multitenancy/custom-data-model-per-tenant.mdx index 6c0745196eddf..e9120696c2737 100644 --- a/docs/pages/guides/recipes/multitenancy/custom-data-model-per-tenant.mdx +++ b/docs/pages/guides/recipes/multitenancy/custom-data-model-per-tenant.mdx @@ -67,10 +67,10 @@ module.exports = { ## Data modeling -### Customizing publicity +### Customizing member-level access -The simplest way to customize the data models is by changing the [publicity][ref-publicity] -of data model entities. It works great for use cases when tenants share parts of +The simplest way to customize the data models is by changing the [member-level access][ref-mls] +to data model entities. It works great for use cases when tenants share parts of their data models. By setting the `public` parameter of [cubes][ref-cubes-public], [views][ref-views-public], @@ -168,7 +168,7 @@ cube(`cube_x`, { -For your convenience, [Playground][ref-playground] ignores publicity configration +For your convenience, [Playground][ref-playground] ignores member-level access configration and marks data model entities that are not accessible for querying through [APIs][ref-apis] with the lock icon. @@ -182,8 +182,8 @@ And here's the *perspective* of `Bob`: ### Customizing other parameters -Similarly to [customizing publicity](#customizing-publicity), you can set other -parameters of data model entities for each tenant individually: +Similarly to [customizing member-level access](#customizing-member-level-access), +you can set other parameters of data model entities for each tenant individually: - By setting `sql` or [`sql_table` parameters][ref-cube-sql-table] of cubes, you can ensure that each tenant accesses data from its own tables or database schemas. @@ -364,7 +364,7 @@ code that fetches data model files for each tenant. [ref-scheduled-refresh-contexts]: /reference/configuration/config#scheduled_refresh_contexts [ref-context-to-app-id]: /reference/configuration/config#context_to_app_id [ref-config-files]: /product/configuration#cubepy-and-cubejs-files -[ref-publicity]: /product/data-modeling/concepts/publicity +[ref-mls]: /product/auth/member-level-security [ref-cubes-public]: /reference/data-model/cube#public [ref-views-public]: /reference/data-model/view#public [ref-measures-public]: /reference/data-model/measures#public diff --git a/docs/pages/guides/style-guide.mdx b/docs/pages/guides/style-guide.mdx index 1e4bc38e62ea1..8224e1cc0177f 100644 --- a/docs/pages/guides/style-guide.mdx +++ b/docs/pages/guides/style-guide.mdx @@ -70,12 +70,14 @@ cube_project - `description` - `public` - `refresh_key` + - `meta` - `pre_aggregations` - `joins` - `dimensions` - `hierarchies` - `segments` - `measures` + - `access_policy` ### Dimensions & measures @@ -141,6 +143,7 @@ cubes: - `public` - `cubes` - `folders` + - `access_policy` ### Example view diff --git a/docs/pages/product/_meta.js b/docs/pages/product/_meta.js index d1326632afc38..fd3fb9b793fbd 100644 --- a/docs/pages/product/_meta.js +++ b/docs/pages/product/_meta.js @@ -4,7 +4,7 @@ module.exports = { "configuration": "Configuration", "data-modeling": "Data modeling", "caching": "Caching", - "auth": "Authentication & authorization", + "auth": "Access control", "apis-integrations": "APIs & integrations", "workspace": "Workspace", "deployment": "Deployment", diff --git a/docs/pages/product/auth.mdx b/docs/pages/product/auth.mdx index 3825a1bc4e626..71adf50b07238 100644 --- a/docs/pages/product/auth.mdx +++ b/docs/pages/product/auth.mdx @@ -1,9 +1,4 @@ ---- -redirect_from: - - /security ---- - -# Overview +# Access control In Cube, authorization (or access control) is based on the **security context**. The diagram below shows how it works during the request processing in Cube: diff --git a/docs/pages/product/auth/_meta.js b/docs/pages/product/auth/_meta.js index 9f2bfa9f089e5..68c456f8a9cf7 100644 --- a/docs/pages/product/auth/_meta.js +++ b/docs/pages/product/auth/_meta.js @@ -1,3 +1,6 @@ module.exports = { - "context": "Security context" + "context": "Security context", + "member-level-security": "Member-level security", + "row-level-security": "Row-level security", + "data-access-policies": "Data access policies" } \ No newline at end of file diff --git a/docs/pages/product/auth/data-access-policies.mdx b/docs/pages/product/auth/data-access-policies.mdx new file mode 100644 index 0000000000000..25396ff9c1916 --- /dev/null +++ b/docs/pages/product/auth/data-access-policies.mdx @@ -0,0 +1,49 @@ +Data access policies + +TODO + +## Data access roles + +TODO + +## Member-level access + +TODO + + +``` +Привет! Можешь проверить, что я правильно мыслю? Снова access policies. +Вот есть такие политики. Для пользователя с ролью status_checker_2_3_4 она ожидаемо делает доступным куб и все его мемберы. + access_policy: + - role: "*" + member_level: + includes: [] + + - role: status_checker_2_3_4 + member_level: + includes: '*' +А вот такие политики (поменял includes на excludes) для пользователя с ролью status_checker_2_3_4 + access_policy: + - role: "*" + member_level: + excludes: '*' + + - role: status_checker_2_3_4 + member_level: + includes: '*' +``` + + + +написать, что public takes preference + + +написать, что вьюшки не наследуют mls от кубов + +When evaluating Cube and View level policies: +- member level policy at the view always wins (you can expose a hidden + member of a Cube on a View) + +## Row-level access + +TODO \ No newline at end of file diff --git a/docs/pages/product/data-modeling/concepts/publicity.mdx b/docs/pages/product/auth/member-level-security.mdx similarity index 98% rename from docs/pages/product/data-modeling/concepts/publicity.mdx rename to docs/pages/product/auth/member-level-security.mdx index 7d5d7f625fba3..7513dfea24f43 100644 --- a/docs/pages/product/data-modeling/concepts/publicity.mdx +++ b/docs/pages/product/auth/member-level-security.mdx @@ -1,4 +1,4 @@ -# Publicity of data model entities +# Member-level security The data model serves as a facade of your data and enables running [queries][ref-queries] via a [rich set of APIs][ref-apis] by referencing data @@ -10,7 +10,7 @@ By default, all cubes, views, measures, dimensions, and segments are *public*, meaning that they can be used in API queries and they are visible during data model introspection. -## Managing publicity +## Managing member-level access You can explicitly make a data model entity public or private by setting its `public` parameter to `true` or `false`. This parameter is available for diff --git a/docs/pages/product/auth/row-level-security.mdx b/docs/pages/product/auth/row-level-security.mdx new file mode 100644 index 0000000000000..4cdd2a58c916a --- /dev/null +++ b/docs/pages/product/auth/row-level-security.mdx @@ -0,0 +1,3 @@ +# Row-level security + +TODO \ No newline at end of file diff --git a/docs/pages/product/configuration.mdx b/docs/pages/product/configuration.mdx index a8a196acae343..990706bee4110 100644 --- a/docs/pages/product/configuration.mdx +++ b/docs/pages/product/configuration.mdx @@ -152,8 +152,7 @@ Cube can be run in an insecure, development mode by setting the mode does the following: - Disables authentication checks. -- Disables access control checks based on the [publicity][ref-data-model-publicity] -of data model entities. +- Disables [member-level access control][ref-mls]. - Enables Cube Store in single instance mode. - Enables background refresh for in-memory cache and [scheduled pre-aggregations][link-scheduled-refresh]. @@ -177,6 +176,6 @@ of data model entities. [ref-dynamic-data-models]: /product/data-modeling/dynamic [ref-custom-docker-image]: /product/deployment/core#extend-the-docker-image [link-docker-env-vars]: https://docs.docker.com/compose/environment-variables/set-environment-variables/ -[ref-data-model-publicity]: /product/data-modeling/concepts/publicity +[ref-mls]: /product/auth/member-level-security [link-current-python-version]: https://github.com/cube-js/cube/blob/master/packages/cubejs-docker/latest.Dockerfile#L13 [link-current-nodejs-version]: https://github.com/cube-js/cube/blob/master/packages/cubejs-docker/latest.Dockerfile#L1 \ No newline at end of file diff --git a/docs/pages/product/data-modeling/concepts.mdx b/docs/pages/product/data-modeling/concepts.mdx index 2cb3282c1215c..e05b4faa817b2 100644 --- a/docs/pages/product/data-modeling/concepts.mdx +++ b/docs/pages/product/data-modeling/concepts.mdx @@ -34,7 +34,7 @@ We'll use a sample e-commerce database with two tables, `orders` and ## Cubes -A [cube][ref-cubes] represents a dataset in Cube, and is conceptually similar to a [view in +_Cubes_ represent datasets in Cube and are conceptually similar to [views in SQL][wiki-view-sql]. Cubes are usually declared in separate files with one cube per file. Typically, a cube points to a single table in your database using the [`sql_table` property][ref-schema-ref-sql-table]: @@ -82,15 +82,18 @@ cubes: -Within each cube are definitions of [dimensions][self-dimensions], -[measures][self-measures], and [segments](#segments). [Joins](#joins) are used -to define relations between cubes; [pre-aggregations](#pre-aggregations) are -designed to accelerate queries to cubes. +Each cube contains the definitions of its _members_: [dimensions](#dimensions), +[measures](#measures), and [segments](#segments). You can control the access to +cubes and their members by configuring the [member-level security][ref-mls]. + +[Joins](#joins) are used to define relations between cubes. +[Pre-aggregations](#pre-aggregations) are used to accelerate queries to cubes. +Cubes and their members can be further referenced by [views](#views). Note that cubes support [extension][ref-extending-cubes], [polymorphism][ref-polymorphic-cubes], and [data blending][ref-data-blending]. -Also, cubes should not necessarily be defined statically; you can actually build -[dynamic data models][ref-dynamic-data-models]. +Cubes can be defined statically and you can also build [dynamic data +models][ref-dynamic-data-models]. @@ -100,18 +103,24 @@ For massive [multi-tenancy][ref-multitenancy] configurations, e.g., with more th + + +See the reference documentaton for the full list of cube [parameters][ref-cubes]. + + + ## Views -Views sit on top of the data graph of cubes and create a facade of your whole +_Views_ sit on top of the data graph of cubes and create a facade of your whole data model with which data consumers can interact. They are useful for defining metrics, managing governance and data access, and controlling ambiguous join paths. -Views can **not** have their own members. Instead, they use the `cubes` -parameter to include members of cubes. Optionally, you can also group members of a view -into [folders][ref-ref-folders]. +Views do **not** define their own members. Instead, they reference cubes by +specific join paths and include their members. Optionally, you can also group +members of a view into [folders][ref-ref-folders]. In the example below, we create the `orders` view which includes select members from `base_orders`, `products`, and `users` cubes: @@ -182,12 +191,18 @@ views: Views do **not** define any [pre-aggregations](#pre-aggregations). Instead, they [reuse][ref-matching-preaggs] pre-aggregations from underlying cubes. -View may not only be defined statically; you can actually build -[dynamic data models][ref-dynamic-data-models]. +View can be defined statically and you can also build [dynamic data +models][ref-dynamic-data-models]. + + + +See the reference documentaton for the full list of view [parameters][ref-views]. + + ## Dimensions -Dimensions represent the properties of a **single** data point in the cube. +_Dimensions_ represent the properties of a **single** data point in the cube. [The `orders` table](#top) contains only dimensions, so representing them in the `orders` cube is straightforward: @@ -289,10 +304,29 @@ Also, [proxy dimensions][ref-proxy-dimensions] are helpful for code reusability and [subquery dimensions][ref-subquery-dimensions] can be used to join cubes implicitly. + + +See the reference documentaton for the full list of [dimension parameters][ref-dimensions]. + + + ### Dimension types -Dimensions can be of different types. See the [dimension type -reference][ref-schema-dimension-types] for details. +Dimensions can be of different types, e.g., `string`, `number`, or `time`. Often, +data types in SQL are mapped to dimension types in the following way: + +| Data type in SQL | Dimension type in Cube | +| --- | --- | +| `timestamp`, `date`, `time` | [`time`](/reference/data-model/types-and-formats#time-1) | +| `text`, `varchar` | [`string`](/reference/data-model/types-and-formats#string-1) | +| `integer`, `bigint`, `decimal` | [`number`](/reference/data-model/types-and-formats#number-1) | +| `boolean` | [`boolean`](/reference/data-model/types-and-formats#boolean-1) | + + + +See the [dimension type reference][ref-ref-dimension-types] for details. + + ### Time dimensions @@ -377,7 +411,7 @@ Time dimensions are essential to enabling performance boosts such as ## Measures -Measures represent the properties of a **set of data points** in the cube. To +_Measures_ represent the properties of a **set of data points** in the cube. To add a measure called `count` to our `orders` cube, for example, we can do the following: @@ -438,15 +472,19 @@ cubes: -Also, [calculated measures][ref-calculated-measures] can be used to perform calculations -on other measures. +[Calculated measures][ref-calculated-measures] and [subquery dimensions][ref-subquery-dimensions] +can be used for measure composition. -### Measure types + -Measures can be of different types. See the [measure type -reference][ref-schema-measure-types] for details. +See the reference documentaton for the full list of measure [parameters][ref-measures]. -Often, aggregate functions in SQL are mapped to measure types in the following way: + + +### Measure types + +Measures can be of different types, e.g., `count`, `sum`, or `number`. Often, +aggregate functions in SQL are mapped to measure types in the following way: | Aggregate function in SQL | Measure type in Cube | | --- | --- | @@ -462,6 +500,12 @@ Often, aggregate functions in SQL are mapped to measure types in the following w | `SUM` | [`sum`](/reference/data-model/types-and-formats#sum) | | Any function returning a timestamp, e.g., `MAX(time)` | [`time`](/reference/data-model/types-and-formats#time) | + + +See the [measure type reference][ref-ref-measure-types] for details. + + + ### Measure additivity Additivity is a property of measures that detemines whether measure values, @@ -578,7 +622,7 @@ an impact on [pre-aggregation matching][ref-matching-preaggs]. ## Joins -Joins define the relationships between cubes, which then allows accessing and +_Joins_ define the relationships between cubes, which then allows accessing and comparing properties from two or more cubes at the same time. In Cube, all joins are `LEFT JOIN`s. @@ -624,15 +668,22 @@ cubes: -There are three [types of join relationships][ref-schema-ref-joins-types] +There are three [types of join relationships][ref-ref-join-types] (`one_to_one`, `one_to_many`, and `many_to_one`) and a few [other -concepts][ref-working-with-joins]. +concepts][ref-working-with-joins] such as the direction of joins and trasitive +joins pitfalls. + + + +See the reference documentaton for the full list of join [parameters][ref-joins]. + + ## Segments -Segments are filters that are predefined in the data model instead of [a Cube -query][ref-backend-query-filters]. They allow simplifying Cube queries and make -it easy to re-use common filters across a variety of queries. +_Segments_ are pre-defined filters that are kept within the data model instead of +[a Cube query][ref-backend-query-filters]. They help to simplify queries and make +it easy to reuse common filters across a variety of queries. To add a segment which limits results to completed orders, we can do the following: @@ -663,11 +714,17 @@ cubes: + + +See the reference documentaton for the full list of segment [parameters][ref-segments]. + + + ## Pre-aggregations -Pre-aggregations are a powerful way of caching frequently-used, expensive -queries and keeping the cache up-to-date on a periodic basis. Within a data -model, they are defined under the `pre_aggregations` property: +_Pre-aggregations_ provide a powerful way to accelerate frequently used queries +and keep the cache up-to-date. Within a data model, they are defined using the +`pre_aggregations` property: @@ -706,31 +763,41 @@ cubes: A more thorough introduction can be found in [Getting Started with Pre-Aggregations][ref-caching-preaggs-intro]. + + +See the reference documentaton for the full list of pre-aggregation +[parameters][ref-preaggs]. + + + + [ref-backend-query-filters]: /product/apis-integrations/rest-api/query-format#filters-format [ref-caching-preaggs-intro]: /product/caching/getting-started-pre-aggregations [ref-caching-use-preaggs-partition-time]: /product/caching/using-pre-aggregations#partitioning -[ref-schema-dimension-types]: - /reference/data-model/types-and-formats#dimension-types -[ref-schema-measure-types]: - /reference/data-model/types-and-formats#measure-types -[ref-schema-ref-joins-types]: - /reference/data-model/joins#relationship +[ref-ref-dimension-types]: /reference/data-model/types-and-formats#dimension-types +[ref-ref-measure-types]: /reference/data-model/types-and-formats#measure-types +[ref-ref-join-types]: /reference/data-model/joins#relationship [ref-schema-ref-sql]: /reference/data-model/cube#sql [ref-schema-ref-sql-table]: /reference/data-model/cube#sql_table [ref-tutorial-incremental-preagg]: /reference/data-model/pre-aggregations#incremental [ref-cubes]: /reference/data-model/cube +[ref-views]: /reference/data-model/view +[ref-dimensions]: /reference/data-model/dimensions +[ref-measures]: /reference/data-model/measures +[ref-joins]: /reference/data-model/joins +[ref-segments]: /reference/data-model/segments +[ref-preaggs]: /reference/data-model/pre-aggregations [ref-extending-cubes]: /product/data-modeling/concepts/code-reusability-extending-cubes [ref-polymorphic-cubes]: /product/data-modeling/concepts/polymorphic-cubes [ref-data-blending]: /product/data-modeling/concepts/data-blending [ref-dynamic-data-models]: /product/data-modeling/dynamic [ref-proxy-dimensions]: /product/data-modeling/concepts/calculated-members#proxy-dimensions [ref-subquery-dimensions]: /product/data-modeling/concepts/calculated-members#subquery-dimensions +[ref-calculated-measures]: /product/data-modeling/concepts/calculated-members#calculated-measures [ref-working-with-joins]: /product/data-modeling/concepts/working-with-joins -[self-dimensions]: #dimensions -[self-measures]: #measures [wiki-olap]: https://en.wikipedia.org/wiki/Online_analytical_processing [wiki-view-sql]: https://en.wikipedia.org/wiki/View_(SQL) [ref-matching-preaggs]: /product/caching/matching-pre-aggregations @@ -744,5 +811,6 @@ Pre-Aggregations][ref-caching-preaggs-intro]. [ref-ref-primary-key]: /reference/data-model/dimensions#primary_key [ref-custom-granularity-recipe]: /guides/recipes/data-modeling/custom-granularity [ref-proxy-granularity]: /product/data-modeling/concepts/calculated-members#time-dimension-granularity +[ref-mls]: /product/auth/member-level-security [ref-ref-hierarchies]: /reference/data-model/hierarchies [ref-ref-folders]: /reference/data-model/view#folders \ No newline at end of file diff --git a/docs/pages/product/data-modeling/concepts/_meta.js b/docs/pages/product/data-modeling/concepts/_meta.js index 5ee99fa7eda18..27e102e8ed734 100644 --- a/docs/pages/product/data-modeling/concepts/_meta.js +++ b/docs/pages/product/data-modeling/concepts/_meta.js @@ -1,5 +1,4 @@ module.exports = { - "publicity": "Publicity", "calculated-members": "Calculated members", "code-reusability-extending-cubes": "Extending cubes", "polymorphic-cubes": "Polymorphic cubes", diff --git a/docs/pages/product/data-modeling/dynamic/jinja.mdx b/docs/pages/product/data-modeling/dynamic/jinja.mdx index 135a60b3afb90..be3ae80634422 100644 --- a/docs/pages/product/data-modeling/dynamic/jinja.mdx +++ b/docs/pages/product/data-modeling/dynamic/jinja.mdx @@ -245,6 +245,10 @@ If you'd like to split your Python code into several files, see +### Inline expressions + +TODO + [jinja]: https://jinja.palletsprojects.com/ [jinja-docs]: https://jinja.palletsprojects.com/en/3.1.x/templates/ diff --git a/docs/pages/reference/configuration/config.mdx b/docs/pages/reference/configuration/config.mdx index b9abd79237c33..f0803edb676a9 100644 --- a/docs/pages/reference/configuration/config.mdx +++ b/docs/pages/reference/configuration/config.mdx @@ -1308,6 +1308,10 @@ module.exports = { +### `context_to_roles` + +TODO + ## Utility ### `logger` diff --git a/docs/pages/reference/data-model/_meta.js b/docs/pages/reference/data-model/_meta.js index 781d8f47f4df7..66735c5ec41c5 100644 --- a/docs/pages/reference/data-model/_meta.js +++ b/docs/pages/reference/data-model/_meta.js @@ -7,6 +7,7 @@ module.exports = { "segments": "Segments", "joins": "Joins", "pre-aggregations": "Pre-aggregations", + "data-access-policies": "Data access policies", "types-and-formats": "Types and formats", "context-variables": "Context variables" } \ No newline at end of file diff --git a/docs/pages/reference/data-model/cube.mdx b/docs/pages/reference/data-model/cube.mdx index 24ee25cd3a71e..eec0b4420d2fd 100644 --- a/docs/pages/reference/data-model/cube.mdx +++ b/docs/pages/reference/data-model/cube.mdx @@ -12,7 +12,7 @@ Cubes are typically declared in separate files with one cube per file. Within each cube are definitions of [measures][ref-ref-measures], [dimensions][ref-ref-dimensions], [hierarchies][ref-ref-hierarchies], [segments][ref-ref-segments], [joins][ref-ref-joins] between cubes, -and [pre-aggregations][ref-ref-pre-aggs]. +[pre-aggregations][ref-ref-pre-aggs], and [data access policies][ref-ref-dap]. @@ -110,88 +110,31 @@ cubes: -### `data_source` +### `sql_alias` -Each cube can have its own `data_source` name to support scenarios where data -should be fetched from multiple databases. The value of the `data_source` -parameter will be passed to the [`driverFactory()`][ref-config-driverfactory] -function as part of the `context` parameter. By default, each cube has a -`default` value for its `data_source`; to override it you can use: +Use `sql_alias` when auto-generated cube alias prefix is too long and truncated +by databases such as Postgres: ```javascript -cube(`order_facts`, { - data_source: `prod_db`, +cube(`order_facts_about_literally_everything_in_the_world`, { sql_table: `orders`, + sql_alias: `order_facts`, }); ``` ```yaml cubes: - - name: order_facts - data_source: prod_db + - name: order_facts_about_literally_everything_in_the_world sql_table: orders + sql_alias: order_facts ``` -### `description` - -This parameter provides a human-readable description of a cube. -When applicable, it will be displayed in [Playground][ref-playground] and exposed -to data consumers via [APIs and integrations][ref-apis]. - -A description can give a hint both to your team and end users, making sure they -interpret the data correctly. - - - -```javascript -cube(`orders`, { - sql_table: `orders`, - title: `Product Orders`, - description: `All orders-related information`, -}); -``` - -```yaml -cubes: - - name: orders - sql_table: orders - title: Product Orders - description: All orders-related information -``` - - - -### `meta` - -Custom metadata. Can be used to pass any information to the frontend. - - - -```javascript -cube(`orders`, { - sql_table: `orders`, - title: `Product Orders`, - meta: { - any: `value` - } -}); -``` - -```yaml -cubes: - - name: orders - sql_table: orders - title: Product Orders - meta: - any: value - -``` - - +It'll generate aliases for members such as `order_facts__count`. `sql_alias` affects +all member names including pre-aggregation table names. ### `extends` @@ -276,6 +219,151 @@ cube(`extended_order_facts`, { }); ``` +### `data_source` + +Each cube can have its own `data_source` name to support scenarios where data +should be fetched from multiple databases. The value of the `data_source` +parameter will be passed to the [`driverFactory()`][ref-config-driverfactory] +function as part of the `context` parameter. By default, each cube has a +`default` value for its `data_source`; to override it you can use: + + + +```javascript +cube(`order_facts`, { + data_source: `prod_db`, + sql_table: `orders`, +}); +``` + +```yaml +cubes: + - name: order_facts + data_source: prod_db + sql_table: orders +``` + + + +### `sql` + +The `sql` parameter specifies the SQL that will be used to generate a table that +will be queried by a cube. It can be any valid SQL query, but usually it takes +the form of a `SELECT * FROM my_table` query. Please note that you don't need to +use `GROUP BY` in a SQL query on the cube level. This query should return a +plain table, without aggregations. + + + +```javascript +cube(`orders`, { + sql: `SELECT * FROM orders`, +}); +``` + +```yaml +cubes: + - name: orders + sql: SELECT * FROM orders +``` + + + +With JavaScript models, you can also reference other cubes' SQL statements for +code reuse: + +```javascript +cube(`companies`, { + sql: ` + SELECT users.company_name, users.company_id + FROM ${users.sql()} AS users + `, +}); +``` + +It is recommended to prefer the [`sql_table`](#parameters-sql-table) parameter +over the `sql` parameter for all cubes that are supposed to use queries like +this: `SELECT * FROM table`. + +### `sql_table` + +The `sql_table` parameter is used as a concise way for defining a cube that uses +a query like this: `SELECT * FROM table`. Instead of using the +[`sql`](#parameters-sql) parameter, use `sql_table` with the table name that this +cube will query. + + + +```javascript +cube(`orders`, { + sql_table: `public.orders`, +}); +``` + +```yaml +cubes: + - name: orders + sql_table: public.orders +``` + + + +### `title` + +Use `title` to change the display name of the cube. By default, Cube will +humanize the cube's name, so for instance, `users_orders` would become +`Users Orders`. If default humanizing doesn't work in your case, please use the +title parameter. It is highly recommended to give human readable names to your +cubes. It will help everyone on a team better understand the data structure and +will help maintain a consistent set of definitions across an organization. + + + +```javascript +cube(`orders`, { + sql_table: `orders`, + title: `Product Orders`, +}); +``` + +```yaml +cubes: + - name: orders + sql_table: orders + title: Product Orders +``` + + + +### `description` + +This parameter provides a human-readable description of a cube. +When applicable, it will be displayed in [Playground][ref-playground] and exposed +to data consumers via [APIs and integrations][ref-apis]. + +A description can give a hint both to your team and end users, making sure they +interpret the data correctly. + + + +```javascript +cube(`orders`, { + sql_table: `orders`, + title: `Product Orders`, + description: `All orders-related information`, +}); +``` + +```yaml +cubes: + - name: orders + sql_table: orders + title: Product Orders + description: All orders-related information +``` + + + ### `public` The `public` parameter is used to manage the visibility of a cube. Valid values @@ -318,7 +406,7 @@ The default values for `refresh_key` are Refresh key of a query is a concatenation of all cubes refresh keys involved in query. For rollup queries pre-aggregation table name is used as a refresh key. -You can set up a custom refresh check SQL by changing `refresh_key` property. +You can set up a custom refresh check SQL by changing the `refresh_key` parameter. Often, a `MAX(updated_at_timestamp)` for OLTP data is a viable option, or examining a metadata table for whatever system is managing the data to see when it last ran. timestamp in that case. @@ -374,7 +462,7 @@ cubes: `every` - can be set as an interval with granularities `second`, `minute`, `hour`, `day`, and `week` or accept CRON string with some limitations. If you -set `every` as CRON string, you can use the `timezone` property. +set `every` as CRON string, you can use the `timezone` parameter. For example: @@ -462,121 +550,61 @@ SELECT FLOOR(EXTRACT(EPOCH FROM NOW()) / 5) └───────────────────────── second (0 - 59, optional) ``` -### `sql` +### `meta` -The `sql` parameter specifies the SQL that will be used to generate a table that -will be queried by a cube. It can be any valid SQL query, but usually it takes -the form of a `SELECT * FROM my_table` query. Please note that you don't need to -use `GROUP BY` in a SQL query on the cube level. This query should return a -plain table, without aggregations. +Custom metadata. Can be used to pass any information to the frontend. ```javascript cube(`orders`, { - sql: `SELECT * FROM orders`, + sql_table: `orders`, + title: `Product Orders`, + meta: { + any: `value` + } }); ``` ```yaml cubes: - name: orders - sql: SELECT * FROM orders -``` - - - -With JavaScript models, you can also reference other cubes' SQL statements for -code reuse: - -```javascript -cube(`companies`, { - sql: ` - SELECT users.company_name, users.company_id - FROM ${users.sql()} AS users - `, -}); -``` - -It is recommended to prefer the [`sql_table`](#parameters-sql-table) property -over the `sql` property for all cubes that are supposed to use queries like -this: `SELECT * FROM table`. - -### `sql_table` - -The `sql_table` property is used as a concise way for defining a cube that uses -a query like this: `SELECT * FROM table`. Instead of using the -[`sql`](#parameters-sql) property, use `sql_table` with the table name that this -cube will query. - - - -```javascript -cube(`orders`, { - sql_table: `public.orders`, -}); -``` + sql_table: orders + title: Product Orders + meta: + any: value -```yaml -cubes: - - name: orders - sql_table: public.orders ``` -### `sql_alias` +### `pre_aggregations` -Use `sql_alias` when auto-generated cube alias prefix is too long and truncated -by DB such as Postgres: +The `pre_aggregations` parameter is used to configure [pre-aggregations][ref-ref-pre-aggs]. - +### `joins` -```javascript -cube(`order_facts`, { - sql_table: `orders`, - sql_alias: `ofacts`, -}); -``` +The `joins` parameter is used to configure [joins][ref-ref-joins]. -```yaml -cubes: - - name: order_facts - sql_table: orders - sql_alias: ofacts -``` +### `dimensions` - +The `dimensions` parameter is used to configure [dimensions][ref-ref-dimensions]. -It'll generate aliases for members such as `ofacts__count`. `sql_alias` affects -all member names including pre-aggregation table names. +### `hierarchies` -### `title` +The `hierarchies` parameter is used to configure [hierarchies][ref-ref-hierarchies]. -Use `title` to change the display name of the cube. By default, Cube will -humanize the cube's name, so for instance, `users_orders` would become -`Users Orders`. If default humanizing doesn't work in your case, please use the -title parameter. It is highly recommended to give human readable names to your -cubes. It will help everyone on a team better understand the data structure and -will help maintain a consistent set of definitions across an organization. +### `segments` - +The `segments` parameter is used to configure [segments][ref-ref-segments]. -```javascript -cube(`orders`, { - sql_table: `orders`, - title: `Product Orders`, -}); -``` +### `measures` -```yaml -cubes: - - name: orders - sql_table: orders - title: Product Orders -``` +The `measures` parameter is used to configure [measures][ref-ref-measures]. - +### `access_policy` + +The `access_policy` parameter is used to configure [data access policies][ref-ref-dap]. [ref-config-driverfactory]: /reference/configuration/config#driverfactory @@ -598,3 +626,4 @@ cubes: [ref-ref-segments]: /reference/data-model/segments [ref-ref-joins]: /reference/data-model/joins [ref-ref-pre-aggs]: /reference/data-model/pre-aggregations +[ref-ref-dap]: /reference/data-model/data-access-policies \ No newline at end of file diff --git a/docs/pages/reference/data-model/data-access-policies.mdx b/docs/pages/reference/data-model/data-access-policies.mdx new file mode 100644 index 0000000000000..580133f4fd08a --- /dev/null +++ b/docs/pages/reference/data-model/data-access-policies.mdx @@ -0,0 +1,174 @@ +# Data access policies + +You can use the `access_policy` parameter within [cubes][ref-ref-cubes] and [views][ref-ref-views] +to configure [data access policies][ref-dap] for them. + +## Parameters + +The `access_policy` parameter should define a list of access policies. Each policy +can be configured using the following parameters: + +- [`role`](#role) defines which [data access role][ref-dap-roles] a policy applies +to. +- [`conditions`](#conditions) can be optionally used to specify when a policy +takes effect. +- [`member_level`](#member-level) and [`row_level`](#row-level) are used to +configure [member-level][ref-dap-mls] and [row-level][ref-dap-rls] access. + +### `role` + +The `role` parameter defines which [data access role][ref-dap-roles], as defined +by the [`context_to_roles`][ref-context-to-roles] configuration parameter, a +policy applies to. To define a policy that applies to all users regardless of +their roles, use the _any role_ shorthand: `role: "*"`. + +In the following example, three access policies are defined, with the first one +applying to all users and two other applying to users with `marketing` or +`finance` roles, respectively. + +```yaml +cubes: + - name: orders + # ... + + access_policy: + - role: "*" + # ... + + - role: marketing + # ... + + - role: finance + # ... +``` + +### `conditions` + +The optional `conditions` parameter, when present, defines a list of conditions +that should all be `true` in order for a policy to take effect. Each condition is +configured with an `if` parameter that is expected to reference the security +context via an [inline expression][ref-python-inline-expressions]. + +TODO: JavaScript??? Maybe it should be in Syntax, not in Python. + +In the following example, a permissive policy for all roles will only apply to +EMEA-based users, as determined by the `is_EMEA_based` attribute in the security +context: + +```yaml +cubes: + - name: orders + # ... + + access_policy: + - role: "*" + conditions: + - if: "{ securityContext.is_EMEA_based }" + member_level: + includes: "*" +``` +You can use the `conditions` parameter to define multiple policies for the same +role. + +In the following example, the first policy provides access to a _subset of members_ +to managers who are full-time employees while the other one provides access to +_all members_ to managers who are full-time employees and have also completed a +data privacy training: + + +```yaml +cubes: + - name: orders + # ... + access_policy: + - role: manager + conditions: + - if: "{ securityContext.is_full_time_employee }" + member_level: + includes: + - status + - count + - role: manager + conditions: + - if: "{ securityContext.is_full_time_employee }" + - if: "{ securityContext.has_completed_privacy_training }" + member_level: + includes: "*" +``` + +### `member_level` + +The optional `member_level` parameter, when present, configures [member-level +access][ref-dap-mls] for a policy. You can provide the lists of allowed and +disallowed members with `includes` and `excludes` parameters, respectively. +You can also use the _all members_ shorthand with both of these paramaters: +`includes: "*"`, `excludes: "*"`. + +In the following example, member-level access is configured this way: + +| Users | Accessible members | +| --- | --- | +| Users with the `admin` role | All members except for `count` | +| Users with the `manager` role | All members except for `count` | +| Users with the `observer` role | All members except for `count` and `count_7d` | +| Users with the `guest` role | Only the `count_30d` measure | +| All other users | No access to this cube at all | + +```yaml +cubes: + - name: orders + # ... + measures: + - name: count + type: count + public: false + - name: count_7d + type: count + rolling_window: + trailing: 7 days + - name: count_30d + type: count + rolling_window: + trailing: 30 days + access_policy: + - role: "*" + member_level: + includes: [] + + - role: admin + + - role: manager + member_level: + includes: "*" + + - role: observer + member_level: + excludes: + - count_7d + + - role: guest + member_level: + includes: + - count_30d +``` + +Note that access policies respect [member-level security][ref-mls] restrictions +configured via `public` parameters. See [member-level access][ref-dap-mls] to +learn more about policy evaluation. + +### `row_level` + +TODO: `filters`, `or` + + + + +[ref-ref-cubes]: /reference/data-model/cube +[ref-ref-views]: /reference/data-model/view +[ref-dap]: /product/auth/data-access-policies +[ref-dap-roles]: /product/auth/data-access-policies#data-access-roles +[ref-dap-mls]: /product/auth/data-access-policies#member-level-access +[ref-dap-rls]: /product/auth/data-access-policies#row-level-access +[ref-context-to-roles]: /reference/configuration/config#context_to_roles +[ref-python-inline-expressions]: /product/data-modeling/dynamic/jinja#inline-expressions +[ref-mls]: /product/auth/member-level-security \ No newline at end of file diff --git a/docs/pages/reference/data-model/dimensions.mdx b/docs/pages/reference/data-model/dimensions.mdx index 7c35bb09898ac..cdeee806f31b4 100644 --- a/docs/pages/reference/data-model/dimensions.mdx +++ b/docs/pages/reference/data-model/dimensions.mdx @@ -6,11 +6,11 @@ redirect_from: # Dimensions -The `dimensions` property contains a set of dimensions. You can think about a -dimension as an attribute related to a measure, e.g. the measure `user_count` +You can use the `dimensions` parameter within [cubes][ref-ref-cubes] to define dimensions. +You can think about a dimension as an attribute related to a measure, e.g. the measure `user_count` can have dimensions like `country`, `age`, `occupation`, etc. -Any dimension should have the following parameters: `name`, `sql` and `type`. +Any dimension should have the following parameters: [`name`](#name), [`sql`](#sql), and [`type`](#type). Dimensions can be also organized into [hierarchies][ref-ref-hierarchies]. @@ -699,6 +699,7 @@ cube(`orders`, { +[ref-ref-cubes]: /reference/data-model/cube [ref-schema-ref-joins]: /reference/data-model/joins [ref-subquery]: /product/data-modeling/concepts/calculated-members#subquery-dimensions [self-subquery]: #sub-query diff --git a/docs/pages/reference/data-model/hierarchies.mdx b/docs/pages/reference/data-model/hierarchies.mdx index 92212b49388bd..98bcb8abe3b40 100644 --- a/docs/pages/reference/data-model/hierarchies.mdx +++ b/docs/pages/reference/data-model/hierarchies.mdx @@ -1,7 +1,7 @@ # Hierarchies -The `hierarchies` property contains a set of hierarchies. You can think about a -hierarchy as a means to group [dimensions][ref-ref-dimensions] together and organize +You can use the `hierarchies` parameter within [cubes][ref-ref-cubes] to define hierarchies. +You can think about a hierarchy as a means to group [dimensions][ref-ref-dimensions] together and organize them into levels of granularity, allowing users to drill down or roll up for analysis. @@ -298,6 +298,7 @@ cubes: +[ref-ref-cubes]: /reference/data-model/cube [ref-ref-dimensions]: /reference/data-model/dimensions [ref-naming]: /product/data-modeling/syntax#naming [ref-apis-support]: /product/apis-integrations#support-for-data-modeling diff --git a/docs/pages/reference/data-model/joins.mdx b/docs/pages/reference/data-model/joins.mdx index defb03fa38779..dab74c78ef6ed 100644 --- a/docs/pages/reference/data-model/joins.mdx +++ b/docs/pages/reference/data-model/joins.mdx @@ -6,9 +6,8 @@ redirect_from: # Joins -The `joins` parameter declares a block to define relationships between cubes. It -allows users to access and compare fields from two or more cubes at the same -time. +You can use the `joins` parameter within [cubes][ref-ref-cubes] to define joins to other cubes. +Joins allow to access and compare members from two or more cubes at the same time. @@ -664,6 +663,8 @@ In case there are multiple join paths that can be used to join the same set of c Cube makes join trees as predictable and stable as possible, but this isn't guaranteed in case multiple join paths exist. Please use views to address join predictability and stability. + +[ref-ref-cubes]: /reference/data-model/cube [ref-restapi-query-filter-op-set]: /product/apis-integrations/rest-api/query-format#set [ref-schema-fundamentals-join-dir]: @@ -675,4 +676,4 @@ Please use views to address join predictability and stability. [wiki-djikstra-alg]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm [wiki-left-join]: https://en.wikipedia.org/wiki/Join_(SQL)#Left_outer_join [wiki-1-1]: https://en.wikipedia.org/wiki/One-to-one_(data_model) -[wiki-1-m]: https://en.wikipedia.org/wiki/One-to-many_(data_model) +[wiki-1-m]: https://en.wikipedia.org/wiki/One-to-many_(data_model) \ No newline at end of file diff --git a/docs/pages/reference/data-model/measures.mdx b/docs/pages/reference/data-model/measures.mdx index 64d378c23a02a..96ff9eb2ba724 100644 --- a/docs/pages/reference/data-model/measures.mdx +++ b/docs/pages/reference/data-model/measures.mdx @@ -6,10 +6,10 @@ redirect_from: # Measures -The `measures` parameter contains a set of measures and each measure is an -aggregation over a certain column in your database table. +You can use the `measures` parameter within [cubes][ref-ref-cubes] to define measures. +Each measure is an aggregation over a certain column in your database table. -Any measure should have the following parameters: `name`, `sql` and `type`. +Any measure should have the following parameters: [`name`](#name), [`sql`](#sql), and [`type`](#type). ## Parameters @@ -527,6 +527,7 @@ You can create calculated measures from several joined cubes. In this case, a join will be created automatically. +[ref-ref-cubes]: /reference/data-model/cube [ref-schema-ref-types-formats-measures-types]: /reference/data-model/types-and-formats#measure-types [ref-schema-ref-types-formats-measures-formats]: diff --git a/docs/pages/reference/data-model/pre-aggregations.mdx b/docs/pages/reference/data-model/pre-aggregations.mdx index abea423da852e..3ff747b7a8d1e 100644 --- a/docs/pages/reference/data-model/pre-aggregations.mdx +++ b/docs/pages/reference/data-model/pre-aggregations.mdx @@ -6,8 +6,8 @@ redirect_from: # Pre-aggregations -[Pre-aggregations][ref-pre-aggs] can be defined using the `pre_aggregations` -parameter of a [cube][ref-ref-cubes]. +You can use the `pre_aggregations` parameter within [cubes][ref-ref-cubes] to define +[pre-aggregations][ref-pre-aggs]. Pre-aggregations must have, at minimum, a [name](#name) and a [type](#type). Pre-aggregations must include all dimensions and measures you will query with. diff --git a/docs/pages/reference/data-model/segments.mdx b/docs/pages/reference/data-model/segments.mdx index 2c074f5aaddd5..e9a903a47f692 100644 --- a/docs/pages/reference/data-model/segments.mdx +++ b/docs/pages/reference/data-model/segments.mdx @@ -6,9 +6,10 @@ redirect_from: # Segments -Segments are predefined filters. You can use segments to define complex -filtering logic in SQL. For example, users for one particular city can be -treated as a segment: +You can use the `segments` parameter within [cubes][ref-ref-cubes] to define segments. +Segments are predefined filters. You can use segments to define complex filtering logic in SQL. + +For example, users for one particular city can be treated as a segment: @@ -336,6 +337,7 @@ cubes: +[ref-ref-cubes]: /reference/data-model/cube [ref-backend-query]: /product/apis-integrations/rest-api/query-format [ref-schema-gen]: /guides/recipes/code-reusability/schema-generation [ref-naming]: /product/data-modeling/syntax#naming diff --git a/docs/pages/reference/data-model/view.mdx b/docs/pages/reference/data-model/view.mdx index 800bdb98b7eee..9d800f967c36a 100644 --- a/docs/pages/reference/data-model/view.mdx +++ b/docs/pages/reference/data-model/view.mdx @@ -152,7 +152,7 @@ above for `base_orders`. The other required parameter inside the `cubes` block is `includes`. Use it to list measures, dimensions, or segments you'd like to include into the view. -To include all members from a cube, use the "includes all" form: `includes: "*"`. +To include all members from a cube, use the _includes all_ shorthand: `includes: "*"`. In that case, you can also use the `excludes` parameter to list members that you'd like to exclude. @@ -340,6 +340,10 @@ views: +### `access_policy` + +The `access_policy` parameter is used to configure [data access policies][ref-ref-dap]. + ### `includes` (deprecated) @@ -391,6 +395,7 @@ views: [ref-naming]: /product/data-modeling/syntax#naming [ref-apis]: /product/apis-integrations [ref-ref-cubes]: /reference/data-model/cube +[ref-ref-dap]: /reference/data-model/data-access-policies [ref-apis-support]: /product/apis-integrations#support-for-data-modeling [ref-playground]: /product/workspace/playground#viewing-the-data-model [ref-viz-tools]: /product/configuration/visualization-tools \ No newline at end of file diff --git a/docs/redirects.json b/docs/redirects.json index 62f19d77c508f..d17711d0eb1d8 100644 --- a/docs/redirects.json +++ b/docs/redirects.json @@ -1,4 +1,9 @@ [ + { + "source": "/product/data-modeling/concepts/publicity", + "destination": "/product/auth/member-level-security", + "permanent": true + }, { "source": "/guides/recipes/data-modeling/fiscal-year-quarter-dimensions", "destination": "/guides/recipes/data-modeling/custom-granularity", From d9f1c73c4559eb03f16f779548eb09b440a3472d Mon Sep 17 00:00:00 2001 From: Igor Lukanin Date: Wed, 29 Jan 2025 20:22:23 +0100 Subject: [PATCH 2/3] LDAP --- docs/pages/product/auth.mdx | 143 ++++++++++------ .../product/auth/data-access-policies.mdx | 2 +- docs/pages/product/workspace/_meta.js | 2 +- docs/pages/product/workspace/sso.mdx | 152 +++++++++++++++--- docs/pages/reference/configuration/config.mdx | 62 ++++++- 5 files changed, 277 insertions(+), 84 deletions(-) diff --git a/docs/pages/product/auth.mdx b/docs/pages/product/auth.mdx index 71adf50b07238..ea96d962a98a4 100644 --- a/docs/pages/product/auth.mdx +++ b/docs/pages/product/auth.mdx @@ -1,6 +1,50 @@ # Access control -In Cube, authorization (or access control) is based on the **security context**. +Cube authenticates requests to [APIs & integrations][ref-apis] via a number +of API-specific mechanisms. Most APIs use the following mechanisms: + +* [User name and password](#user-name-and-password) authentication (e.g., used by the [SQL API][ref-sql-api]). +* [JSON Web Tokens](#json-web-tokens) (JWT) (e.g., used by the [REST API][ref-rest-api]). + +Once the authentication is completed, the API request is associated with a +[security context][ref-sec-ctx]. It is then used to configure [member-level security][ref-mls] +and [row-level security][ref-rls] as well as set [data access policies][ref-dap]. + +## User name and password + +With this mechanism, user name and password are checked. + +### Configuration + +Relevant configuration options: [`check_sql_auth`][ref-config-check-sql-auth] and [`can_switch_sql_user`][ref-config-can-switch-sql-user]. + +Relevant environment variables: `CUBEJS_SQL_USER`, `CUBEJS_SQL_PASSWORD`, `CUBEJS_SQL_SUPER_USER`. + +### Authentication integration + +When using Cube Cloud, you can also validate user name and password pairs via +the Cube Cloud [authentication mechanism][ref-auth-sso], including its [LDAP integration][ref-ldap-integration]. + + + +Authentication integration is available in Cube Cloud on the [Enterprise and above](https://cube.dev/pricing) product tiers. + + + +You can enable the authentication integration by navigating to the Settings → Configuration +of your Cube Cloud deployment and using the Enable Cloud Auth Integration toggle. + +## JSON Web Tokens + +With this mechanism, a JSON Web Token (JWT) is checked. + +### Configuration + +Relevant configuration options: [`check_auth`][ref-config-check-auth] and [`jwt`][ref-config-jwt]. + +Relevant environment variables: `CUBEJS_API_SECRET`, `CUBEJS_JWT_KEY`, `CUBEJS_JWK_URL`, +`CUBEJS_JWT_AUDIENCE`, `CUBEJS_JWT_ISSUER`, `CUBEJS_JWT_SUBJECT`, `CUBEJS_JWT_CLAIMS_NAMESPACE`. + The diagram below shows how it works during the request processing in Cube:
@@ -11,18 +55,37 @@ The diagram below shows how it works during the request processing in Cube: />
-Authentication is handled outside of Cube. A typical use case would be: +### Custom authentication + +Cube also allows you to provide your own JWT verification logic. You can use the +[`check_auth`][ref-config-check-auth] configuration option to verify a JWT and set the security context. + +For example, if you needed to retrieve user information from an LDAP server, +you might do the following: -1. A web server serves an HTML page containing the Cube client, which needs to - communicate securely with the Cube API. -2. The web server should generate a JWT with an expiry to achieve this. The - server could include the token in the HTML it serves or provide the token to - the frontend via an XHR request, which is then stored it in local storage or +```javascript +module.exports = { + checkAuth: async (req, auth) => { + try { + const userInfo = await getUserFromLDAP(req.get("X-LDAP-User-ID")) + return { security_context: userInfo } + } + catch { + throw new Error("Could not authenticate user from LDAP") + } + } +} +``` + +A typical use case would be: + +1. A web server serves a page which needs to communicate with the Cube API. +2. The web server generates a JWT. The + server includes the token in the page or provides the token to + the frontend via an XHR request. The token is then stored in the local storage or a cookie. -3. The JavaScript client is initialized using this token, and includes it in - calls to the Cube API. -4. The token is received by Cube, and verified using any available JWKS (if - configured) +3. The token is used for calls to the Cube API. +4. The token is received by Cube, and verified using any available JWKS (if configured) 5. Once decoded, the token claims are injected into the [security context][ref-sec-ctx]. @@ -33,7 +96,7 @@ can still use it to [pass a security context][ref-sec-ctx].
-## Generating JSON Web Tokens (JWT) +### Generating JSON Web Tokens Authentication tokens are generated based on your API secret. Cube CLI generates an API Secret when a project is scaffolded and saves this value in the `.env` @@ -113,7 +176,7 @@ const cubeApi = cube( You can optionally store this token in local storage or in a cookie, so that you can then use it to query the Cube API. -## Using JSON Web Key Sets (JWKS) +### Using JSON Web Key Sets @@ -123,8 +186,6 @@ Cognito][ref-recipe-cognito] with Cube. -### Configuration - As mentioned previously, Cube supports verifying JWTs using industry-standard JWKS. The JWKS can be provided either from a URL, or as a JSON object conforming to [JWK specification RFC 7517 Section 4][link-jwk-ref], encoded as a string. @@ -172,7 +233,7 @@ Or configure the same using environment variables: CUBEJS_JWK_URL='' ``` -### Verifying claims +#### Verifying claims Cube can also verify the audience, subject and issuer claims in JWTs. Similarly to JWK configuration, these can also be configured in the `cube.js` @@ -196,7 +257,7 @@ CUBEJS_JWT_ISSUER='' CUBEJS_JWT_SUBJECT='' ``` -### Custom claims namespace +#### Custom claims namespace Cube can also extract claims defined in custom namespaces. Simply specify the namespace in your `cube.js` configuration file: @@ -209,49 +270,25 @@ module.exports = { }; ``` -### Caching - -Cube caches JWKS by default when -[`CUBEJS_JWK_URL` or `jwt.jwkUrl` is specified](#configuration). - -- If the response contains a `Cache-Control` header, then Cube uses it to - determine cache expiry. -- The keys inside the JWKS are checked for expiry values and used for cache - expiry. -- If an inbound request supplies a JWT referencing a key not found in the cache, - the cache is refreshed. - -## Custom authentication - -Cube also allows you to provide your own JWT verification logic by setting a -[`checkAuth()`][ref-config-check-auth] function in the `cube.js` configuration -file. This function is expected to verify a JWT and return its claims as the -security context. - -As an example, if you needed to retrieve user information from an LDAP server, -you might do the following: - -```javascript -module.exports = { - checkAuth: async (req, auth) => { - try { - const userInfo = await getUserFromLDAP(req.get("X-LDAP-User-ID")); - return { security_context: userInfo }; - } catch { - throw new Error("Could not authenticate user from LDAP"); - } - }, -}; -``` [link-jwt-docs]: https://github.com/auth0/node-jsonwebtoken#token-expiration-exp-claim [link-jwt-libs]: https://jwt.io/#libraries-io [link-jwk-ref]: https://tools.ietf.org/html/rfc7517#section-4 -[ref-config-check-auth]: /reference/configuration/config#checkauth +[ref-config-check-auth]: /reference/configuration/config#check_auth +[ref-config-jwt]: /reference/configuration/config#jwt +[ref-config-check-sql-auth]: /reference/configuration/config#check_sql_auth +[ref-config-can-switch-sql-user]: /reference/configuration/config#can_switch_sql_user [ref-config-migrate-cube]: /product/configuration#migration-from-express-to-docker-template [ref-recipe-auth0]: /guides/recipes/auth/auth0-guide [ref-recipe-cognito]: /guides/recipes/auth/aws-cognito [ref-sec-ctx]: /product/auth/context -[link-slack]: https://slack.cube.dev/ +[ref-apis]: /product/apis-integrations +[ref-rest-api]: /product/apis-integrations/rest-api +[ref-sql-api]: /product/apis-integrations/sql-api +[ref-dap]: /product/auth/data-access-policies +[ref-mls]: /product/auth/member-level-security +[ref-rls]: /product/auth/row-level-security +[ref-auth-sso]: /product/workspace/sso +[ref-ldap-integration]: /product/workspace/sso#ldap-integration \ No newline at end of file diff --git a/docs/pages/product/auth/data-access-policies.mdx b/docs/pages/product/auth/data-access-policies.mdx index 25396ff9c1916..3d9d03a337965 100644 --- a/docs/pages/product/auth/data-access-policies.mdx +++ b/docs/pages/product/auth/data-access-policies.mdx @@ -1,4 +1,4 @@ -Data access policies +# Data access policies TODO diff --git a/docs/pages/product/workspace/_meta.js b/docs/pages/product/workspace/_meta.js index 0b05eebdbebe8..6e324c427cec5 100644 --- a/docs/pages/product/workspace/_meta.js +++ b/docs/pages/product/workspace/_meta.js @@ -13,7 +13,7 @@ module.exports = { "performance": "Performance Insights", "monitoring": "Monitoring Integrations", "access-control": "Access Control", - "sso": "Single Sign-on", + "sso": "Authentication & SSO", "audit-log": "Audit Log", "encryption-keys": "Encryption keys", "budgets": "Budgets", diff --git a/docs/pages/product/workspace/sso.mdx b/docs/pages/product/workspace/sso.mdx index 151f6e5f77698..c452cf60b73a5 100644 --- a/docs/pages/product/workspace/sso.mdx +++ b/docs/pages/product/workspace/sso.mdx @@ -1,22 +1,20 @@ ---- -redirect_from: - - /workspace/sso/ ---- +# Authentication & SSO -# Single Sign-on - -As an account administrator, you can manage how your team accesses Cube Cloud. -There are options to log in using email and password, a GitHub account, or a -Google account. +As an account administrator, you can manage how your team and users access Cube Cloud. +You can authenticate using email and password, a GitHub account, or a Google account. Cube Cloud also provides single sign-on (SSO) via identity providers supporting -industry-proven [SAML 2.0 protocol][wiki-saml], e.g., Okta, Google Workspace, -Azure AD, etc. +[SAML 2.0](#saml-20), e.g., Okta, Google Workspace, Azure AD, etc. + +Finally, Cube Cloud provides the [LDAP integration](#ldap-integration), enabling +users of [APIs & integrations][ref-apis] to authenticate via an LDAP catalog +and assume roles that work with [data access policies][ref-dap] once [authentication +integration][ref-auth-integration] is enabled. -Single sign-on is available in Cube Cloud on -[Enterprise and above](https://cube.dev/pricing) product tiers. +Authentication is available in Cube Cloud on [all product tiers](https://cube.dev/pricing).
+[SAML 2.0](#saml-20) and [LDAP integration](#ldap-integration) are available on [Enterprise and above](https://cube.dev/pricing) product tiers.
@@ -25,10 +23,23 @@ Single sign-on is available in Cube Cloud on style="border: 0;" /> -## Guides +## Configuration + +To manage authentication settings, navigate to Team & Security settings +of your Cube Cloud account, and switch to the Authentication & SSO tab: + + + +Use the toggles in Password, Google, and GitHub +sections to enable or disable these authentication options. + +### SAML 2.0 + +Use the toggle in the SAML 2.0 section to enable or disable the authentication +via an identity provider supporting the [SAML 2.0 protocol][wiki-saml]. +Once it's enabled, you'll see the SAML 2.0 Settings section directly below. -Single sign-on works with various identity providers. Check the following guides -to get tool-specific instructions: +Check the following guides to get tool-specific instructions on configuration: -## Configuration +### LDAP integration -To manage sign-in and single sign-on settings, click your user name from the -top-right corner, navigate to Team & Security, and switch to -the Authentication & SSO tab: +Use the toggle in the LDAP Integration section to enable or disable the +integration with an [LDAP catalog][wiki-ldap]. +Once it's enabled, you'll see the LDAP Settings section directly below. - + + +Cube Cloud will be accessing your LDAP server from the IP addresses shown under +LDAP Settings. If needed, add these IP addresses to an allowlist. + + + +You can configure [connection settings](#connection-settings) and use the +Test Connection button to validate them. You can also configure +[user properties](#user-properties-mapping) mapping, [user roles](#user-roles-mapping) mapping, +and [user attributes](#user-attributes-mapping) mapping. + +#### Connection settings + +You have to configure the following connection settings: + +| Option | Description | +| --- | --- | +| LDAP Server URL | Address of your LDAP server | +| Use Secure LDAP | Use an encrypted connection (LDAPS) | +| Don't Verify CA | Disable certificate authority verification | +| Certificate | Certificate for LDAPS in the PEM format | +| Certificate Authority | Certificate for the private CA in the PEM format | +| Key | Key for mutual TLS (mTLS) in the PEM format | +| Bind DN | User name for LDAP authentication | +| Bind Credentials | Password for LDAP authentication | +| Search Base | Base DN for searching users | +| User Object Class | Object class for user entries | + +Use the tooltips in Cube Cloud to get more information about each setting. + +#### User properties mapping + +You have to configure how user data in an LDAP catalog maps to user properties in Cube Cloud. +The following properties are required: + +| Property | Description | +| --- | --- | +| Login Attribute | Login name | +| Id Attribute | Unique identifier | +| Email Attribute | Email address | +| Name Attribute | Full name | + +Use the tooltips in Cube Cloud to get more information about each setting. + +#### User roles mapping + +You can configure how user data in an LDAP catalog maps to roles in Cube Cloud. +You can also use mapped roles with [data access policies][ref-dap] once [authentication +integration][ref-auth-integration] is enabled. + +Mapping is performed as follows: +* Roles Attribute is retrieved from an LDAP catalog. +* Retrieved value is transformed using rules under Role mapping. +* If the value matches an existing role in Cube Cloud, then the user assumes this role. + +Additionally, the user always assumes the role specified under Default Cloud role. + + + +All roles will be available under `cloud.roles` array in the [security context][ref-security-context]: + +```json +{ + "cloud": { + "roles": [ + "Everyone", + "manager" + ] + } +} +``` + +#### User attributes mapping + +You can also bring more user data from an LDAP catalog to use with [data access policies][ref-dap]. +Mapping is performed using the rules under Attribute mapping. + +All mapped attributes and their values will be available under `cloud.userAttributes` +dictionary in the [security context][ref-security-context]: + +```json +{ + "cloud": { + "userAttributes": { + "fullName": "John Doe", + "department": "Finance", + "location": "San Mateo" + } + } +} +``` [wiki-saml]: https://en.wikipedia.org/wiki/SAML_2.0 +[wiki-ldap]: https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol +[ref-apis]: /product/apis-integrations +[ref-dap]: /product/auth/data-access-policies +[ref-security-context]: /product/auth/context +[ref-auth-integration]: /product/auth#authentication-integration \ No newline at end of file diff --git a/docs/pages/reference/configuration/config.mdx b/docs/pages/reference/configuration/config.mdx index f0803edb676a9..36a70e231969d 100644 --- a/docs/pages/reference/configuration/config.mdx +++ b/docs/pages/reference/configuration/config.mdx @@ -52,8 +52,6 @@ environment variable. The default value is `model`. Use [`repositoryFactory`][self-repofactory] for [multitenancy][ref-multitenancy] or when a more flexible setup is needed. -{/* TODO: https://cubedevinc.atlassian.net/browse/CC-3095 */} - ### `context_to_app_id` It's a [multitenancy][ref-multitenancy] option. @@ -1310,13 +1308,62 @@ module.exports = { ### `context_to_roles` -TODO +Used by [data access policies][ref-dap]. This option is used to derive a list of +[data access roles][ref-dap-roles] from the [security context][ref-sec-ctx]. + + + +```python +from cube import config + +@config('context_to_roles') +def context_to_roles(ctx: dict) -> list[str]: + return ctx['securityContext'].get('roles', ['default']) +``` + +```javascript + +module.exports = { + contextToRoles: ({ securityContext }) => { + return securityContext.roles || ['default'] + } +} +``` + + + +If the [user roles mapping][ref-ldap-roles-mapping] in the [LDAP integration][ref-ldap-integration] +is configured and the [authentication integration][ref-auth-integration] is enabled, +the `context_to_roles` option might be defined as follows: + + + +```python +from cube import config + +@config('context_to_roles') +def context_to_roles(ctx: dict) -> list[str]: + cloud_ctx = ctx['securityContext'].get('cloud', {'roles': []}) + return cloud_ctx.get('roles', []) +``` + +```javascript + +module.exports = { + contextToRoles: ({ securityContext }) => { + const cloud_ctx = securityContext.cloud || { roles: [] } + return cloud_ctx.roles || [] + } +} +``` + + ## Utility ### `logger` -A function to server as a custom logger. +A function to define a custom logger. Accepts the following arguments: - `message`: the message to be logged @@ -1443,4 +1490,9 @@ If not defined, Cube will lookup for environment variable [link-snake-case]: https://en.wikipedia.org/wiki/Snake_case [link-camel-case]: https://en.wikipedia.org/wiki/Camel_case [link-github-cube-drivers]: https://github.com/cube-js/cube/tree/master/packages -[ref-ungrouped-query]: /product/apis-integrations/queries#ungrouped-query \ No newline at end of file +[ref-ungrouped-query]: /product/apis-integrations/queries#ungrouped-query +[ref-dap]: /product/auth/data-access-policies +[ref-dap-roles]: /product/auth/data-access-policies#data-access-roles +[ref-auth-integration]: /product/auth#authentication-integration +[ref-ldap-roles-mapping]: /product/workspace/sso#user-roles-mapping +[ref-ldap-integration]: /product/workspace/sso#ldap-integration \ No newline at end of file From 764c7ab24015a0251907518fd088eb767c150989 Mon Sep 17 00:00:00 2001 From: Igor Lukanin Date: Tue, 4 Feb 2025 16:14:47 +0100 Subject: [PATCH 3/3] . --- docs/pages/product/apis-integrations.mdx | 18 +- docs/pages/product/auth.mdx | 79 ++++-- docs/pages/product/auth/context.mdx | 27 +- .../product/auth/data-access-policies.mdx | 165 ++++++++++-- .../product/auth/member-level-security.mdx | 38 ++- .../pages/product/auth/row-level-security.mdx | 45 +++- .../product/data-modeling/dynamic/jinja.mdx | 4 - docs/pages/product/workspace/sso.mdx | 8 +- .../data-model/data-access-policies.mdx | 254 +++++++++++++++--- .../reference/data-model/hierarchies.mdx | 2 +- docs/pages/reference/data-model/view.mdx | 2 +- 11 files changed, 536 insertions(+), 106 deletions(-) diff --git a/docs/pages/product/apis-integrations.mdx b/docs/pages/product/apis-integrations.mdx index ebd26c51a323e..82c7b3fe48c8c 100644 --- a/docs/pages/product/apis-integrations.mdx +++ b/docs/pages/product/apis-integrations.mdx @@ -48,7 +48,7 @@ for an unofficial, community-maintained [client library for Python](https://gith -### Support for data modeling +### Data modeling Support for data modeling features differ across APIs, integrations, and [visualization tools][ref-viz-tools]. Some of the features with partial support are listed below: @@ -58,6 +58,17 @@ tools][ref-viz-tools]. Some of the features with partial support are listed belo | [Hierarchies][ref-hierarchies] | ✅ [Tableau][ref-tableau] via [Semantic Layer Sync][ref-sls]
✅ Cube Cloud for Excel
✅ [Cube Cloud for Sheets][ref-cube-cloud-for-sheets] | ❌ All other tools | | [Folders][ref-folders] | ✅ [Tableau][ref-tableau] via [Semantic Layer Sync][ref-sls]
✅ Cube Cloud for Excel
✅ [Cube Cloud for Sheets][ref-cube-cloud-for-sheets] | ❌ All other tools | +### Authentication methods + +Support for authentication methods differ across APIs, integrations, and [visualization +tools][ref-viz-tools]: + +| Method | Supported in | +| --- | --- | +| [User name and password][ref-auth-user-pass] | ✅ [SQL API][ref-sql-api] and [Semantic Layer Sync][ref-sls]
✅ [MDX API][ref-mdx-api] | +| [Identity provider][ref-auth-idp] | ✅ Cube Cloud for Excel
✅ [Cube Cloud for Sheets][ref-cube-cloud-for-sheets] | +| [Access token][ref-auth-tokens] | ✅ [REST API][ref-rest-api]
✅ [GraphQL API][ref-graphql-api]
✅ [AI API][ref-ai-api] | + ## Management APIs In case you'd like Cube to work with data orchestration tools and let them push @@ -83,4 +94,7 @@ API][ref-orchestration-api]. [ref-viz-tools]: /product/configuration/visualization-tools [ref-hierarchies]: /reference/data-model/hierarchies [ref-folders]: /reference/data-model/view#folders -[ref-tableau]: /product/configuration/visualization-tools/tableau \ No newline at end of file +[ref-tableau]: /product/configuration/visualization-tools/tableau +[ref-auth-user-pass]: /product/auth#user-name-and-password +[ref-auth-idp]: /product/auth#identity-provider +[ref-auth-tokens]: /product/auth#access-token \ No newline at end of file diff --git a/docs/pages/product/auth.mdx b/docs/pages/product/auth.mdx index ea96d962a98a4..be27c6d3313f4 100644 --- a/docs/pages/product/auth.mdx +++ b/docs/pages/product/auth.mdx @@ -1,18 +1,25 @@ # Access control -Cube authenticates requests to [APIs & integrations][ref-apis] via a number -of API-specific mechanisms. Most APIs use the following mechanisms: +Cube supports a few methods to authenticates requests to [APIs & integrations][ref-apis]. +Usually, an API supports authentication via [one of these methods][ref-apis-methods]: -* [User name and password](#user-name-and-password) authentication (e.g., used by the [SQL API][ref-sql-api]). -* [JSON Web Tokens](#json-web-tokens) (JWT) (e.g., used by the [REST API][ref-rest-api]). +* [User name and password](#user-name-and-password) (e.g., used by the [SQL API][ref-sql-api]). +* [Identity provider](#identity-provider) (e.g., used by the [Cube Cloud for Sheets][ref-cube-cloud-sheets]). +* [Access token](#access-token) (e.g., used by the [REST API][ref-rest-api]). -Once the authentication is completed, the API request is associated with a -[security context][ref-sec-ctx]. It is then used to configure [member-level security][ref-mls] +Regardless of the method, the authentication flow includes the following steps: + +* User identity information (e.g., password or access token) is passed to Cube. +* Cube validates the identity information or trusts the identity provider. +* User identity information is optionally enriched with additional attributes, +such the user's role, via [authentication integration](#authentication-integration). +* Finally, the API request is associated with a [security context][ref-sec-ctx]. +It is then used to configure [member-level security][ref-mls] and [row-level security][ref-rls] as well as set [data access policies][ref-dap]. ## User name and password -With this mechanism, user name and password are checked. +Some visualization tools (e.g., BI tools) can pass user name and, sometimes, password to Cube. ### Configuration @@ -20,30 +27,28 @@ Relevant configuration options: [`check_sql_auth`][ref-config-check-sql-auth] an Relevant environment variables: `CUBEJS_SQL_USER`, `CUBEJS_SQL_PASSWORD`, `CUBEJS_SQL_SUPER_USER`. -### Authentication integration +## Identity provider -When using Cube Cloud, you can also validate user name and password pairs via -the Cube Cloud [authentication mechanism][ref-auth-sso], including its [LDAP integration][ref-ldap-integration]. +Some visualization tools (e.g., [Cube Cloud for Sheets][ref-cube-cloud-sheets]) implement +an OAuth 2.0 flow to authenticate users. -Authentication integration is available in Cube Cloud on the [Enterprise and above](https://cube.dev/pricing) product tiers. +OAuth 2.0 flow is available in Cube Cloud on [Premium and above](https://cube.dev/pricing) product tiers. -You can enable the authentication integration by navigating to the Settings → Configuration -of your Cube Cloud deployment and using the Enable Cloud Auth Integration toggle. +During this flow, users are redirected to a login page of an identity provider (e.g., Google) +where they enter their credentials. Then, the identity provider passes the user identity +information to Cube Cloud. -## JSON Web Tokens +This method does not require any configuration. -With this mechanism, a JSON Web Token (JWT) is checked. +## Access token -### Configuration - -Relevant configuration options: [`check_auth`][ref-config-check-auth] and [`jwt`][ref-config-jwt]. - -Relevant environment variables: `CUBEJS_API_SECRET`, `CUBEJS_JWT_KEY`, `CUBEJS_JWK_URL`, -`CUBEJS_JWT_AUDIENCE`, `CUBEJS_JWT_ISSUER`, `CUBEJS_JWT_SUBJECT`, `CUBEJS_JWT_CLAIMS_NAMESPACE`. +Some visualization tools (e.g., custom front-end applications) can pass access tokens based +on the [JSON Web Token][wiki-jwt] (JWT) standard to Cube. These tokens can be either generated +by these applications or obtained from an identity provider. Cube then validates these tokens. The diagram below shows how it works during the request processing in Cube: @@ -55,9 +60,16 @@ The diagram below shows how it works during the request processing in Cube: /> +### Configuration + +Relevant configuration options: [`check_auth`][ref-config-check-auth] and [`jwt`][ref-config-jwt]. + +Relevant environment variables: `CUBEJS_API_SECRET`, `CUBEJS_JWT_KEY`, `CUBEJS_JWK_URL`, +`CUBEJS_JWT_AUDIENCE`, `CUBEJS_JWT_ISSUER`, `CUBEJS_JWT_SUBJECT`, `CUBEJS_JWT_CLAIMS_NAMESPACE`. + ### Custom authentication -Cube also allows you to provide your own JWT verification logic. You can use the +Cube allows you to provide your own JWT verification logic. You can use the [`check_auth`][ref-config-check-auth] configuration option to verify a JWT and set the security context. For example, if you needed to retrieve user information from an LDAP server, @@ -270,9 +282,24 @@ module.exports = { }; ``` +## Authentication integration + +When using Cube Cloud, you can enrich the security context with information about +an authenticated user, obtained during their authentication or loaded via an +[LDAP integration][ref-ldap-integration]. + + + +Authentication integration is available in Cube Cloud on [all product tiers](https://cube.dev/pricing). + + + +You can enable the authentication integration by navigating to the Settings → Configuration +of your Cube Cloud deployment and using the Enable Cloud Auth Integration toggle. + -[link-jwt-docs]: - https://github.com/auth0/node-jsonwebtoken#token-expiration-exp-claim +[wiki-jwt]: https://en.wikipedia.org/wiki/JSON_Web_Token +[link-jwt-docs]: https://github.com/auth0/node-jsonwebtoken#token-expiration-exp-claim [link-jwt-libs]: https://jwt.io/#libraries-io [link-jwk-ref]: https://tools.ietf.org/html/rfc7517#section-4 [ref-config-check-auth]: /reference/configuration/config#check_auth @@ -285,10 +312,12 @@ module.exports = { [ref-recipe-cognito]: /guides/recipes/auth/aws-cognito [ref-sec-ctx]: /product/auth/context [ref-apis]: /product/apis-integrations +[ref-apis-methods]: /product/apis-integrations#authentication-methods [ref-rest-api]: /product/apis-integrations/rest-api [ref-sql-api]: /product/apis-integrations/sql-api [ref-dap]: /product/auth/data-access-policies [ref-mls]: /product/auth/member-level-security [ref-rls]: /product/auth/row-level-security [ref-auth-sso]: /product/workspace/sso -[ref-ldap-integration]: /product/workspace/sso#ldap-integration \ No newline at end of file +[ref-ldap-integration]: /product/workspace/sso#ldap-integration +[ref-cube-cloud-sheets]: /product/apis-integrations/google-sheets \ No newline at end of file diff --git a/docs/pages/product/auth/context.mdx b/docs/pages/product/auth/context.mdx index 641497f7aee37..7be8ba6b3cdd6 100644 --- a/docs/pages/product/auth/context.mdx +++ b/docs/pages/product/auth/context.mdx @@ -31,6 +31,30 @@ inside: - the [`COMPILE_CONTEXT`][ref-cubes-compile-ctx] global, which is used to support [multi-tenant deployments][link-multitenancy]. +## Contents + +By convention, the contents of the security context should be an object (dictionary) +with nested structure: + +```json +{ + "sub": "1234567890", + "iat": 1516239022, + "user_name": "John Doe", + "user_id": 42, + "location": { + "city": "San Francisco", + "state": "CA" + } +} +``` + +### Reserved elements + +Some features of Cube Cloud (e.g., [authentication integration][ref-auth-integration] +and [LDAP integration][ref-ldap-integration]) use the `cloud` element in the security context. +This element is reserved and should not be used for other purposes. + ## Using query_rewrite You can use [`query_rewrite`][ref-config-queryrewrite] to amend incoming queries @@ -223,7 +247,6 @@ def masked(sql, security_context): return "'--- masked ---'" ``` - ### Usage with pre-aggregations To generate pre-aggregations that rely on `COMPILE_CONTEXT`, [configure @@ -248,3 +271,5 @@ build one from a JSON object. [ref-cubes-compile-ctx]: /reference/data-model/context-variables#compile_context [ref-devtools-playground]: /product/workspace/playground#editing-the-security-context +[ref-auth-integration]: /product/auth#authentication-integration +[ref-ldap-integration]: /product/workspace/sso#ldap-integration \ No newline at end of file diff --git a/docs/pages/product/auth/data-access-policies.mdx b/docs/pages/product/auth/data-access-policies.mdx index 3d9d03a337965..362c4f7c011e0 100644 --- a/docs/pages/product/auth/data-access-policies.mdx +++ b/docs/pages/product/auth/data-access-policies.mdx @@ -1,49 +1,164 @@ # Data access policies -TODO +Data access policies provide a holistic mechanism to manage [member-level](#member-level-access) +and [row-level](#row-level-access) security for different [data access roles](#data-access-roles). +You can define access control rules in data model files, allowing for an organized +and maintainable approach to security. ## Data access roles -TODO +Each request to Cube includes a [security context][ref-sec-ctx], which can contain +information about the user. You can use the [`context_to_roles` configuration +option][ref-ctx-to-roles] to derive the user's roles from the security context: -## Member-level access + -TODO +```python +from cube import config +@config('context_to_roles') +def context_to_roles(ctx: dict) -> list[str]: + return ctx['securityContext'].get('roles', ['default']) +``` +```javascript +module.exports = { + contextToRoles: ({ securityContext }) => { + return securityContext.roles || ['default'] + } +} ``` -Привет! Можешь проверить, что я правильно мыслю? Снова access policies. -Вот есть такие политики. Для пользователя с ролью status_checker_2_3_4 она ожидаемо делает доступным куб и все его мемберы. + + + +A user can have more than one role. + +## Policies + +You can define policies that target specific roles and contain member-level and (or) +row-level security rules: + + + +```yaml +cubes: + - name: orders + # ... + access_policy: + # For all roles, restrict access entirely - role: "*" member_level: includes: [] - - - role: status_checker_2_3_4 - member_level: - includes: '*' -А вот такие политики (поменял includes на excludes) для пользователя с ролью status_checker_2_3_4 - access_policy: - - role: "*" - member_level: - excludes: '*' - - - role: status_checker_2_3_4 + + # For the `manager` role, + # allow access to all members + # but filter rows by the user's country + - role: manager + conditions: + - if: "{ securityContext.is_EMEA_based }" member_level: - includes: '*' + includes: "*" + row_level: + filters: + - member: country + operator: eq + values: [ "{ securityContext.country }" ] ``` +```javascript +cube(`orders`, { + // ... + + access_policy: [ + { + // For all roles, restrict access entirely + role: `*`, + member_level: { + includes: [] + } + }, + { + // For the `manager` role, + // allow access to all members + // but filter rows by the user's country + role: `manager`, + conditions: [ + { if: securityContext.is_EMEA_based } + ], + member_level: { + includes: `*` + }, + row_level: { + filters: [ + { + member: `country`, + operator: `equals`, + values: [ securityContext.country ] + } + ] + } + } + ] +}) +``` + + + +You can define data access policies both in cubes and views. If you use views, +it is recommended to keep all your cubes private and define access policies in views only. +It will make your security rules easier to maintain and reason about, especially +when it comes to [member-level access](#member-level-access). + +For more details on available parameters, check out the [data access policies reference][ref-ref-dap]. + +## Policy evaluation + +When processing a request, Cube will evaluate the data access policies and combine them +with relevant custom security rules, e.g., [`public` parameters][ref-mls-public] for member-level security +and [`query_rewrite` filters][ref-rls-queryrewrite] for row-level security. + +### Member-level access + +Member-level security rules in data access policies are _combined together_ +with `public` parameters of cube and view members using the _AND_ semantics. +Both will apply to the request. + +_When querying a view,_ member-level security rules defined in the view are _**not** combined together_ +with member-level security rules defined in relevant cubes. +**Only the ones from the view will apply to the request.** + + + +This is consistent with how column-level security works in SQL databases. If you have +a view that exposes a subset of columns from a table, it doesnt matter if the +columns in the table are public or not, the view will expose them anyway. + + + +### Row-level access +Row-level filters in data access policies are _combined together_ with filters defined +using the [`query_rewrite` configuration option][ref-config-queryrewrite]. +Both will apply to the request. -написать, что public takes preference +_When querying a view,_ row-level filters defined in the view are _combined together_ +with row-level filters defined in relevant cubes. Both will apply to the request. + -написать, что вьюшки не наследуют mls от кубов +This is consistent with how row-level security works in SQL databases. If you have +a view that exposes a subset of rows from another view, the result set will be +filtered by the row-level security rules of both views. -When evaluating Cube and View level policies: -- member level policy at the view always wins (you can expose a hidden - member of a Cube on a View) + -## Row-level access -TODO \ No newline at end of file +[ref-mls]: /product/auth/member-level-security +[ref-mls-public]: /product/auth/member-level-security#managing-member-level-access +[ref-rls]: /product/auth/row-level-security +[ref-rls-queryrewrite]: /product/auth/row-level-security#managing-row-level-access +[ref-sec-ctx]: /product/auth/context +[ref-ctx-to-roles]: /reference/configuration/config#context_to_roles +[ref-ref-dap]: /reference/data-model/data-access-policies +[ref-config-queryrewrite]: /reference/configuration/config#query_rewrite \ No newline at end of file diff --git a/docs/pages/product/auth/member-level-security.mdx b/docs/pages/product/auth/member-level-security.mdx index 7513dfea24f43..2e469ed845c66 100644 --- a/docs/pages/product/auth/member-level-security.mdx +++ b/docs/pages/product/auth/member-level-security.mdx @@ -1,21 +1,25 @@ # Member-level security -The data model serves as a facade of your data and enables running -[queries][ref-queries] via a [rich set of APIs][ref-apis] by referencing data -model [entities][ref-data-modeling-concepts]: cubes, views, and their members. -Controlling whether they are public or not helps manage data access and expose -a coherent representation of the data model to end users. +The data model serves as a facade of your data. With member-level security, +you can define whether [data model entities][ref-data-modeling-concepts] (cubes, views, +and their members) are exposed to end users and can be queried via [APIs & +integrations][ref-apis]. -By default, all cubes, views, measures, dimensions, and segments are *public*, -meaning that they can be used in API queries and they are visible during data -model introspection. +Member-level security in Cube is similar to column-level security in SQL databases. +Defining whether users have access to [cubes][ref-cubes] and [views][ref-views] is +similar to defining access to database tables; defining whether they have access +to dimensions and measures — to columns. + +__By default, all cubes, views, and their members are *public*,__ meaning that they +can be accessed by any users and they are also visible during data model introspection. ## Managing member-level access You can explicitly make a data model entity public or private by setting its `public` parameter to `true` or `false`. This parameter is available for [cubes][ref-cubes-public], [views][ref-views-public], [measures][ref-measures-public], -[dimensions][ref-dimensions-public], and [segments][ref-segments-public]. +[dimensions][ref-dimensions-public], [hierarchies][ref-hierarchies-public], and +[segments][ref-segments-public]. @@ -56,6 +60,16 @@ anyway, you can use the [`query_rewrite` configuration option][ref-query-rewrite ## Best practices +### Data access policies + +You can use [data access policies][ref-dap] to manage both member-level and +[row-level][ref-rls] security for different roles. With them, you can keep +access control rules in one place instead of spreading them across a number of +`public` parameters in a cube or a view. + +__It is recommended to use data access policies by default.__ You can also combine +them with setting some `public` parameters manually for specific cases. + ### Cubes and views If your data model contains both [cubes][ref-cubes] and [views][ref-views], @@ -116,11 +130,11 @@ cube(`users`, { [ref-data-modeling-concepts]: /product/data-modeling/concepts [ref-apis]: /product/apis-integrations -[ref-queries]: /product/apis-integrations/queries [ref-cubes-public]: /reference/data-model/cube#public [ref-views-public]: /reference/data-model/view#public [ref-measures-public]: /reference/data-model/measures#public [ref-dimensions-public]: /reference/data-model/dimensions#public +[ref-hierarchies-public]: /reference/data-model/hierarchies#public [ref-segments-public]: /reference/data-model/segments#public [ref-cubes]: /product/data-modeling/concepts#cubes [ref-views]: /product/data-modeling/concepts#views @@ -129,4 +143,6 @@ cube(`users`, { [ref-dynamic-data-modeling]: /product/data-modeling/dynamic [ref-query-rewrite]: /reference/configuration/config#query_rewrite [ref-dev-mode]: /product/configuration#development-mode -[ref-playground]: /product/workspace/playground \ No newline at end of file +[ref-playground]: /product/workspace/playground +[ref-dap]: /product/auth/data-access-policies +[ref-rls]: /product/auth/row-level-security \ No newline at end of file diff --git a/docs/pages/product/auth/row-level-security.mdx b/docs/pages/product/auth/row-level-security.mdx index 4cdd2a58c916a..d5c149cf42dbf 100644 --- a/docs/pages/product/auth/row-level-security.mdx +++ b/docs/pages/product/auth/row-level-security.mdx @@ -1,3 +1,46 @@ # Row-level security -TODO \ No newline at end of file +The data model serves as a facade of your data. With row-level security, +you can define whether some [data model][ref-data-modeling-concepts] facts are exposed +to end users and can be queried via [APIs & integrations][ref-apis]. + +Row-level security in Cube is similar to row-level security in SQL databases. +Defining whether users have access to specific facts from [cubes][ref-cubes] and +[views][ref-views] is similar to defining access to rows in database tables. + +__By default, all rows are *public*,__ meaning that no filtering is applied to +data model facts when they are accessed by any users. + +## Managing row-level access + +You can implement row-level access control by applying additional filters conditionally +in the [`query_rewrite` configuration option][ref-query-rewrite]. + +### Dynamic data models + +You can implement row-level access control at the data model level +[dynamically][ref-dynamic-data-modeling] by adjusting the [`sql` parameter][ref-cubes-sql] +of cubes. + +## Best practices + +### Data access policies + +You can use [data access policies][ref-dap] to manage both [member-level][ref-mls] +and row-level security for different roles. With them, you can define access control +rules in data model files instead of mixing them together in a single block of code +in `query_rewrite`. + +__It is recommended to use data access policies by default.__ You can also combine +them with using your own code in `query_rewrite` for specific cases. + + +[ref-data-modeling-concepts]: /product/data-modeling/concepts +[ref-apis]: /product/apis-integrations +[ref-cubes]: /product/data-modeling/concepts#cubes +[ref-views]: /product/data-modeling/concepts#views +[ref-cubes-sql]: /reference/data-model/cube#sql +[ref-dynamic-data-modeling]: /product/data-modeling/dynamic +[ref-query-rewrite]: /reference/configuration/config#query_rewrite +[ref-dap]: /product/auth/data-access-policies +[ref-mls]: /product/auth/member-level-security \ No newline at end of file diff --git a/docs/pages/product/data-modeling/dynamic/jinja.mdx b/docs/pages/product/data-modeling/dynamic/jinja.mdx index be3ae80634422..135a60b3afb90 100644 --- a/docs/pages/product/data-modeling/dynamic/jinja.mdx +++ b/docs/pages/product/data-modeling/dynamic/jinja.mdx @@ -245,10 +245,6 @@ If you'd like to split your Python code into several files, see -### Inline expressions - -TODO - [jinja]: https://jinja.palletsprojects.com/ [jinja-docs]: https://jinja.palletsprojects.com/en/3.1.x/templates/ diff --git a/docs/pages/product/workspace/sso.mdx b/docs/pages/product/workspace/sso.mdx index c452cf60b73a5..58d99656b4214 100644 --- a/docs/pages/product/workspace/sso.mdx +++ b/docs/pages/product/workspace/sso.mdx @@ -125,11 +125,11 @@ Additionally, the user always assumes the role specified under Default Clou -All roles will be available under `cloud.roles` array in the [security context][ref-security-context]: +All roles will be available under `cubeCloud.roles` array in the [security context][ref-security-context]: ```json { - "cloud": { + "cubeCloud": { "roles": [ "Everyone", "manager" @@ -143,12 +143,12 @@ All roles will be available under `cloud.roles` array in the [security context][ You can also bring more user data from an LDAP catalog to use with [data access policies][ref-dap]. Mapping is performed using the rules under Attribute mapping. -All mapped attributes and their values will be available under `cloud.userAttributes` +All mapped attributes and their values will be available under `cubeCloud.userAttributes` dictionary in the [security context][ref-security-context]: ```json { - "cloud": { + "cubeCloud": { "userAttributes": { "fullName": "John Doe", "department": "Finance", diff --git a/docs/pages/reference/data-model/data-access-policies.mdx b/docs/pages/reference/data-model/data-access-policies.mdx index 580133f4fd08a..891189955adad 100644 --- a/docs/pages/reference/data-model/data-access-policies.mdx +++ b/docs/pages/reference/data-model/data-access-policies.mdx @@ -12,8 +12,8 @@ can be configured using the following parameters: to. - [`conditions`](#conditions) can be optionally used to specify when a policy takes effect. -- [`member_level`](#member-level) and [`row_level`](#row-level) are used to -configure [member-level][ref-dap-mls] and [row-level][ref-dap-rls] access. +- [`member_level`](#member-level) and [`row_level`](#row-level) parameters are used +to configure [member-level][ref-dap-mls] and [row-level][ref-dap-rls] access. ### `role` @@ -26,12 +26,15 @@ In the following example, three access policies are defined, with the first one applying to all users and two other applying to users with `marketing` or `finance` roles, respectively. + + ```yaml cubes: - name: orders # ... access_policy: + # Applies to any role - role: "*" # ... @@ -42,19 +45,43 @@ cubes: # ... ``` +```javascript +cube(`orders`, { + // ... + + access_policy: [ + { + // Applies to any role + role: `*`, + // ... + }, + { + role: `marketing`, + // ... + }, + { + role: `finance`, + // ... + } + ] +}) +``` + + + ### `conditions` The optional `conditions` parameter, when present, defines a list of conditions that should all be `true` in order for a policy to take effect. Each condition is -configured with an `if` parameter that is expected to reference the security -context via an [inline expression][ref-python-inline-expressions]. - -TODO: JavaScript??? Maybe it should be in Syntax, not in Python. +configured with an `if` parameter that is expected to reference the [security +context][ref-sec-ctx]. In the following example, a permissive policy for all roles will only apply to EMEA-based users, as determined by the `is_EMEA_based` attribute in the security context: + + ```yaml cubes: - name: orders @@ -67,6 +94,27 @@ cubes: member_level: includes: "*" ``` + +```javascript +cube(`orders`, { + // ... + + access_policy: [ + { + role: `*`, + conditions: [ + { if: securityContext.is_EMEA_based } + ], + member_level: { + includes: `*` + } + } + ] +}) +``` + + + You can use the `conditions` parameter to define multiple policies for the same role. @@ -75,11 +123,13 @@ to managers who are full-time employees while the other one provides access to _all members_ to managers who are full-time employees and have also completed a data privacy training: + ```yaml cubes: - name: orders # ... + access_policy: - role: manager conditions: @@ -88,6 +138,7 @@ cubes: includes: - status - count + - role: manager conditions: - if: "{ securityContext.is_full_time_employee }" @@ -96,71 +147,208 @@ cubes: includes: "*" ``` +```javascript +cube(`orders`, { + // ... + + access_policy: [ + { + role: `manager`, + conditions: [ + { if: securityContext.is_full_time_employee } + ], + member_level: { + includes: [ + `status`, + `count` + ] + } + }, + { + role: `manager`, + conditions: [ + { if: securityContext.is_full_time_employee }, + { if: securityContext.has_completed_privacy_training } + ], + member_level: { + includes: `*` + } + } + ] +}) +``` + + + ### `member_level` The optional `member_level` parameter, when present, configures [member-level -access][ref-dap-mls] for a policy. You can provide the lists of allowed and -disallowed members with `includes` and `excludes` parameters, respectively. -You can also use the _all members_ shorthand with both of these paramaters: -`includes: "*"`, `excludes: "*"`. +access][ref-dap-mls] for a policy by specifying allowed or disallowed members. + +You can either provide a list of allowed members with the `includes` parameter, +or a list of disallowed members with the `excludes` parameter. There's also the +_all members_ shorthand for both of these paramaters: `includes: "*"`, `excludes: "*"`. In the following example, member-level access is configured this way: -| Users | Accessible members | +| Scope | Access | | --- | --- | -| Users with the `admin` role | All members except for `count` | | Users with the `manager` role | All members except for `count` | | Users with the `observer` role | All members except for `count` and `count_7d` | | Users with the `guest` role | Only the `count_30d` measure | | All other users | No access to this cube at all | + + ```yaml cubes: - name: orders # ... - measures: - - name: count - type: count - public: false - - name: count_7d - type: count - rolling_window: - trailing: 7 days - - name: count_30d - type: count - rolling_window: - trailing: 30 days + access_policy: - role: "*" member_level: + # Includes nothing, i.e., excludes all members includes: [] - - role: admin - - role: manager member_level: - includes: "*" + # Includes all members except for `count` + excludes: + - count - role: observer member_level: + # Includes all members except for `count` and `count_7d` excludes: + - count - count_7d - role: guest + # Includes only `count_30d`, excludes all other members member_level: includes: - count_30d ``` -Note that access policies respect [member-level security][ref-mls] restrictions +```javascript +cube(`orders`, { + // ... + + access_policy: [ + { + role: `*`, + // Includes nothing, i.e., excludes all members + member_level: { + includes: [] + } + }, + { + role: `manager`, + // Includes all members except for `count` + member_level: { + excludes: [ + `count` + ] + } + }, + { + role: `observer`, + // Includes all members except for `count` and `count_7d` + member_level: { + excludes: [ + `count`, + `count_7d` + ] + } + }, + { + role: `guest`, + // Includes only `count_30d`, excludes all other members + member_level: { + includes: [ + `count_30d` + ] + } + } + ] +}) +``` + + + +Note that access policies also respect [member-level security][ref-mls] restrictions configured via `public` parameters. See [member-level access][ref-dap-mls] to learn more about policy evaluation. ### `row_level` -TODO: `filters`, `or` +The optional `row_level` parameter, when present, configures [row-level +access][ref-dap-rls] for a policy by specifying `filters` that should apply to result set rows. +You can also use the optional `allow_all` parameter to explicitly allow or disallow all rows. + +In the following example, users with the `manager` role are allowed to access only +rows that have the `state` dimension matching the state from the [security context][ref-sec-ctx]. +All other users are disallowed from accessing any rows at all. + + + +```yaml +cubes: + - name: orders + # ... + + access_policy: + - role: "*" + row_level: + allow_all: false + + - role: manager + row_level: + filters: + - member: state + operator: eq + values: [ "{ securityContext.state }" ] +``` + +```javascript +cube(`orders`, { + // ... + + access_policy: [ + { + role: `*`, + row_level: { + allow_all: false + } + }, + { + role: `manager`, + row_level: { + filters: [ + { + member: `state`, + operator: `equals`, + values: [ securityContext.state ] + } + ] + } + } + ] +}) +``` + +For convenience, row filters are configured using the same format as [filters in +REST API][ref-rest-query-filters] queries, allowing to use the same set of +[filter operators][ref-rest-query-ops], e.g., `equals`, `contains`, `gte`, etc. +You can also use `and` and `or` parameters to combine multiple filters into +[boolean logical operators][ref-rest-boolean-ops]. + +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. [ref-ref-cubes]: /reference/data-model/cube @@ -170,5 +358,9 @@ TODO: `filters`, `or` [ref-dap-mls]: /product/auth/data-access-policies#member-level-access [ref-dap-rls]: /product/auth/data-access-policies#row-level-access [ref-context-to-roles]: /reference/configuration/config#context_to_roles -[ref-python-inline-expressions]: /product/data-modeling/dynamic/jinja#inline-expressions -[ref-mls]: /product/auth/member-level-security \ No newline at end of file +[ref-mls]: /product/auth/member-level-security +[ref-rls]: /product/auth/row-level-security +[ref-sec-ctx]: /product/auth/context +[ref-rest-query-filters]: /product/apis-integrations/rest-api/query-format#filters-format +[ref-rest-query-ops]: /product/apis-integrations/rest-api/query-format#filters-operators +[ref-rest-boolean-ops]: /product/apis-integrations/rest-api/query-format#boolean-logical-operators \ No newline at end of file diff --git a/docs/pages/reference/data-model/hierarchies.mdx b/docs/pages/reference/data-model/hierarchies.mdx index 98bcb8abe3b40..bcc4c382dcd39 100644 --- a/docs/pages/reference/data-model/hierarchies.mdx +++ b/docs/pages/reference/data-model/hierarchies.mdx @@ -301,6 +301,6 @@ cubes: [ref-ref-cubes]: /reference/data-model/cube [ref-ref-dimensions]: /reference/data-model/dimensions [ref-naming]: /product/data-modeling/syntax#naming -[ref-apis-support]: /product/apis-integrations#support-for-data-modeling +[ref-apis-support]: /product/apis-integrations#data-modeling [ref-playground]: /product/workspace/playground#viewing-the-data-model [ref-viz-tools]: /product/configuration/visualization-tools \ No newline at end of file diff --git a/docs/pages/reference/data-model/view.mdx b/docs/pages/reference/data-model/view.mdx index 9d800f967c36a..fe7bb50f74b45 100644 --- a/docs/pages/reference/data-model/view.mdx +++ b/docs/pages/reference/data-model/view.mdx @@ -396,6 +396,6 @@ views: [ref-apis]: /product/apis-integrations [ref-ref-cubes]: /reference/data-model/cube [ref-ref-dap]: /reference/data-model/data-access-policies -[ref-apis-support]: /product/apis-integrations#support-for-data-modeling +[ref-apis-support]: /product/apis-integrations#data-modeling [ref-playground]: /product/workspace/playground#viewing-the-data-model [ref-viz-tools]: /product/configuration/visualization-tools \ No newline at end of file