From 743380d1f171d4c0dd46dc0cd5b47d8ea93bea44 Mon Sep 17 00:00:00 2001 From: Renu Kumari Date: Tue, 17 Aug 2021 07:30:19 -0400 Subject: Add basic security to query interface - Added WebSecurity configuration and corresponding test case - Updated existing test cases to handle spring security - Moved QueryResponseFactory to QueryController to avoid cyclic dependency Issue-ID: CPS-530 Signed-off-by: Renu Kumari Change-Id: I7e03ed9ccf983090ce514873b86fc9b2f851ed4f --- .../controller/rest/ControllerSecuritySpec.groovy | 99 ++++++++++++++++++++++ .../controller/rest/QueryControllerSpec.groovy | 10 +-- .../cps/temporal/domain/SearchCriteriaSpec.groovy | 6 +- src/test/resources/application.yml | 9 +- 4 files changed, 115 insertions(+), 9 deletions(-) create mode 100644 src/test/groovy/org/onap/cps/temporal/controller/rest/ControllerSecuritySpec.groovy (limited to 'src/test') diff --git a/src/test/groovy/org/onap/cps/temporal/controller/rest/ControllerSecuritySpec.groovy b/src/test/groovy/org/onap/cps/temporal/controller/rest/ControllerSecuritySpec.groovy new file mode 100644 index 0000000..2ced672 --- /dev/null +++ b/src/test/groovy/org/onap/cps/temporal/controller/rest/ControllerSecuritySpec.groovy @@ -0,0 +1,99 @@ +/* + * ============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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.temporal.controller.rest + +import org.onap.cps.temporal.controller.rest.config.WebSecurityConfig +import org.onap.cps.temporal.controller.rest.model.AnchorDetailsMapper +import org.onap.cps.temporal.controller.rest.model.AnchorDetailsMapperImpl +import org.onap.cps.temporal.controller.rest.model.AnchorHistory +import org.onap.cps.temporal.controller.rest.model.SortMapper +import org.onap.cps.temporal.domain.NetworkData +import org.onap.cps.temporal.service.NetworkDataService +import org.spockframework.spring.SpringBean +import org.spockframework.spring.StubBeans +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest +import org.springframework.context.annotation.Import +import org.springframework.data.domain.Pageable +import org.springframework.data.domain.Slice +import org.springframework.data.domain.SliceImpl +import org.springframework.http.HttpHeaders +import org.springframework.http.HttpStatus +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.setup.MockMvcBuilders +import org.springframework.web.context.WebApplicationContext +import spock.lang.Shared +import spock.lang.Specification +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; + +@WebMvcTest(QueryController) +@Import([WebSecurityConfig, SortMapper, AnchorDetailsMapperImpl]) +class ControllerSecuritySpec extends Specification { + + @SpringBean + NetworkDataService mockNetworkDataService = Mock() { + searchNetworkData(_) >> new SliceImpl([], Pageable.ofSize(1), false) + } + + QueryController.QueryResponseFactory mockQueryResponseFactory = Mock() + + MockMvc mvc + + @Autowired + WebApplicationContext context + + @Shared + def testEndpoint = '/cps-temporal/api/v1/dataspaces/my-dataspace/anchors/my-anchor/history' + + def setup() { + mvc = MockMvcBuilders.webAppContextSetup(this.context).apply(springSecurity()).build(); + } + + def 'Get request with authentication: #scenario.'() { + given: 'authentication' + HttpHeaders httpHeaders = new HttpHeaders() + httpHeaders.setBasicAuth(username, password) + when: 'request is sent with authentication' + def response = mvc.perform(get(testEndpoint).headers(httpHeaders) + ).andReturn().response + then: 'expected http status is returned' + assert response.status == expectedHttpStatus.value() + where: + scenario | username | password || expectedHttpStatus + 'correct credentials' | 'testUser' | 'testPassword' || HttpStatus.OK + 'unknown username' | 'unknown-user' | 'password' || HttpStatus.UNAUTHORIZED + 'wrong password' | 'cpsuser' | 'wrong-password' || HttpStatus.UNAUTHORIZED + } + + def 'Get urls without authentication : #scenario.'() { + when: 'request is sent without authentication' + def response = mvc.perform(get(url) + ).andReturn().response + then: 'expected http status is returned' + assert response.status == expectedHttpStatus.value() + where: + scenario | url | expectedHttpStatus + 'permitted url' | '/swagger/openapi.yml' | HttpStatus.OK + 'not-permitted url' | testEndpoint | HttpStatus.UNAUTHORIZED + } + +} diff --git a/src/test/groovy/org/onap/cps/temporal/controller/rest/QueryControllerSpec.groovy b/src/test/groovy/org/onap/cps/temporal/controller/rest/QueryControllerSpec.groovy index a18a134..7847b34 100644 --- a/src/test/groovy/org/onap/cps/temporal/controller/rest/QueryControllerSpec.groovy +++ b/src/test/groovy/org/onap/cps/temporal/controller/rest/QueryControllerSpec.groovy @@ -21,8 +21,7 @@ package org.onap.cps.temporal.controller.rest import org.onap.cps.temporal.controller.utils.DateTimeUtility -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders - +import com.fasterxml.jackson.databind.ObjectMapper import java.time.OffsetDateTime import org.onap.cps.temporal.controller.rest.model.AnchorDetails import org.onap.cps.temporal.controller.rest.model.AnchorDetailsMapperImpl @@ -41,13 +40,14 @@ import org.springframework.data.domain.SliceImpl import org.springframework.data.domain.Sort import org.springframework.http.HttpStatus import org.springframework.http.MediaType +import org.springframework.security.test.context.support.WithMockUser import org.springframework.test.web.servlet.MockMvc -import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper -import spock.lang.Shared import spock.lang.Specification +import spock.lang.Shared @WebMvcTest(QueryController) -@Import([SortMapper, QueryResponseFactory, AnchorDetailsMapperImpl]) +@Import([SortMapper, AnchorDetailsMapperImpl]) +@WithMockUser class QueryControllerSpec extends Specification { @SpringBean diff --git a/src/test/groovy/org/onap/cps/temporal/domain/SearchCriteriaSpec.groovy b/src/test/groovy/org/onap/cps/temporal/domain/SearchCriteriaSpec.groovy index 3d6a354..32bc660 100644 --- a/src/test/groovy/org/onap/cps/temporal/domain/SearchCriteriaSpec.groovy +++ b/src/test/groovy/org/onap/cps/temporal/domain/SearchCriteriaSpec.groovy @@ -131,12 +131,12 @@ class SearchCriteriaSpec extends Specification { then: 'exception is thrown' def illegalArgumentException = thrown(IllegalArgumentException) def message = illegalArgumentException.getMessage(); - assert message.contains("sort") + assert message.contains('sort') assert message.contains(expectedExceptionMessage) where: scenario | sort | expectedExceptionMessage - 'null' | null | "null" - 'unsupported properties' | Sort.by(Sort.Direction.ASC, 'unsupported') | "Invalid sorting" + 'null' | null | 'null' + 'unsupported properties' | Sort.by(Sort.Direction.ASC, 'unsupported') | 'Invalid sorting' 'missing required sort' | Sort.by(Sort.Direction.ASC, 'anchor') | 'Missing mandatory sort' } diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index fce4a17..6765057 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -63,4 +63,11 @@ app: topic: cps.cfg-state-events query: response: - max-page-size: 20 \ No newline at end of file + max-page-size: 20 + +security: + # comma-separated uri patterns which do not require authorization + permit-uri: /manage/**,/swagger-ui/**,/swagger-resources/**,/swagger/openapi.yml + auth: + username: testUser + password: testPassword -- cgit 1.2.3-korg