mirror of
https://github.com/google/guice.git
synced 2024-04-21 12:32:36 +00:00
904 lines
28 KiB
Java
904 lines
28 KiB
Java
package com.google.inject;
|
|
|
|
import static com.google.common.truth.Truth.assertThat;
|
|
import static java.lang.annotation.ElementType.METHOD;
|
|
import static java.lang.annotation.ElementType.TYPE_USE;
|
|
import static org.junit.Assert.assertThrows;
|
|
|
|
import com.google.common.collect.ImmutableSet;
|
|
import com.google.common.testing.TestLogHandler;
|
|
import com.google.inject.RestrictedBindingSource.RestrictionLevel;
|
|
import com.google.inject.spi.ElementSource;
|
|
import com.google.inject.spi.Elements;
|
|
import com.google.inject.spi.InjectionPoint;
|
|
import com.google.inject.spi.ModuleAnnotatedMethodScanner;
|
|
import com.google.inject.util.Modules;
|
|
import java.lang.annotation.Annotation;
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.lang.annotation.Target;
|
|
import java.lang.reflect.Method;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.LogRecord;
|
|
import java.util.logging.Logger;
|
|
import javax.inject.Named;
|
|
import javax.inject.Qualifier;
|
|
import org.junit.Test;
|
|
import org.junit.runner.RunWith;
|
|
import org.junit.runners.JUnit4;
|
|
|
|
/**
|
|
* Tests for {@link RestrictedBindingSource}.
|
|
*
|
|
* @author vzm@google.com (Vladimir Makaric)
|
|
*/
|
|
@RunWith(JUnit4.class)
|
|
public class RestrictedBindingSourceTest {
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Core Functionality Tests
|
|
// --------------------------------------------------------------------------
|
|
|
|
private static final String BINDING_PERMISSION_ERROR = "Unable to bind key";
|
|
private static final String USE_NETWORK_MODULE = "Please install NetworkModule.";
|
|
private static final String USE_ROUTING_MODULE = "Please install RoutingModule.";
|
|
private static final String NETWORK_ANNOTATION_IS_RESTRICTED =
|
|
"The @Network annotation can only be used to annotate Network library Keys.";
|
|
|
|
@RestrictedBindingSource.Permit
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
@interface NetworkLibrary {}
|
|
|
|
@Qualifier
|
|
@RestrictedBindingSource(
|
|
explanation = USE_NETWORK_MODULE,
|
|
permits = {NetworkLibrary.class})
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
@interface GatewayIpAdress {}
|
|
|
|
@Qualifier
|
|
@RestrictedBindingSource(
|
|
explanation = USE_NETWORK_MODULE,
|
|
permits = {NetworkLibrary.class})
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
@interface Hostname {}
|
|
|
|
@NetworkLibrary
|
|
private static class NetworkModule extends AbstractModule {
|
|
@Provides
|
|
@GatewayIpAdress
|
|
int provideIpAddress() {
|
|
return 21321566;
|
|
}
|
|
|
|
@Override
|
|
protected void configure() {
|
|
bind(String.class).annotatedWith(Hostname.class).toInstance("google.com");
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void networkLibraryCanProvideItsBindings() {
|
|
Guice.createInjector(new NetworkModule());
|
|
}
|
|
|
|
@RestrictedBindingSource(
|
|
explanation = USE_ROUTING_MODULE,
|
|
permits = {NetworkLibrary.class})
|
|
@ImplementedBy(RoutingTableImpl.class) // For testing untargetted bindings.
|
|
interface RoutingTable {
|
|
int getNextHopIpAddress(int destinationIpAddress);
|
|
}
|
|
|
|
@NetworkLibrary
|
|
private static class RoutingModule extends AbstractModule {
|
|
@Provides
|
|
RoutingTable provideRoutingTable(@GatewayIpAdress int gateway) {
|
|
return destinationIp -> gateway;
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void networkBindingCantBeProvidedByOtherModules() {
|
|
AbstractModule rogueModule =
|
|
new AbstractModule() {
|
|
// This will fail, the gateway IP can only be provided by the network library.
|
|
@Provides
|
|
@GatewayIpAdress
|
|
int provideGatewayIp() {
|
|
return 42;
|
|
}
|
|
|
|
@Override
|
|
protected void configure() {
|
|
install(new RoutingModule());
|
|
}
|
|
};
|
|
|
|
CreationException expected = assertThatInjectorCreationFails(rogueModule);
|
|
|
|
assertThat(expected).hasMessageThat().contains(BINDING_PERMISSION_ERROR);
|
|
assertThat(expected).hasMessageThat().contains(USE_NETWORK_MODULE);
|
|
}
|
|
|
|
@Test
|
|
public void missingImplementationErrorForRestrictedBindingIncludesExplanation() {
|
|
CreationException expected = assertThatInjectorCreationFails(new RoutingModule());
|
|
|
|
assertThat(expected)
|
|
.hasMessageThat()
|
|
.contains(
|
|
"Hint: This key is restricted and cannot be bound directly. Restriction explanation:"
|
|
+ " Please install NetworkModule.");
|
|
}
|
|
|
|
@Test
|
|
public void canBindRestrictedTypeWithUnrestrictedQualifierAnnotation() {
|
|
Guice.createInjector(
|
|
new AbstractModule() {
|
|
@Provides
|
|
@Named("custom")
|
|
RoutingTable provideRoutingTable() {
|
|
return ip -> ip;
|
|
}
|
|
});
|
|
}
|
|
|
|
@Test
|
|
public void twoRogueNetworkBindingsYieldTwoErrorMessages() {
|
|
AbstractModule rogueModule =
|
|
new AbstractModule() {
|
|
@Provides
|
|
@GatewayIpAdress
|
|
int provideGatewayIp() {
|
|
return 42;
|
|
}
|
|
|
|
@Provides
|
|
RoutingTable provideRoutingTable() {
|
|
return destinationIp -> 0;
|
|
}
|
|
};
|
|
|
|
CreationException expected = assertThatInjectorCreationFails(rogueModule);
|
|
|
|
assertThat(expected).hasMessageThat().contains(BINDING_PERMISSION_ERROR);
|
|
assertThat(expected).hasMessageThat().contains(USE_NETWORK_MODULE);
|
|
assertThat(expected).hasMessageThat().contains(USE_ROUTING_MODULE);
|
|
}
|
|
|
|
@RestrictedBindingSource.Permit
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
@interface NetworkTestLibrary {}
|
|
|
|
@Qualifier
|
|
@RestrictedBindingSource(
|
|
explanation = USE_NETWORK_MODULE,
|
|
permits = {NetworkLibrary.class, NetworkTestLibrary.class})
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
@interface MacAddress {}
|
|
|
|
@NetworkTestLibrary
|
|
private static class TestMacAddressModule extends AbstractModule {
|
|
@Provides
|
|
@MacAddress
|
|
String provideMacAddress() {
|
|
return "deadbeef";
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void bindingWithTwoPermitsAllowedIfOnePresent() {
|
|
Guice.createInjector(new TestMacAddressModule());
|
|
}
|
|
|
|
private static class RoutingTableImpl implements RoutingTable {
|
|
@Inject
|
|
RoutingTableImpl() {}
|
|
|
|
@Override
|
|
public int getNextHopIpAddress(int destinationIpAddress) {
|
|
return destinationIpAddress + 2;
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void untargettedBindingAllowedWithPermit() {
|
|
@NetworkLibrary
|
|
class PermittedNetworkModule extends AbstractModule {
|
|
@Override
|
|
protected void configure() {
|
|
bind(RoutingTable.class);
|
|
}
|
|
}
|
|
|
|
Guice.createInjector(new PermittedNetworkModule());
|
|
}
|
|
|
|
@Test
|
|
public void untargettedBindingDisallowedWithoutPermit() {
|
|
AbstractModule rogueModule =
|
|
new AbstractModule() {
|
|
@Override
|
|
protected void configure() {
|
|
bind(RoutingTable.class);
|
|
}
|
|
};
|
|
|
|
CreationException expected = assertThatInjectorCreationFails(rogueModule);
|
|
|
|
assertThat(expected).hasMessageThat().contains(BINDING_PERMISSION_ERROR);
|
|
assertThat(expected).hasMessageThat().contains(USE_ROUTING_MODULE);
|
|
}
|
|
|
|
@RestrictedBindingSource.Permit
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
@Target(TYPE_USE)
|
|
@interface FooPermit {}
|
|
|
|
@Qualifier
|
|
@RestrictedBindingSource(
|
|
explanation = "Only modules with FooPermit can bind @Foo bindings.",
|
|
permits = {FooPermit.class})
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
@interface Foo {}
|
|
|
|
@Test
|
|
public void permitOnAnonymousClassWorks() {
|
|
Guice.createInjector(
|
|
new @FooPermit AbstractModule() {
|
|
@Provides
|
|
@Foo
|
|
String provideFooString() {
|
|
return "foo";
|
|
}
|
|
});
|
|
}
|
|
|
|
@Qualifier
|
|
@RestrictedBindingSource(
|
|
explanation = USE_NETWORK_MODULE,
|
|
permits = {NetworkLibrary.class},
|
|
restrictionLevel = RestrictionLevel.WARNING)
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
@interface HostIp {}
|
|
|
|
@Test
|
|
public void rogueBindingWithWarningRestrictionLevel() {
|
|
Logger logger = Logger.getLogger(RestrictedBindingSource.class.getName());
|
|
TestLogHandler testLogHandler = new TestLogHandler();
|
|
logger.addHandler(testLogHandler);
|
|
|
|
Guice.createInjector(
|
|
new AbstractModule() {
|
|
@Provides
|
|
@HostIp
|
|
int provideRogueHostIp() {
|
|
return 4;
|
|
}
|
|
});
|
|
|
|
List<LogRecord> logs = testLogHandler.getStoredLogRecords();
|
|
assertThat(logs).hasSize(1);
|
|
assertThat(logs.get(0).getLevel()).isEqualTo(Level.WARNING);
|
|
assertThat(logs.get(0).getMessage()).contains(USE_NETWORK_MODULE);
|
|
assertThat(logs.get(0).getMessage()).contains("provideRogueHostIp");
|
|
logger.removeHandler(testLogHandler);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Module Exemption Tests
|
|
// --------------------------------------------------------------------------
|
|
|
|
private static final String USE_DNS_MODULE = "Use the official DNS module";
|
|
|
|
@Qualifier
|
|
@RestrictedBindingSource(
|
|
explanation = USE_DNS_MODULE,
|
|
permits = {NetworkLibrary.class},
|
|
exemptModules =
|
|
"com.google.inject.RestrictedBindingSourceTest\\$FooRogueDnsModule"
|
|
+ "|com.google.inject.RestrictedBindingSourceTest\\$BarRogueDnsModule"
|
|
+ "|com.google.inject.RestrictedBindingSourceTest\\$TopLevelModulePrivatelyBindingDnsAddress")
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
@interface DnsAddress {}
|
|
|
|
static class FooRogueDnsModule extends AbstractModule {
|
|
@Provides
|
|
@DnsAddress
|
|
int rogueDns() {
|
|
return 4;
|
|
}
|
|
}
|
|
|
|
static class BarRogueDnsModule extends AbstractModule {
|
|
@Provides
|
|
@DnsAddress
|
|
int rogueDns() {
|
|
return 5;
|
|
}
|
|
}
|
|
|
|
// Non-exempt
|
|
static class BazRogueDnsModule extends AbstractModule {
|
|
@Provides
|
|
@DnsAddress
|
|
int rogueDns() {
|
|
return 5;
|
|
}
|
|
}
|
|
|
|
static class TopLevelModulePrivatelyBindingDnsAddress extends AbstractModule {
|
|
@Override
|
|
protected void configure() {
|
|
install(
|
|
new PrivateModule() {
|
|
@Override
|
|
protected void configure() {
|
|
// Non-exempt module.
|
|
install(new BazRogueDnsModule());
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void exemptModulesCanCreateRestrictedBinding() {
|
|
Guice.createInjector(new FooRogueDnsModule());
|
|
Guice.createInjector(new BarRogueDnsModule());
|
|
}
|
|
|
|
@Test
|
|
public void nonExemptModuleCantCreateRestrictedBinding() {
|
|
CreationException expected = assertThatInjectorCreationFails(new BazRogueDnsModule());
|
|
|
|
assertThat(expected).hasMessageThat().contains(BINDING_PERMISSION_ERROR);
|
|
assertThat(expected).hasMessageThat().contains(USE_DNS_MODULE);
|
|
}
|
|
|
|
@Test
|
|
public void parentModuleExeptionAppliesToChildPrivateModule() {
|
|
Guice.createInjector(new TopLevelModulePrivatelyBindingDnsAddress());
|
|
}
|
|
|
|
@Test
|
|
public void exemptModuleCanBeOverridenIfRestrictedBindingIsNotOverriden() {
|
|
// This tests that we check for exemptions on the module stack of the original element source.
|
|
Guice.createInjector(
|
|
Modules.override(
|
|
new AbstractModule() {
|
|
@Override
|
|
protected void configure() {
|
|
install(new BarRogueDnsModule());
|
|
}
|
|
|
|
@Provides
|
|
String random() {
|
|
return "foo";
|
|
}
|
|
})
|
|
.with(
|
|
new AbstractModule() {
|
|
@Provides
|
|
String random() {
|
|
return "bar";
|
|
}
|
|
}));
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Binder.withSource Tests
|
|
// --------------------------------------------------------------------------
|
|
|
|
@NetworkLibrary
|
|
private static class PermittedModule extends AbstractModule {
|
|
|
|
@Override
|
|
protected void configure() {
|
|
Method userUnpermittedModuleMethod;
|
|
try {
|
|
userUnpermittedModuleMethod = UnpermittedModule.class.getMethod("foo");
|
|
} catch (NoSuchMethodException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
|
|
binder()
|
|
.withSource(userUnpermittedModuleMethod)
|
|
.bind(String.class)
|
|
.annotatedWith(Hostname.class)
|
|
.toInstance("google.com");
|
|
}
|
|
}
|
|
|
|
private static class UnpermittedModule extends AbstractModule {
|
|
public String foo() {
|
|
return "bar";
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void permittedModuleCanWithSourceAnUnpermittedModuleMethod() {
|
|
Guice.createInjector(new PermittedModule());
|
|
}
|
|
|
|
@Test
|
|
public void unpermittedModuleCantWithSourceAPermittedModule() {
|
|
AbstractModule rogueModule =
|
|
new AbstractModule() {
|
|
@Override
|
|
protected void configure() {
|
|
binder()
|
|
.withSource(PermittedModule.class)
|
|
.bindConstant()
|
|
.annotatedWith(GatewayIpAdress.class)
|
|
.to(0);
|
|
}
|
|
};
|
|
|
|
CreationException expected = assertThatInjectorCreationFails(rogueModule);
|
|
|
|
assertThat(expected).hasMessageThat().contains(BINDING_PERMISSION_ERROR);
|
|
assertThat(expected).hasMessageThat().contains(USE_NETWORK_MODULE);
|
|
}
|
|
|
|
@Test
|
|
public void getElements_getModule_works() {
|
|
Guice.createInjector(Elements.getModule(Elements.getElements(new NetworkModule())));
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// ModuleAnnotatedMethodScanner tests
|
|
// --------------------------------------------------------------------------
|
|
|
|
@Target(METHOD)
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
private @interface NetworkProvides {}
|
|
|
|
@Qualifier
|
|
@RestrictedBindingSource(
|
|
explanation = NETWORK_ANNOTATION_IS_RESTRICTED,
|
|
permits = {NetworkLibrary.class})
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
@interface Network {}
|
|
|
|
// Adds the NetworkLibrary-owned Network annotation to keys produced by @NetworkProvides methods.
|
|
private static class NetworkProvidesScanner extends ModuleAnnotatedMethodScanner {
|
|
@Override
|
|
public String toString() {
|
|
return "NetworkProvidesScanner";
|
|
}
|
|
|
|
@Override
|
|
public Set<? extends Class<? extends Annotation>> annotationClasses() {
|
|
return ImmutableSet.of(NetworkProvides.class);
|
|
}
|
|
|
|
@Override
|
|
public <T> Key<T> prepareMethod(
|
|
Binder binder, Annotation annotation, Key<T> key, InjectionPoint injectionPoint) {
|
|
return key.withAnnotation(Network.class);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void rogueBindingByMethodScannerDenied() {
|
|
AbstractModule rogueModule =
|
|
new AbstractModule() {
|
|
@NetworkProvides
|
|
String provideNetworkString() {
|
|
return "lorem ipsum";
|
|
}
|
|
};
|
|
|
|
CreationException expected =
|
|
assertThatInjectorCreationFails(rogueModule, scannerModule(new NetworkProvidesScanner()));
|
|
|
|
assertThat(expected).hasMessageThat().contains(BINDING_PERMISSION_ERROR);
|
|
assertThat(expected).hasMessageThat().contains(NETWORK_ANNOTATION_IS_RESTRICTED);
|
|
}
|
|
|
|
@Test
|
|
public void bindingsAddedByMethodScannerAllowedByNetworkLib() {
|
|
@NetworkLibrary
|
|
class NetworkModuleWithCustomProvides extends AbstractModule {
|
|
@NetworkProvides
|
|
String provideNetworkString() {
|
|
return "the real network string";
|
|
}
|
|
}
|
|
|
|
Guice.createInjector(
|
|
new NetworkModuleWithCustomProvides(), scannerModule(new NetworkProvidesScanner()));
|
|
}
|
|
|
|
@Test
|
|
public void scannerWithPermitCanCreateRestrictedBinding() {
|
|
@NetworkLibrary
|
|
class NetworkProvidesScannerWithPermit extends NetworkProvidesScanner {}
|
|
|
|
AbstractModule moduleWithNetworkProvidesMethod =
|
|
new AbstractModule() {
|
|
@NetworkProvides
|
|
String provideNetworkString() {
|
|
return "lorem ipsum";
|
|
}
|
|
};
|
|
|
|
Guice.createInjector(
|
|
moduleWithNetworkProvidesMethod, scannerModule(new NetworkProvidesScannerWithPermit()));
|
|
}
|
|
|
|
@Test
|
|
public void scannerWithPermitCanCreateRestrictedBindings() {
|
|
@NetworkLibrary
|
|
class NetworkProvidesScannerWithPermit extends NetworkProvidesScanner {
|
|
@Override
|
|
public <T> Key<T> prepareMethod(
|
|
Binder binder, Annotation annotation, Key<T> key, InjectionPoint injectionPoint) {
|
|
binder.install(
|
|
new AbstractModule() {
|
|
@Provides
|
|
@GatewayIpAdress
|
|
int provideGatewayIp() {
|
|
return 42;
|
|
}
|
|
});
|
|
return Key.get(key.getTypeLiteral(), Network.class);
|
|
}
|
|
}
|
|
AbstractModule moduleWithNetworkProvidesMethod =
|
|
new AbstractModule() {
|
|
@NetworkProvides
|
|
String provideNetworkString() {
|
|
return "lorem ipsum";
|
|
}
|
|
};
|
|
|
|
Injector injector =
|
|
Guice.createInjector(
|
|
moduleWithNetworkProvidesMethod, scannerModule(new NetworkProvidesScannerWithPermit()));
|
|
|
|
assertThat(injector.getInstance(Key.get(Integer.class, GatewayIpAdress.class))).isEqualTo(42);
|
|
}
|
|
|
|
@Test
|
|
public void moduleInstalledByScannerInheritsMethodModulePermit() {
|
|
class NetworkProvidesScannerWithoutPermit extends NetworkProvidesScanner {
|
|
@Override
|
|
public <T> Key<T> prepareMethod(
|
|
Binder binder, Annotation annotation, Key<T> key, InjectionPoint injectionPoint) {
|
|
binder.install(
|
|
new AbstractModule() {
|
|
@Provides
|
|
@GatewayIpAdress
|
|
int provideGatewayIp() {
|
|
return 42;
|
|
}
|
|
});
|
|
return key.withAnnotation(Network.class);
|
|
}
|
|
}
|
|
@NetworkLibrary
|
|
class ScannedModuleWithPermit extends AbstractModule {
|
|
@NetworkProvides
|
|
String provideNetworkString() {
|
|
return "lorem ipsum";
|
|
}
|
|
}
|
|
|
|
Injector injector =
|
|
Guice.createInjector(
|
|
new ScannedModuleWithPermit(),
|
|
scannerModule(new NetworkProvidesScannerWithoutPermit()));
|
|
|
|
assertThat(injector.getInstance(Key.get(Integer.class, GatewayIpAdress.class))).isEqualTo(42);
|
|
}
|
|
|
|
private static Module scannerModule(ModuleAnnotatedMethodScanner scanner) {
|
|
return new AbstractModule() {
|
|
@Override
|
|
protected void configure() {
|
|
binder().scanModulesForAnnotatedMethods(scanner);
|
|
}
|
|
};
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Modules.override tests
|
|
// --------------------------------------------------------------------------
|
|
|
|
@Test
|
|
public void modulesOverrideCantOverrideRestrictedBinding() {
|
|
Module rogueModule =
|
|
Modules.override(new NetworkModule())
|
|
.with(
|
|
new AbstractModule() {
|
|
@Provides
|
|
@GatewayIpAdress
|
|
int provideRogueGatewayIp() {
|
|
return 12345;
|
|
}
|
|
});
|
|
|
|
CreationException expected = assertThatInjectorCreationFails(rogueModule);
|
|
|
|
assertThat(expected).hasMessageThat().contains(BINDING_PERMISSION_ERROR);
|
|
assertThat(expected).hasMessageThat().contains(USE_NETWORK_MODULE);
|
|
}
|
|
|
|
@Test
|
|
public void modulesOverrideRestrictedBindingAllowedIfParentHasPermit() {
|
|
@NetworkLibrary
|
|
class NetworkModuleVersion2 extends AbstractModule {
|
|
@Override
|
|
protected void configure() {
|
|
install(
|
|
Modules.override(new NetworkModule())
|
|
.with(
|
|
new AbstractModule() {
|
|
@Provides
|
|
@GatewayIpAdress
|
|
int provideGatewayIpV2() {
|
|
return 2;
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
|
|
assertThat(
|
|
Guice.createInjector(new NetworkModuleVersion2())
|
|
.getInstance(Key.get(Integer.class, GatewayIpAdress.class)))
|
|
.isEqualTo(2);
|
|
}
|
|
|
|
@Qualifier
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
@interface UnrestrictedQualifier {}
|
|
|
|
@Test
|
|
public void modulesOverrideCanOverrideUnrestrictedBinding() {
|
|
Module overrideModule =
|
|
Modules.override(
|
|
new NetworkModule(),
|
|
new AbstractModule() {
|
|
@Override
|
|
protected void configure() {
|
|
bindConstant().annotatedWith(UnrestrictedQualifier.class).to("foo");
|
|
}
|
|
})
|
|
.with(
|
|
new AbstractModule() {
|
|
@Provides
|
|
@UnrestrictedQualifier
|
|
String provideRogueGatewayIp() {
|
|
return "bar";
|
|
}
|
|
});
|
|
|
|
assertThat(
|
|
Guice.createInjector(overrideModule)
|
|
.getInstance(Key.get(String.class, UnrestrictedQualifier.class)))
|
|
.isEqualTo("bar");
|
|
}
|
|
|
|
@Test
|
|
public void nestedModulesOverrideCanOverrideUnrestrictedBindings() {
|
|
Module overrideModule =
|
|
Modules.override(
|
|
Modules.override(
|
|
new NetworkModule(),
|
|
new AbstractModule() {
|
|
@Override
|
|
protected void configure() {
|
|
bindConstant().annotatedWith(UnrestrictedQualifier.class).to("foo");
|
|
bindConstant().annotatedWith(UnrestrictedQualifier.class).to(42);
|
|
}
|
|
})
|
|
.with(
|
|
new AbstractModule() {
|
|
@Provides
|
|
@UnrestrictedQualifier
|
|
String overrideString() {
|
|
return "bar";
|
|
}
|
|
}))
|
|
.with(
|
|
new AbstractModule() {
|
|
@Provides
|
|
@UnrestrictedQualifier
|
|
int overrideLong() {
|
|
return 45;
|
|
}
|
|
});
|
|
|
|
Injector injector = Guice.createInjector(overrideModule);
|
|
assertThat(injector.getInstance(Key.get(String.class, UnrestrictedQualifier.class)))
|
|
.isEqualTo("bar");
|
|
assertThat(injector.getInstance(Key.get(Integer.class, UnrestrictedQualifier.class)))
|
|
.isEqualTo(45);
|
|
}
|
|
|
|
@Test
|
|
public void modulesOverridePrivateModule() {
|
|
Guice.createInjector(
|
|
Modules.override(
|
|
new PrivateModule() {
|
|
@Override
|
|
protected void configure() {
|
|
install(new NetworkModule());
|
|
expose(Key.get(String.class, Hostname.class));
|
|
}
|
|
|
|
@Provides
|
|
@Exposed
|
|
@Named("custom-gateway")
|
|
int customGateway(@GatewayIpAdress int gateway) {
|
|
return gateway + 4;
|
|
}
|
|
})
|
|
.with(
|
|
new AbstractModule() {
|
|
@Provides
|
|
@Named("custom-gateway")
|
|
int provideCustomGatewayOverride() {
|
|
return 12345;
|
|
}
|
|
}));
|
|
}
|
|
|
|
@Test
|
|
public void originalElementSourceNotTrustedIfSetExternally() {
|
|
ElementSource networkElementSource =
|
|
(ElementSource) Elements.getElements(new NetworkModule()).get(0).getSource();
|
|
|
|
AbstractModule rogueModule =
|
|
new AbstractModule() {
|
|
@Override
|
|
protected void configure() {
|
|
binder()
|
|
.withSource(networkElementSource)
|
|
.bindConstant()
|
|
.annotatedWith(GatewayIpAdress.class)
|
|
.to(12);
|
|
}
|
|
};
|
|
|
|
// Confirm that the original element source was spoofed.
|
|
@SuppressWarnings("unchecked")
|
|
Binding<Integer> rogueGatewayBinding =
|
|
(Binding<Integer>) Elements.getElements(rogueModule).get(0);
|
|
assertThat(((ElementSource) rogueGatewayBinding.getSource()).getOriginalElementSource())
|
|
.isEqualTo(networkElementSource);
|
|
|
|
// Will fail because the original element source isn't trusted, becase it wasn't set by Guice
|
|
// internals.
|
|
CreationException expected = assertThatInjectorCreationFails(rogueModule);
|
|
|
|
assertThat(expected).hasMessageThat().contains(BINDING_PERMISSION_ERROR);
|
|
assertThat(expected).hasMessageThat().contains(USE_NETWORK_MODULE);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// PrivateModule tests
|
|
// --------------------------------------------------------------------------
|
|
|
|
@NetworkLibrary
|
|
private static class NetworkModuleThatInstalls extends AbstractModule {
|
|
final Module module;
|
|
|
|
NetworkModuleThatInstalls(Module module) {
|
|
this.module = module;
|
|
}
|
|
|
|
@Override
|
|
protected void configure() {
|
|
install(module);
|
|
}
|
|
}
|
|
|
|
private static class PrivateModuleCreatesUnexposedNetworkBinding extends PrivateModule {
|
|
@Override
|
|
protected void configure() {
|
|
bindConstant().annotatedWith(GatewayIpAdress.class).to(0);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void parentHasPermit_childPrivateModuleCanBind() {
|
|
Guice.createInjector(
|
|
new NetworkModuleThatInstalls(
|
|
// Allowed because the parent has the @NetworkLibrary permit.
|
|
new PrivateModuleCreatesUnexposedNetworkBinding()));
|
|
}
|
|
|
|
@Test
|
|
public void noPermitOnStack_privateModuleCantBind() {
|
|
AbstractModule rogueModule =
|
|
new AbstractModule() {
|
|
@Override
|
|
protected void configure() {
|
|
// Disallowed because there's no permit on the module stack.
|
|
install(new PrivateModuleCreatesUnexposedNetworkBinding());
|
|
}
|
|
};
|
|
|
|
CreationException expected = assertThatInjectorCreationFails(rogueModule);
|
|
|
|
assertThat(expected).hasMessageThat().contains(BINDING_PERMISSION_ERROR);
|
|
assertThat(expected).hasMessageThat().contains(USE_NETWORK_MODULE);
|
|
}
|
|
|
|
private static class PrivateModuleExposesNetworkBinding extends PrivateModule {
|
|
@Override
|
|
protected void configure() {
|
|
install(
|
|
new AbstractModule() {
|
|
@Override
|
|
protected void configure() {
|
|
bindConstant().annotatedWith(GatewayIpAdress.class).to(0);
|
|
}
|
|
});
|
|
expose(Key.get(Integer.class, GatewayIpAdress.class));
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void parentHasPermit_childPrivateModuleCanExposeBinding() {
|
|
Guice.createInjector(
|
|
new NetworkModuleThatInstalls(
|
|
// Allowed because the parent has the @NetworkLibrary permit.
|
|
new PrivateModuleExposesNetworkBinding()));
|
|
}
|
|
|
|
@Test
|
|
public void noPermitOnStack_childPrivateModuleCantExposeBinding() {
|
|
AbstractModule rogueModule =
|
|
new AbstractModule() {
|
|
@Override
|
|
protected void configure() {
|
|
// Disallowed because there's no permit on the module stack.
|
|
install(new PrivateModuleExposesNetworkBinding());
|
|
}
|
|
};
|
|
|
|
CreationException expected = assertThatInjectorCreationFails(rogueModule);
|
|
|
|
assertThat(expected).hasMessageThat().contains(BINDING_PERMISSION_ERROR);
|
|
assertThat(expected).hasMessageThat().contains(USE_NETWORK_MODULE);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Child Injector tests
|
|
// --------------------------------------------------------------------------
|
|
|
|
@Test
|
|
public void childInjectorCantBindRestrictedBindingWithoutPermit() {
|
|
Injector parent = Guice.createInjector(new NetworkModule());
|
|
AbstractModule rogueModule =
|
|
new AbstractModule() {
|
|
@Provides
|
|
RoutingTable provideRoutingTable() {
|
|
return destinationIp -> 0;
|
|
}
|
|
};
|
|
|
|
CreationException expected =
|
|
assertThrows(CreationException.class, () -> parent.createChildInjector(rogueModule));
|
|
|
|
assertThat(expected).hasMessageThat().contains(BINDING_PERMISSION_ERROR);
|
|
assertThat(expected).hasMessageThat().contains(USE_ROUTING_MODULE);
|
|
}
|
|
|
|
@Test
|
|
public void childInjectorCanBindRestrictedBindingWithPermit() {
|
|
Injector parent = Guice.createInjector(new NetworkModule());
|
|
parent.createChildInjector(new RoutingModule());
|
|
}
|
|
|
|
CreationException assertThatInjectorCreationFails(Module... modules) {
|
|
return assertThrows(CreationException.class, () -> Guice.createInjector(modules));
|
|
}
|
|
}
|