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