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.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.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.*
52 import kotlin.test.assertEquals
53 import kotlin.test.assertNotNull
55 @RunWith(SpringRunner::class)
56 @EnableAutoConfiguration(exclude = [DataSourceAutoConfiguration::class])
57 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
58 @ContextConfiguration(classes = [BluePrintRestLibConfiguration::class, SampleController::class,
59 SecurityConfiguration::class,
60 BlueprintPropertyConfiguration::class, BluePrintProperties::class])
61 @TestPropertySource(properties =
64 "server.ssl.enabled=true",
65 "server.ssl.key-store=classpath:keystore.p12",
66 "server.ssl.key-store-password=changeit",
67 "server.ssl.keyStoreType=PKCS12",
68 "server.ssl.keyAlias=tomcat",
69 "blueprintsprocessor.restclient.sample.type=basic-auth",
70 "blueprintsprocessor.restclient.sample.url=http://127.0.0.1:8081",
71 "blueprintsprocessor.restclient.sample.username=admin",
72 "blueprintsprocessor.restclient.sample.password=jans",
73 "blueprintsprocessor.restclient.test.type=ssl-basic-auth",
74 "blueprintsprocessor.restclient.test.url=https://localhost:8443",
75 "blueprintsprocessor.restclient.test.username=admin",
76 "blueprintsprocessor.restclient.test.password=jans",
77 "blueprintsprocessor.restclient.test.keyStoreInstance=PKCS12",
78 "blueprintsprocessor.restclient.test.sslTrust=src/test/resources/keystore.p12",
79 "blueprintsprocessor.restclient.test.sslTrustPassword=changeit"
81 class RestClientServiceTest {
84 lateinit var bluePrintRestLibPropertyService: BluePrintRestLibPropertyService
87 lateinit var httpHandler : HttpHandler
89 lateinit var http : WebServer
91 fun localPort() = http.port
95 // Second Http server required for non-SSL requests to be processed along with the https server.
96 val factory: ReactiveWebServerFactory = NettyReactiveWebServerFactory(8081)
97 this.http = factory.getWebServer(this.httpHandler)
108 val restClientService = bluePrintRestLibPropertyService
109 .blueprintWebClientService("sample")
110 val response = restClientService.exchangeResource(
111 HttpMethod.PATCH.name, "/sample/name", "")
112 assertEquals("Patch request successful", response.body,
113 "failed to get patch response")
118 val restClientService = bluePrintRestLibPropertyService
119 .blueprintWebClientService("sample")
120 val headers = mutableMapOf<String, String>()
121 headers["X-Transaction-Id"] = "1234"
122 val response = restClientService.exchangeResource(HttpMethod.GET.name,
124 assertNotNull(response.body, "failed to get response")
128 fun testSimpleBasicAuth() {
129 val json: String = "{\n" +
130 " \"type\" : \"basic-auth\",\n" +
131 " \"url\" : \"http://localhost:8081\",\n" +
132 " \"username\" : \"admin\",\n" +
133 " \"password\" : \"jans\"\n" +
135 val mapper = ObjectMapper()
136 val actualObj: JsonNode = mapper.readTree(json)
137 val restClientService = bluePrintRestLibPropertyService
138 .blueprintWebClientService(actualObj)
139 lateinit var res:String
141 val get = async(start = CoroutineStart.LAZY) {
142 restClientService.exchangeNB(HttpMethod.GET.name,
143 "/sample/basic", "").body}
147 assertNotNull(res, "failed to get response")
148 assertEquals(res, "Basic request arrived successfully")
152 fun testSampleAaiReq() {
153 val restClientService = bluePrintRestLibPropertyService
154 .blueprintWebClientService("test")
155 val headers = mutableMapOf<String, String>()
156 headers["X-TransactionId"] = "9999"
157 headers["X-FromAppId"] = "AAI"
159 " \"customer\": {\n" +
160 " \"global-customer-id\": \"ONSDEMOBJHKCustomer\",\n" +
161 " \"subscriber-name\": \"ONSDEMOBJHKCustomer\",\n" +
162 " \"subscriber-type\": \"CUST\",\n" +
163 " \"resource-version\": \"1552985011163\"\n" +
166 lateinit var res1: Customer
167 lateinit var res2: Customer
168 lateinit var res3: String
169 lateinit var res4: String
170 lateinit var res5: String
171 lateinit var res6: String
173 val get1 = async(start = CoroutineStart.LAZY) {
174 restClientService.exchangeNB(HttpMethod.GET.name,
175 "/sample/aai/v14/business/customers", "", headers,
176 Customer::class.java).body}
178 val get2 = async(start = CoroutineStart.LAZY) {
179 restClientService.exchangeNB(HttpMethod.GET.name,
180 "/sample/aai/v14/business/customers", "", headers,
181 Customer::class.java).body}
183 val post = async(start = CoroutineStart.LAZY) {
184 restClientService.exchangeNB(HttpMethod.POST.name,
185 "/sample/aai/v14/business/customers", post1, headers,
186 String::class.java).body}
188 val put = async(start = CoroutineStart.LAZY) {
189 restClientService.exchangeNB(HttpMethod.PUT.name,
190 "/sample/aai/v14/business/customers", post1, headers,
191 String::class.java).body}
193 val patch = async(start = CoroutineStart.LAZY) {
194 restClientService.exchangeNB(HttpMethod.PATCH.name,
195 "/sample/aai/v14/business/customers", post1, headers,
196 String::class.java).body}
198 val delete = async(start = CoroutineStart.LAZY) {
199 restClientService.exchangeNB(HttpMethod.DELETE.name,
200 "/sample/aai/v14/business/customers", "", headers,
201 String::class.java).body}
214 res6 = delete.await()
216 assertNotNull(res1, "failed to get response")
217 assertNotNull(res2, "failed to get response")
218 assertEquals(res1.id, "ONSDEMOBJHKCustomer")
219 assertEquals(res1.name, "ONSDEMOBJHKCustomer")
220 assertEquals(res1.type, "CUST")
221 assertEquals(res1.resource, "1552985011163")
222 assertEquals(res2.id, "ONSDEMOBJHKCustomer")
223 assertEquals(res2.name, "ONSDEMOBJHKCustomer")
224 assertEquals(res2.type, "CUST")
225 assertEquals(res2.resource, "1552985011163")
226 assertEquals(res3, "The message is successfully posted")
227 assertEquals(res4, "The put request is success")
228 assertEquals(res5, "The patch request is success")
229 assertEquals(res6, "The message is successfully deleted")
234 * Sample controller code for testing both http and https requests.
237 @RequestMapping("/sample")
238 open class SampleController {
241 fun getName(): String = "Sample Controller"
243 @PatchMapping("/name")
244 fun patchName(): String = "Patch request successful"
246 @GetMapping("/basic")
247 fun getBasic(): String = "Basic request arrived successfully"
250 @GetMapping("/aai/v14/business/customers")
252 @RequestHeader(name = "X-TransactionId", required = true)
254 @RequestHeader(name = "X-FromAppId", required = true)
255 appId: String) : String {
256 if (transId != "9999" || appId != "AAI") {
260 " \"id\": \"ONSDEMOBJHKCustomer\",\n" +
261 " \"name\": \"ONSDEMOBJHKCustomer\",\n" +
262 " \"type\": \"CUST\",\n" +
263 " \"resource\": \"1552985011163\"\n" +
267 @PostMapping("/aai/v14/business/customers")
268 fun postAaiCustomers(
269 @RequestHeader(name = "X-TransactionId", required = true)
271 @RequestHeader(name = "X-FromAppId", required = true)
272 appId: String) : String {
273 if (transId != "9999" || appId != "AAI") {
276 return "The message is successfully posted"
280 @PutMapping("/aai/v14/business/customers")
282 @RequestHeader(name = "X-TransactionId", required = true)
284 @RequestHeader(name = "X-FromAppId", required = true)
285 appId: String) : String {
286 if (transId != "9999" || appId != "AAI") {
289 return "The put request is success"
292 @PatchMapping("/aai/v14/business/customers")
293 fun patchAaiCustomers(
294 @RequestHeader(name = "X-TransactionId", required = true)
296 @RequestHeader(name = "X-FromAppId", required = true)
297 appId: String) : String {
298 if (transId != "9999" || appId != "AAI") {
301 return "The patch request is success"
304 @DeleteMapping("/aai/v14/business/customers")
305 fun deleteAaiCustomers(
306 @RequestHeader(name = "X-TransactionId", required = true)
308 @RequestHeader(name = "X-FromAppId", required = true)
309 appId: String) : String {
310 if (transId != "9999" || appId != "AAI") {
313 return "The message is successfully deleted"
318 * Security configuration required for basic authentication with username and
319 * password for any request in the server.
321 open class SecurityConfiguration {
324 open fun userDetailsService(): MapReactiveUserDetailsService {
325 val user: UserDetails = User.withDefaultPasswordEncoder()
330 return MapReactiveUserDetailsService(user)
334 open fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
337 .authorizeExchange().anyExchange().authenticated()
344 * Data class required for response
350 val resource: String)