/*******************************************************************************
* Copyright (c) 2015 IBM Corp.
*
* Licensed 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 com.ibm.ws.repository.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.Authenticator;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import com.ibm.ws.repository.connections.RepositoryConnectionList;
import com.ibm.ws.repository.connections.RestRepositoryConnection;
import com.ibm.ws.repository.connections.RestRepositoryConnectionProxy;
import com.ibm.ws.repository.exceptions.RepositoryBackendException;
import com.ibm.ws.repository.exceptions.RepositoryException;
import com.ibm.ws.repository.exceptions.RepositoryResourceException;
import com.ibm.ws.repository.resources.RepositoryResource;
import com.ibm.ws.repository.resources.internal.ProductResourceImpl;
import com.ibm.ws.repository.strategies.writeable.AddNewStrategy;
import com.ibm.ws.repository.transport.exceptions.RequestFailureException;
/**
* Junit tests(s) for proxy support.
*
* These tests need to run in order as once a proxy session is established it does not query
* the Authenticator for new credentials as there is already a session i.e. if you pass in correct
* credentials then incorrect ones the second test case will succeed.
*
* To work round this there is one Junit test below that calls all the tests as just normal method
* calls rather than Junit tests.
*/
// TODO Following annotation is not available until we go to Junit 4.11. See comment in testAllProxyTests().
// When we do this test can be restructured to use multiple Junit tests rather than running under one.
// @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Ignore
// TODO: make this test work!
public class ProxyTest {
final static String PROXY_AUTH_PORT = "3128"; // squid listens on this port for an authenticated proxy
final static String PROXY_NOAUTH_PORT = "3129"; // squid listens on this port for an unauthenticated proxy
static boolean _verbose = false;
RestRepositoryConnection _proxyLogon = null; // copy of the LoginInfoEntry
RestRepositoryConnectionProxy proxyAuth = null; // proxy object to represent the authenticated proxy
RestRepositoryConnectionProxy proxyNoAuth = null; // proxy object to represent the unauthenticated proxy
private static final Logger logger = Logger.getLogger(ProxyTest.class.getName());
private final RestRepositoryConnection _restConnection = null;
/**
* Constructor
*/
public ProxyTest() throws FileNotFoundException, IOException {}
/**
* Before class only used at the moment to turn verbose output on or off
*
* @throws Exception
*/
@BeforeClass
public static void setupProxy() throws Exception {
ProxyTest.setVerbose(true);
}
/**
* Create the proxy objects co-located on the massive server we are running on. Do this in a before
* rather than a before class to cope with the fail-over code.
*
* @throws MalformedURLException
* @throws RepositoryException
*/
@Before
public void before() throws MalformedURLException, RepositoryException {
URL url = new URL(_restConnection.getRepositoryUrl());
proxyAuth = new RestRepositoryConnectionProxy(new URL(url.getProtocol() + "://" + url.getHost() + ":" + PROXY_AUTH_PORT));
proxyNoAuth = new RestRepositoryConnectionProxy(new URL(url.getProtocol() + "://" + url.getHost() + ":" + PROXY_NOAUTH_PORT));
_proxyLogon = new RestRepositoryConnection(_restConnection.getUserId(), _restConnection.getPassword(), _restConnection.getApiKey(), _restConnection.getRepositoryUrl(), _restConnection.getSoftlayerUserId(), _restConnection.getSoftlayerPassword(), _restConnection.getAttachmentBasicAuthUserId(), _restConnection.getAttachmentBasicAuthPassword());
_proxyLogon.setUserAgent(_restConnection.getUserAgent());
RestRepositoryConnectionProxy oldProxy = _proxyLogon.getProxy();
if (oldProxy != null) {
_proxyLogon.setProxy(new RestRepositoryConnectionProxy(oldProxy.getProxyURL()));
}
loadResources();
if (_verbose) {
logger.log(Level.INFO, "@Before Running on " + _proxyLogon.getRepositoryUrl());
logger.log(Level.INFO, "@Before ProxyAuth set to " + proxyAuth.getProxyURL());
logger.log(Level.INFO, "@Before ProxyNoAuth set to " + proxyNoAuth.getProxyURL());
}
}
/**
* This test case will call individual tests to ensure the order that they run in. When we successfully
* create a connection to a proxy the session is kept alive and further attempts will not have their
* credentials checked. Because of this we have to do failing tests before successful ones.
*
* @throws RepositoryBackendException
* @throws MalformedURLException
* @throws ProxyTestException
*
* @throws Exception
*/
@Test
public void testAllProxyTests() throws MalformedURLException, RepositoryBackendException, ProxyTestException {
testGetAllWithoutProxy();
testUnauthenticatedProxy();
testAuthenticatedProxyInvalid();
testAuthenticatedProxyValid();
}
/**
* Just test the data is there and can be read it ... a sanity test
*
* @throws RepositoryBackendException
* @throws MalformedURLException
*
* @throws Exception
*/
private void testGetAllWithoutProxy() throws MalformedURLException, RepositoryBackendException {
logger.log(Level.INFO, "\n--testGetAllWithoutProxy()");
int results = getAll(null); // no proxy
assertEquals("Unexpected number of assets found after adding assets",
3, results);
}
/**
* Test against an unauthenticated proxy
*
* @throws MalformedURLException
* @throws RepositoryBackendException
* @throws RepositoryTestException
*
* @throws Exception
*/
private void testUnauthenticatedProxy() throws MalformedURLException, RepositoryBackendException, ProxyTestException {
logger.log(Level.INFO, "\n--testUnauthenticatedProxy()");
setAuthenticator("proxyuser1", "AUTHENTICATOR_SHOULDNT_BE_CALLED_AS_UNAUTHENTICATED");
int results = 0;
try {
results = getAll(proxyNoAuth); // unauthenticated proxy
} catch (RepositoryBackendException rbe) {
Throwable cause = rbe.getCause();
if (cause instanceof ConnectException) {
throw new ProxyTestException(
"ConnectionException: check the squid service is started on " + proxyNoAuth.getProxyURL(),
rbe);
} else {
// unknown cause
throw rbe;
}
}
assertEquals("Unexpected number of assets found through an unauthenticated proxy",
3, results);
}
/**
* Test against an authenticated proxy with invalid password
*
* @throws MalformedURLException
* @throws RepositoryBackendException
* @throws ProxyTestException
*/
private void testAuthenticatedProxyInvalid() throws MalformedURLException, RepositoryBackendException, ProxyTestException {
logger.log(Level.INFO, "\n--testAuthenticatedProxyInvalid()");
setAuthenticator("proxyuser1", "INVALID_PASSWORD");
try {
getAll(proxyAuth); // authenticated proxy
} catch (RepositoryBackendException rbe) {
Throwable cause = rbe.getCause();
if (cause == null) {
// there was no wrapped exception .. throw original
throw rbe;
} else if (cause instanceof RequestFailureException) {
// A RequestFailureException might be an HTTP 407
String message = cause.getMessage();
if (_verbose) {
logger.log(Level.INFO, "Message was: " + message);
}
if (message.contains("Server returned HTTP response code: 407")) {
// expected HTTP 407
logger.log(Level.INFO, "Proxy returned HTTP 407 for invalid credentials as expected");
return;
} else {
// not 407 so throw the whole exception
throw rbe;
}
} else if (cause instanceof ConnectException) {
// A ConnectException could indicate a proxy error
throw new ProxyTestException(
"ConnectionException: check the squid service is started on " + proxyAuth.getProxyURL(),
rbe);
} else {
// unknown cause
throw rbe;
}
}
fail("we should have received an HTTP 407 in a RequestFailureException");
}
/**
* Test against an authenticated proxy with valid userid and pasword
*
* @throws MalformedURLException
* @throws ProxyTestException
* @throws RepositoryBackendException
*/
private void testAuthenticatedProxyValid() throws MalformedURLException, ProxyTestException, RepositoryBackendException {
logger.log(Level.INFO, "\n--testAuthenticatedProxyValid()");
setAuthenticator("proxyuser1", "proxypassword1");
// the following code will never be exercised until this test runs in its own Junit test
// as it will fail in the preceding testAuthenticatedProxyInvalid()
int results = 0;
try {
results = getAll(proxyAuth); // authenticated proxy
} catch (RepositoryBackendException rbe) {
Throwable cause = rbe.getCause();
if (cause instanceof ConnectException) {
throw new ProxyTestException("ConnectionException: check the squid service is started on " + proxyAuth.getProxyURL(),
rbe);
} else {
// unknown cause
throw rbe;
}
}
assertEquals("Unexpected number of assets found through an authenticated proxy",
3, results);
}
/**
* Get all the resources in the repository using the provided proxy.
*
* @param proxy - the proxy or null if we are not using a proxy
* @return int - the number of records returned
* @throws MalformedURLException
* @throws RepositoryBackendException
*/
private int getAll(RestRepositoryConnectionProxy proxy) throws MalformedURLException, RepositoryBackendException {
_proxyLogon.setProxy(proxy); // set the passed proxy (null, auth or noauth)
RepositoryConnectionList loginInfo = new RepositoryConnectionList(_proxyLogon);
Collection<? extends RepositoryResource> resources = loginInfo.getAllResources();
logger.log(Level.INFO, "Read " + resources.size() + " resources");
if (_verbose) {
for (RepositoryResource res : resources) {
logger.log(Level.INFO, "Resource=" + res.getName() + ", id=" + res.getId());
}
}
return resources.size();
}
/**
* Return the provided proxy userid and password on a call to the system Authenticator
*
* @param pUser
* @param pPwd
*/
private void setAuthenticator(final String pUser, final String pPwd) {
if (_verbose) {
logger.log(Level.INFO, "Authenticator set to return user=" + pUser + ", pwd=" + pPwd);
}
Authenticator.setDefault(new Authenticator() {
@Override
public PasswordAuthentication getPasswordAuthentication() {
if (_verbose) {
logger.log(Level.INFO, "AUTHENTICATOR called for type=" + getRequestorType() + ", host=" + getRequestingHost() + ", port=" + getRequestingPort());
}
if (getRequestorType() == RequestorType.PROXY) {
return new PasswordAuthentication(pUser, pPwd.toCharArray());
}
return null;
}
});
}
/**
* Populate the repository using the LoginInfoEntry passed in from the framework
*
* @throws RepositoryBackendException
* @throws RepositoryResourceException
*/
private void loadResources() throws RepositoryBackendException, RepositoryResourceException {
logger.log(Level.INFO, "Loading repository data");
ProductResourceImpl pr = new ProductResourceImpl(_restConnection);
pr.setName("Alpha");
pr.uploadToMassive(new AddNewStrategy());
pr = new ProductResourceImpl(_restConnection);
pr.setName("Beta");
pr.uploadToMassive(new AddNewStrategy());
pr = new ProductResourceImpl(_restConnection);
pr.setName("Gamma");
pr.uploadToMassive(new AddNewStrategy());
RepositoryConnectionList loginInfo = new RepositoryConnectionList(_restConnection);
Collection<? extends RepositoryResource> resources = loginInfo.getAllResources();
if (_verbose) {
logger.log(Level.INFO, "Added " + resources.size() + " resources");
}
}
/**
* Set the verbose output flag
*
* @param verbose - true or false
* @return the value set
*/
private static boolean setVerbose(boolean verbose) {
_verbose = verbose;
return verbose;
}
/**
* Inner class to wrap exception and return information on proxy connection errors
*/
private class ProxyTestException extends Exception {
private static final long serialVersionUID = 1L;
protected ProxyTestException(String message, Throwable cause) {
super(message, cause);
}
@Override
public Throwable getCause() {
return super.getCause();
}
}
}