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.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(classes = [BluePrintRestLibConfiguration::class, SampleController::class,
66 SecurityConfiguration::class,
67 BlueprintPropertyConfiguration::class, BluePrintProperties::class])
68 @TestPropertySource(properties =
71 "server.ssl.enabled=true",
72 "server.ssl.key-store=classpath:keystore.p12",
73 "server.ssl.key-store-password=changeit",
74 "server.ssl.keyStoreType=PKCS12",
75 "server.ssl.keyAlias=tomcat",
76 "blueprintsprocessor.restclient.sample.type=basic-auth",
77 "blueprintsprocessor.restclient.sample.url=http://127.0.0.1:8081",
78 "blueprintsprocessor.restclient.sample.username=admin",
79 "blueprintsprocessor.restclient.sample.password=jans",
80 "blueprintsprocessor.restclient.test.type=ssl-basic-auth",
81 "blueprintsprocessor.restclient.test.url=https://localhost:8443",
82 "blueprintsprocessor.restclient.test.username=admin",
83 "blueprintsprocessor.restclient.test.password=jans",
84 "blueprintsprocessor.restclient.test.keyStoreInstance=PKCS12",
85 "blueprintsprocessor.restclient.test.sslTrust=src/test/resources/keystore.p12",
86 "blueprintsprocessor.restclient.test.sslTrustPassword=changeit"
88 class RestClientServiceTest {
91 lateinit var bluePrintRestLibPropertyService: BluePrintRestLibPropertyService
94 lateinit var httpHandler : HttpHandler
96 lateinit var http : WebServer
98 fun localPort() = http.port
102 // Second Http server required for non-SSL requests to be processed along with the https server.
103 val factory: ReactiveWebServerFactory = NettyReactiveWebServerFactory(8081)
104 this.http = factory.getWebServer(this.httpHandler)
115 val restClientService = bluePrintRestLibPropertyService
116 .blueprintWebClientService("sample")
117 val response = restClientService.exchangeResource(
118 HttpMethod.PATCH.name, "/sample/name", "")
119 assertEquals("Patch request successful", response.body,
120 "failed to get patch response")
125 val restClientService = bluePrintRestLibPropertyService
126 .blueprintWebClientService("sample")
127 val headers = mutableMapOf<String, String>()
128 headers["X-Transaction-Id"] = "1234"
129 val response = restClientService.exchangeResource(HttpMethod.GET.name,
131 assertNotNull(response.body, "failed to get response")
135 fun testSimpleBasicAuth() {
136 val json: String = "{\n" +
137 " \"type\" : \"basic-auth\",\n" +
138 " \"url\" : \"http://localhost:8081\",\n" +
139 " \"username\" : \"admin\",\n" +
140 " \"password\" : \"jans\"\n" +
142 val mapper = ObjectMapper()
143 val actualObj: JsonNode = mapper.readTree(json)
144 val restClientService = bluePrintRestLibPropertyService
145 .blueprintWebClientService(actualObj)
146 lateinit var res:String
148 val get = async(start = CoroutineStart.LAZY) {
149 restClientService.exchangeNB(HttpMethod.GET.name,
150 "/sample/basic", "").body}
154 assertNotNull(res, "failed to get response")
155 assertEquals(res, "Basic request arrived successfully")
159 fun testSampleAaiReq() {
160 val restClientService = bluePrintRestLibPropertyService
161 .blueprintWebClientService("test")
162 val headers = mutableMapOf<String, String>()
163 headers["X-TransactionId"] = "9999"
164 headers["X-FromAppId"] = "AAI"
166 " \"customer\": {\n" +
167 " \"global-customer-id\": \"ONSDEMOBJHKCustomer\",\n" +
168 " \"subscriber-name\": \"ONSDEMOBJHKCustomer\",\n" +
169 " \"subscriber-type\": \"CUST\",\n" +
170 " \"resource-version\": \"1552985011163\"\n" +
173 lateinit var res1: Customer
174 lateinit var res2: Customer
175 lateinit var res3: String
176 lateinit var res4: String
177 lateinit var res5: String
178 lateinit var res6: String
180 val get1 = async(start = CoroutineStart.LAZY) {
181 restClientService.exchangeNB(HttpMethod.GET.name,
182 "/sample/aai/v14/business/customers", "", headers,
183 Customer::class.java).body}
185 val get2 = async(start = CoroutineStart.LAZY) {
186 restClientService.exchangeNB(HttpMethod.GET.name,
187 "/sample/aai/v14/business/customers", "", headers,
188 Customer::class.java).body}
190 val post = async(start = CoroutineStart.LAZY) {
191 restClientService.exchangeNB(HttpMethod.POST.name,
192 "/sample/aai/v14/business/customers", post1, headers,
193 String::class.java).body}
195 val put = async(start = CoroutineStart.LAZY) {
196 restClientService.exchangeNB(HttpMethod.PUT.name,
197 "/sample/aai/v14/business/customers", post1, headers,
198 String::class.java).body}
200 val patch = async(start = CoroutineStart.LAZY) {
201 restClientService.exchangeNB(HttpMethod.PATCH.name,
202 "/sample/aai/v14/business/customers", post1, headers,
203 String::class.java).body}
205 val delete = async(start = CoroutineStart.LAZY) {
206 restClientService.exchangeNB(HttpMethod.DELETE.name,
207 "/sample/aai/v14/business/customers", "", headers,
208 String::class.java).body}
221 res6 = delete.await()
223 assertNotNull(res1, "failed to get response")
224 assertNotNull(res2, "failed to get response")
225 assertEquals(res1.id, "ONSDEMOBJHKCustomer")
226 assertEquals(res1.name, "ONSDEMOBJHKCustomer")
227 assertEquals(res1.type, "CUST")
228 assertEquals(res1.resource, "1552985011163")
229 assertEquals(res2.id, "ONSDEMOBJHKCustomer")
230 assertEquals(res2.name, "ONSDEMOBJHKCustomer")
231 assertEquals(res2.type, "CUST")
232 assertEquals(res2.resource, "1552985011163")
233 assertEquals(res3, "The message is successfully posted")
234 assertEquals(res4, "The put request is success")
235 assertEquals(res5, "The patch request is success")
236 assertEquals(res6, "The message is successfully deleted")
241 * Sample controller code for testing both http and https requests.
244 @RequestMapping("/sample")
245 open class SampleController {
248 fun getName(): String = "Sample Controller"
250 @PatchMapping("/name")
251 fun patchName(): String = "Patch request successful"
253 @GetMapping("/basic")
254 fun getBasic(): String = "Basic request arrived successfully"
257 @GetMapping("/aai/v14/business/customers")
259 @RequestHeader(name = "X-TransactionId", required = true)
261 @RequestHeader(name = "X-FromAppId", required = true)
262 appId: String) : String {
263 if (transId != "9999" || appId != "AAI") {
267 " \"id\": \"ONSDEMOBJHKCustomer\",\n" +
268 " \"name\": \"ONSDEMOBJHKCustomer\",\n" +
269 " \"type\": \"CUST\",\n" +
270 " \"resource\": \"1552985011163\"\n" +
274 @PostMapping("/aai/v14/business/customers")
275 fun postAaiCustomers(
276 @RequestHeader(name = "X-TransactionId", required = true)
278 @RequestHeader(name = "X-FromAppId", required = true)
279 appId: String) : String {
280 if (transId != "9999" || appId != "AAI") {
283 return "The message is successfully posted"
287 @PutMapping("/aai/v14/business/customers")
289 @RequestHeader(name = "X-TransactionId", required = true)
291 @RequestHeader(name = "X-FromAppId", required = true)
292 appId: String) : String {
293 if (transId != "9999" || appId != "AAI") {
296 return "The put request is success"
299 @PatchMapping("/aai/v14/business/customers")
300 fun patchAaiCustomers(
301 @RequestHeader(name = "X-TransactionId", required = true)
303 @RequestHeader(name = "X-FromAppId", required = true)
304 appId: String) : String {
305 if (transId != "9999" || appId != "AAI") {
308 return "The patch request is success"
311 @DeleteMapping("/aai/v14/business/customers")
312 fun deleteAaiCustomers(
313 @RequestHeader(name = "X-TransactionId", required = true)
315 @RequestHeader(name = "X-FromAppId", required = true)
316 appId: String) : String {
317 if (transId != "9999" || appId != "AAI") {
320 return "The message is successfully deleted"
325 * Security configuration required for basic authentication with username and
326 * password for any request in the server.
328 open class SecurityConfiguration {
331 open fun userDetailsService(): MapReactiveUserDetailsService {
332 val user: UserDetails = User.withDefaultPasswordEncoder()
337 return MapReactiveUserDetailsService(user)
341 open fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
344 .authorizeExchange().anyExchange().authenticated()
351 * Data class required for response
357 val resource: String)