You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ro...@apache.org on 2012/12/04 20:59:37 UTC

svn commit: r1417140 - in /qpid/branches/0.20/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest: ./ SaslRestTest.java

Author: robbie
Date: Tue Dec  4 19:59:36 2012
New Revision: 1417140

URL: http://svn.apache.org/viewvc?rev=1417140&view=rev
Log:
QPID-4483: Add system tests for Sasl authentication in Java Broker web management console

merged from trunk r1415148

Modified:
    qpid/branches/0.20/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/   (props changed)
    qpid/branches/0.20/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/SaslRestTest.java

Propchange: qpid/branches/0.20/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/
------------------------------------------------------------------------------
--- svn:mergeinfo (added)
+++ svn:mergeinfo Tue Dec  4 19:59:36 2012
@@ -0,0 +1,7 @@
+/qpid/branches/0.5.x-dev/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest:886720-886722,887145,892761,894875,916304,916325,930288,931179
+/qpid/branches/0.5.x-dev/systests/src/main/java/org/apache/qpid/systest/rest:886720-886722
+/qpid/branches/java-broker-0-10/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest:795950-829653
+/qpid/branches/java-network-refactor/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest:805429-821809
+/qpid/branches/qpid-2935/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest:1061302-1072333
+/qpid/trunk/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest:1412359,1413363-1413364,1413376,1413539,1413549,1413567,1413676,1415127,1415148
+/qpid/trunk/qpid/systests/src/main/java/org/apache/qpid/systest/rest:796646-796653

