aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xcps-rest/pom.xml28
-rw-r--r--cps-rest/src/main/java/org/onap/cps/config/WebSecurityConfig.java61
-rwxr-xr-xcps-rest/src/test/groovy/org/onap/cps/rest/controller/AdminRestControllerSpec.groovy96
-rwxr-xr-xcps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy66
-rw-r--r--cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy18
-rw-r--r--cps-rest/src/test/groovy/org/onap/cps/rest/controller/RestControllerSpecification.groovy34
-rw-r--r--cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy30
-rw-r--r--csit/tests/cps-admin/cps-admin.robot16
-rw-r--r--csit/tests/cps-data/cps-data.robot6
-rw-r--r--docker-compose/docker-compose.yml2
10 files changed, 284 insertions, 73 deletions
diff --git a/cps-rest/pom.xml b/cps-rest/pom.xml
index 9264c6815..43875e8f4 100755
--- a/cps-rest/pom.xml
+++ b/cps-rest/pom.xml
@@ -1,3 +1,22 @@
+<!--
+ ============LICENSE_START=======================================================
+ Copyright (c) 2020 Linux Foundation.
+ Modifications Copyright (C) 2021 Bell Canada.
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ============LICENSE_END=========================================================
+-->
+
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
@@ -35,6 +54,10 @@
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-security</artifactId>
+ </dependency>
+ <dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
</dependency>
@@ -86,6 +109,11 @@
</exclusion>
</exclusions>
</dependency>
+ <dependency>
+ <groupId>org.springframework.security</groupId>
+ <artifactId>spring-security-test</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/cps-rest/src/main/java/org/onap/cps/config/WebSecurityConfig.java b/cps-rest/src/main/java/org/onap/cps/config/WebSecurityConfig.java
new file mode 100644
index 000000000..943e02c27
--- /dev/null
+++ b/cps-rest/src/main/java/org/onap/cps/config/WebSecurityConfig.java
@@ -0,0 +1,61 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (c) 2021 Bell Canada.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.config;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+
+/**
+ * Configuration class to implement application security.
+ * It enforces Basic Authentication access control.
+ */
+@Configuration
+@EnableWebSecurity
+public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+ private static final String ACTUATOR_HEALTH_PATTERN = "/manage/health/**";
+ private static final String ACTUATOR_INFO_PATTERN = "/manage/info";
+ private static final String DEFAULT_USER_NAME = "cpsuser";
+ private static final String DEFAULT_USER_PASSWORD = "cpsr0cks!";
+ private static final String USER_NAME =
+ StringUtils.defaultIfBlank(System.getenv("CPS_USERNAME"), DEFAULT_USER_NAME);
+ private static final String USER_PASSWORD =
+ StringUtils.defaultIfBlank(System.getenv("CPS_PASSWORD"), DEFAULT_USER_PASSWORD);
+ private static final String USER_ROLE = "USER";
+
+ @Override
+ protected void configure(final HttpSecurity http) throws Exception {
+ http
+ .csrf().disable()
+ .authorizeRequests()
+ .antMatchers(ACTUATOR_HEALTH_PATTERN, ACTUATOR_INFO_PATTERN).permitAll()
+ .anyRequest().authenticated()
+ .and().httpBasic();
+ }
+
+ @Override
+ protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
+ auth.inMemoryAuthentication().withUser(USER_NAME).password("{noop}" + USER_PASSWORD).roles(USER_ROLE);
+ }
+
+}
diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/AdminRestControllerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/AdminRestControllerSpec.groovy
index ca99743ca..f38193803 100755
--- a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/AdminRestControllerSpec.groovy
+++ b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/AdminRestControllerSpec.groovy
@@ -1,7 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2020 Pantheon.tech
- * Modifications Copyright (C) 2020 Bell Canada. All rights reserved.
+ * Modifications Copyright (C) 2020, 2021 Bell Canada. All rights reserved.
* Copyright (C) 2021 Nordix Foundation
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,10 +28,10 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import org.modelmapper.ModelMapper
-import org.onap.cps.api.CpsQueryService
import org.onap.cps.api.CpsAdminService
import org.onap.cps.api.CpsDataService
import org.onap.cps.api.CpsModuleService
+import org.onap.cps.api.CpsQueryService
import org.onap.cps.spi.exceptions.DataspaceAlreadyDefinedException
import org.onap.cps.spi.exceptions.SchemaSetInUseException
import org.onap.cps.spi.model.Anchor
@@ -46,11 +46,10 @@ import org.springframework.mock.web.MockMultipartFile
import org.springframework.test.web.servlet.MockMvc
import org.springframework.util.LinkedMultiValueMap
import org.springframework.util.MultiValueMap
-import spock.lang.Specification
import spock.lang.Unroll
@WebMvcTest
-class AdminRestControllerSpec extends Specification {
+class AdminRestControllerSpec extends RestControllerSpecification {
@SpringBean
CpsModuleService mockCpsModuleService = Mock()
@@ -83,8 +82,12 @@ class AdminRestControllerSpec extends Specification {
given: 'an endpoint'
def createDataspaceEndpoint = "$basePath/v1/dataspaces";
when: 'post is invoked'
- def response = mvc.perform(
- post(createDataspaceEndpoint).param('dataspace-name', dataspaceName)).andReturn().response
+ def response =
+ mvc.perform(
+ post(createDataspaceEndpoint)
+ .header("Authorization", getAuthorizationHeader())
+ .param('dataspace-name', dataspaceName))
+ .andReturn().response
then: 'service method is invoked with expected parameters'
1 * mockCpsAdminService.createDataspace(dataspaceName)
and: 'dataspace is create successfully'
@@ -98,7 +101,12 @@ class AdminRestControllerSpec extends Specification {
def thrownException = new DataspaceAlreadyDefinedException("", new RuntimeException())
mockCpsAdminService.createDataspace(dataspaceName) >> { throw thrownException }
when: 'post is invoked'
- def response = mvc.perform(post(createDataspaceEndpoint).param('dataspace-name', dataspaceName)).andReturn().response
+ def response =
+ mvc.perform(
+ post(createDataspaceEndpoint)
+ .header("Authorization", getAuthorizationHeader())
+ .param('dataspace-name', dataspaceName))
+ .andReturn().response
then: 'dataspace creation fails'
response.status == HttpStatus.BAD_REQUEST.value()
}
@@ -110,8 +118,13 @@ class AdminRestControllerSpec extends Specification {
and: 'an endpoint'
def schemaSetEndpoint = "$basePath/v1/dataspaces/$dataspaceName/schema-sets"
when: 'file uploaded with schema set create request'
- def response = mvc.perform(multipart(schemaSetEndpoint)
- .file(multipartFile).param('schema-set-name', schemaSetName)).andReturn().response
+ def response =
+ mvc.perform(
+ multipart(schemaSetEndpoint)
+ .file(multipartFile)
+ .header("Authorization", getAuthorizationHeader())
+ .param('schema-set-name', schemaSetName))
+ .andReturn().response
then: 'associated service method is invoked with expected parameters'
1 * mockCpsModuleService.createSchemaSet(dataspaceName, schemaSetName, _) >>
{ args -> yangResourceMapCapture = args[2] }
@@ -127,8 +140,13 @@ class AdminRestControllerSpec extends Specification {
and: 'an endpoint'
def schemaSetEndpoint = "$basePath/v1/dataspaces/$dataspaceName/schema-sets"
when: 'file uploaded with schema set create request'
- def response = mvc.perform(multipart(schemaSetEndpoint)
- .file(multipartFile).param('schema-set-name', schemaSetName)).andReturn().response
+ def response =
+ mvc.perform(
+ multipart(schemaSetEndpoint)
+ .file(multipartFile)
+ .header("Authorization", getAuthorizationHeader())
+ .param('schema-set-name', schemaSetName))
+ .andReturn().response
then: 'associated service method is invoked with expected parameters'
1 * mockCpsModuleService.createSchemaSet(dataspaceName, schemaSetName, _) >>
{ args -> yangResourceMapCapture = args[2] }
@@ -143,8 +161,13 @@ class AdminRestControllerSpec extends Specification {
given: 'an endpoint'
def schemaSetEndpoint = "$basePath/v1/dataspaces/$dataspaceName/schema-sets"
when: 'zip archive having #caseDescriptor is uploaded with create schema set request'
- def response = mvc.perform(multipart(schemaSetEndpoint)
- .file(multipartFile).param('schema-set-name', schemaSetName)).andReturn().response
+ def response =
+ mvc.perform(
+ multipart(schemaSetEndpoint)
+ .file(multipartFile)
+ .header("Authorization", getAuthorizationHeader())
+ .param('schema-set-name', schemaSetName))
+ .andReturn().response
then: 'create schema set rejected'
response.status == HttpStatus.BAD_REQUEST.value()
where: 'following cases are tested'
@@ -159,8 +182,13 @@ class AdminRestControllerSpec extends Specification {
and: 'an endpoint'
def schemaSetEndpoint = "$basePath/v1/dataspaces/$dataspaceName/schema-sets"
when: 'file uploaded with schema set create request'
- def response = mvc.perform(multipart(schemaSetEndpoint)
- .file(multipartFile).param('schema-set-name', schemaSetName)).andReturn().response
+ def response =
+ mvc.perform(
+ multipart(schemaSetEndpoint)
+ .file(multipartFile)
+ .header("Authorization", getAuthorizationHeader())
+ .param('schema-set-name', schemaSetName))
+ .andReturn().response
then: 'create schema set rejected'
response.status == HttpStatus.BAD_REQUEST.value()
}
@@ -171,8 +199,13 @@ class AdminRestControllerSpec extends Specification {
def schemaSetEndpoint = "$basePath/v1/dataspaces/$dataspaceName/schema-sets"
when: 'file uploaded with schema set create request'
def multipartFile = createMultipartFileForIOException(fileType)
- def response = mvc.perform(multipart(schemaSetEndpoint)
- .file(multipartFile).param('schema-set-name', schemaSetName)).andReturn().response
+ def response =
+ mvc.perform(
+ multipart(schemaSetEndpoint)
+ .file(multipartFile)
+ .header("Authorization", getAuthorizationHeader())
+ .param('schema-set-name', schemaSetName))
+ .andReturn().response
then: 'the error response returned indicating internal server error occurrence'
response.status == HttpStatus.INTERNAL_SERVER_ERROR.value()
where: 'following file types are used'
@@ -183,7 +216,9 @@ class AdminRestControllerSpec extends Specification {
given: 'an endpoint'
def schemaSetEndpoint = "$basePath/v1/dataspaces/$dataspaceName/schema-sets/$schemaSetName"
when: 'delete schema set endpoint is invoked'
- def response = mvc.perform(delete(schemaSetEndpoint)).andReturn().response
+ def response =
+ mvc.perform(delete(schemaSetEndpoint).header("Authorization", getAuthorizationHeader()))
+ .andReturn().response
then: 'associated service method is invoked with expected parameters'
1 * mockCpsModuleService.deleteSchemaSet(dataspaceName, schemaSetName, CASCADE_DELETE_PROHIBITED)
and: 'response code indicates success'
@@ -198,7 +233,9 @@ class AdminRestControllerSpec extends Specification {
and: 'an endpoint'
def schemaSetEndpoint = "$basePath/v1/dataspaces/$dataspaceName/schema-sets/$schemaSetName"
when: 'delete schema set endpoint is invoked'
- def response = mvc.perform(delete(schemaSetEndpoint)).andReturn().response
+ def response =
+ mvc.perform(delete(schemaSetEndpoint).header("Authorization", getAuthorizationHeader()))
+ .andReturn().response
then: 'schema set deletion fails with conflict response code'
response.status == HttpStatus.CONFLICT.value()
}
@@ -210,7 +247,9 @@ class AdminRestControllerSpec extends Specification {
and: 'an endpoint'
def schemaSetEndpoint = "$basePath/v1/dataspaces/$dataspaceName/schema-sets/$schemaSetName"
when: 'get schema set API is invoked'
- def response = mvc.perform(get(schemaSetEndpoint)).andReturn().response
+ def response =
+ mvc.perform(get(schemaSetEndpoint).header("Authorization", getAuthorizationHeader()))
+ .andReturn().response
then: 'the correct schema set is returned'
response.status == HttpStatus.OK.value()
response.getContentAsString().contains(schemaSetName)
@@ -224,8 +263,12 @@ class AdminRestControllerSpec extends Specification {
and: 'an endpoint'
def anchorEndpoint = "$basePath/v1/dataspaces/$dataspaceName/anchors"
when: 'post is invoked'
- def response = mvc.perform(post(anchorEndpoint).contentType(MediaType.APPLICATION_JSON)
- .params(requestParams as MultiValueMap)).andReturn().response
+ def response =
+ mvc.perform(
+ post(anchorEndpoint).contentType(MediaType.APPLICATION_JSON)
+ .header("Authorization", getAuthorizationHeader())
+ .params(requestParams as MultiValueMap))
+ .andReturn().response
then: 'anchor is created successfully'
1 * mockCpsAdminService.createAnchor(dataspaceName, schemaSetName, anchorName)
response.status == HttpStatus.CREATED.value()
@@ -238,7 +281,9 @@ class AdminRestControllerSpec extends Specification {
and: 'an endpoint'
def anchorEndpoint = "$basePath/v1/dataspaces/$dataspaceName/anchors"
when: 'get all anchors API is invoked'
- def response = mvc.perform(get(anchorEndpoint)).andReturn().response
+ def response =
+ mvc.perform(get(anchorEndpoint).header("Authorization", getAuthorizationHeader()))
+ .andReturn().response
then: 'the correct anchor is returned'
response.status == HttpStatus.OK.value()
response.getContentAsString().contains(anchorName)
@@ -250,8 +295,9 @@ class AdminRestControllerSpec extends Specification {
and: 'an endpoint'
def anchorEndpoint = "$basePath/v1/dataspaces/$dataspaceName/anchors/$anchorName"
when: 'get anchor API is invoked'
- def response = mvc.perform(get(anchorEndpoint))
- .andReturn().response
+ def response =
+ mvc.perform(get(anchorEndpoint).header("Authorization", getAuthorizationHeader()))
+ .andReturn().response
def responseContent = response.getContentAsString()
then: 'the correct anchor is returned'
response.status == HttpStatus.OK.value()
diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy
index b9b680d35..ef834a7a2 100755
--- a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy
+++ b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/DataRestControllerSpec.groovy
@@ -2,6 +2,7 @@
* ============LICENSE_START=======================================================
* Copyright (C) 2021 Nordix Foundation
* Modifications Copyright (C) 2021 Pantheon.tech
+ * Modifications Copyright (C) 2021 Bell Canada.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,10 +29,10 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put
import org.modelmapper.ModelMapper
-import org.onap.cps.api.CpsQueryService
import org.onap.cps.api.CpsAdminService
import org.onap.cps.api.CpsDataService
import org.onap.cps.api.CpsModuleService
+import org.onap.cps.api.CpsQueryService
import org.onap.cps.spi.exceptions.AnchorNotFoundException
import org.onap.cps.spi.exceptions.DataNodeNotFoundException
import org.onap.cps.spi.exceptions.DataspaceNotFoundException
@@ -45,11 +46,10 @@ import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import spock.lang.Shared
-import spock.lang.Specification
import spock.lang.Unroll
@WebMvcTest
-class DataRestControllerSpec extends Specification {
+class DataRestControllerSpec extends RestControllerSpecification {
@SpringBean
CpsDataService mockCpsDataService = Mock()
@@ -93,9 +93,12 @@ class DataRestControllerSpec extends Specification {
def endpoint = "$dataNodeBaseEndpoint/anchors/$anchorName/nodes"
def json = 'some json (this is not validated)'
when: 'post is invoked with datanode endpoint and json'
- def response = mvc.perform(
- post(endpoint).contentType(MediaType.APPLICATION_JSON).content(json)
- ).andReturn().response
+ def response =
+ mvc.perform(
+ post(endpoint)
+ .header("Authorization", getAuthorizationHeader())
+ .contentType(MediaType.APPLICATION_JSON).content(json))
+ .andReturn().response
then: 'a created response is returned'
response.status == HttpStatus.CREATED.value()
then: 'the java API was called with the correct parameters'
@@ -109,9 +112,9 @@ class DataRestControllerSpec extends Specification {
def endpoint = "$dataNodeBaseEndpoint/anchors/$anchorName/node"
mockCpsDataService.getDataNode(dataspaceName, anchorName, xpath, OMIT_DESCENDANTS) >> dataNodeWithLeavesNoChildren
when: 'get request is performed through REST API'
- def response = mvc.perform(
- get(endpoint).param('xpath', xpath)
- ).andReturn().response
+ def response =
+ mvc.perform(get(endpoint).header("Authorization", getAuthorizationHeader()).param('xpath', xpath))
+ .andReturn().response
then: 'a success response is returned'
response.status == HttpStatus.OK.value()
and: 'response contains expected leaf and value'
@@ -127,10 +130,13 @@ class DataRestControllerSpec extends Specification {
def endpoint = "$dataNodeBaseEndpoint/anchors/$anchorName/node"
mockCpsDataService.getDataNode(dataspaceName, anchorName, xpath, expectedCpsDataServiceOption) >> dataNode
when: 'get request is performed through REST API'
- def response = mvc.perform(get(endpoint)
- .param('xpath', xpath)
- .param('include-descendants', includeDescendantsOption))
- .andReturn().response
+ def response =
+ mvc.perform(
+ get(endpoint)
+ .header("Authorization", getAuthorizationHeader())
+ .param('xpath', xpath)
+ .param('include-descendants', includeDescendantsOption))
+ .andReturn().response
then: 'a success response is returned'
response.status == HttpStatus.OK.value()
and: 'the response contains child is #expectChildInResponse'
@@ -148,9 +154,9 @@ class DataRestControllerSpec extends Specification {
def endpoint = "$dataNodeBaseEndpoint/anchors/$anchorName/node"
mockCpsDataService.getDataNode(dataspaceName, anchorName, xpath, _) >> { throw exception }
when: 'get request is performed through REST API'
- def response = mvc.perform(
- get(endpoint).param("xpath", xpath)
- ).andReturn().response
+ def response =
+ mvc.perform(get(endpoint).header("Authorization", getAuthorizationHeader()).param("xpath", xpath))
+ .andReturn().response
then: 'a success response is returned'
response.status == httpStatus.value()
where:
@@ -167,12 +173,14 @@ class DataRestControllerSpec extends Specification {
def jsonData = 'json data'
def endpoint = "$dataNodeBaseEndpoint/anchors/$anchorName/nodes"
when: 'patch request is performed'
- def response = mvc.perform(
- patch(endpoint)
- .contentType(MediaType.APPLICATION_JSON)
- .content(jsonData)
- .param('xpath', xpath)
- ).andReturn().response
+ def response =
+ mvc.perform(
+ patch(endpoint)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(jsonData)
+ .header("Authorization", getAuthorizationHeader())
+ .param('xpath', xpath)
+ ).andReturn().response
then: 'the service method is invoked with expected parameters'
1 * mockCpsDataService.updateNodeLeaves(dataspaceName, anchorName, xpathServiceParameter, jsonData)
and: 'response status indicates success'
@@ -189,12 +197,14 @@ class DataRestControllerSpec extends Specification {
def jsonData = 'json data'
def endpoint = "$dataNodeBaseEndpoint/anchors/$anchorName/nodes"
when: 'put request is performed'
- def response = mvc.perform(
- put(endpoint)
- .contentType(MediaType.APPLICATION_JSON)
- .content(jsonData)
- .param('xpath', xpath)
- ).andReturn().response
+ def response =
+ mvc.perform(
+ put(endpoint)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(jsonData)
+ .header("Authorization", getAuthorizationHeader())
+ .param('xpath', xpath))
+ .andReturn().response
then: 'the service method is invoked with expected parameters'
1 * mockCpsDataService.replaceNodeTree(dataspaceName, anchorName, xpathServiceParameter, jsonData)
and: 'response status indicates success'
diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy
index 0927c9d1e..907528a55 100644
--- a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy
+++ b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/QueryRestControllerSpec.groovy
@@ -1,6 +1,7 @@
/*
* ============LICENSE_START=======================================================
* Copyright (C) 2021 Nordix Foundation
+ * Modifications Copyright (C) 2021 Bell Canada.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,14 +20,12 @@
package org.onap.cps.rest.controller
-
import com.google.gson.Gson
import org.modelmapper.ModelMapper
import org.onap.cps.api.CpsAdminService
import org.onap.cps.api.CpsDataService
import org.onap.cps.api.CpsModuleService
import org.onap.cps.api.CpsQueryService
-import org.onap.cps.spi.model.DataNode
import org.onap.cps.spi.model.DataNodeBuilder
import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
@@ -34,8 +33,6 @@ import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.http.HttpStatus
import org.springframework.test.web.servlet.MockMvc
-import spock.lang.Shared
-import spock.lang.Specification
import spock.lang.Unroll
import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS
@@ -43,7 +40,7 @@ import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
@WebMvcTest
-class QueryRestControllerSpec extends Specification {
+class QueryRestControllerSpec extends RestControllerSpecification {
@SpringBean
CpsDataService mockCpsDataService = Mock()
@@ -77,10 +74,13 @@ class QueryRestControllerSpec extends Specification {
and: 'the query endpoint'
def dataNodeEndpoint = "$basePath/v1/dataspaces/$dataspaceName/anchors/$anchorName/nodes/query"
when: 'query data nodes API is invoked'
- def response = mvc.perform(get(dataNodeEndpoint)
- .param('cps-path', cpsPath)
- .param('include-descendants', includeDescendantsOption))
- .andReturn().response
+ def response =
+ mvc.perform(
+ get(dataNodeEndpoint)
+ .header("Authorization", getAuthorizationHeader())
+ .param('cps-path', cpsPath)
+ .param('include-descendants', includeDescendantsOption))
+ .andReturn().response
then: 'the response contains the the datanode in json format'
response.status == HttpStatus.OK.value()
response.getContentAsString().contains(new Gson().toJson(dataNode))
diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/RestControllerSpecification.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/RestControllerSpecification.groovy
new file mode 100644
index 000000000..a700ea213
--- /dev/null
+++ b/cps-rest/src/test/groovy/org/onap/cps/rest/controller/RestControllerSpecification.groovy
@@ -0,0 +1,34 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (c) 2021 Bell Canada.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+*/
+
+package org.onap.cps.rest.controller
+
+import spock.lang.Specification
+
+/**
+ * Abstract class for all rest controller specifications.
+ */
+abstract class RestControllerSpecification extends Specification {
+
+ def authorizationHeader = 'Basic Y3BzdXNlcjpjcHNyMGNrcyE='
+
+ def getAuthorizationHeader() {
+ return authorizationHeader
+ }
+
+} \ No newline at end of file
diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy
index 4e10e2c14..5ddc9d95b 100644
--- a/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy
+++ b/cps-rest/src/test/groovy/org/onap/cps/rest/exceptions/CpsRestExceptionHandlerSpec.groovy
@@ -2,6 +2,7 @@
* ============LICENSE_START=======================================================
* Copyright (C) 2020 Pantheon.tech
* Copyright (C) 2021 Nordix Foundation
+ * Modifications Copyright (C) 2021 Bell Canada.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +27,7 @@ import org.onap.cps.api.CpsAdminService
import org.onap.cps.api.CpsDataService
import org.onap.cps.api.CpsModuleService
import org.onap.cps.api.CpsQueryService
+import org.onap.cps.rest.controller.RestControllerSpecification
import org.onap.cps.spi.exceptions.AnchorAlreadyDefinedException
import org.onap.cps.spi.exceptions.CpsException
import org.onap.cps.spi.exceptions.DataInUseException
@@ -40,17 +42,17 @@ import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.test.web.servlet.MockMvc
import spock.lang.Shared
-import spock.lang.Specification
import spock.lang.Unroll
import static org.springframework.http.HttpStatus.BAD_REQUEST
import static org.springframework.http.HttpStatus.CONFLICT
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR
import static org.springframework.http.HttpStatus.NOT_FOUND
+import static org.springframework.http.HttpStatus.UNAUTHORIZED
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
@WebMvcTest
-class CpsRestExceptionHandlerSpec extends Specification {
+class CpsRestExceptionHandlerSpec extends RestControllerSpecification {
@SpringBean
CpsAdminService mockCpsAdminService = Mock()
@@ -164,6 +166,25 @@ class CpsRestExceptionHandlerSpec extends Specification {
new SchemaSetInUseException(dataspaceName, existingObjectName)]
}
+ def 'Get request without authentication is not authorized'() {
+ when: 'request is sent without authentication'
+ def response =
+ mvc.perform(get("$basePath/v1/dataspaces/dataspace-name/anchors")).andReturn().response
+ then: 'HTTP Unauthorized status code is returned'
+ assert UNAUTHORIZED.value() == response.status
+ }
+
+ def 'Get request with invalid authentication is not authorized'() {
+ when: 'request is sent with invalid authentication'
+ def response =
+ mvc.perform(
+ get("$basePath/v1/dataspaces/dataspace-name/anchors")
+ .header("Authorization", 'Basic invalid auth'))
+ .andReturn().response
+ then: 'HTTP Unauthorized status code is returned'
+ assert UNAUTHORIZED.value() == response.status
+ }
+
/*
* NB. The test uses 'get JSON by id' endpoint and associated service method invocation
* to test the exception handling. The endpoint chosen is not a subject of test.
@@ -174,7 +195,10 @@ class CpsRestExceptionHandlerSpec extends Specification {
}
def performTestRequest() {
- return mvc.perform(get("$basePath/v1/dataspaces/dataspace-name/anchors")).andReturn().response
+ return mvc.perform(
+ get("$basePath/v1/dataspaces/dataspace-name/anchors")
+ .header("Authorization", getAuthorizationHeader()))
+ .andReturn().response
}
void assertTestResponse(response, expectedStatus,
diff --git a/csit/tests/cps-admin/cps-admin.robot b/csit/tests/cps-admin/cps-admin.robot
index 446a59b86..0e07f38aa 100644
--- a/csit/tests/cps-admin/cps-admin.robot
+++ b/csit/tests/cps-admin/cps-admin.robot
@@ -9,6 +9,7 @@ Suite Setup Create Session CPS_HOST ${CPS_HOST}
*** Variables ***
+${auth} Basic Y3BzdXNlcjpjcHNyMGNrcyE=
${basePath} /cps/api
${dataspaceName} CSIT-Dataspace
${schemaSetName} CSIT-SchemaSet
@@ -18,7 +19,8 @@ ${anchorName} CSIT-Anchor
Create Dataspace
${uri}= Set Variable ${basePath}/v1/dataspaces
${params}= Create Dictionary dataspace-name=${dataspaceName}
- ${response}= POST On Session CPS_HOST ${uri} params=${params}
+ ${headers}= Create Dictionary Authorization=${auth}
+ ${response}= POST On Session CPS_HOST ${uri} params=${params} headers=${headers}
Should Be Equal As Strings ${response.status_code} 201
Create Schema Set from YANG file
@@ -27,7 +29,8 @@ Create Schema Set from YANG file
${fileData}= Get Binary File ${DATADIR}${/}test-tree.yang
${fileTuple}= Create List test.yang ${fileData} application/zip
&{files}= Create Dictionary file=${fileTuple}
- ${response}= POST On Session CPS_HOST ${uri} files=${files} params=${params}
+ ${headers}= Create Dictionary Authorization=${auth}
+ ${response}= POST On Session CPS_HOST ${uri} files=${files} params=${params} headers=${headers}
Should Be Equal As Strings ${response.status_code} 201
Create Schema Set from ZIP file
@@ -36,12 +39,14 @@ Create Schema Set from ZIP file
${fileData}= Get Binary File ${DATADIR}${/}yang-resources.zip
${fileTuple}= Create List test.zip ${fileData} application/zip
&{files}= Create Dictionary file=${fileTuple}
- ${response}= POST On Session CPS_HOST ${uri} files=${files} params=${params}
+ ${headers}= Create Dictionary Authorization=${auth}
+ ${response}= POST On Session CPS_HOST ${uri} files=${files} params=${params} headers=${headers}
Should Be Equal As Strings ${response.status_code} 201
Get Schema Set info
${uri}= Set Variable ${basePath}/v1/dataspaces/${dataspaceName}/schema-sets/${schemaSetName}
- ${response}= Get On Session CPS_HOST ${uri} expected_status=200
+ ${headers}= Create Dictionary Authorization=${auth}
+ ${response}= Get On Session CPS_HOST ${uri} headers=${headers} expected_status=200
${responseJson}= Set Variable ${response.json()}
Should Be Equal As Strings ${responseJson['name']} ${schemaSetName}
Should Be Equal As Strings ${responseJson['dataspaceName']} ${dataspaceName}
@@ -49,5 +54,6 @@ Get Schema Set info
Create Anchor
${uri}= Set Variable ${basePath}/v1/dataspaces/${dataspaceName}/anchors
${params}= Create Dictionary schema-set-name=${schemaSetName} anchor-name=${anchorName}
- ${response}= POST On Session CPS_HOST ${uri} params=${params}
+ ${headers}= Create Dictionary Authorization=${auth}
+ ${response}= POST On Session CPS_HOST ${uri} params=${params} headers=${headers}
Should Be Equal As Strings ${response.status_code} 201 \ No newline at end of file
diff --git a/csit/tests/cps-data/cps-data.robot b/csit/tests/cps-data/cps-data.robot
index 8b0202b3b..ff1e8d048 100644
--- a/csit/tests/cps-data/cps-data.robot
+++ b/csit/tests/cps-data/cps-data.robot
@@ -9,6 +9,7 @@ Suite Setup Create Session CPS_HOST ${CPS_HOST}
*** Variables ***
+${auth} Basic Y3BzdXNlcjpjcHNyMGNrcyE=
${basePath} /cps/api
${dataspaceName} CSIT-Dataspace
${anchorName} CSIT-Anchor
@@ -16,7 +17,7 @@ ${anchorName} CSIT-Anchor
*** Test Cases ***
Create Data Node
${uri}= Set Variable ${basePath}/v1/dataspaces/${dataspaceName}/anchors/${anchorName}/nodes
- ${headers} Create Dictionary Content-Type=application/json
+ ${headers} Create Dictionary Content-Type=application/json Authorization=${auth}
${jsonData}= Get Binary File ${DATADIR}${/}test-tree.json
${response}= POST On Session CPS_HOST ${uri} headers=${headers} data=${jsonData}
Should Be Equal As Strings ${response.status_code} 201
@@ -24,7 +25,8 @@ Create Data Node
Get Data Node by XPath
${uri}= Set Variable ${basePath}/v1/dataspaces/${dataspaceName}/anchors/${anchorName}/node
${params}= Create Dictionary xpath=/test-tree/branch[@name='Left']/nest
- ${response}= Get On Session CPS_HOST ${uri} params=${params} expected_status=200
+ ${headers}= Create Dictionary Authorization=${auth}
+ ${response}= Get On Session CPS_HOST ${uri} params=${params} headers=${headers} expected_status=200
${responseJson}= Set Variable ${response.json()}
Should Be Equal As Strings ${responseJson['name']} Small
diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml
index fcb4a5381..a2241bcc2 100644
--- a/docker-compose/docker-compose.yml
+++ b/docker-compose/docker-compose.yml
@@ -20,7 +20,7 @@ version: "3.7"
services:
#cps-standalone:
# container_name: cps-service
- # image: ps-service:${VERSION}
+ # image: cps-service:${VERSION}
# volumes:
# - "./application.yml:/app/resources/application.yml"
# ports: