Blogs
How to Propagate Permissions to Child Entities
Introduction
Roles and Permissions is one of Liferay foundation features that allows to develop secure and reliable applications. It's crucial to organize them in a structured, transparent and easy-manageable way. Sometimes it can be hard, especially when it comes to hierarchical entities. Fortunately, Liferay has a dedicated feature for this case - Permissions Propagator.
Permission Propagator Overview
Permission Propagator is a Liferay feature that allows to propagate permissions from a parent entity to child ones. PermissionPropagator interface declares a method to be implemented for permissions propagation:
public void propagateRolePermissions(
ActionRequest actionRequest, String className, String primKey,
long[] roleIds)
throws
PortalException;
Also, there is a BasePermissionPropagator class that provides basic functionality for permissions propagation.
Currently Liferay has two implementation for PermissionPropagator:
- MBPermissionPropagatorImpl - to propagate permissions for child MB Categories and Threads from a parent category;
- WikiPermissionPropagatorImpl - to propagate permissions from a parent WikiNode to child WikiPages.
By default, PermissionPropagator is disabled. To enable it - you need to add the following property to portal-ext.propeties:
permissions.propagation.enabled=true
Once you enable it - you can check it "in action". Create a parent MB Category, a child MB Category and a child MB Thread: once you define permissions to parent MB Category - they should be automatically propagated to child categories and threads.
Custom Permission Propagator
What if you need a custom Permissions Propagator, e.g. to
propagate DL Folder permissions from a parent folder to child ones?
If you check one of the implementations listed above - you'll find
that it's just an OSGi component for PermissionPropagator service,
registered for specific portlet(s) by
specifying javax.portlet.name
property:
@Component(
property = {
"javax.portlet.name=" + MBPortletKeys.MESSAGE_BOARDS,
"javax.portlet.name=" + MBPortletKeys.MESSAGE_BOARDS_ADMIN
},
service = PermissionPropagator.class
)
public class MBPermissionPropagatorImpl extends BasePermissionPropagator {
In the same way you can create a PermissionPropagator component for Document Library portlets:
@Component( property = { "javax.portlet.name=" + DLPortletKeys.DOCUMENT_LIBRARY, "javax.portlet.name=" + DLPortletKeys.DOCUMENT_LIBRARY_ADMIN }, service = PermissionPropagator.class ) public class DLPermissionPropagatorImpl extends BasePermissionPropagator {
and implement the propagateRolePermissions
method.
In the method implementation you can fetch the list of child
folders based on a current one and
invoke propagateRolePermissions
method from base
implementation for each of them. The complete implementation (a bit
simplified) is listed below:
@Component( property = { "javax.portlet.name=" + DLPortletKeys.DOCUMENT_LIBRARY, "javax.portlet.name=" + DLPortletKeys.DOCUMENT_LIBRARY_ADMIN }, service = PermissionPropagator.class ) public class DLPermissionPropagatorImpl extends BasePermissionPropagator { @Override public void propagateRolePermissions(ActionRequest actionRequest, String className, String primKey, long[] roleIds) throws PortalException { if (!className.equals(DLFolder.class.getName())) { return; } long dlFolderId = GetterUtil.getLong(primKey); DLFolder dlFolder = dlFolderLocalService.fetchDLFolder(dlFolderId); List<DLFolder> childFolders = getChildFolders(dlFolder); for (DLFolder childFolder: childFolders) { for (long roleId : roleIds) { propagateRolePermissions( actionRequest, roleId, DLFolder.class.getName(), dlFolderId, DLFolder.class.getName(), childFolder.getFolderId()); } } } private List<DLFolder> getChildFolders(DLFolder parentFolder) { List<DLFolder> childFolders = dlFolderLocalService.getFolders(parentFolder.getGroupId(), parentFolder.getFolderId()); List<DLFolder> allChildFolders = new ArrayList<>(childFolders); for (DLFolder childFolder: childFolders) { allChildFolders.addAll(getChildFolders(childFolder)); } return allChildFolders; } @Reference private DLFolderLocalService dlFolderLocalService; }
The getChildFolders method returns a list of all child folders recursively, and then permissions are propagated for each of them.
PermissionPropagator is invoked automatically when permissions are updated in Permissions Configuration.
Deploy module with DLPermissionPropagatorImpl component and check it: now when you define permissions for a parent DL Folder - they should be propagated to all child ones.
You can find complete sources for this example here: https://github.com/vitaliy-koshelenko/lifedev-portal/tree/permissions-propagator/modules/lifedev-dl-permisson-propagator
In a similar way you can implement permission propagation for any other hierarchical entities, even custom ones.
Conclusion
PermissionPropagator is out-of-the-box Lifeary feature, that is disabled by default and implemented only for Message Boards and Wiki Pages. But it can be enabled in portal properties, if permission propagation is required. Also, custom PermissionPropagator components can be implemented to propagate permissions in different widgets, for different entities. This way you can ensure that child entities have the same permissions as defined for the parent one.
Leave your comments and feedback.
Stand with Ukraine
Enjoy 😏