Binding objects using custom binders
You might already have read the Play documentation about object binding. As validation is
extremely important in any application, it basically has to fulfill several tasks.
First, it should not allow the user to enter wrong data. After a user has filled a form, he should
get a positive or negative feedback, irrespective of whether the entered content was valid or
not. The same goes for storing data. Before storing data you should make sure that storing
it does not pose any future problems as now the model and the view layer should make sure
that only valid data is stored or shown in the application. The perfect place to put such a
validation is the controller.
As a HTTP request basically is composed of a list of keys and values, the web framework
needs to have a certain logic to create real objects out of this argument to make sure the
application developer does not have to do this tedious task.
You can find the source code of this example in the chapter2/binder directory.
How to do it…
Create or reuse a class you want created from an item as shown in the following code snippet:
public class OrderItem {
@Required public String itemId;
public Boolean hazardous;
public Boolean bulk;
public Boolean toxic;
public Integer piecesIncluded;
public String toString() {
return MessageFormat.format("{0}/{1}/{2}/{3}/{4}", itemId,
piecesIncluded, bulk, toxic, hazardous);
}
}
Create an appropriate form snippet for the index.xml template:
#{form @Application.createOrder()}
<input type="text" name="item" /><br />
<input type="submit" value="Create Order">
#{/form}
Create the controller:
public static void createOrder(@Valid OrderItem item) {
if (validation.hasErrors()) {
render("@index");
}
renderText(item.toString());
}
Create the type binder doing this magic:
@Global
public class OrderItemBinder implements TypeBinder<OrderItem> {
@Override
public Object bind(String name, Annotation[] annotations, String
value,
Class actualClass) throws Exception {
OrderItem item = new OrderItem();
List<String> identifier = Arrays.asList(value.split("-", 3));
if (identifier.size() >= 3) {
item.piecesIncluded = Integer.parseInt(identifier.get(2));
}
if (identifier.size() >= 2) {
int c = Integer.parseInt(identifier.get(1));
item.bulk = (c & 4) == 4;
item.hazardous = (c & 2) == 2;
item.toxic = (c & 1) == 1;
}
if (identifier.size() >= 1) { item.itemId = identifier.get(0);
}
return item;
}
}
How it works…
With the exception of the binder definition all of the preceding code has been seen earlier. By
working with the Play samples you already got to know how to handle objects as arguments
in controllers. This specific example creates a complete object out of a simple String. By
naming the string in the form value (<input …name=”item” />) the same as the controller
argument name (createOrder(@Valid OrderItem item)) and using the controller
argument class type in the OrderItemBinder definition (OrderItemBinder implements
TypeBinder<OrderItem>), the mapping is done.
The binder splits the string by a hyphen, uses the first value for item ID, the last for
piìesIncluded, and checks certain bits in order to set some Boolean properties.
By using curl you can verify the behavior very easily as shown:
curl -v -X POST --data "item=Foo-3-5" localhost:9000/order
Foo/5/false/true/true
Here Foo resembles the item ID, 5 is the piecesIncluded property, and 3 is the argument
means that the first two bits are set and so the hazardous and toxic properties are set, while
bulk is not.
There’s more…
The TypeBinder feature has been introduced in Play 1.1 and is documented at http://
www.playframework.org/documentation/1.2/controllers#custombinding.
Using type binders on objects
Currently, it is only possible to create objects out of one single string with a TypeBinder. If
you want to create one object out of several submitted form values you will have to create your
own plugin for this as workaround. You can check more about this at:
http://groups.google.com/group/play-framework/browse_thread/
thread/62e7fbeac2c9e42d
Be careful with JPA using model classes
As soon as you try to use model classes with a type binder you will stumble upon strange
behavior, as your objects will always only have null or default values when freshly instanced.
The JPA plugin already uses a binding and overwrites every binding you are doing.
Validating objects using annotations
Basic validation should be clear for you now. It is well-documented in the official
documentation and shows you how to use the different annotations such as @Min, @Max,
@Url, @Email, @InFuture, @InPast, or @Range. You should go a step forward and add
custom validation. An often needed requirement is to create some unique string used as
identifier. The standard way to go is to create a UUID and use it. However, validation of the
UUID should be pretty automatic and you want to be sure to have a valid UUID in your models.
You can find the source code of this example in the chapter2/annotation-validation
directory.
Getting ready
As common practice is to develop your application in a test driven way, we will write an
appropriate test as first code in this recipe. In case you need more information about
writing and using tests in Play, you should read http://www.playframework.org/
documentation/1.2/test.
This is the test that should work:
public class UuidTest extends FunctionalTest {
@Test
public void testThatValidUuidWorks() {
String uuid = UUID.randomUUID().toString();
Response response = GET("/" + uuid);
assertIsOk(response);
assertContentEquals(uuid + " is valid", response);
}
@Test
public void testThatInvalidUuidWorksNot() {
Response response = GET("/absolutely-No-UUID");
assertStatus(500, response);
}
}
So whenever a valid UUID is used in the URL, it should be returned in the body and whenever
an invalid UUID has been used, it should return HTTP error 500.
How to do it…
Add an appropriate configuration line to your conf/routes file:
GET /{uuid} Application.showUuid
Create a simple @UUID annotation, practically in its own annotations or validations package:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Constraint(checkWith = UuidCheck.class)
public @interface Uuid {
String message() default "validation.invalid.uuid";
}
Create the appropriate controller, which uses the @Uuid annotation:
public class Application extends Controller {
public static void showUuid(@Uuid String uuid) {
if (validation.hasErrors()) {
flash.error("Fishy uuid");
error();
}
renderText(uuid + " is valid");
}
}
Create the check, which is triggered by the validation. You might want to put it into the
checks package:
public class UuidCheck extends AbstractAnnotationCheck<Uuid> {
@Override
public booleanisSatisfied(Object validatedObject, Object value,
OValContext context, Validator validator)
throws OValException {
try {
UUID.fromString(value.toString());
return true;
} catch (IllegalArgumentException e) {}
return false;
}
}
How it works…
When starting your application via play test and going to http://localhost:9000/@
tests you should be able to run the UuidTest without problems.
Except the UuidCheck class , most of this here is old stuff. The Uuid annotation has two
specialties. First it references the UuidCheck with a constraint annotation and second you
can specify a message as argument. This message is used for internationalization.
The UuidCheck class is based on an Oval class. Oval is a Java library and used by the Play
framework for most of the validation tasks and can be pretty easily extended as you can see
here. All you need to implement is the isSatisfied() method . In this case it has tried to
convert a String to a UUID. If it fails, the runtime exception thrown by the conversion is caught
and false is returned, marking the check as invalid.
There’s more…
The oval framework is pretty complex and the logic performed here barely scratches
the surface. For more information about oval, check the main documentation at
http://oval.sourceforge.net/.
Using the configure() method for setup
The AbstractAnnotationCheck class allows you to overwrite the configure(T object)
method (where T is generic depending on your annotation). This allows you to set up missing
annotation parameters with default data; for example, default values for translations. This is
done by many of the already included Play framework checks as well.
Annotations can be used in models as well
Remember that the annotation created above may also be used in your models, so you can
label any String as a UUID in order to store it in your database and to make sure it is valid
when validating the whole object.
@Uuid public String registrationUuid;
Adding annotation-based right checks to your controller
Sooner or later any business application will have some login/logout mechanism and after
that there are different types of users. Some users are allowed to do certain things, others
are not. This can be solved via a check in every controller, but is way too much overhead. This
recipe shows a clean and fast (though somewhat limited) solution to creating security checks,
without touching anything of the business logic inside your controller.
You can find the source code of this example in the chapter2/annotation-rights
directory.
Getting ready
Again we will start with a test, which performs several checks for security:
public class UserRightTest extends FunctionalTest {
@Test
public void testSecretsWork() {
login("user", "user");
Response response = GET("/secret");
assertIsOk(response);
assertContentEquals("This is secret", response);
}
@Test
public void testSecretsAreNotFoundForUnknownUser() {
Response response = GET("/secret");
assertStatus(404, response);
}
@Test
public void testSuperSecretsAreAllowedForAdmin() {
login("admin", "admin");
Response response = GET("/top-secret");
assertIsOk(response);
assertContentEquals("This is top secret", response);
}
@Test
public void testSecretsAreDeniedForUser() {
login("user", "user");
Response response = GET("/top-secret");
assertStatus(403, response);
}
private void login(String user, String pass) {
String data = "username=" + user + "&password=" + pass;
Response response = POST("/login",
APPLICATION_X_WWW_FORM_URLENCODED, data);
assertIsOk(response);
}
}
As you can see here, every test logs in with a certain user first, and then tries to access a
resource. Some are intended to fail, while some should return a successful access. Before
every access, a login is needed using the login() method . In case you wonder why a simple
HTTP request stores the returned login credentials for all of the next requests, this is actually
done by the logic of the FunctionalTest, which stores all returned cookies from the login
request during the rest of the test.
How to do it…
Add the needed routes:
POST /login Application.login
GET /secret Application.secret
GET /top-secret Application.topsecret
Create User and Right entities:
@Entity
public class User extends Model {
public String username;
public String password;
@ManyToMany
public Set<Right> rights;
public booleanhasRight(String name) {
Right r = Right.find("byName", name).first();
return rights.contains(r);
}
}
A simple entity representing a right and consisting of a name is shown in the following code:
@Entity
public class Right extends Model {
@Column(unique=true)
public String name;
}
Create a Right annotation:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Right {
String value();
}
Lastly, create all the controller methods:
public class Application extends Controller {
public static void index() {
render();
}
@Before(unless = "login")
public static void checkForRight() {
String sessionUser = session.get("user");
User user = User.find("byUsername", sessionUser).first();
notFoundIfNull(user);
Right right = getActionAnnotation(Right.class);
if (!user.hasRight(right.value())) {
forbidden("User has no right to do this");
}
}
public static void login(String username, String password) {
User user = User.find("byUsernameAndPassword", username,
password).first();
if (user == null) {
forbidden();
}
session.put("user", user.username);
}
@Right("Secret")
public static void secret() {
renderText("This is secret");
}
@Right("TopSecret")
public static void topsecret() {
renderText("This is top secret");
}
}
How it works…
Going through this step by step reveals surprisingly few new items, but rather a simple and
concise change at the core of each controller call. Neither the routes are new, nor the entity
definitions, or its possibility to create the hasRight() method . The only real new logic is
inside the controller. The logic here is not meant as business logic of your application but
rather permission checking. On the one hand every security aware controller has a @Right
annotation at its definition, which defines the required right as a text string.
On the other hand all the logic regard permissions is executed at the checkForRight()
method before every controller call. It inspects the annotation value and checks whether
the currently logged-in user has this specific annotation value as a right set using the
hasRight() method defined in the user entity.
There’s more…
This is a pretty raw method to check for rights. It imposes several design weaknesses and
severe performance issues. But it is a start to go further.
Be flexible with roles instead of rights
The security model here is pretty weak. You should think of using roles on user level instead
of rights, and check these roles for the rights called. This allows you to create less fine-grained
permission checks such as a “Content editor” and a “publisher” role for example.
More speed with caching
The whole code presented here can be pretty slow. First you could cache the roles or rights of
a certain user. Furthermore you could cache the security right of the controller action and the
login credentials, which are looked up on every request.
Increased complexity with context-sensitive rights
The security checks compared here are very simple. If you want to have a right, then only the
owner of an object can change it, you are not completely off with the solution presented here.
You need to define more logic inside your controller call.
Check out the deadbolt module
There is a module which already does more checks and has more options than this example.
You should take a look at the deadbolt module at http://www.playframework.org/
modules/deadbolt. This module offers restrictions not only on the controller level, but also
inside Views. Deadbolt also provides arbitrary dynamic security, which allows you to provide
application-specific security strategies.






September 20, 2011
Java