#432 Injector - prevent static field injection, add more tests

This commit is contained in:
ljacqu
2016-04-30 10:44:32 +02:00
parent 2c491803d3
commit 908399e271
14 changed files with 211 additions and 45 deletions
+2 -2
View File
@@ -253,8 +253,8 @@ public class AuthMe extends JavaPlugin {
initializer.provide(DataFolder.class, getDataFolder());
// Register elements we instantiate manually
initializer.register(newSettings);
initializer.register(messages);
initializer.register(NewSetting.class, newSettings);
initializer.register(Messages.class, messages);
initializer.register(DataSource.class, database);
initializer.provide(BaseCommands.class, CommandInitializer.buildCommands(initializer));
@@ -9,7 +9,6 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -51,20 +50,6 @@ public class AuthMeServiceInitializer {
return get(clazz, new HashSet<Class<?>>());
}
/**
* Registers an instantiation by its type.
*
* @param object the object to register
* @throws IllegalStateException if an object of the same type has already been registered
*/
public void register(Object object) {
if (object instanceof Type) {
throw new IllegalStateException("You tried to register a Type object: '" + object
+ "'. This likely indicates an error. Please use register(Class<T>, T) if really desired.");
}
storeObject(object);
}
/**
* Register an object with a custom class (supertype). Use this for example to specify a
* concrete implementation of an interface or an abstract class.
@@ -8,7 +8,7 @@ import java.lang.annotation.Target;
/**
* Annotation to denote the collection of AuthMe commands.
*/
@Target(ElementType.PARAMETER)
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface BaseCommands {
}
@@ -1,5 +1,7 @@
package fr.xephi.authme.initialization;
import com.google.common.base.Preconditions;
import javax.inject.Inject;
import javax.inject.Provider;
import java.lang.annotation.Annotation;
@@ -36,6 +38,7 @@ class ConstructorInjection<T> implements Injection<T> {
@Override
public T instantiateWith(Object... values) {
validateNoNullValues(values);
try {
return constructor.newInstance(values);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
@@ -60,7 +63,7 @@ class ConstructorInjection<T> implements Injection<T> {
*
* @param clazz the class to process
* @param <T> the class' type
* @return injection constructor for the class
* @return injection constructor for the class, null if not applicable
*/
@SuppressWarnings("unchecked")
private static <T> Constructor<T> getInjectionConstructor(Class<T> clazz) {
@@ -74,4 +77,10 @@ class ConstructorInjection<T> implements Injection<T> {
return null;
}
private static void validateNoNullValues(Object[] array) {
for (Object entry : array) {
Preconditions.checkNotNull(entry);
}
}
}
@@ -8,7 +8,7 @@ import java.lang.annotation.Target;
/**
* Annotation for specifying the plugin's data folder.
*/
@Target(ElementType.PARAMETER)
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataFolder {
}
@@ -8,6 +8,7 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -57,6 +58,7 @@ class FieldInjection<T> implements Injection<T> {
for (int i = 0; i < fields.length; ++i) {
try {
Preconditions.checkNotNull(values[i]);
fields[i].set(instance, values[i]);
} catch (IllegalAccessException e) {
throw new UnsupportedOperationException(e);
@@ -65,15 +67,15 @@ class FieldInjection<T> implements Injection<T> {
return instance;
}
private static Class<?> getFirstNonInjectAnnotation(Field field) {
for (Annotation annotation : field.getAnnotations()) {
if (annotation.annotationType() != Inject.class) {
return annotation.annotationType();
}
}
return null;
}
/**
* Returns a provider for a {@code FieldInjection<T>} instance, i.e. a provides an object
* with which field injection can be performed on the given class if applicable. The provided
* value is {@code null} if field injection cannot be applied to the class.
*
* @param clazz the class to provide field injection for
* @param <T> the class' type
* @return field injection provider for the given class
*/
public static <T> Provider<FieldInjection<T>> provide(final Class<T> clazz) {
return new Provider<FieldInjection<T>>() {
@Override
@@ -92,6 +94,10 @@ class FieldInjection<T> implements Injection<T> {
List<Field> fields = new ArrayList<>();
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Inject.class)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException(String.format("Field '%s' in class '%s' is static but "
+ "annotated with @Inject", field.getName(), clazz.getSimpleName()));
}
field.setAccessible(true);
fields.add(field);
}
@@ -99,6 +105,15 @@ class FieldInjection<T> implements Injection<T> {
return fields;
}
private static Class<?> getFirstNonInjectAnnotation(Field field) {
for (Annotation annotation : field.getAnnotations()) {
if (annotation.annotationType() != Inject.class) {
return annotation.annotationType();
}
}
return null;
}
private static <T> Constructor<T> getDefaultConstructor(Class<T> clazz) {
try {
Constructor<?> defaultConstructor = clazz.getDeclaredConstructor();