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.apache.catalina.connector.Connector
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
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 =
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"
89 class RestClientServiceTest {
92 lateinit var bluePrintRestLibPropertyService: BluePrintRestLibPropertyService
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")
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,
112 assertNotNull(response, "failed to get response")
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" +
123 val mapper = ObjectMapper()
124 val actualObj: JsonNode = mapper.readTree(json)
125 val restClientService = bluePrintRestLibPropertyService
126 .blueprintWebClientService(actualObj)
127 lateinit var res:String
129 val get = async(start = CoroutineStart.LAZY) {
130 restClientService.exchangeNB(HttpMethod.GET.name,
131 "/sample/basic", "")}
135 assertNotNull(res, "failed to get response")
136 assertEquals(res, "Basic request arrived successfully")
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"
147 " \"customer\": {\n" +
148 " \"global-customer-id\": \"ONSDEMOBJHKCustomer\",\n" +
149 " \"subscriber-name\": \"ONSDEMOBJHKCustomer\",\n" +
150 " \"subscriber-type\": \"CUST\",\n" +
151 " \"resource-version\": \"1552985011163\"\n" +
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
161 val get1 = async(start = CoroutineStart.LAZY) {
162 restClientService.exchangeNB(HttpMethod.GET.name,
163 "/sample/aai/v14/business/customers", "", headers,
164 Customer::class.java)}
166 val get2 = async(start = CoroutineStart.LAZY) {
167 restClientService.exchangeNB(HttpMethod.GET.name,
168 "/sample/aai/v14/business/customers", "", headers,
169 Customer::class.java)}
171 val post = async(start = CoroutineStart.LAZY) {
172 restClientService.exchangeNB(HttpMethod.POST.name,
173 "/sample/aai/v14/business/customers", post1, headers,
176 val put = async(start = CoroutineStart.LAZY) {
177 restClientService.exchangeNB(HttpMethod.PUT.name,
178 "/sample/aai/v14/business/customers", post1, headers,
181 val patch = async(start = CoroutineStart.LAZY) {
182 restClientService.exchangeNB(HttpMethod.PATCH.name,
183 "/sample/aai/v14/business/customers", post1, headers,
186 val delete = async(start = CoroutineStart.LAZY) {
187 restClientService.exchangeNB(HttpMethod.DELETE.name,
188 "/sample/aai/v14/business/customers", "", headers,
202 res6 = delete.await()
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")
222 * Sample controller code for testing both http and https requests.
225 @RequestMapping("/sample")
226 open class SampleController {
229 fun getName(): String = "Sample Controller"
231 @PatchMapping("/name")
232 fun patchName(): String = "Patch request successful"
234 @GetMapping("/basic")
235 fun getBasic(): String = "Basic request arrived successfully"
238 @GetMapping("/aai/v14/business/customers")
240 @RequestHeader(name = "X-TransactionId", required = true)
242 @RequestHeader(name = "X-FromAppId", required = true)
243 appId: String) : String {
244 if (transId != "9999" || appId != "AAI") {
248 " \"id\": \"ONSDEMOBJHKCustomer\",\n" +
249 " \"name\": \"ONSDEMOBJHKCustomer\",\n" +
250 " \"type\": \"CUST\",\n" +
251 " \"resource\": \"1552985011163\"\n" +
255 @PostMapping("/aai/v14/business/customers")
256 fun postAaiCustomers(
257 @RequestHeader(name = "X-TransactionId", required = true)
259 @RequestHeader(name = "X-FromAppId", required = true)
260 appId: String) : String {
261 if (transId != "9999" || appId != "AAI") {
264 return "The message is successfully posted"
268 @PutMapping("/aai/v14/business/customers")
270 @RequestHeader(name = "X-TransactionId", required = true)
272 @RequestHeader(name = "X-FromAppId", required = true)
273 appId: String) : String {
274 if (transId != "9999" || appId != "AAI") {
277 return "The put request is success"
280 @PatchMapping("/aai/v14/business/customers")
281 fun patchAaiCustomers(
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 patch request is success"
292 @DeleteMapping("/aai/v14/business/customers")
293 fun deleteAaiCustomers(
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 message is successfully deleted"
306 * Security configuration required for basic authentication with username and
307 * password for any request in the server.
311 open class SecurityConfig : WebSecurityConfigurerAdapter() {
313 @Throws(Exception::class)
314 override fun configure(http: HttpSecurity) {
317 .authorizeRequests().anyRequest().authenticated()
323 @Throws(Exception::class)
324 open fun configureGlobal(auth: AuthenticationManagerBuilder) {
325 auth.inMemoryAuthentication()
327 .password(passwordEncoder().encode("jans"))
332 open fun passwordEncoder(): PasswordEncoder {
333 return BCryptPasswordEncoder()
338 * Http server required for http request to be processed along with the https
344 fun servletContainer(): ServletWebServerFactory {
346 val connector = Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL)
347 connector.setPort(8080)
349 val tomcat = TomcatServletWebServerFactory()
350 tomcat.addAdditionalTomcatConnectors(connector)
356 * Data class required for response
362 val resource: String)