This is an internal documentation. There is a good chance you’re looking for something else. See Disclaimer.
ACL¶
*.acl
files are used to control access to certain resources (for example entities).
Folder structure¶
The files are contained in the resources/acl
folder of the module:
The structure is the same for all modules.
The main file that will be loaded is called module.acl
, but usually the ACL rules are split up
into different files (entity.acl, action.acl and so on).
Note
When a new *.acl
file is created it needs to be included into the module.acl
file:
include 'entity.acl';
include 'action.acl';
The Access Control Language¶
For defining the access control rules, there is a language, called ACL (Access Control Language). The top-level structure of this language is the section. A section defines the selector for the following rules, until a new section is started. The section header looks like this:
entityManager(MyEntity):
// the rules
The selector defines the security domain (in this example entityManager
) and the objects (in this example
the entity model MyEntity
) that are affected by the acl rules.
The following figure shows the syntax for the rules:
So, each rule starts with the keyword grant
or deny
, optionally followed by a list of permissions, optionally
followed by the keyword to
and a list of subjects (role name or ‘&’ + principal name), optionally followed
by the keyword if
and a condition (or unless
and a condition, which is a shortcut for if not (...))
and finally
terminated by a semicolon.
Optionally, a rule may end with and stop
, which declares a rule final. If a final rule applies,
processing of rules stops at this point.
Example:
entity(User):
deny access(write) to usermanager if deleted and stop;
This rule applies to all User
entities.
When
grant
is used access will allowed; whendeny
is used access will be forbidden if the rule matches
access(write)
is the name of the permission. The different security domains offer different permissions.
to usermanager
defines that the rule should only be applied to principals with a certain role.
if deleted
is the condition. The rule is only applied if the secured objects matches the condition. Not every security domain supports conditions.
and stop
marks this rule as final (see above).
Note
All security features are disabled when the privileged()
invoker of the SecurityManager
is used.
Keywords¶
The following words are ACL keywords: grant
, deny
, include
, to
, if
, unless
, principal
, null
,
true
, false
, or
, and
, not
, role
, implies
.
Also, the keyword permission
is currently reserved, but unused.
Security domains¶
Different objects are secured with different SecurityDomains. A security domain defines which permissions are available and how or if conditions can be applied to a rule.
entityManager¶
The target of this domain is an EntityModel.
It provides the create
permission, which allows creating a new instance of an entity.
Conditions are not supported.
entityManager(User):
grant create to usermanager;
This permission is checked when a new entity instance is created:
context.getEntityManager("User").create();
entity¶
This domain controls the read and write access to persisted entities and provides the access
and delete
permissions.
The access
permission takes an optional parameter to specify whether the rule targets read or write permission.
If no parameter is given both read and write permissions are affected.
For example this rule affects the write operation only:
entity(User):
deny access(write);
Note
The combinations deny access(read)
and grant access(write)
are not allowed, because it does not make
sense to deny only read access (but allow write access) or to grant only write access (but deny read access).
Conditions¶
This domain supports conditions to restrict the affected entities with a TQL like syntax. For example:
entity(User):
deny access(write) if exists(relAddress) and enabled;
Using the keyword principal.user_id
the primary key of the currently logged in user can
be used in the condition.
The keyword now
specifies the current date/time. It’s fields are actually operators which operate on that object.
These can be combined freely, e.g. now.yesterday.date
means yesterday at 0.00.
The following “path elements” (operators) are available:
date Keep the date as-is, set the time to 0:00.
time Keep the time as-is, set the date to January 1st, 1970.
tomorrow Plus one day.
yesterday Minus one day.
The applicable conditions are added to every query for a given entity model, so that only readable entities are returned from the database.
Updating relations¶
When an entity is added to or removed from a Relation the permissions of the both sides of the relation are combined:
GRANT
+GRANT
=GRANT
GRANT
+NO_RULE
=GRANT
GRANT
+DENY
=DENY
NO_RULE
+DENY
=DENY
NO_RULE
+NO_RULE
=NO_RULE
DENY
+DENY
=DENY
If the combined result is not GRANT
the relation may not be changed.
This has the following implications:
A relation may be updated through its reverse side unless there is an explicit
DENY
rule.For new entities
GRANT
is assumed, so new entities may always be added to a relation unless there is an explicitDENY
rule.
Note
The rules of this security domain do not affect entities which are in state CONCEPTION.
entityPath¶
The entityPath
domain is related to the entity
domain as it also affects entity instances and provides the same
access
permission (but no delete
permission).
But while the latter affects entities as a whole, the former affects access to single fields or relations of an entity.
The same Conditions are supported as for the entity
domain (obviously referencing the
entity containing the field and not the field itself).
entityPath(User, email):
deny access(write);
This denies write access only to a single field of an entity (email in this case).
These rules are checked whenever getValue()
, requireValue()
or setValue()
(or a similar method like getString()
/ requireString()
) is
called on the Entity.
Note
When a user has been granted access to an entity (through the entity
security domain) and there are no
specific rules for a field, the entity rules are also applicable for the field.
Otherwise the two security domains are evaluated independently and the entityPath
domain can override the
rules of the entity
domain for specific fields.
Additional domains¶
netuiactions¶
Controls whether actions may be executed:
netuiactions("nice2.businessunit.CopyBusinessUnitValuesAction"):
grant netuiPerform to configurator;
infoBoxes¶
Controls to whom infoboxes are displayed on the home page:
infoBoxes(welcomebox, sysreqinfobox, securityinfobox):
grant boxDisplay;
reports¶
Controls whether a specific report may be generated:
reports("report.schooling"):
grant generateReport to eventmanager, eventguest;
Policy¶
All ACL rules are compiled into a security Policy.
The rules are applied in the order they were defined in the *.acl
files (and depending on module
dependencies). So it is always possible to override an earlier rule (unless and stop
was defined
on a rule).
Because the full policy (stored in the SecurityManager) for all possible principals and all possible objects may get very big for a full-scale application, this policy will be reduced whenever possible. There are two points, where some important facts get known that allow to filter out rules that won’t apply anyway:
After login, the exact principal is known. At this point, a new policy will be generated for that user (stored in the SecurityContext) that doesn’t contain any rules anymore that don’t affect that principal.
When a guard is needed, the exact object is known. At this point, all rules that don’t affect this object will be filtered out using the selector (stored in the Guard).
Therefore, in practice, the policy for a concrete object will finally be relatively compact.
Checking permissions manually¶
Normally the permissions are checked automatically when querying or updating data. But sometimes it is necessary to check permissions manually. This can be done by obtaining a Guard from the SecurityContext. The Guard instance can then be used to evaluate permissions.
Roles¶
For more info see Initial Values
Roles are loaded into the database via a yaml file:
Role:
localized:
label: entities.Role.tipguest.label
description: entities.Role.tipguest.description
single-relations:
relRole_type: nice_role_type WHERE unique_id = 'guest'
fields:
unique_id: tipguest
force_two_factor_auth: false
config:
update-mode: keep_changes
identifier: unique_id
---
Role:
localized:
label: entities.Role.tipmanager.label
description: entities.Role.tipmanager.description
single-relations:
relRole_type: nice_role_type WHERE unique_id = 'manager'
fields:
unique_id: tipmanager
force_two_factor_auth: false
config:
update-mode: keep_changes
identifier: unique_id
relRole_type
set an already defined role type from nice_role_type to this new role.unique_id
the id that can then be used in acl and other places.force_two_factor_auth
true or false if 2FA should be forced.
ACL for (public) widgets¶
Some widgets will only fully work if a customer ACL rule is added as the permission depends on the use-case of the customer. As an example in the membership registration widget the section dropdown is empty for anonymous users. Here some guidelines on how to write ACL rules for such cases:
Only grant access to the minimal number of entities (e.g. use a flag or status)
You cannot only grant access to some paths. You must grant access to the entity and deny access to all paths which should not be available. As an example normally
relResource_entitydocs
andrelFolder_entitydocs
contain sensible data which should not be public availableIf you write an ACL for extranet users (without a role) add
except &anonymous
if the entity must not be public available
As an example for the membership registration widget if the publish
flag on the Address
should be used, the constriction must be adjusted and the ACL rules added:
entity(Address):
grant access(read) to &anonymous if publish;
entityPath(Address, relResource_entitydocs, relFolder_entitydocs, ...):
deny access to &anonymous;