View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.vfs2.provider.ftps;
18  
19  import static org.apache.commons.vfs2.VfsTestUtils.getTestDirectory;
20  
21  import java.io.File;
22  import java.net.URL;
23  import java.time.Duration;
24  
25  import org.apache.commons.io.FileUtils;
26  import org.apache.commons.vfs2.AbstractProviderTestConfig;
27  import org.apache.commons.vfs2.FileObject;
28  import org.apache.commons.vfs2.FileSystemManager;
29  import org.apache.commons.vfs2.FileSystemOptions;
30  import org.apache.commons.vfs2.ProviderTestSuite;
31  import org.apache.commons.vfs2.impl.DefaultFileSystemManager;
32  import org.apache.ftpserver.FtpServer;
33  import org.apache.ftpserver.FtpServerFactory;
34  import org.apache.ftpserver.ftplet.FtpException;
35  import org.apache.ftpserver.ftplet.UserManager;
36  import org.apache.ftpserver.listener.ListenerFactory;
37  import org.apache.ftpserver.ssl.SslConfiguration;
38  import org.apache.ftpserver.ssl.SslConfigurationFactory;
39  import org.apache.ftpserver.usermanager.Md5PasswordEncryptor;
40  import org.apache.ftpserver.usermanager.PropertiesUserManagerFactory;
41  import org.apache.ftpserver.usermanager.impl.BaseUser;
42  import org.junit.jupiter.api.Assertions;
43  
44  /**
45   * Abstract tests for FTP file systems.
46   */
47  abstract class AbstractFtpsProviderTestCase extends AbstractProviderTestConfig {
48  
49      static final class FtpProviderTestSuite extends ProviderTestSuite {
50  
51          private final boolean implicit;
52  
53          public FtpProviderTestSuite(final AbstractFtpsProviderTestCase providerConfig) throws Exception {
54              super(providerConfig);
55              implicit = providerConfig.isImplicit();
56          }
57  
58          @Override
59          protected void setUp() throws Exception {
60              if (getSystemTestUriOverride() == null) {
61                  setUpClass(implicit);
62              }
63              super.setUp();
64          }
65  
66          @Override
67          protected void tearDown() throws Exception {
68              try {
69                  // This will report running threads of the FTP server.
70                  // However, shutting down the FTP server first will always
71                  // report an exception closing the manager, because the
72                  // server is already down
73                  super.tearDown();
74              } finally {
75                  tearDownClass();
76              }
77          }
78      }
79  
80      private static final String LISTENER_NAME = "default";
81  
82      private static int socketPort;
83  
84      /**
85       * Use %40 for @ in URLs
86       */
87      private static String connectionUri;
88  
89      private static FtpServer embeddedFtpServer;
90  
91      private static final String TEST_URI = "test.ftps.uri";
92  
93      private static final String USER_PROPS_RES = "org.apache.ftpsserver/users.properties";
94  
95      private static final String SERVER_JKS_RES = "org.apache.ftpsserver/ftpserver.jks";
96  
97      static String getConnectionUri() {
98          return connectionUri;
99      }
100 
101     static int getSocketPort() {
102         return socketPort;
103     }
104 
105     static String getSystemTestUriOverride() {
106         return System.getProperty(TEST_URI);
107     }
108 
109     /**
110      * Creates and starts an embedded Apache FTP EmbeddedFtpServer (MINA).
111      *
112      * @param implicit FTPS connection mode
113      * @throws FtpException
114      */
115     synchronized static void setUpClass(final boolean implicit) throws FtpException {
116         if (embeddedFtpServer != null) {
117             return;
118         }
119         // Let the OS find use an ephemeral port by using 0.
120         socketPort = 0;
121         final FtpServerFactory serverFactory = new FtpServerFactory();
122         final PropertiesUserManagerFactory propertiesUserManagerFactory = new PropertiesUserManagerFactory();
123         // TODO Update to SHA512
124         propertiesUserManagerFactory.setPasswordEncryptor(new Md5PasswordEncryptor());
125         final URL userPropsResource = ClassLoader.getSystemClassLoader().getResource(USER_PROPS_RES);
126         Assertions.assertNotNull(userPropsResource, USER_PROPS_RES);
127         propertiesUserManagerFactory.setUrl(userPropsResource);
128         final UserManager userManager = propertiesUserManagerFactory.createUserManager();
129         final BaseUser user = (BaseUser) userManager.getUserByName("test");
130         // Pickup the home dir value at runtime even though we have it set in the user prop file
131         // The user prop file requires the "homedirectory" to be set
132         user.setHomeDirectory(getTestDirectory());
133         serverFactory.setUserManager(userManager);
134         final ListenerFactory listenerFactory = new ListenerFactory();
135         listenerFactory.setPort(socketPort);
136 
137         // define SSL configuration
138         final URL serverJksResource = ClassLoader.getSystemClassLoader().getResource(SERVER_JKS_RES);
139         Assertions.assertNotNull(serverJksResource, SERVER_JKS_RES);
140         // System.out.println("Loading " + serverJksResource);
141         final SslConfigurationFactory sllConfigFactory = new SslConfigurationFactory();
142         final File keyStoreFile = FileUtils.toFile(serverJksResource);
143         Assertions.assertTrue(keyStoreFile.exists(), keyStoreFile.toString());
144         sllConfigFactory.setKeystoreFile(keyStoreFile);
145         sllConfigFactory.setKeystorePassword("password");
146 
147         // set the SSL configuration for the listener
148         final SslConfiguration sslConfiguration = sllConfigFactory.createSslConfiguration();
149         final NoProtocolSslConfigurationProxy noProtocolSslConfigurationProxy = new NoProtocolSslConfigurationProxy(sslConfiguration);
150         listenerFactory.setSslConfiguration(noProtocolSslConfigurationProxy);
151         listenerFactory.setImplicitSsl(implicit);
152 
153         // replace the default listener
154         serverFactory.addListener(LISTENER_NAME, listenerFactory.createListener());
155 
156         // start the server
157         embeddedFtpServer = serverFactory.createServer();
158         embeddedFtpServer.start();
159         Thread.yield();
160         if (embeddedFtpServer.isStopped() || embeddedFtpServer.isSuspended()) {
161             try {
162                 Thread.sleep(200);
163             } catch (final InterruptedException e) {
164                 e.printStackTrace();
165             }
166         }
167         socketPort = ((org.apache.ftpserver.impl.DefaultFtpServer) embeddedFtpServer).getListener(LISTENER_NAME).getPort();
168         // System.out.println("Using port " + SocketPort);
169         // System.out.printf("jdk.tls.disabledAlgorithms = %s%n", System.getProperty("jdk.tls.disabledAlgorithms"));
170         connectionUri = "ftps://test:test@localhost:" + socketPort;
171     }
172 
173     /**
174      * Stops the embedded Apache FTP EmbeddedFtpServer (MINA).
175      */
176     synchronized static void tearDownClass() {
177         if (embeddedFtpServer != null) {
178             embeddedFtpServer.suspend();
179             embeddedFtpServer.stop();
180             Thread.yield();
181             int count = 10;
182             while (count-- > 0 && !embeddedFtpServer.isStopped()) {
183                 final int millis = 200;
184                 System.out.println(String.format("Waiting %,d milliseconds for %s to stop", millis, embeddedFtpServer));
185                 try {
186                     Thread.sleep(millis);
187                 } catch (final InterruptedException e) {
188                     e.printStackTrace();
189                 }
190             }
191             embeddedFtpServer = null;
192         }
193     }
194 
195     protected FileSystemOptions fileSystemOptions;
196 
197     /**
198      * Returns the base folder for tests. You can override the DEFAULT_URI by using the system property name defined by TEST_URI.
199      */
200     @Override
201     public FileObject getBaseTestFolder(final FileSystemManager manager) throws Exception {
202         String uri = getSystemTestUriOverride();
203         if (uri == null) {
204             uri = connectionUri;
205         }
206         return manager.resolveFile(uri, getFileSystemOptions());
207     }
208 
209     protected FileSystemOptions getFileSystemOptions() {
210         if (fileSystemOptions == null) {
211             fileSystemOptions = new FileSystemOptions();
212             setupOptions(FtpsFileSystemConfigBuilder.getInstance());
213         }
214         return fileSystemOptions;
215     }
216 
217     protected abstract boolean isImplicit();
218 
219     /**
220      * Prepares the file system manager.
221      */
222     @Override
223     public void prepare(final DefaultFileSystemManager manager) throws Exception {
224         manager.addProvider("ftps", new FtpsFileProvider());
225     }
226 
227     protected void setupOptions(final FtpsFileSystemConfigBuilder builder) {
228         builder.setConnectTimeout(fileSystemOptions, Duration.ofSeconds(10));
229         builder.setDataTimeout(fileSystemOptions, Duration.ofSeconds(10));
230     }
231 
232 }