summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Gemmell <robbie@apache.org>2012-12-04 19:59:36 +0000
committerRobert Gemmell <robbie@apache.org>2012-12-04 19:59:36 +0000
commitf223632243ebe462ec9403fe4b04d7ef0618a3cb (patch)
treee0a1001ce0bdaf932c990d674b1588c193fd3a20
parentcdf3a3ed338af87bd218683d54643f9ee2d0c5a7 (diff)
downloadqpid-python-f223632243ebe462ec9403fe4b04d7ef0618a3cb.tar.gz
QPID-4483: Add system tests for Sasl authentication in Java Broker web management console
merged from trunk r1415148 git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/0.20@1417140 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/SaslRestTest.java390
1 files changed, 389 insertions, 1 deletions
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/SaslRestTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/SaslRestTest.java
index d321bde2b2..a775af5dcf 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/SaslRestTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/SaslRestTest.java
@@ -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 QpidRestTestCase
{
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");
+ }
}