Modified: qpid/branches/0.20/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/SaslRestTest.java
URL: http://svn.apache.org/viewvc/qpid/branches/0.20/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/SaslRestTest.java?rev=1417140&r1=1417139&r2=1417140&view=diff
==============================================================================
--- qpid/branches/0.20/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/SaslRestTest.java (original)
+++ qpid/branches/0.20/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/SaslRestTest.java Tue Dec  4 19:59:36 2012
@@ -20,13 +20,42 @@
  */
 package org.apache.qpid.systest.rest;
 
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.util.List;
 import java.util.Map;
 
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.qpid.tools.security.Passwd;
+import org.codehaus.jackson.JsonParseException;
+import org.codehaus.jackson.map.JsonMappingException;
+
 public class SaslRestTest extends QpidRestTestCase
 {
-    public void testGet() throws Exception
+    @Override
+    public void startBroker()
+    {
+        // prevent broker from starting in setUp
+    }
+
+    public void startBrokerNow() throws Exception
     {
+        super.startBroker();
+    }
+
+    public void testGetMechanismsWithBrokerPlainPasswordPrincipalDatabase() throws Exception
+    {
+        startBrokerNow();
+
         Map<String, Object> saslData = getRestTestHelper().getJsonAsMap("/rest/sasl");
         assertNotNull("mechanisms attribute is not found", saslData.get("mechanisms"));
 
@@ -37,6 +66,365 @@ public class SaslRestTest extends QpidRe
         {
             assertTrue("Mechanism " + mechanism + " is not found", mechanisms.contains(mechanism));
         }
+        assertNull("Unexpected user was returned", saslData.get("user"));
+    }
+
+    public void testGetMechanismsWithBrokerBase64MD5FilePrincipalDatabase() throws Exception
+    {
+        configureBase64MD5FilePrincipalDatabase();
+        startBrokerNow();
+
+        Map<String, Object> saslData = getRestTestHelper().getJsonAsMap("/rest/sasl");
+        assertNotNull("mechanisms attribute is not found", saslData.get("mechanisms"));
+
+        @SuppressWarnings("unchecked")
+        List<String> mechanisms = (List<String>) saslData.get("mechanisms");
+        String[] expectedMechanisms = { "CRAM-MD5-HEX", "CRAM-MD5-HASHED" };
+        for (String mechanism : expectedMechanisms)
+        {
+            assertTrue("Mechanism " + mechanism + " is not found", mechanisms.contains(mechanism));
+        }
+        assertNull("Unexpected user was returned", saslData.get("user"));
+    }
+
+    public void testPlainSaslAuthenticationForValidCredentials() throws Exception
+    {
+        startBrokerNow();
+
+        byte[] responseBytes = generatePlainClientResponse("admin", "admin");
+        String responseData = Base64.encodeBase64String(responseBytes);
+        String parameters= "mechanism=PLAIN&response=" + responseData;
+
+        HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/sasl", "POST");
+        OutputStream os = connection.getOutputStream();
+        os.write(parameters.getBytes());
+        os.flush();
+
+         int code = connection.getResponseCode();
+        assertEquals("Unexpected response code", 200, code);
+
+        List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
+
+        // request authenticated user details
+        connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET");
+        applyCookiesToConnection(cookies, connection);
+        Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection);
+        assertEquals("Unexpected user", "admin", response2.get("user"));
+    }
+
+    public void testPlainSaslAuthenticationForIncorrectPassword() throws Exception
+    {
+        startBrokerNow();
+
+        byte[] responseBytes = generatePlainClientResponse("admin", "incorrect");
+        String responseData = Base64.encodeBase64String(responseBytes);
+        String parameters= "mechanism=PLAIN&response=" + responseData;
+
+        HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/sasl", "POST");
+        OutputStream os = connection.getOutputStream();
+        os.write(parameters.getBytes());
+        os.flush();
+
+        int code = connection.getResponseCode();
+        assertEquals("Unexpected response code", 403, code);
+
+        List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
+
+        // request authenticated user details
+        connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET");
+        applyCookiesToConnection(cookies, connection);
+        Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection);
+        assertNull("Unexpected user", response2.get("user"));
+    }
+
+    public void testPlainSaslAuthenticationForNonExistingUser() throws Exception
+    {
+        startBrokerNow();
+
+        byte[] responseBytes = generatePlainClientResponse("nonexisting", "admin");
+        String responseData = Base64.encodeBase64String(responseBytes);
+        String parameters= "mechanism=PLAIN&response=" + responseData;
+
+        HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/sasl", "POST");
+        OutputStream os = connection.getOutputStream();
+        os.write(parameters.getBytes());
+        os.flush();
+
+        int code = connection.getResponseCode();
+        assertEquals("Unexpected response code", 403, code);
+
+        List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
+
+        // request authenticated user details
+        connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET");
+        applyCookiesToConnection(cookies, connection);
+        Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection);
+        assertNull("Unexpected user", response2.get("user"));
+    }
+
+    public void testCramMD5SaslAuthenticationForValidCredentials() throws Exception
+    {
+        startBrokerNow();
+
+        // request the challenge for CRAM-MD5
+        HttpURLConnection connection = requestSasServerChallenge("CRAM-MD5");
+        List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
+
+        // authenticate user with correct credentials
+        int code = authenticateUser(connection, "admin", "admin", "CRAM-MD5");
+        assertEquals("Unexpected response code", 200, code);
+
+        // request authenticated user details
+        connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET");
+        applyCookiesToConnection(cookies, connection);
+        Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection);
+        assertEquals("Unexpected user", "admin", response2.get("user"));
+    }
+
+    public void testCramMD5SaslAuthenticationForIncorrectPassword() throws Exception
+    {
+        startBrokerNow();
+
+        // request the challenge for CRAM-MD5
+        HttpURLConnection connection = requestSasServerChallenge("CRAM-MD5");
+        List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
+
+        // authenticate user with correct credentials
+        int code = authenticateUser(connection, "admin", "incorrect", "CRAM-MD5");
+        assertEquals("Unexpected response code", 403, code);
+
+        // request authenticated user details
+        connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET");
+        applyCookiesToConnection(cookies, connection);
+        Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection);
+        assertNull("Unexpected user", response2.get("user"));
+    }
+
+    public void testCramMD5SaslAuthenticationForNonExistingUser() throws Exception
+    {
+        startBrokerNow();
+
+        // request the challenge for CRAM-MD5
+        HttpURLConnection connection = requestSasServerChallenge("CRAM-MD5");
+        List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
+
+        // authenticate user with correct credentials
+        int code = authenticateUser(connection, "nonexisting", "admin", "CRAM-MD5");
+        assertEquals("Unexpected response code", 403, code);
+
+        // request authenticated user details
+        connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET");
+        applyCookiesToConnection(cookies, connection);
+        Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection);
+        assertNull("Unexpected user",  response2.get("user"));
+    }
+
+    public void testCramMD5HexSaslAuthenticationForValidCredentials() throws Exception
+    {
+        configureBase64MD5FilePrincipalDatabase();
+        startBrokerNow();
+
+        // request the challenge for CRAM-MD5-HEX
+        HttpURLConnection connection = requestSasServerChallenge("CRAM-MD5-HEX");
+        List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
+
+        // authenticate user with correct credentials
+        int code = authenticateUser(connection, "admin", "admin", "CRAM-MD5-HEX");
+        assertEquals("Unexpected response code", 200, code);
+
+        // request authenticated user details
+        connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET");
+        applyCookiesToConnection(cookies, connection);
+        Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection);
+        assertEquals("Unexpected user", "admin", response2.get("user"));
+    }
+
+    public void testCramMD5HexSaslAuthenticationForIncorrectPassword() throws Exception
+    {
+        configureBase64MD5FilePrincipalDatabase();
+        startBrokerNow();
+
+        HttpURLConnection connection = requestSasServerChallenge("CRAM-MD5-HEX");
+        List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
+
+        // try to authenticate user with incorrect passowrd
+        int code = authenticateUser(connection, "admin", "incorrect", "CRAM-MD5-HEX");
+        assertEquals("Unexpected response code", 403, code);
+
+        // request authenticated user details
+        connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET");
+        applyCookiesToConnection(cookies, connection);
+        Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection);
+        assertNull("Unexpected user", response2.get("user"));
+    }
+
+    public void testCramMD5HexSaslAuthenticationForNonExistingUser() throws Exception
+    {
+        configureBase64MD5FilePrincipalDatabase();
+        startBrokerNow();
+
+        HttpURLConnection connection = requestSasServerChallenge("CRAM-MD5-HEX");
+        List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
+
+        // try to authenticate non-existing user
+        int code = authenticateUser(connection, "nonexisting", "admin", "CRAM-MD5-HEX");
+        assertEquals("Unexpected response code", 403, code);
+
+        // request authenticated user details
+        connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET");
+        applyCookiesToConnection(cookies, connection);
+        Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection);
+        assertNull("Unexpected user", response2.get("user"));
+    }
+
+    private HttpURLConnection requestSasServerChallenge(String mechanism) throws IOException
+    {
+        HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/sasl", "POST");
+        OutputStream os = connection.getOutputStream();
+        os.write(("mechanism=" + mechanism).getBytes());
+        os.flush();
+        return connection;
+    }
+
+    private int authenticateUser(HttpURLConnection requestChallengeConnection, String userName, String userPassword, String mechanism)
+            throws IOException, JsonParseException, JsonMappingException, Exception
+    {
+        // get the response
+        Map<String, Object> response = getRestTestHelper().readJsonResponseAsMap(requestChallengeConnection);
+        String challenge = (String) response.get("challenge");
+        assertNotNull("Challenge is not found", challenge);
+
+        // preserve cookies to have the same server session
+        List<String> cookies = requestChallengeConnection.getHeaderFields().get("Set-Cookie");
+
+        // generate the authentication response for the challenge received
+        byte[] challengeBytes = Base64.decodeBase64(challenge);
+        byte[] responseBytes = generateClientResponse(mechanism, userName, userPassword, challengeBytes);
+        String responseData = Base64.encodeBase64String(responseBytes);
+        String requestParameters = ("id=" + response.get("id") + "&response=" + responseData);
+
+        // re-open connection
+        HttpURLConnection authenticateConnection = getRestTestHelper().openManagementConnection("/rest/sasl", "POST");
+
+        // set cookies to use the same server session
+        applyCookiesToConnection(cookies, authenticateConnection);
+        OutputStream os = authenticateConnection.getOutputStream();
+        os.write(requestParameters.getBytes());
+        os.flush();
+        return authenticateConnection.getResponseCode();
+    }
+
+    private byte[] generateClientResponse(String mechanism, String userName, String userPassword, byte[] challengeBytes) throws Exception
+    {
+        byte[] responseBytes =  null;
+        if ("CRAM-MD5-HEX".equalsIgnoreCase(mechanism))
+        {
+            responseBytes = generateCramMD5HexClientResponse(userName, userPassword, challengeBytes);
+        }
+        else if ("CRAM-MD5".equalsIgnoreCase(mechanism))
+        {
+            responseBytes = generateCramMD5ClientResponse(userName, userPassword, challengeBytes);
+        }
+        else
+        {
+            throw new RuntimeException("Not implemented test mechanism " + mechanism);
+        }
+        return responseBytes;
     }
 
