From d8bcd1756df6bae0fc6b794ea4bf10a4e8b7e541 Mon Sep 17 00:00:00 2001 From: Ruslan Kashapov Date: Thu, 11 Mar 2021 14:15:49 +0200 Subject: Move web security configuration to application module Issue-ID: CPS-288 Change-Id: Id244cde9ae9c87c27b69ed5a6a51772a4aef62fd Signed-off-by: Ruslan Kashapov --- cps-application/pom.xml | 47 ++++++++++++- .../org/onap/cps/config/WebSecurityConfig.java | 82 ++++++++++++++++++++++ .../rest/controller/ControllerSecuritySpec.groovy | 62 ++++++++++++++++ .../onap/cps/rest/controller/TestController.java | 34 +++++++++ cps-application/src/test/resources/application.yml | 4 ++ cps-rest/pom.xml | 9 --- .../org/onap/cps/config/WebSecurityConfig.java | 61 ---------------- .../rest/controller/AdminRestControllerSpec.groovy | 34 +++------ .../rest/controller/DataRestControllerSpec.groovy | 11 ++- .../rest/controller/QueryRestControllerSpec.groovy | 12 ++-- .../controller/RestControllerSpecification.groovy | 34 --------- .../exceptions/CpsRestExceptionHandlerSpec.groovy | 39 +++------- docker-compose/application.yml | 7 ++ 13 files changed, 263 insertions(+), 173 deletions(-) create mode 100644 cps-application/src/main/java/org/onap/cps/config/WebSecurityConfig.java create mode 100755 cps-application/src/test/groovy/org/onap/cps/rest/controller/ControllerSecuritySpec.groovy create mode 100644 cps-application/src/test/java/org/onap/cps/rest/controller/TestController.java create mode 100644 cps-application/src/test/resources/application.yml delete mode 100644 cps-rest/src/main/java/org/onap/cps/config/WebSecurityConfig.java delete mode 100644 cps-rest/src/test/groovy/org/onap/cps/rest/controller/RestControllerSpecification.groovy diff --git a/cps-application/pom.xml b/cps-application/pom.xml index 19a720439..dd6ded0e7 100644 --- a/cps-application/pom.xml +++ b/cps-application/pom.xml @@ -36,7 +36,7 @@ org.onap.cps.Application ${project.version} 2.6.0 - 0.0 + 0.7 nexus3.onap.org:10003/onap/ @@ -51,6 +51,14 @@ + + org.springframework.boot + spring-boot-starter-jetty + + + org.springframework.boot + spring-boot-starter-security + org.springframework.boot spring-boot-starter-actuator @@ -59,6 +67,43 @@ org.springframework.cloud spring-cloud-starter-sleuth + + + org.springframework.security + spring-security-test + test + + + org.codehaus.groovy + groovy + test + + + org.spockframework + spock-core + test + + + org.spockframework + spock-spring + test + + + cglib + cglib-nodep + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + 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 000000000..fbf1be9a1 --- /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 000000000..01d373582 --- /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 000000000..34325c71a --- /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 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 000000000..56363e360 --- /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 be1c3f66d..e9dee5db0 100755 --- a/cps-rest/pom.xml +++ b/cps-rest/pom.xml @@ -53,10 +53,6 @@ org.springframework.boot spring-boot-starter-jetty - - org.springframework.boot - spring-boot-starter-security - io.swagger.core.v3 swagger-annotations @@ -109,11 +105,6 @@ - - org.springframework.security - spring-security-test - test - 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 943e02c27..000000000 --- a/cps-rest/src/main/java/org/onap/cps/config/WebSecurityConfig.java +++ /dev/null @@ -1,61 +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 - 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 f38193803..5b5be1c16 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 ef834a7a2..15627d598 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 907528a55..f3f1417f1 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 a700ea213..000000000 --- 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 5ddc9d95b..bb9199e6a 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.DataInUseException @@ -42,17 +47,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() @@ -166,25 +165,6 @@ 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 JSON by id' endpoint and associated service method invocation * to test the exception handling. The endpoint chosen is not a subject of test. @@ -197,8 +177,7 @@ class CpsRestExceptionHandlerSpec extends RestControllerSpecification { def performTestRequest() { return mvc.perform( get("$basePath/v1/dataspaces/dataspace-name/anchors") - .header("Authorization", getAuthorizationHeader())) - .andReturn().response + ).andReturn().response } void assertTestResponse(response, expectedStatus, diff --git a/docker-compose/application.yml b/docker-compose/application.yml index 9b841cb86..be4b688cf 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: -- cgit 1.2.3-korg