Your message has been sent.
This article has been saved to your account.
Go to my account
This article has been emailed to your Kindle.
Send this article
Complete the form below to send this article, Common API in Liferay Portal Systems Development, to a friend (or to yourself). We will never share your details (or your friend's) with anyone. For more information, read our Privacy Policy.
This article by Jonas X.Yuan, author of Liferay Portal Systems Development will introduce user management and password policy first. Then it will address authentication and authorization. LDAP and SSO integration will be introduced afterwards. Tracking and auditing services API will be addressed in detail after this. Finally, it will address scripting engine, polling, web services, WSRP, and the OSGi framework.
By the end of this article, you will have learned about the following:
- User management
- Password policy
- Authentication and authorization
- LDAP and SSO
- Tracking and auditing
- Rules engine and reporting engine
(For more resources on Liferay, see here.)
User management
The portal has defined user management with a set of entities, such as, User, Contact, Address, EmailAddress, Phone, Website, and Ticket, and so on at /portal/service.xml. In the following section, we're going to address the User entity, its association, and relationship.
Models and services
The following figure depicts these entities and their relationships. The entity User has a one-to-one association with the entity Contact, which may have many contacts as children. And the entity Contact has a one-to-one association with the entity Account, which may have many accounts as children. The entity Contact can have a many-to-many association with the entities Address, EmailAddress, Phone, Website, and Ticket. Logically, the entities Address, EmailAddress, Phone, Website, and Ticket may have a many-to-many association with the other entities, such as Group, Organization, and UserGroup as shown in the following image:

Services
The following table shows user-related service interfaces, extensions, utilities, wrappers, and their main methods:
|
Interface |
Extension |
Utility/Wrapper |
Main methods |
|
UserService, UserLocalService |
PersistedModelLocalService |
User(Local)ServiceUtil, User(Local)ServiceWrapper |
add*, authenticate*, check*, decrypt*, delete*, get*, has*, search, unset*, update*, and so on. |
|
ContactService, ContactLocalService |
persistedmodellocalservice> |
Contact(Local)ServiceUtil, Contact(Local)ServiceWrapper |
add*, create*, delete*, get*, update*, dynamicQuery, and so on. |
|
AccountService, AccountLocalService |
Account(Local)ServiceUtil, Account(Local)ServiceWrapper |
add*, create*, delete*, get*, update*, dynamicQuery, and so on. |
|
|
AddressService, AddressLocalService |
Address(Local)ServiceUtil, Address(Local)ServiceWrapper |
add*, create*, delete*, get*, update*, dynamicQuery, and so on. |
|
|
EmailAddressService, EmailAddressLocalService |
PersistedModelLocalService |
Address(Local)ServiceUtil, Address(Local)ServiceWrapper |
add*, create*, delete*, get*, update*, dynamicQuery, and so on. |
|
PhoneService, PhoneLocalService |
PersistedModelLocalService |
Phone(Local)ServiceUtil, Phone(Local)ServiceWrapper |
add*, create*, delete*, get*, update*, dynamicQuery, and so on. |
|
WebsiteService, WebsiteLocalService |
PersistedModelLocalService |
Website(Local)ServiceUtil, Website(Local)ServiceWrapper |
add*, create*, delete*, get*, update*, dynamicQuery, and so on. |
|
TicketLocalService |
PersistedModelLocalService |
TicketLocalServiceUtil, TicketLocalServiceWrapper |
add*, create*, delete*, get*, update*, dynamicQuery, and so on. |
Relationships
The portal also defined many-to-many relationships between User and Group, User and Organization, User and Team, User and UserGroup, as shown in the following code:
<column name="groups" type="Collection"
entity="Group" mapping-table="Users_Groups" />
<column name="userGroups" type="Collection"
entity="UserGroup" mapping-table="Users_UserGroups" />
In particular, you will be able to find a similar definition at /portal/service.xml.
Sample portal service portlet
The portal provides a sample portal service plugin called sample-portal-service-portlet (refer to the plugin details at /portlets/sample-portal-service-portlet). The following is the code snippet:
List organizations =
OrganizationServiceUtil.getUserOrganizations(
themeDisplay.getUserId());
// add your logic
The previous code shows how to consume Liferay services through regular Java calls. These services include com.liferay.portal.service.OrganizationServiceUtil and the model involves com.liferay.portal.model.Organization. Similarly, you can use other services, for example, com.liferay.portal.service.UserServiceUtil and com.liferay.portal.service.GroupServiceUtil; and models, for example, com.liferay.portal.model.User, com.liferay.portal.model.Group. Of course, you can find other services and models—you will find services located at the com. liferay.portal.service package in the /portal-service/src folder. In the same way, you will find models located at the com.liferay.portal.model package in the /portal-service/src folder.
What's the difference between *LocalServiceUtil and *ServiceUtil? The sign * represents models, for example, Organization, User, Group, and so on. Generally speaking, *Service is the remote service interface that defines the service methods available to remote code. *ServiceUtil has an additional permission check, since this method might be called as a remote service. *ServiceUtil is a facade class that combines the service locator with the actual call to the service *Service. While *LocalService is the internal service interface,*LocalServiceUtil is a facade class that combines the service locator with the actual call to the service *LocalService. *Service has a PermissionChecker in each method, and *LocalService usually doesn't have the same.
Authorization
Authorization is a process of finding out if the user, once identified, is permitted to have access to a resource. The portal implemented authorization by assigning permissions via roles and checking permissions, and this is called Role-Based Access Control (RBAC).
The following figure depicts an overview of authorization. A user can be a member of Group, UserGroup, Organization, or Team. And a user or a group of users, such as Group, UserGroup, or Organization can be a member of Role. And the entity Role can have many ResourcePermission entities associated with it, while the entity ResourcePermission may contain many ResourceAction entities, as shown in the following diagram:

The following table shows the entities Role, ResourcePermission, and ResourceAction:
|
Interface |
Extension |
Wrapper/SOAP |
Main methods |
|
Role |
RoleModel, PersistedModel |
RoleWrapper, RoleSoap |
clone, compareTo, get*, set*, toCacheModel, toEscapedModel, and so on. |
|
ResourceAction |
ResourceActionModel, PersistedModel |
ResourceActionWrapper, ResourceActionSoap |
clone, compareTo, get*, set*, toCacheModel, toEscapedModel, and so on. |
|
ResourcePermission |
ResourcePermissionModel, PersistedModel |
ResourcePermissionWrapper, ResourcePermissionSoap |
clone, compareTo, get*, set*, toCacheModel, toEscapedModel, and so on. |
In addition, the portal specifies role constants in the class RoleConstants.
The entity ResourceAction gets specified with the columns name, actionId, and bitwiseValue as follows:
<column name="name" type="String" />
<column name="actionId" type="String" />
<column name="bitwiseValue" type="long" />
The entity ResourcePermission gets specified with the columns name, scope, primKey, roleId, and actionIds as follows:
<column name="name" type="String" />
<column name="scope" type="int" />
<column name="primKey" type="String" />
<column name="roleId" type="long" />
<column name="ownerId" type="long" />
<column name="actionIds" type="long" />
In addition, the portal specified resource permission constants in the class ResourcePermissionConstants
Password policy
The portal implements enterprise password policies and user account lockout using the entities PasswordPolicy and PasswordPolicyRel, as shown in the following table:
|
Interface |
Extension |
Wrapper/Soap |
Description |
|
PasswordPolicy |
PasswordPolicyModel, PersistedModel |
PasswordPolicyWrapper, PasswordPolicySoap |
Columns: name, description, minAge, minAlphanumeric, minLength, minLowerCase, minNumbers, minSymbols, minUpperCase, lockout, maxFailure, lockoutDuration, and so on. |
|
PasswordPolicyRel |
PasswordPolicyRelModel, PersistedModel |
PasswordPolicyRelWrapper, PasswordPolicyRelSoap |
Columns: passwordPolicyId, classNameId, and classPK. Ability to associate the entity PasswordPolicy with other entities. |
Passwords toolkit
The portal has defined the following properties related to the passwords toolkit in portal.properties:
passwords.toolkit=
com.liferay.portal.security.pwd.PasswordPolicyToolkit
passwords.passwordpolicytoolkit.generator=dynamic
passwords.passwordpolicytoolkit.static=iheartliferay
The property passwords.toolkit defines a class name that extends com.liferay.portal.security.pwd.BasicToolkit, which is called to generate and validate passwords.
If you choose to use com.liferay.portal.security.pwd.PasswordPolicyToolkit as your password toolkit, you can choose either static or dynamic password generation. Static is set through the property passwords.passwordpolicytoolkit.static and dynamic uses the class com.liferay.util.PwdGenerator to generate the password. If you are using LDAP password syntax checking, you will also have to use the static generator, so that you can guarantee that passwords obey their rules.
The passwords' toolkits get addressed in detail in the following table:
|
Class |
Interface |
Utility |
Property |
Main methods |
|
DigesterImpl |
Digester |
DigesterUtil |
passwords.digest.encoding |
digest, digestBase64, digestHex, digestRaw, and so on. |
|
Base64 |
None |
None |
None |
decode, encode, fromURLSafe, objectToString, stringToObject, toURLSafe, and so on. |
|
PwdEncryptor |
None |
None |
passwords.encryption.algorithm |
encrypt, default types: MD2, MD5, NONE, SHA, SHA-256, SHA-384, SSHA, UFC-CRYPT, and so on . |
Authentication
Authentication is the process of determining whether someone or something is, in fact, who or what it is declared to be. The portal defines the class called PwdAuthenticator for authentication, as shown in the following code:
public static boolean authenticate(
String login, String clearTextPassword,
String currentEncryptedPassword)
{
String encryptedPassword = PwdEncryptor.encrypt(
clearTextPassword, currentEncryptedPassword);
if (currentEncryptedPassword.equals(encryptedPassword))
{
return true;
}
}
As you can see, it encrypts the clear text password first into the variable encryptedPassword. It then tests whether the variable currentEncryptedPassword has the same value as that of the variable encryptedPassword or not. The classes UserLocalServiceImpl (the method authenticate) and EditUserAction (the method updateUser) call the class PwdAuthenticator for authentication.
A Message Authentication Code (MAC) is a short piece of information used to authenticate a message. The portal supports MAC through the following properties:
auth.mac.allow=false
auth.mac.algorithm=MD5
auth.mac.shared.key=
To use authentication with MAC, simply post to a URL as follows:
It passes the MAC in the password field. Make sure that the MAC gets URL encoded, since it might contain characters not allowed in a URL. Authentication with MAC also requires that you set the following property in system-ext.properties:
com.liferay.util.servlet.SessionParameters=false
As shown in the previous code, it encrypts session parameters, so that browsers can't remember them.
Authentication pipeline
The portal provides the authentication pipeline framework for authentication, as shown in the following code:
auth.pipeline.pre=com.liferay.portal.security.auth.LDAPAuth
auth.pipeline.post=
auth.pipeline.enable.liferay.check=true
As you can see, the property auth.pipeline.enable.liferay.check is set to true to enable password checking by the internal portal authentication. If it is set to false, essentially, password checking is delegated to the authenticators configured in the auth.pipeline.pre and auth.pipeline.post settings.
The interface com.liferay.portal.security.auth.Authenticator defines the constant values that should be used as return code from the classes implementing the interface. If authentication is successful, it returns SUCCESS; if the user exists but the passwords doesn't match, then it returns FAILURE. If the user doesn't exist in the system, it returns DNE. Constants get defined in the interface Authenticator.
As shown in the following table, the available authenticator is com.liferay.portal.security.auth.LDAPAuth:
|
Class |
Extension |
Involved properties |
Main Methods |
|
PasswordPolicyToolkit |
BasicToolkit |
passwords.passwordpolicytoolkit.charset.lowercase, passwords.passwordpolicytoolkit.charset.numbers, passwords.passwordpolicytoolkit.charset.symbols, passwords.passwordpolicytoolkit.charset.uppercase, passwords.passwordpolicytoolkit.generator, passwords.passwordpolicytoolkit.static |
generate, validate |
|
RegExpToolkit |
BasicToolkit |
passwords.regexptoolkit.pattern, passwords.regexptoolkit.charset, passwords.regexptoolkit.length |
generate, validate |
|
PwdToolkitUtil |
None |
passwords.toolkit |
Generate, validate |
|
PwdGenerator |
None |
None |
getPassword. getPinNumber |
Authentication token
The portal provides the interface com.liferay.portal.security.auth.AuthToken for the authentication token as follows:
auth.token.check.enabled=true
auth.token.impl=
com.liferay.portal.security.auth.SessionAuthToken
As shown in the previous code, the property auth.token.check.enabled is set to true to enable authentication token security checks. The checks can be disabled for specific actions via the property auth.token.ignore.actions or for specific portlets via the init parameter check-auth-token in portlet.xml.
The property auth.token.impl is set to the authentication token class. This class must implement the interface AuthToken. The class SessionAuthToken is used to prevent CSRF (Cross-Site Request Forgery) attacks.
The following table shows the interface AuthToken and its implementation:
|
Class |
Interface |
Properties |
Main Methods |
|
LDAPAuth |
Authenticator |
ldap.auth.method, ldap.referral, ldap.auth.password.encryption.algorithm, ldap.base.dn, ldap.error.user.lockout, ldap.error.password.expired, ldap.import.user.password.enabled, ldap.base.provider.url, auth.pipeline.enable.liferay.check, ldap.auth.required |
authenticateByEmailAddress, authenticateByScreenName, authenticateByUserId |
JAAS
Java Authentication and Authorization Service (JAAS) is a Java security framework for user-centric security to augment the Java code-based security. The portal has specified a set of properties for JAAS as follows:
portal.jaas.enable=false
portal.jaas.auth.type=userId
portal.impersonation.enable=true
The property portal.jaas.enable is set to false to disable JAAS security checks. Disabling JAAS would speed up login. Note that JAAS must be disabled, if administrators are able to impersonate other users. JAAS can authenticate users based on their e-mail address, screen name, user ID, or login, as determined by the property company.security.auth.type. By default, the class com.liferay.portal.security.jaas.PortalLoginModule loads the correct JAAS login module, based on what application server or servlet container the portal is deployed on. You can set a JAAS implementation class to override this behavior.
The following table shows this class and its associations:
|
Class |
Interface |
Properties |
Main methods |
|
AuthTokenImpl |
AuthToken |
auth.token.impl |
check, getToken |
|
AuthTokenWrapper |
AuthToken |
None |
check, getToken |
|
AuthTokenUtil |
None |
None |
check, getToken |
|
SessionAuthToken |
AuthToken |
auth.token.shared.secret |
check, getToken |
As you have noticed, the classes com.liferay.portal.kernel.security.jaas, PortalLoginModule, and com.liferay.portal.security.jaas.PortalLoginModule, implement the interface LoginModule, configured by the property portal.jaas.impl.
As shown in the following table, the portal has provided different login module implementation for different application servers or servlet containers:
|
Class |
Interface/Extension |
Package |
Main methods |
|
ProtectedPrincipal |
Principal |
com.liferay.portal.kernel.servlet |
getName, equals, hasCode, toString |
|
PortalPrincipal |
ProtectedPrincipal |
com.liferay.portal.kernel.security.jaas |
PortalPrincipal |
|
PortalRole |
PortalPrincipal |
com.liferay.portal.kernel.security.jaas |
PortalRole |
|
PortalGroup |
PortalPrincipal, java.security.acl.Group |
com.liferay.portal.kernel.security.jaas |
addMember, isMember, members, removeMember |
|
PortalLoginModule |
javax.security.auth.spi.LoginModule |
com.liferay.portal.kernel.security.jaas, com.liferay.portal.security.jaas |
abort, commit, initialize, login, logout |
LDAP and SSO
The portal supports integration of SSO (Single Sign-on), CAS, NTLM, OpenID, OpenSSO, Facebook, and SiteMinder. As shown in the following diagram, the portal also supports integration of LDAP (Lightweight Directory Access Protocol) authentication, import, and export. LDAP server, which can be integrated into the portal, includes Apache Directory Server, Fedora Directory Server, Microsoft Active Directory Server, Novell eDirectory, OpenLDAP, OpenDS, and so on:

LDAP
As shown in the previous diagram, the portal does have capabilities to synchronize user custom attributes between Liferay and LDAP, to implement LDAP pagination via SearchControls, to configure the portal to create a role for each LDAP group, to configure the portal to either import or not import each LDAP user's password, to configure the portal to either export or not export each LDAP user's password, to override LDAP import and export processes via Spring beans, and so on.
As mentioned earlier, the portal defined the class LDAPAuth, implemented the interface Authenticator and the class LDAPAuthResult, and checked the following related properties:
ldap.referral,
ldap.auth.method,
ldap.auth.password.encryption.algorithm,
ldap.base.dn,
ldap.import.user.password.enabled,
ldap.error.user.lockout,
ldap.error.password.expired,
ldap.base.provider.url,
auth.pipeline.enable.liferay.check,
ldap.auth.required
The portal also defines a set of classes, such as LDAPUser, LDAPGroup, and Modifications, as shown in the following table:
|
Class Name |
Interface/Extension |
Server type |
Main methods |
|
BasicLoginModule |
LoginModule |
basic, at the package com.liferay.portal.security.jaas.ext |
abort, commit, initiate, login, logout |
|
PortalLoginModule |
BasicLoginModule |
jboss |
Commit |
|
PortalLoginModule |
BasicLoginModule |
jetty |
Commit |
|
PortalLoginModule |
BasicLoginModule |
jonas, _JGROUP: org.objectweb.jonas.security.auth.JGroup; _JPRINCIPAL: org.objectweb.jonas.security.auth.JPrincipal; _JROLE: org.objectweb.jonas.security.auth.JRole; |
Commit |
|
PortalLoginModule |
BasicLoginModule |
Resin |
Commit |
|
PortalLoginModule |
BasicLoginModule |
Tomcat |
Commit |
|
PortalLoginModule |
BasicLoginModule |
Weblogic |
commit |
LDAP import and export
LDAPUser will be mapped into the portal User, while LDAPGroup and LDAPUser membership will be mapped into the portal UserGroup, Role, and User's membership. This process is called LDAP import. Meanwhile, the portal User, UserGroup, and User's membership will be mapped back into LDAPUser, Group, and User's membership, respectively. This process is called LDAP export.
The portal has defined a set of interfaces, utilities, and implementations for LDAP import and export, as shown in the following table:
|
Class |
Involved LDAP interfaces |
Involved portal interface |
Main methods |
|
LDAPUser |
LDAP User |
Contact, User, UserGroupRole |
getAimSn, getBirthday, getComments, getContact, and so on. |
|
LDAPGroup |
LDAP Group |
UserGroup, Role |
getCompanyId, getDescription, getGroupName, and so on. |
|
Modifications |
BasicAttribute, DirContext, ModificationItem |
None |
addItem |
Securing the LDAP user's password
When importing users from LDAP, the user's info and password is imported by default. Of course, all passwords stored in the portal are secure. It is important to note that the LDAP password mapping field is optional. In some use cases, the fact that the portal stores users' password is against a company's security policy rules. Fortunately, the portal secures the LDAP user's password via the following properties.
ldap.import.user.password.enabled=true
ldap.import.user.password.autogenerated=false
ldap.import.user.password.default=test
The property ldap.import.user.password.enabled is set to false when the LDAP user's password shouldn't be imported. The property ldap.import.user.password.autogenerated is set to true to auto-generate the password for the imported user from LDAP. This property is only in use if the property ldap.import.user.password.enabled is set to false.
The property ldap.import.user.password.default is set as either screenName or plain text for the default password of the imported LDAP user. Setting the value to screenName will use the user's screen name as the password for the imported LDAP user. Setting the value to any other plain text value will use that value as the password for the imported LDAP user. This property is only in use if the properties ldap.import.user.password.enabled and ldap.import.user.password.autogenerated are both set to false.
When exporting the LDAP users' information, you may not export an LDAP user's password from the portal to the LDAP server. This feature can be implemented by the following property:
ldap.export.user.password.enabled=true
As you can see, the property ldap.export.user.password.enabled is set to false, when the LDAP user's password shouldn't be exported.
Speed-up search of users and groups
As mentioned earlier, base DN (Distinguished Name) is used as a base to search both users and groups. When the number of users and groups is small, you wouldn't meet any performance bottleneck. But if the number of users and groups is huge, like 500K+ users and 50K+ groups in the LDAP server, you would meet with a performance bottleneck when searching users and groups, as each user may be part of 50+ groups.
The real use case is that there are 500K+ users and 50K+ groups. Each user may be part of 50+ groups. When logged in as a user from the LDAP server, it always takes 20-30 seconds by default. It should take less than one second in general.
As you can see, LDAP import process and authentication can be improved a lot by using the base DN as the users-base DN for users search, and the custom group base DN should be used as the groups-base DN for groups search. Ideally, we should divide base DN ldap.base.dn into ldap.users.base.dn and ldap.groups.base.dn for users search and groups search, respectively.
Single Sign-On
The SSO integration is achieved by the filters, servlets, and/or utility classes. The following table shows the filters and utility classes of CAS, NTLM, and OpenSSO:
|
Class |
Interfaces |
Properties |
Main methods |
|
PortalLDAPUtil |
UserConverterKeys GroupConverterKeys |
ldap.range.size, ldap.base.dn, ldap.import.user.search.filter, company.security.auth.type, company.security.auth.type, users.screen.name.always. autogenerate, ldap.page.size |
getContext, getGroup*, getLdapSerrverId, getUser*, hasUser, searchLDAP, and so on. |
|
PortalLDAPContext |
DummyDirContext extends DummyContext implements DirContext |
None |
getAttributes, setAttributes |
|
LDAPSettingsUtil |
ldap.auth.search.filter, ldap.contact.custom.mappings, ldap.contact.mappings, ldap.group.mappings, ldap.user.custom.mappings, ldap.user.mappings, ldap.export.enabled, ldap.export.group.enabled, ldap.import.enabled, ldap.import.on.startup, ldap.password.policy.enabled |
get*, is* |
|
|
BasePortalToLDAPConverter |
PortalToLDAPConverter |
ldap.group.default.object.classes, ldap.user.default.object.classes, |
get* |
|
BaseLDAPToPortalConverter |
LDAPToPortalConverter |
users.screen.name.always.autogenerate, users.email.address.required |
importLDAPGroup, importLDAPUser |
|
PortalLDAPImporterImpl |
PortalLDAPImporter |
ldap.base.provider.url, ldap.import.method, ldap.base.dn, ldap.auth.search.filter, company.security.auth.type, ldap.import.group.search.filter, ldap.import.group.search.filter.enabled, ldap.export.enabled |
importFromLDAP, importFromLDAPUser* |
|
PortalLDAPExporterImpl |
PortalLDAPExporter |
ldap.auth.required |
exportToLDAP PortalLDAPExporterUtil |
|
DefaultAttributesTransformer |
AttributesTransformer |
None |
transformGroup, transformUser |
FacebookConnect
The portal comes with an additional Single Sign-On option—Facebook SSO using OAuth 2.0. Facebook connection got defined in the interface FacebookConnect and the servlet FacebookServlet, as shown in the following table:
|
SSO Filter |
Extension/Interface |
Properties |
Main Methods |
|
CASFilter |
BasePortalFilter, BaseFilter, LiferayFilter |
cas.auth.enabled, cas.server.name, cas.server.url, cas.service.url, cas.login.url, cas.logout.url |
reload,getLog, getTickicketValidator, isFilterEnabled, processFilter |
|
NtlmFilter |
BasePortalFilter |
ntlm.auth.enabled, ntlm.auth.domain, ntlm.auth.domain.controller, ntlm.auth.domain.controller.name, ntlm.auth.service.account, ntlm.auth.service.password |
getLog, getNtmlManager, init, isFilterEnabled, processFilter |
|
NtlmPostFilter |
BasePortalFilter |
None |
getLog, processFilter |
|
OpenSSOFilter |
BasePortalFilter |
open.sso.auth.enabled, open.sso.login.url, open.sso.logout.url, open.sso.service.url, |
isFilterEnabled, processFilter |
|
OpenSSOUtil |
None |
None |
getAttributes, getSubjectId, isAuthenticated, isValidServiceUrl, isvalidUrl, isvalidaUrls |
OpenId and SiteMinder
As shown in the following table, the portal opens the door to integrate OpenId and SiteMinder:
|
Class |
Interface |
Properties/servlet mapping |
Main methods |
|
FacebookConnectImpl |
FacebookConnect |
facebook.connect.app.id, facebook.connect.app.secret, facebook.connect.auth.enabled, facebook.connect.graph.url, facebook.connect.oauth.auth.url, facebook.connect.oauth.redirect.url, facebook.connect.oauth.token.url |
getAccessToken, getAppId, getAppSecret, getAuthURL, getGraphResources, getGraphUrl, and so on. |
|
FacebookConnectUtil |
None |
None |
setFacbookConnet, and so on. |
|
FacebookServlet |
HttpServlet |
<servlet-name>Facebook Servlet</servlet-name> <url-pattern>/facebook/*</url-pattern> |
fixFbm, service |
|
FacebookConnectAction |
PortletAction |
None |
Render, strutsExecute |
AutoLogin
The portal has specified the AutoLogin interface.
As you can see in the following table, you can set a request attribute with the variable AUTO_LOGIN_REDIRECT to tell the filter AutoLoginFilter to stop processing filters and redirect the user to a specified location. You can also set a request attribute with the variable AUTO_LOGIN_REDIRECT_AND_CONTINUE to tell the filter AutoLoginFilter to continue processing filters and then redirect the user to a specified location:
|
Class |
Interface |
Properties |
Main methods |
|
OpenIdUtil |
org.openid4java.consumer.ConsumerManager, InMemoryConsumerAssociationStore, InMemoryNonceVerifier |
open.id.auth.enabled |
getConsumerManager, getScreenName, isEnabled, normalize |
|
OpenIdAutoLogin |
AutoLogin |
None |
login |
|
OpenIdAction |
PortletAction |
None |
processAction, render |
|
SiteMinderLogoutAction |
Action |
siteminder.auth.enabled |
run |
|
SiteMinderAutoLogin |
AutoLogin |
siteminder.user.header, siteminder.import.from.ldap |
login |
UserTracker
Besides the UserTracker models, such as UserTracker and UserTrackerPath, the portal also provides a set of UserTracker services, as shown in the following table:
|
Service Interface |
Extension |
Main methods |
Utility/Wrapper |
|
BrowserTrackerLocalService |
PersistedModelLocalService |
add*, create*, delete*, fetch*, get*, set*, update* |
BrowserTrackerLocalServiceUtil (Wrapper) |
|
PasswordTrackerLocalService |
PersistedModelLocalService |
add*, create*, delete*, fetch*, get*, is*, set*, track*, update* |
PasswordTrackerLocalServiceUtil (Wrapper) |
In particular, the portal has specified the following tracking-related properties in portal.properties. Note that this is not a lightweight way for the portal.
live.users.enabled=false
session.tracker.memory.enabled=true
session.tracker.ignore.paths=\
/portal/render_portlet,\
/document_library/get_file
As shown in the previous code, the property live.users.enabled is set to true to enable tracking via LiveUsers. The property session.tracker.memory.enabled is set to true to track user clicks in memory for the duration of the user's session. Setting this to true allows you to view all live sessions via LiveUsers.
The property session.tracker.friendly.paths.enabled is set to true to convert the tracked paths to friendly URLs via PortalRequestProcessor. The property session.tracker.ignore.paths defines a list of comma-delimited paths that shouldn't be tracked in the PortalRequestProcessor.
The property session.tracker.persistence.enabled is set to true to track user clicks in the database, after a user's session is invalidated. Setting this to true will allow you to generate usage reports from the database via UserTrackerLocalServiceImpl (you can call UserTrackerLocalServiceUtil in the plugins). Use this cautiously, because this will store a lot of usage data.
The following table shows where the previous properties get checked:
|
Service Interface |
Extension |
Main methods |
Utility/Wrapper |
|
UserTrackerLocalService |
PersistedModelLocalService |
add*, create*, delete*, fetch*, get*, set*, update* |
UserTrackerLocalServiceUtil (Wrapper) |
|
UserTrackerPathLocalService |
PersistedModelLocalService |
add*, create*, delete*, fetch*, get*, set*, update* |
UserTrackerPathLocalServiceUtil (Wrapper) |
Auditing
An audit trail of user actions is required by many organizations. Fortunately, the portal provides an audit service—a pluggable way of storing the audit trail from the portal and plugins. The information processed by the audit service plugin can be stored into a log file or database. Loosely speaking, audit services employ Liferay Lightweight Message Bus and Plugin architecture. The audit services handle the processing and logging of the audit messages, sent through the Message Bus. Therefore, any plugin can then produce audit messages to the audit Message Bus destination.
Audit services
The portal has provided a set of audit services interfaces, such as AuditMessage, AuditMessageFactory, AuditMessageProcessor, and AuditRouter, as shown in the following table:
|
Class Name |
Extension/Involved models |
Involved properties |
Main methods |
|
PortalRequestProcessor |
TilesRequestProcessor |
session.tracker.friendly.paths.enabled, session.tracker.ignore.paths |
processPath |
|
LiveUsers |
Company, UserTracker |
live.users.enabled, session.tracker.memory.enabled |
deleteGroup, getGroupUsers, getGroupUsersCount, getSessionUsers, getUserTracker, joinGroup, leaveGroup, signIn, signOut |
|
UserTrackerLocalServiceImpl |
UserTrackerLocalServiceBaseImpl |
session.tracker.persistence.enabled |
addUserTracker |
The audit service beans have been specified in audit-spring.xml.
Audit filter
An auditing framework has been added to the portal. The auditing framework will allow the system administrator to track a user's action. The portal has the following settings for the auditing framework in portal.properties:
com.liferay.portal.servlet.filters.audit.AuditFilter=false
As you can see, the auditing framework has been disabled by default. This is helpful to speed up the portal. To enable the auditing framework, you can set the property to true in portal-ext.properties.
The audit filter populates AuditRequestThreadLocal with the appropriate request values to generate audit requests. Note that if an administrator is impersonating another user, the audit records look as if the actions are performed by the impersonated user. Ideally, the records should record the real user that performs an action:
|
Utility/Implementation |
Interface/Extension |
Involved classes |
Description |
|
AuditMessageFactoryImpl |
AuditMessageFactory |
AuditMessage JSONObject |
Audit message factory |
|
AuditMessageFactoryUtil |
AuditMessageFactory |
AuditMessage JSONObject |
Audit message factory utility |
|
AuditRouterProxyBean |
BaseProxyBean implements AuditRouter |
AuditMessage |
Audit router proxy bean |
|
AuditRouterUtil |
AuditRouter |
AuditMessage |
Audit router utility |
|
(available implementation in the plugins) |
AuditMessageProcessor |
AuditMessage |
Audit message processor interface |
As shown in the previous table, the interface TryFilter extends DirectCallFilter.
In addition, the interface InitialThreadLocal
Velocity variables
As mentioned before, the class VelocityVariablesImpl implements VelocityVariables. Two Velocity variables (auditMessageFactoryUtil and auditRouterUtil) get defined in the class VelocityVariablesImpl as follows:
// Audit message factory
velocityContext.put("auditMessageFactoryUtil",
AuditMessageFactoryUtil.getAuditMessageFactory());
// Audit router util
velocityContext.put("auditRouterUtil",
AuditRouterUtil.getAuditRouter());
As shown in the previous code, Two Velocity variables, auditMessageFactoryUtil and auditRouterUtil get specified as AuditMessageFactoryUtil.getAuditMessageFactory() and AuditRouterUtil.getAuditRouter(), respectively.
Auditing in the portal
The class LayoutAction of the portal core has applied the classes AuditMessage and AuditRouterUtil.
It applies the audit framework on the object Layout (that is, instances of page) in the portal. You can leverage similar sample code and apply it on the other objects, such as users, groups, and so on. In fact, an audit message has been specified in the portal.properties as follows:
audit.message.com.liferay.portal.model.Layout.VIEW=false
Of course, you would be able to override the same in the portal-ext.properties.
Audit hook
The following events get audited in the plugin audit-hook: LOGIN, LOGOUT, LOGIN FAILURE, IMPERSONATION, ROLE CREATE, REMOVE, UPDATE, ROLE GRANT, REVOKE, USER CREATE, REMOVE, UPDATE, USER GROUP CREATE, REMOVE, UPDATE, USER GROUP ASSIGN, and REVOKE. How? The plugin audit-hook adds the following hooks in the liferay-hook.xml as follows:
<hook>
<portal-properties>portal.properties</portal-properties>
</hook>
Furthermore, the audit-hook provides the hooks in the /WEB-INF/src/portal.properties as follows:
// see details in portal.properties
The following table shows detailed messages of these portal properties hooks:
|
Class name |
Interface/extension |
Involved classes |
Description |
|
AuditRequestThreadLocal |
None |
AutoResetThreadLocal<T> extends InitialThreadLocal<T> |
Audit request local thread |
|
AuditFilter |
BasePortalFilter implements TryFilter |
HttpServletRequest; HttpServletResponse; HttpSession; AuditRequestThreadLocal |
Audit filter |
Event types get defined in the interface EventTypes. EventTypes cover ADD, ASSIGN, DELETE, IMPERSONATE, LOGIN, LOGIN_FAILURE, LOGOUT, UNASSIGN, and UPDATE. Of course, you can add more event types in the plugin audit-hook
Audit reports
The plugin audit-portlet provides the ability to manage audit reports. The plugin stores the information processed by the audit service in a model called AuditEvent, with a package named com.liferay.portal.audit. The model is specified in detail at service.xml as follows:
<column name="eventType" type="String" />
<column name="className" type="String" />
<column name="classPK" type="String" />
<column name="additionalInfo" type="String" />
As you can see, the entry AuditEvent includes the following columns:
- event ID as primary key
- Additional Info
- Class Name
- Class PK
- Client Host
- Client IP
- Cmpany ID
- Message
- Server Name
- Server Port
- Session ID
- Timestamp
- User ID
- User Name
- Oiginal Message
The audit service beans have been overridden in audit-spring.xml, as shown in the following table:
|
Class Name |
Interface/Abstract Class |
Property |
Description |
|
LoginFailureAudit |
LoginFailure |
auth.failure |
This class will run when a user has a failed login or when a user has reached the maximum number of failed logins. |
|
ImpersonationAudit |
Action |
servlet.service.events.pre |
The pre-service events process before Struts processes the request. |
|
LoginAudit |
Action |
login.events.post |
The post-service events process after Struts processes the request. |
|
LogoutAudit |
Action |
logout.events.post |
The post-service events process after Struts processes the request. |
|
AddressListener |
BaseModelListener<Address> |
value.object.listener.* |
Model Address onBeforeUpdate |
|
ContactListener |
BaseModelListener<Contact> |
value.object.listener.* |
Model Contact onBeforeUpdate |
|
RoleListener |
BaseModelListener<Role> |
value.object.listener.* |
Model Role: onBeforeAddAssociation, onBeforeCreate, onBeforeRemove, onBeforeRemoveAssociation, onBeforeUpdate |
|
UserGroupListener |
BaseModelListener<UserGroup> |
value.object.listener.* |
Model User Group: onBeforeAddAssociation, onBeforeCreate, onBeforeRemove, onBeforeRemoveAssociation, onBeforeUpdate |
|
UserGroupRoleListener |
BaseModelListener<UserGroupRole> |
value.object.listener.* |
Model User Group Role: onBeforeCreate, onBeforeRemove |
|
UserListener |
BaseModelListener<User> |
value.object.listener.* |
Model User: onBeforeCreate, onBeforeRemove, onBeforeUpdate |
As you can see, the audit message can be stored into a log file or a database. The audit log CSV (Comma Separated Values) is configured to be the properties, as shown in the previous table. In particular, you can find more details of the plugins audit-hook and audit-portlet in the attached code.
Web server servlet
The portal has defined the downloading service, that is, the servlet class WebServerServlet. The servlet is specified in web.xml as follows:
<!-- see details in web.xml -->
<servlet-mapping>
<servlet-name>Web Server Servlet</servlet-name>
<url-pattern>/documents/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Web Server Servlet</servlet-name>
<url-pattern>/image/*</url-pattern>
</servlet-mapping>
Therefore, documents (for example, basic documents, records, images, videos, and audios) have the following download file URL pattern:
String fileUrl = themeDisplay.getPortalURL() +
themeDisplay.getPathContext() + "/documents/" +
themeDisplay.getScopeGroupId() + StringPool.SLASH +
folderId + StringPool.SLASH +
HttpUtil.encodeURL(fileEntry.getTitle());
You can refer to the details at /portlet/document_library/view_file_entry.jsp. The following table shows a summary of the servlet class WebServerServlet:
|
Bean |
Interface |
Properties |
Description |
|
JSONLogMessageFormatter |
LogMessageFormatter |
None |
JSON message formatters |
|
CSVLogMessageFormatter |
LogMessageFormatter |
columns: additionalInfo, className, classPK, clientHost,clientIP, companyId,eventType, message, serverName, serverPort,sessionID, timestamp,userId, userName |
CSV message formatters |
|
DatabaseAuditRouterProcessor |
AuditRouterProcessor |
None |
Database audit router processor |
|
LogAuditRouterProcessor |
AuditRouterProcessor |
logMessageFormatter: CSVLogMessageFormatter outputToConsole: false |
Log audit router processor |
|
DefaultAuditRouter |
AuditRouter |
globalAuditMessageProcessors: list (AuditMessageProcessor) - DatabaseAuditRouterProcessor, LogAuditRouterProcessor |
Default audit router |
As shown in the previous table, the interface WebServerServletToken has defined the following functions like getting and resetting a token using the image ID.
Document Library record
The plugin Document Library record hook used to audit who downloaded a special resource like Document Library documents, images, records, audios, videos, and so on.
The plugin uses a set of models, namely, DLRecordDifinition and DLRecordLog. As you can see, the plugin document-library-record-portlet has specified services and models with a package named com.liferay.dlrecord. You will be able to find the details at /WEB-INF/service.xml. The entity DLRecordDifinition, which specifies the DLFileEntry definition, includes the following columns:
<column name="name" type="String" />
<column name="title" type="String" />
<column name="signinRequired" type="boolean" />
The entity DLRecordLog, which specifies who downloaded the document from the DLFileEntry definition, includes the following columns:
<column name="definitionId" type="long" />
<column name="documentType" type="String" />
<column name="createDate" type="Date" />
As shown in the previous code, it provides capabilities to regroup the logs by the definition ID, content type, user ID, group ID, company ID, and created date. Of course, you can customize this service model and use service builder in the SDK plugins to re-generate services.
In addition, the plugin document-library-record-portlet hooks the servlet WebServerServlet and traces the URL patterns such as /documents/* and /image/*. Of course, you can find details of the plugin document-library-record-portlet from the attached code.
Rule and reporting engines
Loosely speaking, a business rule is a statement that defines or constrains the business aspect. In general, a business rule is different from strategy. Business rules tell an organization what it can do in detail, while strategy tells an organization how to focus the business at a macro level to optimize results. And going further, a business rule engine is a software system that executes business rules in a runtime production environment. The portal provides capability to integrate the rule engine called Drools: a Business Rule Management System (BRMS) with a forward-chaining inference-based rules engine, using an enhanced implementation of the Rete algorithm. Refer to for more details.
The portal also provides capability to integrate the reporting engine called JasperReports. How is the integration being done? It is through portlet as plugins that access JasperReports' web services. JasperReports is an open source Java reporting engine that uses data coming from any kind of data source, and produces pixel-perfect documents, viewed, printed, or exported in a variety of document formats, including HTML, PDF, Excel, OpenOffice, and Word. Refer to .
An alternative reporting engine is BIRT, which is an open source software project that provides reporting and business intelligence capabilities for rich client and web applications. Refer to .
Rule engine
The portal has specified the rule engine interface called RuleEngine, where its methods include add, containsRuleDomain, execute, remove, and update. The following table shows the interface and its related objects and classes:
|
Class name |
Interface |
Involved interfaces/classes |
Description |
|
WebServerServlet |
HttpServlet |
Company, Group, Image, User, DLFileEntry, DLFileShortcut |
Web server servlet for the URL pattern: /documents/*, /image/* |
|
WebServerEntry |
None |
Date |
Web server entry |
|
WebServerServletTokenImpl |
WebServerServletToken |
MultiVMPool, PortalCache, CacheUtil, |
Web server servlet token implementation |
The portal has specified the rule engine-related beans in the XML file rule-spring.xml at src/META-INF/rules-spring.xml:
|
Interface/Class |
Utility/Interface |
Implementation |
Description |
|
RulesEngine |
RulesEngineUtil |
RulesEngineProxyBean |
Rule engine interface and implementation |
|
Fact |
Serializable |
None |
Involved class: Fact<T> |
|
Query |
Serializable |
None |
Involved class: QueryType |
|
RulesResourceRetriever |
Serializable |
None |
Involved class: ResourceRetriever |
|
RulesLanguage |
None |
None |
DROOLS_BRL, DROOLS_CHANGE_SET, DROOLS_DECISION_TABLE, DROOLS_DOMAIN_SPECIFIC, DROOLS_DOMAIN_SPECIFIC_RULE, DROOLS_PKG, DROOLS_RULE_FLOW, DROOLS_RULE_LANGUAGE, DROOLS_XML_LANGUAGE |
Drools web
The portal provides full integration of Drools within the rule engine framework—the plugin drools-web uses the Drools server as the rule engine in the portal. The rule Spring beans and its related messaging Spring beans is specified in the XML files drools-spring.xml and messaging-spring.xml in the folder /docroot/WEB-INF/src/META-INF, respectively. As shown in the following lines, the XML files drools-spring.xml and messaging-spring.xml are defined as the values of the parameter named portalContextConfigLocation in web.xml:
<context-param>
<param-name>portalContextConfigLocation</param-name>
<param-value>
/WEB-INF/classes/META-INF/drools-spring.xml,
/WEB-INF/classes/META-INF/messaging-spring.xml
</param-value>
</context-param>
The plugin drools-web implements the interface RulesEngine, as shown in the following table.
|
Bean |
Id/Ref/Class |
Properties |
Description |
|
Advice |
messagingProxyAdvice |
None |
AOP configuration: <aop:around pointcut= "bean(com.liferay.portal.bi.rules. RulesEngineProxyBean)" method="invoke" /> |
|
Proxy |
com.liferay.portal.bi.rules.RulesEngineProxyBean |
singleDestinationSynchronousMessageSender singleDestinationMessageSender |
destinationName: "liferay/rules_engine" |
|
Utility |
com.liferay.portal.kernel.bi.rules.RulesEngineUtil |
rulesEngine |
Rules engine utility bean |
|
Messaging |
destination.rules_engine messagingConfigurator.rules |
name: liferay/rules_engine destinations messageBus |
Classes: ParallelDestination, DefaultMessagingConfigurator, and MessageBus |
Report engine
The portal has specified the interface called ReportEngine for the reporting engine, where its methods include compile, destroy, execute, init, setEngineParameters, and setReportFormatExporterRegistry. The following table shows the interface and its related objects and classes:
|
Implementation |
Interface |
Involved classes |
Description |
|
RulesEngineImpl |
RulesEngine |
org.drools.*: KnowledgeBase, KnowledgeBuilder, ResourceType, Command, Resource, QueryResults, QueryResultsRow |
com.liferay.portal.kernel.bi.rules.* Fact; Query; QueryType; RulesLanguage; RulesResourceRetriever |
|
ClassFieldAccessorCache |
none |
ProtectionDomain; HashMap; Map; WeakHashMap; ConcurrentHashMap; ConcurrentMap; org.drools.core.util.asm.ClassFieldInspector |
Class field Accessor cache |
JasperReports web
The portal provides full integration of the JasperReports with the reporting framework: the plugin jasperreports-web uses the JasperReports server as the reporting engine in the portal. The reporting Spring beans and its related messaging Spring beans get specified in the XML files jasperreports-spring.xml and messaging-spring.xml, respectively, at the folder /docroot/WEB-INF/src/META-INF. As shown in the following lines, the XML files jasperreports-spring.xml and messaging-spring.xml get defined as the value of the parameter name portalContextConfigLocation in web.xml:
<context-param>
<param-name>portalContextConfigLocation</param-name>
<param-value>
/WEB-INF/classes/META-INF/jasperreports-spring.xml,
/WEB-INF/classes/META-INF/messaging-spring.xml
</param-value>
</context-param>
The following table shows a summary of this report engine implementation:
|
Interface/Class |
Utility/Interface |
Main involved classes |
Description |
|
ReportEngine |
ReportEngineUtil |
ServletContext |
Report engine interface |
|
ReportRequest |
Serializable |
Map |
Report request |
|
ReportRequestContext |
Serializable |
Map, HashMap |
Report request context |
|
ReportFormat |
None |
None |
Report formats: CSV("csv"), HTML("html"), PDF("pdf"), RTF("rtf"), TXT("txt"), XLS("xls"), XML("xml") |
|
ReportFormatExporter |
None |
reportRequest,ReportResultContainer |
Report format exporter |
|
ReportDataSourceType |
None |
None |
Report data source types: CSV("csv"), EMPTY("empty"), JDBC("jdbc"), PORTAL("portal"), XLS("xls"), XML("xml") |
Reports portlet
The portal provides the plugin called reports-portlet, which has the ability to schedule reports and deliver them via Document Library and e-mail. The plugin defines two portlets: Reports Admin at the Control Panel and Reports Display. The portlet Reports Display is instanceable—that is, you can add more than one instance of the portlet on a page.
As shown in the following diagram, the plugin reports-portlet defines three entities, namely, Entity, Definition, and Source. The entity Source may have many Definition instances, and the entity Definition may have many Entity instances, which is shown as follows:

Based on service.xml, a set of reporting services get generated, as shown in the following table:
|
Implementation |
Interface |
Involved classes |
Description |
|
ReportEngineImpl |
ReportEngine |
ReportRequest, ReportFormatExporter, ReportRequestContext, ReportResultContainer |
Jasper reports engine implementation |
|
CachedReportCompiler, DefaultReportCompiler |
ReportCompiler |
ReportDesignRetriever, JasperCompileManager, JasperReport |
Cache report compiler, default report compiler |
|
BaseReportFormatExporter, CsvReportFormatExporter, HtmlReportFormatExporter, PdfReportFormatExporter, RtfReportFormatExporter, TextFormatExporter, XlsFormatExporter, XmlFormatExporter |
ReportFormatExporter |
JRExporter, JRCsvExporter, JRHtmlExporter, JRPdfExporter, JRRtfExporter, JRTxtExporter, JRXlsExporter, JRXmlExporter |
Report format exporter: CSV, HTML, PDF, RTF, TXT, XLS, and XML |
Summary
In this article, you learnt about the following:
- User management
- Password policy
- Authentication and authorization
- LDAP and SSO
- Tracking and auditing
- Rules engine and reporting engine
Further resources on this subject:
- Building your First Liferay Site [Article]
- Enhancing your Site with PHP and jQuery [Article]
- Administrating the MySQL Server with phpMyAdmin [Article]
- Getting Started with the Alfresco Records Management Module [Article]
About the Author :
Jonas X. Yuan
Dr. Yuan is an expert on Liferay Portal and Content Management Systems (CMS). As an open source community contributor, he had published three Liferay books from 2008 to 2010. He is also an expert on Liferay integration with Ad Server OpenX, different search engines, enterprise content types including videos, audios, images, documents, and web contents, and other technologies such as BPM Intalio and Business Intelligence Pentaho, LDAP, and SSO. He holds a Ph.D. in Computer Science from the University of Zurich, where he focused on Integrity Control in Federated Database Systems. He earned his M.S. and B.S. degrees from China, where he conducted research on expert systems for predicting landslides. Previously, he worked as a Project Manager and a Technical Architect in Web GIS (Geographic Information System). He is experienced in SystemsDevelopment Lifecycle (SDLC) and has deep, hands-on, skills in J2EE technologies. He developed a BPEL (Business Process Execution Language) Engine called BPELPower from scratch in NASA data center. As the chief architect, Dr. Yuan led and successfully launched several large scale Liferay/Alfresco projects for millions of users each month.



Post new comment