| /** |
| * 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.falcon.security; |
| |
| import org.apache.falcon.cluster.util.EntityBuilderTestUtil; |
| import org.apache.falcon.entity.store.ConfigurationStore; |
| import org.apache.falcon.entity.v0.EntityType; |
| import org.apache.falcon.entity.v0.cluster.Cluster; |
| import org.apache.falcon.util.FalconTestUtil; |
| import org.apache.falcon.util.StartupProperties; |
| import org.apache.hadoop.security.UserGroupInformation; |
| import org.mockito.Mock; |
| import org.mockito.Mockito; |
| import org.mockito.MockitoAnnotations; |
| import org.testng.Assert; |
| import org.testng.annotations.BeforeClass; |
| import org.testng.annotations.DataProvider; |
| import org.testng.annotations.Test; |
| |
| import javax.servlet.Filter; |
| import javax.servlet.FilterChain; |
| import javax.servlet.FilterConfig; |
| import javax.servlet.ServletOutputStream; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| import javax.ws.rs.HttpMethod; |
| import java.io.IOException; |
| |
| /** |
| * Test for FalconAuthorizationFilter using mock objects. |
| */ |
| public class FalconAuthorizationFilterTest { |
| |
| public static final String CLUSTER_ENTITY_NAME = "primary-cluster"; |
| public static final String PROCESS_ENTITY_NAME = "sample-process"; |
| |
| @Mock |
| private HttpServletRequest mockRequest; |
| |
| @Mock |
| private HttpServletResponse mockResponse; |
| |
| @Mock |
| private FilterChain mockChain; |
| |
| @Mock |
| private FilterConfig mockConfig; |
| |
| @Mock |
| private UserGroupInformation mockUgi; |
| |
| private ConfigurationStore configStore; |
| private Cluster clusterEntity; |
| private org.apache.falcon.entity.v0.process.Process processEntity; |
| |
| @BeforeClass |
| public void setUp() throws Exception { |
| MockitoAnnotations.initMocks(this); |
| |
| CurrentUser.authenticate(EntityBuilderTestUtil.USER); |
| Assert.assertEquals(CurrentUser.getUser(), EntityBuilderTestUtil.USER); |
| |
| configStore = ConfigurationStore.get(); |
| |
| addClusterEntity(); |
| addProcessEntity(); |
| Assert.assertNotNull(processEntity); |
| } |
| |
| @DataProvider(name = "resourceWithNoEntity") |
| private Object[][] createOptions() { |
| return new Object[][] { |
| {"/admin/version"}, |
| {"/entities/list/feed"}, |
| {"/entities/list/process"}, |
| {"/entities/list/cluster"}, |
| {"/metadata/lineage/vertices/all"}, |
| {"/metadata/lineage/vertices/_1"}, |
| {"/metadata/lineage/vertices/properties/_1"}, |
| {"/metadata/discovery/process_entity/sample-process/relations"}, |
| {"/metadata/discovery/process_entity/list?cluster=primary-cluster"}, |
| }; |
| } |
| |
| @Test (dataProvider = "resourceWithNoEntity") |
| public void testDoFilter(String resource) throws Exception { |
| boolean[] enabledFlags = {false, true}; |
| for (boolean enabled : enabledFlags) { |
| StartupProperties.get().setProperty( |
| "falcon.security.authorization.enabled", String.valueOf(enabled)); |
| |
| Filter filter = new FalconAuthorizationFilter(); |
| synchronized (StartupProperties.get()) { |
| filter.init(mockConfig); |
| } |
| |
| StringBuffer requestUrl = new StringBuffer("http://localhost" + resource); |
| Mockito.when(mockRequest.getRequestURL()).thenReturn(requestUrl); |
| Mockito.when(mockRequest.getRequestURI()).thenReturn("/api" + resource); |
| Mockito.when(mockRequest.getPathInfo()).thenReturn(resource); |
| |
| try { |
| filter.doFilter(mockRequest, mockResponse, mockChain); |
| } finally { |
| filter.destroy(); |
| } |
| } |
| } |
| |
| @DataProvider(name = "invalidResource") |
| private Object[][] createBadOptions() { |
| return new Object[][] { |
| {"/admin1/version"}, |
| {"/entities1/list/feed"}, |
| {"/metadata1/lineage/vertices/all"}, |
| {"/foo/bar"}, |
| }; |
| } |
| |
| @Test (dataProvider = "invalidResource") |
| public void testDoFilterBadResource(String resource) throws Exception { |
| boolean[] enabledFlags = {false, true}; |
| for (boolean enabled : enabledFlags) { |
| StartupProperties.get().setProperty( |
| "falcon.security.authorization.enabled", String.valueOf(enabled)); |
| |
| Filter filter = new FalconAuthorizationFilter(); |
| synchronized (StartupProperties.get()) { |
| filter.init(mockConfig); |
| } |
| |
| StringBuffer requestUrl = new StringBuffer("http://localhost" + resource); |
| Mockito.when(mockRequest.getRequestURL()).thenReturn(requestUrl); |
| Mockito.when(mockRequest.getRequestURI()).thenReturn("/api" + resource); |
| Mockito.when(mockRequest.getPathInfo()).thenReturn(resource); |
| |
| Mockito.when(mockResponse.getOutputStream()).thenReturn( |
| new ServletOutputStream() { |
| @Override |
| public void write(int b) throws IOException { |
| System.out.print(b); |
| } |
| }); |
| |
| try { |
| filter.doFilter(mockRequest, mockResponse, mockChain); |
| |
| // todo: verify the response error code to 400 |
| } finally { |
| filter.destroy(); |
| } |
| } |
| } |
| |
| @DataProvider(name = "resourceWithEntity") |
| private Object[][] createOptionsForResourceWithEntity() { |
| return new Object[][] { |
| {"/entities/status/process/"}, |
| {"/entities/suspend/process/"}, |
| {"/instance/running/process/"}, |
| }; |
| } |
| |
| @Test (dataProvider = "resourceWithEntity") |
| public void testDoFilterForEntity(String resource) throws Exception { |
| boolean[] enabledFlags = {false, true}; |
| for (boolean enabled : enabledFlags) { |
| StartupProperties.get().setProperty( |
| "falcon.security.authorization.enabled", String.valueOf(enabled)); |
| |
| Filter filter = new FalconAuthorizationFilter(); |
| synchronized (StartupProperties.get()) { |
| filter.init(mockConfig); |
| } |
| |
| String uri = resource + processEntity.getName(); |
| StringBuffer requestUrl = new StringBuffer("http://localhost" + uri); |
| Mockito.when(mockRequest.getRequestURL()).thenReturn(requestUrl); |
| Mockito.when(mockRequest.getRequestURI()).thenReturn("/api" + uri); |
| Mockito.when(mockRequest.getPathInfo()).thenReturn(uri); |
| |
| try { |
| filter.doFilter(mockRequest, mockResponse, mockChain); |
| } finally { |
| filter.destroy(); |
| } |
| } |
| } |
| |
| @Test |
| public void testDoFilterForEntityWithInvalidEntity() throws Exception { |
| CurrentUser.authenticate(FalconTestUtil.TEST_USER_1); |
| |
| StartupProperties.get().setProperty("falcon.security.authorization.enabled", "true"); |
| |
| Filter filter = new FalconAuthorizationFilter(); |
| synchronized (StartupProperties.get()) { |
| filter.init(mockConfig); |
| } |
| |
| String uri = "/entities/suspend/process/bad-entity"; |
| StringBuffer requestUrl = new StringBuffer("http://localhost" + uri); |
| Mockito.when(mockRequest.getRequestURL()).thenReturn(requestUrl); |
| Mockito.when(mockRequest.getRequestURI()).thenReturn("/api" + uri); |
| Mockito.when(mockRequest.getPathInfo()).thenReturn(uri); |
| Mockito.when(mockRequest.getMethod()).thenReturn(HttpMethod.GET); |
| |
| try { |
| filter.doFilter(mockRequest, mockResponse, mockChain); |
| |
| // todo: verify the response error code to 403 |
| } finally { |
| filter.destroy(); |
| } |
| } |
| |
| @Test |
| public void testDoFilterForDeleteEntityInvalidEntity() throws Exception { |
| CurrentUser.authenticate(FalconTestUtil.TEST_USER_1); |
| |
| StartupProperties.get().setProperty("falcon.security.authorization.enabled", "true"); |
| |
| Filter filter = new FalconAuthorizationFilter(); |
| synchronized (StartupProperties.get()) { |
| filter.init(mockConfig); |
| } |
| |
| String uri = "/entities/suspend/process/bad-entity"; |
| StringBuffer requestUrl = new StringBuffer("http://localhost" + uri); |
| Mockito.when(mockRequest.getRequestURL()).thenReturn(requestUrl); |
| Mockito.when(mockRequest.getRequestURI()).thenReturn("/api" + uri); |
| Mockito.when(mockRequest.getPathInfo()).thenReturn(uri); |
| Mockito.when(mockRequest.getMethod()).thenReturn(HttpMethod.DELETE); |
| |
| try { |
| filter.doFilter(mockRequest, mockResponse, mockChain); |
| } finally { |
| filter.destroy(); |
| } |
| } |
| |
| |
| |
| public void addClusterEntity() throws Exception { |
| clusterEntity = EntityBuilderTestUtil.buildCluster(CLUSTER_ENTITY_NAME); |
| configStore.publish(EntityType.CLUSTER, clusterEntity); |
| } |
| |
| public void addProcessEntity() throws Exception { |
| processEntity = EntityBuilderTestUtil.buildProcess(PROCESS_ENTITY_NAME, |
| clusterEntity, "classified-as=Critical"); |
| EntityBuilderTestUtil.addProcessWorkflow(processEntity); |
| EntityBuilderTestUtil.addProcessACL(processEntity); |
| |
| configStore.publish(EntityType.PROCESS, processEntity); |
| } |
| } |