http://git-wip-us.apache.org/repos/asf/shiro/blob/f2dfa7ff/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index f686566..bc1996f 100644 --- a/pom.xml +++ b/pom.xml @@ -111,6 +111,22 @@ </properties> + <modules> + <module>lang</module> + <module>crypto</module> + <module>event</module> + <module>cache</module> + <module>config</module> + <module>core</module> + <module>web</module> + <module>support</module> + <module>samples</module> + <module>tools</module> + <module>all</module> + <module>integration-tests</module> + <module>test-coverage</module> + </modules> + <mailingLists> <mailingList> <name>Apache Shiro Users Mailing List</name> @@ -198,21 +214,6 @@ </developer> </developers> - <modules> - <module>lang</module> - <module>crypto</module> - <module>event</module> - <module>cache</module> - <module>config</module> - <module>core</module> - <module>web</module> - <module>support</module> - <module>samples</module> - <module>tools</module> - <module>all</module> - <module>test-coverage</module> - </modules> - <build> <pluginManagement> <plugins> @@ -415,6 +416,25 @@ </excludes> </configuration> </plugin> + <plugin> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-maven-plugin</artifactId> + <version>${jetty.version}</version> + <configuration> + <contextPath>/</contextPath> + <httpConnector> + <port>9080</port> + <idleTimeout>60000</idleTimeout> + </httpConnector> + <requestLog implementation="org.eclipse.jetty.server.NCSARequestLog"> + <filename>./target/yyyy_mm_dd.request.log</filename> + <retainDays>90</retainDays> + <append>true</append> + <extended>false</extended> + <logTimeZone>GMT</logTimeZone> + </requestLog> + </configuration> + </plugin> </plugins> </pluginManagement> <plugins> @@ -476,6 +496,13 @@ <goal>prepare-agent</goal> </goals> </execution> + <execution> + <id>prepare-agent-integration</id> + <phase>pre-integration-test</phase> + <goals> + <goal>prepare-agent-integration</goal> + </goals> + </execution> </executions> </plugin> <!-- Allow Groovy tests to run: --> @@ -516,7 +543,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> - <version>2.3.1</version> + <version>3.0.2</version> <configuration> <archive> <manifest> @@ -1091,6 +1118,12 @@ <version>1.1.2</version> <scope>test</scope> </dependency> + <dependency> + <groupId>com.github.mjeanroy</groupId> + <artifactId>junit-servers-jetty</artifactId> + <version>0.4.2</version> + <scope>test</scope> + </dependency> </dependencies> </dependencyManagement> @@ -1186,7 +1219,7 @@ </plugin> <plugin> <artifactId>maven-surefire-report-plugin</artifactId> - <version>2.5</version> + <version>2.19.1</version> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> @@ -1236,8 +1269,9 @@ <artifactId>jacoco-maven-plugin</artifactId> <reportSets> <reportSet> - <id>javadoc-aggregate</id> + <id>jacoco-aggregate</id> <reports> + <report>report-integration</report> <report>report</report> </reports> </reportSet>
http://git-wip-us.apache.org/repos/asf/shiro/blob/f2dfa7ff/samples/guice/pom.xml ---------------------------------------------------------------------- diff --git a/samples/guice/pom.xml b/samples/guice/pom.xml index 0727392..e8ae907 100644 --- a/samples/guice/pom.xml +++ b/samples/guice/pom.xml @@ -34,30 +34,9 @@ <build> <plugins> - <plugin> - <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <forkMode>never</forkMode> - </configuration> - </plugin> <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> - <version>${jetty.version}</version> - <configuration> - <contextPath>/</contextPath> - <httpConnector> - <port>9080</port> - <idleTimeout>60000</idleTimeout> - </httpConnector> - <requestLog implementation="org.eclipse.jetty.server.NCSARequestLog"> - <filename>./target/yyyy_mm_dd.request.log</filename> - <retainDays>90</retainDays> - <append>true</append> - <extended>false</extended> - <logTimeZone>GMT</logTimeZone> - </requestLog> - </configuration> </plugin> </plugins> </build> http://git-wip-us.apache.org/repos/asf/shiro/blob/f2dfa7ff/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java ---------------------------------------------------------------------- diff --git a/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java b/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java index 01a0113..945ae70 100644 --- a/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java +++ b/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java @@ -31,6 +31,7 @@ import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.mgt.WebSecurityManager; import javax.inject.Singleton; +import javax.servlet.Filter; import javax.servlet.ServletContext; import java.net.MalformedURLException; import java.net.URL; @@ -56,7 +57,8 @@ public class SampleShiroServletModule extends ShiroWebModule { this.addFilterChain("/login.jsp", AUTHC); this.addFilterChain("/logout", LOGOUT); this.addFilterChain("/account/**", AUTHC); - this.addFilterChain("/remoting/**", AUTHC, config(ROLES, "b2bClient"), config(PERMS, "remote:invoke:lan,wan")); + + this.addFilterChain("/remoting/**", filterConfig(AUTHC), filterConfig(ROLES, "b2bClient"), filterConfig(PERMS, "remote:invoke:lan,wan")); } @Provides http://git-wip-us.apache.org/repos/asf/shiro/blob/f2dfa7ff/samples/jaxrs/pom.xml ---------------------------------------------------------------------- diff --git a/samples/jaxrs/pom.xml b/samples/jaxrs/pom.xml index c2ef72f..ad3716e 100644 --- a/samples/jaxrs/pom.xml +++ b/samples/jaxrs/pom.xml @@ -62,12 +62,6 @@ </pluginManagement> <plugins> <plugin> - <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <forkMode>never</forkMode> - </configuration> - </plugin> - <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> </plugin> http://git-wip-us.apache.org/repos/asf/shiro/blob/f2dfa7ff/samples/servlet-plugin/pom.xml ---------------------------------------------------------------------- diff --git a/samples/servlet-plugin/pom.xml b/samples/servlet-plugin/pom.xml index 75c7cb6..c03d4f1 100644 --- a/samples/servlet-plugin/pom.xml +++ b/samples/servlet-plugin/pom.xml @@ -34,12 +34,6 @@ <build> <plugins> <plugin> - <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <forkMode>never</forkMode> - </configuration> - </plugin> - <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>${jetty.version}</version> http://git-wip-us.apache.org/repos/asf/shiro/blob/f2dfa7ff/samples/web/pom.xml ---------------------------------------------------------------------- diff --git a/samples/web/pom.xml b/samples/web/pom.xml index d10a98b..0d7dbb0 100644 --- a/samples/web/pom.xml +++ b/samples/web/pom.xml @@ -40,12 +40,6 @@ <build> <plugins> <plugin> - <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <forkMode>never</forkMode> - </configuration> - </plugin> - <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>${jetty.version}</version> http://git-wip-us.apache.org/repos/asf/shiro/blob/f2dfa7ff/support/guice/pom.xml ---------------------------------------------------------------------- diff --git a/support/guice/pom.xml b/support/guice/pom.xml index 0d2778b..295243b 100644 --- a/support/guice/pom.xml +++ b/support/guice/pom.xml @@ -31,10 +31,6 @@ <name>Apache Shiro :: Support :: Guice</name> <packaging>bundle</packaging> - <properties> - <guice.version>3.0</guice.version> - </properties> - <dependencies> <dependency> <groupId>org.apache.shiro</groupId> @@ -68,6 +64,16 @@ <artifactId>jcl-over-slf4j</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build> @@ -89,7 +95,18 @@ </instructions> </configuration> </plugin> + <plugin> + <!-- Package tests so we can re-run them with guice4 --> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> </plugins> </build> - </project> http://git-wip-us.apache.org/repos/asf/shiro/blob/f2dfa7ff/support/guice/src/main/java/org/apache/shiro/guice/ShiroModule.java ---------------------------------------------------------------------- diff --git a/support/guice/src/main/java/org/apache/shiro/guice/ShiroModule.java b/support/guice/src/main/java/org/apache/shiro/guice/ShiroModule.java index 00c40a4..2eac73d 100644 --- a/support/guice/src/main/java/org/apache/shiro/guice/ShiroModule.java +++ b/support/guice/src/main/java/org/apache/shiro/guice/ShiroModule.java @@ -18,20 +18,33 @@ */ package org.apache.shiro.guice; +import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Set; import java.util.WeakHashMap; import javax.annotation.PreDestroy; +import com.google.inject.Provider; +import com.google.inject.matcher.Matchers; +import com.google.inject.name.Names; +import com.google.inject.spi.InjectionListener; +import com.google.inject.spi.TypeEncounter; +import com.google.inject.spi.TypeListener; import org.apache.shiro.config.ConfigurationException; import org.apache.shiro.env.Environment; +import org.apache.shiro.event.EventBus; +import org.apache.shiro.event.EventBusAware; +import org.apache.shiro.event.Subscribe; +import org.apache.shiro.event.support.DefaultEventBus; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.realm.Realm; import org.apache.shiro.session.mgt.DefaultSessionManager; import org.apache.shiro.session.mgt.SessionManager; +import org.apache.shiro.util.ClassUtils; import org.apache.shiro.util.Destroyable; import com.google.inject.Key; @@ -57,6 +70,9 @@ public abstract class ShiroModule extends PrivateModule implements Destroyable { bindSessionManager(bind(SessionManager.class)); bindEnvironment(bind(Environment.class)); bindListener(BeanTypeListener.MATCHER, new BeanTypeListener()); + bindEventBus(bind(EventBus.class)); + bindListener(Matchers.any(), new SubscribedEventTypeListener()); + bindListener(Matchers.any(), new EventBusAwareTypeListener()); final DestroyableInjectionListener.DestroyableRegistry registry = new DestroyableInjectionListener.DestroyableRegistry() { public void add(Destroyable destroyable) { ShiroModule.this.add(destroyable); @@ -70,6 +86,7 @@ public abstract class ShiroModule extends PrivateModule implements Destroyable { bindListener(LifecycleTypeListener.MATCHER, new LifecycleTypeListener(registry)); expose(SecurityManager.class); + expose(EventBus.class); configureShiro(); bind(realmCollectionKey()) @@ -153,6 +170,15 @@ public abstract class ShiroModule extends PrivateModule implements Destroyable { } /** + * Binds the EventBus. Override this method in order to provide your own {@link EventBus} binding. + * @param bind + * @since 1.4 + */ + protected void bindEventBus(AnnotatedBindingBuilder<EventBus> bind) { + bind.to(DefaultEventBus.class).asEagerSingleton(); + } + + /** * Destroys all beans created within this module that implement {@link org.apache.shiro.util.Destroyable}. Should be called when this * module will no longer be used. * @@ -167,4 +193,39 @@ public abstract class ShiroModule extends PrivateModule implements Destroyable { public void add(Destroyable destroyable) { this.destroyables.add(destroyable); } + + private class SubscribedEventTypeListener implements TypeListener { + @Override + public <I> void hear(TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEncounter) { + + final Provider<EventBus> eventBusProvider = typeEncounter.getProvider(EventBus.class); + + List<Method> methods = ClassUtils.getAnnotatedMethods(typeLiteral.getRawType(), Subscribe.class); + if (methods != null && !methods.isEmpty()) { + typeEncounter.register( new InjectionListener<I>() { + @Override + public void afterInjection(Object o) { + eventBusProvider.get().register(o); + } + }); + } + } + } + + private class EventBusAwareTypeListener implements TypeListener { + @Override + public <I> void hear(TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEncounter) { + + final Provider<EventBus> eventBusProvider = typeEncounter.getProvider(EventBus.class); + + if (EventBusAware.class.isAssignableFrom(typeLiteral.getRawType())) { + typeEncounter.register( new InjectionListener<I>() { + @Override + public void afterInjection(Object o) { + ((EventBusAware)o).setEventBus(eventBusProvider.get()); + } + }); + } + } + } } http://git-wip-us.apache.org/repos/asf/shiro/blob/f2dfa7ff/support/guice/src/main/java/org/apache/shiro/guice/web/ShiroWebModule.java ---------------------------------------------------------------------- diff --git a/support/guice/src/main/java/org/apache/shiro/guice/web/ShiroWebModule.java b/support/guice/src/main/java/org/apache/shiro/guice/web/ShiroWebModule.java index 45bc916..15431e5 100644 --- a/support/guice/src/main/java/org/apache/shiro/guice/web/ShiroWebModule.java +++ b/support/guice/src/main/java/org/apache/shiro/guice/web/ShiroWebModule.java @@ -18,10 +18,7 @@ */ package org.apache.shiro.guice.web; -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.*; import javax.servlet.Filter; import javax.servlet.ServletContext; @@ -31,6 +28,7 @@ import org.apache.shiro.env.Environment; import org.apache.shiro.guice.ShiroModule; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.mgt.SessionManager; +import org.apache.shiro.util.StringUtils; import org.apache.shiro.web.env.WebEnvironment; import org.apache.shiro.web.filter.PathMatchingFilter; import org.apache.shiro.web.filter.authc.AnonymousFilter; @@ -94,7 +92,7 @@ public abstract class ShiroWebModule extends ShiroModule { * We use a LinkedHashMap here to ensure that iterator order is the same as add order. This is important, as the * FilterChainResolver uses iterator order when searching for a matching chain. */ - private final Map<String, Key<? extends Filter>[]> filterChains = new LinkedHashMap<String, Key<? extends Filter>[]>(); + private final Map<String, FilterConfig<? extends Filter>[]> filterChains = new LinkedHashMap<String, FilterConfig<? extends Filter>[]>(); private final ServletContext servletContext; public ShiroWebModule(ServletContext servletContext) { @@ -134,37 +132,65 @@ public abstract class ShiroWebModule extends ShiroModule { this.configureShiroWeb(); - setupFilterChainConfigs(); - - bind(FilterChainResolver.class).toProvider(new FilterChainResolverProvider(filterChains)); + bind(FilterChainResolver.class).toProvider(new FilterChainResolverProvider(setupFilterChainConfigs())); } - private void setupFilterChainConfigs() { - Map<Key<? extends PathMatchingFilter>, Map<String, String>> configs = new HashMap<Key<? extends PathMatchingFilter>, Map<String, String>>(); + private Map<String, Key<? extends Filter>[]> setupFilterChainConfigs() { + + // loop through and build a map of Filter Key -> Map<Path, Config> + Map<Key<? extends Filter>, Map<String, String>> filterToPathToConfig = new HashMap<Key<? extends Filter>, Map<String, String>>(); + + // At the same time build a map to return with Path -> Key[] + Map<String, Key<? extends Filter>[]> resultConfigMap = new HashMap<String, Key<? extends Filter>[]>(); + + for (Map.Entry<String, FilterConfig<? extends Filter>[]> filterChain : filterChains.entrySet()) { + + String path = filterChain.getKey(); + + // collect the keys used for this path + List<Key<? extends Filter>> keysForPath = new ArrayList<Key<? extends Filter>>(); - for (Map.Entry<String, Key<? extends Filter>[]> filterChain : filterChains.entrySet()) { for (int i = 0; i < filterChain.getValue().length; i++) { - Key<? extends Filter> key = filterChain.getValue()[i]; - if (key instanceof FilterConfigKey) { - FilterConfigKey<? extends PathMatchingFilter> configKey = (FilterConfigKey<? extends PathMatchingFilter>) key; - key = configKey.getKey(); - filterChain.getValue()[i] = key; - if (!PathMatchingFilter.class.isAssignableFrom(key.getTypeLiteral().getRawType())) { - throw new ConfigurationException("Config information requires a PathMatchingFilter - can't apply to " + key.getTypeLiteral().getRawType()); - } - if (configs.get(castToPathMatching(key)) == null) configs.put(castToPathMatching(key), new HashMap<String, String>()); - configs.get(castToPathMatching(key)).put(filterChain.getKey(), configKey.getConfigValue()); - } else if (PathMatchingFilter.class.isAssignableFrom(key.getTypeLiteral().getRawType())) { - if (configs.get(castToPathMatching(key)) == null) configs.put(castToPathMatching(key), new HashMap<String, String>()); - configs.get(castToPathMatching(key)).put(filterChain.getKey(), ""); + FilterConfig<? extends Filter> filterConfig = filterChain.getValue()[i]; + + Key<? extends Filter> key = filterConfig.getKey(); + String config = filterConfig.getConfigValue(); + + // initialize key in filterToPathToConfig, if it doesn't exist + if (filterToPathToConfig.get(key) == null) { + filterToPathToConfig.put((key), new HashMap<String, String>()); + } + // now set the value + filterToPathToConfig.get(key).put(path, config); + + // Config error if someone configured a non PathMatchingFilter with a config value + if (StringUtils.hasText(config) && !PathMatchingFilter.class.isAssignableFrom(key.getTypeLiteral().getRawType())) { + throw new ConfigurationException("Config information requires a PathMatchingFilter - can't apply to " + key.getTypeLiteral().getRawType()); } + + // store the key in keysForPath + keysForPath.add(key); } + + // map the current path to all of its Keys + resultConfigMap.put(path, keysForPath.toArray(new Key[keysForPath.size()])); } - for (Key<? extends PathMatchingFilter> filterKey : configs.keySet()) { - bindPathMatchingFilter(filterKey, configs.get(filterKey)); + + // now we find only the PathMatchingFilter and configure bindings + // non PathMatchingFilter, can be loaded with the default provider via the class name + for (Key<? extends Filter> key : filterToPathToConfig.keySet()) { + if (PathMatchingFilter.class.isAssignableFrom(key.getTypeLiteral().getRawType())) { + bindPathMatchingFilter(castToPathMatching(key), filterToPathToConfig.get(key)); + } + else { + bind(key); + } } + + return resultConfigMap; } + private <T extends PathMatchingFilter> void bindPathMatchingFilter(Key<T> filterKey, Map<String, String> configs) { bind(filterKey).toProvider(new PathMatchingFilterProvider<T>(filterKey, configs)).asEagerSingleton(); } @@ -218,6 +244,126 @@ public abstract class ShiroWebModule extends ShiroModule { bind.to(WebGuiceEnvironment.class).asEagerSingleton(); } + protected final void addFilterChain(String pattern, Key<? extends Filter> key) { + // check for legacy API + if (key instanceof FilterConfigKey) { + addLegacyFilterChain(pattern, (FilterConfigKey) key); + } + else { + addFilterChain(pattern, new FilterConfig<Filter>((Key<Filter>) key, "")); + } + } + + /** + * Maps 'n' number of <code>filterConfig</code>s to a specific path pattern.<BR/> + * For example, a path of '/my_private_resource/**' to 'filterConfig(AUTHC)' would require + * any resource under the path '/my_private_resource' would be processed through the {@link FormAuthenticationFilter}. + * + * @param pattern URL patter to be mapped to a FilterConfig, e.g. '/my_private-path/**' + * @param filterConfigs FilterConfiguration representing the Filter and config to be used when processing resources on <code>pattern</code>. + * @since 1.4 + */ + protected final void addFilterChain(String pattern, FilterConfig<? extends Filter>... filterConfigs) { + filterChains.put(pattern, filterConfigs); + } + + /** + * Builds a FilterConfig from a Filer and configuration String + * @param baseKey The Key of the Filter class to be used. + * @param <T> A Servlet Filter class. + * @return A FilterConfig used to map a String path to this configuration. + * @since 1.4 + */ + protected static <T extends Filter> FilterConfig<T> filterConfig(Key<T> baseKey, String configValue) { + return new FilterConfig<T>(baseKey, configValue); + } + + /** + * Builds a FilterConfig from a Filer and configuration String + * @param baseKey The Key of the Filter class to be used. + * @param <T> A Servlet Filter class. + * @return A FilterConfig used to map a String path to this configuration. + * @since 1.4 + */ + protected static <T extends Filter> FilterConfig<T> filterConfig(Key<T> baseKey) { + return filterConfig(baseKey, ""); + } + + /** + * Builds a FilterConfig from a Filer and configuration String + * @param typeLiteral The TyleLiteral of the filter key to be used. + * @param configValue the configuration used. + * @param <T> A Servlet Filter class. + * @return A FilterConfig used to map a String path to this configuration. + * @since 1.4 + */ + @SuppressWarnings({"UnusedDeclaration"}) + protected static <T extends Filter> FilterConfig<T> filterConfig(TypeLiteral<T> typeLiteral, String configValue) { + return filterConfig(Key.get(typeLiteral), configValue); + } + + /** + * Builds a FilterConfig from a Filer and configuration String + * @param type The filter to be used. + * @param configValue the configuration used. + * @param <T> A Servlet Filter class. + * @return A FilterConfig used to map a String path to this configuration. + * @since 1.4 + */ + @SuppressWarnings({"UnusedDeclaration"}) + protected static <T extends Filter> FilterConfig<T> filterConfig(Class<T> type, String configValue) { + return filterConfig(Key.get(type), configValue); + } + + + /** + * Filter configuration which pairs a Filter class with its configuration used on a path. + * @param <T> The Servlet Filter class. + * @since 1.4 + */ + public static class FilterConfig<T extends Filter> { + private Key<T> key; + private String configValue; + + private FilterConfig(Key<T> key, String configValue) { + super(); + this.key = key; + this.configValue = configValue; + } + + public Key<T> getKey() { + return key; + } + + public String getConfigValue() { + return configValue; + } + } + + + + + + + + // legacy methods + + + static boolean isGuiceVersion3() { + try { + Class.forName("com.google.inject.multibindings.MapKey"); + return false; + } catch (ClassNotFoundException e) { + return true; + } + } + + private void addLegacyFilterChain(String pattern, FilterConfigKey filterConfigKey) { + + FilterConfig<Filter> filterConfig = new FilterConfig<Filter>(filterConfigKey.getKey(), filterConfigKey.getConfigValue()); + addFilterChain(pattern, filterConfig); + } + /** * Adds a filter chain to the shiro configuration. * <p/> @@ -228,24 +374,52 @@ public abstract class ShiroWebModule extends ShiroModule { * @param keys */ @SuppressWarnings({"UnusedDeclaration"}) + @Deprecated protected final void addFilterChain(String pattern, Key<? extends Filter>... keys) { - filterChains.put(pattern, keys); + + // We need to extract the keys and FilterConfigKey and convert to the new format. + + FilterConfig[] filterConfigs = new FilterConfig[keys.length]; + for (int ii = 0; ii < keys.length; ii++) { + Key<? extends Filter> key = keys[ii]; + // If this is a path matching filter, we need to remember the config + if (key instanceof FilterConfigKey) { + // legacy config + FilterConfigKey legacyKey = (FilterConfigKey) key; + filterConfigs[ii] = new FilterConfig(legacyKey.getKey(), legacyKey.getConfigValue()); + } + else { + // Some other type of Filter key, no config + filterConfigs[ii] = new FilterConfig(key, ""); + } + } + + filterChains.put(pattern, filterConfigs); } + @Deprecated protected static <T extends PathMatchingFilter> Key<T> config(Key<T> baseKey, String configValue) { + + if( !isGuiceVersion3()) { + throw new ConfigurationException("Method ShiroWebModule.config(Key<? extends PathMatchingFilter>, String configValue), is not supported when using Guice 4+"); + } + return new FilterConfigKey<T>(baseKey, configValue); } @SuppressWarnings({"UnusedDeclaration"}) + @Deprecated protected static <T extends PathMatchingFilter> Key<T> config(TypeLiteral<T> typeLiteral, String configValue) { return config(Key.get(typeLiteral), configValue); } @SuppressWarnings({"UnusedDeclaration"}) + @Deprecated protected static <T extends PathMatchingFilter> Key<T> config(Class<T> type, String configValue) { return config(Key.get(type), configValue); } + @Deprecated private static class FilterConfigKey<T extends PathMatchingFilter> extends Key<T> { private Key<T> key; private String configValue; @@ -264,4 +438,5 @@ public abstract class ShiroWebModule extends ShiroModule { return configValue; } } + } http://git-wip-us.apache.org/repos/asf/shiro/blob/f2dfa7ff/support/guice/src/main/java/org/apache/shiro/guice/web/SimpleFilterChain.java ---------------------------------------------------------------------- diff --git a/support/guice/src/main/java/org/apache/shiro/guice/web/SimpleFilterChain.java b/support/guice/src/main/java/org/apache/shiro/guice/web/SimpleFilterChain.java index 4ff8b56..eee6230 100644 --- a/support/guice/src/main/java/org/apache/shiro/guice/web/SimpleFilterChain.java +++ b/support/guice/src/main/java/org/apache/shiro/guice/web/SimpleFilterChain.java @@ -44,4 +44,13 @@ class SimpleFilterChain implements FilterChain { originalChain.doFilter(request, response); } } + + /** + * Exposed for testing, not part of public API. + * @return an Iterater of filters. + */ + Iterator<? extends Filter> getFilters() { + return chain; + } + } http://git-wip-us.apache.org/repos/asf/shiro/blob/f2dfa7ff/support/guice/src/test/java/org/apache/shiro/guice/ShiroModuleTest.java ---------------------------------------------------------------------- diff --git a/support/guice/src/test/java/org/apache/shiro/guice/ShiroModuleTest.java b/support/guice/src/test/java/org/apache/shiro/guice/ShiroModuleTest.java index 4507076..526f4f7 100644 --- a/support/guice/src/test/java/org/apache/shiro/guice/ShiroModuleTest.java +++ b/support/guice/src/test/java/org/apache/shiro/guice/ShiroModuleTest.java @@ -28,6 +28,10 @@ import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.env.Environment; +import org.apache.shiro.event.EventBus; +import org.apache.shiro.event.EventBusAware; +import org.apache.shiro.event.Subscribe; +import org.apache.shiro.event.support.DefaultEventBus; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.realm.Realm; @@ -37,11 +41,13 @@ import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Destroyable; import org.junit.Test; +import java.lang.reflect.Field; import java.util.Collection; +import java.util.Map; import static org.easymock.EasyMock.*; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; public class ShiroModuleTest { @@ -204,6 +210,82 @@ public class ShiroModuleTest { verify(myDestroyable); } + /** + * @since 1.4 + * @throws Exception + */ + @Test + public void testEventListener() throws Exception { + + final MockRealm mockRealm = createMock(MockRealm.class); + final EventBus eventBus = createMock(EventBus.class); + + // expect both objects to be registered + eventBus.register(anyObject(MockEventListener1.class)); + eventBus.register(anyObject(MockEventListener2.class)); + replay(eventBus); + + final ShiroModule shiroModule = new ShiroModule() { + @Override + protected void configureShiro() { + bindRealm().to(MockRealm.class); + + // bind our event listeners + binder().bind(MockEventListener1.class).asEagerSingleton(); + binder().bind(MockEventListener2.class).asEagerSingleton(); + } + + @Override + protected void bindEventBus(AnnotatedBindingBuilder<EventBus> bind) { + bind.toInstance(eventBus); + } + + @Provides + public MockRealm createRealm() { + return mockRealm; + } + + }; + Guice.createInjector(shiroModule); + + verify(eventBus); + + } + + /** + * @since 1.4 + * @throws Exception + */ + @Test + public void testEventBusAware() throws Exception { + + final MockRealm mockRealm = createMock(MockRealm.class); + + final ShiroModule shiroModule = new ShiroModule() { + @Override + protected void configureShiro() { + bindRealm().to(MockRealm.class); + + binder().bind(MockEventBusAware.class).asEagerSingleton(); + expose(MockEventBusAware.class); + } + + @Provides + public MockRealm createRealm() { + return mockRealm; + } + + }; + Injector injector = Guice.createInjector(shiroModule); + EventBus eventBus = injector.getInstance(EventBus.class); + SecurityManager securityManager = injector.getInstance(SecurityManager.class); + + MockEventBusAware eventBusAware = injector.getInstance(MockEventBusAware.class); + + assertSame(eventBus, eventBusAware.eventBus); + assertSame(eventBus, ((DefaultSecurityManager)securityManager).getEventBus()); + } + public static interface MockRealm extends Realm { } @@ -227,4 +309,27 @@ public class ShiroModuleTest { public static interface MyDestroyable extends Destroyable { } + + public static class MockEventListener1 { + @Subscribe + public void listenToAllAndDoNothing(Object o) {} + } + + public static class MockEventListener2 { + @Subscribe + public void listenToAllAndDoNothing(Object o) {} + } + + public static class MockEventBusAware implements EventBusAware { + private EventBus eventBus; + + public EventBus getEventBus() { + return eventBus; + } + + @Override + public void setEventBus(EventBus eventBus) { + this.eventBus = eventBus; + } + } } http://git-wip-us.apache.org/repos/asf/shiro/blob/f2dfa7ff/support/guice/src/test/java/org/apache/shiro/guice/web/FilterConfigTest.java ---------------------------------------------------------------------- diff --git a/support/guice/src/test/java/org/apache/shiro/guice/web/FilterConfigTest.java b/support/guice/src/test/java/org/apache/shiro/guice/web/FilterConfigTest.java index 98add89..c1b3c32 100644 --- a/support/guice/src/test/java/org/apache/shiro/guice/web/FilterConfigTest.java +++ b/support/guice/src/test/java/org/apache/shiro/guice/web/FilterConfigTest.java @@ -45,7 +45,8 @@ public class FilterConfigTest { bindRealm().to(ShiroModuleTest.MockRealm.class); addFilterChain("/index.html", AUTHC_BASIC); - addFilterChain("/index2.html", config(PERMS, "permission")); +// addFilterChain("/index2.html", config(PERMS, "permission")); + addFilterChain("/index2.html", filterConfig(PERMS, "permission")); } @Provides http://git-wip-us.apache.org/repos/asf/shiro/blob/f2dfa7ff/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java ---------------------------------------------------------------------- diff --git a/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java b/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java index a3a3f76..ff50d0f 100644 --- a/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java +++ b/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java @@ -21,6 +21,7 @@ package org.apache.shiro.guice.web; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; +import com.google.inject.Key; import com.google.inject.Provides; import com.google.inject.binder.AnnotatedBindingBuilder; import org.apache.shiro.guice.ShiroModuleTest; @@ -28,21 +29,38 @@ import org.apache.shiro.env.Environment; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.realm.Realm; import org.apache.shiro.session.mgt.SessionManager; +import org.apache.shiro.web.env.EnvironmentLoader; import org.apache.shiro.web.env.WebEnvironment; +import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter; +import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; +import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter; +import org.apache.shiro.web.filter.authz.RolesAuthorizationFilter; import org.apache.shiro.web.filter.mgt.FilterChainResolver; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.mgt.WebSecurityManager; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.apache.shiro.web.session.mgt.ServletContainerSessionManager; +import org.easymock.EasyMock; +import org.junit.Assume; import org.junit.Test; import javax.inject.Named; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; import java.util.Collection; +import java.util.Iterator; + +import static org.easymock.EasyMock.*; +import static org.junit.Assert.*; +import static org.hamcrest.Matchers.*; -import static org.easymock.EasyMock.createMock; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; public class ShiroWebModuleTest { @@ -146,6 +164,162 @@ public class ShiroWebModuleTest { assertTrue( environment == webEnvironment ); } + /** + * @since 1.4 + */ + @Test + public void testAddFilterChainGuice3and4() { + + final ShiroModuleTest.MockRealm mockRealm = createMock(ShiroModuleTest.MockRealm.class); + ServletContext servletContext = createMock(ServletContext.class); + HttpServletRequest request = createMock(HttpServletRequest.class); + + servletContext.setAttribute(eq(EnvironmentLoader.ENVIRONMENT_ATTRIBUTE_KEY), EasyMock.anyObject()); + expect(request.getAttribute("javax.servlet.include.context_path")).andReturn("").anyTimes(); + expect(request.getCharacterEncoding()).andReturn("UTF-8").anyTimes(); + expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_authc"); + expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_custom_filter"); + expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_authc_basic"); + expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_perms"); + expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/multiple_configs"); + replay(servletContext, request); + + Injector injector = Guice.createInjector(new ShiroWebModule(servletContext) { + @Override + protected void configureShiroWeb() { + bindRealm().to(ShiroModuleTest.MockRealm.class); + expose(FilterChainResolver.class); + this.addFilterChain("/test_authc/**", filterConfig(AUTHC)); + this.addFilterChain("/test_custom_filter/**", Key.get(CustomFilter.class)); + this.addFilterChain("/test_authc_basic/**", AUTHC_BASIC); + this.addFilterChain("/test_perms/**", filterConfig(PERMS, "remote:invoke:lan,wan")); + this.addFilterChain("/multiple_configs/**", filterConfig(AUTHC), filterConfig(ROLES, "b2bClient"), filterConfig(PERMS, "remote:invoke:lan,wan")); + } + + @Provides + public ShiroModuleTest.MockRealm createRealm() { + return mockRealm; + } + }); + + FilterChainResolver resolver = injector.getInstance(FilterChainResolver.class); + assertThat(resolver, instanceOf(SimpleFilterChainResolver.class)); + SimpleFilterChainResolver simpleFilterChainResolver = (SimpleFilterChainResolver) resolver; + + // test the /test_authc resource + FilterChain filterChain = simpleFilterChainResolver.getChain(request, null, null); + assertThat(filterChain, instanceOf(SimpleFilterChain.class)); + Filter nextFilter = getNextFilter((SimpleFilterChain) filterChain); + assertThat(nextFilter, instanceOf(FormAuthenticationFilter.class)); + + // test the /test_custom_filter resource + filterChain = simpleFilterChainResolver.getChain(request, null, null); + assertThat(filterChain, instanceOf(SimpleFilterChain.class)); + nextFilter = getNextFilter((SimpleFilterChain) filterChain); + assertThat(nextFilter, instanceOf(CustomFilter.class)); + + // test the /test_authc_basic resource + filterChain = simpleFilterChainResolver.getChain(request, null, null); + assertThat(filterChain, instanceOf(SimpleFilterChain.class)); + nextFilter = getNextFilter((SimpleFilterChain) filterChain); + assertThat(nextFilter, instanceOf(BasicHttpAuthenticationFilter.class)); + + // test the /test_perms resource + filterChain = simpleFilterChainResolver.getChain(request, null, null); + assertThat(filterChain, instanceOf(SimpleFilterChain.class)); + nextFilter = getNextFilter((SimpleFilterChain) filterChain); + assertThat(nextFilter, instanceOf(PermissionsAuthorizationFilter.class)); + + // test the /multiple_configs resource + filterChain = simpleFilterChainResolver.getChain(request, null, null); + assertThat(filterChain, instanceOf(SimpleFilterChain.class)); + assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(FormAuthenticationFilter.class)); + assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(RolesAuthorizationFilter.class)); + assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(PermissionsAuthorizationFilter.class)); + + verify(servletContext, request); + } + + /** + * @since 1.4 + */ + @Test + public void testAddFilterChainGuice3Only() { + + Assume.assumeTrue("This test only runs agains Guice 3.x", ShiroWebModule.isGuiceVersion3()); + + final ShiroModuleTest.MockRealm mockRealm = createMock(ShiroModuleTest.MockRealm.class); + ServletContext servletContext = createMock(ServletContext.class); + HttpServletRequest request = createMock(HttpServletRequest.class); + + servletContext.setAttribute(eq(EnvironmentLoader.ENVIRONMENT_ATTRIBUTE_KEY), EasyMock.anyObject()); + expect(request.getAttribute("javax.servlet.include.context_path")).andReturn("").anyTimes(); + expect(request.getCharacterEncoding()).andReturn("UTF-8").anyTimes(); + expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_authc"); + expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_custom_filter"); + expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_perms"); + expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/multiple_configs"); + replay(servletContext, request); + + Injector injector = Guice.createInjector(new ShiroWebModule(servletContext) { + @Override + protected void configureShiroWeb() { + bindRealm().to(ShiroModuleTest.MockRealm.class); + expose(FilterChainResolver.class); + this.addFilterChain("/test_authc/**", AUTHC); + this.addFilterChain("/test_custom_filter/**", Key.get(CustomFilter.class)); + this.addFilterChain("/test_perms/**", config(PERMS, "remote:invoke:lan,wan")); + this.addFilterChain("/multiple_configs/**", AUTHC, config(ROLES, "b2bClient"), config(PERMS, "remote:invoke:lan,wan")); + } + + @Provides + public ShiroModuleTest.MockRealm createRealm() { + return mockRealm; + } + }); + + FilterChainResolver resolver = injector.getInstance(FilterChainResolver.class); + assertThat(resolver, instanceOf(SimpleFilterChainResolver.class)); + SimpleFilterChainResolver simpleFilterChainResolver = (SimpleFilterChainResolver) resolver; + + // test the /test_authc resource + FilterChain filterChain = simpleFilterChainResolver.getChain(request, null, null); + assertThat(filterChain, instanceOf(SimpleFilterChain.class)); + Filter nextFilter = getNextFilter((SimpleFilterChain) filterChain); + assertThat(nextFilter, instanceOf(FormAuthenticationFilter.class)); + + // test the /test_custom_filter resource + filterChain = simpleFilterChainResolver.getChain(request, null, null); + assertThat(filterChain, instanceOf(SimpleFilterChain.class)); + nextFilter = getNextFilter((SimpleFilterChain) filterChain); + assertThat(nextFilter, instanceOf(CustomFilter.class)); + + // test the /test_perms resource + filterChain = simpleFilterChainResolver.getChain(request, null, null); + assertThat(filterChain, instanceOf(SimpleFilterChain.class)); + nextFilter = getNextFilter((SimpleFilterChain) filterChain); + assertThat(nextFilter, instanceOf(PermissionsAuthorizationFilter.class)); + + // test the /multiple_configs resource + filterChain = simpleFilterChainResolver.getChain(request, null, null); + assertThat(filterChain, instanceOf(SimpleFilterChain.class)); + assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(FormAuthenticationFilter.class)); + assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(RolesAuthorizationFilter.class)); + assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(PermissionsAuthorizationFilter.class)); + + verify(servletContext, request); + } + + private Filter getNextFilter(SimpleFilterChain filterChain) { + + Iterator<? extends Filter> filters = filterChain.getFilters(); + if (filters.hasNext()) { + return filters.next(); + } + + return null; + } + public static class MyDefaultWebSecurityManager extends DefaultWebSecurityManager { @Inject public MyDefaultWebSecurityManager(Collection<Realm> realms) { @@ -162,4 +336,16 @@ public class ShiroWebModuleTest { super(filterChainResolver, servletContext, securityManager); } } + + public static class CustomFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) throws ServletException {} + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {} + + @Override + public void destroy() {} + } } http://git-wip-us.apache.org/repos/asf/shiro/blob/f2dfa7ff/support/guice/src/test/resources/log4j.properties ---------------------------------------------------------------------- diff --git a/support/guice/src/test/resources/log4j.properties b/support/guice/src/test/resources/log4j.properties new file mode 100644 index 0000000..bbb023d --- /dev/null +++ b/support/guice/src/test/resources/log4j.properties @@ -0,0 +1,34 @@ +# +# 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. +# + +log4j.rootLogger=TRACE, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n + +# Pattern to output: date priority [category] - message +log4j.appender.logfile.layout=org.apache.log4j.PatternLayout +log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n + +# General Apache libraries is WARN +log4j.logger.org.apache=WARN + +log4j.logger.org.apache.shiro=TRACE +log4j.logger.org.apache.shiro.util.ThreadContext=WARN \ No newline at end of file