TIBCO Software Inc. EBX®
Documentation > Developer Guide > EBX® Scripting
Navigation modeDocumentation > Developer Guide > EBX® Scripting

Record permission

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.

The last statement can be an "if then", "if then else" or "return" 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:

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 or an underscore.

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 any Unicode character except a double quote (").

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.

Types

Supported types

The following types are supported:

TypeEBX® 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:

CharacterEscape 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

ValueSyntax

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:

FormatExamples

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;

Note

Currently, it is not possible to:

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:

Precedence LevelOperatorOperand typeResult typeAssociativity

8

[] (access to an element of a list)

. (access to fields)

() (parenthesis)

List index must be a decimal.

Can be any type.

Left to right.

7

not

Boolean.

Boolean.

6

*

/

Decimal.

Decimal.

Left to right.

5

+

-

Decimal.

Decimal.

Left to right.

4

<

<=

>

>=

String, Decimal, timestamp, date, time (3).

Boolean.

Not associative.

3

=

<>

String, decimal, timestamp, date, time, boolean (3).

Boolean.

2

and

Boolean.

Boolean.

Left to right.

1

or

Boolean.

Boolean.

Left to right.

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 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.

The last statement can be a "if then", "if then else" or a "return" 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 statementDescription

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

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:

NameTypeDescription

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:

NameTypeDescription

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:

NameTypeDescription

userId

string

The current user’s id.

userEmail

string

The current user’s email.

To check current user’s roles, see Roles.

Example:

if session.userId = 'jdoe' then
  return readWrite;

Functions

Roles

The following table describes isMember() function:

SyntaxDescription

isMember('rolea', 'roleb'…)

Returns true if the current user has at least one of the specified roles.

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 is_member('administrator'). The same rule applies for other built in roles.

Example:

if isMember('SALES', 'SUPPORT') then
  return readWrite;

// Role administrator and not surrounded by quotes:
if isMember(administrator, 'SUPPORT') then
  return readOnly;

String matching functions

A string matching function takes three parameters:

SyntaxDescription

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:

SyntaxDescription

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;

Miscellaneous functions

The following table describes built-in functions that return a boolean.

SyntaxDescription

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;
Documentation > Developer Guide > EBX® Scripting