93eb12dd02b3a41e2c4e17b126ac436f8b884252
[ccsdk/cds.git] /
1 /*
2  * Copyright © 2017-2018 AT&T Intellectual Property.
3  * Copyright (C) 2019 Nordix Foundation
4  * Modifications Copyright © 2019 Huawei.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 package org.onap.ccsdk.cds.blueprintsprocessor.rest.service
20
21 import com.fasterxml.jackson.databind.JsonNode
22 import com.fasterxml.jackson.databind.ObjectMapper
23 import kotlinx.coroutines.CoroutineStart
24 import kotlinx.coroutines.async
25 import kotlinx.coroutines.runBlocking
26 import org.apache.catalina.connector.Connector
27 import org.junit.Ignore
28 import org.junit.Test
29 import org.junit.runner.RunWith
30 import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintProperties
31 import org.onap.ccsdk.cds.blueprintsprocessor.core.BlueprintPropertyConfiguration
32 import org.onap.ccsdk.cds.blueprintsprocessor.rest.BluePrintRestLibConfiguration
33 import org.springframework.beans.factory.annotation.Autowired
34 import org.springframework.boot.autoconfigure.EnableAutoConfiguration
35 import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
36 import org.springframework.boot.test.context.SpringBootTest
37 import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
38 import org.springframework.boot.web.servlet.server.ServletWebServerFactory
39 import org.springframework.context.annotation.Bean
40 import org.springframework.context.annotation.Configuration
41 import org.springframework.http.HttpMethod
42 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
43 import org.springframework.security.config.annotation.web.builders.HttpSecurity
44 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
45 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
46 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
47 import org.springframework.security.crypto.password.PasswordEncoder
48 import org.springframework.stereotype.Component
49 import org.springframework.test.context.ContextConfiguration
50 import org.springframework.test.context.TestPropertySource
51 import org.springframework.test.context.junit4.SpringRunner
52 import org.springframework.web.bind.annotation.DeleteMapping
53 import org.springframework.web.bind.annotation.GetMapping
54 import org.springframework.web.bind.annotation.PatchMapping
55 import org.springframework.web.bind.annotation.PostMapping
56 import org.springframework.web.bind.annotation.PutMapping
57 import org.springframework.web.bind.annotation.RequestHeader
58 import org.springframework.web.bind.annotation.RequestMapping
59 import org.springframework.web.bind.annotation.RestController
60 import kotlin.test.assertEquals
61 import kotlin.test.assertNotNull
62
63 @RunWith(SpringRunner::class)
64 @EnableAutoConfiguration(exclude = [DataSourceAutoConfiguration::class])
65 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
66 @ContextConfiguration(classes = [BluePrintRestLibConfiguration::class,
67     BlueprintPropertyConfiguration::class,
68     SampleController::class, BluePrintProperties::class,
69     BluePrintProperties::class])
70 @TestPropertySource(properties =
71 [
72     "server.port=8443",
73     "server.ssl.enabled=true",
74     "server.ssl.key-store=classpath:keystore.p12",
75     "server.ssl.key-store-password=changeit",
76     "server.ssl.keyStoreType=PKCS12",
77     "server.ssl.keyAlias=tomcat",
78     "blueprintsprocessor.restclient.sample.type=basic-auth",
79     "blueprintsprocessor.restclient.sample.url=http://127.0.0.1:8080",
80     "blueprintsprocessor.restclient.sample.username=admin",
81     "blueprintsprocessor.restclient.sample.password=jans",
82     "blueprintsprocessor.restclient.test.type=ssl-basic-auth",
83     "blueprintsprocessor.restclient.test.url=https://localhost:8443",
84     "blueprintsprocessor.restclient.test.username=admin",
85     "blueprintsprocessor.restclient.test.password=jans",
86     "blueprintsprocessor.restclient.test.keyStoreInstance=PKCS12",
87     "blueprintsprocessor.restclient.test.sslTrust=src/test/resources/keystore.p12",
88     "blueprintsprocessor.restclient.test.sslTrustPassword=changeit"
89 ])
90 @Ignore
91 class RestClientServiceTest {
92
93     @Autowired
94     lateinit var bluePrintRestLibPropertyService: BluePrintRestLibPropertyService
95
96     @Test
97     fun testPatch() {
98         val restClientService = bluePrintRestLibPropertyService
99                 .blueprintWebClientService("sample")
100         val response = restClientService.exchangeResource(
101                 HttpMethod.PATCH.name, "/sample/name", "")
102         assertEquals("Patch request successful", response,
103                 "failed to get patch response")
104     }
105
106     @Test
107     fun testBaseAuth() {
108         val restClientService = bluePrintRestLibPropertyService
109                 .blueprintWebClientService("sample")
110         val headers = mutableMapOf<String, String>()
111         headers["X-Transaction-Id"] = "1234"
112         val response = restClientService.exchangeResource(HttpMethod.GET.name,
113                 "/sample/name", "")
114         assertNotNull(response, "failed to get response")
115     }
116
117     @Test
118     fun testSimpleBasicAuth() {
119         val json: String = "{\n" +
120                 "  \"type\" : \"basic-auth\",\n" +
121                 "  \"url\" : \"http://localhost:8080\",\n" +
122                 "  \"username\" : \"admin\",\n" +
123                 "  \"password\" : \"jans\"\n" +
124                 "}"
125         val mapper = ObjectMapper()
126         val actualObj: JsonNode = mapper.readTree(json)
127         val restClientService = bluePrintRestLibPropertyService
128                 .blueprintWebClientService(actualObj)
129         lateinit var res:String
130         runBlocking {
131             val get = async(start = CoroutineStart.LAZY) {
132                 restClientService.exchangeNB(HttpMethod.GET.name,
133                         "/sample/basic", "")}
134             get.start()
135             res = get.await()
136         }
137         assertNotNull(res, "failed to get response")
138         assertEquals(res, "Basic request arrived successfully")
139     }
140
141     @Test
142     fun testSampleAaiReq() {
143         val restClientService = bluePrintRestLibPropertyService
144                 .blueprintWebClientService("test")
145         val headers = mutableMapOf<String, String>()
146         headers["X-TransactionId"] = "9999"
147         headers["X-FromAppId"] = "AAI"
148         val post1 = "{\n" +
149                 "  \"customer\": {\n" +
150                 "    \"global-customer-id\": \"ONSDEMOBJHKCustomer\",\n" +
151                 "    \"subscriber-name\": \"ONSDEMOBJHKCustomer\",\n" +
152                 "    \"subscriber-type\": \"CUST\",\n" +
153                 "    \"resource-version\": \"1552985011163\"\n" +
154                 "  }\n" +
155                 "}"
156         lateinit var res1: Customer
157         lateinit var res2: Customer
158         lateinit var res3: String
159         lateinit var res4: String
160         lateinit var res5: String
161         lateinit var res6: String
162         runBlocking {
163             val get1 = async(start = CoroutineStart.LAZY) {
164                 restClientService.exchangeNB(HttpMethod.GET.name,
165                         "/sample/aai/v14/business/customers", "", headers,
166                         Customer::class.java)}
167
168             val get2 = async(start = CoroutineStart.LAZY) {
169                 restClientService.exchangeNB(HttpMethod.GET.name,
170                         "/sample/aai/v14/business/customers", "", headers,
171                         Customer::class.java)}
172
173             val post = async(start = CoroutineStart.LAZY) {
174                 restClientService.exchangeNB(HttpMethod.POST.name,
175                         "/sample/aai/v14/business/customers", post1, headers,
176                         String::class.java)}
177
178             val put = async(start = CoroutineStart.LAZY) {
179                 restClientService.exchangeNB(HttpMethod.PUT.name,
180                         "/sample/aai/v14/business/customers", post1, headers,
181                         String::class.java)}
182
183             val patch = async(start = CoroutineStart.LAZY) {
184                 restClientService.exchangeNB(HttpMethod.PATCH.name,
185                         "/sample/aai/v14/business/customers", post1, headers,
186                         String::class.java)}
187
188             val delete = async(start = CoroutineStart.LAZY) {
189                 restClientService.exchangeNB(HttpMethod.DELETE.name,
190                         "/sample/aai/v14/business/customers", "", headers,
191                         String::class.java)}
192
193             get1.start()
194             get2.start()
195             post.start()
196             put.start()
197             patch.start()
198             delete.start()
199             res1 = get1.await()
200             res2 = get2.await()
201             res3 = post.await()
202             res4 = put.await()
203             res5 = patch.await()
204             res6 = delete.await()
205         }
206         assertNotNull(res1, "failed to get response")
207         assertNotNull(res2, "failed to get response")
208         assertEquals(res1.id, "ONSDEMOBJHKCustomer")
209         assertEquals(res1.name, "ONSDEMOBJHKCustomer")
210         assertEquals(res1.type, "CUST")
211         assertEquals(res1.resource, "1552985011163")
212         assertEquals(res2.id, "ONSDEMOBJHKCustomer")
213         assertEquals(res2.name, "ONSDEMOBJHKCustomer")
214         assertEquals(res2.type, "CUST")
215         assertEquals(res2.resource, "1552985011163")
216         assertEquals(res3, "The message is successfully posted")
217         assertEquals(res4, "The put request is success")
218         assertEquals(res5, "The patch request is success")
219         assertEquals(res6, "The message is successfully deleted")
220     }
221 }
222
223 /**
224  * Sample controller code for testing both http and https requests.
225  */
226 @RestController
227 @RequestMapping("/sample")
228 open class SampleController {
229
230     @GetMapping("/name")
231     fun getName(): String = "Sample Controller"
232
233     @PatchMapping("/name")
234     fun patchName(): String = "Patch request successful"
235
236     @GetMapping("/basic")
237     fun getBasic(): String = "Basic request arrived successfully"
238
239
240     @GetMapping("/aai/v14/business/customers")
241     fun getAaiCustomers(
242             @RequestHeader(name = "X-TransactionId", required = true)
243             transId: String,
244             @RequestHeader(name = "X-FromAppId", required = true)
245             appId: String) : String {
246         if (transId != "9999" || appId != "AAI") {
247             return ""
248         }
249         return "{\n" +
250                 "  \"id\": \"ONSDEMOBJHKCustomer\",\n" +
251                 "  \"name\": \"ONSDEMOBJHKCustomer\",\n" +
252                 "  \"type\": \"CUST\",\n" +
253                 "  \"resource\": \"1552985011163\"\n" +
254                 "}"
255     }
256
257     @PostMapping("/aai/v14/business/customers")
258     fun postAaiCustomers(
259             @RequestHeader(name = "X-TransactionId", required = true)
260             transId: String,
261             @RequestHeader(name = "X-FromAppId", required = true)
262             appId: String) : String {
263         if (transId != "9999" || appId != "AAI") {
264             return ""
265         }
266         return "The message is successfully posted"
267     }
268
269
270     @PutMapping("/aai/v14/business/customers")
271     fun putAaiCustomers(
272             @RequestHeader(name = "X-TransactionId", required = true)
273             transId: String,
274             @RequestHeader(name = "X-FromAppId", required = true)
275             appId: String) : String {
276         if (transId != "9999" || appId != "AAI") {
277             return ""
278         }
279         return "The put request is success"
280     }
281
282     @PatchMapping("/aai/v14/business/customers")
283     fun patchAaiCustomers(
284             @RequestHeader(name = "X-TransactionId", required = true)
285             transId: String,
286             @RequestHeader(name = "X-FromAppId", required = true)
287             appId: String) : String {
288         if (transId != "9999" || appId != "AAI") {
289             return ""
290         }
291         return "The patch request is success"
292     }
293
294     @DeleteMapping("/aai/v14/business/customers")
295     fun deleteAaiCustomers(
296             @RequestHeader(name = "X-TransactionId", required = true)
297             transId: String,
298             @RequestHeader(name = "X-FromAppId", required = true)
299             appId: String) : String {
300         if (transId != "9999" || appId != "AAI") {
301             return ""
302         }
303         return "The message is successfully deleted"
304     }
305 }
306
307 /**
308  * Security configuration required for basic authentication with username and
309  * password for any request in the server.
310  */
311 @Configuration
312 @EnableWebSecurity
313 open class SecurityConfig : WebSecurityConfigurerAdapter() {
314
315     @Throws(Exception::class)
316     override fun configure(http: HttpSecurity) {
317         http
318                 .csrf().disable()
319                 .authorizeRequests().anyRequest().authenticated()
320                 .and()
321                 .httpBasic()
322     }
323
324     @Autowired
325     @Throws(Exception::class)
326     open fun configureGlobal(auth: AuthenticationManagerBuilder) {
327         auth.inMemoryAuthentication()
328                 .withUser("admin")
329                 .password(passwordEncoder().encode("jans"))
330                 .roles("USER")
331     }
332
333     @Bean
334     open fun passwordEncoder(): PasswordEncoder {
335         return BCryptPasswordEncoder()
336     }
337 }
338
339 /**
340  * Http server required for http request to be processed along with the https
341  * server.
342  */
343 @Component
344 class HttpServer {
345     @Bean
346     fun servletContainer(): ServletWebServerFactory {
347
348         val connector = Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL)
349         connector.setPort(8080)
350
351         val tomcat = TomcatServletWebServerFactory()
352         tomcat.addAdditionalTomcatConnectors(connector)
353         return tomcat
354     }
355 }
356
357 /**
358  * Data class required for response
359  */
360 data class Customer(
361        val id: String,
362        val name: String,
363        val type: String,
364        val resource: String)