001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020 package org.apache.commons.logging.security; 021 022 import java.io.PrintWriter; 023 import java.io.StringWriter; 024 import java.lang.reflect.Field; 025 import java.lang.reflect.Method; 026 import java.util.Hashtable; 027 028 import junit.framework.Test; 029 import junit.framework.TestCase; 030 031 import org.apache.commons.logging.Log; 032 import org.apache.commons.logging.LogFactory; 033 import org.apache.commons.logging.PathableClassLoader; 034 import org.apache.commons.logging.PathableTestSuite; 035 036 /** 037 * Tests for logging with a security policy that forbids JCL access to anything. 038 * <p> 039 * Performing tests with security permissions disabled is tricky, as building error 040 * messages on failure requires certain security permissions. If the security manager 041 * blocks these, then the test can fail without the error messages being output. 042 * <p> 043 * This class has only one unit test, as we are (in part) checking behaviour in 044 * the static block of the LogFactory class. As that class cannot be unloaded after 045 * being loaded into a classloader, the only workaround is to use the 046 * PathableClassLoader approach to ensure each test is run in its own 047 * classloader, and use a separate testcase class for each test. 048 */ 049 public class SecurityForbiddenTestCase extends TestCase 050 { 051 private SecurityManager oldSecMgr; 052 053 // Dummy special hashtable, so we can tell JCL to use this instead of 054 // the standard one. 055 public static class CustomHashtable extends Hashtable { 056 } 057 058 /** 059 * Return the tests included in this test suite. 060 */ 061 public static Test suite() throws Exception { 062 PathableClassLoader parent = new PathableClassLoader(null); 063 parent.useExplicitLoader("junit.", Test.class.getClassLoader()); 064 parent.addLogicalLib("commons-logging"); 065 parent.addLogicalLib("testclasses"); 066 067 Class testClass = parent.loadClass( 068 "org.apache.commons.logging.security.SecurityForbiddenTestCase"); 069 return new PathableTestSuite(testClass, parent); 070 } 071 072 public void setUp() { 073 // save security manager so it can be restored in tearDown 074 oldSecMgr = System.getSecurityManager(); 075 } 076 077 public void tearDown() { 078 // Restore, so other tests don't get stuffed up if a test 079 // sets a custom security manager. 080 System.setSecurityManager(oldSecMgr); 081 } 082 083 /** 084 * Test what happens when JCL is run with absolutely no security 085 * priveleges at all, including reading system properties. Everything 086 * should fall back to the built-in defaults. 087 */ 088 public void testAllForbidden() { 089 System.setProperty( 090 LogFactory.HASHTABLE_IMPLEMENTATION_PROPERTY, 091 CustomHashtable.class.getName()); 092 MockSecurityManager mySecurityManager = new MockSecurityManager(); 093 System.setSecurityManager(mySecurityManager); 094 095 try { 096 // Use reflection so that we can control exactly when the static 097 // initialiser for the LogFactory class is executed. 098 Class c = this.getClass().getClassLoader().loadClass( 099 "org.apache.commons.logging.LogFactory"); 100 Method m = c.getMethod("getLog", new Class[] {Class.class}); 101 Log log = (Log) m.invoke(null, new Object[] {this.getClass()}); 102 log.info("testing"); 103 104 // check that the default map implementation was loaded, as JCL was 105 // forbidden from reading the HASHTABLE_IMPLEMENTATION_PROPERTY property. 106 // 107 // The default is either the java Hashtable class (java < 1.2) or the 108 // JCL WeakHashtable (java >= 1.3). 109 System.setSecurityManager(oldSecMgr); 110 Field factoryField = c.getDeclaredField("factories"); 111 factoryField.setAccessible(true); 112 Object factoryTable = factoryField.get(null); 113 assertNotNull(factoryTable); 114 String ftClassName = factoryTable.getClass().getName(); 115 assertTrue("Custom hashtable unexpectedly used", 116 !CustomHashtable.class.getName().equals(ftClassName)); 117 118 assertEquals(0, mySecurityManager.getUntrustedCodeCount()); 119 } catch(Throwable t) { 120 // Restore original security manager so output can be generated; the 121 // PrintWriter constructor tries to read the line.separator 122 // system property. 123 System.setSecurityManager(oldSecMgr); 124 StringWriter sw = new StringWriter(); 125 PrintWriter pw = new PrintWriter(sw); 126 t.printStackTrace(pw); 127 fail("Unexpected exception:" + t.getMessage() + ":" + sw.toString()); 128 } 129 } 130 }