17ee57001efff21a7ae22947ce773c7596f06a83
[sdc.git] /
1 /*
2  * Copyright 2017 Huawei Technologies Co., Ltd.
3  * Modifications Copyright 2018 European Support Limited
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * 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.openecomp.sdcrests.vsp.rest.services;
18
19 import static javax.ws.rs.core.HttpHeaders.CONTENT_DISPOSITION;
20 import static org.openecomp.core.utilities.file.FileUtils.getFileExtension;
21 import static org.openecomp.core.utilities.file.FileUtils.getNetworkPackageName;
22
23 import java.nio.ByteBuffer;
24 import java.nio.charset.StandardCharsets;
25 import java.security.KeyManagementException;
26 import java.security.KeyStore;
27 import java.security.KeyStoreException;
28 import java.security.NoSuchAlgorithmException;
29 import java.security.cert.CertificateException;
30 import java.security.cert.X509Certificate;
31 import java.util.List;
32 import java.util.Objects;
33 import java.util.Optional;
34 import javax.inject.Named;
35 import javax.net.ssl.SSLContext;
36 import javax.net.ssl.TrustManager;
37 import javax.net.ssl.TrustManagerFactory;
38 import javax.net.ssl.X509TrustManager;
39 import javax.ws.rs.client.Client;
40 import javax.ws.rs.client.ClientBuilder;
41 import javax.ws.rs.core.Response;
42 import org.onap.config.api.ConfigurationManager;
43 import org.openecomp.core.utilities.orchestration.OnboardingTypesEnum;
44 import org.openecomp.sdc.common.errors.CoreException;
45 import org.openecomp.sdc.common.errors.ErrorCode;
46 import org.openecomp.sdc.common.errors.ErrorCodeAndMessage;
47 import org.openecomp.sdc.common.errors.GeneralErrorBuilder;
48 import org.openecomp.sdc.logging.api.Logger;
49 import org.openecomp.sdc.logging.api.LoggerFactory;
50 import org.openecomp.sdc.vendorsoftwareproduct.OrchestrationTemplateCandidateManager;
51 import org.openecomp.sdc.vendorsoftwareproduct.OrchestrationTemplateCandidateManagerFactory;
52 import org.openecomp.sdc.vendorsoftwareproduct.dao.type.VspDetails;
53 import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardPackageInfo;
54 import org.openecomp.sdc.vendorsoftwareproduct.types.UploadFileResponse;
55 import org.openecomp.sdc.versioning.VersioningManager;
56 import org.openecomp.sdc.versioning.VersioningManagerFactory;
57 import org.openecomp.sdc.versioning.dao.types.Version;
58 import org.openecomp.sdcrests.vendorsoftwareproducts.types.UploadFileResponseDto;
59 import org.openecomp.sdcrests.vsp.rest.VnfPackageRepository;
60 import org.openecomp.sdcrests.vsp.rest.mapping.MapUploadFileResponseToUploadFileResponseDto;
61 import org.springframework.context.annotation.Scope;
62 import org.springframework.stereotype.Service;
63
64 /**
65  * Enables integration API interface with VNF Repository (VNFSDK).
66  * <ol>
67  *     <li>Get all the VNF Package Meta-data.</li>
68  *     <li>Download a VNF Package.</li>
69  *     <li>Import a VNF package to SDC catalog (Download & validate).</li>
70  * </ol>
71  *
72  * @version Amsterdam release (ONAP 1.0)
73  */
74 @Named
75 @Service("vnfPackageRepository")
76 @Scope(value = "prototype")
77 public class VnfPackageRepositoryImpl implements VnfPackageRepository {
78
79     private static final Logger LOGGER = LoggerFactory.getLogger(VnfPackageRepositoryImpl.class);
80     private static final Client CLIENT = trustSSLClient();
81
82     private static Client trustSSLClient() {
83         try {
84             SSLContext sslcontext = SSLContext.getInstance("TLS");
85             sslcontext.init(null, new TrustManager[]{new MyTrustManager()}, new java.security.SecureRandom());
86             return ClientBuilder.newBuilder().sslContext(sslcontext).hostnameVerifier((requestedHost, remoteServerSession)
87                     -> requestedHost.equalsIgnoreCase(remoteServerSession.getPeerHost())).build();
88
89         } catch (NoSuchAlgorithmException | KeyManagementException e) {
90             LOGGER.error("Failed to initialize SSL unsecure context", e);
91         }
92         return ClientBuilder.newClient();
93     }
94
95     private static class MyTrustManager implements X509TrustManager {
96         TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
97         private MyTrustManager() throws NoSuchAlgorithmException {
98         }
99
100         @Override
101         public X509Certificate[] getAcceptedIssuers() {
102             return new X509Certificate[] {};
103         }
104
105         @Override
106         public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
107             X509TrustManager x509Tm = getDefaultTrustManager(tmf);
108             if(x509Tm == null) {
109                 throw new CertificateException("No X509TrustManager found");
110             }
111             x509Tm.checkServerTrusted(certs, authType);
112         }
113
114         @Override
115         public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
116             X509TrustManager x509Tm = getDefaultTrustManager(tmf);
117             if(x509Tm == null) {
118                 throw new CertificateException("No X509TrustManager found");
119             }
120             x509Tm.checkClientTrusted(certs, authType);
121         }
122
123         private X509TrustManager getDefaultTrustManager(TrustManagerFactory tmf) {
124             try {
125                 tmf.init((KeyStore)null);
126             } catch (KeyStoreException e) {
127                 throw new IllegalStateException(e);
128             }
129             X509TrustManager x509Tm = null;
130             for(TrustManager tm: tmf.getTrustManagers())
131             {
132                 if(tm instanceof X509TrustManager) {
133                     x509Tm = (X509TrustManager) tm;
134                     break;
135                 }
136             }
137             return x509Tm;
138         }
139     }
140
141     private final Configuration config;
142
143     public VnfPackageRepositoryImpl(Configuration config) {
144         this.config = config;
145     }
146
147     public VnfPackageRepositoryImpl() {
148         this(new FileConfiguration());
149     }
150
151     @Override
152     public Response getVnfPackages(String vspId, String versionId, String user) {
153         LOGGER.debug("Get VNF Packages from Repository: {}", vspId);
154         final String getVnfPackageUri = config.getGetUri();
155         Response remoteResponse = CLIENT.target(getVnfPackageUri).request().get();
156         if (remoteResponse.getStatus() != Response.Status.OK.getStatusCode()) {
157             return handleUnexpectedStatus("querying VNF package metadata", getVnfPackageUri, remoteResponse);
158         }
159         LOGGER.debug("Response from VNF Repository: {}", remoteResponse);
160         return Response.ok(remoteResponse.readEntity(String.class)).build();
161     }
162
163     @Override
164     public Response importVnfPackage(String vspId, String versionId, String csarId, String user) {
165         LOGGER.debug("Import VNF Packages from Repository: {}", csarId);
166         final String downloadPackageUri = String.format(config.getDownloadUri(), csarId);
167         Response remoteResponse = CLIENT.target(downloadPackageUri).request().get();
168         if (remoteResponse.getStatus() != Response.Status.OK.getStatusCode()) {
169             return handleUnexpectedStatus("downloading VNF package", downloadPackageUri, remoteResponse);
170         }
171         LOGGER.debug("Response from VNF Repository for download package is success. URI={}", downloadPackageUri);
172         byte[] payload = remoteResponse.readEntity(String.class).getBytes(StandardCharsets.ISO_8859_1);
173         return uploadVnfPackage(vspId, versionId, csarId, payload);
174     }
175
176     private Response uploadVnfPackage(final String vspId, final String versionId, final String csarId, final byte[] payload) {
177         try {
178             final OrchestrationTemplateCandidateManager candidateManager = OrchestrationTemplateCandidateManagerFactory.getInstance()
179                 .createInterface();
180             final String filename = formatFilename(csarId);
181             final String fileExtension = getFileExtension(filename);
182             final OnboardPackageInfo onboardPackageInfo = new OnboardPackageInfo(getNetworkPackageName(filename), fileExtension,
183                 ByteBuffer.wrap(payload), OnboardingTypesEnum.getOnboardingTypesEnum(fileExtension));
184             final VspDetails vspDetails = new VspDetails(vspId, getVersion(vspId, versionId));
185             final UploadFileResponse response = candidateManager.upload(vspDetails, onboardPackageInfo);
186             final UploadFileResponseDto uploadFileResponse = new MapUploadFileResponseToUploadFileResponseDto()
187                 .applyMapping(response, UploadFileResponseDto.class);
188             return Response.ok(uploadFileResponse).build();
189         } catch (final Exception e) {
190             ErrorCode error = new GeneralErrorBuilder().build();
191             LOGGER.error("Exception while uploading package received from VNF Repository", new CoreException(error, e));
192             return generateInternalServerError(error);
193         }
194     }
195
196     @Override
197     public Response downloadVnfPackage(String vspId, String versionId, String csarId, String user) {
198         LOGGER.debug("Download VNF package from repository: csarId={}", csarId);
199         final String downloadPackageUri = String.format(config.getDownloadUri(), csarId);
200         Response remoteResponse = CLIENT.target(downloadPackageUri).request().get();
201         if (remoteResponse.getStatus() != Response.Status.OK.getStatusCode()) {
202             return handleUnexpectedStatus("downloading VNF package", downloadPackageUri, remoteResponse);
203         }
204         byte[] payload = remoteResponse.readEntity(String.class).getBytes(StandardCharsets.ISO_8859_1);
205         Response.ResponseBuilder response = Response.ok(payload);
206         response.header(CONTENT_DISPOSITION, "attachment; filename=" + formatFilename(csarId));
207         LOGGER.debug("Response from VNF Repository for download package is success. URI={}", downloadPackageUri);
208         return response.build();
209     }
210
211     private Version getVersion(String vspId, String versionId) {
212         VersioningManager versioningManager = VersioningManagerFactory.getInstance().createInterface();
213         return findVersion(versioningManager.list(vspId), versionId).orElse(new Version(versionId));
214     }
215
216     Optional<Version> findVersion(List<Version> versions, String requestedVersion) {
217         return versions.stream().filter(ver -> Objects.equals(ver.getId(), requestedVersion)).findAny();
218     }
219
220     private static Response handleUnexpectedStatus(String action, String uri, Response response) {
221         ErrorCode error = new GeneralErrorBuilder().build();
222         if (LOGGER.isErrorEnabled()) {
223             String body = response.hasEntity() ? response.readEntity(String.class) : "";
224             LOGGER.error("Unexpected response status while {}: URI={}, status={}, body={}", action, uri, response.getStatus(), body,
225                 new CoreException(error));
226         }
227         return generateInternalServerError(error);
228     }
229
230     private static Response generateInternalServerError(ErrorCode error) {
231         ErrorCodeAndMessage payload = new ErrorCodeAndMessage(Response.Status.INTERNAL_SERVER_ERROR, error);
232         return Response.serverError().entity(payload).build();
233     }
234
235     private static String formatFilename(String csarId) {
236         return "temp_" + csarId + ".csar";
237     }
238
239     interface Configuration {
240
241         String getGetUri();
242
243         String getDownloadUri();
244     }
245
246     static class FileConfiguration implements Configuration {
247
248         @Override
249         public String getGetUri() {
250             return LazyFileConfiguration.INSTANCE.getGetUri();
251         }
252
253         @Override
254         public String getDownloadUri() {
255             return LazyFileConfiguration.INSTANCE.getDownloadUri();
256         }
257
258         private static class LazyFileConfiguration implements Configuration {
259
260             private static final String CONFIG_NAMESPACE = "vnfrepo";
261             private static final String DEFAULT_HOST = "localhost";
262             private static final String DEFAULT_PORT = "8702";
263             private static final String DEFAULT_URI_PREFIX = "/onapapi/vnfsdk-marketplace/v1/PackageResource/csars";
264             private static final String DEFAULT_LIST_URI = DEFAULT_URI_PREFIX + "/";
265             private static final String DEFAULT_DOWNLOAD_URI = DEFAULT_URI_PREFIX + "/%s/files";
266             private static final LazyFileConfiguration INSTANCE = new LazyFileConfiguration();
267             private final String getUri;
268             private final String downloadUri;
269
270             private LazyFileConfiguration() {
271                 org.onap.config.api.Configuration config = ConfigurationManager.lookup();
272                 String host = readConfig(config, "vnfRepoHost", DEFAULT_HOST);
273                 String port = readConfig(config, "vnfRepoPort", DEFAULT_PORT);
274                 String listPackagesUri = readConfig(config, "getVnfUri", DEFAULT_LIST_URI);
275                 String downloadPackageUri = readConfig(config, "downloadVnfUri", DEFAULT_DOWNLOAD_URI);
276                 this.getUri = formatUri(host, port, listPackagesUri);
277                 this.downloadUri = formatUri(host, port, downloadPackageUri);
278             }
279
280             private String readConfig(org.onap.config.api.Configuration config, String key, String defaultValue) {
281                 try {
282                     String value = config.getAsString(CONFIG_NAMESPACE, key);
283                     return (value == null) ? defaultValue : value;
284                 } catch (Exception e) {
285                     LOGGER.error("Failed to read VNF repository configuration key '{}', default value '{}' will be used", key, defaultValue, e);
286                     return defaultValue;
287                 }
288             }
289
290             private static String formatUri(String host, String port, String path) {
291                 return "https://" + host + ":" + port + (path.startsWith("/") ? path : "/" + path);
292             }
293
294             public String getGetUri() {
295                 return getUri;
296             }
297
298             public String getDownloadUri() {
299                 return downloadUri;
300             }
301         }
302     }
303 }