Version 8

    Background

    I wanted to write a test case for web security using FORM authentication in the AS7 testsuite. I chose the testsuite/integration submodule.

    Requirements

     

    • I wanted to be able to run the test case in Eclipse.
    • I wanted to use JPDA to set breakpoints in the AS7 codebase.

     

    Choices of Technologies

    • Arquilian
    • Shrinkwrap
    • JUnit
    • Eclipse

     

    Note:  Both Arquilian and Shrinkwrap are available in the AS7 development environment by default.

     

    Test Case

     

     

    /*
     * JBoss, Home of Professional Open Source.
     * Copyright (c) 2011, Red Hat, Inc., and individual contributors
    <snip> Copyright header
     */
    package org.jboss.as.testsuite.integration.websecurity;
    
    import static org.junit.Assert.assertEquals;
    
    import java.io.File;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.List;
    
    import org.apache.http.Header;
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpResponse;
    import org.apache.http.NameValuePair;
    import org.apache.http.StatusLine;
    import org.apache.http.client.entity.UrlEncodedFormEntity;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.cookie.Cookie;
    import org.apache.http.impl.client.DefaultHttpClient;
    import org.apache.http.message.BasicNameValuePair;
    import org.apache.http.protocol.HTTP;
    import org.jboss.arquillian.api.Deployment;
    import org.jboss.arquillian.api.Run;
    import org.jboss.arquillian.api.RunModeType;
    import org.jboss.arquillian.junit.Arquillian;
    import org.jboss.shrinkwrap.api.ShrinkWrap;
    import org.jboss.shrinkwrap.api.spec.WebArchive;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    
    /**
     * Unit Test web security
     * 
     * @author Anil Saldhana
     */
    @RunWith(Arquillian.class)
    @Run(RunModeType.AS_CLIENT)
    public class WebSecurityFORMTestCase {
    
        private static final String URL = "http://localhost:8080/web-secure/secured/";
    
        @Deployment
        public static WebArchive deployment() {
            ClassLoader tccl = Thread.currentThread().getContextClassLoader();
            URL webxml = tccl.getResource("web-secure.war/web.xml");
            WebArchive war = ShrinkWrap.create(WebArchive.class, "web-secure.war");
            war.addClass(SecuredServlet.class);
    
            URL userProp = tccl.getResource("web-secure.war/users.properties");
            URL roleProp = tccl.getResource("web-secure.war/roles.properties");
            URL loginJSP = tccl.getResource("web-secure.war/login.jsp");
            URL errorJSP = tccl.getResource("web-secure.war/error.jsp");
    
            war.addResource(loginJSP, "login.jsp");
            war.addResource(errorJSP, "error.jsp");
    
            war.addResource(userProp, "/WEB-INF/classes/users.properties");
            war.addResource(roleProp, "/WEB-INF/classes/roles.properties");
    
            war.setWebXML(webxml);
    
            return war;
        }
    
        /**
         * Test with user "anil" who has the right password and the right role to access the servlet
         * 
         * @throws Exception
         */
        @Test
        public void testSuccessfulAuth() throws Exception {
            makeCall("anil", "anil", 200);
        }
    
        /**
         * <p>
         * Test with user "marcus" who has the right password but does not have the right role
         * </p>
         * <p>
         * Should be a HTTP/403
         * </p>
         * 
         * @throws Exception
         */
        @Test
        public void testUnsuccessfulAuth() throws Exception {
            makeCall("marcus", "marcus", 403);
        }
    
        protected void makeCall(String user, String pass, int expectedStatusCode) throws Exception {
            DefaultHttpClient httpclient = new DefaultHttpClient();
            try {
                HttpGet httpget = new HttpGet(URL);
    
                HttpResponse response = httpclient.execute(httpget);
    
                HttpEntity entity = response.getEntity();
                if (entity != null)
                    entity.consumeContent();
    
                // We should get the Login Page
                StatusLine statusLine = response.getStatusLine();
                System.out.println("Login form get: " + statusLine);
                assertEquals(200, statusLine.getStatusCode());
    
                System.out.println("Initial set of cookies:");
                List<Cookie> cookies = httpclient.getCookieStore().getCookies();
                if (cookies.isEmpty()) {
                    System.out.println("None");
                } else {
                    for (int i = 0; i < cookies.size(); i++) {
                        System.out.println("- " + cookies.get(i).toString());
                    }
                }
    
                // We should now login with the user name and password
                HttpPost httpost = new HttpPost(URL + "/j_security_check");
    
                List<NameValuePair> nvps = new ArrayList<NameValuePair>();
                nvps.add(new BasicNameValuePair("j_username", user));
                nvps.add(new BasicNameValuePair("j_password", pass));
    
                httpost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
    
                response = httpclient.execute(httpost);
                entity = response.getEntity();
                if (entity != null)
                    entity.consumeContent();
    
                statusLine = response.getStatusLine();
    
                // Post authentication - we have a 302
                assertEquals(302, statusLine.getStatusCode());
                Header locationHeader = response.getFirstHeader("Location");
                String location = locationHeader.getValue();
    
                HttpGet httpGet = new HttpGet(location);
                response = httpclient.execute(httpGet);
    
                entity = response.getEntity();
                if (entity != null)
                    entity.consumeContent();
    
                System.out.println("Post logon cookies:");
                cookies = httpclient.getCookieStore().getCookies();
                if (cookies.isEmpty()) {
                    System.out.println("None");
                } else {
                    for (int i = 0; i < cookies.size(); i++) {
                        System.out.println("- " + cookies.get(i).toString());
                    }
                }
    
                // Either the authentication passed or failed based on the expected status code
                statusLine = response.getStatusLine();
                assertEquals(expectedStatusCode, statusLine.getStatusCode());
            } finally {
                // When HttpClient instance is no longer needed,
                // shut down the connection manager to ensure
                // immediate deallocation of all system resources
                httpclient.getConnectionManager().shutdown();
            }
        }
    }
    

     

     

    In this test case, I am specifying that the test case should run using Arquilian  (via @RunWith) annotation.  Plus I am telling Arquilian to run the test as a client test case and not inside AS7.  [via @Run(RunModeType.AS_CLIENT) ]  If you want the test to run inside AS7, then you have to use IN_CONTAINER (which is default anyway).

     

    Also note above. that I am adding users.properties and roles.properties to WEB-INF/classes directory inside the Web Archive.

     

     

    Web Deployment

    I need the following Items in my web archive

    • Servlet
    • web.xml
    • Two properties files (users.properties and roles.properties)

     

     

    Let us look at the servlet

     

     

    /*
     * JBoss, Home of Professional Open Source.
     * Copyright (c) 2011, Red Hat, Inc., and individual contributors
     * as indicated by the @author tags. See the copyright.txt file in the
     * distribution for a full listing of individual contributors.
     * <SNIP>
     */
    package org.jboss.as.testsuite.integration.websecurity;
    
    import java.io.IOException;
    import java.io.Writer;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.HttpConstraint;
    import javax.servlet.annotation.ServletSecurity;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * A simple servlet that just writes back a string
     * 
     * @author Anil Saldhana
     */
    @WebServlet(urlPatterns = { "/secured/" })
    @ServletSecurity(@HttpConstraint(rolesAllowed = { "gooduser" }))
    public class SecuredServlet extends HttpServlet {
    
        private static final long serialVersionUID = 1L;
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
            Writer writer = resp.getWriter();
            writer.write("GOOD");
        }
    }
    

     

    My test resources were in a folder as

     

     

    web-secure.war/

    login.jsp

    error.jsp

    users.properties

    roles.properties

    web.xml

     

    Where should I look for the source code?


    https://github.com/anilsaldhana/jboss-as/commit/0efdc673a45d6d172559183243a99e0927e33e65

     

    Show me the test in action, buddy

     

     

    anil@localhost:~/as7/jboss-as/testsuite/integration$ mvn clean install
    
    Running org.jboss.as.testsuite.integration.websecurity.WebSecurityFORMTestCase
    Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.531 sec
    

     

    Troubleshooting

     

    • How do I run AS7 with JPDA enabled?
      • Uncomment the following line in bin/standalone.conf
      • #JAVA_OPTS="$JAVA_OPTS -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y"
        
    • Any Eclipse settings for the test case?
      • VM arguments in the eclipse config for the test case was
      • -Djboss.home=/home/anil/as7/jboss-as/testsuite/integration/../../build/target/jboss-7.0.0.Beta2
        
    • How do I see what is in the web archive ShrinkWrap creates? You definitely need to add this always.
      • System.out.println(war.toString(true));
        
    • What about when Arquilian is running in a managed mode?
      • Stuart Douglas gave some insight into this.
      • There are two ways, you can either start a standalone instance and attach your 
        debugger to it normally,
        and then run
        
        mvn install -Premote  -Dtest=com.my.Test
        
        Alternatively 
        
        mvn install -Djboss.options="-Xdebug  -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8787"
        
        And then attach. This way will not let you use -Dtest= though,
         so generally when I am debugging I use the remote profile.