DFC shall provide a bearer authorization token in HTTP
[nonrtric/plt/ranpm.git] / datafilecollector / src / main / java / org / oran / datafile / ftp / FtpesClient.java
1 /*-
2  * ============LICENSE_START======================================================================
3  * Copyright (C) 2018-2023 Nordix Foundation. All rights reserved.
4  * Copyright (C) 2020-2021 Nokia. All rights reserved.
5  * ===============================================================================================
6  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7  * in compliance with 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 distributed under the License
12  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13  * or implied. See the License for the specific language governing permissions and limitations under
14  * the License.
15  * ============LICENSE_END========================================================================
16  */
17
18 package org.oran.datafile.ftp;
19
20 import java.io.File;
21 import java.io.FileOutputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.OutputStream;
25 import java.nio.file.Files;
26 import java.nio.file.Path;
27 import java.nio.file.Paths;
28 import java.security.GeneralSecurityException;
29 import java.security.KeyStore;
30 import java.security.KeyStoreException;
31 import java.security.NoSuchAlgorithmException;
32 import java.security.UnrecoverableKeyException;
33 import java.security.cert.CertificateException;
34
35 import javax.net.ssl.KeyManager;
36 import javax.net.ssl.KeyManagerFactory;
37 import javax.net.ssl.TrustManager;
38 import javax.net.ssl.TrustManagerFactory;
39
40 import org.apache.commons.net.ftp.FTP;
41 import org.apache.commons.net.ftp.FTPReply;
42 import org.apache.commons.net.ftp.FTPSClient;
43 import org.oran.datafile.commons.FileCollectClient;
44 import org.oran.datafile.exceptions.DatafileTaskException;
45 import org.oran.datafile.exceptions.NonRetryableDatafileTaskException;
46 import org.oran.datafile.model.FileServerData;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49 import org.springframework.core.io.FileSystemResource;
50
51 /**
52  * Gets file from PNF with FTPS protocol.
53  */
54 public class FtpesClient implements FileCollectClient {
55     private static final Logger logger = LoggerFactory.getLogger(FtpesClient.class);
56
57     private static final int DEFAULT_PORT = 21;
58
59     FTPSClient realFtpsClient = new FTPSClient();
60     private final FileServerData fileServerData;
61     private static TrustManager theTrustManager = null;
62     private static KeyManager theKeyManager = null;
63
64     private final Path keyCertPath;
65     private final String keyCertPasswordPath;
66     private final Path trustedCaPath;
67     private final String trustedCaPasswordPath;
68
69     /**
70      * Constructor.
71      *
72      * @param fileServerData info needed to connect to the PNF.
73      * @param keyCertPath path to DFC's key cert.
74      * @param keyCertPasswordPath path of file containing password for DFC's key
75      *        cert.
76      * @param trustedCaPath path to the PNF's trusted keystore.
77      * @param trustedCaPasswordPath path of file containing password for the PNF's
78      *        trusted keystore.
79      */
80     public FtpesClient(FileServerData fileServerData, Path keyCertPath, String keyCertPasswordPath, Path trustedCaPath,
81         String trustedCaPasswordPath) {
82         this.fileServerData = fileServerData;
83         this.keyCertPath = keyCertPath;
84         this.keyCertPasswordPath = keyCertPasswordPath;
85         this.trustedCaPath = trustedCaPath;
86         this.trustedCaPasswordPath = trustedCaPasswordPath;
87     }
88
89     @Override
90     public void open() throws DatafileTaskException {
91         try {
92             realFtpsClient.setNeedClientAuth(trustedCaPath != null);
93             realFtpsClient.setKeyManager(getKeyManager(keyCertPath, keyCertPasswordPath));
94             realFtpsClient.setTrustManager(getTrustManager(trustedCaPath, trustedCaPasswordPath));
95             setUpConnection();
96         } catch (DatafileTaskException e) {
97             throw e;
98         } catch (Exception e) {
99             throw new DatafileTaskException("Could not open connection: " + e, e);
100         }
101     }
102
103     @Override
104     public void close() {
105         logger.trace("starting to closeDownConnection");
106         if (realFtpsClient.isConnected()) {
107             try {
108                 boolean logOut = realFtpsClient.logout();
109                 logger.trace("logOut: {}", logOut);
110             } catch (Exception e) {
111                 logger.trace("Unable to logout connection.", e);
112             }
113             try {
114                 realFtpsClient.disconnect();
115                 logger.trace("disconnected!");
116             } catch (Exception e) {
117                 logger.trace("Unable to disconnect connection.", e);
118             }
119         }
120     }
121
122     @Override
123     public void collectFile(String remoteFileName, Path localFileName) throws DatafileTaskException {
124         logger.trace("collectFile called");
125
126         try (OutputStream output = createOutputStream(localFileName)) {
127             logger.trace("begin to retrieve from xNF.");
128             if (!realFtpsClient.retrieveFile(remoteFileName, output)) {
129                 throw new NonRetryableDatafileTaskException(
130                     "Could not retrieve file. No retry attempts will be done, file :" + remoteFileName);
131             }
132         } catch (IOException e) {
133             throw new DatafileTaskException("Could not fetch file: " + e, e);
134         }
135         logger.trace("collectFile fetched: {}", localFileName);
136     }
137
138     private static int getPort(Integer port) {
139         return port != null ? port : DEFAULT_PORT;
140     }
141
142     private void setUpConnection() throws DatafileTaskException, IOException {
143
144         realFtpsClient.connect(fileServerData.serverAddress, getPort(fileServerData.port));
145         logger.trace("after ftp connect");
146
147         if (!realFtpsClient.login(fileServerData.userId, fileServerData.password)) {
148             throw new DatafileTaskException("Unable to log in to xNF. " + fileServerData.serverAddress);
149         }
150
151         if (FTPReply.isPositiveCompletion(realFtpsClient.getReplyCode())) {
152             realFtpsClient.enterLocalPassiveMode();
153             realFtpsClient.setFileType(FTP.BINARY_FILE_TYPE);
154             // Set protection buffer size
155             realFtpsClient.execPBSZ(0);
156             // Set data channel protection to private
157             realFtpsClient.execPROT("P");
158             realFtpsClient.setBufferSize(1024 * 1024);
159         } else {
160             throw new DatafileTaskException("Unable to connect to xNF. " + fileServerData.serverAddress
161                 + " xNF reply code: " + realFtpsClient.getReplyCode());
162         }
163
164         logger.trace("setUpConnection successfully!");
165     }
166
167     private TrustManager createTrustManager(Path trustedCaPath, String trustedCaPassword)
168         throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException {
169         logger.trace("Creating trust manager from file: {}", trustedCaPath);
170         try (InputStream fis = createInputStream(trustedCaPath)) {
171             KeyStore keyStore = KeyStore.getInstance("JKS");
172             keyStore.load(fis, trustedCaPassword.toCharArray());
173             TrustManagerFactory factory = TrustManagerFactory.getInstance("SunX509");
174             factory.init(keyStore);
175             return factory.getTrustManagers()[0];
176         }
177     }
178
179     protected InputStream createInputStream(Path localFileName) throws IOException {
180         FileSystemResource realResource = new FileSystemResource(localFileName);
181         return realResource.getInputStream();
182     }
183
184     protected OutputStream createOutputStream(Path localFileName) throws IOException, DatafileTaskException {
185         File localFile = localFileName.toFile();
186         if (!localFile.createNewFile()) {
187             logger.debug("Local file {} already created", localFileName);
188             throw new NonRetryableDatafileTaskException("Local file already created: " + localFileName);
189         }
190         OutputStream output = new FileOutputStream(localFile);
191         logger.trace("File {} opened xNF", localFileName);
192         return output;
193     }
194
195     protected TrustManager getTrustManager(Path trustedCaPath, String trustedCaPasswordPath)
196         throws KeyStoreException, NoSuchAlgorithmException, IOException, CertificateException {
197         synchronized (FtpesClient.class) {
198             if (theTrustManager == null && trustedCaPath != null) {
199                 String trustedCaPassword = Files.readString(Paths.get((trustedCaPasswordPath)));
200                 theTrustManager = createTrustManager(trustedCaPath, trustedCaPassword);
201             }
202             return theTrustManager;
203         }
204     }
205
206     protected KeyManager getKeyManager(Path keyCertPath, String keyCertPasswordPath)
207         throws IOException, GeneralSecurityException {
208
209         synchronized (FtpesClient.class) {
210             if (theKeyManager == null) {
211                 String keyCertPassword = Files.readString(Paths.get((keyCertPasswordPath)));
212                 theKeyManager = createKeyManager(keyCertPath, keyCertPassword);
213             }
214             return theKeyManager;
215         }
216     }
217
218     private KeyManager createKeyManager(Path keyCertPath, String keyCertPassword) throws IOException, KeyStoreException,
219         NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {
220         logger.trace("Creating key manager from file: {}", keyCertPath);
221         try (InputStream fis = createInputStream(keyCertPath)) {
222             KeyStore keyStore = KeyStore.getInstance("JKS");
223             keyStore.load(fis, keyCertPassword.toCharArray());
224             KeyManagerFactory factory = KeyManagerFactory.getInstance("SunX509");
225             factory.init(keyStore, keyCertPassword.toCharArray());
226             return factory.getKeyManagers()[0];
227         }
228     }
229 }