/** * Copyright (c) 2014 by the original author or authors. * * This code 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 2.1 of the License, or (at your option) any later version. * * The above copyright notice and this permission notice shall be included in all copies or * substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package ch.sdi.core.impl.ftp; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.apache.ftpserver.FtpServer; import org.apache.ftpserver.FtpServerFactory; import org.apache.ftpserver.ftplet.Authority; import org.apache.ftpserver.ftplet.FtpException; import org.apache.ftpserver.ftplet.UserManager; import org.apache.ftpserver.listener.Listener; import org.apache.ftpserver.listener.ListenerFactory; import org.apache.ftpserver.ssl.SslConfigurationFactory; import org.apache.ftpserver.usermanager.impl.BaseUser; import org.apache.ftpserver.usermanager.impl.WritePermission; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.StreamUtils; import ch.sdi.core.TestUtils; import ch.sdi.core.impl.ftp.FtpExecutor; import ch.sdi.core.intf.SdiMainProperties; /** * Tests the FtpExecutor with anonymous login, username/password without SLL and with implicite SSL by * uploading two files (test resources) to the target "./../testTarget" * <p> * The test opens an embedded FTPServer as counterpart for our FTPClient. For SSL the server needs a * certificate which it finds in src\test\Resources\keystore.jks (password is "password"). * <p> * * @version 1.0 (22.11.2014) * @author Heri */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes={FtpExecutor.class }) public class FtpExecutorTest implements ApplicationContextAware { /** */ private static final String TEST_TARGET_DIR_LOCAL = "./../testTarget/"; /** logger for this class */ private static Logger myLog = LogManager.getLogger( FtpExecutorTest.class ); @Autowired private ConfigurableEnvironment myEnv; private FtpExecutor myClassUnderTest; private static String myTargetDirLocal; private static List<Authority> myFtpAuthorities; private FtpServerFactory myServerFactory; private static ApplicationContext myCtx = null; @Override public void setApplicationContext( ApplicationContext aCtx ) throws BeansException { myCtx = aCtx; } /** * @throws java.lang.Exception */ @BeforeClass public static void setUpStatic() throws Exception { myTargetDirLocal = new File( TEST_TARGET_DIR_LOCAL ).getCanonicalPath() + File.separator; myFtpAuthorities = new ArrayList<Authority>(); myFtpAuthorities.add(new WritePermission()); } /** * @throws java.lang.Exception */ @AfterClass public static void setTearDownStatic() throws Exception { } /** * @throws java.lang.Exception */ @Before public void setUp() throws Exception { myClassUnderTest = myCtx.getBean( FtpExecutor.class ); myServerFactory = new FtpServerFactory(); } /** * Test method for {@link ch.sdi.core.impl.ftp.FtpExecutor#executeUpload(java.io.InputStream, java.lang.String)}. */ @Test public void testInitBySpring() throws Throwable { myLog.debug( "Testing self-initialize by Spring context" ); String targetDir = myTargetDirLocal; cleanTargetDir( targetDir ); Map<String, InputStream> filesToUpload = createFileUploadMap( targetDir ); TestUtils.addToEnvironment( myEnv, SdiMainProperties.KEY_FTP_CMD_LINE, "-A localhost" ); registerFtpUser( "anonymous", System.getProperty( "user.name" ) + "@" + InetAddress.getLocalHost().getHostName() ); FtpServer server = startFtpServer(); try { // omit call to init in order to auto initialize by spring context myClassUnderTest.connectAndLogin(); myClassUnderTest.uploadFiles( filesToUpload ); myClassUnderTest.logoutAndDisconnect(); assertFilesUploaded( createFileUploadMap( targetDir ) ); } finally { if ( server != null ) { myLog.debug( "stopping the embedded FTP server" ); server.stop(); } // if myServer != null } } /** * Test method for {@link ch.sdi.core.impl.ftp.FtpExecutor#executeUpload(java.io.InputStream, java.lang.String)}. */ @Test public void testUploadAnonymous() throws Throwable { myLog.debug( "Testing Anonymous login" ); String targetDir = myTargetDirLocal; cleanTargetDir( targetDir ); Map<String, InputStream> filesToUpload = createFileUploadMap( targetDir ); List<String> args = new ArrayList<String>(); args.add( "-bla" ); // invalid option should be ignored args.add( "-A" ); // anonymous args.add( "localhost" ); registerFtpUser( "anonymous", System.getProperty( "user.name" ) + "@" + InetAddress.getLocalHost().getHostName() ); FtpServer server = startFtpServer(); try { myClassUnderTest.init( args.toArray( new String[args.size()] ) ); myClassUnderTest.connectAndLogin(); myClassUnderTest.uploadFiles( filesToUpload ); myClassUnderTest.logoutAndDisconnect(); assertFilesUploaded( createFileUploadMap( targetDir ) ); } finally { if ( server != null ) { myLog.debug( "stopping the embedded FTP server" ); server.stop(); } // if myServer != null } } /** * Test method for {@link ch.sdi.core.impl.ftp.FtpExecutor#executeUpload(java.io.InputStream, java.lang.String)}. */ @Test public void testUploadLogin() throws Throwable { myLog.debug( "Testing normal login" ); String targetDir = myTargetDirLocal; cleanTargetDir( targetDir ); Map<String, InputStream> filesToUpload = createFileUploadMap( targetDir ); List<String> args = new ArrayList<String>(); args.add( "localhost" ); args.add( "heri" ); // user args.add( "heri" ); // pw registerFtpUser( "heri", "heri" ); FtpServer server = startFtpServer(); try { myClassUnderTest.init( args.toArray( new String[args.size()] ) ); myClassUnderTest.connectAndLogin(); myClassUnderTest.uploadFiles( filesToUpload ); myClassUnderTest.logoutAndDisconnect(); assertFilesUploaded( createFileUploadMap( targetDir ) ); } finally { if ( server != null ) { myLog.debug( "stopping the embedded FTP server" ); server.stop(); } // if myServer != null } } /** * Test method for {@link ch.sdi.core.impl.ftp.FtpExecutor#executeUpload(java.io.InputStream, java.lang.String)}. */ @Test public void testUploadLoginSSLImplicite() throws Throwable { myLog.debug( "Testing SSL login (implicite)" ); String targetDir = myTargetDirLocal; cleanTargetDir( targetDir ); Map<String, InputStream> filesToUpload = createFileUploadMap( targetDir ); List<String> args = new ArrayList<String>(); args.add( "-p" ); args.add( "false" ); // activate implicite SSL args.add( "localhost" ); args.add( "heri" ); // user args.add( "heri" ); // pw ListenerFactory listenerFactory = new ListenerFactory(); // define SSL configuration SslConfigurationFactory ssl = new SslConfigurationFactory(); ssl.setKeystoreFile(new File("keystore.jks")); // this is in core/test/resources and contains one // selfsigned certificate ssl.setKeystorePassword("password"); listenerFactory.setSslConfiguration(ssl.createSslConfiguration()); listenerFactory.setImplicitSsl(false); // replace the default listener Listener listenerOrg = myServerFactory.getListener( "default" ); try { myServerFactory.addListener("default", listenerFactory.createListener()); registerFtpUser( "heri", "heri" ); FtpServer server = startFtpServer(); try { myClassUnderTest.init( args.toArray( new String[args.size()] ) ); myClassUnderTest.connectAndLogin(); myClassUnderTest.uploadFiles( filesToUpload ); myClassUnderTest.logoutAndDisconnect(); assertFilesUploaded( createFileUploadMap( targetDir ) ); } finally { if ( server != null ) { myLog.debug( "stopping the embedded FTP server" ); server.stop(); } // if myServer != null } } finally { myServerFactory.addListener("default", listenerOrg ); } } /** * @param aCreateFileUploadMap */ private void assertFilesUploaded( Map<String, InputStream> aCreateFileUploadMap ) { for ( String targetFileName : aCreateFileUploadMap.keySet() ) { assertFileUpdloaded( targetFileName, aCreateFileUploadMap.get( targetFileName ) ); } } /** * @param aTargetFileName * @param aInputStream */ private void assertFileUpdloaded( String aTargetFileName, InputStream aInputStream ) { File target = new File( aTargetFileName ); Assert.assertTrue( target.exists() ); byte[] input = copyStreamToByteArray( aInputStream ); byte[] output = null; try { output = copyStreamToByteArray( new FileInputStream( target ) ); } catch ( FileNotFoundException t ) { myLog.error( "Should not occur", t ); Assert.fail( "FileNotFoundException while opening file " + target.getPath() ); } Assert.assertArrayEquals( input, output ); } /** * @param aInputStream * @return */ private byte[] copyStreamToByteArray( InputStream aInputStream ) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { StreamUtils.copy( aInputStream, baos ); } catch ( IOException t ) { Assert.fail( "IOException while copying stream. " + t ); } byte[] input = baos.toByteArray(); return input; } /** * @param aTargetDir */ private void cleanTargetDir( String aTargetDir ) { File[] list = new File( aTargetDir ).listFiles(); for ( File file : list ) { file.delete(); } } /** * @param aUsername * @param aPassword * @throws FtpException */ private void registerFtpUser( String aUsername, String aPassword ) throws FtpException { BaseUser user = new BaseUser(); user.setName( aUsername ); user.setPassword( aPassword ); user.setAuthorities(myFtpAuthorities); UserManager userManager = myServerFactory.getUserManager(); userManager.save(user); } /** * @return * @throws FtpException */ private FtpServer startFtpServer() throws FtpException { FtpServer result; result = myServerFactory.createServer(); myLog.debug( "starting an embedded FTP server" ); result.start(); return result; } /** * @param aTargetDir * @return */ private Map<String, InputStream> createFileUploadMap( String aTargetDir ) { Map<String,InputStream> filesToUpload = new TreeMap<String,InputStream>(); filesToUpload.put( aTargetDir + "sdimain_test.properties", ClassLoader.getSystemResourceAsStream( "sdimain.properties" ) ); filesToUpload.put( aTargetDir + "log4j2.xml", ClassLoader.getSystemResourceAsStream( "log4j2.xml" ) ); return filesToUpload; } }