Record permission
Related information
Introduction
Use the record permission DSL (Domain Specific Language) to specify access rules on records of a given table.
The main goals of the record permission DSL are to not require Java coding, and to be easy to use by people without deep programming knowledge.
You can specify permission on any table using a script. The script consists of a sequence of if then else and return statements that indicate the permission for a record.
You can edit the script using the Data Model Assistant (DMA) .
Lexical structure
Introduction
The script has following structure:
begin
<statement 1>
<statement 2>
...
<last statement>
end
All statements except the last one must be an "if then" or an "if then else" statement.
Example:
if isMember(administrator) then
// Administrator has no restrictions.
return readWrite;
if isMember('french-team') and record.Country='F' then
//Members of 'french-team' can view and modify data for France.
return readWrite;
if isMember('us-team') and record.Country='US' then
//Members of 'us-team' can view and modify data for US.
return readWrite;
// This statement is not actually needed as 'hidden' is the default permission.
return hidden;
Character set
The Unicode character set is supported.
Character case sensitivity
The DSL is case-sensitive.
Comments
A single line comment extends from // to the end of the current line:
// This is a comment
if record.LastName = 'Doe' then // This is another comment.
return readOnly;
A multi-line comment extends from **/***and ends with */ :
/* This is a sample of a multi-line
comment */
if record.isActive then
return readWrite;
Keywords
There are two types of keyword:
-
Reserved keywords are: if , then , else , begin , end , return , null , and , or , not , true , false , \_ebx .
-
Unreserved keywords are all other keywords defined by the DSL.
The main difference between the two types of keywords is that unreserved ones, but not reserved ones, can be used as plain (unquoted) identifiers.
Identifiers
Unquoted identifier
An unquoted identifier is an unlimited-length sequence of letters, digits or underscore ( \_ ). The first character must be a letter.
Valid letters are a to z and A to Z . Valid digits are 0 to 9 .
An unquoted identifier cannot be equal to a reserved keyword.
Quoted identifiers
A quoted identifier is an unlimited length of Unicode characters
Quoted identifiers must be used surrounded by double quotes.
An unquoted identifier can be used surrounded by double quotes. This means that identifier "a\_name" is equal to a\_name.
Quoted identifiers can be reserved keywords except "\_ebx" .
Types
Supported types
The following types are supported:
| Type | EBX® corresponding types |
|---|---|
| Boolean | xs:boolean |
| Decimal | xs:decimal xs:int xs:integer |
| String | xs:string xs:anyURI xs:Name osd:text osd:html osd:email osd:password osd:color osd:resource osd:locale osd:dataspaceKey osd:datasetName |
| Timestamp | xs:dateTime |
| Date | xs:date |
| Time | xs:time |
Literals
String literal
String literals can be any sequence of Unicode characters surrounded by single quotes. The following table displays characters that need to be replaced by an escape sequence:
| Character | Escape sequence |
|---|---|
| Tab | \t |
| Backspace | \b |
| New line | \n |
| Carriage return | \r |
| Form feed | \f |
| Single quote | \' |
| Backlash | \\ |
A character can be specified by a Unicode escape sequence that has the format \uXXXX with XXXX the hexadecimal code of the Unicode character.
Examples
| Value | Syntax |
|---|---|
| O’Harra | 'O\'Harra' |
| Noël | 'No\u00EBl' |
| été | '\u00e9\u00E9' |
Note
An invalid escape or Unicode sequence generates an error at compile time.
Decimal literal
The following decimal formats are supported:
| Format | Examples |
|---|---|
| Integer | 546 -67 |
| Floating point | 54.987 -433.876 0.00054 -0.0032 |
| Exponent notation | 34.654e-5 -45E+65 1.543e23 |
Timestamp literal
Timestamp literals have the format dt(yyyy-MM-dd hh:mm:ss.sss) .
Seconds are optional. When seconds are not specified, 0 is assumed. Seconds can have fractions up to millisecond precision.
The dates that are not valid in the Gregorian calendar generate an error at compile time.
Examples:
dt(2010-01-02 00:00:00.000)
dt(2019-2-3 12:56:7)
dt(2019-2-3 12:56:7.5)
dt(2019-5-7 1:6)
Date literal
Date literals have format d(yyyy-MM-dd) .
The dates that are not valid in the Gregorian calendar generate an error at compile time.
Examples:
d(2010-01-02)
d(2019-2-3)
dt(2019-5-7)
Time literal
Time literals have the format t(hh:mm:ss.sss) .
Seconds are optional. When seconds arenot specified, 0 is assumed. Seconds can have fractions up to millisecond precision.
Invalid times generate an error at compile time.
Examples:
t(00:00:00)
t(12:56:7)
t(12:56:7.5)
t(1:6)
Boolean literal
Boolean literals are true and false .
Field access
Simple fields
Only access to optimized/indexed table field is supported. Dot notation is used to access tables fields. For example, a condition of current table fields whose path is ./OfficeAddress/City would be:
if record.OfficeAddress.City = 'Paris' then
return readWrite;
The alias record always refers to the current record. Depending on context, other aliases might be available.
Each step (parts separated by a dot) is an identifier . This means that the following quoted notation can be used for any step:
if record."OfficeAddress".City = 'Paris' then
return readWrite;
This is useful for steps equal to a reserved keyword or using characters, such as the minus character ( - ) or dot ( . ), which are not compatible with unquoted identifiers.
At runtime, any step can evaluate to null . In this case, the full field expression evaluates to null .
Foreign table fields
You can access foreign tables by "following" foreign keys using dot notation.
In the following example, the field Supervisor is a foreign key:
if record.Supervisor.Name = 'John Doe' then
return readWrite;
There can be multiple levels of foreign keys, such as in the following example:
if record.Supervisor.Supervisor.Supervisor.Name = 'John Doe' then
return readOnly;
List (multi-valued) field access
Multi-valued fields are not supported.
Associations
Aggregate functions can take as input expressions based on an association. In the following example, the field ManagedUsers is an association:
// All users that manage at least 2 persons have read write access.
if count(record.ManagedUsers[]) >= 2 then
return readWrite;
You can apply a filter on an association. You must use an alias to access fields from the association. An association alias is declared using : . In the following example, the alias is u1 :
// All users that manage at least one person whose office is in Briton has read only access.
if exists(record.ManagedUsers:u1[u1.OfficeAddress.City='Briton']) then
return readOnly;
A filter on an association can reference a field of the current record:
// All users that manage at least one person whose office is in the same city as user has read only access.
if exists(record.ManagedUsers:t1[t1.OfficeAddress.City=record.OfficeAddress.City]) then
return readOnly;
A filter on an association can include embedded aggregate functions:
// All users that manage at least one person who manages at least one person has read only access.
if exists(record.ManagedUsers:t1[exists(t1.ManagedUsers[])]) then
return readOnly;
A filter on an association on an embedded aggregate function can reference fields of the current record and fields of parent associations:
// All users that manage at least one person who manages at least one person whose office is in the same city
// as his/her two first managers in the hierarchy has read only access.
if exists(record.ManagedUsers:t1[exits(t1.ManagedUsers:t2[t2.OfficeAddress.City=record.OfficeAddress.City and t2.OfficeAddress.City=t1.OfficeAddress.City])]) then
return readOnly;
Operators
By default, operation evaluation order is based on precedence and associativity. The order of evaluation can be indicated explicitly by using parentheses.
The following table shows all operators from highest to lowest precedence and their associativity:
Null value management
Arithmetic operators
An arithmetic operator ( *, / , + and - ) returns null if any operand is null .
Comparison operators
A Comparison operator ( < , <= , > , => , = and <> ) returns null if any operand is null .
Boolean operators
Boolean operators use thread-value logic.
Truth table for and is:
| And | true | false | null |
|---|---|---|---|
| true | true | false | null |
| false | false | false | false |
| null | null | false | null |
Truth table for or is:
| Or | true | false | null |
|---|---|---|---|
| true | true | true | true |
| false | true | false | null |
| null | true | null | null |
Index expressions
An indexed expression with an index that evaluates null or is out of range returns null .
Functions
Functions usually return null if a parameter is null . An exception is the function isNull(value) , which never returns null .
If statement
"If then" statement
An "if then" statement has the following syntax:
if condition-expression then
then-body-statement
The condition expression must evaluate to a boolean type.
If the expression evaluates to true , the "then" statement is executed. If the expression evaluates to false or null , the "then" statement is ignored.
The 'then' statement is a body statement .
"If then else" statement
An "if then else" statement has the following syntax:
if condition-expression then
then-body-statement
else
else-body-statement
The condition expression must be of boolean type.
If the expression evaluates to true , then the "then" statement is executed. If the expression evaluates to false or null , then the "else" statement is executed.
A "then" or "else" statement is a body statement .
Note
The expression:
if condition-expression then
statement-a;
else
statement-b;
might not be equivalent to:
if not condition-expression then
statement-b;
else
statement-a;
Indeed, if the expression is null, then the "else" statement is executed in both cases.
"Then" or "else" body statement
A body statement can be:
-
A return statement ,
-
An "if then" or "if then else" statement,
-
A statement block.
A statement block is used to group multiple statements. It starts with the keyword begin and ends with the keyword end .
begin
<statement 1>
<statement 2>
...
<last statement>
end
All statements except last one must be an "if then" or an "if then else" statement.
if isMember('sales-team')then
begin
if record.Country='F' then
return readWrite;
if record.Country='UK' then
return readOnly;
end
else
begin
if record.Country='D' then
return readOnly;
if record.Country='B' then
return readWrite;
return hidden;
end
Return statement
A return statement specifies access to records that meet given conditions.
The following table shows valid return statements.
| Return statement | Description |
|---|---|
| return hidden; | The record is hidden (the user has no access). |
| return readOnly; | The record is read only for the current user. |
| return readWrite; | The record can be read and modified by the current user. |
If no return statement applies to a given record, the value hidden is assumed.
Context variables
Introduction
Record permissions can depend on the current dataspace, dataset, or session.
Dataspace
The predefined alias dataspace provides access to information on the current dataspace.
This alias gives access to the following fields:
| Name | Type | Description |
|---|---|---|
| name | string | The name of the dataspace. Because the dataspace namespace and the snapshot namespace are independent, the returned string is only an identifier in the context of one of the namespaces. For a global dataspace or snapshot identifier, use the field id . |
| id | string | The persistent identifier of a dataspace or snapshot. Compared to name , this identifier additionally specifies whether this id is for a dataspace or a snapshot. |
| isSnapshot | boolean | Is true if dataspace is a snapshot and false if dataspace is a branch. |
Example:
if dataspace.name = 'branch-R' then
return readOnly;
Dataset
The predefined alias dataset provides access to information on the current dataset.
This alias has the following fields:
| Name | Type | Description |
|---|---|---|
| name | string | The name of the dataset. |
Example:
if dataset.name = 'TEST' then
return readWrite;
Session
The predefined alias session provides access to information on the current user session.
This alias has the following fields:
| Name | Type | Description |
|---|---|---|
| userId | string | The current user’s id. |
| userEmail | string | The current user’s email. |
| trackingInfo | string | The current session’s tracking information. |
To check current user’s roles, see Roles .
Example:
if session.userId = 'jdoe' then
return readWrite;
Context functions
Roles
The following table describes the isMember() function:
| Syntax | Description |
|---|---|
| isMember(roleExpression…) | Returns true if the current user has at least one of the roles evaluated from the specified expressions. A roleExpression can be a role literal or a field path . Regarding role literals, the following built-in roles must be specified without quotes: administrator , readOnly , everyone . Custom roles are specified as string literals. Specifying a role that does not exist is not an error. (It is considered as a role with no members.) Note The roles administrator and 'administrator' can be two distinct roles. This means that isMember(administrator) might not return the same result as isMember('administrator') . The same rule applies for other built in roles. |
Example:
// Role literals:
if isMember('SALES', 'SUPPORT') then
return readWrite;
// Role administrator (not surrounded by quotes):
if isMember(administrator) then
return readOnly;
// Field paths:
if isMember(record.AuthorizedRole) then
return readWrite;
// Different role expressions can be used at the same time:
if isMember('SALES', 'SUPPORT', administrator, record.AuthorizedRole) then
return readWrite;
In order to make the permission depend on roles configured in a table, it must be related to the record by one or many associations. Then the function isMember must be used as follows:
// All records that are assigned to a zone which is authorized for the current user
// (i.e there is at least one of user's role configured in the Roles.AuthorizedRole field).
if exists(record.Zones:Z[exists(Z.Roles:R[isMember(R.AuthorizedRole)])]) then
return readOnly;
Session
The following table describes the session functions:
| Syntax | Description |
|---|---|
| getSessionInputParameter(parameterKey, lookupInParentSessions) | Returns the session input parameter corresponding to the specified parameterKey . In the context of a workflow interaction, the function looks for the parameter in the input parameters of the workflow task. When lookupInParentSessions is true , the function looks for the parameter in the parent sessions. Otherwise, it looks for the parameter only in the current session. |
| isInWorkflowInteraction(lookupInParentSessions) | Returns true if the current session is in a workflow interaction context. When lookupInParentSessions is true , the function returns true if a parent session is in a workflow interaction context. Otherwise, the function evaluates only the context of the current session. |
Example:
// Give read write access if the scripted rule is called in a workflow interaction context and the 'instance' input parameter for the workflow task is 'Library'.
if isInWorkflowInteraction(true) and getSessionInputParameter('instance', true) = 'Library' then
return readWrite;
Other functions
String matching functions
A string matching function takes three parameters:
-
A stringExpression that evaluates to a string.
-
A pattern that must be a string literal.
-
An optional boolean literal isCaseSensitive that indicates if matching is case-sensitive. If omitted, it is assumed that matching is case-insensitive.
| Syntax | Description |
|---|---|
| matches(stringExpression, pattern, isCaseSensitive) | Returns true if stringExpression matches a java regular expression pattern . |
| startsWith(stringExpression, pattern, isCaseSensitive) | Returns true if stringExpression starts with string pattern . |
| endsWith(stringExpression, pattern, isCaseSensitive) | Returns true if stringExpression ends with string pattern . |
| contains(stringExpression, pattern, isCaseSensitive) | Returns true if stringExpression contains a string pattern . |
| containsWholeWord(stringExpression, pattern, isCaseSensitive) | Returns true if stringExpression contains a whole word pattern . |
Examples:
// Give read write access if first name starts with 'a', 'b' or 'c' (case-sensitive).
if matches(record.FirstName, '[a-c].*', true) then
return readWrite;
// Give read only access if first name starts with 'Lé' (case-insensitive).
if startsWith(record.FirstName, 'Lé') then
return readOnly;
// Give read only access if first name ends with 'my' (case-insensitive).
if endsWith(record.FirstName, 'my') then
return readOnly;
// Give read only access if email contains with 'BeauMont@' (case-sensitive).
if contains(record.Email, 'BeauMont@', true) then
return readOnly;
// Give read write access if last name contains the whole word 'Michel' (case-insensitive).
if containsWholeWord(record.LastName, 'Michel', false) then
return readWrite;
Aggregate functions
The following aggregate function can be used with associations:
| Syntax | Description |
|---|---|
| count(expression) | Returns the number of rows of an expression. |
| exists(expression) | Returns true if the expression evaluates to at least one row. |
Examples:
// Give read write access if managed users count is at least 2.
if count(record.ManagedUsers[]) >= 2 then
return readWrite;
// Give read only access if count of managed users that are not in Paris is less than 4.
if count(record.ManagedUsers:m1[m1.OfficeAddress.City<>'Paris']) < 4 then
return readOnly;
// Give read write access if managed users count is at least 1.
if exists(record.ManagedUsers[]) then
return readWrite;
// Give read write access if count of managed users that are not in Briton is at least 1.
if exists(record.ManagedUsers:u1[u1.OfficeAddress.City<>'Briton']) then
return readWrite;
Date and time functions
The following table lists the date and time functions.
| Syntax | Description |
|---|---|
| datetimeNow() | Returns the current date and time |
| dateNow() | Returns the current date |
| timeNow() | Returns the current time |
Examples:
// Give read write access if the last possible update date hasn't expired yet.
if record.LastPossibleUpdateDate >= dateNow() then
return readWrite;
else
return readOnly;
Miscellaneous functions
The following table describes built-in functions that return a boolean.
| Syntax | Description |
|---|---|
| isNull(value) | Returns true if value is null. Value can be an expression be of any type. |
Examples:
// Give read write access if no supervisor.
if isNull(record.supervisor) then
return readWrite;