Liferay Permission Propagator

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:

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.

Note: make sure permission propagation is enabled permissions.propagation.enabled=true

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 😏

Vitaliy Koshelenko

Liferay Architect at Aimprosoft

v.koshelenko@aimprosoft.com


 

 

Blogs

Hello,  what portal property should be used to allaow permission propagation?

Hello! This one:

permissions.propagation.enabled=true

Thank you sp much for your reply.

in portal.properties  thsi is how this property is described:

# # Set the following to true to enable propagation of permissions between # models. # # For example, when setting the permissions on a specific Wiki node, if # you assign a role a permission (e.g. DELETE), then the assignment of that # permission is also propagated to all Wiki pages that belong to that Wiki # node. # # The actual logic of how permissions are propagated among models is # specified per portlet. See liferay-portlet.xml's use of the element # "permission-propagator".

 

I looked at my Liferay 7.4 installation and I did not find the  "permission-propagator" element anywhere. 

So, I am still unsure as to how I can enable the propagartion dor, say, document library.

Did you read the blog content? Or only title? :)

There is an example for Document Library, you can check it out.

Property description text is obsolete, and is based on Liferay 6.2 version, where all portlet configuration was defined in XML descriptors (e.g. liferay-portlet.xml). 

For 7.x version OSGi configuration is used instead: you need to implement an OSGi component for PermissionPropagator service and associate it with the portlet you need, like in example here for DL: "javax.portlet.name=" + DLPortletKeys.DOCUMENT_LIBRARY

Vitaliy, thanks for the great blog post! It highlights a solution I wasn't aware of, and it seems directly applicable to a few challenges we've been facing. ​​​​​​​

Hello Vitalik, thank you for your interesting post.

Unfortunately it seems to me that it doesn't provide the desired result.

After deploying the module, I update a folder with permissions assigned to a specific role and in the log I correctly see the messages "[DLPermissionPropagatorImpl:73] Propagated permissions from DLFolder #488 ....".

But when I go to this new folder the documents holds the original permissions.

Moreover if I create a new sub-folder or I upload a new file in the folder the UI propose me the classical Permissions option (Anyone (Guest role) Site Members, Owner). Again if I update the permissions of the new folder in the log I correctly see the messages "[DLPermissionPropagatorImpl:73] Propagated permissions from DLFolder #488 ...."  but when I upload new documents I get the same the classic Permission choices propose.

I added some more options in portal-ext.propeties but with no luck:

permissions.check.guest.enabled=true ​​​​​​​permissions.view.dynamic.inheritance=true permissions.propagation.enabled=true

Please could you help us more? Thank you in advance!

Hi Ivano,

 

Unfortunately - yes, it does not work dynamically for newly added folders/files.

The goal of Permissions Propagator is to make sure when you assign permssions to a parent entity - they're propagated automatically to child ones (that already exist). Permissions Propagator is invoked when you define permissons in permissions configuration window, and is not aware of any entities created afterwards.

For "dynamic" permissions based on parent entity - probably, you'll need other extension points, e.g. model listeners on DL Folders.

 

Ragards,

Vitaliy

Hi Vitaliy, thank you for your feedback.

​​​​I'm sorry to contradict you but the permissions are not propagated even for documents already present in the folder.

I created a folder and uploaded a document. Then I change the folder permissions and in the log I see the message "[DLPermissionPropagatorImpl:73] Propagated permissions from DLFolder #48827 'Folder for Teachers' to DLFileEntry #48829 '100 use cases ... .pdf'"

If I look at the permissions of the document these are only the same by default as its original creation.

If you tell me where to do it I can send you screen shots and other documentation.

Regards, ​​​​​​​Ivano