|
@@ -1,400 +0,0 @@
|
|
|
-/*
|
|
|
- * Copyright 2002-2017 the original author or authors.
|
|
|
- *
|
|
|
- * 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.
|
|
|
- */
|
|
|
-package org.springframework.security.config.http
|
|
|
-
|
|
|
-import javax.servlet.ServletContext
|
|
|
-import javax.servlet.ServletRegistration
|
|
|
-import javax.servlet.http.HttpServletResponse
|
|
|
-
|
|
|
-import org.mockito.invocation.InvocationOnMock
|
|
|
-import org.mockito.stubbing.Answer
|
|
|
-
|
|
|
-import org.springframework.beans.factory.parsing.BeanDefinitionParsingException
|
|
|
-import org.springframework.mock.web.MockFilterChain
|
|
|
-import org.springframework.mock.web.MockHttpServletRequest
|
|
|
-import org.springframework.mock.web.MockHttpServletResponse
|
|
|
-import org.springframework.mock.web.MockServletContext
|
|
|
-import org.springframework.security.access.SecurityConfig
|
|
|
-import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
|
|
-import org.springframework.web.bind.annotation.RequestMapping
|
|
|
-import org.springframework.web.bind.annotation.RestController
|
|
|
-
|
|
|
-import static org.mockito.Mockito.*
|
|
|
-/**
|
|
|
- *
|
|
|
- * @author Rob Winch
|
|
|
- */
|
|
|
-class InterceptUrlConfigTests extends AbstractHttpConfigTests {
|
|
|
-
|
|
|
- def "SEC-2256: intercept-url method is not given priority"() {
|
|
|
- when:
|
|
|
- httpAutoConfig {
|
|
|
- 'intercept-url'(pattern: '/anyurl', access: "ROLE_USER")
|
|
|
- 'intercept-url'(pattern: '/anyurl', 'method':'GET',access: 'ROLE_ADMIN')
|
|
|
- }
|
|
|
- createAppContext()
|
|
|
-
|
|
|
- def fids = getFilter(FilterSecurityInterceptor).securityMetadataSource
|
|
|
- def attrs = fids.getAttributes(createFilterinvocation("/anyurl", "GET"))
|
|
|
- def attrsPost = fids.getAttributes(createFilterinvocation("/anyurl", "POST"))
|
|
|
-
|
|
|
- then:
|
|
|
- attrs.size() == 1
|
|
|
- attrs.contains(new SecurityConfig("ROLE_USER"))
|
|
|
- attrsPost.size() == 1
|
|
|
- attrsPost.contains(new SecurityConfig("ROLE_USER"))
|
|
|
- }
|
|
|
-
|
|
|
- def "SEC-2355: intercept-url support patch"() {
|
|
|
- setup:
|
|
|
- MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
|
|
|
- MockHttpServletResponse response = new MockHttpServletResponse()
|
|
|
- MockFilterChain chain = new MockFilterChain()
|
|
|
- xml.http('use-expressions':false) {
|
|
|
- 'http-basic'()
|
|
|
- 'intercept-url'(pattern: '/**', 'method':'PATCH',access: 'ROLE_ADMIN')
|
|
|
- csrf(disabled:true)
|
|
|
- }
|
|
|
- createAppContext()
|
|
|
- when: 'Method other than PATCH is used'
|
|
|
- springSecurityFilterChain.doFilter(request,response,chain)
|
|
|
- then: 'The response is OK'
|
|
|
- response.status == HttpServletResponse.SC_OK
|
|
|
- when: 'Method of PATCH is used'
|
|
|
- request = new MockHttpServletRequest(method:'PATCH')
|
|
|
- response = new MockHttpServletResponse()
|
|
|
- chain = new MockFilterChain()
|
|
|
- springSecurityFilterChain.doFilter(request, response, chain)
|
|
|
- then: 'The response is unauthorized'
|
|
|
- response.status == HttpServletResponse.SC_UNAUTHORIZED
|
|
|
- }
|
|
|
-
|
|
|
- def "intercept-url supports hasAnyRoles"() {
|
|
|
- setup:
|
|
|
- MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
|
|
|
- MockHttpServletResponse response = new MockHttpServletResponse()
|
|
|
- MockFilterChain chain = new MockFilterChain()
|
|
|
- xml.http('use-expressions':true) {
|
|
|
- 'http-basic'()
|
|
|
- 'intercept-url'(pattern: '/**', access: "hasAnyRole('ROLE_DEVELOPER','ROLE_USER')")
|
|
|
- csrf(disabled:true)
|
|
|
- }
|
|
|
- when:
|
|
|
- createAppContext()
|
|
|
- then: 'no error'
|
|
|
- noExceptionThrown()
|
|
|
- when: 'ROLE_USER can access'
|
|
|
- login(request, 'user', 'password')
|
|
|
- springSecurityFilterChain.doFilter(request,response,chain)
|
|
|
- then: 'The response is OK'
|
|
|
- response.status == HttpServletResponse.SC_OK
|
|
|
- when: 'ROLE_A cannot access'
|
|
|
- request = new MockHttpServletRequest(method:'GET')
|
|
|
- response = new MockHttpServletResponse()
|
|
|
- chain = new MockFilterChain()
|
|
|
- login(request, 'bob', 'bobspassword')
|
|
|
- springSecurityFilterChain.doFilter(request,response,chain)
|
|
|
- then: 'The response is Forbidden'
|
|
|
- response.status == HttpServletResponse.SC_FORBIDDEN
|
|
|
- }
|
|
|
-
|
|
|
- def "SEC-2256: intercept-url supports path variables"() {
|
|
|
- setup:
|
|
|
- MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
|
|
|
- MockHttpServletResponse response = new MockHttpServletResponse()
|
|
|
- MockFilterChain chain = new MockFilterChain()
|
|
|
- xml.http('use-expressions':true) {
|
|
|
- 'http-basic'()
|
|
|
- 'intercept-url'(pattern: '/user/{un}/**', access: "#un == authentication.name")
|
|
|
- 'intercept-url'(pattern: '/**', access: "denyAll")
|
|
|
- }
|
|
|
- createAppContext()
|
|
|
- login(request, 'user', 'password')
|
|
|
- when: 'user can access'
|
|
|
- request.servletPath = '/user/user/abc'
|
|
|
- springSecurityFilterChain.doFilter(request,response,chain)
|
|
|
- then: 'The response is OK'
|
|
|
- response.status == HttpServletResponse.SC_OK
|
|
|
- when: 'user cannot access otheruser'
|
|
|
- request = new MockHttpServletRequest(method:'GET', servletPath : '/user/otheruser/abc')
|
|
|
- login(request, 'user', 'password')
|
|
|
- response = new MockHttpServletResponse()
|
|
|
- chain.reset()
|
|
|
- springSecurityFilterChain.doFilter(request,response,chain)
|
|
|
- then: 'The response is OK'
|
|
|
- response.status == HttpServletResponse.SC_FORBIDDEN
|
|
|
- when: 'user can access case insensitive URL'
|
|
|
- request = new MockHttpServletRequest(method:'GET', servletPath : '/USER/user/abc')
|
|
|
- login(request, 'user', 'password')
|
|
|
- response = new MockHttpServletResponse()
|
|
|
- chain.reset()
|
|
|
- springSecurityFilterChain.doFilter(request,response,chain)
|
|
|
- then: 'The response is OK'
|
|
|
- response.status == HttpServletResponse.SC_FORBIDDEN
|
|
|
- }
|
|
|
-
|
|
|
- def "gh-3786 intercept-url supports cammel case path variables"() {
|
|
|
- setup:
|
|
|
- MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
|
|
|
- MockHttpServletResponse response = new MockHttpServletResponse()
|
|
|
- MockFilterChain chain = new MockFilterChain()
|
|
|
- xml.http('use-expressions':true) {
|
|
|
- 'http-basic'()
|
|
|
- 'intercept-url'(pattern: '/user/{userName}/**', access: "#userName == authentication.name")
|
|
|
- 'intercept-url'(pattern: '/**', access: "denyAll")
|
|
|
- }
|
|
|
- createAppContext()
|
|
|
- login(request, 'user', 'password')
|
|
|
- when: 'user can access'
|
|
|
- request.servletPath = '/user/user/abc'
|
|
|
- springSecurityFilterChain.doFilter(request,response,chain)
|
|
|
- then: 'The response is OK'
|
|
|
- response.status == HttpServletResponse.SC_OK
|
|
|
- when: 'user cannot access otheruser'
|
|
|
- request = new MockHttpServletRequest(method:'GET', servletPath : '/user/otheruser/abc')
|
|
|
- login(request, 'user', 'password')
|
|
|
- response = new MockHttpServletResponse()
|
|
|
- chain.reset()
|
|
|
- springSecurityFilterChain.doFilter(request,response,chain)
|
|
|
- then: 'The response is OK'
|
|
|
- response.status == HttpServletResponse.SC_FORBIDDEN
|
|
|
- when: 'user can access case insensitive URL'
|
|
|
- request = new MockHttpServletRequest(method:'GET', servletPath : '/USER/user/abc')
|
|
|
- login(request, 'user', 'password')
|
|
|
- response = new MockHttpServletResponse()
|
|
|
- chain.reset()
|
|
|
- springSecurityFilterChain.doFilter(request,response,chain)
|
|
|
- then: 'The response is OK'
|
|
|
- response.status == HttpServletResponse.SC_FORBIDDEN
|
|
|
- }
|
|
|
-
|
|
|
- def "SEC-2256: intercept-url supports path variable type conversion"() {
|
|
|
- setup:
|
|
|
- MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
|
|
|
- MockHttpServletResponse response = new MockHttpServletResponse()
|
|
|
- MockFilterChain chain = new MockFilterChain()
|
|
|
- xml.http('use-expressions':true) {
|
|
|
- 'http-basic'()
|
|
|
- 'intercept-url'(pattern: '/user/{un}/**', access: "@id.isOne(#un)")
|
|
|
- 'intercept-url'(pattern: '/**', access: "denyAll")
|
|
|
- }
|
|
|
- bean('id', Id)
|
|
|
- createAppContext()
|
|
|
- login(request, 'user', 'password')
|
|
|
- when: 'can access id == 1'
|
|
|
- request.servletPath = '/user/1/abc'
|
|
|
- springSecurityFilterChain.doFilter(request,response,chain)
|
|
|
- then: 'The response is OK'
|
|
|
- response.status == HttpServletResponse.SC_OK
|
|
|
- when: 'user cannot access 2'
|
|
|
- request = new MockHttpServletRequest(method:'GET', servletPath : '/user/2/abc')
|
|
|
- login(request, 'user', 'password')
|
|
|
- chain.reset()
|
|
|
- springSecurityFilterChain.doFilter(request,response,chain)
|
|
|
- then: 'The response is OK'
|
|
|
- response.status == HttpServletResponse.SC_FORBIDDEN
|
|
|
- }
|
|
|
-
|
|
|
- def "intercept-url supports mvc matchers"() {
|
|
|
- setup:
|
|
|
- MockServletContext servletContext = mockServletContext();
|
|
|
- MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
|
|
|
- MockHttpServletResponse response = new MockHttpServletResponse()
|
|
|
- MockFilterChain chain = new MockFilterChain()
|
|
|
- xml.http('request-matcher':'mvc') {
|
|
|
- 'http-basic'()
|
|
|
- 'intercept-url'(pattern: '/path', access: "denyAll")
|
|
|
- }
|
|
|
- bean('pathController',PathController)
|
|
|
- xml.'mvc:annotation-driven'()
|
|
|
-
|
|
|
- createWebAppContext(servletContext)
|
|
|
- when:
|
|
|
- request.servletPath = "/path"
|
|
|
- springSecurityFilterChain.doFilter(request, response, chain)
|
|
|
- then:
|
|
|
- response.status == HttpServletResponse.SC_UNAUTHORIZED
|
|
|
- when:
|
|
|
- request = new MockHttpServletRequest(method:'GET')
|
|
|
- response = new MockHttpServletResponse()
|
|
|
- chain = new MockFilterChain()
|
|
|
- request.servletPath = "/path.html"
|
|
|
- springSecurityFilterChain.doFilter(request, response, chain)
|
|
|
- then:
|
|
|
- response.status == HttpServletResponse.SC_UNAUTHORIZED
|
|
|
- when:
|
|
|
- request = new MockHttpServletRequest(method:'GET')
|
|
|
- response = new MockHttpServletResponse()
|
|
|
- chain = new MockFilterChain()
|
|
|
- request.servletPath = "/path/"
|
|
|
- springSecurityFilterChain.doFilter(request, response, chain)
|
|
|
- then:
|
|
|
- response.status == HttpServletResponse.SC_UNAUTHORIZED
|
|
|
- }
|
|
|
-
|
|
|
- def "intercept-url mvc supports path variables"() {
|
|
|
- setup:
|
|
|
- MockServletContext servletContext = mockServletContext();
|
|
|
- MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
|
|
|
- MockHttpServletResponse response = new MockHttpServletResponse()
|
|
|
- MockFilterChain chain = new MockFilterChain()
|
|
|
- xml.http('request-matcher':'mvc') {
|
|
|
- 'http-basic'()
|
|
|
- 'intercept-url'(pattern: '/user/{un}/**', access: "#un == 'user'")
|
|
|
- }
|
|
|
- xml.'mvc:annotation-driven'()
|
|
|
- createWebAppContext(servletContext)
|
|
|
- when: 'user can access'
|
|
|
- request.servletPath = '/user/user/abc'
|
|
|
- springSecurityFilterChain.doFilter(request,response,chain)
|
|
|
- then: 'The response is OK'
|
|
|
- response.status == HttpServletResponse.SC_OK
|
|
|
- when: 'cannot access otheruser'
|
|
|
- request = new MockHttpServletRequest(method:'GET', servletPath : '/user/otheruser/abc')
|
|
|
- login(request, 'user', 'password')
|
|
|
- chain.reset()
|
|
|
- springSecurityFilterChain.doFilter(request,response,chain)
|
|
|
- then: 'The response is OK'
|
|
|
- response.status == HttpServletResponse.SC_FORBIDDEN
|
|
|
- when: 'user can access case insensitive URL'
|
|
|
- request = new MockHttpServletRequest(method:'GET', servletPath : '/USER/user/abc')
|
|
|
- login(request, 'user', 'password')
|
|
|
- chain.reset()
|
|
|
- springSecurityFilterChain.doFilter(request,response,chain)
|
|
|
- then: 'The response is OK'
|
|
|
- response.status == HttpServletResponse.SC_FORBIDDEN
|
|
|
- }
|
|
|
-
|
|
|
- def "intercept-url mvc matchers with servlet path"() {
|
|
|
- setup:
|
|
|
- MockServletContext servletContext = mockServletContext("/spring");
|
|
|
- MockHttpServletRequest request = new MockHttpServletRequest(method:'GET')
|
|
|
- MockHttpServletResponse response = new MockHttpServletResponse()
|
|
|
- MockFilterChain chain = new MockFilterChain()
|
|
|
- xml.http('request-matcher':'mvc') {
|
|
|
- 'http-basic'()
|
|
|
- 'intercept-url'(pattern: '/path', access: "denyAll", 'servlet-path': "/spring")
|
|
|
- }
|
|
|
- bean('pathController',PathController)
|
|
|
- xml.'mvc:annotation-driven'()
|
|
|
- createWebAppContext(servletContext)
|
|
|
- when:
|
|
|
- request.servletPath = "/spring"
|
|
|
- request.requestURI = "/spring/path"
|
|
|
- springSecurityFilterChain.doFilter(request, response, chain)
|
|
|
- then:
|
|
|
- response.status == HttpServletResponse.SC_UNAUTHORIZED
|
|
|
- when:
|
|
|
- request = new MockHttpServletRequest(method:'GET')
|
|
|
- response = new MockHttpServletResponse()
|
|
|
- chain = new MockFilterChain()
|
|
|
- request.servletPath = "/spring"
|
|
|
- request.requestURI = "/spring/path.html"
|
|
|
- springSecurityFilterChain.doFilter(request, response, chain)
|
|
|
- then:
|
|
|
- response.status == HttpServletResponse.SC_UNAUTHORIZED
|
|
|
- when:
|
|
|
- request = new MockHttpServletRequest(method:'GET')
|
|
|
- response = new MockHttpServletResponse()
|
|
|
- chain = new MockFilterChain()
|
|
|
- request.servletPath = "/spring"
|
|
|
- request.requestURI = "/spring/path/"
|
|
|
- springSecurityFilterChain.doFilter(request, response, chain)
|
|
|
- then:
|
|
|
- response.status == HttpServletResponse.SC_UNAUTHORIZED
|
|
|
- }
|
|
|
-
|
|
|
- def "intercept-url ant matcher with servlet path fails"() {
|
|
|
- when:
|
|
|
- xml.http('request-matcher':'ant') {
|
|
|
- 'http-basic'()
|
|
|
- 'intercept-url'(pattern: '/path', access: "denyAll", 'servlet-path': "/spring")
|
|
|
- }
|
|
|
- createAppContext()
|
|
|
- then:
|
|
|
- thrown(BeanDefinitionParsingException)
|
|
|
- }
|
|
|
-
|
|
|
- def "intercept-url regex matcher with servlet path fails"() {
|
|
|
- when:
|
|
|
- xml.http('request-matcher':'regex') {
|
|
|
- 'http-basic'()
|
|
|
- 'intercept-url'(pattern: '/path', access: "denyAll", 'servlet-path': "/spring")
|
|
|
- }
|
|
|
- createAppContext()
|
|
|
- then:
|
|
|
- thrown(BeanDefinitionParsingException)
|
|
|
- }
|
|
|
-
|
|
|
- def "intercept-url ciRegex matcher with servlet path fails"() {
|
|
|
- when:
|
|
|
- xml.http('request-matcher':'ciRegex') {
|
|
|
- 'http-basic'()
|
|
|
- 'intercept-url'(pattern: '/path', access: "denyAll", 'servlet-path': "/spring")
|
|
|
- }
|
|
|
- createAppContext()
|
|
|
- then:
|
|
|
- thrown(BeanDefinitionParsingException)
|
|
|
- }
|
|
|
-
|
|
|
- def "intercept-url default matcher with servlet path fails"() {
|
|
|
- when:
|
|
|
- xml.http() {
|
|
|
- 'http-basic'()
|
|
|
- 'intercept-url'(pattern: '/path', access: "denyAll", 'servlet-path': "/spring")
|
|
|
- }
|
|
|
- createAppContext()
|
|
|
- then:
|
|
|
- thrown(BeanDefinitionParsingException)
|
|
|
- }
|
|
|
-
|
|
|
- public static class Id {
|
|
|
- public boolean isOne(int i) {
|
|
|
- return i == 1;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private ServletContext mockServletContext() {
|
|
|
- return mockServletContext("/");
|
|
|
- }
|
|
|
-
|
|
|
- private ServletContext mockServletContext(String servletPath) {
|
|
|
- MockServletContext servletContext = spy(new MockServletContext());
|
|
|
- final ServletRegistration registration = mock(ServletRegistration.class);
|
|
|
- when(registration.getMappings()).thenReturn(Collections.singleton(servletPath));
|
|
|
- Answer<Map<String, ? extends ServletRegistration>> answer = new Answer<Map<String, ? extends ServletRegistration>>() {
|
|
|
- @Override
|
|
|
- public Map<String, ? extends ServletRegistration> answer(InvocationOnMock invocation) throws Throwable {
|
|
|
- return Collections.<String, ServletRegistration>singletonMap("spring", registration);
|
|
|
- }
|
|
|
- };
|
|
|
- when(servletContext.getServletRegistrations()).thenAnswer(answer);
|
|
|
- return servletContext;
|
|
|
- }
|
|
|
-
|
|
|
- def login(MockHttpServletRequest request, String username, String password) {
|
|
|
- String toEncode = username + ':' + password
|
|
|
- request.addHeader('Authorization','Basic ' + Base64.encoder.encodeToString(toEncode.getBytes('UTF-8')))
|
|
|
- }
|
|
|
-
|
|
|
- @RestController
|
|
|
- static class PathController {
|
|
|
- @RequestMapping("/path")
|
|
|
- public String path() {
|
|
|
- return "path";
|
|
|
- }
|
|
|
- }
|
|
|
-}
|