User ---(ManyToMany)--- Role --- (ManyToMany)--- Permission ---------| \ | \ | \--- (ManyToMany)--- AccessToken | | Scoped (OneToMany) (ManyToMany) | | AccessItem --------|Permissions are defined by modules, and permissions can not be created, deleted or modified at runtime.
public class MyModule extends AbstractModule {
@Override
public List<String> getPermissions(InstanceType instanceType) {
List<String> permissions = super.getPermissions(instanceType);
permissions.add("PermissionXYZ");
return permissions;
}
}
Permission names will be combined with module name to create ModuleScopedName(s),
globally unique permission names.
A user obtains permissions by owning roles. Roles and access tokens can be managed under System module in manage center, and roles can be assigned to users. A scope means a subset of data, and defined by modules.
When an InstanceType is initialized or an instance is created, its mapped DataAccessUnit (a logic persistence unit) will be initialized, and the following roles will be created for the DataAccessUnit:For example, Example HR module:
<access-control>
<entityType name="EMP">
<entity accessType="CREATE" accessControl="USER{CreateEMP}" />
<entity accessType="DELETE" accessControl="USER{DeleteEMP}" />
<entity accessType="EDIT" accessControl="OWNER|USER{EditEMP}" />
<entity accessType="VIEW" accessControl="SUSER|OWNER|USER{ViewEMP}" />
<entity accessType="EXPORT" accessControl="SUSER{ExportEMP}" />
<properties accessType="EDIT" mode="EDIT" accessControl="SUSER" >
<property>name</property>
</properties>
</entityType>
<action name="ApproveExpenseClaims" accessControl="USER{ApproveEC}">
</access-control>
accessType can be CREATE, DELETE, EDIT, VIEW, SEARCH, EXPORT or REPORT, which
are mapped to AccessType enum names. An accessControl(for one accessType on
an entityType or for one action) consists of a list of accessControl items
(separated by |, OR relationship) and each item is a user type followed by
a list of permissions(optional).
USER_TYPE{Permission,...} | USER_TYPE{Permission,...}For example:
USER USER{Permission1} SUSER{Permission1}|USER{Permission2,Permission3} PUBLIC|USER{Permission1,Permission2} NOBODY ANONYMOUS|OWNER|USER{Permission1}If a user matches any accessControl item USER_TYPE{permission list}, that is,
User types
For the OWNER user type to be effective, the entity type must implement OwnerAware interface, or set owner in DataDescriptor and pass it to AccessControlAccessor. For access control for a list of entities (e.g., view query results from persistence), use DataDescriptor.
For the HR module example above, entity type Employee is mapped to DataType(HR.EMP), and ExpenseClaim is mapped to DataType(HR.EC). The module prefix can be omitted in module access control XML.
The permissions required to access employees as follows:
In addition to access controls for entity types, access controls can be enforced for an action to be taken by current user. For the example above,
<action name="ApproveExpenseClaims" accessControl="USER{ApproveEC}">Action names are defined by modules. If the user is not authorized to take the action, the choices are:
An access token consists of a number of access items, each of which specifies a scope and a list of permissions assigned to the access token.
A scope can be a single entity, a subset of data or all.Access Items Scope Permissions ------------------------------------------- Repository.public GET,PUT Repository.internal GET,PUT
A guest access token is for public access, which enables scoped data to be accessed by anyone without user authentication. Take Repository module for instance, a guest access token may be created to allow everyone to get artifacts from a public repository.
Access Items Scope Permissions ------------------------------------------- Repository.public GET
// create AccessControlAccessorWrapper
AccessControlContext acContext = new AccessControlContext(scope);
AccessControlAccessorWrapper acAccessor = new AccessControlAccessorWrapper(
AccessControlAccessor.getInstance(), acContext);
// create AccessControlAccessorWrapper from viewConfig or backingBean
// AccessControlAccessorWrapper acAccessor = viewConfig.getAccessControlAccessor(true);
// AccessControlAccessorWrapper acAccessor = backingBean.getAccessControlAccessor();
// example method calls, see Java docs for complete API
BackingBeanContext context = BackingBeanContext.getInstance();
User currentUser = context.getAuthenticatedUser();
if (acAccessor.isUserHasPermission(currentUser, moduleName, permissionName)) {
...
}
if (acAccessor.canUserAccessType(currentUser, AccessType.EDIT, entityType)) {
...
}
if (acAccessor.canAccessEntity(currentUser, AccessType.EDIT, DataDescriptor dataDescriptor)) {
...
}
if (acAccessor.canUserTakeAction(currentUser, actionName)) {
...
}
For scoped access control, set scope in AccessControlContext or call methods with
parameter dataDescriptor with scope set.
If a user can manage an entity type, it implies that the user can create, delete and edit entities of the entity type. In other words, Manage[TypeName] is a super permission of Create[TypeName], Delete[TypeName], Edit[TypeName], and View[TypeName]. Similarly, Edit[TypeName] is a super permission of View[TypeName]. But Create[TypeName] is not a super permission of View[TypeName].
Manage[TypeName] -------------------------------------------------------------- / | | \ Create[TypeName] Delete[TypeName] Edit[TypeName] View[TypeName] | View[TypeName]
protected boolean isHasCreatePermission()
protected boolean isHasDeletePermission(PersistenceDataBackingBean<T> backingBean, T entity)
protected boolean isHasEditPermission(PersistenceDataBackingBean<T> backingBean, T entity)
protected boolean isHasViewPermission(PersistenceDataBackingBean<T> backingBean, T entity)
Access controls can also be set in the ViewConfig of EntityBackingBean
or EntityListBackingBean. For example,
EntityViewConfig viewConfig = new EntityViewConfig(ViewType.ENTITY);
// EntityListViewConfig viewConfig = new EntityListViewConfig(ViewType.ENTITY_LIST_WIDE);
viewConfig.setHasCreatePermission(false);
viewConfig.setHasDeletePermission(false);
viewConfig.setHasEditPermission(false);
viewConfig.setHasViewPermission(true);
viewConfig.setMenuNodeSupported(menuNodes);
viewConfig.setMenuNodeDenied(menuNodes);
Override module access control(full or partial) in the ViewConfig of an embedded object:
For example,
<object xmlns="http://www.cmobilecom.com/af/objects" id="object1" type="entities">
<viewConfig>
<viewType>ENTITY_LIST_WIDE</viewType>
<accessControl module="ModuleName">
<entityType name="EMP">
<entity accessType="CREATE" accessControl="USER{CreateEMP}" />
</entityType>
<action name="ApproveExpenseClaims" accessControl="USER{ApproveEC}">
</accessControl>
</viewConfig>
</object>
In addition, permissions can be granted to or denied from current user by an AccessControlContext.
AccessControlContext context = new AccessControlContext(permissionGranted, permissionDenied);
AccessControlAccessorWrapper wrapper = new AccessControlAccessorWrapper(accessControlAccessor, context);
<properties accessType="EDIT" mode="EDIT" accessControl="SUSER{CreateEMP}" >
<property>name</property>
<property>nid</property>
</properties>
The mode specifies when the access control will be activated. Its value
can be CREATE, EDIT, VIEW, QUERY or ALL. All mode means that the access control
will be activated in all modes.
For the example above, to edit employee name or nid, the user needs to be a system user with CreateEMP permission.
Property visibility and edit control can be specified using annotation. For example,
@Property(name="approver", view={ViewType.ENTITY},
mode={ModeType.VIEW, ModeType.EDIT, ModeType.QUERY},
editable=EditControl.QUERY_ONLY,
query=@Query(groupByProperty=true, orderByProperty=true)),
The property "approver" will be visible in VIEW, EDIT and QUERY modes, but not in CREATE mode.
It is editable only in Query mode for search. The default value for editable is
EditControl.ACCESS_CONTROL, using the property access control in XML file.
The editable control of a property can be overridden by calling setEditable(Boolean) method after entity property model is built:
EntityProperty entityProperty = getEntityProperty("propertyName");
entityProperty.setEditable(true or false);
Properties can be configured to be shown/hidden and/or editable in ViewConfig
before building property model of EntityBackingBean or EntityListBackingBean.
@Override
protected EntityViewConfig buildViewConfig(T entity) throws SystemException {
EntityViewConfig viewConfig = (EntityViewConfig) super.buildViewConfig();
viewConfig.setPropertiesToShow(properties);
//viewConfig.setPropertiesToHide(properties);
viewConfig.getPropertyDescriptor("propertyName", true, false).setEditable(false);
}
@Override
protected EntityListViewConfig buildEntityListViewConfig(
EntityListBackingBean<T> entityListBackingBean,
EntityListViewConfig viewConfig) throws SystemException {
viewConfig.setPropertiesToShow(properties);
//viewConfig.setPropertiesToHide(properties);
viewConfig.getPropertyDescriptor("propertyName", true, false).setEditable(false);
}
MyModule.PermissionXYZ=MyModule: Permission XYZIf the description of a role is empty, then system will check resource bundle for the key: Role.RoleName. If it is defined, then get its value as description using current locale. For example,
Role.SuperUser=Super User