In order to add access-control to our application we first need to integrate JPA Security.
Integrating JPA Security is as simple as configuring the persistence provider:
<persistence-unit name="contacts" transaction-type="RESOURCE_LOCAL"> <provider>net.sf.jpasecurity.persistence.SecurePersistenceProvider</provider> <class>net.sf.jpasecurity.contacts.model.User</class> <class>net.sf.jpasecurity.contacts.model.Contact</class> <properties> <property name="net.sf.jpasecurity.persistence.provider" value="org.hibernate.ejb.HibernatePersistence" /> <property name="net.sf.jpasecurity.security.authentication.provider" value="net.sf.jpasecurity.security.authentication.StaticAuthenticationProvider"/> <property name="hibernate.hbm2ddl.auto" value="create-drop" /> <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" /> <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver" /> <property name="hibernate.connection.url" value="jdbc:hsqldb:mem:contacts" /> <property name="hibernate.connection.username" value="sa" /> <property name="hibernate.connection.password" value="" /> </properties> </persistence-unit>
The following line has changed in order to use JPA Security:
<provider>net.sf.jpasecurity.persistence.SecurePersistenceProvider</provider>
The following line is added to specify the persistence provider:
<property name="net.sf.jpasecurity.persistence.provider" value="org.hibernate.ejb.HibernatePersistence" /> <property name="net.sf.jpasecurity.security.authentication.provider" value="net.sf.jpasecurity.security.authentication.StaticAuthenticationProvider"/>
OK, let's run our program again and let's see what happens...
As you can see from the output: nothing happend, everything stayed as before. JPA Security smoothly integrated into our application, we even can't see it work. We have not defined access rules for now.
We define access rules by annotating our domain objects:
import javax.annotation.security.DeclareRoles; import javax.annotation.security.RolesAllowed; import net.sf.jpasecurity.security.Permit; @Entity @DeclareRoles({"admin", "user"}) @RolesAllowed("admin") @Permit(rule = "name = CURRENT_PRINCIPAL") public class User { ... }
import javax.annotation.security.RolesAllowed; import net.sf.jpasecurity.security.Permit; @Entity @RolesAllowed("admin") @Permit(rule = "owner.name = CURRENT_PRINCIPAL") public class Contact { ... }
We have added three annotations to User and two annotations to Contact:
These access rules that show the capability of JPA Security. We have specified, that a User object may be accessed either by an admin or by the user represented by that object. For the Contact objects we have specified that they may be accessed by admins, too and that they may be accessed by their owners.
Let's execute our program now and see what happens...
As we can see an exception is thrown, a java.lang.SecurityException at the line where we try to persist the user john. That's great. We should have expected this, as we are not authenticated and thus are not permitted to save any user.
Apparently we need admin rights to create users, that means we must run the createUsers method as admin. We can to this by using the runAs method of the StaticAuthenticationProvider. We change the method createUsers to run as user "root" with the role "admin":
public static void createUsers(final EntityManagerFactory entityManagerFactory) { StaticAuthenticationProvider.runAs("root", Arrays.asList("admin"), new PrivilegedAction() { public Object run() { EntityManager entityManager; entityManager = entityManagerFactory.createEntityManager(); entityManager.getTransaction().begin(); entityManager.persist(new User("John")); entityManager.persist(new User("Mary")); entityManager.getTransaction().commit(); entityManager.close(); return null; } }); }
Now let's execute our program again and see what happens now...
Another exception is thrown, this time it is a javax.persistence.NoResultException at the line where we try to select john from the database. That's good news. We are just authenticated during the method createUsers. So when we try to create the contacts, we are not authenticated thus not permitted to select john from the database. This means, JPA Security works. An additional output proves this, as we can see the number of users we get displayed by our displayUserCount method is actually 0. JPA Security filters out the users we are not permitted to see (which are all).
We are now using runAs to create the contacts, too:
public static void createContacts(final EntityManagerFactory entityManagerFactory) { StaticAuthenticationProvider.runAs("root", Arrays.asList("admin"), new PrivilegedAction() { public Object run() { EntityManager entityManager; entityManager = entityManagerFactory.createEntityManager(); entityManager.getTransaction().begin(); User john = (User)entityManager.createQuery("SELECT user FROM User user WHERE user.name = 'John'").getSingleResult(); User mary = (User)entityManager.createQuery("SELECT user FROM User user WHERE user.name = 'Mary'").getSingleResult(); entityManager.persist(new Contact(john, "peter@jpasecurity.sf.net")); entityManager.persist(new Contact(john, "0 12 34 - 56 789")); entityManager.persist(new Contact(mary, "paul@jpasecurity.sf.net")); entityManager.persist(new Contact(mary, "12 34 56 78 90")); entityManager.getTransaction().commit(); entityManager.close(); return null; } }); }
When we now run the application, we see that the exceptions are gone away. When we take a look at the output of our displayUserCount and displayContactCount methods, we see that it is actually 0. We should have expected this since we just authenticated for the createXXX methods. Let's authenticate as "root" with the role admin at the beginning of the main method:
public static void main(String[] args) { StaticAuthenticationProvider.authenticate("root", "admin"); EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("contacts"); createUsers(entityManagerFactory); displayUserCount(entityManagerFactory); createContacts(entityManagerFactory); displayContactCount(entityManagerFactory); }
When we now execute our program we can see from the output, that we get 2 users and 4 contacts. This is what we expected, as we are permitted to select all data with the role "admin".
But what is, when we authenticate as "John"? Let's try...
public static void main(String[] args) { StaticAuthenticationProvider.authenticate("John"); EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("contacts"); createUsers(entityManagerFactory); displayUserCount(entityManagerFactory); createContacts(entityManagerFactory); displayContactCount(entityManagerFactory); }
Now the output says that we get 1 user and 2 contacts (that are Johns contacts). We get only one user because we are only allowed to retrieve our own User object. Obviously JPA Security works: It filters out the users and contacts we are not allowed to see. We may authenticate as "Mary" and see that it works, too.
Congratulations, you have successfully set up and worked with JPA Security. Next steps may be to read the manual, try the petclinic tutorial or take a look at the API-Documentation.