Saturday, May 30, 2009

Authentication and authorization with JBoss security domains

Authentication and authorization comprise the fundamentals of security, and as such, they are important aspects of almost any application. Having in mind that security is such a frequent issue, it comes as no surprise that many people have devised many different ways of carrying it out, and so we now have literally countless options to choose from. If you are going to deploy your application to JBoss AS, you can get the basic authentication and authorization set up in virtually no time. To carry out these tasks, JBoss uses the notion of a security domain overlaying the standard JAAS (Java Authentication and Authorization Service).

Configuring the data source (*-ds.xml)



In order to set up a security domain for your app to use, you first need to set up a data source against which you will check the user's credentials. More often than not, your source of data will be a relational database. JBoss conveniently provides templates for many populars databases, so setting up a data source should be a breeze. These templates can be found under
<JBoss home>/docs/examples/jca1. Notice that all the filenames in this directory end with "-ds". This is the convention for naming the data source configuration files.

Let's assume we want to use MySQL. All we need to do, is make a copy of mysql-ds.xml, edit it to reflect our situation, and put it into the deployment directory. Editing this file is pretty straight forward, you decide on a JNDI name you want your data source bound to (I'll use MySqlDS), input your connection URL, username and password. There's really no need to change the driver class.

When this is done, you place this file under <JBoss home>/server/<profile name>/deploy2. That's it for this step.

Configuring a security domain (login-config.xml)



Next, you need to edit JBoss' login-config.xml file (found under<JBoss home>/server/<profile name>/conf). There are other ways to set up the security domain, but is by far the easiest. All you need to do here is nest a snippet similar to the following inside the policy element.
<!-- My custom security domain -->
<application-policy name="custom-domain">
<authentication>
<login-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule"
flag = "required">
<module-option name="dsJndiName">java:/MySqlDS</module-option>
<module-option name="principalsQuery">SELECT password FROM USERS WHERE username=?</module-option>
<module-option name="rolesQuery">SELECT role, 'Roles' FROM ROLES WHERE username=?</module-option>
</login-module>
</authentication>
</application-policy>

Pay attention that dsJndiName attribute value must be identical to the jndi-name you chose in your mysql-ds.xml, preceded by java:/.
principalsQuery is the SQL query used to retrieve the user's password which, of course, has to match the one entered, and rolesQuery is the SQL query used to retrieve the roles associated with the user (leave the 'Roles' part as it is, it's not clear from the JBoss' documentation what purpose it has).

Our security domain is now ready. The next thing to do would be to instruct our application to use it.

Declaring page-level security (web.xml)



Presume we have a web.xml with the following security constraints configured:
<security-constraint>
<web-resource-collection>
<web-resource-name>Secure Resource</web-resource-name>
<url-pattern>/secure/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>registereduser</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login.html</form-login-page>
<form-error-page>/error.html</form-error-page>
</form-login-config>
</login-config>
<security-role>
<role-name>registereduser</role-name>
</security-role>
<resource-ref>
<res-ref-name>jdbc/mysql</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>

This tells our app server that only the users with the registereduser role can access the resources under /secure/ (this is the job of the url-pattern element). It also tells we are going to use a form to perform authentication (notice the auth-method element), and that this form is found on the page named login.html (the form-login-page element). The request gets redirected to the page specified under form-error-page in case the login failed. You can also use BASIC authentication method (just place BASIC instead of FORM inside auth-method element) . This will cause the browser to display a simple pop-up dialog that asks for username and password when a secured resource is requested. You don't need form-login-config for this approach.

Creating a login form (j_security_check)



The login page is obviously required to contain a login form. This form must specify j_security_check as it's action, and it also must have a username input field named j_username and a password input field named j_password.

This is how it might look like in it's simplest form:
<form name="loginForm" method="post" action="j_security_check">
<table>
<tr>
<td>User Name:</td>
<td><input type="text" name="j_username"></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" name="j_password"></td>
</tr>
<tr colspan="2">
<td><input type="submit" value="login"></td>
</tr>
</table>
</form>

Binding the application to a security domain (jboss-web.xml)


