/** * 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.regression.security; import org.apache.falcon.regression.Entities.ProcessMerlin; import org.apache.falcon.regression.core.bundle.Bundle; import org.apache.falcon.regression.core.enumsAndConstants.MerlinConstants; import org.apache.falcon.regression.core.helpers.ColoHelper; import org.apache.falcon.regression.core.helpers.entity.AbstractEntityHelper; import org.apache.falcon.regression.core.util.AssertUtil; import org.apache.falcon.regression.core.util.BundleUtil; import org.apache.falcon.regression.core.util.HadoopUtil; import org.apache.falcon.regression.core.util.InstanceUtil; import org.apache.falcon.regression.core.util.MatrixUtil; import org.apache.falcon.regression.core.util.OSUtil; import org.apache.falcon.regression.testHelper.BaseTestClass; import org.apache.hadoop.fs.FileSystem; import org.apache.oozie.client.OozieClient; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.util.Date; /** * Tests ACL of process with different operations. */ @Test(groups = "authorization") public class ProcessAclTest extends BaseTestClass { private ColoHelper cluster = servers.get(0); private FileSystem clusterFS = serverFS.get(0); private OozieClient clusterOC = serverOC.get(0); private String baseTestDir = cleanAndGetTestDir(); private String aggregateWorkflowDir = baseTestDir + "/aggregator"; private String feedInputPath = baseTestDir + "/input" + MINUTE_DATE_PATTERN; private final AbstractEntityHelper processHelper = prism.getProcessHelper(); private String processString; @BeforeClass(alwaysRun = true) public void uploadWorkflow() throws Exception { HadoopUtil.uploadDir(clusterFS, aggregateWorkflowDir, OSUtil.RESOURCES_OOZIE); } @BeforeMethod(alwaysRun = true) public void setup() throws Exception { Bundle bundle = BundleUtil.readELBundle(); bundles[0] = new Bundle(bundle, cluster); bundles[0].generateUniqueBundle(this); bundles[0].setInputFeedDataPath(feedInputPath); bundles[0].setProcessWorkflow(aggregateWorkflowDir); bundles[0].setProcessACL(MerlinConstants.CURRENT_USER_NAME, MerlinConstants.CURRENT_USER_GROUP, "*"); final ProcessMerlin processMerlin = bundles[0].getProcessObject(); //setting end date of the process to 10 minutes in future final Date tenMinInFuture = new DateTime(DateTimeZone.UTC).plusMinutes(10).toDate(); processMerlin.getClusters().getClusters().get(0).getValidity().setEnd(tenMinInFuture); processString = processMerlin.toString(); } /** * Test process read operation by different types of user. * @param user the user that would attempt operation * @param op the read operation that would be performed * @param isAllowed is operation expected to go through * @throws Exception */ @Test(dataProvider = "generateUserReadOpsPermissions") public void othersReadProcess(final String user, final EntityOp op, final boolean isAllowed) throws Exception { bundles[0].submitProcess(true); final boolean executeRes = op.executeAs(user, processHelper, processString); Assert.assertEquals(executeRes, isAllowed, "Unexpected result user " + user + " performing: " + op); } @DataProvider(name = "generateUserReadOpsPermissions") public Object[][] generateUserReadOpsPermissions() { final EntityOp[] falconReadOps = {EntityOp.status, EntityOp.dependency, EntityOp.listing, EntityOp.definition, }; final Object[][] allowedCombinations = MatrixUtil.crossProduct( new String[]{MerlinConstants.FALCON_SUPER_USER_NAME, MerlinConstants.FALCON_SUPER_USER2_NAME, MerlinConstants.USER2_NAME, }, falconReadOps, new Boolean[]{true} ); final Object[][] notAllowedCombinations = MatrixUtil.crossProduct( new String[]{MerlinConstants.DIFFERENT_USER_NAME}, falconReadOps, new Boolean[]{false} ); return MatrixUtil.append(allowedCombinations, notAllowedCombinations); } /** * Test edit operation on submitted process by different users. * @param user the user that would attempt operation * @param op the edit operation that would be performed * @param isAllowed is operation expected to go through * @throws Exception */ @Test(dataProvider = "generateUserSubmittedEditOpsPermission") public void othersEditSubmittedProcess(final String user, final EntityOp op, boolean isAllowed) throws Exception { bundles[0].submitProcess(true); if (op == EntityOp.update) { processString = new ProcessMerlin(processString).withProperty("abc", "xyz").toString(); } final boolean executeRes = op.executeAs(user, processHelper, processString); Assert.assertEquals(executeRes, isAllowed, "Unexpected result user " + user + " performing: " + op); } @DataProvider(name = "generateUserSubmittedEditOpsPermission") public Object[][] generateUserSubmittedEditOpsPermission() { final EntityOp[] falconEditOps = {EntityOp.delete, EntityOp.update}; final Object[][] allowedCombinations = MatrixUtil.crossProduct( new String[]{ MerlinConstants.FALCON_SUPER_USER_NAME, MerlinConstants.FALCON_SUPER_USER2_NAME, MerlinConstants.USER2_NAME, }, falconEditOps, new Boolean[]{true} ); final Object[][] notAllowedCombinations = MatrixUtil.crossProduct( new String[]{MerlinConstants.DIFFERENT_USER_NAME}, falconEditOps, new Boolean[]{false} ); return MatrixUtil.append(allowedCombinations, notAllowedCombinations); } /** * Test edit operation on scheduled process by different users. * @param user the user that would attempt operation * @param op the edit operation that would be performed * @param isAllowed is operation expected to go through * @throws Exception */ @Test(dataProvider = "generateUserScheduledEditOpsPermission") public void othersEditScheduledProcess(final String user, final EntityOp op, boolean isAllowed) throws Exception { bundles[0].submitFeedsScheduleProcess(); InstanceUtil.waitTillInstancesAreCreated(clusterOC, bundles[0].getProcessData(), 0); if (op == EntityOp.resume) { processHelper.suspend(processString); } else if (op == EntityOp.update) { processString = new ProcessMerlin(processString).withProperty("abc", "xyz").toString(); } final boolean executeRes = op.executeAs(user, processHelper, processString); Assert.assertEquals(executeRes, isAllowed, "Unexpected result user " + user + " performing: " + op); } @DataProvider(name = "generateUserScheduledEditOpsPermission") public Object[][] generateUserScheduledEditOpsPermission() { final Object[][] allowedCombinations = MatrixUtil.crossProduct( new String[]{ MerlinConstants.FALCON_SUPER_USER_NAME, MerlinConstants.FALCON_SUPER_USER2_NAME, MerlinConstants.USER2_NAME, }, new EntityOp[]{EntityOp.delete, EntityOp.update, EntityOp.suspend, EntityOp.resume}, new Boolean[]{true} ); final Object[][] notAllowedCombinations = MatrixUtil.crossProduct( new String[]{MerlinConstants.DIFFERENT_USER_NAME}, new EntityOp[]{ EntityOp.delete, EntityOp.update, EntityOp.suspend, EntityOp.resume, }, new Boolean[]{false} ); return MatrixUtil.append(allowedCombinations, notAllowedCombinations); } /** * Test process acl modification. * @throws Exception */ @Test(dataProvider = "generateAclOwnerAndGroup") public void processAclUpdate(final String newOwner, final String newGroup) throws Exception { bundles[0].submitFeedsScheduleProcess(); final ProcessMerlin processMerlin = new ProcessMerlin(processString); processMerlin.setACL(newOwner, newGroup, "*"); final String newProcess = processMerlin.toString(); AssertUtil.assertFailed(processHelper.update(processString, newProcess), "AuthorizationException: Permission denied"); } @DataProvider(name = "generateAclOwnerAndGroup") public Object[][] generateAclOwnerAndGroup() { return new Object[][]{ //{MerlinConstants.USER2_NAME, MerlinConstants.CURRENT_USER_GROUP}, {MerlinConstants.DIFFERENT_USER_NAME, MerlinConstants.DIFFERENT_USER_GROUP}, }; } @AfterMethod(alwaysRun = true) public void tearDown() { removeTestClassEntities(); } }