LUCENE-6921: Fix SPIClassIterator#isParentClassLoader to don't require extra permissions

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1718078 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Uwe Schindler 2015-12-05 12:55:29 +00:00
parent 4394851104
commit 9d4c415450
3 changed files with 72 additions and 12 deletions

View File

@ -138,6 +138,9 @@ Other
to use MethodHandles and work without extra security privileges.
(Uwe Schindler, Robert Muir)
* LUCENE-6921: Fix SPIClassIterator#isParentClassLoader to don't
require extra permissions. (Uwe Schindler)
======================= Lucene 5.4.0 =======================
New Features

View File

@ -23,12 +23,15 @@ import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Locale;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.ServiceConfigurationError;
/**
@ -47,35 +50,51 @@ public final class SPIClassIterator<S> implements Iterator<Class<? extends S>> {
private final Enumeration<URL> profilesEnum;
private Iterator<String> linesIterator;
/** Creates a new SPI iterator to lookup services of type {@code clazz} using the context classloader. */
public static <S> SPIClassIterator<S> get(Class<S> clazz) {
return new SPIClassIterator<>(clazz, Thread.currentThread().getContextClassLoader());
}
/** Creates a new SPI iterator to lookup services of type {@code clazz} using the given classloader. */
public static <S> SPIClassIterator<S> get(Class<S> clazz, ClassLoader loader) {
return new SPIClassIterator<>(clazz, loader);
}
/** Utility method to check if some class loader is a (grand-)parent of or the same as another one.
* This means the child will be able to load all classes from the parent, too. */
public static boolean isParentClassLoader(final ClassLoader parent, ClassLoader child) {
while (child != null) {
if (child == parent) {
return true;
}
child = child.getParent();
/**
* Utility method to check if some class loader is a (grand-)parent of or the same as another one.
* This means the child will be able to load all classes from the parent, too.
* <p>
* If Lucene's codebase doesn't have enough permissions to do the check, {@code false} is returned.
*/
public static boolean isParentClassLoader(final ClassLoader parent, final ClassLoader child) {
if (parent == child) {
return true; // don't try to use AccessController for performance
}
return false;
return AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> {
try {
ClassLoader cl = child;
while (cl != null) {
if (cl == parent) {
return true;
}
cl = cl.getParent();
}
return false;
} catch (SecurityException se) {
return false;
}
});
}
private SPIClassIterator(Class<S> clazz, ClassLoader loader) {
this.clazz = clazz;
this.clazz = Objects.requireNonNull(clazz, "clazz");
this.loader = Objects.requireNonNull(loader, "loader");
try {
final String fullName = META_INF_SERVICES + clazz.getName();
this.profilesEnum = (loader == null) ? ClassLoader.getSystemResources(fullName) : loader.getResources(fullName);
this.profilesEnum = loader.getResources(fullName);
} catch (IOException ioe) {
throw new ServiceConfigurationError("Error loading SPI profiles for type " + clazz.getName() + " from classpath", ioe);
}
this.loader = (loader == null) ? ClassLoader.getSystemClassLoader() : loader;
this.linesIterator = Collections.<String>emptySet().iterator();
}

View File

@ -0,0 +1,38 @@
package org.apache.lucene.util;
/*
* 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.
*/
import java.net.URL;
import java.net.URLClassLoader;
public class TestSPIClassIterator extends LuceneTestCase {
public void testParentChild() throws Exception {
final ClassLoader parent = getClass().getClassLoader();
final ClassLoader child = URLClassLoader.newInstance(new URL[0], parent);
assertTrue(checkNoPerms(parent, parent));
assertTrue(checkNoPerms(child, child));
assertTrue(checkNoPerms(parent, child));
assertFalse(checkNoPerms(child, parent));
}
private boolean checkNoPerms(ClassLoader parent, ClassLoader child) throws Exception {
return runWithRestrictedPermissions(() -> SPIClassIterator.isParentClassLoader(parent, child));
}
}