blob: e27dfbe810060d2d5a678ab963a494231e5851d5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.registry.security.authorization
import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException
import org.apache.nifi.registry.security.authorization.resource.Authorizable
import org.apache.nifi.registry.security.authorization.resource.ResourceType
import org.apache.nifi.registry.service.AuthorizationService
import org.apache.nifi.registry.web.security.authorization.HttpMethodAuthorizationRules
import org.apache.nifi.registry.web.security.authorization.ResourceAuthorizationFilter
import org.apache.nifi.registry.web.security.authorization.StandardHttpMethodAuthorizationRules
import org.springframework.http.HttpMethod
import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.mock.web.MockHttpServletResponse
import spock.lang.Specification
import javax.servlet.FilterChain
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
class ResourceAuthorizationFilterSpec extends Specification {
AuthorizableLookup authorizableLookup = new StandardAuthorizableLookup()
AuthorizationService mockAuthorizationService = Mock(AuthorizationService)
FilterChain mockFilterChain = Mock(FilterChain)
ResourceAuthorizationFilter.Builder resourceAuthorizationFilterBuilder
// runs before every feature method
def setup() {
mockAuthorizationService.getAuthorizableLookup() >> authorizableLookup
resourceAuthorizationFilterBuilder = ResourceAuthorizationFilter.builder().setAuthorizationService(mockAuthorizationService)
}
// runs after every feature method
def cleanup() {
//mockAuthorizationService = null
//mockFilterChain = null
resourceAuthorizationFilterBuilder = null
}
// runs before the first feature method
def setupSpec() {}
// runs after the last feature method
def cleanupSpec() {}
def "unsecured requests are allowed without an authorization check"() {
setup:
def resourceAuthorizationFilter = resourceAuthorizationFilterBuilder.addResourceType(ResourceType.Actuator).build()
def httpServletRequest = createUnsecuredRequest()
def httpServletResponse = createResponse()
when: "doFilter() is called"
resourceAuthorizationFilter.doFilter(httpServletRequest, httpServletResponse, mockFilterChain)
then: "response is forwarded without authorization check"
0 * mockAuthorizationService._
1 * mockFilterChain.doFilter(_ as HttpServletRequest, _ as HttpServletResponse)
}
def "secure requests to an unguarded resource are allowed without an authorization check"() {
setup:
def resourceAuthorizationFilter = resourceAuthorizationFilterBuilder.addResourceType(ResourceType.Actuator).build()
def httpServletRequest = createSecureRequest(HttpMethod.POST, ResourceType.Bucket)
def httpServletResponse = createResponse()
when: "doFilter() is called"
resourceAuthorizationFilter.doFilter(httpServletRequest, httpServletResponse, mockFilterChain)
then: "response is forwarded without authorization check"
0 * mockAuthorizationService._
1 * mockFilterChain.doFilter(_ as HttpServletRequest, _ as HttpServletResponse)
}
def "secure requests to an unguarded HTTP method are allowed without an authorization check"() {
setup:
HttpMethodAuthorizationRules rules = new StandardHttpMethodAuthorizationRules(EnumSet.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE))
def resourceAuthorizationFilter = resourceAuthorizationFilterBuilder.addResourceType(ResourceType.Actuator, rules).build()
def httpServletRequest = createSecureRequest(HttpMethod.GET, ResourceType.Actuator)
def httpServletResponse = createResponse()
when: "doFilter() is called"
resourceAuthorizationFilter.doFilter(httpServletRequest, httpServletResponse, mockFilterChain)
then: "response is forwarded without authorization check"
0 * mockAuthorizationService._
1 * mockFilterChain.doFilter(_ as HttpServletRequest, _ as HttpServletResponse)
}
def "secure requests matching resource configuration rules perform authorization check"() {
setup:
// Stubbing setup for mockAuthorizationService is done in the then block as we are also verifying interactions with mock
def resourceAuthorizationFilter = resourceAuthorizationFilterBuilder.addResourceType(ResourceType.Actuator).build()
def authorizedRequest = createSecureRequest(HttpMethod.GET, ResourceType.Actuator)
def unauthorizedRequest = createSecureRequest(HttpMethod.POST, ResourceType.Actuator)
def httpServletResponse = createResponse()
when: "doFilter() is called with an authorized request"
resourceAuthorizationFilter.doFilter(authorizedRequest, httpServletResponse, mockFilterChain)
then: "response is forwarded after authorization check"
1 * mockAuthorizationService.authorize(_ as Authorizable, RequestAction.READ) >> { allowAccess() }
1 * mockFilterChain.doFilter(_ as HttpServletRequest, _ as HttpServletResponse)
when: "doFilter() is called with an unauthorized request"
resourceAuthorizationFilter.doFilter(unauthorizedRequest, httpServletResponse, mockFilterChain)
then: "authorization check is performed and response is not forwarded"
1 * mockAuthorizationService.authorize(_ as Authorizable, RequestAction.WRITE) >> { denyAccess() }
0 * mockFilterChain.doFilter(*_)
}
static private HttpServletRequest createUnsecuredRequest() {
HttpServletRequest req = new MockHttpServletRequest()
req.setScheme("http")
req.setSecure(false)
return req
}
static private HttpServletRequest createSecureRequest(HttpMethod httpMethod, ResourceType resourceType) {
HttpServletRequest req = new MockHttpServletRequest()
req.setMethod(httpMethod.name())
req.setScheme("https")
req.setServletPath(resourceType.getValue())
req.setSecure(true)
return req
}
static private HttpServletResponse createResponse() {
HttpServletResponse res = new MockHttpServletResponse()
return res
}
static private void allowAccess() {
// Do nothing (no thrown exception indicates access is allowed
}
static private void denyAccess() {
throw new AccessDeniedException("This is an expected AccessDeniedException.")
}
}