So, web.xml dictates the security constraints, but there is no mention of how these constraints will be carried out. This is the job of the jboss-web.xml config file (which is, of course, JBoss specific). It is in this file where we specify what security domain we want to use for authentication. Here's how it should look like in this case:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<security-domain>java:/jaas/custom-domain</security-domain>
<resource-ref>
<res-ref-name>jdbc/mysql</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<jndi-name>java:/MySqlDS</jndi-name>
</resource-ref>
</jboss-web>

Pay attention to the security-domain element. It is necessary to prefix the domain name with java:/jaas/, and the name its self must be exactly the same as the one you typed into the login-config.xml file.

That's it! Now every unauthenticated request to the secure resource (under /secure/, as specified in web.xml) will first be redirected to the login page (or, in case of BASIC auth-method, a login pop-up will displayed). If the user logs in successfully and is authorized i.e. associated with any of the required roles (only registereduser in our case), the requested resource will be displayed. Otherwise, if the login attempt fails, or the user doesn't have any of the required roles, the error page will be displayed instead. Users stay logged in for the entire duration of the session.

Method-level security (isUserInRole)



Declarative security in web.xml is enough if you only need page-level security (based on the URL pattern), but if you need to dynamically check user's role on a method level, you can do so by calling isUserInRole("registereduser") on a request object (acquired from the ExtrnalContext). Here's a short example of a method doing this:

public Boolean isRegisteredUser() {
HttpServletRequest httpServletRequest =
(HttpServletRequest) FacesContext.getCurrentInstance() //first get the current FacesContext instance
.getExternalContext() //than get the ExternalContext
.getRequest(); //get the underlying request
// return True if this request was issued by a user with a "registereduser" role
return httpServletRequest.isUserInRole("registereduser");
}

You can get the username in pretty much the same way, just call getRemoteUser() instead of isUserInRole("...").

Notes:
1 <JBoss home> refers to your JBoss installation directory (the same as JBOSS_HOME environmental variable).
2 <profile name> refers to your deployment profile name (you will probably be using default).

Sunday, April 5, 2009

Validators in JSF

In JSF, validators are components that provide a mechanism for clean and easy validation of user input and, if necessary, generation of messages that can be displayed back to the client.

JSF comes with some basic built-in validators, but it allows for new, custom ones, to be added by the developer.

Built-in validators



The following validators are available out of the box:

f:validateDoubleRange: Checks whether a numeric input (convertible to double) is within a specified range.
f:validateLength: Checks if the string input length is within a given range.
f:validateLongRange: Checks whether a numeric input (convertible to long) is within a given range.


In order to be used, a validator needs to be nested into an h:input component that requires it.
To dictate the boundaries of the acceptable range, you need to specify the values for the minimum and/or maximum attributes to each of these validators.

Let's exemplify this. Assume we were creating an application that calculates monthly rates for the chosen loan. The client is expected to input the number of months they require the loan for, but our application does not allows this to be less than 6 or greater than 120 (or 10 years).

If we had a backing bean called loansManager and, within it, a HtmlInputText field, the component we would use would look like this:
<h:inputText id="account" required="true"
binding="#{loansManager.client.accountNumber}"
requiredMessage="Value is required!">
<f:validator validatorId="AccountNumberValidator"/>
</h:inputText>

Custom validators



If you need validation beyond the scope of the afore mentioned built-in components, you will have to write a custom validator to take care of your requirements.

You have two options here, to create a validator class or to create a validator method in one of your backing beans.

The first approach means implementing the Validator interface and it's validate method.

