From 4f211c2f9f0948b88f610c8f4e68d5c02526b9d6 Mon Sep 17 00:00:00 2001 From: Sebastian Van Syckel Date: Tue, 21 Jul 2026 12:32:58 +0200 Subject: [PATCH 1/4] Node.js: clarify instance-based auth --- guides/security/authorization.md | 42 ++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/guides/security/authorization.md b/guides/security/authorization.md index 3b64d1331..2f8722c1e 100644 --- a/guides/security/authorization.md +++ b/guides/security/authorization.md @@ -257,14 +257,22 @@ Here, users can read and write orders they've created, and `Auditor` users can r Restrictions can be defined on different types of CDS resources, but there are some limitations with regards to supported privileges: -| CDS Resource | `grant` | `to` | `where` | Remark | -|-----------------|:-------:|:----:|:-----------------:|---------------| -| service | | | | = `@requires` | -| entity | | | 1 | | -| action/function | | | 2 | = `@requires` | - -> 1For bound actions and functions that are not bound against a collection, Node.js supports instance-based authorization at the entity level. For example, you can use `where` clauses that *contain references to the model*, such as `where: CreatedBy = $user`. For all bound actions and functions, Node.js supports simple static expressions at the entity level that *don't have any reference to the model*, such as `where: $user.level = 2`. -> 2 For unbound actions and functions, Node.js supports simple static expressions that *don't have any reference to the model*, such as `where: $user.level = 2`. +| CDS Resource | `grant` | `to` | `where` | Remark | +|-----------------------|:-------:|:----:|:-----------------:|---------------| +| service | | | | = `@requires` | +| entity | | | | | +| bound action/function | | | 1 | = `@requires` | +| action/function | | | 2 | = `@requires` | + +> 1 For [bound actions and functions](../../cds/cdl#bound-actions) that *are not bound to a collection of instances*, Node.js supports instance-based authorization. +> Example: +> ```cds +> entity Orders @(restrict: [ +> { grant: 'cancel', where: (CreatedBy = $user) }, +> ]) {/*...*/} +> ``` + +> 2 For (unbound) actions and functions, Node.js supports simple static expressions that *don't have any reference to the model*, such as `where: $user.level = 2`. Unsupported privilege properties are ignored by the runtime. Especially, for bound or unbound actions, the `grant` property is implicitly removed (assuming `grant: '*'` instead). The same also holds for functions: @@ -314,7 +322,7 @@ The resulting authorizations are illustrated in the following access matrix: | `CustomerService.Orders` (*) | | 1 | | | | `CustomerService.monthlyBalance` | | | | | -> 1 A `Vendor` user can only access the instances that they created.
+> 1 A `Customer` user can only access the instances that they created.
The example models access rules for different roles in the same service. In general, this is _not recommended_ due to the high complexity. See [best practices](#dedicated-services) for information about how to avoid this. @@ -440,13 +448,11 @@ This means that, the condition applies to following standard CDS events only: - `UPDATE` (as reject condition) - `DELETE` (as reject condition) -
- -In addition, the runtime [checks the filter condition of the input data](#input-data-auth) for following standard CDS events: +The Java runtime additionally [checks the filter condition of the input data](#input-data-auth) for following standard CDS events: - `CREATE` (input filter) - `UPDATE` (input filer) -
+The Node.js runtime, on the other hand, supports simple static expressions that *don't have any reference to the model* (e.g., `where: $user.level = 2`) for `CREATE` as well as unbound actions and functions. You can define filter conditions in the `where`-clause of restrictions based on [CQL](/cds/cql)-predicates, declared as [compiler expressions](../../cds/cdl#expressions-as-annotation-values): @@ -609,12 +615,13 @@ Paths on 1:n associations (`Association to many`) evaluate to `true`, _if the co
+// TODO: Node.js does not support his?
-### Checking Input Data { #input-data-auth .java} +### Checking Input Data (Java only) { #input-data-auth } Input data of `CREATE` and `UPDATE` events is also validated with regards to instance-based authorization conditions. Invalid input that does not meet the condition is rejected with response code `400`. @@ -633,7 +640,12 @@ Starting with CAP Java `4.0`, deep authorization is active by default. It can be disabled by setting cds.security.authorization.instanceBased.checkInputData: false. -### Rejected Entity Selection { #reject-403 .java} +### Simple Static Checks (Node.js only) { #simple-static-checks } + +TODO + + +### Rejected Entity Selection { #reject-403 } //> TODO: Node.js? Entities that have an instance-based authorization condition, that is [`@restrict.where`](/guides/security/authorization#restrict-annotation), are guarded by the CAP Java runtime by adding a filter condition to the DB query **excluding not matching instances from the result**. From cf490c17128cc6a93a07f5d2bfd1cea8f40cb466 Mon Sep 17 00:00:00 2001 From: Sebastian Van Syckel Date: Tue, 21 Jul 2026 13:03:43 +0200 Subject: [PATCH 2/4] restore toggle --- guides/security/authorization.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/guides/security/authorization.md b/guides/security/authorization.md index 2f8722c1e..ce9294856 100644 --- a/guides/security/authorization.md +++ b/guides/security/authorization.md @@ -448,11 +448,19 @@ This means that, the condition applies to following standard CDS events only: - `UPDATE` (as reject condition) - `DELETE` (as reject condition) -The Java runtime additionally [checks the filter condition of the input data](#input-data-auth) for following standard CDS events: +
+ +In addition, the Java runtime [checks the filter condition of the input data](#input-data-auth) for following standard CDS events: - `CREATE` (input filter) - `UPDATE` (input filer) -The Node.js runtime, on the other hand, supports simple static expressions that *don't have any reference to the model* (e.g., `where: $user.level = 2`) for `CREATE` as well as unbound actions and functions. +
+ +
+ +In addition, for `CREATE` as well as unbound actions and functions, the Node.js runtime supports simple static expressions that *don't have any reference to the model*, such as `where: $user.level = 2`. + +
You can define filter conditions in the `where`-clause of restrictions based on [CQL](/cds/cql)-predicates, declared as [compiler expressions](../../cds/cdl#expressions-as-annotation-values): From c110fde24f28cf299fa63a30c95ccfed50b517be Mon Sep 17 00:00:00 2001 From: Sebastian Van Syckel Date: Tue, 21 Jul 2026 13:05:48 +0200 Subject: [PATCH 3/4] more toggles --- guides/security/authorization.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/guides/security/authorization.md b/guides/security/authorization.md index ce9294856..1e57b709d 100644 --- a/guides/security/authorization.md +++ b/guides/security/authorization.md @@ -629,7 +629,7 @@ Paths on 1:n associations (`Association to many`) evaluate to `true`, _if the co
-### Checking Input Data (Java only) { #input-data-auth } +### Checking Input Data { #input-data-auth .java} Input data of `CREATE` and `UPDATE` events is also validated with regards to instance-based authorization conditions. Invalid input that does not meet the condition is rejected with response code `400`. @@ -648,12 +648,13 @@ Starting with CAP Java `4.0`, deep authorization is active by default. It can be disabled by setting cds.security.authorization.instanceBased.checkInputData: false. -### Simple Static Checks (Node.js only) { #simple-static-checks } +### Simple Static Checks { #simple-static-checks .node} TODO -### Rejected Entity Selection { #reject-403 } //> TODO: Node.js? +//> TODO: Node.js? +### Rejected Entity Selection { #reject-403 .java} Entities that have an instance-based authorization condition, that is [`@restrict.where`](/guides/security/authorization#restrict-annotation), are guarded by the CAP Java runtime by adding a filter condition to the DB query **excluding not matching instances from the result**. From 9ebc788f9bf762da109b842b8b41c68edeae9f17 Mon Sep 17 00:00:00 2001 From: Sebastian Van Syckel Date: Tue, 21 Jul 2026 13:23:39 +0200 Subject: [PATCH 4/4] Simple Static Checks --- guides/security/authorization.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/guides/security/authorization.md b/guides/security/authorization.md index 1e57b709d..c78099e6d 100644 --- a/guides/security/authorization.md +++ b/guides/security/authorization.md @@ -650,7 +650,16 @@ It can be disabled by setting cds.security.authorization.instanceBa ### Simple Static Checks { #simple-static-checks .node} -TODO +Most instance-based [`@restrict.where`](#restrict-annotation) conditions reference business data (for example, `where: 'createdBy = $user'`) and can only be enforced against persisted data — pushed into the query for `READ`, or verified with a `COUNT` for `UPDATE`/`DELETE`. + +Some conditions, though, reduce to a plain comparison of literals once [user attributes](#user-attrs) are resolved: + +```cds +entity Reviews @(restrict: [ + { grant: 'CREATE', where: '$user.level >= 2' } ]); +``` + +For a user with `level = 3`, this becomes `3 >= 2`, which the runtime evaluates in memory — granting or rejecting with `403` without any database access. Such _simple static checks_ apply to `CREATE` (and its draft variant `NEW`) and to unbound actions and functions, where there's no persisted instance to query. They're only recognized for a single binary comparison (`=`, `!=`, `<`, `<=`, `>`, `>=`) with no reference to entity elements. //> TODO: Node.js?