2 * Copyright © 2017-2018 AT&T Intellectual Property.
3 * Copyright (C) 2019 Nordix Foundation
4 * Modifications Copyright © 2019 Huawei.
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 package org.onap.ccsdk.cds.blueprintsprocessor.rest.service
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.junit.After
27 import org.junit.Before
29 import org.junit.runner.RunWith
30 import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintPropertiesService
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.netty.NettyReactiveWebServerFactory
38 import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory
39 import org.springframework.boot.web.server.WebServer
40 import org.springframework.context.annotation.Bean
41 import org.springframework.http.HttpMethod
42 import org.springframework.http.server.reactive.HttpHandler
43 import org.springframework.security.config.web.server.ServerHttpSecurity
44 import org.springframework.security.core.userdetails.MapReactiveUserDetailsService
45 import org.springframework.security.core.userdetails.User
46 import org.springframework.security.core.userdetails.UserDetails
47 import org.springframework.security.web.server.SecurityWebFilterChain
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
62 @RunWith(SpringRunner::class)
63 @EnableAutoConfiguration(exclude = [DataSourceAutoConfiguration::class])
64 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
65 @ContextConfiguration(
66 classes = [BluePrintRestLibConfiguration::class, SampleController::class,
67 SecurityConfiguration::class,
68 BluePrintPropertyConfiguration::class, BluePrintPropertiesService::class]
74 "server.ssl.enabled=true",
75 "server.ssl.key-store=classpath:keystore.p12",
76 "server.ssl.key-store-password=changeit",
77 "server.ssl.keyStoreType=PKCS12",
78 "server.ssl.keyAlias=tomcat",
79 "blueprintsprocessor.restclient.sample.type=basic-auth",
80 "blueprintsprocessor.restclient.sample.url=http://127.0.0.1:8081",
81 "blueprintsprocessor.restclient.sample.username=admin",
82 "blueprintsprocessor.restclient.sample.password=jans",
83 "blueprintsprocessor.restclient.test.type=ssl-basic-auth",
84 "blueprintsprocessor.restclient.test.url=https://localhost:8443",
85 "blueprintsprocessor.restclient.test.username=admin",
86 "blueprintsprocessor.restclient.test.password=jans",
87 "blueprintsprocessor.restclient.test.keyStoreInstance=PKCS12",
88 "blueprintsprocessor.restclient.test.sslTrust=src/test/resources/keystore.p12",
89 "blueprintsprocessor.restclient.test.sslTrustPassword=changeit"
92 class RestClientServiceTest {
95 lateinit var bluePrintRestLibPropertyService: BluePrintRestLibPropertyService
98 lateinit var httpHandler: HttpHandler
100 lateinit var http: WebServer
102 fun localPort() = http.port
106 // Second Http server required for non-SSL requests to be processed along with the https server.
107 val factory: ReactiveWebServerFactory = NettyReactiveWebServerFactory(8081)
108 this.http = factory.getWebServer(this.httpHandler)
119 val restClientService = bluePrintRestLibPropertyService
120 .blueprintWebClientService("sample")
121 val response = restClientService.exchangeResource(
122 HttpMethod.PATCH.name, "/sample/name", ""
125 "Patch request successful", response.body,
126 "failed to get patch response"
132 val restClientService = bluePrintRestLibPropertyService
133 .blueprintWebClientService("sample")
134 val headers = mutableMapOf<String, String>()
135 headers["X-Transaction-Id"] = "1234"
136 val response = restClientService.exchangeResource(
140 assertNotNull(response.body, "failed to get response")
144 fun testSimpleBasicAuth() {
145 val json: String = "{\n" +
146 " \"type\" : \"basic-auth\",\n" +
147 " \"url\" : \"http://localhost:8081\",\n" +
148 " \"username\" : \"admin\",\n" +
149 " \"password\" : \"jans\"\n" +
151 val mapper = ObjectMapper()
152 val actualObj: JsonNode = mapper.readTree(json)
153 val restClientService = bluePrintRestLibPropertyService
154 .blueprintWebClientService(actualObj)
155 lateinit var res: String
157 val get = async(start = CoroutineStart.LAZY) {
158 restClientService.exchangeNB(
166 assertNotNull(res, "failed to get response")
167 assertEquals(res, "Basic request arrived successfully")
171 fun testSampleAaiReq() {
172 val restClientService = bluePrintRestLibPropertyService
173 .blueprintWebClientService("test")
174 val headers = mutableMapOf<String, String>()
175 headers["X-TransactionId"] = "9999"
176 headers["X-FromAppId"] = "AAI"
178 " \"customer\": {\n" +
179 " \"global-customer-id\": \"ONSDEMOBJHKCustomer\",\n" +
180 " \"subscriber-name\": \"ONSDEMOBJHKCustomer\",\n" +
181 " \"subscriber-type\": \"CUST\",\n" +
182 " \"resource-version\": \"1552985011163\"\n" +
185 lateinit var res1: Customer
186 lateinit var res2: Customer
187 lateinit var res3: String
188 lateinit var res4: String
189 lateinit var res5: String
190 lateinit var res6: String
192 val get1 = async(start = CoroutineStart.LAZY) {
193 restClientService.exchangeNB(
195 "/sample/aai/v14/business/customers", "", headers,
200 val get2 = async(start = CoroutineStart.LAZY) {
201 restClientService.exchangeNB(
203 "/sample/aai/v14/business/customers", "", headers,
208 val post = async(start = CoroutineStart.LAZY) {
209 restClientService.exchangeNB(
210 HttpMethod.POST.name,
211 "/sample/aai/v14/business/customers", post1, headers,
216 val put = async(start = CoroutineStart.LAZY) {
217 restClientService.exchangeNB(
219 "/sample/aai/v14/business/customers", post1, headers,
224 val patch = async(start = CoroutineStart.LAZY) {
225 restClientService.exchangeNB(
226 HttpMethod.PATCH.name,
227 "/sample/aai/v14/business/customers", post1, headers,
232 val delete = async(start = CoroutineStart.LAZY) {
233 restClientService.exchangeNB(
234 HttpMethod.DELETE.name,
235 "/sample/aai/v14/business/customers", "", headers,
251 res6 = delete.await()
253 assertNotNull(res1, "failed to get response")
254 assertNotNull(res2, "failed to get response")
255 assertEquals(res1.id, "ONSDEMOBJHKCustomer")
256 assertEquals(res1.name, "ONSDEMOBJHKCustomer")
257 assertEquals(res1.type, "CUST")
258 assertEquals(res1.resource, "1552985011163")
259 assertEquals(res2.id, "ONSDEMOBJHKCustomer")
260 assertEquals(res2.name, "ONSDEMOBJHKCustomer")
261 assertEquals(res2.type, "CUST")
262 assertEquals(res2.resource, "1552985011163")
263 assertEquals(res3, "The message is successfully posted")
264 assertEquals(res4, "The put request is success")
265 assertEquals(res5, "The patch request is success")
266 assertEquals(res6, "The message is successfully deleted")
271 * Sample controller code for testing both http and https requests.
274 @RequestMapping("/sample")
275 open class SampleController {
278 fun getName(): String = "Sample Controller"
280 @PatchMapping("/name")
281 fun patchName(): String = "Patch request successful"
283 @GetMapping("/basic")
284 fun getBasic(): String = "Basic request arrived successfully"
286 @GetMapping("/aai/v14/business/customers")
288 @RequestHeader(name = "X-TransactionId", required = true)
290 @RequestHeader(name = "X-FromAppId", required = true)
293 if (transId != "9999" || appId != "AAI") {
297 " \"id\": \"ONSDEMOBJHKCustomer\",\n" +
298 " \"name\": \"ONSDEMOBJHKCustomer\",\n" +
299 " \"type\": \"CUST\",\n" +
300 " \"resource\": \"1552985011163\"\n" +
304 @PostMapping("/aai/v14/business/customers")
305 fun postAaiCustomers(
306 @RequestHeader(name = "X-TransactionId", required = true)
308 @RequestHeader(name = "X-FromAppId", required = true)
311 if (transId != "9999" || appId != "AAI") {
314 return "The message is successfully posted"
317 @PutMapping("/aai/v14/business/customers")
319 @RequestHeader(name = "X-TransactionId", required = true)
321 @RequestHeader(name = "X-FromAppId", required = true)
324 if (transId != "9999" || appId != "AAI") {
327 return "The put request is success"
330 @PatchMapping("/aai/v14/business/customers")
331 fun patchAaiCustomers(
332 @RequestHeader(name = "X-TransactionId", required = true)
334 @RequestHeader(name = "X-FromAppId", required = true)
337 if (transId != "9999" || appId != "AAI") {
340 return "The patch request is success"
343 @DeleteMapping("/aai/v14/business/customers")
344 fun deleteAaiCustomers(
345 @RequestHeader(name = "X-TransactionId", required = true)
347 @RequestHeader(name = "X-FromAppId", required = true)
350 if (transId != "9999" || appId != "AAI") {
353 return "The message is successfully deleted"
358 * Security configuration required for basic authentication with username and
359 * password for any request in the server.
361 open class SecurityConfiguration {
364 open fun userDetailsService(): MapReactiveUserDetailsService {
365 val user: UserDetails = User.withDefaultPasswordEncoder()
370 return MapReactiveUserDetailsService(user)
374 open fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
377 .authorizeExchange().anyExchange().authenticated()
384 * Data class required for response