PDA

View Full Version : How to set-up SecurityContext in a MockStrutsTestCase



melinate
Jan 28th, 2005, 03:29 PM
I'm adding Method Invocation security to my AppFuse project [Acegi authentication + Struts + Spring + Hibernate]. I've gotten pretty far, but I can't find an example that helps me to set up the SecureContext for use in my testing.

The application runs when in the servlet container [Tomcat 5.0.30], but I seem to have not gotten the configuration quite right, because I can still access methods I should not be able to.

The existing test setup method is included below. I need to add a SecureContext to this somehow so I can test the normal webapp stuff as well as testing my authorization rules.

I'm fairly new to Acegi and actually Spring in general so if this is fairly obvious to someone else feel free to bop me on the head ;)

Thanks for any help,

Nathan Anderson

-----

protected void setUp() throws Exception {
super.setUp();

// initialize Spring
MockServletContext sc = new MockServletContext("");
sc.addInitParameter(ContextLoader.CONFIG_LOCATION_ PARAM,
"/WEB-INF/applicationContext*.xml");
ServletContextListener contextListener = new ContextLoaderListener();
ServletContextEvent event = new ServletContextEvent(sc);
contextListener.contextInitialized(event);

// magic bridge to make StrutsTestCase aware of Spring's Context
getSession().getServletContext().setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT _ATTRIBUTE,
sc.getAttribute(WebApplicationContext.ROOT_WEB_APP LICATION_CONTEXT_ATTRIBUTE));

ctx = WebApplicationContextUtils.getRequiredWebApplicati onContext(
getSession().getServletContext());

// populate the userForm and place into session
UserManager userMgr = (UserManager) ctx.getBean("userManager");
user = userMgr.getUser("tomcat");
getSession().setAttribute(Constants.USER_KEY, user);
}

Ben Alex
Jan 30th, 2005, 04:28 PM
My first tip would be to look at AbstractDependencyInjectionSpringContextTests, which is a more elegant way of setting up tests with an application context. This class with DI your unit test with its collaborators - a very nice way of doing it.

Regard the Acegi Security part of the question, take a look at the Attributes Sample's BankTests. It shows the code to setup and tear down the secure context, as well as populate it with a test authority.

Generally speaking you should try and test without using Acegi Security. For most applications that means simply not including the MethodSecurityInterceptor or SecurityEnforcementFilter at test time. What are you actually trying to test?

melinate
Jan 30th, 2005, 06:02 PM
I'll take a look at those examples you directed me to...

I fix the configuration and the application now works exactly like it should [including the Acegi Security features]. So now I just need to fix these tests to move on.

I really should have two sets of tests. One set is in place now to test my Struts Actions, this is what broke when I added the MethodSecurityInterceptor to my Spring configuration. And a separate set of tests for the security features.

Right now the Spring configuration is the same for deployment as it is for testing. I hope to not have to change that, but we'll see after I take a look at the examples you suggested.

Thanks,
Nathan

melinate
Jan 31st, 2005, 12:27 PM
I had a chance to look at those examples last night and try some stuff out. So far without complete success, but I'm hopeful I'm on the right track :)

Because I am unit testing Struts actions, I have to have my test classes extend MockStrutsTestCase. That means that AbstractDependencyInjectionSpringContextTests can't be used in this case [although it is a good suggestion and will likely be used elsewhere ;)]. What MockStrutsTestCase allows us to do is send a request to the controller and test the results of the action [proper redirection, no error messages, etc.] without running it inside a servlet container.

Because the tests are basically setting up a its own "servlet container" for the action to use, I need to save the SecureContext into the mock Session. I tried this by making a call to the following methods at the begining and end of each test [similar to the BankTests example], but I still get "A valid SecureContext was not provided in the RequestContext":

protected void createSecureContext(String user, String[] authorities) {

GrantedAuthority[] ga = null;

if (authorities!=null && authorities.length>0) {
ga = new GrantedAuthorityImpl[authorities.length];

for &#40;int i=0; i<authorities.length; i++&#41; &#123;
ga&#91;i&#93; = new GrantedAuthorityImpl&#40;authorities&#91;i&#93;&#41;;
&#125;
&#125;

TestingAuthenticationToken auth = new TestingAuthenticationToken&#40;user,
user, ga&#41;;

SecureContextImpl secureContext = new SecureContextImpl&#40;&#41;;
secureContext.setAuthentication&#40;auth&#41;;
getMockRequest&#40;&#41;.getSession&#40;&#41;.setAttribute&#40;HttpSes sionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATIO N_KEY, secureContext&#41;;
&#125;

protected void destroySecureContext&#40;&#41; &#123;
getMockRequest&#40;&#41;.getSession&#40;&#41;.setAttribute&#40;HttpSes sionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATIO N_KEY, null&#41;;
&#125;
Any glaring mistakes?

Nathan

melinate
Jan 31st, 2005, 07:47 PM
Ben,

I looked further into the option of not using the MethodSecurityInterceptor during testing and found it to be quite easy, and actually fairly consistant with the way AppFuse currently works.

So unless something unexpected happends I'll be doing my unit tests without security enabled, just as you recommend.

Thanks for the help,

Nathan

Ben Alex
Feb 4th, 2005, 01:42 AM
Any glaring mistakes?

Whilst you've shifted to testing without security - a good idea I might again mention - just for your info the reason why this did not work is all Acegi Security classes must have an Authentication available via the ContextHolder. The role of the HttpSessionIntegrationFilter is to copy the Authentication between ContextHolder and HttpSession. As such, if HttpSessionIntegrationFilter is not being called by MockStrutsTestCase's "servlet simulator", Authentication will never become available on ContextHolder and then you'll get the error described. I'd expect the solution would be to use ContextHolder.setContext(SecureContext), but it really depends on the internals of how MockStrutsTestCase works with regard to threading.

carbon60
Aug 31st, 2007, 02:18 PM
This thread exposes one of my problems: a domain object might need a specific "created_by" field to be populated with the user creating the object. How do you deal with this in tests that aren't running under Acegi?

Thanks,

A.

karldmoore
Sep 3rd, 2007, 01:26 PM
You can setup the security context information yourself programmatically in the test to allow this to work.