Overriding Liferay Core Permissions

By Rafał Pydyniak | 2022-05-23

In Liferay you can define custom permissions for your entities/portlets. For example you can say that in your application in the employees management panel there are four actions: "List employees", "Add employee", "Edit employee", "Remove employee". Roles can then be associated with these permissions as required. Definining that is quite simple and described in Liferay docs which you should read if you haven't done that before.

But what if you need to override some permissions which were defined in Liferay core modules? Well you can of course do that as well. If you're wondering how - keep reading.

Note there are some articles on my blog which do not require any specific knowledge. This article is not one of them. I'm trying to make it as simple as possible so you don't need to be a Liferay expert but some basic knowledge about Liferay or OSGi might be required to follow.

Note 2 you can find a complete sources for article on new github repository I created for Liferay code samples

Why would you want to override Liferay core permissions?

There might be a different answers to the question in this section header. I have encountered different reasons to override Liferay permissions in different projects but in general I think there are two main reasons to do that:

  • You're not happy with default permissions given to Guest or Site member roles.

If you're already familiar with defining permissions in Liferay you probably know that you can define what permissions are given to guest/site member by default. Liferay core permissions also do that. For example by default GUEST has VIEW permission on every JournalArticle: default view permission on journal article for guest What if we don't want that? If we want to control it a little bit more and by default don't give anyone a VIEW permission on any article? Well in such scenario you might want to override Liferay core to remove this part.

  • Second reason might be that you want to add some custom action for some Liferay core portlet or model permissions.

You might for example want to add a custom permission to check if user can checkout an order.

Things to consider when overriding permissions - when you should not do it?

Even though overriding the permissions is quite simple which you will see in a few moments there are things you need to consider:

In most cases you should not remove actions or if you really want to do that - be really careful with that

Of course, in general you could do that but keep in mind that overriding the default.xml (or any other *.xml with permissions definition) only defines what permissions are available. All the permissions checking on the other hand is done in the Java/JSP code. What does it mean for you? Well if you remove some resource action you might end up with errors connected to checking permissions which (after your changes) do not exist. If you would really want to remove some permission you need to make sure to remove all calls to it as well which means more Liferay core overrides.

Adding permission does not mean checking for permissions. In permissions *.xml files you only define what is available in Liferay

All the checking if user has the permission is done elsewhere (Java/JSP code). So even if you add some custom permission to some core portlet or entity you still need to add a logic of checking it. So in general adding custom actions makes sense mostly if you also hook the module in which you add some extra block of code and where you can add usage of your permission.

OSGi vs non OSGi modules - two different approaches for overriding permissions

To start the work on permissions override you first need to determine where the permissions are defined. How to do that? Well you should have a local copy of Liferay portal (available on Github) for the version your using (checkout to a specific tag for your version).

The next step is to find a correct module in the code. Finding code you need in Liferay core is a topic for a separate article - there are many ways to do that.

For finding the definition of permissions it's luckily a little easier. First go to the "Define permissions" section of any role. Then find the block of permissions where you want to change something. For example in my case I want to change the "Web Content Article" default permissions:

Web Content Article permissions
Web Content Article permissions
Once I'm there I can go to the browser's developer tools and see the id of title element:
Title of permissions
Title of permissions
We can see it is "resource_com_liferay_journal_model_JournalArticle" and we're interested in the last part. The "JournalArticle" is the model name we're interested in. In this case you could probably guess the name if you work with Liferay for some time - JournalArticle is a well known name. In some other cases though this might let you find what you need.

Next step is to search for this model name in Liferay sources - you will get hundreds of results - this is too much. Luckily we can filter that:

  • Use the file mask to filter only "*.xml" files - the permissions are defined in XML files so we can use that.
  • Use the <model-name> tag with the name you found earlier and a little regex.

These two filters should be enough to find the xml file you need:

Find permissions definition XML
Find permissions definition XML
In this case only three files were found. Thirty seconds later you should be able to find out that the file we're looking for is the "journal.xml" which is located in journal-service module. Looking at the module structure you can tell that this is an OSGi module.

