Table of Contents
Portlet applications may invoke the services provided by Liferay Portal by using the portal-client.jar client library.
It is also possible to use Liferay Service Builder to develop your portlets using the same service oriented architecture that Liferay Portal is based on.
The following sections describe the most important services that can be used. Refer the documentation in the public Liferay wiki for more information
The Permissions service is provided by Liferay Portal to developers to write security related functionality in their own portlets. Currently this functionality is only provided for portlets developed within the Liferay sources or through the extension environment.
Fine grain permissioning is one of the main new features of Liferay Portal 4. Developers can now implement access security into their custom portlets, giving administrators and users a lot more control over their portlets and contents. This document will provide a reference for implementing this new security feature into their custom portlets. Developers should first read the Security and Permissions section of the Liferay User Guide before continuing with this document.
Adding fine grain permissioning to custom portlets consists of four main steps (also known as DRAC):
Define all resources and their permissions.
For all the resources defined in step 1, register them into the permission system. This is also known simply as “adding resources.”
Associate the necessary permissions to these resources.
Check permission before returning resources.
In this section, each of the four main steps in adding Liferay’s security feature into custom portlets (built on top of the Liferay portal) will be explained. The following are two definitions that are important to remember.
A generic term for any object represented in the portal. Example of resources includes portlets (e.g., Message Boards, Calendar, etc.), Java classes (e.g., Message Board Topics, Calendar Events, etc.), and files (e.g., documents, images, etc.)
An action acting on a resource. For example, the view in “viewing the calendar portlet” is defined as a permission in Liferay.
Keep in mind that the permission for a portlet resource is implemented a little differently from the other resources such as Java classes and files. In each of the subsections below, the permission implementation for the portlet resource is explained first, then the model (and file) resource.
For your custom portlet, Liferay portal needs to know whether
there are resources that require permission and whether there are
custom permissions. The default configuration is encapsulated in an
XML file found in the
portal/portal-ejb/classes/resource-actions
directory, you might use it as a reference to create a similar file
for your portlet. If your portlet only needs the view and the
configuration permission, and that the portlet doesn’t use any
models with permission, then you do not need to create this XML
file. The reason is that all portlets in Liferay automatically
inherit these permissions. However, if your portlet does have custom
permission and/or uses models that have custom permissions, then you
will need to create an XML file defining the resources and actions.
Let’s take a look at blogs.xml
in
portal/portal-ejb/classes/resource-actions
and
see how the blogs portlet defined these resources and
actions:
<?xml version="1.0"?> <resource-action-mapping> <portlet-resource> <portlet-name>33</portlet-name> <supports> <action-key>ADD_ENTRY</action-key> <action-key>CONFIGURATION</action-key> <action-key>VIEW</action-key> </supports> <community-defaults> <action-key>VIEW</action-key> </community-defaults> <guest-defaults> <action-key>VIEW</action-key> </guest-defaults> <guest-unsupported> <action-key>ADD_ENTRY</action-key> </guest-unsupported> </portlet-resource> <model-resource> <model-name>com.liferay.portlet.blogs.model.BlogsCategory</model-name> <portlet-ref> <portlet-name>33</portlet-name> </portlet-ref> <supports> <action-key>DELETE</action-key> <action-key>PERMISSIONS</action-key> <action-key>UPDATE</action-key> <action-key>VIEW</action-key> </supports> <community-defaults> <action-key>VIEW</action-key> </community-defaults> <guest-defaults> <action-key>VIEW</action-key> </guest-defaults> <guest-unsupported> <action-key>UPDATE</action-key> </guest-unsupported> </model-resource> <model-resource> <model-name>com.liferay.portlet.blogs.model.BlogsEntry</model-name> <portlet-ref> <portlet-name>33</portlet-name> </portlet-ref> <supports> <action-key>ADD_COMMENT</action-key> <action-key>DELETE</action-key> <action-key>PERMISSIONS</action-key> <action-key>UPDATE</action-key> <action-key>VIEW</action-key> </supports> <community-defaults> <action-key>VIEW</action-key> </community-defaults> <guest-defaults> <action-key>VIEW</action-key> </guest-defaults> <guest-unsupported> <action-key>UPDATE</action-key> </guest-unsupported> </model-resource> </resource-action-mapping>
In the XML, the first thing defined is the portlet itself.
Right under the root element
,
we have a child element called
<resource-action-mapping>
<portlet-resource>
. In this element, we
define the portlet name, which is 33 in our case. Next, we list
all the actions this portlet supports under the
tag.
Keep in mind that this is at the portlet level. To understand what
should be listed here, developers should ask themselves what
actions belong to the portlet itself or what actions are performed
on the portlet that may require a security check. In our case,
users need permission to add an entry
(<supports>
ADD_ENTRY
), configure blogs portlet settings
(CONFIGURATION
), and view the blogs itself
(VIEW
). Each of these supported permissions
is within its own <action-key>
tag.
After we’ve defined all the actions that require a check, we move
on to define some of the default permission settings. The
community-defaults
tag defines what actions
are permitted by default for this portlet on the community (group)
page the portlet resides. Put it another way, what should a user
that has access to the community this portlet resides be able to
do minimally? For the blogs portlet, a user with access to the
community containing the blogs portlet should be able to view it.
Likewise, the guest-defaults
tag defines what
actions are permitted by default to guests visiting a layout
containing this portlet. So if a guest has access to the community
page that contains a blogs portlet, the guest should, at the very
least, be able to view the portlet according to
blogs.xml
(not necessarily the content of the
portlet). Otherwise, the guest will see an error message within
the portlet. Depending on your custom portlet, you may add more
actions here that make sense. The guest-unsupported
tag contains actions that a visiting guest should never
be able to do. For example, the guest visiting the blogs portlet
should never be able to add a blog entry since the blog belongs to
either a user or a group of users. So even if a user wants to
grant guests the ability to add a blog entry to her blog, there is
no way for her to grant that permission because the
blogs.xml
doesn’t permit such an action for
guests.
After defining the portlet as a resource, we move on to
define models within the portlet that also require access check.
The model resource is surrounded by the
<model-resource>
tag. Within this tag,
we first define the model name. This must be the fully qualified
Java class name of the model. Next we define the portlet name that
this model belongs to under the portlet-ref
tag. Though unlikely, a model can belong to multiple
portlets, which you may use multiple
<portlet-name>
tags to define. Similar
to the portlet resource element, the model resource element also
allows you to define a supported list of actions that require
permission to perform. You must list out all the performable
actions that require a permission check. As you can see for a blog
entry, a user must have permission in order to add comments to an
entry, delete an entry, change the permission setting of an entry,
update an entry, or simply to view an entry. The
<community-defaults>
tag, the
<guest-defaults>
tag, and the
<guest-unsupported>
tag are all similar
in meaning to what’s explained for portlet resource in section
3.1.1.
After defining your permission scheme for your custom
portlet, you then need to tell Liferay the location of this file.
For Liferay core, the XML file would normally reside in
portal/portal-ejb/classes/resource-actions
and a reference to the file would appear in the
default.xml
file. For the extension
environment, the recommended setup is to put your XML file in
ext/ext-ejb/classes/resource-actions
. Create
a file called default-ext.xml
and model it
after the default.xml
file. Add all your
custom resource-action XML files in the
default-ext.xml
file. Then copy the property
resource.actions.configs found in
portal.properties
and paste it into
portal-ext.properties
. Lastly, add a comma to
the end of the property value and then add the path to your
default-ext.xml
file. (i.e.
resource.actions.configs=resource-actions/default.xml,resource-actions/default-ext.xml
)
Below is an example of the default.xml
file.
<?xml version="1.0"?> <resource-action-mapping> <resource file="resource-actions/portal.xml" /> <resource file="resource-actions/blogs.xml" /> <resource file="resource-actions/bookmarks.xml" /> <resource file="resource-actions/calendar.xml" /> <resource file="resource-actions/communities.xml" /> <resource file="resource-actions/documentlibrary.xml" /> <resource file="resource-actions/imagegallery.xml" /> <resource file="resource-actions/journal.xml" /> <resource file="resource-actions/messageboards.xml" /> <resource file="resource-actions/polls.xml" /> <resource file="resource-actions/shopping.xml" /> <resource file="resource-actions/wiki.xml" /> </resource-action-mapping>
After defining resources and actions, the next task is to
write code that adds resources into the permissioning system. A lot
of the logic to add resources is encapsulated in the
ResourceLocalServiceImpl
class. So adding
resources is as easy as calling the add resource method in
ResourceLocalServiceUtil
class.
public void addResources( String companyId, String groupId, String userId, String name, String primKey, boolean portletActions, boolean addCommunityPermissions, boolean addGuestPermissions);
For all the Java objects that require access permission, you
need to make sure that they are added as resources every time a new
one is created. For example, every time a user adds a new entry to
her blog, the addResources
(…) method is called
to add the new entry to the resource system. Here’s an example of
the call from the BlogsEntryLocalServiceImpl
class.
ResourceLocalServiceUtil.addResources( entry.getCompanyId(), entry.getGroupId(), entry.getUserId(), BlogsEntry.class.getName(), entry.getPrimaryKey().toString(), false, addCommunityPermissions, addGuestPermissions);
The parameters companyId
,
groupId
, and userId
should
be self explanatory. The name
parameter is the
fully qualified Java class name for the resource object being added.
The p
rimKey
parameter is
the primary key of the resource object. As for the
portletActions
parameter, set this to true if
you’re adding portlet action permissions. In our example, we set it
to false
because we’re adding a model resource,
which should be associated with permissions related to the model
action defined in blogs.xml
. The
addCommunityPermissions
and the
addGuestPermissions
parameters are inputs from
the user. If set to true, ResourceLocalService
will then add the default permissions to the current community group
and the guest group for this resource respectively.
If you would like to provide your user the ability to choose
whether to add the default community permission and the guest
permission for the resources within your custom portlet, Liferay
has a custom JSP tag you may use to quickly add that
functionality. Simply insert the
<liferay-ui:input-permissions />
tag
into the appropriate JSP and the checkboxes will show up on your
JSP. Of course, make sure the tag is within the appropriate
<form>
tags.
To prevent having a lot of dead resources taking up space in
the Resource_
database table, you must
remember to remove them from the Resource_
table when the resource is no longer applicable. Simply
call the deleteResource(…)
method in
ResourceLocalServiceUtil
. Here’s an example
of a blogs entry being removed:
ResourceLocalServiceUtil.deleteResource( entry.getCompanyId(), BlogsEntry.class.getName(), Resource.TYPE_CLASS, Resource.SCOPE_INDIVIDUAL, entry.getPrimaryKey().toString());
On the portlet level, no code needs to be written in order to have the permission system work for your custom portlet. Your custom portlet will automatically have all the permission features. If you’ve defined any custom permissions (supported actions) in your portlet-resource tag in section 3.1, those are automatically added to a list of permissions and users can readily choose them. Of course, for your custom permissions to have any value, you’ll need to show or hide certain functionality in your portlet. You can do that by checking the permission first before performing the intended functionality. This will be covered in section 3.4.
In order to allow a user to set permissions on the model
resources, you will need to expose the permission interface to the
user. This can be done by adding two Liferay UI tag to your JSP.
The first one is the
<liferay-security:permissionsURL>
tag
which returns a URL that takes the user to the page to configure
the permission settings. The second tag is the
<liferay-ui:icon>
tag that shows a
permission icon to the user. Below is an example found in the file
view_entry_content.jsp.
<liferay-security:permissionsURL modelResource="<%= BlogsEntry.class.getName() %>" modelResourceDescription="<%= entry.getTitle() %>" resourcePrimKey="<%= entry.getPrimaryKey().toString() %>" var="entryURL" /> <liferay-ui:icon image="permissions" url="<%= entryURL %>" />
The attributes you need to provide to the first tag are
modelResource
,
modelResourceDescription
,
resourcePrimKey
, and var. The
modelResource
attribute is the fully
qualified Java object class name. It then gets translated in
Language.properties
to a more readable name
(underlined in red in figure 3.3.2.1).
model.resource.com.liferay.portlet.blogs.model.BlogsEntry=Entry
As for the modelResourceDescription
attribute, you can pass in anything that best describes this model
instance. In the example, the blogs title was passed in, which is
reflected in figure 3.3.2.1 with the blue underline. The
resourcePrimKey
attribute is simply the
primary key of your model instance. The var
attribute is the variable name this URL String will get
assigned to. This variable is then passed to the
<liferay-ui:icon>
tag so the permission
icon will have the proper URL link. There’s also an optional
attribute redirect
that’s available if you
want to override the default behavior of the upper right arrow
link shown in figure 3.3.2.1. That is all you need to do to enable
users to configure the permission settings for model
resources!!
The last major step to implementing permission to your custom portlet is to check permission. This may be done in a couple of places. For example, your business layer should check for permission before deleting a resource, or your user interface should hide a button that adds a model (e.g., a calendar event) if the user does not have permission to do so.
Similar to the other steps, the default permissions for the
portlet resources are automatically checked for you. You do not
need to implement anything for your portlet to discriminate
whether a user is allowed to view or to configure the portlet
itself. However, you do need to implement any custom permission
you have defined in your resource-actions XML file. In the blogs
portlet example, one custom supported action is
ADD_ENTRY
. There are two places in the source
code that check for this permission. The first one is in the file
view_entries.jsp
. The presence of the add
entry button is contingent on whether the user has permission to
add entry (and also whether the user is in tab one).
<% boolean showAddEntryButton = tabs1.equals("entries") && PortletPermission.contains(permissionChecker, plid, PortletKeys.BLOGS, ActionKeys.ADD_ENTRY); %>
The second place that checks for the add entry permission is
in the file BlogsEntryServiceImpl
. (Notice
the difference between this file and the
BlogsEntryLocalServiceImpl
.) In the
addEntry(…)
method, a call is made to check
whether the incoming request has permission to add entry.
PortletPermission.check( getPermissionChecker(), plid, PortletKeys.BLOGS, ActionKeys.ADD_ENTRY);
If the check fails, it throws a
PrincipalException
and the add entry request
aborts. You’re probably wondering what the PortletPermission class
and the PermissionChecker class do. Let’s take a look at these two
classes.
The PermissionChecker class has a method called
hasPermission(…)
that checks whether a user
making a resource request has the necessary access permission.
If the user is not signed in (guest user), it checks for guest
permissions. Otherwise, it checks for user permissions. This
class is available to you in two places. First in your business
logic layer, you can obtain an instance of the PermissionChecker
by calling the getPermissionChecker()
method inside your ServiceImpl
class. This
method is available because all ServiceImpl
(not LocalServiceImpl
) extends the
PrincipalBean
class, which implements the
getPermissionChecker()
method. The other
place where you can obtain an instance of the PermissionChecker
class is in your JSP files. If your JSP file contains the
portlet tag <portlet:defineObjects />
or includes another JSP file that does, you’ll have an instance
of the PermissionChecker class available to you via the
permissionChecker
variable. Now that you
know what the PermissionChecker
does and
how to obtain an instance of it, let’s take a look at Liferay’s
convention in using it.
PortletPermission
is a helper class
that makes it easy for you to check permission on portlet
resources (as oppose to model resources, covered later in
section 3.4.2). It has two static methods called
check(…)
and another two called
contains(…)
. They are all essentially the
same. The two differences between them are:
Only one check(…)
method and one
contains(…)
method take in the portlet
layout ID variable (plid
).
The check(…)
methods throw a new
PrincipalException
if user does not
have permission, and the contains(…)
methods return a boolean indicating whether user
has permission.
The contains(…)
methods are meant to
be used in your JSP files since they return a boolean instead of
throwing an exception. The check(…) methods are meant to be
called in your business layer (ServiceImpl). Let’s revisit the
blogs portlet example below. (The addEntry(…) method is found in
BlogsEntryServiceImpl.)
public BlogsEntry addEntry( String plid, String categoryId, String[] tags, String title, String content, int displayDateMonth, int displayDateDay, int displayDateYear, int displayDateHour, int displayDateMinute, boolean addCommunityPermissions, boolean addGuestPermissions) throws PortalException, SystemException { PortletPermission.check( getPermissionChecker(), plid, PortletKeys.BLOGS, ActionKeys.ADD_ENTRY); return BlogsEntryLocalServiceUtil.addEntry( getUserId(), plid, categoryId, tags, title, content, displayDateMonth, displayDateDay, displayDateYear, displayDateHour, displayDateMinute, addCommunityPermissions, addGuestPermissions); }
Before the addEntry(…)
method calls
BlogsEntryLocalServiceUtil.addEntry(…)
to
add a blogs entry, it calls
PortletPermission.check(…)
to validate user
permission. If the check fails, a
PrincipalException
is thrown and an entry
will not be added. Note the parameters passed into the method.
Again, the getPermissionChecker()
method is
readily available in all ServiceImpl
classes. The plid
variable is passed into
the method by its caller (most likely from a
PortletAction
class).
PortletKeys.BLOGS
is just a static
String
indicating that the permission check
is against the blogs portlet.
ActionKeys.ADD_ENTRY
is also a static
String to indicate the action requiring the permission check.
You’re encouraged to do likewise with your custom portlet names
and custom action keys.
Whether you need to pass in a portlet layout ID (plid) depends on whether your custom portlet supports multiple instances. Let’s take a look at the message board portlet for example. A community may need three separate page layouts, each having a separate instance of the message board portlet. Only by using the portlet layout ID will the permission system be able to distinguish the three separate instances of the message board portlet. This way, permission can be assigned separately in all three instances. Though in general, most portlets won’t need to use the portlet layout ID in relation to the permission system.
Since the ServiceImpl
class extends
the PrincipalBean
class, it has access to
information of the current user making the service request.
Therefore, the ServiceImpl
class is the
ideal place in your business layer to check user permission.
Liferay’s convention is to implement the actual business logic
inside the LocalServiceImpl
methods, and
then the ServiceImpl
calls these methods
via the LocalServiceUtil
class after the
permission check completes successfully. Your
PortletAction
classes should make calls to
ServiceUtil
(wrapper to
ServiceImpl
) guaranteeing that permission
is first checked before the request is fulfilled.
Checking model resource permission is very similar to checking portlet resource permission. The only major difference is that instead of calling methods found in the PortletPermission class mention previously, you need to create your own helper class to assist you in checking permission. The next section will detail how this is done.
It is advisable to have a helper class to help check
permission on your custom models. This custom permission class
is similar to the PortletPermission class but is tailored to
work with your custom models. While you can implement this class
however you like, we encourage you to model after the
PortletPermission class, which contains four static methods.
Let’s take a look at the
BlogsEntryPermission
class.
public class BlogsEntryPermission { public static void check( PermissionChecker permissionChecker, String entryId, String actionId) throws PortalException, SystemException { if (!contains(permissionChecker, entryId, actionId)) { throw new PrincipalException(); } } public static void check( PermissionChecker permissionChecker, BlogsEntry entry, String actionId) throws PortalException, SystemException { if (!contains(permissionChecker, entry, actionId)) { throw new PrincipalException(); } } public static boolean contains( PermissionChecker permissionChecker, String entryId, String actionId) throws PortalException, SystemException { BlogsEntry entry = BlogsEntryLocalServiceUtil.getEntry(entryId); return contains(permissionChecker, entry, actionId); } public static boolean contains( PermissionChecker permissionChecker, BlogsEntry entry, String actionId) throws PortalException, SystemException { return permissionChecker.hasPermission( entry.getGroupId(), BlogsEntry.class.getName(), entry.getPrimaryKey().toString(), actionId); } }
Again, the two check(…) methods are meant to be called in your business layer, while the two contains(…) methods can be used in your JSP files. As you can see, it’s very similar to the PortletPermission class. The two notable differences are:
Instead of having the portletId
as one of the parameters, the methods in this custom class
take in either an entryId
or a
BlogsEntry
object.
None of the methods need to receive the portlet layout
ID (plid
) as a parameter. (Your custom
portlet may choose to use the portlet layout ID if need
be.)
Let’s see how this class is used in the blogs portlet code.
public BlogsEntry getEntry(String entryId) throws PortalException, SystemException { BlogsEntryPermission.check( getPermissionChecker(), entryId, ActionKeys.VIEW); return BlogsEntryLocalServiceUtil.getEntry(entryId); }
In the BlogsEntryServiceImpl
class is
a method called getEntry(…)
. Before this
method returns the blogs entry object, it calls the custom
permission helper class to check permission. If this call
doesn’t throw an exception, the entry is retrieved and returned
to its caller.
<c:if test="<%= BlogsEntryPermission.contains(permissionChecker, entry, ActionKeys.UPDATE) %>"> <portlet:renderURL windowState="<%= WindowState.MAXIMIZED.toString() %>" var="entryURL"> <portlet:param name="struts_action" value="/blogs/edit_entry" /> <portlet:param name="redirect" value="<%= currentURL %>" /> <portlet:param name="entryId" value="<%= entry.getEntryId() %>" /> </portlet:renderURL> <liferay-ui:icon image="edit" url="<%= entryURL %>" /> </c:if>
In the view_entry_content.jsp
file,
the BlogsEntryPermission.contains(…)
method
is called to check whether or not to show the edit button.
That’s all there is to it!
Let’s review what we’ve just covered. Implementing permission
into your custom portlet consists of four main steps. First step is to
define any custom resources and actions. Next step is to implement
code to register (or add) any newly created resources such as a
BlogsEntry
object. The third step is to provide
an interface for the user to configure permission. Lastly, implement
code to check permission before returning resources or showing custom
features. Two major resources are portlets and Java objects. There is
not a lot that needs to be done for the portlet resource to implement
the permission system since Liferay Portal has a lot of that work done
for you. You mainly focus your efforts on any custom Java objects
you’ve built. You’re now well on your way to implement security to
your custom Liferay portlets! For other user guides, please visit the
Liferay
documentation page
If you’re wondering how the Liferay user roles, community
roles, and organization / location roles should be implemented in
your custom portlet, this brief section will address that. The short
answer is, nothing needs to be developed specifically for user,
community, and organization / location roles to work with your
custom portlets. Liferay’s permission system has all that provided
for you. When the hasUserPermission(…)
method
is called within the PermissionChecker
class,
Liferay checks all the roles the current user has, whether they’re
organization / location roles, community roles, or user
roles.
Here’s a brief outline of how you can use your own security system in Liferay.
Create your own PermissionChecker
class that extends Liferay’s
PermissionChecker
class.
Register this new class in
portal.properties
(or
portal-ext.properties
for the EXT
environment) under the permissions.checker
property.
Override the hasUserPermission(…)
method and the hasGuestPermission(…)
method
with your own calls to your permission system.
You can call the setValues(…)
method
to pull in parameters from the request object that your
permission checker might need (e.g., userId,
projected
, etc).
You can call the resetValues(…)
method to wipe out old parameters.
Override the isAdmin(…)
method.
Reviewing how Liferay stores all the permission information in the database may help you gain a better understanding to the entire permission system.
The resource_
table contains all the
registered resources outlined in section 3.2.
Every possible secure action that can be done to a
resource will result in a row in the permission_
table. For example, a BlogsEntry
resource may have a row in permission_
for
the view action, and another for the update action.
Whether a user has permission to a resource depends on the
roles the user has, or the community (groups) and organization
the user is in (green tables). If those roles or groups contain
the needed permissionId
in the permissions
table (in blue), then the user has access to the
resource.