Infinispan cache configuration is not always applied to
security-domain
-----------------------------------------------------------------------
Key: WFLY-3858
URL:
https://issues.jboss.org/browse/WFLY-3858
Project: WildFly
Issue Type: Bug
Components: Security
Affects Versions: 8.1.0.Final
Reporter: Robert Tuck
Assignee: Darran Lofthouse
Attachments: debugger output.txt
On Wildfly 8.1.0.Final, I have the following standalone-ha.xml:
<subsystem xmlns="urn:jboss:domain:infinispan:2.0">
...
<cache-container name="security"
default-cache="auth-cache" start="EAGER">
<transport cluster="${cluster.name}_SEC"
lock-timeout="60000"/>
<replicated-cache name="auth-cache"
batching="true" mode="ASYNC">
<eviction strategy="LRU"
max-entries="10000"/>
<expiration lifespan="60000"/>
</replicated-cache>
</cache-container>
</subsystem>
...
<subsystem xmlns="urn:jboss:domain:security:1.2">
<security-domains>
...
<security-domain name="OAuth-Consumer"
cache-type="infinispan">
<authentication>
<login-module
code="com.idbs.ewb.server.auth.module.OAuthConsumerLoginModule"
flag="sufficient"
module="deployment.ewb-server-ear.ear">
<module-option
name="allowedConsumerAuthFailures" value="-1"/>
<module-option
name="consumerLoginFailureTimeoutMs" value="3000"/>
</login-module>
</authentication>
</security-domain>
</security-domains>
</subsystem>
After startup the OAuth-Consumer security domain cache "auth-cache" is not
always configured with the specified settings (~50% of the time). This can be verified by
monitoring the jboss.infinispan nodes with JConsole and retrieving the cache settings, and
tracking the cache hits during logins. This shows that succesful logins are cached but do
not expire after the expected 60s, and that the expiration lifespan is set to -1 rather
than 60000, as are eviction max entries.
After some debugging I have narrowed down the problem to some code that gets called from
inside org.jboss.as.security.plugins.JNDIBasedSecurityManagement:
public SecurityDomainContext createSecurityDomainContext(String securityDomain, Object
cacheFactory) throws Exception {
log.debugf("Creating SDC for domain=" + securityDomain);
AuthenticationManager am = createAuthenticationManager(securityDomain);
// create authentication cache
if (cacheFactory instanceof EmbeddedCacheManager) {
EmbeddedCacheManager cacheManager =
EmbeddedCacheManager.class.cast(cacheFactory);
@SuppressWarnings("rawtypes")
Cache cache = null;
if (cacheManager != null) {
// TODO override global settings with security domain specific
ConfigurationBuilder builder = new ConfigurationBuilder();
Configuration baseCfg =
cacheManager.getCacheConfiguration("auth-cache");
^^^^ This call here doesn’t always return the correct configuration, baseCfg is then
null.
if (baseCfg != null) {
builder.read(baseCfg);
}
cacheManager.defineConfiguration(securityDomain, builder.build());
cache = cacheManager.getCache(securityDomain);
}
if (cache != null && am instanceof CacheableManager) {
@SuppressWarnings({ "unchecked", "rawtypes" })
CacheableManager<Map, Principal> cm = (CacheableManager<Map,
Principal>) am;
cm.setCache(cache);
}
} else if (cacheFactory instanceof DefaultAuthenticationCacheFactory) {
<…>
}
getCacheConfiguration(String) is implemented inside
org.infinispan.manager.DefaultCacheManager:
@Override
public Configuration getCacheConfiguration(String name) {
Configuration configuration = configurationOverrides.get(name);
if (configuration == null && cacheExists(name)) {
return defaultConfiguration;
}
return configuration;
}
Seems like the condition configuration == null occurs when the cache doesn’t exist,
therefore it returns null. This appears to be a race condition between this code and where
it gets registered in wireAndStartCache(String).