Announcement Announcement Module
Collapse
No announcement yet.
How can I test authorization in Junit? Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • How can I test authorization in Junit?

    I'm new to acegi security, and I'm trying to write a valid test in junit. Authentication is working fine, but I'm having trouble testing authorization around a specific method. Here's my configuration so far:

    <!-- ======================== AUTHENTICATION ======================= -->

    <bean id="authenticationManager" lazy-init="true"
    class="net.sf.acegisecurity.providers.ProviderMana ger">
    <property name="providers">
    <list>
    <ref local="daoAuthenticationProvider"/>
    </list>
    </property>
    </bean>

    <bean id="inMemryDaoImpl"
    class="net.sf.acegisecurity.providers.dao.memory.I nMemoryDaoImpl">
    <propery name="userMap">
    <value>
    username=password,UP_LOSSFACTS
    </value>
    </propery>
    </bean>

    <!-- ======================== Authorization ======================= -->

    <bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/>

    <bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.UnanimousBased">
    <property name="allowIfAllAbstainDecisions">
    <value>false</value>
    </property>
    <property name="decisionVoters">
    <list>
    <ref local="roleVoter"/>
    </list>
    </property>
    </bean>

    <bean id="autoproxy"
    class="org.sf.aop.framework.autoproxy.DefaultAdvis orAutoProxyCreator"/>

    <bean id="methodSecurityAdvisor"
    class="net.sf.acegisecurityf.intercept.method.aopa lliance.MethodDefinitionSourceAdvisor"
    autowire="constructor"/>

    <bean id="jclaimsSecurity"
    class="">
    <property name="authenticationManager">
    <ref bean="authenticationManager"/>
    </property>
    <property name="accessDecisionManager">
    <ref local="accessDecisionManager"/>
    </property>
    <property name="objectDefinitionSource">
    <value>
    com.nationwide.thi.jclaims.shared.authentication.A uthenticatedUserTest.methodToSecure=UP_LOSSFACTS
    </value>
    </property>
    </bean>

    What am I doing wrong? And any advice on the best way to write a junit test for this would also be very helpful! Thanks!

  • #2
    Hi,

    You haven't actually said what the trouble is... and this doesn't look complete. You don't have a classname in your "jclaimsSecurity" bean, for example.

    For setting up a Junit test, have you had a look at the test class for MethodSecurityInterceptor:

    http://acegisecurity.sourceforge.net...ptorTests.html

    and the configuration file it uses?

    Luke.

    Comment


    • #3
      That link is broken...

      I have a similar question, and because the link posted above is broken, it's probably quickest if I just ask it here.

      I have Acegi set up and working (AFAIK) in my web app. To be sure of this, I'm trying to write an automated test of the URL patterns defined in the "objectDefinitionSource" property of my FilterSecurityInterceptor.

      I have a JUnit test case that initialises all the security-related beans as well as a mock AuthenticationDao:

      Code:
      import junit.framework.TestCase;
      import net.sf.acegisecurity.providers.dao.AuthenticationDao;
      
      import org.easymock.EasyMock;
      import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
      import org.springframework.context.support.GenericApplicationContext;
      import org.springframework.mock.web.MockHttpServletRequest;
      
      public class AccessDecisionTest extends TestCase {
        
        // Constants
        private static final String
          AUTHENTICATION_DAO_BEAN_NAME = "authenticationDao",
          SECURITY_CONFIG_FILE = "classpath:WEB-INF/cmgr-security.xml";
        
        // Fixture
        private AuthenticationDao mockAuthenticationDao;
        private MockHttpServletRequest mockRequest;
        
        @Override
        protected void setUp() throws Exception {
          mockRequest = new MockHttpServletRequest();
          
          GenericApplicationContext context = new GenericApplicationContext();
          
          // Programatically add the mock AuthenticationDao bean
          mockAuthenticationDao = EasyMock.createStrictMock(AuthenticationDao.class);
          context.getBeanFactory().registerSingleton(
              AUTHENTICATION_DAO_BEAN_NAME, mockAuthenticationDao);
          
          // Read the other security-related beans from the XML file
          XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(context);
          xmlReader.loadBeanDefinitions(SECURITY_CONFIG_FILE);
          context.refresh();
          super.setUp();
        }
      
        public void testNothing() {
          mockRequest.setRequestURI("foo/bar.htm");
          // now what?
        }
      This works fine, all the beans are loaded OK, but obviously nothing else is being tested. In order to test my URL-to-authority mappings, which bean do I pass the request to, and how do I check what authority it says is required?

      Here's my security config (sorry if there's any irrelevant stuff here, I didn't want to leave out anything important):

      Code:
      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
        "http://www.springframework.org/dtd/spring-beans.dtd">
      <beans>
        
      	<bean id="filterChainProxy" class="net.sf.acegisecurity.util.FilterChainProxy">
          <property name="filterInvocationDefinitionSource">
            <value>
              CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
      		    <!-- PATTERN_TYPE_APACHE_ANT -->
              /.*=channelProcessingFilter,integrationFilter,authenticationProcessingFilter,rememberMeProcessingFilter,securityEnforcementFilter
            </value>
          </property>
        </bean>
        
        <bean id="channelProcessingFilter"
          class="net.sf.acegisecurity.securechannel.ChannelProcessingFilter"
        >
          <property name="channelDecisionManager">
            <ref local="channelDecisionManager"/>
          </property>
          <property name="filterInvocationDefinitionSource">
            <value>
              CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
              PATTERN_TYPE_APACHE_ANT
              <!-- These URLs need not be secure -->
              /about*=REQUIRES_INSECURE_CHANNEL
              /index*=REQUIRES_INSECURE_CHANNEL
              /welcome*=REQUIRES_INSECURE_CHANNEL
              <!-- All other URLs should be secure -->
              /*=REQUIRES_SECURE_CHANNEL
            </value>
          </property>
        </bean>
        
        <bean id="channelDecisionManager" class="net.sf.acegisecurity.securechannel.ChannelDecisionManagerImpl">
          <property name="channelProcessors">
            <list>
              <bean class="net.sf.acegisecurity.securechannel.SecureChannelProcessor"/>
              <bean class="net.sf.acegisecurity.securechannel.InsecureChannelProcessor"/>
            </list>
          </property>
        </bean>
        
        <bean id="securityEnforcementFilter"
            class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter"
        >
          <!-- Declare the overall security controller (see below) -->
          <property name="filterSecurityInterceptor" ref="filterSecurityInterceptor"/>
          <!-- Declare which component handles users who haven't authenticated yet -->
          <property name="authenticationEntryPoint" ref="authenticationEntryPoint"/>
        </bean>
      
        <bean id="filterSecurityInterceptor"
            class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor"
        >
          <property name="authenticationManager" ref="authenticationManager"/>
          <property name="accessDecisionManager" ref="accessDecisionManager"/>
          <property name="objectDefinitionSource">
            <value>
              CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
              PATTERN_TYPE_APACHE_ANT
              /keyword*details*=AUTHORITY_ADMIN
              /event*=AUTHORITY_USER
              /org*=AUTHORITY_USER
              /person*=AUTHORITY_USER
              /*=AUTHORITY_GUEST,AUTHORITY_USER,AUTHORITY_ADMIN
            </value>
          </property>
        </bean>
        
        <bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
          <property name="providers">
            <list>
              <ref local="rememberMeAuthenticationProvider"/>
              <ref local="daoAuthenticationProvider"/>
            </list>
          </property>
        </bean>
        
        <bean id="daoAuthenticationProvider"
          class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider"
        >
          <property name="authenticationDao" ref="authenticationDao"/>
          <property name="saltSource">
            <bean class="net.sf.acegisecurity.providers.dao.salt.SystemWideSaltSource">
              <property name="systemWideSalt">
                <value>passthesaltplease</value>
              </property>
            </bean>
          </property>
          <property name="passwordEncoder">
            <bean class="net.sf.acegisecurity.providers.encoding.Md5PasswordEncoder"/>
          </property>
          <property name="userCache" ref="userCache"/>
        </bean>
        
        <bean id="userCache"
          class="net.sf.acegisecurity.providers.dao.cache.EhCacheBasedUserCache"
        >
          <property name="cache" ref="userCacheBackend"/>
        </bean>
      
        <bean id="userCacheBackend"
          class="org.springframework.cache.ehcache.EhCacheFactoryBean"
        >
          <property name="cacheManager">
            <bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
              <property name="configLocation">
                <value>classpath:/ehcache-failsafe.xml</value>
              </property>
            </bean>
          </property>
          <property name="cacheName">
            <value>userCache</value>
          </property>
        </bean>
                 
        <bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.UnanimousBased">
          <property name="decisionVoters">
            <list>
              <!-- A trivial access control voter -->
              <bean class="au.com.bisinfo.cmgr.client.web.controller.accessvoters.YesVoter"/>
            </list>
          </property>
        </bean>
          
        <bean id="authenticationEntryPoint"
            class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"
        >
          <property name="loginFormUrl">
            <value>/login.htm</value>
          </property>  
          <property name="forceHttps">
            <value>true</value>
          </property>
        </bean>
        
        <bean id="authenticationProcessingFilter"
          class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter"
        >
          <property name="authenticationManager" ref="authenticationManager"/>
          <property name="authenticationFailureUrl">
            <value>/login.htm?action=retry</value>
          </property>
          <property name="defaultTargetUrl">
            <value>/</value>
          </property>
          <property name="filterProcessesUrl">
            <value>/j_acegi_security_check</value>
          </property>
          <property name="rememberMeServices" ref="rememberMeServices"/>
        </bean>
        
        <bean id="integrationFilter"
            class="net.sf.acegisecurity.context.HttpSessionContextIntegrationFilter"
        >
          <property name="context">
            <value>net.sf.acegisecurity.context.SecurityContextImpl</value>
          </property>
        </bean>
            
        <bean id="rememberMeProcessingFilter"
            class="net.sf.acegisecurity.ui.rememberme.RememberMeProcessingFilter"
        >
          <property name="rememberMeServices" ref="rememberMeServices"/>
        </bean>
        
        <bean id="rememberMeServices"
            class="net.sf.acegisecurity.ui.rememberme.TokenBasedRememberMeServices"
        >
          <property name="authenticationDao" ref="authenticationDao"/>
          <property name="key">
            <value>forgotmykeyagain</value>
          </property>
        </bean>
           
        <bean id="rememberMeAuthenticationProvider"
            class="net.sf.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider"
        >
          <property name="key">
            <value>iamthekeymaster</value>
          </property>
        </bean>
        
      </beans>
      TIA,

      Andrew

      P.S. keys and salts have been changed to protect the innocent ...

      Comment


      • #4
        Try using AbstractDependencyInjectionSpringContextTests instead of loading the app context manually.

        I'd suggest making a fake FilterInvocation object, then pass it to FilterSecurityInterceptor to see if it behaves as expected. Take a look at our unit tests for this class for further ideas.

        Comment


        • #5
          If you are trying to write object level security, you could check this blog:
          Testing Acegi Security

          Comment


          • #6
            Interesting ideas

            Originally posted by Ben Alex
            Try using AbstractDependencyInjectionSpringContextTests instead of loading the app context manually.
            I did try this at first, but couldn't figure out where/when to call registerSingleton() to add my mock AuthenticationDao before the beans in the XML file tried to reference it. I know I could have created another XML file defining the mock, just for this test, but it seems easier in this case just to use GenericApplicationContext. Would using AbstractDependencyInjectionSpringContextTests help solve my problem, given that all my security beans already seem to be loading OK, or was that just a general tip?

            Originally posted by Ben Alex
            I'd suggest making a fake FilterInvocation object, then pass it to FilterSecurityInterceptor to see if it behaves as expected. Take a look at our unit tests for this class for further ideas.
            Thanks for the tip, Ben. I've just read the JavaDoc for these classes. Just to be clear, are you saying I should pass the fake FilterInvocation to FilterSecurityInterceptor's invoke(FilterInvocation) method? The JavaDoc for this method doesn't explain its contract; is it fair to assume that a ServletException will be thrown if the user doesn't have the appropriate "AUTHORITY" for the URL in question? Or should I just look at your existing unit tests like you said?

            Originally posted by Alarmnummer
            If you are trying to write object level security, you could check this blog
            Hi Peter, I'm not actually testing object security, but I did read your blog and the technique of defining an EasyMock in a Spring config file is sure to be useful to me at some point, so I'll file that trick away for the future.

            Maybe you could post your EasyMockFactoryBean here or on your blog for others to reuse, or better still add it to the Spring JIRA as a proposed new class in the org.springframework.test package?

            Comment


            • #7
              Code:
              public class EasyMockFactoryBean implements InitializingBean, FactoryBean {
              
              	private Class targetClass;
              	private Object mock;
              
              	public Class getTargetClass() {
              		return targetClass;
              	}
              
              	public void setTargetClass(Class targetClass) {
              		this.targetClass = targetClass;
              	}
              
              	public Object getObject() throws Exception {
              		return mock;
              	}
              
              	public Class getObjectType() {
              		return targetClass;
              	}
              
              	public boolean isSingleton() {
              		return true;
              	}
              
              	public void afterPropertiesSet() throws Exception {
              		Assert.notNull(targetClass, "Required property 'targetClass' is unset.");
              		mock = EasyMock.createMock(targetClass);
              	}
              }

              Comment


              • #8
                Where does Assert come from?

                Hi Peter,

                Thanks so much for posting your code. I am creating my own such class as we speak. One question, what Assert class are you using? To get it to compile, I had to import junit.framework.Assert and change that line to say:
                Code:
                Assert.assertNotNull("Required property 'targetClass' is unset.", targetClass);

                Comment


                • #9
                  org.springframework.util

                  It is org.springframework.util.Assert

                  Comment


                  • #10
                    How to download test source code

                    Hi,

                    Could any body let me know how can I get the Acegi test source code? I don't think they are in either acegi-security-1.0.0-RC2.zip or acegi-security-1.0.0-RC2-src.zip.

                    Comment


                    • #11
                      Look in CVS

                      AFAIK, the test code is only in CVS, which you can browse anonymously here:

                      http://cvs.sourceforge.net/viewcvs.p...ttic=0#dirlist

                      Comment


                      • #12
                        Thanks!

                        Originally posted by adeveloper
                        It is org.springframework.util.Assert
                        Thanks, that was the missing piece!

                        Comment


                        • #13
                          You can also browse the source on the web site

                          http://acegisecurity.org/multiprojec...n-reports.html

                          The maven reports are updated by automated builds on a twice-daily basis.

                          Comment


                          • #14
                            Does that include the tests?

                            Originally posted by Luke
                            You can also browse the source on the web site

                            http://acegisecurity.org/multiprojec...n-reports.html
                            We were looking for the source for the Acegi unit tests. That link you gave only seems to show the release code, e.g. I couldn't find org.acegisecurity.intercept.web.FilterSecurityInte rceptorTests.java

                            Comment


                            • #15
                              Problem Solved

                              I did what Ben suggested, and looked at Acegi's unit tests to see how he tests FilterSecurityInterceptor. I tried the same approach in my own test case, but it all got too hairy, e.g. I had to create and set up multiple mocks and ended up testing a whole lot of things that I didn't care about. (For those who haven't read this whole thread, my original aim was simply to test the URL wildcard patterns in my config file).

                              I have since discovered a neater approach, namely to locate the PathBasedFilterInvocationDefinitionMap that I've set up in my Spring config, pass it a given URL, and see what authority it says is required. Here's the test code that does this (I've omitted boring stuff like imports, annotations, and comments for brevity):
                              Code:
                              public class UrlAuthorisationTest
                                extends AbstractDependencyInjectionSpringContextTests
                              {
                                private static final String[] CONFIG_FILES = {
                                  // Whatever config files are used to set up your FilterSecurityInterceptor
                                };
                                
                                private PathBasedFilterInvocationDefinitionMap map;
                                
                                protected String[] getConfigLocations() {
                                  return CONFIG_FILES;
                                }
                                
                                /**
                                 * The Spring superclass calls this setter to inject our filter security
                                 * interceptor
                                 * 
                                 * @param interceptor The interceptor to set.
                                 */
                                public void setInterceptor(FilterSecurityInterceptor interceptor) {
                                  map = (PathBasedFilterInvocationDefinitionMap)
                                      interceptor.getObjectDefinitionSource();
                                }
                                
                                /**
                                 * Helper method checking that the given URL requires the given authority
                                 * 
                                 * @param url the URL whose required authorisation is being checked; this URL
                                 *   can't be blank
                                 * @param authority pass <code>null</code> if the URL isn't expected to
                                 *   require any authority
                                 */
                                protected void testUrlRequiresAuthority(String url, String authority) {
                                  if (StringUtils.isBlank(url)) {
                                    throw new IllegalArgumentException("Invalid URL '" + url + "'");
                                  }
                                  
                                  // Ask our filter's map what authority this URL requires
                                  ConfigAttributeDefinition configAttributeDefinition =
                                    map.lookupAttributes(url);
                                  
                                  // Check the results are what was expected
                                  if (authority == null) {
                                    assertNull("No role should be required for the url '" + url + "'",
                                        configAttributeDefinition);
                                  }
                                  else {
                                    assertNotNull("The url '" + url + "' should require authorisation",
                                        configAttributeDefinition);
                                    logger.debug("configAttributeDefinition: " + configAttributeDefinition);
                                    assertTrue(
                                        "The url '" + url + "' should require the " + authority + " authority",
                                        configAttributeDefinition.contains(new SecurityConfig(authority)));
                                  }
                                }
                              
                                // ----- Test methods for my app's URLs (the stuff above is generic) -----
                                
                                public void testAboutUrl() {
                                  // This URL should be unsecured (requires no authority)
                                  testUrlRequiresAuthority("/about.htm", null);
                                }
                                
                                public void testEventDetailsUrl() {
                                  // This URL should be secured (requires the AUTHORITY_USER authority)
                                  testUrlRequiresAuthority("/event_details.htm", "AUTHORITY_USER");
                                }
                                
                                public void testOrganisationDetailsUrl() {
                                  // This URL should be secured (requires the AUTHORITY_USER authority)
                                  testUrlRequiresAuthority("/org_details.htm", "AUTHORITY_USER");
                                }
                                
                                public void testPersonDetailsUrl() {
                                  // This URL should be secured (requires the AUTHORITY_USER authority)
                                  testUrlRequiresAuthority("/person_details.htm", "AUTHORITY_USER");
                                }
                                
                                public void testWelcomeUrl() {
                                  // This URL should be unsecured (requires no authority)
                                  testUrlRequiresAuthority("/welcome.htm", null);
                                }
                              }
                              HTH someone,

                              Andrew
                              Last edited by Andrew Swan; Apr 17th, 2006, 11:31 PM.

                              Comment

                              Working...
                              X