diff options
author | Toine Siebelink <toine.siebelink@est.tech> | 2021-03-12 16:04:00 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@onap.org> | 2021-03-12 16:04:00 +0000 |
commit | 344434b68c2418c6e901e7acab02886be6e6b1cb (patch) | |
tree | 09c332bc664fe25513919c5f05a3c73fed6edb78 /cps-rest | |
parent | 3de08a9590b5590d5961d9e4047751760043307e (diff) | |
parent | 0d6bbae9baa4531f5b5c3009fa72e691cf30fe41 (diff) |
Merge "Move web security configuration to application module"
Diffstat (limited to 'cps-rest')
7 files changed, 30 insertions, 179 deletions
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 } |