Override definition of permissions in OSGi module

Once you know your module is OSGi one you can start work on overriding permissions. The idea is simple - you need a fragment which will override permissions XML files. Just like you would normally override JSP file.

These are the steps you need to take:

  • Create a fragment module.
  • Specify a Fragment-Host property in bnd.bnd.
  • Create resources structure and override XML files.
  • Deploy your module.

Create a fragment module

You can crate a fragment using blade create command or your IDE. You can also take a look on a module I already created in blog samples.

Specify a Fragment-Host

Once you have your module you want to make sure that your fragment points to a proper bundle version. You can do that in bnd.bnd file by using Fragment-Host property. In my example I will override permissions from journal-service module. Checking the Liferay sources I can tell that I need to override the com.liferay.journal.service (see the bundle symbolic name in bnd.bnd file of overridden module). You also need to check the bundle version - either in sources or in gogo shell:

Checking bundle version in gogo shell
Checking bundle version in gogo shell

Once you have bundle symbolic name and its version you can define Fragment-Host in your module bnd.bnd file:

Checking bundle version in gogo shell
Checking bundle version in gogo shell

Creating resources structure and override XML files

Once our fragment points to a proper Liferay's bundle we can start overriding the permissions files.

We need following files/directories:

  • In the resources directory we need to create a resource-actions directory where we will put updated versions of

XML files we want to override.

  • In my case I want to do the changes in "journal.xml" file. To do that I copy the journal.xml from Liferay sources

with changed name - for example "journal-ext.xml".

  • You also need to copy the "default.xml" and also change its name

for example to default-ext.xml.

  • As we will create some new resource actions we can also create a "content" directory

where we will keep our translations.

  • The last thing you need to do is to create portlet.properties file

in resources directory.

In the end you should end up with following structure:

Files structure for journal.xml override
Files structure for journal.xml override
Now lets start with filling all the files.

First portlet.properties. In this file we want to specify that our default-ext.xml file should be used instead of the original default.xml:

include-and-override=portlet-ext.properties
resource.actions.configs=resource-actions/default-ext.xml

In the default-ext.xml itself we want to change only one line from the original file content - like you might guess we want to use our journal-ext.xml instead of the original journal.xml. So the original default.xml file content is:

<resource-action-mapping>
    <resource file="resource-actions/journal.xml" />
    <resource file="resource-actions/journal_ddm_composite_models.xml" />
</resource-action-mapping>

And our override should look like:

<resource-action-mapping>
    <resource file="resource-actions/journal-ext.xml" />
    <resource file="resource-actions/journal_ddm_composite_models.xml" />
</resource-action-mapping>

Note We don't need to copy other files like journal_ddm_composite_models.xml. If we keep the original name then the original files will be used.

All the changes above are required to finally make our updates in the journal-ext.xml (which is the copy of original journal.xml!).

You can do whatever you need there. If you're not familiar with defining permissions you should check the original Liferay documentation. In my case, for showcase purpose, I will do a few changes.

First I will add an extra "TEST" action permission for JournalPortlet:

Extra "TEST" permission for Journal Portlet
Extra "TEST" permission for Journal Portlet

I will also remove the default ADD_DISCUSSION and VIEW permissions for guest:

Removed permissions for Journal Portlet
Removed permissions for Journal Portlet

Since we added a new action, which has not been yet translated, we should also create a translation for it. That's where we need translation files which we created earlier. In my case I only have a single "Language.properties" but of course you can add different translations. In these files you can add translation like you would normally do in any other module. Just keep in mind your translation for resource action has to start with action. prefix:

action.TEST=Test permission

Deploy module

The deployment in that case is simple and "standard". Simply build a JAR & deploy it to the deploy directory of your Liferay.

Testing our changes

That's it! Once deployed your permissions are ready:

Test permission visible in roles panel
Test permission visible in roles panel

No view permission for newly created web content

Override definition of permissions in non OSGi module

Sometimes you also have to deal with overriding permissions in modules which are not OSGi ones.

