diff --git a/docs/API/SessionClass.md b/docs/API/SessionClass.md
index d0ba8c42173873..990d38dd5c8872 100644
--- a/docs/API/SessionClass.md
+++ b/docs/API/SessionClass.md
@@ -43,6 +43,7 @@ The availability of properties and functions in the `Session` object depends on
|---|
|[](#clearprivileges)
|
|[](#createotp)
|
+|[](#demote)
|
|[](#expirationdate)
|
|[](#getprivileges)
|
|[](#hasprivilege)
|
@@ -50,6 +51,7 @@ The availability of properties and functions in the `Session` object depends on
|[](#idletimeout)
|
|[](#info)
|
|[](#isguest)
|
+|[](#promote)
|
|[](#restore)
|
|[](#setprivileges)
|
|[](#storage)
|
@@ -86,15 +88,16 @@ This function does nothing and always returns **True** with remote client, store
:::
-The `.clearPrivileges()` function removes all the privileges associated to the session and returns **True** if the execution was successful. Unless in ["forceLogin" mode](../REST/authUsers.md#force-login-mode), the session automatically becomes a Guest session.
+The `.clearPrivileges()` function removes all the privileges associated to the session (excluding promoted privileges) and returns **True** if the execution was successful.
+
+Unless in ["forceLogin" mode](../REST/authUsers.md#force-login-mode), the session automatically becomes a Guest session. In "forceLogin" mode, `.clearPrivileges()` does not transform the session to a Guest session, it only clears the session's privileges.
:::note
-In "forceLogin" mode, `.clearPrivileges()` does not transform the session to a Guest session, it only clears the session's privileges.
+This function does not remove **promoted privileges** from the web process, whether they are added through the [roles.json](../ORDA/privileges.md#rolesjson-file) file or the [`promote()`](#promote) function.
:::
-
#### Example
```4d
@@ -158,6 +161,74 @@ $token := Session.createOTP( 60 ) //the token is valid for 1 mn
+
+## .demote()
+
+History
+
+|Release|Changes|
+|---|---|
+|20 R10|Added|
+
+
+
+**.demote**( *promoteId* : Integer )
+
+
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|promoteId|Integer|->|Id returned by the `promote()` function|
+
+
+#### Description
+
+:::note
+
+This function does nothing in remote client, stored procedure, and standalone sessions.
+
+:::
+
+The `.demote()` function removes the promoted privilege whose id you passed in *promoteId* from the web process, if it was previously added by the [`.promote()`](#promote) function.
+
+If no privilege with *promoteId* was promoted using [`.promote()`](#promote) in the web process, the function does nothing.
+
+If several privileges have been added to the web process, the `demote()` function must be called for each one with the appropriate *promoteId*. Privileges are stacked in the order they have been added to the process, it is recommended to unstack privileges in a LIFO (*Last In, First Out*) order.
+
+
+#### Example
+
+
+```4d
+exposed Function search($search : Text) : Collection
+
+ var $employees : Collection
+ var $promoteId1; $promoteId2 : Integer
+
+ $promoteId1:=Session.promote("admin")
+ $promoteId2:=Session.promote("superAdmin")
+
+ $search:="@"+$search+"@"
+
+ $employees:=This.query("type = :1 and lastname = :2"; "Intern"; $search).toCollection()
+
+ Session.demote($promoteId2)
+ Session.demote($promoteId1)
+
+ return $employees
+```
+
+
+#### See also
+
+[`.promote()`](#promote)
+
+
+
+
+
+
+
## .expirationDate
@@ -217,15 +288,13 @@ $expiration:=Session.expirationDate //eg "2021-11-05T17:10:42Z"
The `.getPrivileges()` function returns a collection of all the privilege names associated to the session.
-With remote client, stored procedure and standalone sessions, this function returns a collection only containing "WebAdmin".
-
-
-:::info
+:::note
-Privileges are assigned to a Session using the [`setPrivileges()`](#setprivileges) function.
+This function returns privileges assigned to a Session using the [`setPrivileges()`](#setprivileges) function only. Promoted privileges are NOT returned by the function, whether they are added through the [roles.json](../ORDA/privileges.md#rolesjson-file) file or the [`promote()`](#promote) function.
:::
+With remote client, stored procedure and standalone sessions, this function returns a collection only containing "WebAdmin".
#### Example
@@ -301,6 +370,7 @@ $privileges := Session.getPrivileges()
|Release|Changes|
|---|---|
+|21|Returns True for promoted privileges|
|18 R6|Added|
@@ -321,6 +391,12 @@ $privileges := Session.getPrivileges()
The `.hasPrivilege()` function returns True if the *privilege* is associated to the session, and False otherwise.
+:::note
+
+This function returns True for the *privilege* if called from a function that was promoted for this privilege (either through the [roles.json](../ORDA/privileges.md#rolesjson-file) file or the [`promote()`](#promote) function).
+
+:::
+
With remote client, stored procedure and standalone sessions, this function always returns True, whatever the *privilege*.
@@ -523,6 +599,87 @@ End if
+
+## .promote()
+
+History
+
+|Release|Changes|
+|---|---|
+|20 R10|Added|
+
+
+
+**.promote**( *privilege* : Text ) : Integer
+
+
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|privilege|Text|->|Privilege name|
+|Result|Integer|<-|id to use when calling the [`demote()`](#demote) function|
+
+
+#### Description
+
+:::note
+
+This function does nothing in remote client, stored procedure, and standalone sessions.
+
+:::
+
+The `.promote()` function adds the privilege defined in the *privilege* parameter to the current process during the execution of the calling function and returns the id of the promoted privilege.
+
+Dynamically adding privileges is useful when access rights depend on the execution context, which cannot be fully defined in the "roles.json" file. This is particularly relevant when the same function can be executed by users with different access levels. The use of `.promote()` ensures that only the current process is granted the necessary privileges, without affecting others.
+
+The function does nothing and returns 0 if:
+- the *privilege* does not exist in the [`roles.json`](../ORDA/privileges.md#rolesjson-file) file,
+- the *privilege* is already assigned to the current process (using `.promote()` or through a static [promote action](../ORDA/privileges.md#permission-actions) declared for the calling function in the [`roles.json`](../ORDA/privileges.md#rolesjson-file) file).
+
+You can call the `promote()` function several times in the same process to add different privileges.
+
+The returned id is incremented each time a privilege is dynamically added to the process.
+
+
+To remove a privilege dynamically, call the `demote()` function with the appropriate id.
+
+
+#### Example
+
+Several users connect to a single endpoint that serves different applications. A user from application #1 does not need the "super_admin" privilege because they don't create "VerySensitiveInfo". A user from application #2 needs "super_admin" privilege.
+
+You can dynamically provide appropriate privileges in the *CreateInfo* function:
+
+```4d
+exposed Function createInfo($info1 : Text; $info2 : Text)
+
+var $sensitive : cs.SensitiveInfoEntity
+var $verySensitiveInfo : cs.VerySensitiveInfoEntity
+var $status : Object
+var $promoteId : Integer
+
+$sensitive:=ds.SensitiveInfo.new()
+$sensitive.info:=$info1
+$status:=$sensitive.save()
+
+If (Session.storage.role.name="userApp2")
+ $promoteId:=Session.promote("super_admin")
+ $verySensitiveInfo:=ds.VerySensitiveInfo.new()
+ $verySensitiveInfo.info:=$info2
+ $status:=$verySensitiveInfo.save()
+ Session.demote($promoteId)
+End if
+```
+
+
+#### See also
+
+[`.demote()`](#demote)
[`hasPrivilege()`](#hasprivilege)
+
+
+
+
+
## .restore()
diff --git a/docs/Notes/updates.md b/docs/Notes/updates.md
index 8061ea510025da..ff486687645981 100644
--- a/docs/Notes/updates.md
+++ b/docs/Notes/updates.md
@@ -40,6 +40,7 @@ Read [**What’s new in 4D 20 R10**](https://blog.4d.com/en-whats-new-in-4d-20-R
- New [`4D.Vector`](../API/VectorClass.md) class to process and compare vectors, usually calculated by AIs.
- New options to generate UUIDs in **version 7** for [4D automatic fields](../settings/database.md#auto-uuid-version) and [`Generate UUID`](../commands/generate-uuid) command.
- New [`UDPSocket`](../API/UDPSocketClass.md) and [`UDPEvent`](../API/UDPEventClass.md) classes to send data using UDP sockets. Support of detailed logging for UDP events in the [`4DTCPUDPLog.txt`](../Debugging/debugLogFiles.md#4dtcpudplogtxt) log file (renamed from `4DTCPLog.txt`).
+- New [`.promote()`](../API/SessionClass.md#promote) and [`.demote()`](../API/SessionClass.md#demote) functions in the [Session class](../API/SessionClass.md) to dynamically add/remove privileges in a web process.
- [Automatic selection of licenses to embed](../Desktop/building.md#application-automatically-embedding-available-licenses) in the Build application dialog box, modified [`Create deployment license`](../commands/create-deployment-license.md) command, new [`AutomaticLicenseIntegration`](https://doc.4d.com/4Dv20R10/4D/20-R10/AutomaticLicenseIntegration.300-7611090.en.html) BuildApplication xml key.
- Enhanced security for formula copy/paste in [4D Write Pro](../WritePro/managing-formulas.md) and [styled text areas](../FormObjects/input_overview.md): formulas copied from outside the current 4D application are now always pasted as values only.
- 4D AIKit component: new [OpenAIEmbeddingsAPI class](../aikit/Classes/OpenAIEmbeddingsAPI.md) to create embeddings using OpenAI's API.
@@ -57,6 +58,7 @@ Read [**What’s new in 4D 20 R10**](https://blog.4d.com/en-whats-new-in-4d-20-R
- On Windows, current printer fonts intended only for printing (i.e. not usable on screen) are no longer loaded by 4D at startup.
- The *MeCab* library has been removed. This change only affects the processing of Japanese text.
- When an object variable or parameter is declared with a ["cs" class](../Concepts/classes.md#cs) type, assigning it with an object instance of a different class now generates a syntax error.
+- [`.hasPrivilege()`](../API/SessionClass.md#hasprivilege) returns True for promoted privileges in the web process.
- The [`Time`](../commands/time) command now returns a negative time expression when the *timeValue* parameter is negative. For instance, `Time("-01:02:03")` will return **-01:02:03**. In previous releases, the negative sign was ignored.
diff --git a/docs/ORDA/privileges.md b/docs/ORDA/privileges.md
index 91dabb13a5f496..5ba37761607d18 100644
--- a/docs/ORDA/privileges.md
+++ b/docs/ORDA/privileges.md
@@ -61,16 +61,19 @@ Available actions are related to target resource.
|**update**|Update attributes in any dataclass. |Update attributes in this dataclass.|Update this attribute content (ignored for alias attributes).|n/a|
|**drop**|Delete data in any dataclass. |Delete data in this dataclass. |Delete a not null value for this attribute (except for alias and computed attribute).|n/a|
|**execute**|Execute any function on the project (datastore, dataclass, entity selection, entity, singleton)|Execute any function on the dataclass. Dataclass functions, entity functions, and entity selection functions are handled as dataclass functions|n/a|Execute this function|
-|**promote**|n/a|n/a|n/a|Associates a given privilege during the execution of the function. The privilege is temporary added to the session and removed at the end of the function execution. By security, only the process executing the function is added the privilege, not the whole session.|
+|**promote**|n/a|n/a|n/a|Associates a given privilege during the execution of the function. The privilege is temporary added and removed at the end of the function execution. By security, only the process executing the function is added the privilege, not the whole session.|
-**Notes:**
+:::note Notes
-- An alias can be read as soon as the session privileges allow the access to the alias itself, even if the session privileges do no allow the access to the attributes resolving the alias.
-- A computed attribute can be accessed even if there are no permissions on the attributes upon which it is built.
+- An [alias](./ordaClasses.md#alias-attributes-1) can be read as soon as the session privileges allow the access to the alias itself, even if the session privileges do no allow the access to the attributes resolving the alias.
+- A [computed attribute](./ordaClasses.md#computed-attributes-1) can be accessed even if there are no permissions on the attributes upon which it is built.
- You can assign a permission action to a singleton class (`singleton` type), in which case it will be applied to all its exposed functions, or to a singleton function (`singletonMethod` type).
+- You can set/remove the **promote** action dynamically to a web process using the [`promote()`](../API/SessionClass.md#promote) and [`demote()`](../API/SessionClass.md#demote) functions.
- Default values: in the current implementation, only *Null* is available as default value.
- In REST [force login mode](../REST/authUsers.md#force-login-mode), the [`authentify()` function](../REST/authUsers.md#function-authentify) is always executable by guest users, whatever the permissions configuration.
+:::
+
Setting permissions requires to be consistent, in particular **update** and **drop** permissions also need **read** permission (but **create** does not need it).
diff --git a/versioned_docs/version-20-R10/API/SessionClass.md b/versioned_docs/version-20-R10/API/SessionClass.md
index ece8c7c778dccc..ffb0365de739cd 100644
--- a/versioned_docs/version-20-R10/API/SessionClass.md
+++ b/versioned_docs/version-20-R10/API/SessionClass.md
@@ -33,6 +33,7 @@ The availability of properties and functions in the `Session` object depends on
|---|
|[](#clearprivileges)
|
|[](#createotp)
|
+|[](#demote)
|
|[](#expirationdate)
|
|[](#getprivileges)
|
|[](#hasprivilege)
|
@@ -40,6 +41,7 @@ The availability of properties and functions in the `Session` object depends on
|[](#idletimeout)
|
|[](#info)
|
|[](#isguest)
|
+|[](#promote)
|
|[](#restore)
|
|[](#setprivileges)
|
|[](#storage)
|
@@ -76,15 +78,16 @@ This function does nothing and always returns **True** with remote client, store
:::
-The `.clearPrivileges()` function removes all the privileges associated to the session and returns **True** if the execution was successful. Unless in ["forceLogin" mode](../REST/authUsers.md#force-login-mode), the session automatically becomes a Guest session.
+The `.clearPrivileges()` function removes all the privileges associated to the session (excluding promoted privileges) and returns **True** if the execution was successful.
+
+Unless in ["forceLogin" mode](../REST/authUsers.md#force-login-mode), the session automatically becomes a Guest session. In "forceLogin" mode, `.clearPrivileges()` does not transform the session to a Guest session, it only clears the session's privileges.
:::note
-In "forceLogin" mode, `.clearPrivileges()` does not transform the session to a Guest session, it only clears the session's privileges.
+This function does not remove **promoted privileges** from the web process, whether they are added through the [roles.json](../ORDA/privileges.md#rolesjson-file) file or the [`promote()`](#promote) function.
:::
-
#### Example
```4d
@@ -148,6 +151,74 @@ $token := Session.createOTP( 60 ) //the token is valid for 1 mn
+
+## .demote()
+
+History
+
+|Release|Changes|
+|---|---|
+|20 R10|Added|
+
+
+
+**.demote**( *promoteId* : Integer )
+
+
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|promoteId|Integer|->|Id returned by the `promote()` function|
+
+
+#### Description
+
+:::note
+
+This function does nothing in remote client, stored procedure, and standalone sessions.
+
+:::
+
+The `.demote()` function removes the promoted privilege whose id you passed in *promoteId* from the web process, if it was previously added by the [`.promote()`](#promote) function.
+
+If no privilege with *promoteId* was promoted using [`.promote()`](#promote) in the web process, the function does nothing.
+
+If several privileges have been added to the web process, the `demote()` function must be called for each one with the appropriate *promoteId*. Privileges are stacked in the order they have been added to the process, it is recommended to unstack privileges in a LIFO (*Last In, First Out*) order.
+
+
+#### Example
+
+
+```4d
+exposed Function search($search : Text) : Collection
+
+ var $employees : Collection
+ var $promoteId1; $promoteId2 : Integer
+
+ $promoteId1:=Session.promote("admin")
+ $promoteId2:=Session.promote("superAdmin")
+
+ $search:="@"+$search+"@"
+
+ $employees:=This.query("type = :1 and lastname = :2"; "Intern"; $search).toCollection()
+
+ Session.demote($promoteId2)
+ Session.demote($promoteId1)
+
+ return $employees
+```
+
+
+#### See also
+
+[`.promote()`](#promote)
+
+
+
+
+
+
+
## .expirationDate
@@ -207,15 +278,13 @@ $expiration:=Session.expirationDate //eg "2021-11-05T17:10:42Z"
The `.getPrivileges()` function returns a collection of all the privilege names associated to the session.
-With remote client, stored procedure and standalone sessions, this function returns a collection only containing "WebAdmin".
-
-
-:::info
+:::note
-Privileges are assigned to a Session using the [`setPrivileges()`](#setprivileges) function.
+This function returns privileges assigned to a Session using the [`setPrivileges()`](#setprivileges) function only. Promoted privileges are NOT returned by the function, whether they are added through the [roles.json](../ORDA/privileges.md#rolesjson-file) file or the [`promote()`](#promote) function.
:::
+With remote client, stored procedure and standalone sessions, this function returns a collection only containing "WebAdmin".
#### Example
@@ -291,6 +360,7 @@ $privileges := Session.getPrivileges()
|Release|Changes|
|---|---|
+|21|Returns True for promoted privileges|
|18 R6|Added|
@@ -311,6 +381,12 @@ $privileges := Session.getPrivileges()
The `.hasPrivilege()` function returns True if the *privilege* is associated to the session, and False otherwise.
+:::note
+
+This function returns True for the *privilege* if called from a function that was promoted for this privilege (either through the [roles.json](../ORDA/privileges.md#rolesjson-file) file or the [`promote()`](#promote) function).
+
+:::
+
With remote client, stored procedure and standalone sessions, this function always returns True, whatever the *privilege*.
@@ -510,6 +586,87 @@ End if
+
+## .promote()
+
+History
+
+|Release|Changes|
+|---|---|
+|20 R10|Added|
+
+
+
+**.promote**( *privilege* : Text ) : Integer
+
+
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|privilege|Text|->|Privilege name|
+|Result|Integer|<-|id to use when calling the [`demote()`](#demote) function|
+
+
+#### Description
+
+:::note
+
+This function does nothing in remote client, stored procedure, and standalone sessions.
+
+:::
+
+The `.promote()` function adds the privilege defined in the *privilege* parameter to the current process during the execution of the calling function and returns the id of the promoted privilege.
+
+Dynamically adding privileges is useful when access rights depend on the execution context, which cannot be fully defined in the "roles.json" file. This is particularly relevant when the same function can be executed by users with different access levels. The use of `.promote()` ensures that only the current process is granted the necessary privileges, without affecting others.
+
+The function does nothing and returns 0 if:
+- the *privilege* does not exist in the [`roles.json`](../ORDA/privileges.md#rolesjson-file) file,
+- the *privilege* is already assigned to the current process (using `.promote()` or through a static [promote action](../ORDA/privileges.md#permission-actions) declared for the calling function in the [`roles.json`](../ORDA/privileges.md#rolesjson-file) file).
+
+You can call the `promote()` function several times in the same process to add different privileges.
+
+The returned id is incremented each time a privilege is dynamically added to the process.
+
+
+To remove a privilege dynamically, call the `demote()` function with the appropriate id.
+
+
+#### Example
+
+Several users connect to a single endpoint that serves different applications. A user from application #1 does not need the "super_admin" privilege because they don't create "VerySensitiveInfo". A user from application #2 needs "super_admin" privilege.
+
+You can dynamically provide appropriate privileges in the *CreateInfo* function:
+
+```4d
+exposed Function createInfo($info1 : Text; $info2 : Text)
+
+var $sensitive : cs.SensitiveInfoEntity
+var $verySensitiveInfo : cs.VerySensitiveInfoEntity
+var $status : Object
+var $promoteId : Integer
+
+$sensitive:=ds.SensitiveInfo.new()
+$sensitive.info:=$info1
+$status:=$sensitive.save()
+
+If (Session.storage.role.name="userApp2")
+ $promoteId:=Session.promote("super_admin")
+ $verySensitiveInfo:=ds.VerySensitiveInfo.new()
+ $verySensitiveInfo.info:=$info2
+ $status:=$verySensitiveInfo.save()
+ Session.demote($promoteId)
+End if
+```
+
+
+#### See also
+
+[`.demote()`](#demote)
[`hasPrivilege()`](#hasprivilege)
+
+
+
+
+
## .restore()
diff --git a/versioned_docs/version-20-R10/Notes/updates.md b/versioned_docs/version-20-R10/Notes/updates.md
index ca39400e0f3887..e15e1725a42550 100644
--- a/versioned_docs/version-20-R10/Notes/updates.md
+++ b/versioned_docs/version-20-R10/Notes/updates.md
@@ -15,6 +15,7 @@ Read [**What’s new in 4D 20 R10**](https://blog.4d.com/en-whats-new-in-4d-20-R
- New [`4D.Vector`](../API/VectorClass.md) class to process and compare vectors, usually calculated by AIs.
- New options to generate UUIDs in **version 7** for [4D automatic fields](../settings/database.md#auto-uuid-version) and [`Generate UUID`](../commands/generate-uuid) command.
- New [`UDPSocket`](../API/UDPSocketClass.md) and [`UDPEvent`](../API/UDPEventClass.md) classes to send data using UDP sockets. Support of detailed logging for UDP events in the [`4DTCPUDPLog.txt`](../Debugging/debugLogFiles.md#4dtcpudplogtxt) log file (renamed from `4DTCPLog.txt`).
+- New [`.promote()`](../API/SessionClass.md#promote) and [`.demote()`](../API/SessionClass.md#demote) functions in the [Session class](../API/SessionClass.md) to dynamically add/remove privileges in a web process.
- [Automatic selection of licenses to embed](../Desktop/building.md#application-automatically-embedding-available-licenses) in the Build application dialog box, modified [`Create deployment license`](../commands/create-deployment-license.md) command, new [`AutomaticLicenseIntegration`](https://doc.4d.com/4Dv20R10/4D/20-R10/AutomaticLicenseIntegration.300-7611090.en.html) BuildApplication xml key.
- Enhanced security for formula copy/paste in [4D Write Pro](../WritePro/managing-formulas.md) and [styled text areas](../FormObjects/input_overview.md): formulas copied from outside the current 4D application are now always pasted as values only.
- 4D AIKit component: new [OpenAIEmbeddingsAPI class](../aikit/Classes/OpenAIEmbeddingsAPI.md) to create embeddings using OpenAI's API.
diff --git a/versioned_docs/version-20-R10/ORDA/privileges.md b/versioned_docs/version-20-R10/ORDA/privileges.md
index 91dabb13a5f496..5ba37761607d18 100644
--- a/versioned_docs/version-20-R10/ORDA/privileges.md
+++ b/versioned_docs/version-20-R10/ORDA/privileges.md
@@ -61,16 +61,19 @@ Available actions are related to target resource.
|**update**|Update attributes in any dataclass. |Update attributes in this dataclass.|Update this attribute content (ignored for alias attributes).|n/a|
|**drop**|Delete data in any dataclass. |Delete data in this dataclass. |Delete a not null value for this attribute (except for alias and computed attribute).|n/a|
|**execute**|Execute any function on the project (datastore, dataclass, entity selection, entity, singleton)|Execute any function on the dataclass. Dataclass functions, entity functions, and entity selection functions are handled as dataclass functions|n/a|Execute this function|
-|**promote**|n/a|n/a|n/a|Associates a given privilege during the execution of the function. The privilege is temporary added to the session and removed at the end of the function execution. By security, only the process executing the function is added the privilege, not the whole session.|
+|**promote**|n/a|n/a|n/a|Associates a given privilege during the execution of the function. The privilege is temporary added and removed at the end of the function execution. By security, only the process executing the function is added the privilege, not the whole session.|
-**Notes:**
+:::note Notes
-- An alias can be read as soon as the session privileges allow the access to the alias itself, even if the session privileges do no allow the access to the attributes resolving the alias.
-- A computed attribute can be accessed even if there are no permissions on the attributes upon which it is built.
+- An [alias](./ordaClasses.md#alias-attributes-1) can be read as soon as the session privileges allow the access to the alias itself, even if the session privileges do no allow the access to the attributes resolving the alias.
+- A [computed attribute](./ordaClasses.md#computed-attributes-1) can be accessed even if there are no permissions on the attributes upon which it is built.
- You can assign a permission action to a singleton class (`singleton` type), in which case it will be applied to all its exposed functions, or to a singleton function (`singletonMethod` type).
+- You can set/remove the **promote** action dynamically to a web process using the [`promote()`](../API/SessionClass.md#promote) and [`demote()`](../API/SessionClass.md#demote) functions.
- Default values: in the current implementation, only *Null* is available as default value.
- In REST [force login mode](../REST/authUsers.md#force-login-mode), the [`authentify()` function](../REST/authUsers.md#function-authentify) is always executable by guest users, whatever the permissions configuration.
+:::
+
Setting permissions requires to be consistent, in particular **update** and **drop** permissions also need **read** permission (but **create** does not need it).