17c68e41ba8de3fb8a02d27c31640ee6f836819a
[vfc/nfvo/driver/vnfm/svnfm.git] / nokiav2 / driver / src / test / java / org / onap / vfc / nfvo / driver / vnfm / svnfm / nokia / vnfm / TestCbamSecurityProvider.java
1 /*
2  * Copyright 2016-2017, Nokia Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm;
18
19 import com.google.gson.Gson;
20 import com.google.gson.JsonParseException;
21 import com.google.gson.annotations.SerializedName;
22 import com.nokia.cbam.lcn.v32.JSON;
23 import io.reactivex.Observable;
24 import okhttp3.OkHttpClient;
25 import okhttp3.RequestBody;
26 import okhttp3.ResponseBody;
27 import org.junit.After;
28 import org.junit.Before;
29 import org.junit.Test;
30 import retrofit2.Converter;
31 import retrofit2.Retrofit;
32 import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
33 import retrofit2.converter.gson.GsonConverterFactory;
34 import retrofit2.http.GET;
35 import retrofit2.http.Headers;
36
37 import javax.xml.bind.annotation.XmlAccessType;
38 import javax.xml.bind.annotation.XmlAccessorType;
39 import javax.xml.bind.annotation.XmlElement;
40 import javax.xml.bind.annotation.XmlRootElement;
41 import java.io.IOException;
42 import java.lang.annotation.Annotation;
43 import java.lang.reflect.Type;
44 import java.nio.file.Files;
45 import java.nio.file.Path;
46 import java.nio.file.Paths;
47 import java.util.Base64;
48
49 import static junit.framework.TestCase.*;
50 import static org.springframework.test.util.ReflectionTestUtils.setField;
51
52 interface TestService {
53     @Headers({
54             "Content-Type:application/json"
55     })
56     @GET("subscriptions")
57     Observable<TestResource> subscriptionsGet();
58 }
59
60 @XmlRootElement(name = "Subscription")
61 @XmlAccessorType(XmlAccessType.FIELD)
62 class TestResource {
63     @XmlElement(name = "id")
64     @SerializedName("id")
65     public String id = null;
66 }
67
68 class GsonCustomConverterFactory extends Converter.Factory {
69     private final Gson gson;
70     private final GsonConverterFactory gsonConverterFactory;
71
72     private GsonCustomConverterFactory(Gson gson) {
73         if (gson == null)
74             throw new NullPointerException("gson == null");
75         this.gson = gson;
76         this.gsonConverterFactory = GsonConverterFactory.create(gson);
77     }
78
79     public static GsonCustomConverterFactory create(Gson gson) {
80         return new GsonCustomConverterFactory(gson);
81     }
82
83     @Override
84     public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
85         if (type.equals(String.class))
86             return new GsonResponseBodyConverterToString<Object>(gson, type);
87         else
88             return gsonConverterFactory.responseBodyConverter(type, annotations, retrofit);
89     }
90
91     @Override
92     public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
93         return gsonConverterFactory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, retrofit);
94     }
95 }
96
97 class GsonResponseBodyConverterToString<T> implements Converter<ResponseBody, T> {
98     private final Gson gson;
99     private final Type type;
100
101     GsonResponseBodyConverterToString(Gson gson, Type type) {
102         this.gson = gson;
103         this.type = type;
104     }
105
106     @Override
107     public T convert(ResponseBody value) throws IOException {
108         String returned = value.string();
109         try {
110             return gson.fromJson(returned, type);
111         } catch (JsonParseException e) {
112             return (T) returned;
113         }
114     }
115 }
116
117 public class TestCbamSecurityProvider extends TestBase {
118     CbamSecurityProvider securityProvider = new CbamSecurityProvider() {
119     };
120
121     HttpTestServer testServer = new HttpTestServer();
122     String url;
123
124     @Before
125     public void init() throws Exception {
126         setField(securityProvider, "skipCertificateVerification", true);
127         setField(securityProvider, "skipHostnameVerification", true);
128         testServer = new HttpTestServer();
129         testServer.start();
130         url = testServer._server.getURI().toString();
131     }
132
133     @After
134     public void testServer() throws Exception {
135         testServer.stop();
136     }
137
138     /**
139      * test skipping certificate and skipping hostname verification
140      */
141     @Test
142     public void testSkipHostAndSkipCertifiacateVerification() throws Exception {
143         setField(securityProvider, "skipCertificateVerification", true);
144         setField(securityProvider, "skipHostnameVerification", true);
145         //when
146         TestResource testResource = fireRequest();
147         //verify
148         assertEquals("1234", testResource.id);
149         //when
150         securityProvider.buildTrustManager().checkClientTrusted(null, null);
151         //verify
152         //no security exception is thrown
153     }
154
155     /**
156      * test skipping certificate and doing hostname verification
157      */
158     @Test
159     public void testHostAndSkipCertifiacateVerification() throws Exception {
160         setField(securityProvider, "skipCertificateVerification", true);
161         setField(securityProvider, "skipHostnameVerification", false);
162         url = url.replace("127.0.0.1", "localhost");
163         TestResource testResource = fireRequest();
164         assertEquals("1234", testResource.id);
165     }
166
167     /**
168      * test skipping certificate and doing hostname verification
169      * (if hostname is invalid exception is propagated)
170      */
171     @Test
172     public void testHostAndSkipCertifiacateVerificationNegativeCase() throws Exception {
173         setField(securityProvider, "skipCertificateVerification", true);
174         setField(securityProvider, "skipHostnameVerification", false);
175         //url = url.replace("127.0.0.1", "localhost");
176         try {
177             fireRequest();
178             fail();
179         } catch (Exception e) {
180             assertEquals(javax.net.ssl.SSLPeerUnverifiedException.class, e.getCause().getClass());
181             assertTrue(e.getCause().getMessage().contains("Hostname 127.0.0.1 not verified"));
182         }
183     }
184
185     /**
186      * test certificate and hostname verification
187      */
188     @Test
189     public void testHostAndCertifiacateVerification() throws Exception {
190         Path jksPath = Paths.get(TestCbamTokenProvider.class.getResource("/unittests/localhost.cert.pem").toURI());
191         String cert = Base64.getEncoder().encodeToString(Files.readAllBytes(jksPath));
192         setField(securityProvider, "trustedCertificates", cert);
193         setField(securityProvider, "skipCertificateVerification", false);
194         setField(securityProvider, "skipHostnameVerification", false);
195         url = url.replace("127.0.0.1", "localhost");
196         TestResource testResource = fireRequest();
197         assertEquals("1234", testResource.id);
198     }
199
200     /**
201      * test certificate and hostname verification
202      * (not trusted certificate)
203      */
204     @Test
205     public void testHostAndCertifiacateVerificationNegative() throws Exception {
206         Path jksPath = Paths.get(TestCbamTokenProvider.class.getResource("/unittests/sample.cert.pem").toURI());
207         String cert = Base64.getEncoder().encodeToString(Files.readAllBytes(jksPath));
208         setField(securityProvider, "trustedCertificates", cert);
209         setField(securityProvider, "skipCertificateVerification", false);
210         setField(securityProvider, "skipHostnameVerification", false);
211         url = url.replace("127.0.0.1", "localhost");
212         try {
213             fireRequest();
214             fail();
215         } catch (Exception e) {
216             assertEquals(javax.net.ssl.SSLHandshakeException.class, e.getCause().getClass());
217             assertTrue(e.getCause().getMessage().contains("unable to find valid certification path to requested target"));
218         }
219     }
220
221     /**
222      * test certificate and hostname verification
223      */
224     @Test
225     public void testSkipHostAndCertifiacateVerification() throws Exception {
226         Path jksPath = Paths.get(TestCbamTokenProvider.class.getResource("/unittests/localhost.cert.pem").toURI());
227         String cert = Base64.getEncoder().encodeToString(Files.readAllBytes(jksPath));
228         setField(securityProvider, "trustedCertificates", cert);
229         setField(securityProvider, "skipCertificateVerification", false);
230         setField(securityProvider, "skipHostnameVerification", true);
231         //url = url.replace("127.0.0.1", "localhost");
232         TestResource testResource = fireRequest();
233         assertEquals("1234", testResource.id);
234     }
235
236     /**
237      * empty trusted pem results in error if verification is required
238      */
239     @Test
240     public void testEmptyTrustStoreWhenCheckingIsRequired() throws Exception {
241         setField(securityProvider, "trustedCertificates", "");
242         setField(securityProvider, "skipCertificateVerification", false);
243         try {
244             securityProvider.buildTrustManager();
245             fail();
246         } catch (Exception e) {
247             assertEquals("If the skipCertificateVerification is set to false (default) the trustedCertificates can not be empty", e.getMessage());
248         }
249     }
250
251     /**
252      * invalid PEM results in fast fail error
253      */
254     @Test
255     public void testInvalidPem() throws Exception {
256         setField(securityProvider, "trustedCertificates", "______");
257         setField(securityProvider, "skipCertificateVerification", false);
258         try {
259             securityProvider.buildTrustManager();
260             fail();
261         } catch (Exception e) {
262             assertEquals("The trustedCertificates must be a base64 encoded collection of PEM certificates", e.getMessage());
263         }
264     }
265
266     /**
267      * invalid PEM results in fast fail error
268      */
269     @Test
270     public void testEmptyInvalidPem() throws Exception {
271         setField(securityProvider, "trustedCertificates", "a3VrdQo=");
272         setField(securityProvider, "skipCertificateVerification", false);
273         try {
274             securityProvider.buildTrustManager();
275             fail();
276         } catch (Exception e) {
277             assertEquals("No certificate can be extracted from kuku\n", e.getMessage());
278         }
279     }
280
281     /**
282      * bad certificate content results in fast fail error
283      */
284     @Test
285     public void testEmptyInvalidPemContent() throws Exception {
286         String badCert = "-----BEGIN CERTIFICATE-----\nXXXXXX\n-----END CERTIFICATE-----";
287         setField(securityProvider, "trustedCertificates", Base64.getEncoder().encodeToString(badCert.getBytes()));
288         setField(securityProvider, "skipCertificateVerification", false);
289         try {
290             securityProvider.buildTrustManager();
291             fail();
292         } catch (Exception e) {
293             assertEquals("Unable to create keystore", e.getMessage());
294         }
295     }
296
297     /**
298      * bad certificate content results in fast fail error for SSL socket factory
299      */
300     @Test
301     public void testEmptyInvalidPemContentSSl() throws Exception {
302         String badCert = "-----BEGIN CERTIFICATE-----\nXXXXXX\n-----END CERTIFICATE-----";
303         setField(securityProvider, "trustedCertificates", Base64.getEncoder().encodeToString(badCert.getBytes()));
304         setField(securityProvider, "skipCertificateVerification", false);
305         try {
306             securityProvider.buildSSLSocketFactory();
307             fail();
308         } catch (Exception e) {
309             assertEquals("Unable to create SSL socket factory", e.getMessage());
310         }
311     }
312
313     private TestResource fireRequest() {
314         OkHttpClient client =
315                 new OkHttpClient.Builder()
316                         .sslSocketFactory(securityProvider.buildSSLSocketFactory(), securityProvider.buildTrustManager())
317                         .hostnameVerifier(securityProvider.buildHostnameVerifier()).build();
318         TestService test1 = new Retrofit.Builder().baseUrl(url).client(client)
319                 .addConverterFactory(GsonCustomConverterFactory.create(new JSON().getGson()))
320                 .addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build().create(TestService.class);
321         testServer.respones.add("{ \"id\" : \"1234\" } ");
322         testServer.codes.add(200);
323         TestService test = test1;
324         return test.subscriptionsGet().blockingFirst();
325     }
326
327 }