/** * Copyright (c) 2009--2013 Red Hat, Inc. * * This software is licensed to you under the GNU General Public License, * version 2 (GPLv2). There is NO WARRANTY for this software, express or * implied, including the implied warranties of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 * along with this software; if not, see * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * Red Hat trademarks are not licensed under GPLv2. No permission is * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ package com.redhat.rhn.common.security.acl.test; import com.redhat.rhn.common.security.acl.Acl; import com.redhat.rhn.common.security.acl.AclHandler; import com.redhat.rhn.testing.RhnBaseTestCase; import com.mockobjects.ExpectationValue; import com.mockobjects.Verifiable; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.TreeSet; /* * Test for {@link Acl} * * @version $Rev$ */ public class AclTest extends RhnBaseTestCase { private Acl acl = null; private Map context = null; private MockAclHandler handler = null; /** Constructor * @param name test name */ public AclTest(final String name) { super(name); } /** Sets up the acl, handler, and context objects. */ public void setUp() throws Exception { super.setUp(); acl = new Acl(); context = new HashMap(); handler = new MockAclHandler(); acl.registerHandler(handler); } /** Tears down the acl, handler, and context objects. */ public void tearDown() { acl = null; context = null; handler = null; } /* Test single-statement acls. * Tests the following, to make sure parser is behaving: * <ul> * <li>"handler_zero()" * <li>"handler_zero(true)" * <li>"handler_zero(true,false)" * <li>"handler_zero(true ,false)" * <li>"handler_zero(true , false)" * <li>"handler_zero(true, false)" * <li>"not handler_zero(true)" * </ul> */ public void testSimpleAcl() { // test parsing with no params. should be false handler.setExpected("handler_zero", new String[0]); assertFalse(acl.evalAcl(context, "handler_zero()")); handler.verify(); // test parsing with no 1 param. should be true handler.setExpected("handler_zero", new String[]{"true"}); assertTrue(acl.evalAcl(context, "handler_zero(true)")); handler.verify(); // test 2 params with diff spacings handler.setExpected("handler_zero", new String[]{"true", "false"}); assertTrue(acl.evalAcl(context, "handler_zero(true,false)")); handler.verify(); handler.setExpected("handler_zero", new String[]{"true", "false"}); assertTrue(acl.evalAcl(context, "handler_zero(true ,false)")); handler.verify(); handler.setExpected("handler_zero", new String[]{"true", "false"}); assertTrue(acl.evalAcl(context, "handler_zero(true , false)")); handler.verify(); handler.setExpected("handler_zero", new String[]{"true", "false"}); assertTrue(acl.evalAcl(context, "handler_zero(true, false)")); handler.verify(); // test negation handler.setExpected("handler_zero", new String[]{"true"}); assertFalse(acl.evalAcl(context, "not handler_zero(true)")); handler.verify(); } /* Test expressions connected with Or. * Tests the following: * <ul> * <li>"handler_zero(false) or handler_one(true)" * <li>"handler_zero(true) or handler_one(false)" * </ul> */ public void testMultipleOrStatementsAcl() { handler.setExpected("handler_zero", new String[]{"false"}); handler.setExpected("handler_one", new String[]{"true"}); assertTrue(acl.evalAcl(context, "handler_zero(false) or handler_one(true)")); handler.verify(); handler.setExpected("handler_zero", new String[]{"true"}); // handler_one, even though we give it false in evalAcl, is not expected // to have an expectation value. handler_one won't get called // because handler_zero will return true handler.setExpected("handler_one", null); assertTrue(acl.evalAcl(context, "handler_zero(true) or handler_one(false)")); handler.verify(); } /* Test statements connected with And. * Tests the following: * <ul> * <li>"handler_zero(false) ; handler_one(true)" * <li>"handler_zero(true,false) ; handler_one(true)" * </ul> */ public void testMultipleAndStatementsAcl() { handler.setExpected("handler_zero", new String[]{"false"}); // handler_one, even though we give it false in evalAcl, is not expected // to have an expectation value. handler_one won't get called // because handler_zero will return true handler.setExpected("handler_one", null); assertFalse(acl.evalAcl(context, "handler_zero(false) ; handler_one(true)")); handler.verify(); handler.setExpected("handler_zero", new String[]{"true", "false"}); handler.setExpected("handler_one", new String[]{"true"}); assertTrue(acl.evalAcl(context, "handler_zero(true,false) ; handler_one(true)")); handler.verify(); } /* Test statements connected with And and Or. * Tests the following: * <ul> * <li>"handler_zero(true) or handler_one(false) ; handler_two(true)" * </ul> */ public void testCompoundAcl() { handler.setExpected("handler_zero", new String[]{"true"}); // handler_one, even though we give it false in evalAcl, is not expected // to have an expectation value. handler_one won't get called // because handler_zero will return true handler.setExpected("handler_one", null); handler.setExpected("handler_two", new String[]{"true"}); assertTrue(acl.evalAcl(context, "handler_zero(true) or handler_one(false) ; handler_two(true)")); handler.verify(); } /* Test bad handler. */ public void testBadHandler() { try { acl.evalAcl(null, "handler_does_not_exist(true)"); fail("expected to fail"); } catch (IllegalArgumentException e) { // good } } /* Test bad syntax. */ public void testBadSyntax() { try { acl.evalAcl(null, "handler_zero(true) and handler_zero(true)"); fail("expected to fail"); } catch (IllegalArgumentException e) { // good } } /* Test bad syntax. */ public void test() { try { acl.evalAcl(null, null); fail("expected to fail"); } catch (IllegalArgumentException e) { // good } } /** Makes sure that method names are properly converted to acl handler * names. * Tests the following: * <table> * <tr> * <td>method name</td><td>acl handler name</td> * <td>aclTheQuickBrownFoxJumpedOverTheLazyDog</td> * <td>the_quick_brown_fox_jumped_over_the_lazy_dog</td> * </tr> * <tr> * <td>method name</td><td>acl handler name</td> * <td>aclTestXMLFile</td><td>test_xml_file</td> * <td>aclTestX</td><td>test_x</td> * <td>aclTestXML</td><td>test_xml</td> * </tr> * </table> */ public void testMethodNameToAclName() { acl.registerHandler(new MockAclHandlerWithFunkyNames()); /** Each of the following should call the expected method * from MockAclHandlerWithFunkyNames and return true */ assertTrue(acl.evalAcl(context, "the_quick_brown_fox_jumped_over_the_lazy_dog()")); assertTrue(acl.evalAcl(context, "test_xml_file()")); assertTrue(acl.evalAcl(context, "test_x()")); assertTrue(acl.evalAcl(context, "test_xml()")); assertTrue(acl.evalAcl(context, "xml_test()")); } public void testRegisterByClass() { acl.registerHandler(MockAclHandlerWithFunkyNames.class); assertTrue(acl.evalAcl(context, "xml_test()")); } public void testBadRegisterByClass() { try { acl.registerHandler(Object.class); fail("Expected call to fail"); } catch (IllegalArgumentException e) { // good. } } public void testRegisterByString() { acl.registerHandler(MockAclHandlerWithFunkyNames.class.getName()); assertTrue(acl.evalAcl(context, "xml_test()")); } public void testBadRegisterByString() { try { acl.registerHandler("Bubba"); fail("Expected call to fail"); } catch (IllegalArgumentException e) { // good. } } public void testStringArrayConstructor() { Acl localAcl = new Acl(new String[]{MockAclHandler.class.getName(), MockAclHandlerWithFunkyNames.class.getName()}); // make sure we can call an acl handler from each class assertTrue(localAcl.evalAcl(context, "handler_zero(true)")); assertTrue(localAcl.evalAcl(context, "xml_test()")); } public void testGetAclHandlerNames() { Acl localAcl = new Acl(); localAcl.registerHandler(MockAclHandler.class.getName()); TreeSet ts = localAcl.getAclHandlerNames(); ts.contains("handler_zero"); ts.contains("handler_one"); ts.contains("handle_two"); } // HELPER CLASSES /* Mock AclHandler that can be used to check that the Acl class * is parsing parameters correctly. * If no parameters are given to this AclHandler, its * {@link #handleAcl} method returns false. If the * first parameter equals "true", then handleAcl() returns true. */ public static class MockAclHandler implements AclHandler, Verifiable { private Map expected = null; public MockAclHandler() { reset(); } private void reset() { expected = new HashMap(); expected.put("handler_zero", new ExpectationValue("handler_zero params")); expected.put("handler_one", new ExpectationValue("handler_one params")); expected.put("handler_two", new ExpectationValue("handler_two params")); // defer verifying until verify() is called // otherwise, calling setActual() might throw an Exception, // which we don't want because then we won't get our // assert exceptions Collection expectedValues = expected.values(); Iterator iter = expectedValues.iterator(); while (iter.hasNext()) { ExpectationValue exp = (ExpectationValue)iter.next(); exp.setFailOnVerify(); } } /** Set the parameters expected to be given a handler upon * a call to evalAcl. These get reset with {@link #verify} * is called. */ public void setExpected(String handlerName, String[] params) { if (params != null) { ExpectationValue exp = (ExpectationValue)expected.get(handlerName); exp.setExpected(Arrays.asList(params)); } } public boolean aclHandlerZero(Object ctx, String[] params) { return handlerDelegate("handler_zero", ctx, params); } public boolean aclHandlerOne(Object ctx, String[] params) { return handlerDelegate("handler_one", ctx, params); } public boolean aclHandlerTwo(Object ctx, String[] params) { return handlerDelegate("handler_two", ctx, params); } private boolean handlerDelegate( String name, Object ctx, String[] params) { ExpectationValue exp = (ExpectationValue)expected.get(name); exp.setActual(Arrays.asList(params)); if (params.length == 0) { return false; } if (params[0].equals("true")) { return true; } return false; } /** Call to verify that the expected parameters match * the parameters given to the handler when Acl calls handleAcl. * The expectation values get reset when this is called. */ public void verify() { Collection expectedValues = expected.values(); Iterator iter = expectedValues.iterator(); while (iter.hasNext()) { ExpectationValue exp = (ExpectationValue)iter.next(); exp.verify(); } reset(); } } /** A handler class with a variety of names to test that method names * get converted to acl names correctly. */ public static class MockAclHandlerWithFunkyNames implements AclHandler { public boolean aclTheQuickBrownFoxJumpedOverTheLazyDog( Object ctx, String[] params) { return true; } public boolean aclTestXMLFile(Object ctx, String[] params) { return true; } public boolean aclTestX(Object ctx, String[] params) { return true; } public boolean aclTestXML(Object ctx, String[] params) { return true; } public boolean aclXMLTest(Object ctx, String[] params) { return true; } } }