/**
* Copyright (C) 2012-2013 Selventa, Inc.
*
* This file is part of the OpenBEL Framework.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The OpenBEL Framework is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the OpenBEL Framework. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms under LGPL v3:
*
* This license does not authorize you and you are prohibited from using the
* name, trademarks, service marks, logos or similar indicia of Selventa, Inc.,
* or, in the discretion of other licensors or authors of the program, the
* name, trademarks, service marks, logos or similar indicia of such authors or
* licensors, in any marketing or advertising materials relating to your
* distribution of the program or any covered product. This restriction does
* not waive or limit your obligation to keep intact all copyright notices set
* forth in the program as delivered to you.
*
* If you distribute the program in whole or in part, or any modified version
* of the program, and you assume contractual liability to the recipient with
* respect to the program or modified version, then you will indemnify the
* authors and licensors of the program for any liabilities that these
* contractual assumptions directly impose on those licensors and authors.
*/
package org.openbel.framework.core.protocol.handler;
import static org.openbel.framework.common.BELUtilities.ephemeralPort;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import junit.framework.Assert;
import org.apache.ftpserver.ConnectionConfigFactory;
import org.apache.ftpserver.FtpServer;
import org.apache.ftpserver.FtpServerFactory;
import org.apache.ftpserver.ftplet.Authentication;
import org.apache.ftpserver.ftplet.AuthenticationFailedException;
import org.apache.ftpserver.ftplet.Authority;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.ftplet.User;
import org.apache.ftpserver.ftplet.UserManager;
import org.apache.ftpserver.listener.ListenerFactory;
import org.apache.ftpserver.usermanager.AnonymousAuthentication;
import org.apache.ftpserver.usermanager.ClearTextPasswordEncryptor;
import org.apache.ftpserver.usermanager.PasswordEncryptor;
import org.apache.ftpserver.usermanager.UserManagerFactory;
import org.apache.ftpserver.usermanager.UsernamePasswordAuthentication;
import org.apache.ftpserver.usermanager.impl.AbstractUserManager;
import org.apache.ftpserver.usermanager.impl.BaseUser;
import org.apache.ftpserver.usermanager.impl.ConcurrentLoginPermission;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openbel.framework.core.protocol.ResourceDownloadError;
import org.openbel.framework.core.protocol.handler.FTPProtocolHandler;
/**
* {@link FtpLoaderTest} tests the {@link FTPProtocolHandler}.
*
* @author Anthony Bargnesi {@code <abargnesi@selventa.com>}
*/
public class FtpLoaderTest extends AbstractProtocolTest {
/**
* Defines the test ftp username as {@value}.
*/
private static final String TEST_USERNAME = "ftptest";
/**
* Defines the test ftp password as {@value}.
*/
private static final String TEST_PASSWORD = "ftptest";
/**
* Defines the test ftp root as the "user.dir" system property.
*/
private static final String TEST_USER_FTP_ROOT = System
.getProperty("user.dir");
/**
* Defines the location file where the file will be saved.
*/
private File localDestinationFile;
/**
* Defines the ftp server used as a test ftp daemon.
*/
private FtpServer ftpServer;
/**
* Defines the port the test ftp server will run on.
*/
private int port;
/**
* Set up the ftp test by choosing a random ftp server port.
*/
@Before
public void setupTest() {
port = ephemeralPort();
}
/**
* Tear down the test by stopping the ftp server.
*/
@After
public void teardownTest() {
stopFtpServer();
}
/**
* Test the successful retrieval of a file via ftp inline authentication.
*/
@Test
public void testFTPInlineAuthentication() {
try {
startRestrictedFtpServer();
} catch (FtpException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
try {
localDestinationFile =
new FTPProtocolHandler().downloadResource(
"ftp://ftptest:ftptest@localhost:" + port + "/"
+ TEST_FILE_PATH, "test.belns");
tempFiles.add(localDestinationFile);
testFile(localDestinationFile);
} catch (ResourceDownloadError e) {
e.printStackTrace();
Assert.fail(e.getMessage());
} catch (IOException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
/**
* Test the successful retrieval of a file via prompted password
* authentication.
*/
@Test
public void testFTPPromptedPasswordAuthentication() {
try {
startRestrictedFtpServer();
} catch (FtpException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
try {
ByteArrayInputStream pwdStringInputStream =
new ByteArrayInputStream("ftptest".getBytes("US-ASCII"));
localDestinationFile =
new FTPProtocolHandler(pwdStringInputStream)
.downloadResource("ftp://ftptest@localhost:" + port
+ "/" + TEST_FILE_PATH, "test.belns");
tempFiles.add(localDestinationFile);
testFile(localDestinationFile);
} catch (ResourceDownloadError e) {
e.printStackTrace();
Assert.fail(e.getMessage());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
} catch (IOException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
/**
* Test the successful retrieval of a file using an anonymous ftp
* account.
*/
@Test
public void testFTPAnonymousAuthenticationAllowed() {
try {
startUnrestrictedFtpServer();
} catch (FtpException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
try {
localDestinationFile =
new FTPProtocolHandler().downloadResource(
"ftp://localhost:" + port + "/" + TEST_FILE_PATH,
"test.belns");
tempFiles.add(localDestinationFile);
testFile(localDestinationFile);
} catch (ResourceDownloadError e) {
e.printStackTrace();
Assert.fail(e.getMessage());
} catch (IOException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
/**
* Test the expectation that we will receive a
* {@link ResourceDownloadError} if the ftp server does not allow
* anonymous account access.
*
* @throws ResourceDownloadError - Thrown if the ftp connection
* could not be made because anonymous accounts are restricted. This
* is expected to be thrown and it is a testcase failure if it is not.
*/
@Test(expected = ResourceDownloadError.class)
public void testFTPAnonymousAuthenticationDenied()
throws ResourceDownloadError {
try {
startRestrictedFtpServer();
} catch (FtpException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
localDestinationFile =
new FTPProtocolHandler().downloadResource("ftp://localhost:"
+ port + "/" + TEST_FILE_PATH, "test.belns");
tempFiles.add(localDestinationFile);
try {
testFile(localDestinationFile);
} catch (IOException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
}
/**
* Start up the ftp daemon without anonymous account access.
*
* @throws FtpException - Thrown if an error occurred starting the
* restricted ftp daemon.
*/
protected void startRestrictedFtpServer() throws FtpException {
FtpServerFactory serverFactory = new FtpServerFactory();
ConnectionConfigFactory connectionConfigFactory =
new ConnectionConfigFactory();
connectionConfigFactory.setAnonymousLoginEnabled(false);
serverFactory.setConnectionConfig(connectionConfigFactory
.createConnectionConfig());
serverFactory.setUserManager(new TestUserManagerFactory()
.createUserManager());
startFtpServer(serverFactory);
}
/**
* Start up the ftp daemon with anonymous account access.
*
* @throws FtpException - Thrown if an error occurred starting the
* unrestricted ftp daemon.
*/
protected void startUnrestrictedFtpServer() throws FtpException {
FtpServerFactory serverFactory = new FtpServerFactory();
ConnectionConfigFactory connectionConfigFactory =
new ConnectionConfigFactory();
connectionConfigFactory.setAnonymousLoginEnabled(true);
serverFactory.setConnectionConfig(connectionConfigFactory
.createConnectionConfig());
serverFactory.setUserManager(new TestUserManagerFactory()
.createUserManager());
startFtpServer(serverFactory);
}
/**
* Start the ftp server.
*
* @param ftpServerFactory {@link FtpServerFactory}, the factory used
* to configure the server with
* @throws FtpException - Thrown if an error occurred starting the ftp
* server.
*/
protected void startFtpServer(FtpServerFactory ftpServerFactory)
throws FtpException {
ListenerFactory factory = new ListenerFactory();
factory.setPort(port);
ftpServerFactory.addListener("default", factory.createListener());
ftpServer = ftpServerFactory.createServer();
ftpServer.start();
}
/**
* Stop the ftp server.
*/
protected void stopFtpServer() {
if (ftpServer != null && !ftpServer.isStopped()) {
ftpServer.stop();
}
}
/**
* TestUserManagerFactory defines a mock {@link UserManagerFactory} to
* provide the ftp server admin account.
*
* @author Anthony Bargnesi {@code <abargnesi@selventa.com>}
*/
private static class TestUserManagerFactory implements UserManagerFactory {
/**
* {@inheritDoc}
*/
@Override
public UserManager createUserManager() {
return new TestUserManager("admin",
new ClearTextPasswordEncryptor());
}
}
/**
* TestUserManager defines a mock {@link AbstractUserManager} to establish
* valid users for the ftp server.
*
* @author Anthony Bargnesi {@code <abargnesi@selventa.com>}
*/
private static class TestUserManager extends AbstractUserManager {
private BaseUser testUser;
private BaseUser anonUser;
/**
* Creates the TestUserManager with the {@code adminName} and the
* {@code passwordEncryptor}.
*
* @param adminName {@link String}, the admin name
* @param passwordEncryptor {@link PasswordEncryptor}, the password
* encryptor to use
*/
public TestUserManager(String adminName,
PasswordEncryptor passwordEncryptor) {
super(adminName, passwordEncryptor);
testUser = new BaseUser();
testUser.setAuthorities(Arrays
.asList(new Authority[] { new ConcurrentLoginPermission(1,
1) }));
testUser.setEnabled(true);
testUser.setHomeDirectory(TEST_USER_FTP_ROOT);
testUser.setMaxIdleTime(10000);
testUser.setName(TEST_USERNAME);
testUser.setPassword(TEST_PASSWORD);
anonUser = new BaseUser(testUser);
anonUser.setName("anonymous");
}
/**
* {@inheritDoc}
*/
@Override
public User getUserByName(String username) throws FtpException {
if (TEST_USERNAME.equals(username)) {
return testUser;
} else if (anonUser.getName().equals(username)) {
return anonUser;
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public String[] getAllUserNames() throws FtpException {
return new String[] { TEST_USERNAME, anonUser.getName() };
}
/**
* {@inheritDoc}
*/
@Override
public void delete(String username) throws FtpException {
//no opt
}
/**
* {@inheritDoc}
*/
@Override
public void save(User user) throws FtpException {
//no opt
System.out.println("save");
}
/**
* {@inheritDoc}
*/
@Override
public boolean doesExist(String username) throws FtpException {
return (TEST_USERNAME.equals(username) || anonUser.getName()
.equals(username)) ? true : false;
}
/**
* {@inheritDoc}
*/
@Override
public User authenticate(Authentication authentication)
throws AuthenticationFailedException {
if (UsernamePasswordAuthentication.class
.isAssignableFrom(authentication.getClass())) {
UsernamePasswordAuthentication upAuth =
(UsernamePasswordAuthentication) authentication;
if (TEST_USERNAME.equals(upAuth.getUsername())
&& TEST_PASSWORD.equals(upAuth.getPassword())) {
return testUser;
}
if (anonUser.getName().equals(upAuth.getUsername())) {
return anonUser;
}
} else if (AnonymousAuthentication.class
.isAssignableFrom(authentication.getClass())) {
return anonUser;
}
return null;
}
}
}