diff options
13 files changed, 265 insertions, 180 deletions
diff --git a/cps-application/pom.xml b/cps-application/pom.xml index d64a3bfbf7..53ba1c9658 100644 --- a/cps-application/pom.xml +++ b/cps-application/pom.xml @@ -36,7 +36,7 @@ <app>org.onap.cps.Application</app> <image.version>${project.version}</image.version> <jib-maven-plugin.version>2.6.0</jib-maven-plugin.version> - <minimum-coverage>0.0</minimum-coverage> + <minimum-coverage>0.7</minimum-coverage> <nexus.repository>nexus3.onap.org:10003/onap/</nexus.repository> </properties> @@ -53,12 +53,57 @@ </dependency> <dependency> <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-jetty</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-security</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency> + <!-- T E S T D E P E N D E N C I E S --> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-test</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.codehaus.groovy</groupId> + <artifactId>groovy</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.spockframework</groupId> + <artifactId>spock-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.spockframework</groupId> + <artifactId>spock-spring</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>cglib</groupId> + <artifactId>cglib-nodep</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>org.junit.vintage</groupId> + <artifactId>junit-vintage-engine</artifactId> + </exclusion> + </exclusions> + </dependency> </dependencies> <build> diff --git a/cps-application/src/main/java/org/onap/cps/config/WebSecurityConfig.java b/cps-application/src/main/java/org/onap/cps/config/WebSecurityConfig.java new file mode 100644 index 0000000000..fbf1be9a18 --- /dev/null +++ b/cps-application/src/main/java/org/onap/cps/config/WebSecurityConfig.java @@ -0,0 +1,82 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (c) 2021 Bell Canada. + * Modification Copyright (C) 2021 Pantheon.tech + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +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 USER_ROLE = "USER"; + + private final String username; + private final String password; + private final String[] permitUris; + + /** + * Constructor. Accepts parameters from configuration. + * + * @param permitUris comma-separated list of uri patterns for endpoints permitted + * @param username username + * @param password password + */ + public WebSecurityConfig( + @Autowired @Value("${security.permit-uri}") final String permitUris, + @Autowired @Value("${security.auth.username}") final String username, + @Autowired @Value("${security.auth.password}") final String password + ) { + super(); + this.permitUris = permitUris.isEmpty() ? new String[] {"/v3/api-docs"} : permitUris.split("\\s*,\\s*"); + this.username = username; + this.password = password; + } + + @Override + // The team decided to disable default CSRF Spring protection and not implement CSRF tokens validation. + // CPS is a stateless REST API that is not as vulnerable to CSRF attacks as web applications running in + // web browsers are. CPS does not manage sessions, each request requires the authentication token in the header. + // See https://docs.spring.io/spring-security/site/docs/5.3.8.RELEASE/reference/html5/#csrf + @SuppressWarnings("squid:S4502") + protected void configure(final HttpSecurity http) throws Exception { + http + .csrf().disable() + .authorizeRequests() + .antMatchers(permitUris).permitAll() + .anyRequest().authenticated() + .and().httpBasic(); + } + + @Override + protected void configure(final AuthenticationManagerBuilder auth) throws Exception { + auth.inMemoryAuthentication().withUser(username).password("{noop}" + password).roles(USER_ROLE); + } +} diff --git a/cps-application/src/test/groovy/org/onap/cps/rest/controller/ControllerSecuritySpec.groovy b/cps-application/src/test/groovy/org/onap/cps/rest/controller/ControllerSecuritySpec.groovy new file mode 100755 index 0000000000..01d3735827 --- /dev/null +++ b/cps-application/src/test/groovy/org/onap/cps/rest/controller/ControllerSecuritySpec.groovy @@ -0,0 +1,62 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Pantheon.tech + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.rest.controller + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest +import org.springframework.http.HttpStatus +import org.springframework.test.web.servlet.MockMvc +import spock.lang.Specification + +@WebMvcTest(controllers = TestController.class) +class ControllerSecuritySpec extends Specification { + + @Autowired + MockMvc mvc + + def testEndpoint = '/test' + + def 'Get request with authentication'() { + when: 'request is sent with authentication' + def response = mvc.perform( + get(testEndpoint).header("Authorization", 'Basic Y3BzdXNlcjpjcHNyMGNrcyE=') + ).andReturn().response + then: 'HTTP OK status code is returned' + assert response.status == HttpStatus.OK.value() + } + + def 'Get request without authentication is not authorized'() { + when: 'request is sent without authentication' + def response = mvc.perform(get(testEndpoint)).andReturn().response + then: 'HTTP Unauthorized status code is returned' + assert response.status == HttpStatus.UNAUTHORIZED.value() + } + + def 'Get request with invalid authentication is not authorized'() { + when: 'request is sent with invalid authentication' + def response = mvc.perform( + get(testEndpoint).header("Authorization", 'Basic invalid auth') + ).andReturn().response + then: 'HTTP Unauthorized status code is returned' + assert response.status == HttpStatus.UNAUTHORIZED.value() + } +} diff --git a/cps-application/src/test/java/org/onap/cps/rest/controller/TestController.java b/cps-application/src/test/java/org/onap/cps/rest/controller/TestController.java new file mode 100644 index 0000000000..34325c71ac --- /dev/null +++ b/cps-application/src/test/java/org/onap/cps/rest/controller/TestController.java @@ -0,0 +1,34 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Pantheon.tech + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.rest.controller; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class TestController { + + @GetMapping("/test") + ResponseEntity<String> test() { + return new ResponseEntity<>(HttpStatus.OK); + } +} diff --git a/cps-application/src/test/resources/application.yml b/cps-application/src/test/resources/application.yml new file mode 100644 index 0000000000..56363e3602 --- /dev/null +++ b/cps-application/src/test/resources/application.yml @@ -0,0 +1,4 @@ +security: + auth: + username: cpsuser + password: cpsr0cks! diff --git a/cps-rest/pom.xml b/cps-rest/pom.xml index a2f10d542d..ce304f3328 100755 --- a/cps-rest/pom.xml +++ b/cps-rest/pom.xml @@ -54,10 +54,6 @@ <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> @@ -109,11 +105,6 @@ </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 deleted file mode 100644 index 5538341118..0000000000 --- a/cps-rest/src/main/java/org/onap/cps/config/WebSecurityConfig.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * ============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 - // The team decided to disable default CSRF Spring protection and not implement CSRF tokens validation. - // CPS is a stateless REST API that is not as vulnerable to CSRF attacks as web applications running in - // web browsers are. CPS does not manage sessions, each request requires the authentication token in the header. - // See https://docs.spring.io/spring-security/site/docs/5.3.8.RELEASE/reference/html5/#csrf - @SuppressWarnings("squid:S4502") - 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 f38193803a..5b5be1c161 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 @@ -46,10 +46,11 @@ 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 RestControllerSpecification { +class AdminRestControllerSpec extends Specification { @SpringBean CpsModuleService mockCpsModuleService = Mock() @@ -85,7 +86,6 @@ class AdminRestControllerSpec extends RestControllerSpecification { def response = mvc.perform( post(createDataspaceEndpoint) - .header("Authorization", getAuthorizationHeader()) .param('dataspace-name', dataspaceName)) .andReturn().response then: 'service method is invoked with expected parameters' @@ -104,7 +104,6 @@ class AdminRestControllerSpec extends RestControllerSpecification { def response = mvc.perform( post(createDataspaceEndpoint) - .header("Authorization", getAuthorizationHeader()) .param('dataspace-name', dataspaceName)) .andReturn().response then: 'dataspace creation fails' @@ -122,7 +121,6 @@ class AdminRestControllerSpec extends RestControllerSpecification { 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' @@ -144,7 +142,6 @@ class AdminRestControllerSpec extends RestControllerSpecification { 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' @@ -165,7 +162,6 @@ class AdminRestControllerSpec extends RestControllerSpecification { mvc.perform( multipart(schemaSetEndpoint) .file(multipartFile) - .header("Authorization", getAuthorizationHeader()) .param('schema-set-name', schemaSetName)) .andReturn().response then: 'create schema set rejected' @@ -186,7 +182,6 @@ class AdminRestControllerSpec extends RestControllerSpecification { mvc.perform( multipart(schemaSetEndpoint) .file(multipartFile) - .header("Authorization", getAuthorizationHeader()) .param('schema-set-name', schemaSetName)) .andReturn().response then: 'create schema set rejected' @@ -203,7 +198,6 @@ class AdminRestControllerSpec extends RestControllerSpecification { 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' @@ -216,9 +210,7 @@ class AdminRestControllerSpec extends RestControllerSpecification { 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).header("Authorization", getAuthorizationHeader())) - .andReturn().response + def response = mvc.perform(delete(schemaSetEndpoint)).andReturn().response then: 'associated service method is invoked with expected parameters' 1 * mockCpsModuleService.deleteSchemaSet(dataspaceName, schemaSetName, CASCADE_DELETE_PROHIBITED) and: 'response code indicates success' @@ -233,9 +225,7 @@ class AdminRestControllerSpec extends RestControllerSpecification { 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).header("Authorization", getAuthorizationHeader())) - .andReturn().response + def response = mvc.perform(delete(schemaSetEndpoint)).andReturn().response then: 'schema set deletion fails with conflict response code' response.status == HttpStatus.CONFLICT.value() } @@ -247,9 +237,7 @@ class AdminRestControllerSpec extends RestControllerSpecification { 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).header("Authorization", getAuthorizationHeader())) - .andReturn().response + def response = mvc.perform(get(schemaSetEndpoint)).andReturn().response then: 'the correct schema set is returned' response.status == HttpStatus.OK.value() response.getContentAsString().contains(schemaSetName) @@ -266,7 +254,6 @@ class AdminRestControllerSpec extends RestControllerSpecification { def response = mvc.perform( post(anchorEndpoint).contentType(MediaType.APPLICATION_JSON) - .header("Authorization", getAuthorizationHeader()) .params(requestParams as MultiValueMap)) .andReturn().response then: 'anchor is created successfully' @@ -281,9 +268,7 @@ class AdminRestControllerSpec extends RestControllerSpecification { and: 'an endpoint' def anchorEndpoint = "$basePath/v1/dataspaces/$dataspaceName/anchors" when: 'get all anchors API is invoked' - def response = - mvc.perform(get(anchorEndpoint).header("Authorization", getAuthorizationHeader())) - .andReturn().response + def response = mvc.perform(get(anchorEndpoint)).andReturn().response then: 'the correct anchor is returned' response.status == HttpStatus.OK.value() response.getContentAsString().contains(anchorName) @@ -291,13 +276,12 @@ class AdminRestControllerSpec extends RestControllerSpecification { def 'Get existing anchor by dataspace and anchor name.'() { given: 'service method returns an anchor' - mockCpsAdminService.getAnchor(dataspaceName,anchorName) >> new Anchor(name: anchorName, dataspaceName: dataspaceName, schemaSetName:schemaSetName) + mockCpsAdminService.getAnchor(dataspaceName, anchorName) >> + new Anchor(name: anchorName, dataspaceName: dataspaceName, schemaSetName: schemaSetName) and: 'an endpoint' def anchorEndpoint = "$basePath/v1/dataspaces/$dataspaceName/anchors/$anchorName" when: 'get anchor API is invoked' - def response = - mvc.perform(get(anchorEndpoint).header("Authorization", getAuthorizationHeader())) - .andReturn().response + def response = mvc.perform(get(anchorEndpoint)).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 ef834a7a2a..15627d5982 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 @@ -46,10 +46,11 @@ 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 RestControllerSpecification { +class DataRestControllerSpec extends Specification { @SpringBean CpsDataService mockCpsDataService = Mock() @@ -96,7 +97,6 @@ class DataRestControllerSpec extends RestControllerSpecification { def response = mvc.perform( post(endpoint) - .header("Authorization", getAuthorizationHeader()) .contentType(MediaType.APPLICATION_JSON).content(json)) .andReturn().response then: 'a created response is returned' @@ -113,7 +113,7 @@ class DataRestControllerSpec extends RestControllerSpecification { mockCpsDataService.getDataNode(dataspaceName, anchorName, xpath, OMIT_DESCENDANTS) >> dataNodeWithLeavesNoChildren when: 'get request is performed through REST API' def response = - mvc.perform(get(endpoint).header("Authorization", getAuthorizationHeader()).param('xpath', xpath)) + mvc.perform(get(endpoint).param('xpath', xpath)) .andReturn().response then: 'a success response is returned' response.status == HttpStatus.OK.value() @@ -133,7 +133,6 @@ class DataRestControllerSpec extends RestControllerSpecification { def response = mvc.perform( get(endpoint) - .header("Authorization", getAuthorizationHeader()) .param('xpath', xpath) .param('include-descendants', includeDescendantsOption)) .andReturn().response @@ -155,7 +154,7 @@ class DataRestControllerSpec extends RestControllerSpecification { mockCpsDataService.getDataNode(dataspaceName, anchorName, xpath, _) >> { throw exception } when: 'get request is performed through REST API' def response = - mvc.perform(get(endpoint).header("Authorization", getAuthorizationHeader()).param("xpath", xpath)) + mvc.perform(get(endpoint).param("xpath", xpath)) .andReturn().response then: 'a success response is returned' response.status == httpStatus.value() @@ -178,7 +177,6 @@ class DataRestControllerSpec extends RestControllerSpecification { 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' @@ -202,7 +200,6 @@ class DataRestControllerSpec extends RestControllerSpecification { 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' 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 907528a559..f3f1417f13 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 @@ -20,6 +20,10 @@ package org.onap.cps.rest.controller +import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS +import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get + import com.google.gson.Gson import org.modelmapper.ModelMapper import org.onap.cps.api.CpsAdminService @@ -33,14 +37,11 @@ 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.Specification import spock.lang.Unroll -import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS -import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get - @WebMvcTest -class QueryRestControllerSpec extends RestControllerSpecification { +class QueryRestControllerSpec extends Specification { @SpringBean CpsDataService mockCpsDataService = Mock() @@ -77,7 +78,6 @@ class QueryRestControllerSpec extends RestControllerSpecification { def response = mvc.perform( get(dataNodeEndpoint) - .header("Authorization", getAuthorizationHeader()) .param('cps-path', cpsPath) .param('include-descendants', includeDescendantsOption)) .andReturn().response 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 deleted file mode 100644 index a700ea2132..0000000000 --- a/cps-rest/src/test/groovy/org/onap/cps/rest/controller/RestControllerSpecification.groovy +++ /dev/null @@ -1,34 +0,0 @@ -/* - * ============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 22b5b04292..fb4037271b 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 @@ -21,13 +21,18 @@ package org.onap.cps.rest.exceptions +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.test.web.servlet.request.MockMvcRequestBuilders.get + import groovy.json.JsonSlurper 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.rest.controller.RestControllerSpecification import org.onap.cps.spi.exceptions.AnchorAlreadyDefinedException import org.onap.cps.spi.exceptions.CpsException import org.onap.cps.spi.exceptions.CpsPathException @@ -43,17 +48,11 @@ 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 RestControllerSpecification { +class CpsRestExceptionHandlerSpec extends Specification { @SpringBean CpsAdminService mockCpsAdminService = Mock() @@ -139,7 +138,7 @@ class CpsRestExceptionHandlerSpec extends RestControllerSpecification { where: 'the following exceptions are thrown' exceptionThrown << [new ModelValidationException(errorMessage, errorDetails, null), new DataValidationException(errorMessage, errorDetails, null), - new CpsPathException(errorMessage,errorDetails)] + new CpsPathException(errorMessage, errorDetails)] } @Unroll @@ -154,37 +153,17 @@ class CpsRestExceptionHandlerSpec extends RestControllerSpecification { 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 anchors' endpoint and associated service method invocation * to test the exception handling. The endpoint chosen is not a subject of test. */ def setupTestException(exception) { - mockCpsAdminService.getAnchors(_) >> { throw exception} + mockCpsAdminService.getAnchors(_) >> { throw exception } } def performTestRequest() { return mvc.perform( - get("$basePath/v1/dataspaces/dataspace-name/anchors") - .header("Authorization", getAuthorizationHeader())) + get("$basePath/v1/dataspaces/dataspace-name/anchors")) .andReturn().response } diff --git a/docker-compose/application.yml b/docker-compose/application.yml index 9b841cb860..be4b688cf3 100644 --- a/docker-compose/application.yml +++ b/docker-compose/application.yml @@ -34,6 +34,13 @@ spring: change-log: classpath:changelog/changelog-master.yaml
labels: ${LIQUIBASE_LABELS}
+security:
+ # comma-separated uri patterns which do not require authorization
+ permit-uri: /manage/health/**,/manage/info,/swagger-ui/**,/swagger-resources/**,/v3/api-docs
+ auth:
+ username: ${CPS_USERNAME:cpsuser}
+ password: ${CPS_PASSWORD:cpsr0cks!}
+
# Actuator
management:
endpoints:
|