/* * 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.test.utils.TestSSLConstants.TRUSTSTORE; import static org.apache.qpid.test.utils.TestSSLConstants.TRUSTSTORE_PASSWORD; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLDecoder; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManagerFactory; import javax.servlet.http.HttpServletResponse; import junit.framework.Assert; import org.apache.commons.codec.binary.Base64; import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; import org.apache.qpid.server.security.auth.manager.AbstractPrincipalDatabaseAuthManagerFactory; import org.apache.qpid.ssl.SSLContextFactory; import org.apache.qpid.test.utils.QpidBrokerTestCase; import org.apache.qpid.test.utils.TestBrokerConfiguration; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.JsonParseException; import org.codehaus.jackson.map.JsonMappingException; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.type.TypeReference; public class RestTestHelper { private static final Logger LOGGER = Logger.getLogger(RestTestHelper.class); private int _httpPort; private boolean _useSsl; private String _username; private String _password; private File _passwdFile; public RestTestHelper(int httpPort) { _httpPort = httpPort; } public int getHttpPort() { return _httpPort; } private String getHostName() { return "localhost"; } private String getProtocol() { return _useSsl ? "https" : "http"; } public String getManagementURL() { return getProtocol() + "://" + getHostName() + ":" + getHttpPort(); } public URL getManagementURL(String path) throws MalformedURLException { return new URL(getManagementURL() + path); } public HttpURLConnection openManagementConnection(String path, String method) throws IOException { URL url = getManagementURL(path); HttpURLConnection httpCon = (HttpURLConnection) url.openConnection(); if(_useSsl) { try { // We have to use a SSLSocketFactory from a new SSLContext so that we don't re-use // the JVM's defaults that may have been initialised in previous tests. SSLContext sslContext = SSLContextFactory.buildClientContext( TRUSTSTORE, TRUSTSTORE_PASSWORD, KeyStore.getDefaultType(), TrustManagerFactory.getDefaultAlgorithm(), null, null, null, null, null); SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); ((HttpsURLConnection) httpCon).setSSLSocketFactory(sslSocketFactory); } catch (GeneralSecurityException e) { throw new RuntimeException(e); } } if(_username != null) { String encoded = new String(new Base64().encode((_username + ":" + _password).getBytes())); httpCon.setRequestProperty("Authorization", "Basic " + encoded); } httpCon.setDoOutput(true); httpCon.setRequestMethod(method); return httpCon; } public List> readJsonResponseAsList(HttpURLConnection connection) throws IOException, JsonParseException, JsonMappingException { byte[] data = readConnectionInputStream(connection); ObjectMapper mapper = new ObjectMapper(); TypeReference>> typeReference = new TypeReference>>() { }; List> providedObject = mapper.readValue(new ByteArrayInputStream(data), typeReference); return providedObject; } public Map readJsonResponseAsMap(HttpURLConnection connection) throws IOException, JsonParseException, JsonMappingException { byte[] data = readConnectionInputStream(connection); ObjectMapper mapper = new ObjectMapper(); TypeReference> typeReference = new TypeReference>() { }; Map providedObject = mapper.readValue(new ByteArrayInputStream(data), typeReference); return providedObject; } public byte[] readConnectionInputStream(HttpURLConnection connection) throws IOException { InputStream is = connection.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = -1; while ((len = is.read(buffer)) != -1) { baos.write(buffer, 0, len); } if (LOGGER.isTraceEnabled()) { LOGGER.trace("RESPONSE:" + new String(baos.toByteArray())); } return baos.toByteArray(); } public void writeJsonRequest(HttpURLConnection connection, Map data) throws JsonGenerationException, JsonMappingException, IOException { ObjectMapper mapper = new ObjectMapper(); mapper.writeValue(connection.getOutputStream(), data); } public Map find(String name, Object value, List> data) { for (Map map : data) { Object mapValue = map.get(name); if (value.equals(mapValue)) { return map; } } return null; } public Map find(Map searchAttributes, List> data) { for (Map map : data) { boolean equals = true; for (Map.Entry entry : searchAttributes.entrySet()) { Object mapValue = map.get(entry.getKey()); if (!entry.getValue().equals(mapValue)) { equals = false; break; } } if (equals) { return map; } } return null; } public Map getJsonAsSingletonList(String path) throws IOException { List> response = getJsonAsList(path); Assert.assertNotNull("Response cannot be null", response); Assert.assertEquals("Unexpected response", 1, response.size()); return response.get(0); } public List> getJsonAsList(String path) throws IOException, JsonParseException, JsonMappingException { HttpURLConnection connection = openManagementConnection(path, "GET"); connection.connect(); List> response = readJsonResponseAsList(connection); return response; } public Map getJsonAsMap(String path) throws IOException { HttpURLConnection connection = openManagementConnection(path, "GET"); connection.connect(); Map response = readJsonResponseAsMap(connection); return response; } public void createNewGroupMember(String groupProviderName, String groupName, String memberName, int responseCode) throws IOException { HttpURLConnection connection = openManagementConnection( "/rest/groupmember/" + URLDecoder.decode(groupProviderName, "UTF-8") + "/"+ URLDecoder.decode(groupName, "UTF-8") + "/" + URLDecoder.decode(memberName, "UTF-8"), "PUT"); Map groupMemberData = new HashMap(); // TODO add type writeJsonRequest(connection, groupMemberData); Assert.assertEquals("Unexpected response code", responseCode, connection.getResponseCode()); connection.disconnect(); } public void createNewGroupMember(String groupProviderName, String groupName, String memberName) throws IOException { createNewGroupMember(groupProviderName, groupName, memberName, HttpServletResponse.SC_CREATED); } public void removeMemberFromGroup(String groupProviderName, String groupName, String memberName, int responseCode) throws IOException { HttpURLConnection connection = openManagementConnection( "/rest/groupmember/" + URLDecoder.decode(groupProviderName, "UTF-8") + "/"+ URLDecoder.decode(groupName, "UTF-8") + "/" + URLDecoder.decode(memberName, "UTF-8"), "DELETE"); Assert.assertEquals("Unexpected response code", responseCode, connection.getResponseCode()); connection.disconnect(); } public void removeMemberFromGroup(String groupProviderName, String groupName, String memberName) throws IOException { removeMemberFromGroup(groupProviderName, groupName, memberName, HttpServletResponse.SC_OK); } public void assertNumberOfGroupMembers(Map data, int expectedNumberOfGroupMembers) { @SuppressWarnings("unchecked") List> groups = (List>) data.get("groupmembers"); if (groups == null) { groups = Collections.emptyList(); } Assert.assertEquals("Unexpected number of group members", expectedNumberOfGroupMembers, groups.size()); } public void createGroup(String groupName, String groupProviderName) throws IOException { createGroup(groupName, groupProviderName, HttpServletResponse.SC_CREATED); } public void createGroup(String groupName, String groupProviderName, int responseCode) throws IOException { HttpURLConnection connection = openManagementConnection( "/rest/group/" + URLDecoder.decode(groupProviderName, "UTF-8") + "/"+ URLDecoder.decode(groupName, "UTF-8"), "PUT"); Map groupData = new HashMap(); writeJsonRequest(connection, groupData); Assert.assertEquals("Unexpected response code", responseCode, connection.getResponseCode()); connection.disconnect(); } public void createOrUpdateUser(String username, String password) throws IOException { createOrUpdateUser(username, password, HttpServletResponse.SC_CREATED); } public void createOrUpdateUser(String username, String password, int responseCode) throws IOException { HttpURLConnection connection = openManagementConnection("/rest/user/" + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/" + username, "PUT"); Map data = new HashMap(); data.put("password", password); writeJsonRequest(connection, data); Assert.assertEquals("Unexpected response code", responseCode, connection.getResponseCode()); connection.disconnect(); } public void removeGroup(String groupName, String groupProviderName, int responseCode) throws IOException { HttpURLConnection connection = openManagementConnection( "/rest/group/" + URLDecoder.decode(groupProviderName, "UTF-8") + "/"+ URLDecoder.decode(groupName, "UTF-8"), "DELETE"); Assert.assertEquals("Unexpected response code", responseCode, connection.getResponseCode()); connection.disconnect(); } public void removeGroup(String groupName, String groupProviderName) throws IOException { removeGroup(groupName, groupProviderName, HttpServletResponse.SC_OK); } public void removeUserById(String id) throws IOException { HttpURLConnection connection = openManagementConnection("/rest/user/" + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "?id=" + id, "DELETE"); Assert.assertEquals("Unexpected response code", HttpServletResponse.SC_OK, connection.getResponseCode()); connection.disconnect(); } public void removeUser(String username, int responseCode) throws IOException { HttpURLConnection connection = openManagementConnection("/rest/user/" + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER + "/" + username, "DELETE"); Assert.assertEquals("Unexpected response code", responseCode, connection.getResponseCode()); connection.disconnect(); } public void removeUser(String username) throws IOException { removeUser(username, HttpServletResponse.SC_OK); } public void assertNumberOfGroups(Map data, int expectedNumberOfGroups) { @SuppressWarnings("unchecked") List> groups = (List>) data.get("groups"); if (groups == null) { groups = Collections.emptyList(); } Assert.assertEquals("Unexpected number of groups", expectedNumberOfGroups, groups.size()); } public void setUseSsl(boolean useSsl) { _useSsl = useSsl; } public void setUsernameAndPassword(String username, String password) { _username = username; _password = password; } /** * Create password file that follows the convention username=password, which is deleted by {@link #tearDown()} */ public void configureTemporaryPasswordFile(QpidBrokerTestCase testCase, String... users) throws ConfigurationException, IOException { _passwdFile = createTemporaryPasswdFile(users); testCase.getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER, AbstractPrincipalDatabaseAuthManagerFactory.ATTRIBUTE_PATH, _passwdFile.getAbsolutePath()); } public void tearDown() { if (_passwdFile != null) { if (_passwdFile.exists()) { _passwdFile.delete(); } } } private File createTemporaryPasswdFile(String[] users) throws IOException { BufferedWriter writer = null; try { File testFile = File.createTempFile(this.getClass().getName(),"tmp"); testFile.deleteOnExit(); writer = new BufferedWriter(new FileWriter(testFile)); for (int i = 0; i < users.length; i++) { String username = users[i]; writer.write(username + ":" + username); writer.newLine(); } return testFile; } finally { if (writer != null) { writer.close(); } } } }