Sun GlassFish Web Space Server 10.0 Developer's Guide

Security and Permissions Service

The Sun GlassFish Web Space Server implements a fine-grained permissions system, which developers can use to implement access security into their custom portlets, giving administrators and users a lot more control over their portlets and content. This section of the document will provide a reference for implementing security into custom portlets.

Overview

    Adding fine-grained permissions to custom portlets consists of four main steps. This procedure is also known as DRAC.

  1. Define all resources and their permissions.

  2. Register all the resources mentioned in step 1 into the permissions system. This is also known simply as “adding resources”.

  3. Associate the necessary permissions to these resources.

  4. Check permission before returning resources.

Implementing Permissions

In this section, each of the four main steps in adding the security feature available in the Sun GlassFish Web Space Server into custom portlets (built on top of the Sun GlassFish Web Space Server portal) will be explained. The following are two definitions that are important to remember.


public BlogsEntry addEntry(
			long plid, String title, String content, int displayDateMonth,
			int displayDateDay, int displayDateYear, int displayDateHour,
			int displayDateMinute, String[] tagsEntries,
			boolean addCommunityPermissions, boolean addGuestPermissions,
			ThemeDisplay themeDisplay)
		throws PortalException, SystemException {

		PortletPermissionUtil.check(
			getPermissionChecker(), plid, PortletKeys.BLOGS,
			ActionKeys.ADD_ENTRY);

		return blogsEntryLocalService.addEntry(
			getUserId(), plid, title, content, displayDateMonth, displayDateDay,
			displayDateYear, displayDateHour, displayDateMinute, tagsEntries,
			addCommunityPermissions, addGuestPermissions, themeDisplay);
}

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 are 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 us 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 will not 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 mentioned 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 your implementation after the PortletPermission class, which contains four static methods. Let us take a look at the BlogsEntryPermission class.


public class BlogsEntryPermission {

		public static void check(
				PermissionChecker permissionChecker, long 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, long 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.getEntryId(),
				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 is very similar to the PortletPermission class. The two notable differences are:

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

  2. 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 us 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 does not 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 is all there is to it!

Let us review what we have 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 have built. You are now well on your way to implement security to your custom Liferay portlets!