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();
}
One Comment
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.
Post a Comment