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.security.AllPermission; 027 import java.util.Hashtable; 028 029 import junit.framework.Test; 030 import junit.framework.TestCase; 031 032 import org.apache.commons.logging.Log; 033 import org.apache.commons.logging.LogFactory; 034 import org.apache.commons.logging.PathableClassLoader; 035 import org.apache.commons.logging.PathableTestSuite; 036 037 /** 038 * Tests for logging with a security policy that allows JCL access to everything. 039 * <p> 040 * This class has only one unit test, as we are (in part) checking behaviour in 041 * the static block of the LogFactory class. As that class cannot be unloaded after 042 * being loaded into a classloader, the only workaround is to use the 043 * PathableClassLoader approach to ensure each test is run in its own 044 * classloader, and use a separate testcase class for each test. 045 */ 046 public class SecurityAllowedTestCase extends TestCase 047 { 048 private SecurityManager oldSecMgr; 049 050 // Dummy special hashtable, so we can tell JCL to use this instead of 051 // the standard one. 052 public static class CustomHashtable extends Hashtable { 053 } 054 055 /** 056 * Return the tests included in this test suite. 057 */ 058 public static Test suite() throws Exception { 059 PathableClassLoader parent = new PathableClassLoader(null); 060 parent.useExplicitLoader("junit.", Test.class.getClassLoader()); 061 parent.addLogicalLib("commons-logging"); 062 parent.addLogicalLib("testclasses"); 063 064 Class testClass = parent.loadClass( 065 "org.apache.commons.logging.security.SecurityAllowedTestCase"); 066 return new PathableTestSuite(testClass, parent); 067 } 068 069 public void setUp() { 070 // save security manager so it can be restored in tearDown 071 oldSecMgr = System.getSecurityManager(); 072 } 073 074 public void tearDown() { 075 // Restore, so other tests don't get stuffed up if a test 076 // sets a custom security manager. 077 System.setSecurityManager(oldSecMgr); 078 } 079 080 /** 081 * Test what happens when JCL is run with all permissions enabled. Custom 082 * overrides should take effect. 083 */ 084 public void testAllAllowed() { 085 System.setProperty( 086 LogFactory.HASHTABLE_IMPLEMENTATION_PROPERTY, 087 CustomHashtable.class.getName()); 088 MockSecurityManager mySecurityManager = new MockSecurityManager(); 089 mySecurityManager.addPermission(new AllPermission()); 090 System.setSecurityManager(mySecurityManager); 091 092 try { 093 // Use reflection so that we can control exactly when the static 094 // initialiser for the LogFactory class is executed. 095 Class c = this.getClass().getClassLoader().loadClass( 096 "org.apache.commons.logging.LogFactory"); 097 Method m = c.getMethod("getLog", new Class[] {Class.class}); 098 Log log = (Log) m.invoke(null, new Object[] {this.getClass()}); 099 100 // Check whether we had any security exceptions so far (which were 101 // caught by the code). We should not, as every secure operation 102 // should be wrapped in an AccessController. Any security exceptions 103 // indicate a path that is missing an appropriate AccessController. 104 // 105 // We don't wait until after the log.info call to get this count 106 // because java.util.logging tries to load a resource bundle, which 107 // requires permission accessClassInPackage. JCL explicitly does not 108 // wrap calls to log methods in AccessControllers because writes to 109 // a log file *should* only be permitted if the original caller is 110 // trusted to access that file. 111 int untrustedCodeCount = mySecurityManager.getUntrustedCodeCount(); 112 log.info("testing"); 113 114 // check that the default map implementation was loaded, as JCL was 115 // forbidden from reading the HASHTABLE_IMPLEMENTATION_PROPERTY property. 116 System.setSecurityManager(null); 117 Field factoryField = c.getDeclaredField("factories"); 118 factoryField.setAccessible(true); 119 Object factoryTable = factoryField.get(null); 120 assertNotNull(factoryTable); 121 assertEquals(CustomHashtable.class.getName(), factoryTable.getClass().getName()); 122 123 assertEquals(0, untrustedCodeCount); 124 } catch(Throwable t) { 125 // Restore original security manager so output can be generated; the 126 // PrintWriter constructor tries to read the line.separator 127 // system property. 128 System.setSecurityManager(oldSecMgr); 129 StringWriter sw = new StringWriter(); 130 PrintWriter pw = new PrintWriter(sw); 131 t.printStackTrace(pw); 132 fail("Unexpected exception:" + t.getMessage() + ":" + sw.toString()); 133 } 134 } 135 }