Merge pull request #239 from fpapon/SHIRO-780
[SHIRO-780] NOTICE files of shiro components don't match NOTICE in so…
diff --git a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/ReflectionBuilder.java b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/ReflectionBuilder.java
index 77a1db6..8ff214d 100644
--- a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/ReflectionBuilder.java
+++ b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/ReflectionBuilder.java
@@ -287,10 +287,15 @@
}
processor.execute();
- }
- //SHIRO-413: init method must be called for constructed objects that are Initializable
- LifecycleUtils.init(objects.values());
+ //SHIRO-778: onInit method on AuthenticatingRealm is called twice
+ objects.keySet().stream()
+ .filter(key -> !kvPairs.containsKey(key))
+ .forEach(key -> LifecycleUtils.init(objects.get(key)));
+ } else {
+ //SHIRO-413: init method must be called for constructed objects that are Initializable
+ LifecycleUtils.init(objects.values());
+ }
return objects;
}
diff --git a/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/ReflectionBuilderTest.groovy b/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/ReflectionBuilderTest.groovy
index 5fd0950..8bc4a4f 100644
--- a/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/ReflectionBuilderTest.groovy
+++ b/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/ReflectionBuilderTest.groovy
@@ -18,6 +18,7 @@
*/
package org.apache.shiro.config.ogdl
+import org.apache.shiro.config.ogdl.beans.InitCountBean
import org.apache.shiro.lang.codec.Base64
import org.apache.shiro.lang.codec.CodecSupport
import org.apache.shiro.lang.codec.Hex
@@ -26,6 +27,8 @@
import org.apache.shiro.config.ogdl.event.BeanEvent
import org.junit.Test
+import java.util.concurrent.ConcurrentHashMap
+
import static org.junit.Assert.*
import static org.hamcrest.Matchers.*
@@ -640,6 +643,37 @@
assertNotNull(beanMap.get("two"))
}
+ @Test
+ void testNotMultipleInitialization() {
+ // given
+ Map<String, String> defs = new ConcurrentHashMap<>()
+ defs.put("initcountbean", InitCountBean.getCanonicalName())
+ ReflectionBuilder builder = new ReflectionBuilder()
+
+ // when
+ builder.buildObjects(defs)
+
+ // then
+ assertEquals(1, InitCountBean.getInitCount())
+ InitCountBean.resetCount()
+ }
+
+ @Test
+ void testNotMultipleInitializationWithNullFirst() {
+ // given
+ Map<String, String> defs = new ConcurrentHashMap<>()
+ defs.put("initcountbean", InitCountBean.getCanonicalName())
+ ReflectionBuilder builder = new ReflectionBuilder()
+
+ // when
+ builder.buildObjects(null)
+ builder.buildObjects(defs)
+
+ // then
+ assertEquals(1, InitCountBean.getInitCount())
+ InitCountBean.resetCount()
+ }
+
void assertInstantiatedEvents(String name, Map<String, ?> objects, int expected) {
def bean = objects.get(name) as RecordingBeanListener
def events = bean.getInstantiatedEvents()
diff --git a/config/ogdl/src/test/java/org/apache/shiro/config/ogdl/beans/InitCountBean.java b/config/ogdl/src/test/java/org/apache/shiro/config/ogdl/beans/InitCountBean.java
new file mode 100644
index 0000000..1dccf0a
--- /dev/null
+++ b/config/ogdl/src/test/java/org/apache/shiro/config/ogdl/beans/InitCountBean.java
@@ -0,0 +1,53 @@
+/*
+ * 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.shiro.config.ogdl.beans;
+
+import org.apache.shiro.lang.ShiroException;
+import org.apache.shiro.lang.util.Initializable;
+
+import java.util.StringJoiner;
+import java.util.concurrent.atomic.LongAdder;
+
+public class InitCountBean implements Initializable {
+ private static final LongAdder INIT_COUNT = new LongAdder();
+
+ public InitCountBean() {
+ super();
+ }
+
+ public static long getInitCount() {
+ return INIT_COUNT.longValue();
+ }
+
+ public static void resetCount() {
+ INIT_COUNT.reset();
+ }
+
+ @Override
+ public String toString() {
+ return new StringJoiner(", ", InitCountBean.class.getSimpleName() + "[", "]")
+ .add("INIT_COUNT=" + getInitCount())
+ .toString();
+ }
+
+ @Override
+ public void init() throws ShiroException {
+ INIT_COUNT.increment();
+ }
+}
diff --git a/core/src/main/java/org/apache/shiro/env/DefaultEnvironment.java b/core/src/main/java/org/apache/shiro/env/DefaultEnvironment.java
index 43523a0..160e58f 100644
--- a/core/src/main/java/org/apache/shiro/env/DefaultEnvironment.java
+++ b/core/src/main/java/org/apache/shiro/env/DefaultEnvironment.java
@@ -146,7 +146,7 @@
return null;
}
if (!requiredType.isInstance(o)) {
- String msg = "Object named '" + name + "' is not of required type [" + requiredType.getName() + "].";
+ String msg = "Object named '" + name + "' (of type [" + o.getClass().getName() + "]) is not of required type [" + requiredType.getName() + "].";
throw new RequiredTypeException(msg);
}
return (T)o;
diff --git a/core/src/main/java/org/apache/shiro/session/mgt/AbstractValidatingSessionManager.java b/core/src/main/java/org/apache/shiro/session/mgt/AbstractValidatingSessionManager.java
index e6a1bb3..8991bba 100644
--- a/core/src/main/java/org/apache/shiro/session/mgt/AbstractValidatingSessionManager.java
+++ b/core/src/main/java/org/apache/shiro/session/mgt/AbstractValidatingSessionManager.java
@@ -213,7 +213,7 @@
log.debug("No sessionValidationScheduler set. Attempting to create default instance.");
}
scheduler = new ExecutorServiceSessionValidationScheduler(this);
- scheduler.setInterval(getSessionValidationInterval());
+ scheduler.setSessionValidationInterval(getSessionValidationInterval());
if (log.isTraceEnabled()) {
log.trace("Created default SessionValidationScheduler instance of type [" + scheduler.getClass().getName() + "].");
}
diff --git a/core/src/main/java/org/apache/shiro/session/mgt/ExecutorServiceSessionValidationScheduler.java b/core/src/main/java/org/apache/shiro/session/mgt/ExecutorServiceSessionValidationScheduler.java
index 657a377..fc10de7 100644
--- a/core/src/main/java/org/apache/shiro/session/mgt/ExecutorServiceSessionValidationScheduler.java
+++ b/core/src/main/java/org/apache/shiro/session/mgt/ExecutorServiceSessionValidationScheduler.java
@@ -31,7 +31,7 @@
/**
* SessionValidationScheduler implementation that uses a
* {@link ScheduledExecutorService} to call {@link ValidatingSessionManager#validateSessions()} every
- * <em>{@link #getInterval interval}</em> milliseconds.
+ * <em>{@link #getSessionValidationInterval sessionValidationInterval}</em> milliseconds.
*
* @since 0.9
*/
@@ -44,7 +44,7 @@
ValidatingSessionManager sessionManager;
private ScheduledExecutorService service;
- private long interval = DefaultSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL;
+ private long sessionValidationInterval = DefaultSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL;
private boolean enabled = false;
private String threadNamePrefix = "SessionValidationThread-";
@@ -64,12 +64,12 @@
this.sessionManager = sessionManager;
}
- public long getInterval() {
- return interval;
+ public long getSessionValidationInterval() {
+ return sessionValidationInterval;
}
- public void setInterval(long interval) {
- this.interval = interval;
+ public void setSessionValidationInterval(long sessionValidationInterval) {
+ this.sessionValidationInterval = sessionValidationInterval;
}
public boolean isEnabled() {
@@ -91,7 +91,7 @@
//TODO Implement an integration test to test for jvm exit as part of the standalone example
// (so we don't have to change the unit test execution model for the core module)
public void enableSessionValidation() {
- if (this.interval > 0l) {
+ if (this.sessionValidationInterval > 0l) {
this.service = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
private final AtomicInteger count = new AtomicInteger(1);
@@ -102,7 +102,8 @@
return thread;
}
});
- this.service.scheduleAtFixedRate(this, interval, interval, TimeUnit.MILLISECONDS);
+ this.service.scheduleAtFixedRate(this, sessionValidationInterval,
+ sessionValidationInterval, TimeUnit.MILLISECONDS);
}
this.enabled = true;
}
diff --git a/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java b/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java
index 5581b7a..d3040d1 100644
--- a/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java
+++ b/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java
@@ -41,6 +41,7 @@
import java.util.Collection;
import java.util.List;
+import java.util.StringJoiner;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -515,4 +516,16 @@
return popped;
}
+
+ @Override
+ public String toString() {
+ return new StringJoiner(", ", "DelegatingSubject{", "}")
+ .add("principals=" + principals)
+ .add("authenticated=" + authenticated)
+ .add("host='******")
+ .add("session='******'")
+ .add("sessionCreationEnabled=" + sessionCreationEnabled)
+ .add("securityManager=" + securityManager)
+ .toString();
+ }
}
diff --git a/core/src/test/java/org/apache/shiro/session/mgt/ExecutorServiceSessionValidationSchedulerTest.java b/core/src/test/java/org/apache/shiro/session/mgt/ExecutorServiceSessionValidationSchedulerTest.java
index bb5ba64..8ae53a6 100644
--- a/core/src/test/java/org/apache/shiro/session/mgt/ExecutorServiceSessionValidationSchedulerTest.java
+++ b/core/src/test/java/org/apache/shiro/session/mgt/ExecutorServiceSessionValidationSchedulerTest.java
@@ -36,7 +36,7 @@
executorServiceSessionValidationScheduler = new ExecutorServiceSessionValidationScheduler();
executorServiceSessionValidationScheduler.setSessionManager(defaultSessionManager);
executorServiceSessionValidationScheduler.setThreadNamePrefix("test-");
- executorServiceSessionValidationScheduler.setInterval(1000L);
+ executorServiceSessionValidationScheduler.setSessionValidationInterval(1000L);
executorServiceSessionValidationScheduler.enableSessionValidation();
}
@@ -81,7 +81,7 @@
executorServiceSessionValidationScheduler = new ExecutorServiceSessionValidationScheduler();
executorServiceSessionValidationScheduler.setSessionManager(defaultSessionManager);
executorServiceSessionValidationScheduler.setThreadNamePrefix("test-");
- executorServiceSessionValidationScheduler.setInterval(1000L);
+ executorServiceSessionValidationScheduler.setSessionValidationInterval(1000L);
executorServiceSessionValidationScheduler.enableSessionValidation();
defaultSessionManager.create(session);
Thread.sleep(2000L);
@@ -101,4 +101,4 @@
throw new RuntimeException("Session test exception");
}
}
-}
\ No newline at end of file
+}
diff --git a/core/src/test/java/org/apache/shiro/subject/DelegatingSubjectTest.java b/core/src/test/java/org/apache/shiro/subject/DelegatingSubjectTest.java
index ee04e6d..2bc0bbe 100644
--- a/core/src/test/java/org/apache/shiro/subject/DelegatingSubjectTest.java
+++ b/core/src/test/java/org/apache/shiro/subject/DelegatingSubjectTest.java
@@ -216,4 +216,25 @@
LifecycleUtils.destroy(sm);
}
+
+ @Test
+ public void testToString() {
+ // given
+ String username = "jsmith";
+
+ DefaultSecurityManager securityManager = new DefaultSecurityManager();
+ PrincipalCollection identity = new SimplePrincipalCollection(username, "testRealm");
+ final String hostname = "localhost";
+ final DelegatingSubject sourceSubject = new DelegatingSubject(identity, true, hostname, null, securityManager);
+
+ // when
+ final String subjectToString = sourceSubject.toString();
+
+ // then
+ final Session session = sourceSubject.getSession(true);
+ String sesionId = (String) session.getId();
+ assertFalse("toString must not leak sessionId", subjectToString.contains(sesionId));
+ assertFalse("toString must not leak host", subjectToString.contains(hostname));
+ }
+
}
diff --git a/pom.xml b/pom.xml
index 086f931..d8f3311 100644
--- a/pom.xml
+++ b/pom.xml
@@ -102,8 +102,8 @@
<slf4j.version>1.7.26</slf4j.version>
<logback.version>1.2.3</logback.version>
<log4j.version>1.2.17</log4j.version>
- <spring.version>5.2.5.RELEASE</spring.version>
- <spring-boot.version>2.2.6.RELEASE</spring-boot.version>
+ <spring.version>5.2.8.RELEASE</spring.version>
+ <spring-boot.version>2.3.2.RELEASE</spring-boot.version>
<guice.version>4.2.2</guice.version>
<jaxrs.api.version>2.1.6</jaxrs.api.version>
<htmlunit.version>2.39.0</htmlunit.version>
@@ -264,7 +264,7 @@
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
- <version>4.2.0</version>
+ <version>5.1.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
diff --git a/support/guice/pom.xml b/support/guice/pom.xml
index 66ade20..7b2c50e 100644
--- a/support/guice/pom.xml
+++ b/support/guice/pom.xml
@@ -46,6 +46,10 @@
<optional>true</optional>
</dependency>
<dependency>
+ <groupId>javax.annotation</groupId>
+ <artifactId>javax.annotation-api</artifactId>
+ </dependency>
+ <dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
</dependency>
@@ -54,10 +58,6 @@
<artifactId>guice-multibindings</artifactId>
</dependency>
<dependency>
- <groupId>javax.annotation</groupId>
- <artifactId>javax.annotation-api</artifactId>
- </dependency>
- <dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-servlet</artifactId>
<optional>true</optional>
diff --git a/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebFilterConfiguration.java b/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebFilterConfiguration.java
index e15d50d..e2ff32f 100644
--- a/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebFilterConfiguration.java
+++ b/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebFilterConfiguration.java
@@ -38,7 +38,7 @@
@Autowired
protected ShiroFilterChainDefinition shiroFilterChainDefinition;
- @Autowired
+ @Autowired(required = false)
protected Map<String, Filter> filterMap;
@Value("#{ @environment['shiro.loginUrl'] ?: '/login.jsp' }")
@@ -59,7 +59,10 @@
filterFactoryBean.setSecurityManager(securityManager);
filterFactoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinition.getFilterChainMap());
- filterFactoryBean.setFilters(filterMap);
+
+ if (filterMap != null) {
+ filterFactoryBean.setFilters(filterMap);
+ }
return filterFactoryBean;
}
diff --git a/support/spring/src/test/groovy/org/apache/shiro/spring/config/ShiroWebConfigurationTest.groovy b/support/spring/src/test/groovy/org/apache/shiro/spring/config/ShiroWebConfigurationTest.groovy
new file mode 100644
index 0000000..5105293
--- /dev/null
+++ b/support/spring/src/test/groovy/org/apache/shiro/spring/config/ShiroWebConfigurationTest.groovy
@@ -0,0 +1,82 @@
+/*
+ * 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.shiro.spring.config
+
+import org.apache.shiro.authc.UsernamePasswordToken
+import org.apache.shiro.authz.ModularRealmAuthorizer
+import org.apache.shiro.event.EventBus
+import org.apache.shiro.mgt.DefaultSecurityManager
+import org.apache.shiro.mgt.SecurityManager
+import org.apache.shiro.realm.text.TextConfigurationRealm
+import org.apache.shiro.spring.testconfig.RealmTestConfiguration
+import org.apache.shiro.spring.web.ShiroFilterFactoryBean
+import org.apache.shiro.spring.web.config.ShiroWebConfiguration
+import org.apache.shiro.spring.web.config.ShiroWebFilterConfiguration
+import org.apache.shiro.subject.Subject
+import org.junit.Test
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.test.context.ContextConfiguration
+import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests
+
+import static org.hamcrest.Matchers.*
+import static org.junit.Assert.*
+
+/**
+ * @since 1.4.0
+ */
+@ContextConfiguration(classes = [RealmTestConfiguration, ShiroConfiguration, ShiroWebConfiguration, ShiroWebFilterConfiguration])
+public class ShiroWebConfigurationTest extends AbstractJUnit4SpringContextTests {
+
+ @Autowired
+ private SecurityManager securityManager
+
+ @Autowired
+ private EventBus eventBus;
+
+ @Autowired
+ private ShiroFilterFactoryBean shiroFilterFactoryBean
+
+ @Test
+ public void testMinimalConfiguration() {
+
+ // first do a quick check of the injected objects
+ assertNotNull securityManager
+ assertThat securityManager.realms, allOf(hasSize(1), hasItem(instanceOf(TextConfigurationRealm)))
+ assertNull securityManager.cacheManager
+
+ assertNotNull shiroFilterFactoryBean
+ assertThat shiroFilterFactoryBean.filters, anEmptyMap()
+
+ assertSame(((DefaultSecurityManager)securityManager).getEventBus(), eventBus)
+
+ def defaultSecurityManager = (DefaultSecurityManager) securityManager
+ def authorizor = (ModularRealmAuthorizer) defaultSecurityManager.getAuthorizer();
+ assertNull authorizor.rolePermissionResolver
+ assertNull authorizor.permissionResolver
+
+ // now lets do a couple quick permission tests to make sure everything has been initialized correctly.
+ Subject joeCoder = new Subject.Builder(securityManager).buildSubject()
+ joeCoder.login(new UsernamePasswordToken("joe.coder", "password"))
+ joeCoder.checkPermission("read")
+ assertTrue joeCoder.hasRole("user")
+ assertFalse joeCoder.hasRole("admin")
+ joeCoder.logout()
+ }
+
+}
diff --git a/web/src/main/java/org/apache/shiro/web/env/IniWebEnvironment.java b/web/src/main/java/org/apache/shiro/web/env/IniWebEnvironment.java
index 9d6219e..81e9f11 100644
--- a/web/src/main/java/org/apache/shiro/web/env/IniWebEnvironment.java
+++ b/web/src/main/java/org/apache/shiro/web/env/IniWebEnvironment.java
@@ -263,20 +263,13 @@
Ini ini = getIni();
if (!CollectionUtils.isEmpty(ini)) {
- //only create a resolver if the 'filters' or 'urls' sections are defined:
- Ini.Section urls = ini.getSection(IniFilterChainResolverFactory.URLS);
- Ini.Section filters = ini.getSection(IniFilterChainResolverFactory.FILTERS);
- if (!CollectionUtils.isEmpty(urls) || !CollectionUtils.isEmpty(filters)) {
- //either the urls section or the filters section was defined. Go ahead and create the resolver:
-
- Factory<FilterChainResolver> factory = (Factory<FilterChainResolver>) this.objects.get(FILTER_CHAIN_RESOLVER_NAME);
- if (factory instanceof IniFactorySupport) {
- IniFactorySupport iniFactory = (IniFactorySupport) factory;
- iniFactory.setIni(ini);
- iniFactory.setDefaults(this.objects);
- }
- resolver = factory.getInstance();
+ Factory<FilterChainResolver> factory = (Factory<FilterChainResolver>) this.objects.get(FILTER_CHAIN_RESOLVER_NAME);
+ if (factory instanceof IniFactorySupport) {
+ IniFactorySupport iniFactory = (IniFactorySupport) factory;
+ iniFactory.setIni(ini);
+ iniFactory.setDefaults(this.objects);
}
+ resolver = factory.getInstance();
}
return resolver;