| /* |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| package org.apache.qpid.systest.rest; |
| |
| import static org.apache.qpid.server.security.auth.sasl.SaslUtil.generateCramMD5ClientResponse; |
| import static org.apache.qpid.server.security.auth.sasl.SaslUtil.generateCramMD5HashedClientResponse; |
| import static org.apache.qpid.server.security.auth.sasl.SaslUtil.generateCramMD5HexClientResponse; |
| import static org.apache.qpid.server.security.auth.sasl.SaslUtil.generatePlainClientResponse; |
| |
| 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.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.servlet.http.HttpServletResponse; |
| import javax.xml.bind.DatatypeConverter; |
| |
| import org.apache.qpid.server.model.AuthenticationProvider; |
| import org.apache.qpid.server.security.auth.manager.Base64MD5PasswordDatabaseAuthenticationManager; |
| import org.apache.qpid.test.utils.TestBrokerConfiguration; |
| |
| public class SaslRestTest extends QpidRestTestCase |
| { |
| @Override |
| public void startDefaultBroker() |
| { |
| // prevent broker from starting in setUp |
| } |
| |
| public void startBrokerNow() throws Exception |
| { |
| super.startDefaultBroker(); |
| getRestTestHelper().setUsernameAndPassword(null,null); |
| } |
| |
| public void testGetMechanismsWithBrokerPlainPasswordPrincipalDatabase() throws Exception |
| { |
| startBrokerNow(); |
| |
| Map<String, Object> saslData = getRestTestHelper().getJsonAsMap("/service/sasl"); |
| assertNotNull("mechanisms attribute is not found", saslData.get("mechanisms")); |
| |
| @SuppressWarnings("unchecked") |
| List<String> mechanisms = (List<String>) saslData.get("mechanisms"); |
| String[] expectedMechanisms = { "CRAM-MD5" }; |
| for (String mechanism : expectedMechanisms) |
| { |
| assertTrue("Mechanism " + mechanism + " is not found", mechanisms.contains(mechanism)); |
| } |
| assertNull("Unexpected user was returned: " + saslData.get("user"), saslData.get("user")); |
| } |
| |
| public void testGetMechanismsWithBrokerBase64MD5FilePrincipalDatabase() throws Exception |
| { |
| configureBase64MD5FilePrincipalDatabase(); |
| startBrokerNow(); |
| |
| Map<String, Object> saslData = getRestTestHelper().getJsonAsMap("/service/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"), saslData.get("user")); |
| } |
| |
| public void testPlainSaslAuthenticationForValidCredentials() throws Exception |
| { |
| startBrokerNow(); |
| |
| byte[] responseBytes = generatePlainClientResponse("admin", "admin"); |
| String responseData = DatatypeConverter.printBase64Binary(responseBytes); |
| String parameters= "mechanism=PLAIN&response=" + responseData; |
| |
| HttpURLConnection connection = getRestTestHelper().openManagementConnection("/service/sasl", "POST"); |
| OutputStream os = connection.getOutputStream(); |
| os.write(parameters.getBytes()); |
| os.flush(); |
| |
| int code = getRestTestHelper().submitRequest("/service/sasl", "POST", parameters.getBytes()); |
| assertEquals("Unexpected response code", 200, code); |
| |
| List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); |
| |
| // request authenticated user details |
| connection = getRestTestHelper().openManagementConnection("/service/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 = DatatypeConverter.printBase64Binary(responseBytes); |
| String parameters= "mechanism=PLAIN&response=" + responseData; |
| |
| HttpURLConnection connection = getRestTestHelper().openManagementConnection("/service/sasl", "POST"); |
| OutputStream os = connection.getOutputStream(); |
| os.write(parameters.getBytes()); |
| os.flush(); |
| |
| int code = connection.getResponseCode(); |
| assertEquals("Unexpected response code", 401, code); |
| |
| List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); |
| |
| // request authenticated user details |
| connection = getRestTestHelper().openManagementConnection("/service/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 = DatatypeConverter.printBase64Binary(responseBytes); |
| String parameters= "mechanism=PLAIN&response=" + responseData; |
| |
| HttpURLConnection connection = getRestTestHelper().openManagementConnection("/service/sasl", "POST"); |
| OutputStream os = connection.getOutputStream(); |
| os.write(parameters.getBytes()); |
| os.flush(); |
| |
| int code = connection.getResponseCode(); |
| assertEquals("Unexpected response code", 401, code); |
| |
| List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); |
| |
| // request authenticated user details |
| connection = getRestTestHelper().openManagementConnection("/service/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("/service/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", 401, code); |
| |
| // request authenticated user details |
| connection = getRestTestHelper().openManagementConnection("/service/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", 401, code); |
| |
| // request authenticated user details |
| connection = getRestTestHelper().openManagementConnection("/service/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("/service/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", 401, code); |
| |
| // request authenticated user details |
| connection = getRestTestHelper().openManagementConnection("/service/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", 401, code); |
| |
| // request authenticated user details |
| connection = getRestTestHelper().openManagementConnection("/service/sasl", "GET"); |
| applyCookiesToConnection(cookies, connection); |
| Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection); |
| assertNull("Unexpected user", response2.get("user")); |
| } |
| |
| public void testRequestingAuthenticationForUnsupportedSaslMechanism() throws Exception |
| { |
| startBrokerNow(); |
| HttpURLConnection connection = requestSasServerChallenge("UNSUPPORTED"); |
| int responseCode = connection.getResponseCode(); |
| connection.disconnect(); |
| assertEquals("Unexpected response", HttpServletResponse.SC_EXPECTATION_FAILED, responseCode); |
| } |
| |
| public void testCramMD5HashedSaslAuthenticationForValidCredentials() throws Exception |
| { |
| configureBase64MD5FilePrincipalDatabase(); |
| startBrokerNow(); |
| |
| // request the challenge for CRAM-MD5-HASHED |
| HttpURLConnection connection = requestSasServerChallenge("CRAM-MD5-HASHED"); |
| List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); |
| |
| // authenticate user with correct credentials |
| int code = authenticateUser(connection, "admin", "admin", "CRAM-MD5-HASHED"); |
| assertEquals("Unexpected response code", 200, code); |
| |
| // request authenticated user details |
| connection = getRestTestHelper().openManagementConnection("/service/sasl", "GET"); |
| applyCookiesToConnection(cookies, connection); |
| Map<String, Object> response2 = getRestTestHelper().readJsonResponseAsMap(connection); |
| assertEquals("Unexpected user", "admin", response2.get("user")); |
| } |
| |
| public void testCramMD5HashedSaslAuthenticationForInvalidPassword() throws Exception |
| { |
| configureBase64MD5FilePrincipalDatabase(); |
| startBrokerNow(); |
| |
| HttpURLConnection connection = requestSasServerChallenge("CRAM-MD5-HASHED"); |
| List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); |
| |
| // try to authenticate user with incorrect passowrd |
| int code = authenticateUser(connection, "admin", "incorrect", "CRAM-MD5-HASHED"); |
| assertEquals("Unexpected response code", 401, code); |
| |
| // request authenticated user details |
| connection = getRestTestHelper().openManagementConnection("/service/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("/service/sasl", "POST"); |
| OutputStream os = connection.getOutputStream(); |
| os.write(("mechanism=" + mechanism).getBytes()); |
| os.flush(); |
| return connection; |
| } |
| |
| public int authenticateUser(HttpURLConnection requestChallengeConnection, String userName, String userPassword, String mechanism) |
| throws 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 = DatatypeConverter.parseBase64Binary(challenge); |
| byte[] responseBytes = generateClientResponse(mechanism, userName, userPassword, challengeBytes); |
| String responseData = DatatypeConverter.printBase64Binary(responseBytes); |
| String requestParameters = ("id=" + response.get("id") + "&response=" + responseData); |
| |
| // re-open connection |
| HttpURLConnection authenticateConnection = getRestTestHelper().openManagementConnection("/service/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 if ("CRAM-MD5-HASHED".equalsIgnoreCase(mechanism)) |
| { |
| responseBytes = generateCramMD5HashedClientResponse(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 void configureBase64MD5FilePrincipalDatabase() throws IOException |
| { |
| // generate user password entry |
| String passwordFileEntry; |
| try |
| { |
| |
| MessageDigest md = MessageDigest.getInstance("MD5"); |
| |
| md.update("admin".getBytes("utf-8")); |
| |
| passwordFileEntry = "admin" + ":" + DatatypeConverter.printBase64Binary(md.digest()); |
| } |
| catch (NoSuchAlgorithmException e) |
| { |
| throw new RuntimeException(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 |
| Map<String, Object> newAttributes = new HashMap<String, Object>(); |
| newAttributes.put("path", passwordFile.getAbsolutePath()); |
| newAttributes.put(AuthenticationProvider.TYPE, Base64MD5PasswordDatabaseAuthenticationManager.PROVIDER_TYPE); |
| getDefaultBrokerConfiguration().setObjectAttributes(AuthenticationProvider.class, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER, newAttributes); |
| } |
| } |