| /* |
| * |
| * Derby - Class org.apache.derbyTesting.junit.SecurityManagerSetup |
| * |
| * 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.derbyTesting.junit; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.security.AccessController; |
| import java.security.Policy; |
| import java.security.PrivilegedAction; |
| import java.security.PrivilegedActionException; |
| import java.security.PrivilegedExceptionAction; |
| import java.util.Enumeration; |
| import java.util.Properties; |
| import junit.extensions.TestSetup; |
| import junit.framework.Test; |
| import org.apache.derbyTesting.functionTests.util.PrivilegedFileOpsForTests; |
| |
| /** |
| * Configures the wrapped test to be run with the specified security policy. |
| * <p> |
| * This setup class normally installs the default policy file. This can be |
| * overridden by specifying {@literal java.security.policy=<NONE>} (see |
| * {@linkplain #NO_POLICY}), and can also be overridden by installing a |
| * security manager explicitly before the default security manager is installed. |
| * <p> |
| * Individual tests/suites can be configured to be run without a security |
| * manager, with a specific policy file, or with a specific policy file merged |
| * with the default policy file. The last option is useful when you only need |
| * to extend the default policy with a few extra permissions to run a test. |
| */ |
| public final class SecurityManagerSetup extends TestSetup { |
| |
| /** Constant used to indicate that no security policy is to be installed. */ |
| static final String NO_POLICY = "<NONE>"; |
| |
| // codebase variables used in derby_tests.policy |
| public static final String DERBY_ENGINE = "derbyTesting.engine"; |
| public static final String DERBY_CLIENT = "derbyTesting.client"; |
| public static final String DERBY_SHARED = "derbyTesting.shared"; |
| public static final String DERBY_NETSERVER = "derbyTesting.netserver"; |
| public static final String DERBY_TOOLS = "derbyTesting.tools"; |
| public static final String DERBY_OPTIONAL_TOOLS = "derbyTesting.optionaltools"; |
| public static final String DERBY_TESTING = "derbyTesting.testing"; |
| |
| public static final String TESTING_JUNIT = "derbyTesting.junit"; |
| public static final String TESTING_ANTJUNIT = "derbyTesting.antjunit"; |
| public static final String TESTING_ANT = "derbyTesting.ant"; |
| |
| public static final String LUCENE_CORE = "derbyTesting.lucene.core"; |
| public static final String JAXP = "derbyTesting.jaxpjar"; |
| public static final String PACKAGE_PRIVATE_CLASSES = "derbyTesting.ppcodeclasses"; |
| |
| |
| /** |
| * Does the JVM support Subjects for |
| * authorization through Java security manager. |
| * J2ME/CDC/Foundation 1.1 does not support Subjects. |
| */ |
| public static final boolean JVM_HAS_SUBJECT_AUTHORIZATION; |
| static { |
| JVM_HAS_SUBJECT_AUTHORIZATION = JDBC.haveClass("javax.security.auth.Subject"); |
| } |
| |
| |
| private static final Properties classPathSet = new Properties(); |
| |
| /** |
| * True if the classes are loaded from jars. |
| */ |
| static boolean isJars; |
| |
| /** |
| * True if a security manager was installed outside of the |
| * control of this class and BaseTestCase. |
| */ |
| private static final boolean externalSecurityManagerInstalled; |
| |
| static { |
| // Determine what the set of properties |
| // describing the environment is. |
| externalSecurityManagerInstalled = determineClasspath(); |
| |
| } |
| |
| static { |
| // Work around bug in weme6.2 (DERBY-5558 and DERBY-6079). |
| if (BaseTestCase.isJ9Platform()) { |
| BaseTestCase.setSystemProperty("emma.active", ""); |
| BaseTestCase.setSystemProperty("jacoco.active", ""); |
| } |
| } |
| |
| static final boolean jacocoEnabled = checkIfJacocoIsRunning(); |
| private static boolean checkIfJacocoIsRunning() { |
| return AccessController.doPrivileged(new PrivilegedAction<Boolean>() { |
| public Boolean run() { |
| try { |
| // Check if some arbitrary class from jacocoagent.jar |
| // is available. |
| Class.forName("org.jacoco.agent.rt.RT"); |
| |
| // If we got here, it means the tests are running |
| // under JaCoCo. Set the jacoco.active property to |
| // the empty string in order to activate the |
| // JaCoCo-specific permissions in derby_tests.policy, |
| // and return true. |
| System.setProperty("jacoco.active", ""); |
| return true; |
| } catch (ClassNotFoundException e) { |
| return false; |
| } catch (LinkageError e) { |
| return false; |
| } |
| } |
| }); |
| } |
| |
| private final String decoratorPolicyResource; |
| /** An additional policy to install (may be {@code null}). */ |
| private final String additionalPolicyResource; |
| private SecurityManager decoratorSecurityManager = null; |
| |
| public SecurityManagerSetup(Test test, String policyResource) { |
| this(test, policyResource, false); |
| } |
| |
| /** |
| * Installs a new security policy. |
| * |
| * @param test the test to wrap |
| * @param policyResource the policy to install |
| * @param mergePolicies if {@code false} the specified policy will be the |
| * only policy installed, if {@code true} the specified policy will be |
| * merged with the default test policy for the test framework |
| */ |
| public SecurityManagerSetup(Test test, String policyResource, |
| boolean mergePolicies) { |
| super(test); |
| if (mergePolicies) { |
| // By choice, only support merging with the default test policy. |
| this.decoratorPolicyResource = getDefaultPolicy(); |
| this.additionalPolicyResource = policyResource; |
| } else { |
| this.decoratorPolicyResource = policyResource != null ? |
| policyResource : getDefaultPolicy(); |
| this.additionalPolicyResource = null; |
| } |
| } |
| |
| /** |
| * Use custom policy and SecurityManager |
| * |
| * @param test - Test to wrap |
| * @param policyResource - policy resource. If null use default testing policy |
| * @param securityManager - Custom SecurityManager if null use the system security manager |
| */ |
| public SecurityManagerSetup(Test test, String policyResource, SecurityManager securityManager) |
| { |
| this(test, policyResource, false); |
| this.decoratorSecurityManager = securityManager; |
| } |
| |
| /** |
| * Get a decorator that will ensure no security manger |
| * is installed to run a test. Not supported for suites. |
| * <BR> |
| * An empty suite is returned if a security manager was installed |
| * externally, i.e. not under the control of the BaseTestCase |
| * and this code. In this case the code can not support the |
| * mode of no security manager as it may not have enough information |
| * to re-install the security manager. So the passed in test |
| * will be skipped. |
| * |
| * @param test Test to run without a security manager. Note that |
| * this must be an instance of BaseTestCase as this call depends |
| * on setup code in that class. Arbitrary Test instances cannot be passed in. |
| */ |
| public static Test noSecurityManager(Test test) |
| { |
| if (externalSecurityManagerInstalled) { |
| return new BaseTestSuite( |
| "skipped due to external security manager " |
| + test.toString()); |
| } |
| |
| return new SecurityManagerSetup(test, NO_POLICY); |
| } |
| |
| /** |
| * "Install" no security manager. |
| * |
| */ |
| static void noSecurityManager() |
| { |
| installSecurityManager(NO_POLICY); |
| } |
| |
| /** |
| * Install specific policy file with the security manager |
| * including the special case of no security manager. |
| */ |
| protected void setUp() |
| throws IOException { |
| String resource = getEffectivePolicyResource( |
| decoratorPolicyResource, additionalPolicyResource); |
| installSecurityManager(resource, decoratorSecurityManager); |
| } |
| |
| protected void tearDown() throws Exception |
| { |
| if (NO_POLICY.equals(decoratorPolicyResource)) |
| BaseTestCase.setSystemProperty("java.security.policy", ""); |
| else if ( !externalSecurityManagerInstalled ) |
| { |
| uninstallSecurityManager(); |
| } |
| } |
| |
| /** |
| * Return the name of the default policy. |
| */ |
| private static String getDefaultPolicy() |
| { |
| return "org/apache/derbyTesting/functionTests/util/derby_tests.policy"; |
| } |
| |
| /** |
| * Install a SecurityManager with the default test policy |
| * file: |
| * org/apache/derbyTesting/functionTests/util/derby_tests.policy |
| * |
| */ |
| static void installSecurityManager() |
| { |
| installSecurityManager( getDefaultPolicy() ); |
| } |
| |
| private static void installSecurityManager(String policyFile) { |
| installSecurityManager(policyFile, System.getSecurityManager()); |
| } |
| |
| private static void installSecurityManager(String policyFile, final SecurityManager sm) |
| { |
| if (externalSecurityManagerInstalled) |
| return; |
| |
| Properties set = new Properties(classPathSet); |
| setSecurityPolicy(set, policyFile); |
| for (Enumeration e = set.propertyNames(); e.hasMoreElements();) { |
| String key = (String) e.nextElement(); |
| } |
| SecurityManager currentsm = System.getSecurityManager(); |
| if (currentsm != null) { |
| // SecurityManager installed, see if it has the same settings. |
| |
| String newPolicyProperty = set.getProperty("java.security.policy" ); |
| if ( newPolicyProperty == null ) { newPolicyProperty = ""; } |
| |
| String oldPolicyProperty = BaseTestCase.getSystemProperty("java.security.policy"); |
| SecurityManager oldSecMan = System.getSecurityManager(); |
| |
| if ( oldPolicyProperty == null ) { oldPolicyProperty = ""; } |
| |
| if ( newPolicyProperty.equals( oldPolicyProperty ) && |
| oldSecMan == sm) { return; } |
| |
| // Uninstall the current manager. |
| uninstallSecurityManager(); |
| } |
| |
| // Set the system properties from the desired set. |
| for (Enumeration e = set.propertyNames(); e.hasMoreElements();) { |
| String key = (String) e.nextElement(); |
| BaseTestCase.setSystemProperty(key, set.getProperty(key)); |
| } |
| |
| // Check indicator for no security manager |
| if (NO_POLICY.equals(set.getProperty("java.security.policy"))) |
| return; |
| |
| // and install |
| AccessController.doPrivileged(new PrivilegedAction<Void>() { |
| |
| |
| public Void run() { |
| if (sm == null) |
| System.setSecurityManager(new SecurityManager()); |
| else |
| System.setSecurityManager(sm); |
| Policy.getPolicy().refresh(); |
| return null; |
| } |
| }); |
| println("installed policy " + policyFile); |
| } |
| |
| private static void setSecurityPolicy(Properties set, |
| String policyResource) |
| { |
| if (NO_POLICY.equals(policyResource)) { |
| set.setProperty("java.security.policy", policyResource); |
| return; |
| } |
| try { |
| URL policyURL = getResourceURL(policyResource); |
| set.setProperty("java.security.policy", policyURL.toExternalForm()); |
| } catch (MalformedURLException mue) { |
| BaseTestCase.alarm("Unreadable policy URL: " + policyResource); |
| } |
| } |
| |
| /** |
| * Determine the settings of the classpath in order to configure |
| * the variables used in the testing policy files. |
| * Looks for three items: |
| * |
| * Location of derbyTesting.jar via this class |
| * Location of derby.jar via org.apache.derby.jdbc.EmbeddedDataSource |
| * Location of derbyclient.jar via org.apache.derby.jdbc.ClientDataSource |
| * |
| * Two options are supported, either all are in jar files or |
| * all are on the classpath. Properties are set as follows: |
| * |
| * <P> |
| * Classpath: |
| * <BR> |
| * derbyTesting.codeclasses set to URL of classes folder |
| * <BR> |
| * derbyTesting.ppcodeclasses set to URL of the 'classes.pptesting' folder |
| * if it exists on the classpath. The existence of the package private tests |
| * is determined via org.apache.derby.PackagePrivateTestSuite |
| * <P> |
| * Jar files: |
| * <BR> |
| * derbyTesting.codejar - URL of derby.jar, derbyshared.jar, |
| * derbynet.jar and derbytools.jar, all assumed to be in the |
| * same location. |
| * <BR> |
| * derbyTesting.clientjar - URL of derbyclient.jar |
| * <BR> |
| * derbyTesting.testjar - URL of derbyTesting.jar |
| * <BR> |
| * derbyTesting.testjarpath - File system path to derbyTesting.jar |
| * if the jar has a URL with a file protocol. |
| * |
| */ |
| private static boolean determineClasspath() |
| { |
| // Security manager already installed, assume that |
| // it is set up correctly. |
| if (System.getSecurityManager() != null) { |
| return true; |
| } |
| |
| //We need the junit classes to instantiate this class, so the |
| //following should not cause runtime errors. |
| setCodebase(TESTING_JUNIT, getURL(junit.framework.Test.class)); |
| |
| // Load indirectly so we don't need ant-junit.jar at compile time. |
| setCodebase(TESTING_ANTJUNIT, "org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner"); |
| |
| setCodebase(TESTING_ANT, "org.apache.tools.ant.Task"); |
| |
| // variables for lucene jar files |
| URL luceneCore = getURL( "org.apache.lucene.store.FSDirectory" ); |
| if ( luceneCore != null ) |
| { |
| setCodebase(LUCENE_CORE, luceneCore); |
| classPathSet.setProperty( "derbyTesting.lucene.core.jar.file", luceneCore.getFile() ); |
| } |
| |
| // Load indirectly, normally no EMMA jars in the classpath. |
| // This property is needed to set correct permissions in policy files. |
| URL emma = getURL("com.vladium.emma.EMMAException"); |
| if (emma != null) { |
| classPathSet.setProperty("emma.active", ""); |
| } |
| |
| /* When inserting XML values that use external DTD's, the JAXP |
| * parser needs permission to read the DTD files. So here we set |
| * a property to hold the location of the JAXP implementation |
| * jar file. We can then grant the JAXP impl the permissions |
| * needed for reading the DTD files. |
| */ |
| setCodebase(JAXP, XML.getJAXPParserLocation()); |
| |
| URL testing = getURL(SecurityManagerSetup.class); |
| URL ppTesting = null; |
| // Only try to load PackagePrivateTestSuite if the running JVM is |
| // Java 1.5 or newer (class version 49 = Java 1.5). |
| if (BaseTestCase.getClassVersionMajor() >= 49) { |
| ppTesting = getURL("org.apache.derby.PackagePrivateTestSuite"); |
| } |
| boolean isClasspath = testing.toExternalForm().endsWith("/"); |
| if (isClasspath) { |
| // ppTesting can be null, for instance if 'classes.pptesting' is |
| // not on the classpath. |
| setCodebase(PACKAGE_PRIVATE_CLASSES, ppTesting); |
| isJars = false; |
| } |
| else |
| { |
| isJars = true; |
| } |
| |
| setCodebase(DERBY_TESTING, testing); |
| if (testing.getProtocol().equals("file")) { |
| File f = new File(testing.getPath()); |
| classPathSet.setProperty("derbyTesting.testjarpath", f.getAbsolutePath()); |
| } |
| |
| setCodebase(DERBY_ENGINE, "org.apache.derby.database.Database"); |
| setCodebase(DERBY_CLIENT, "org.apache.derby.client.ClientAutoloadedDriver"); |
| setCodebase(DERBY_SHARED, "org.apache.derby.shared.common.error.MessageUtils"); |
| setCodebase(DERBY_NETSERVER, "org.apache.derby.drda.NetworkServerControl"); |
| setCodebase(DERBY_TOOLS, "org.apache.derby.tools.ij"); |
| setCodebase(DERBY_OPTIONAL_TOOLS, "org.apache.derby.optional.utils.ToolUtilities"); |
| |
| return false; |
| } |
| |
| /** |
| * Set the System property which identifies the |
| * location of a codebase referenced by derby_tests.policy. |
| * |
| * @param codebaseProperty Name of codebase variable. |
| * @param className Name of class used to locate the codebase URL. |
| */ |
| private static void setCodebase |
| ( |
| String codebaseProperty, |
| String className |
| ) |
| { |
| if (className != null) |
| { |
| setCodebase(codebaseProperty, getURL(className)); |
| } |
| } |
| |
| /** |
| * Set the System property which identifies the |
| * location of a codebase referenced by derby_tests.policy. |
| * |
| * @param codebaseProperty Name of codebase variable. |
| * @param codebaseURL URL of codebase |
| */ |
| private static void setCodebase |
| ( |
| String codebaseProperty, |
| URL codebaseURL |
| ) |
| { |
| if (codebaseURL != null) |
| { |
| classPathSet.setProperty(codebaseProperty, codebaseURL.toExternalForm()); |
| } |
| } |
| |
| /** |
| * Return the policy file system properties for use |
| * by the old test harness. This ensures a consistent |
| * approach to setting the properties. There are the |
| * properties used to define the jar file location in |
| * any policy files. |
| */ |
| public static Properties getPolicyFilePropertiesForOldHarness() |
| { |
| return classPathSet; |
| } |
| |
| /** |
| * Get the URL of the code base from a class name. |
| * If the class cannot be loaded, null is returned. |
| */ |
| public static URL getURL(String className) { |
| try { |
| return getURL(Class.forName(className)); |
| } catch (ClassNotFoundException e) { |
| return null; |
| } catch (NoClassDefFoundError e) { |
| return null; |
| } |
| } |
| |
| /** |
| * Get the URL of the code base from a class. |
| */ |
| static URL getURL(final Class cl) |
| { |
| return AccessController.doPrivileged(new PrivilegedAction<URL>() { |
| |
| public URL run() { |
| |
| /* It's possible that the class does not have a "codeSource" |
| * associated with it (ex. if it is embedded within the JVM, |
| * as can happen with Xalan and/or a JAXP parser), so in that |
| * case we just return null. |
| */ |
| if (cl.getProtectionDomain().getCodeSource() == null) |
| return null; |
| |
| return cl.getProtectionDomain().getCodeSource().getLocation(); |
| } |
| }); |
| } |
| |
| /** |
| * Remove the security manager. |
| */ |
| private static void uninstallSecurityManager() |
| { |
| |
| AccessController.doPrivileged |
| ( |
| new PrivilegedAction<Void>() |
| { |
| public Void run() { |
| System.setSecurityManager(null); |
| return null; |
| } |
| } |
| ); |
| |
| } |
| |
| /** |
| * Returns the location of the effective policy resource. |
| * <p> |
| * If two valid policy resources from different locations are specified, |
| * they will be merged into one policy file. |
| * |
| * @param policy1 first policy |
| * @param policy2 second policy (may be {@code null}) |
| * @return The location of a policy resource, or {@linkplain #NO_POLICY}. |
| * @throws IOException if reading or writing a policy resource fails |
| */ |
| private static String getEffectivePolicyResource(String policy1, |
| String policy2) |
| throws IOException { |
| String resource = policy1; |
| if (!NO_POLICY.equals(resource)) { |
| URL url1 = getResourceURL(policy1); |
| resource = url1.toExternalForm(); |
| if (policy2 != null) { |
| URL url2 = getResourceURL(policy2); |
| // Don't use URL.equals - it blocks and goes onto the network. |
| if (!url1.toExternalForm().equals(url2.toExternalForm())) { |
| resource = mergePolicies(url1, url2); |
| } |
| } |
| } |
| return resource; |
| } |
| |
| /** |
| * Returns a URL for the given policy resource. |
| * |
| * @param policy the policy resource |
| * @return A {@code URL} denoting the policy resource. |
| * @throws MalformedURLException if the resource string not a valid URL |
| */ |
| private static URL getResourceURL(final String policy) |
| throws MalformedURLException { |
| URL url = BaseTestCase.getTestResource(policy); |
| if (url == null) { |
| // Assume the policy is expressed as an URL already, probably |
| // as a file. |
| url = new URL(policy); |
| } |
| return url; |
| } |
| |
| /** |
| * Merges the two specified policy resources (typically files), and writes |
| * the combined policy to a new file. |
| * |
| * @param policy1 the first policy |
| * @param policy2 the second policy |
| * @return The resource location string for a policy file. |
| * @throws IOException if reading or writing to one of the resources fails |
| */ |
| private static String mergePolicies(URL policy1, URL policy2) |
| throws IOException { |
| // Create target directory for the merged policy files. |
| String sytemHome = |
| BaseTestCase.getSystemProperty("derby.system.home"); |
| File sysDir = new File(sytemHome == null ? "system" : sytemHome); |
| File varDir = new File(sysDir, "var"); |
| // If running as the first test the system directory may not exist. |
| // This situation looks a little bit suspicious - investigate? |
| mkdir(sysDir); |
| mkdir(varDir); |
| |
| // Read the contents of both policy files and write them out to |
| // a new policy file. Construct a somewhat informative file name. |
| final File mergedPF = new File(varDir, |
| new File(policy2.getPath()).getName() + |
| "-MERGED_WITH-" + |
| new File(policy1.getPath()).getName()); |
| OutputStream o = |
| PrivilegedFileOpsForTests.getFileOutputStream(mergedPF); |
| byte[] buf = new byte[1024]; |
| int read; |
| InputStream i1 = openStream(policy1); |
| while ((read = i1.read(buf)) != -1) { |
| o.write(buf, 0, read); |
| } |
| i1.close(); |
| InputStream i2 = openStream(policy2); |
| while ((read = i2.read(buf)) != -1) { |
| o.write(buf, 0, read); |
| } |
| i2.close(); |
| o.close(); |
| try { |
| return AccessController.doPrivileged( |
| new PrivilegedExceptionAction<String>() { |
| public String run() throws MalformedURLException { |
| return mergedPF.toURI().toURL().toExternalForm(); |
| } |
| }); |
| } catch (PrivilegedActionException pae) { |
| throw (MalformedURLException)pae.getException(); |
| } |
| } |
| |
| /** Opens the resource stream in a privileged block. */ |
| private static InputStream openStream(final URL resource) |
| throws IOException { |
| try { |
| return AccessController.doPrivileged( |
| new PrivilegedExceptionAction<InputStream>(){ |
| public InputStream run() throws IOException { |
| return resource.openStream(); |
| } |
| } |
| ); |
| } catch (PrivilegedActionException pae) { |
| throw (IOException)pae.getException(); |
| } |
| } |
| |
| /** Creates the specified directory if it doesn't exist. */ |
| private static void mkdir(final File dir) { |
| AccessController.doPrivileged( |
| new PrivilegedAction<Void>() { |
| public Void run() { |
| if (!dir.exists() && !dir.mkdir()) { |
| fail("failed to create directory: " + dir.getPath()); |
| } |
| return null; |
| } |
| } |
| ); |
| } |
| |
| /** Prints a debug message if debugging is enabled. */ |
| private static void println(String msg) { |
| BaseTestCase.println("{SecurityManagerSetup} " + msg); |
| } |
| } |