This time assume our application expects the client to input his account number in form ***.*******.**, where each * represents one character (probably should be a digit, but let's keep it simple).

The implementing class would then look like this:

package loans.validators;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

public class AccountNumberValidator implements Validator {

public AccountNumberValidator() {
}

public void validate(FacesContext ctxt, UIComponent cmp, Object val) {

String message = null;

try {
String[] parts = ((String)val).split("-");
if (parts.length < 3)
message = "Wrong account number!";
else {
if (parts[0].length() != 3 || parts[1].length() != 7 || parts[2].length() != 2)
message = "Wrong account number!";
}
} catch (Exception e){}

if (message != null) {
FacesMessage msg = new FacesMessage(
FacesMessage.SEVERITY_ERROR, message, message);
throw new ValidatorException(msg);
}
}
}

To register this validator, you would have to add the following snippet to your faces-config.xml file:
<validator>
<validator-id>AccountNumberValidator</validator-id>
<validator-class>loans.validators.AccountNumberValidator</validator-class>
</validator>

Now, you can add your validator to any h:input component that requires it.

For example:
<h:inputText id="account" required="true"
binding="#{loansManager.client.accountNumber}"
requiredMessage="Value is required!">
<f:validator validatorId="AccountNumberValidator"/>
</h:inputText>

The second option, mentioned earlier, is writing a validator method instead. For this, we would make a validateAccountNumber method in our loansManager backing bean. The method should look like this:

public void validateAccountNumber(FacesContext ctxt, UIComponent cmp, Object val) {

String message = null;

try {
String[] parts = ((String)val).split("-");
if (parts.length < 3)
message = "Wrong account number!";
else {
if (parts[0].length() != 3 || parts[1].length() != 7 || parts[2].length() != 2)
message = "Wrong account number!";
}
} catch (Exception e){}

if (message != null) {
FacesMessage msg = new FacesMessage(
FacesMessage.SEVERITY_ERROR, message, message);
ctxt.addMessage(cmp.getClientId(ctxt), mess);
}
}

Note that this time, instead of throwing a validator exception, we're manually adding a message the FacesContext's queue. In the previous example this was done automatically.

To use this new validator, you would add it to your h:input component like this:
<h:inputText id="account" required="true"
binding="#{loansManager.client.accountNumber}"
validator="#{loansManager.validateAccountNumber}"
requiredMessage="Value is required!"/>

Note:
You might as well do validation anywhere inside your application and just add the message to the FacesContext in the same manner you've seen earlier, but this has one important constraint. In place of cmp.getClientId(ctxt) inside
ctxt.addMessage(cmp.getClientId(ctxt), msg);
you would have to hard code the component id:
ctxt.addMessage("account", msg);

since you no longer have the reference to the component itself.
Also, you might put a global message (not bound to any component) into a FacesContext message queue with
ctxt.addMessage(null, msg);

but these messages can only be displayed with <h:messages/> element.

Saturday, April 4, 2009

Extended Persistence Context

Extended persistence context is an interesting feature of the JPA specification. It allows one to declare that the injected EntityManager lives beyond the scope of the JTA transaction, that is, to extend it to the lifetime of the stateful session bean itself. This is achieved with the following annotation:
@PersistenceContext(type=PersistenceContextType.EXTENDED) EntityManager em;

Doing this has a couple of implications.
First of all, within the extended persistence context, managed entities remain managed even after the transaction commits (or is rolled back). This behavior is very different than that of a transaction scoped context, were entities get detached at the end of the transaction.
Second implication important to note is that changes done to the managed entities from outside of a transaction will not be propagated to the database immediately, and will be buffered instead. In order to get the changes reflected in the database, the EntitiyManager needs to be accessed from a transaction context and flushed (by invoking it's flush() method).

For obvious reasons, extended persistence context is only available within stateful Session Beans.

The most frequently quoted example of use is a multi-step conversation with a client, where the data that is to be persisted in a single transaction is acquired form a client in a series of steps with arbitrary periods of time in between. In this scenario, changes are sequentially applied to the managed entity in question, and the EntityManager is flushed at the very end of the conversation.

To illustrate this, image you were dealing with a multi-page client registration form. On each page, the client is expected to input some information about themselves, but each time the information pertains to a different aspect. For example, the first page could ask for general details, like name and title, the second would deal with the contact info, and the third could ask for some optional data, like an average income or a favorite hobby.
Now, to achieve this, you could create a stateful bean similar to the following:
@Stateful
public class RegistrationBean implements Registration
{
@PersistenceContext(type=PersistenceContextType.EXTENDED) EntityManager em;

private Customer customer;

public long createCustomer(String name, String lastName)
{
customer = new Customer();
customer.setName(name);
customer.setLastName(lastName);
em.persist(customer);
return customer.getId();
}

public void setContacts(String address, String phoneNumber, String email)
{
customer.setAddress(address);
customer.setPhoneNumber(phoneNumber);
customer.setEmail(email);
}

public void setOptionals(...)
{
...
}

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void confirmRegistration() {
em.flush();
}
}

In conclusion, it's worth to note that the same effect could have been achieved by wrapping the whole conversation in a single user-managed transaction, but that it still makes more sense to exploit the power of an extended persistence context as it provides both a simpler and cleaner solution.