+    private void applyCookiesToConnection(List<String> cookies, HttpURLConnection connection)
+    {
+        for (String cookie : cookies)
+        {
+            connection.addRequestProperty("Cookie", cookie.split(";", 2)[0]);
+        }
+    }
+
+    private static byte SEPARATOR = 0;
+
+    private byte[] generatePlainClientResponse(String userName, String userPassword) throws Exception
+    {
+        byte[] password = userPassword.getBytes("UTF8");
+        byte user[] = userName.getBytes("UTF8");
+        byte response[] = new byte[password.length + user.length + 2 ];
+        int size = 0;
+        response[size++] = SEPARATOR;
+        System.arraycopy(user, 0, response, size, user.length);
+        size += user.length;
+        response[size++] = SEPARATOR;
+        System.arraycopy(password, 0, response, size, password.length);
+        return response;
+    }
+
+    private byte[] generateCramMD5HexClientResponse(String userName, String userPassword, byte[] challengeBytes) throws Exception
+    {
+        String macAlgorithm = "HmacMD5";
+        byte[] digestedPasswordBytes = MessageDigest.getInstance("MD5").digest(userPassword.getBytes("UTF-8"));
+        byte[] hexEncodedDigestedPasswordBytes = toHex(digestedPasswordBytes).getBytes("UTF-8");
+        Mac mac = Mac.getInstance(macAlgorithm);
+        mac.init(new SecretKeySpec(hexEncodedDigestedPasswordBytes, macAlgorithm));
+        final byte[] messageAuthenticationCode = mac.doFinal(challengeBytes);
+        String responseAsString = userName + " " + toHex(messageAuthenticationCode);
+        return responseAsString.getBytes();
+    }
+
+    private byte[] generateCramMD5ClientResponse(String userName, String userPassword, byte[] challengeBytes) throws Exception
+    {
+        String macAlgorithm = "HmacMD5";
+        Mac mac = Mac.getInstance(macAlgorithm);
+        mac.init(new SecretKeySpec(userPassword.getBytes("UTF-8"), macAlgorithm));
+        final byte[] messageAuthenticationCode = mac.doFinal(challengeBytes);
+        String responseAsString = userName + " " + toHex(messageAuthenticationCode);
+        return responseAsString.getBytes();
+    }
+
+    private String toHex(byte[] data)
+    {
+        StringBuffer hash = new StringBuffer();
+        for (int i = 0; i < data.length; i++)
+        {
+            String hex = Integer.toHexString(0xFF & data[i]);
+            if (hex.length() == 1)
+            {
+                hash.append('0');
+            }
+            hash.append(hex);
+        }
+        return hash.toString();
+    }
+
+    private void configureBase64MD5FilePrincipalDatabase() throws IOException, ConfigurationException
+    {
+        // generate user password entry
+        String passwordFileEntry;
+        try
+        {
+            passwordFileEntry = new Passwd().getOutput("admin", "admin");
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            throw new ConfigurationException(e);
+        }
+
+        // store the entry in the file
+        File passwordFile = File.createTempFile("passwd", "pwd");
+        passwordFile.deleteOnExit();
+
+        FileWriter writer = null;
+        try
+        {
+            writer = new FileWriter(passwordFile);
+            writer.write(passwordFileEntry);
+        }
+        finally
+        {
+            writer.close();
+        }
+
+        // configure broker to use Base64MD5PasswordFilePrincipalDatabase
+        setConfigurationProperty("security.pd-auth-manager.principal-database.class",
+                "org.apache.qpid.server.security.auth.database.Base64MD5PasswordFilePrincipalDatabase");
+        setConfigurationProperty("security.pd-auth-manager.principal-database.attributes.attribute.value", passwordFile.getAbsolutePath());
+        setConfigurationProperty("management.http.sasl-auth", "true");
+    }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org