Note I think, at least for now, the portal-impl is the only non osgi module where permissions are defined

Note 2 The steps I describe worked for me for different 7.4 versions (including GA18 which is used in the blog samples) and I think it will work in future as well but you can never be sure. This isn't a standard way of handling things in Liferay. Sadly I haven't found any better way of doing that.

This means you can't simply use fragments to override them. Luckily we still can make it work.

The steps are quite similar to the ones we did for OSGi one so for the same things I will not describe them again. Basically we need to (I'm overriding documentlibrary.xml from portal-impl but the steps are of course the same for other XML permissions files):

  • Create a module.
  • Create default-ext.xml and documentlibrary-ext.xml (in my example! You might need another one) files - just like we did

in OSGi module override.

  • Add our default-ext.xml to the portal-ext.properties or to the Liferay environment variables for Docker environments.
  • Deploy our module to proper place.

Like you can see it's almost the same with tiny changes - we don't need to define portlet.properties and we don't need to define a Fragment-Host in our bnd.bnd. This is because we're not using fragments here.

Inform Liferay about our custom default-ext.xml file

After you created your module and made your changes in default-ext.xml and documentlibrary-ext.xml, which I won't describe again but you can take a look on a sources , you need to inform Liferay about our custom default-ext.xml file You can do that by adding a line to your:

  • portal-ext.properties
  • Docker environment variables

depending on your environment type.

For the portal-ext.properties file it should look like that:

resource.actions.configs=META-INF/resource-actions/default.xml,resource-actions/default.xml,resource-actions/default-ext.xml

While for docker environments the env variable should look like:

LIFERAY_RESOURCE_PERIOD_ACTIONS_PERIOD_CONFIGS=META-INF/resource-actions/default.xml,resource-actions/default.xml,resource-actions/default-ext.xml

What exactly is the value we used? Well this property defines list of resource action configurations which are read from classpath. So you first have to check up the original value in portal.properties file of Liferay's portal-impl module

Original resource-actions value
Original resource-actions value
and then add your default-xml. Of course, you can name it differently.

Deploying our module

Now how do we deploy that? We can't simply deploy our OSGi module as usual - it won't work. Instead, we need it to be available in classpath during Liferay start up. To do that we should place our JAR into tomcat/lib directory.

So basically:

  • Create a JAR
  • Put it into liferay/tomcat/lib/ directory
  • Restart the server

In my liferay-blog-samples I also overrode the Gradle's deploy task to make it more automatic

deploy() {
into "$rootDir/docker/liferay/tomcat/lib"
}

But of course keep in mind this works because I use Gradle and the samples have the docker-compose environment defined in the same repository.

Testing our changes

Once we do that we can verify that our changes work:

Extra XYZ permission for document
Extra XYZ permission for document

Changed permissions for GUEST and SITE_MEMBER

As you might've seen I haven't specified any translation for my XYZ resource action. Of course, you can do that just like in the OSGi example.

Possible issue with overriding portal-impl permissions on first Liferay startup

One issue I have encountered while overriding portal-impl permissions definitions is that it won't work if you start up Liferay for the first time, with empty database when Liferay creates tables and basic data.

So instead you should :

  • Let Liferay start for the first time.
  • Deploy your portal-impl override and add portal-ext/docker env property.
  • Restart the server.

Of course this won't be necessary in existing projects where you already have some data in a database.

I don't know why this happens but since this is only an issue if you're starting a totally fresh database I didn't go into the details and didn't try to find exact reason.

Final thoughts

I believe changing permissions in Liferay core modules should be quite easy for you know. The ways of doing that are not that hard - especially for OSGi modules it's easy and straightforward but at the same time these things are not too well documented and in some projects I have seen some weird ways of doing such changes. That's why I decided to create a small blog post to show how it can be done in quite easy way.

Of course maybe there is still some place for improvement - if you know some, let me know at [email protected] and I will update this article.

Good luck and if you need any help you can always send me an email. I will do my best to help.

Copyright: Rafał Pydyniak