10.3. Security Service FAQ

This section addresses common questions developers have when implementing a security service through the kernel.

Q: How do I determine whether the user is logged in?

A: KernelHelper.getKernelRequestContext(request).getUserContext().isLoggedIn() returns true if the user is logged in, false otherwise.

Q: How do I get the ID for the current user?

A: KernelHelper.getKernelRequestContext(request).getUserContext().getUserID() returns the current user's ID. This method throws an exception if the user is not logged in, so check with isLoggedIn() first. If the calling code needs a User object for the current user, it should call getUser() instead.

Q: How do I get the User object for the current user?

A: KernelHelper.getKernelRequestContext(request).getUserContext().getUser() returns a User object for the current user. This method throws an exception if the user is not logged in, so check with isLoggedIn() first. This method throws DataObjectNotFoundException if the user doesn't exist; calling code should catch this exception and handle it appropriately. The User object provided by this method is cached for the current thread, so multiple calls to getUser() incur at most one database hit.

KernelHelper.getCurrentUser(request) is a convenience method that returns the current User object if available, returns null if the user is not logged in, or throws a RuntimeException if the user is logged in but does not exist.

Q: How can a user be logged in but not exist in the database?

A: WAF logs in users automatically using cookies (or other mechanisms), so it is possible for a client to present a valid cookie for a user that doesn't exist. This might happen if the user is deleted, the database is cleared, or an attacker spoofs a non-existent user's cookie. WAF uses cryptographic mechanisms to make the last case unlikely.

Q: How do I require that a user is logged in for a given page?

A: com.arsdigita.ui.login.UserAuthenticationListener is a RequestListener that requires that the user is logged in and that the user exists. This listener should be added to any Bebop Page that requires login using Page.addRequestListener().

If the user is not logged in or doesn't exist, the client is redirected to the login page with return_url set to the original request URL. This ensures that the client will return to the original page after logging in.

If the user is logged in, this listener provides the methods isLoggedIn() and getUser(). Note that these methods differ from those in UserContext since they also ensure that the user exists.

Q: How do I require that a user is securely authenticated (using SSL or similar) for a given page?

A: This is not yet provided in a single API, but this can be accomplished using a UserAuthenticationListener and then calling com.arsdigita.kernel.Initializer.getSecurityHelper.isSecure(request) to check whether the current request is secure.

NoteNote
 

In the current implementation of WAF, a user must be logged in securely to access a page over SSL. This restriction will be loosened in the future, and an API for checking secure authentication will be provided.

Q: How do I get the HttpSession object for the current request?

A: WAF does not currently wrap session retrieval, so request.getSession() will return the current HttpSession object.

NoteNote
 

Session management is independent of user authentication, so a session will always exist, whether or not the user is logged in.

Q: How do I log in a user using a username and password?

A: KernelHelper.getKernelRequestContext(request).getUserContext().login(username, password, forever), where forever should be true if the user should be granted a permanent login cookie, false if the cookie should only last for the current session. For an example, see com.arsdigita.ui.login.UserRegistrationForm.

TipTip
 

There are several subtle security issues in managing passwords safely, so developers are encouraged to use the provided UI code rather than creating their own. This ensures that developers automatically benefit from fixes in future releases.

Q: How do I log in as another user (Administrators only)?

A: Three methods allow an administrator to assume the role of another user — the "\" is used to break the lines for printing purposes only::

  1. KernelHelper.getKernelRequestContext(request).getUserContext().\ login(username)

  2. KernelHelper.getKernelRequestContext(request).getUserContext().\ login(userID)

  3. KernelHelper.getKernelRequestContext(request).getUserContext().\ login(User)

The first two versions of this method are convenience wrappers for the third. These methods require that the current user be an administrator.

After calling any of these methods, the UserContext is updated with the target user's information; however, cached user information (such as that provided by UserAuthenticationListener) will still contain the administrator's information.

Immediately after calling any of these methods, the client should be redirected to a new page (such as the user workspace). See com.arsdigita.ui.admin.UserLoginPage for an example.

Q: How do I log out a user?

A: KernelHelper.getKernelRequestContext(request).getUserContext().logout() logs out the current user. com.arsdigita.ui.login.UserLogoutListener is an ActionListener that logs out the user by calling this method.

Developers can link to this action listener using an ActionLink. Alternately, developers can link to a com.arsdigita.ui.login.UserLogoutPage; this page logs out the user and immediately redirects the client to the registration page.

Q: How do I get a user's email address?

A: The user's User object given to the method user.getPrimaryEmail().getEmailAddress() returns the user's primary email address as a string. It is possible that getPrimaryEmail() or getEmailAddress() might return null.

com.arsdigita.ui.login.EmailInitListener is a FormInitListener that initializes a form parameter with the current user's email address, if possible. If the user is not logged in or has no email address, it leaves the parameter unchanged. See com.arsdigita.ui.login.UserRegistrationForm for an example.

Q: How do I set a user's password?

A: Given the user's UserAuthentication object, auth.setPassword(password) sets the user's password.

CautionCaution
 

Use the method auth.setPassword(password) carefully, as it can allow an attacker to override the user's password.

com.arsdigita.ui.login.PasswordValidationListener is a ParameterValidationListener that checks whether a form parameter value is a strong password. This listener should always be used before setting the user's password. See com.arsdigita.ui.login.ChangePasswordForm for an example.

Q: What is / How do I get a user's UserAuthentication object?

A: There are four methods for retrieving a user's UserAuthentication object:

  1. UserAuthentication.retrieveForLoginName(String loginName)

  2. UserAuthentication.retrieveForUser(OID userOID)

  3. UserAuthentication.retrieveForUser(BigDecimal userID)

  4. UserAuthentication.retrieveForUser(User user)

The first is appropriate if the username is entered in a form; the last two are appropriate if the user is already logged in. The UserAuthentication object allows code to check and set a user's password and password retrieval question and answer.

CautionCaution
 

Access to the UserAuthentication object is dangerous, since it can allow an attacker to compromise a user's account. If at all possible, use the provided UI components and login modules rather than access this object directly.

Q: What is / How do I use the SecurityLogger class?

A: SecurityLogger wraps the Log4j category called SECURITY. It provides static methods for appending entries to that log, and automatically inserts the current date and time and, if applicable, the current client's IP address.

SecurityLogger is used to log security-relevant events for future auditing. Examples of such events are bad passwords, malformed cookies, and expired login page accesses. To use the class, simply call the appropriate class method, such as SecurityLogger.warn().