Cmobilecom AF 5.19 Developer Guide

12 Access Control

Contents: Access control prevents data from being accessed by unauthorized users. Access control can be enforced using XML, AccessToken, annotation and API.

User - Role - Permission

A user can have many roles, and each role can have many global or scoped permissions.
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: These pre-defined roles can not be deleted, but their associated lists of permissions and access tokens can be edited. i.e. add/remove permissions or access tokens.

Access Control XML

Each module can provide an access control XML file ac.xml that describes the permissions required to access entities, properties and take actions. Its entry name in module jar is [module]/conf/ac.xml.

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, then the user is authorized to access all entities of the corresponding entity type for the accessType. It is for all entities except OWNER user type. For scoped access control, use access tokens.

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:

If the access control for an entity type or for an accessType is not specified, it defaults to USER{ModuleView}. That is, authenticated user with the permission to view the module to which the entityType belongs.

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:

Access Token

Access Token is used for scoped access control. A access token has a secure unique number that is random generated 80 character long. It is impossible to guess the number for current computing power.

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. Modules can define any scopes. For example, Repository module may define the scope for each managed repository as Repository.[repositoryName], and allow repository publishers to upload artifacts into repositories.
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

Access Control Accessor

A module can define any permissions as needed. Permissions can be assigned to a Role that can be assigned to a User. To check if a user has a certain permission, use AccessControlAccessorWrapper API for convenience. AccessControlAccessorWrapper is a wrapper of AccessControlAccessor and AccessControlContext. For methods not exposed by AccessControlAccessorWrapper, use AccessControlAccessor API directly.

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

Super Permissions

Permission naming convention is AccessType followed by entity type name. For example, permission for creating employees is CreateEMP, where "Create" is accessType (first letter uppercase) and EMP is the mapped name for entity type Employee (defined by Module implementation).

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]

Override Access Control

To override the access control defined in XML, or enforce a different access control logic, override the following methods as needed in its EntityBackingBean.

	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);

Property Access Control

Access controls for the properties of an entity type can be specified under its entityType element in the ac.xml file as the example above.

	<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);
}

Resource Bundle Keys

Permission display names are defined in module resource bundle with key: ModuleName.PermissionName. For example,
	MyModule.PermissionXYZ=MyModule: Permission XYZ
If 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
AuthenticationLarge Object and MediaFrames / No Frames