Skip to content

Spring Security 3 Unit Testing Example with DbUnit

This tutorial extends on the baseline laid by Spring Security Tutorial maven archetype.

I’ve modified the spring security config file and will be unit testing a simple ProfileService (CRUD) on a Profile entity (User object). The tests include login/logout and spring services security unit testing.

application-security.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans 
    xmlns="http://www.springframework.org/schema/security" 
    xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd


http://www.springframework.org/schema/security


http://www.springframework.org/schema/security/spring-security-3.0.xsd">

	<global-method-security pre-post-annotations="enabled"/>
	
	
	<http use-expressions="true">
		<intercept-url pattern="/secure/extreme/**" access="hasRole('ROLE_SUPERVISOR')"/>
		<intercept-url pattern="/secure/**" access="isAuthenticated()" />
		<intercept-url pattern="/**" access="permitAll" />
		<form-login />
		<logout />
		<remember-me />
		
		<!-- Uncomment to limit the number of sessions a user can have -->
		<session-management invalid-session-url="/timeout.jsp">
			<concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
		</session-management>
	</http>
	
	<authentication-manager>
		<authentication-provider>
			<!--  <password-encoder hash="md5"/> -->
			<user-service>
				<user name="admin" password="admin" authorities="ROLE_ADMIN, ROLE_USER" />
				<user name="user" password="user" authorities="ROLE_USER" />
				<user name="reports" password="reports" authorities="ROLE_REPORTS" />
			</user-service>
		</authentication-provider>
	</authentication-manager>	
		
</beans:beans>

Base DB Unit Test with utils for Spring Security

import java.io.FileInputStream;
import java.sql.Connection;
import javax.inject.Inject;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
import org.dbunit.database.*;
import org.dbunit.operation.DatabaseOperation;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.test.context.transaction.*;

/**
 * Base Test that wraps a DbUnit style Test case by inserting a sample testdata 
 * on startup. Provides necessary functions to login/logout, add roles, etc
 */
public abstract class BaseSecurityTest {

	private static final Logger logger = Logger
			.getLogger(BaseSecurityTest.class);

	Connection con;
	IDatabaseConnection dbUnitCon;
	IDataSet dataSet;

	@Inject
	private DataSource dataSource;

	protected abstract String getTestClassname();

	@BeforeTransaction
	public void beforeTx() throws Exception {
		logger.debug("Opening Db Connection ... ");
		con = DataSourceUtils.getConnection(dataSource);
		dbUnitCon = new DatabaseConnection(con);
		dataSet = getDataSet(getTestClassname());

		DatabaseOperation.CLEAN_INSERT.execute(dbUnitCon, dataSet);
	}

	@AfterTransaction
	public void afterTx() throws Exception {
		logger.debug("Releasing Db Connection ...");
		DataSourceUtils.releaseConnection(con, dataSource);
	}

	protected IDataSet getDataSet(String name) throws Exception {
		return new FlatXmlDataSetBuilder().build(new FileInputStream(
				"src/test/resources/" + name + ".xml"));
	}

	protected void clearContext() {
		SecurityContextHolder.clearContext();
	}

	protected void login(String username, String password) {
		SecurityContextHolder.getContext().setAuthentication(
				new UsernamePasswordAuthenticationToken(username, password));

		logger.debug("User:" + username + " logged in");
	}

	protected String getLoginDetails() {
		Object principal = SecurityContextHolder.getContext()
				.getAuthentication().getPrincipal();

		if (principal instanceof UserDetails) {
			return ((UserDetails)principal).getUsername();
		}
		else {	  
			return principal.toString();		
		}
		
	}
}

ProfileServiceTest

import static org.junit.Assert.assertEquals;
import java.util.List;
import javax.inject.Inject;
import org.apache.log4j.Logger;
import xxx.Profile;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
		"classpath*:spring/applicationContext-core.xml",
		"classpath*:spring/applicationContext-mail.xml",
		"classpath*:spring/applicationContext-security.xml" })
@TransactionConfiguration
@Transactional
public class ProfileServiceTest extends BaseSecurityTest {

	private static final Logger logger = Logger
			.getLogger(ProfileServiceTest.class);

	@Inject
	private ProfileService profileService;

	@Override
	protected String getTestClassname() {
		return "ProfileServiceTest";
	}

	@Test
	public void testUserLogin() {
		login("admin", "admin");
		String username = getLoginDetails();
		assertEquals(username, "admin");
		
		login("user", "user");
		String username2 = getLoginDetails();
		assertEquals(username2, "user");
	
		login("reports", "reports");
		String username3 = getLoginDetails();
		assertEquals(username3, "reports");
	}

	@Test
	public void testRead() {
		
		// Test with role that has access to the service		
		login("user", "user");
		profileService.read("1");
		
		login("reports", "reports");
		profileService.read("1");
		
		// Test with role that doesn't have access to the service
		login("admin", "admin");
		try {
			profileService.read("1");
		}
		catch (AccessDeniedException e) {
			// Expected
			assert(true);
			return;
		}
		
		// it should not reach here
		assert(false);
	}
	
	@Test
	public void testFindProfiles() {
		List<Profile> list = profileService.FindProfiles();
		assertEquals(4, list.size());
	}

	@Test
	public void testFindProfileById() {
		List<Profile> list = profileService.findProfileById("123");
		assertEquals(1, list.size());
	}

	@Test
	public void testSave() {
		
		// Test with role that has access to the service				
		login("admin", "admin");
		Profile dummy = new Profile();
		profileService.save(dummy);		
		
		// Test with role that doesn't have access to the service
		login("reports", "reports");
		try {
			profileService.save(dummy);
		}
		catch (AccessDeniedException e) {
			// Expected
			assert(true);
			return;
		}
		
		// it should not reach here
		assert(false);
	}
}

The Profile Service

public interface ProfileService {

	@PreAuthorize("hasRole('ROLE_USER') or hasRole('ROLE_REPORTS')")	
	Profile read(String id);

	List<Profile> findProfiles();

	List<Profile> findProfileById(String Id);

	@PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER')")
	Profile save(Profile account);
	
	@PreAuthorize("hasRole('ROLE_ADMIN')")
	void delete();

}
[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

One Comment

  1. Sasya Y wrote:

    I am trying to write unit tests for @PreAuthorize. Please let me what configurations should be specified in my xml file other than the test xml that you have specified in the above example. Though we have specified authorities in the xml file when will this be loaded.

    I tried the above example but it is not working for me.

    Saturday, November 12, 2011 at 5:41 am | Permalink

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*