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 java.io.File;
20  import java.io.IOException;
21  import java.net.URL;
22  import java.time.Duration;
23  
24  import org.apache.commons.io.FileUtils;
25  import org.apache.commons.vfs2.AbstractProviderTestConfig;
26  import org.apache.commons.vfs2.FileObject;
27  import org.apache.commons.vfs2.FileSystemManager;
28  import org.apache.commons.vfs2.FileSystemOptions;
29  import org.apache.commons.vfs2.ProviderTestSuite;
30  import org.apache.commons.vfs2.impl.DefaultFileSystemManager;
31  import org.apache.ftpserver.FtpServer;
32  import org.apache.ftpserver.FtpServerFactory;
33  import org.apache.ftpserver.ftplet.FtpException;
34  import org.apache.ftpserver.ftplet.UserManager;
35  import org.apache.ftpserver.listener.ListenerFactory;
36  import org.apache.ftpserver.ssl.SslConfigurationFactory;
37  import org.apache.ftpserver.usermanager.PropertiesUserManagerFactory;
38  import org.apache.ftpserver.usermanager.impl.BaseUser;
39  import org.junit.Assert;
40  
41  /**
42   * Abstract tests for FTP file systems.
43   */
44  abstract class AbstractFtpsProviderTestCase extends AbstractProviderTestConfig {
45      static final class FtpProviderTestSuite extends ProviderTestSuite {
46          private final boolean implicit;
47  
48          public FtpProviderTestSuite(final AbstractFtpsProviderTestCase providerConfig) throws Exception {
49              super(providerConfig);
50              this.implicit = providerConfig.isImplicit();
51          }
52  
53          @Override
54          protected void setUp() throws Exception {
55              if (getSystemTestUriOverride() == null) {
56                  setUpClass(implicit);
57              }
58              super.setUp();
59          }
60  
61          @Override
62          protected void tearDown() throws Exception {
63              try {
64                  // This will report running threads of the FTP server.
65                  // However, shutting down the FTP server first will always
66                  // report an exception closing the manager, because the
67                  // server is already down
68                  super.tearDown();
69              } finally {
70                  tearDownClass();
71              }
72          }
73      }
74  
75      private static int SocketPort;
76  
77      /**
78       * Use %40 for @ in URLs
79       */
80      private static String ConnectionUri;
81  
82      private static FtpServer Server;
83  
84      private static final String TEST_URI = "test.ftps.uri";
85  
86      private static final String USER_PROPS_RES = "org.apache.ftpsserver/users.properties";
87  
88      private static final String SERVER_JKS_RES = "org.apache.ftpsserver/ftpserver.jks";
89  
90      static String getConnectionUri() {
91          return ConnectionUri;
92      }
93  
94      static int getSocketPort() {
95          return SocketPort;
96      }
97  
98      static String getSystemTestUriOverride() {
99          return System.getProperty(TEST_URI);
100     }
101 
102     /**
103      * Creates and starts an embedded Apache FTP Server (MINA).
104      *
105      * @param implicit FTPS connection mode
106      * @throws FtpException
107      * @throws IOException
108      */
109     static void setUpClass(final boolean implicit) throws FtpException, IOException {
110         if (Server != null) {
111             return;
112         }
113         final FtpServerFactory serverFactory = new FtpServerFactory();
114         final PropertiesUserManagerFactory propertiesUserManagerFactory = new PropertiesUserManagerFactory();
115         final URL userPropsResource = ClassLoader.getSystemClassLoader().getResource(USER_PROPS_RES);
116         Assert.assertNotNull(USER_PROPS_RES, userPropsResource);
117         propertiesUserManagerFactory.setUrl(userPropsResource);
118         final UserManager userManager = propertiesUserManagerFactory.createUserManager();
119         final BaseUser user = (BaseUser) userManager.getUserByName("test");
120         // Pickup the home dir value at runtime even though we have it set in the user prop file
121         // The user prop file requires the "homedirectory" to be set
122         user.setHomeDirectory(getTestDirectory());
123         serverFactory.setUserManager(userManager);
124         final ListenerFactory factory = new ListenerFactory();
125         // Let the OS find use an ephemeral port by using 0 here.
126         factory.setPort(0);
127 
128         // define SSL configuration
129         final URL serverJksResource = ClassLoader.getSystemClassLoader().getResource(SERVER_JKS_RES);
130         Assert.assertNotNull(SERVER_JKS_RES, serverJksResource);
131         final SslConfigurationFactory ssl = new SslConfigurationFactory();
132         final File keyStoreFile = FileUtils.toFile(serverJksResource);
133         Assert.assertTrue(keyStoreFile.toString(), keyStoreFile.exists());
134         ssl.setKeystoreFile(keyStoreFile);
135         ssl.setKeystorePassword("password");
136 
137         // set the SSL configuration for the listener
138         factory.setSslConfiguration(ssl.createSslConfiguration());
139         factory.setImplicitSsl(implicit);
140 
141         // replace the default listener
142         serverFactory.addListener("default", factory.createListener());
143 
144         // start the server
145         Server = serverFactory.createServer();
146         Server.start();
147         SocketPort = ((org.apache.ftpserver.impl.DefaultFtpServer) Server).getListener("default").getPort();
148         ConnectionUri = "ftps://test:test@localhost:" + SocketPort;
149     }
150 
151     /**
152      * Stops the embedded Apache FTP Server (MINA).
153      */
154     static void tearDownClass() {
155         if (Server != null) {
156             Server.stop();
157             Server = null;
158         }
159     }
160 
161     protected FileSystemOptions fileSystemOptions;
162 
163     /**
164      * Returns the base folder for tests. You can override the DEFAULT_URI by using the system property name defined by
165      * TEST_URI.
166      */
167     @Override
168     public FileObject getBaseTestFolder(final FileSystemManager manager) throws Exception {
169         String uri = getSystemTestUriOverride();
170         if (uri == null) {
171             uri = ConnectionUri;
172         }
173         return manager.resolveFile(uri, getFileSystemOptions());
174     }
175 
176     protected FileSystemOptions getFileSystemOptions() {
177         if (fileSystemOptions == null) {
178             fileSystemOptions = new FileSystemOptions();
179             setupOptions(FtpsFileSystemConfigBuilder.getInstance());
180         }
181         return fileSystemOptions;
182     }
183 
184     protected abstract boolean isImplicit();
185 
186     /**
187      * Prepares the file system manager.
188      */
189     @Override
190     public void prepare(final DefaultFileSystemManager manager) throws Exception {
191         manager.addProvider("ftps", new FtpsFileProvider());
192     }
193 
194     protected void setupOptions(final FtpsFileSystemConfigBuilder builder) {
195         builder.setConnectTimeout(fileSystemOptions, Duration.ofSeconds(1));
196         builder.setDataTimeout(fileSystemOptions, Duration.ofSeconds(2));
197     }
198 }