Skip to content

Commit

Permalink
CXF-8765: Option to remove Ehcache
Browse files Browse the repository at this point in the history
  • Loading branch information
reta committed Dec 21, 2024
1 parent baec165 commit 9b1cdfd
Show file tree
Hide file tree
Showing 23 changed files with 1,342 additions and 55 deletions.
7 changes: 7 additions & 0 deletions rt/rs/security/sso/saml/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,12 @@
<classifier>jakarta</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.ehcache.modules</groupId>
<artifactId>ehcache-107</artifactId>
<version>${cxf.ehcache3.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/**
* 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.cxf.rs.security.saml.sso.jcache;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.time.Instant;

import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.cache.Caching;
import javax.cache.configuration.MutableConfiguration;
import javax.cache.spi.CachingProvider;

import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.jaxrs.utils.ResourceUtils;
import org.apache.cxf.rs.security.saml.sso.TokenReplayCache;
import org.apache.wss4j.common.cache.EHCacheReplayCache;
import org.apache.wss4j.common.cache.EHCacheValue;
import org.apache.wss4j.common.util.Loader;

/**
* An in-memory EHCache implementation of the TokenReplayCache interface.
* The default TTL is 60 minutes and the max TTL is 12 hours.
*/

public class JCacheTokenReplayCache implements TokenReplayCache<String> {

public static final String CACHE_KEY = "cxf.samlp.replay.cache";

private static final org.slf4j.Logger LOG =
org.slf4j.LoggerFactory.getLogger(EHCacheReplayCache.class);
private static final String DEFAULT_CONFIG_URL = "/cxf-samlp-jcache.xml";

private final Cache<String, EHCacheValue> cache;
private final CacheManager cacheManager;

public JCacheTokenReplayCache() throws URISyntaxException {
this(DEFAULT_CONFIG_URL, null);
}

public JCacheTokenReplayCache(Bus bus) throws URISyntaxException {
this(DEFAULT_CONFIG_URL, bus);
}

public JCacheTokenReplayCache(String configFile) throws URISyntaxException {
this(configFile, null);
}

public JCacheTokenReplayCache(String configFile, Bus bus) throws URISyntaxException {
if (bus == null) {
bus = BusFactory.getThreadDefaultBus(true);
}
URL configFileURL = null;
try {
configFileURL =
ResourceUtils.getClasspathResourceURL(configFile, JCacheTokenReplayCache.class, bus);
} catch (Exception ex) {
// ignore
}

final CachingProvider cachingProvider = Caching.getCachingProvider();

cacheManager = cachingProvider.getCacheManager(
getConfigFileURL(configFileURL).toURI(),
getClass().getClassLoader());

cache = getOrCreate(cacheManager, CACHE_KEY, String.class, EHCacheValue.class);
}

private static <K, V> Cache<K, V> getOrCreate(CacheManager cacheManager, String name,
Class<K> kclass, Class<V> vclass) {

final Cache<K, V> cache = cacheManager.getCache(name, kclass, vclass);
if (cache != null) {
return cache;
}

final MutableConfiguration<K, V> cacheConfiguration = new MutableConfiguration<>();
cacheConfiguration.setTypes(kclass, vclass);

return cacheManager.createCache(name, cacheConfiguration);
}

private URL getConfigFileURL(URL suppliedConfigFileURL) {
if (suppliedConfigFileURL == null) {
//using the default
try {
URL configFileURL = Loader.getResource(DEFAULT_CONFIG_URL);
if (configFileURL == null) {
configFileURL = new URL(DEFAULT_CONFIG_URL);
}
return configFileURL;
} catch (IOException e) {
// Do nothing
LOG.debug(e.getMessage());
}
}
return suppliedConfigFileURL;
}

/**
* Add the given identifier to the cache. It will be cached for a default amount of time.
* @param identifier The identifier to be added
*/
public void putId(String identifier) {
putId(identifier, null);
}

/**
* Add the given identifier to the cache to be cached for the given time
* @param identifier The identifier to be added
* @param expiry A custom expiry time for the identifier. Can be null in which case, the default expiry is used.
*/
public void putId(String identifier, Instant expiry) {
if (identifier == null || "".equals(identifier)) {
return;
}

cache.put(identifier, new EHCacheValue(identifier, expiry));
}

/**
* Return true if the given identifier is contained in the cache
* @param identifier The identifier to check
*/
public boolean contains(String identifier) {
if (cache == null) {
return false;
}
EHCacheValue element = cache.get(identifier);
return element != null;
}

public synchronized void close() {
if (!cacheManager.isClosed()) {
cacheManager.destroyCache(CACHE_KEY);
cacheManager.close();
}
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/**
* 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.cxf.rs.security.saml.sso.state.jcache;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;

import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.cache.Caching;
import javax.cache.configuration.MutableConfiguration;
import javax.cache.spi.CachingProvider;

import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.jaxrs.utils.ResourceUtils;
import org.apache.cxf.rs.security.saml.sso.state.RequestState;
import org.apache.cxf.rs.security.saml.sso.state.ResponseState;
import org.apache.cxf.rs.security.saml.sso.state.SPStateManager;
import org.apache.wss4j.common.util.Loader;

/**
* An in-memory EHCache implementation of the SPStateManager interface.
* The default TTL is 5 minutes.
*/
public class JCacheSPStateManager implements SPStateManager {

public static final String REQUEST_CACHE_KEY = "cxf.samlp.request.state.cache";
public static final String RESPONSE_CACHE_KEY = "cxf.samlp.response.state.cache";
private static final String DEFAULT_CONFIG_URL = "/cxf-samlp-jcache.xml";
private static final org.slf4j.Logger LOG =
org.slf4j.LoggerFactory.getLogger(JCacheSPStateManager.class);

private final Cache<String, RequestState> requestCache;
private final Cache<String, ResponseState> responseCache;
private final CacheManager cacheManager;

public JCacheSPStateManager() throws URISyntaxException {
this(DEFAULT_CONFIG_URL, null);
}

public JCacheSPStateManager(Bus bus) throws URISyntaxException {
this(DEFAULT_CONFIG_URL, bus);
}

public JCacheSPStateManager(String configFileURL) throws URISyntaxException {
this(configFileURL, null);
}

public JCacheSPStateManager(String configFile, Bus bus) throws URISyntaxException {
if (bus == null) {
bus = BusFactory.getThreadDefaultBus(true);
}

URL configFileURL = null;
try {
configFileURL =
ResourceUtils.getClasspathResourceURL(configFile, JCacheSPStateManager.class, bus);
} catch (Exception ex) {
// ignore
}

final CachingProvider cachingProvider = Caching.getCachingProvider();

cacheManager = cachingProvider.getCacheManager(
getConfigFileURL(configFileURL).toURI(),
getClass().getClassLoader());

requestCache = getOrCreate(cacheManager, REQUEST_CACHE_KEY, String.class, RequestState.class);
responseCache = getOrCreate(cacheManager, RESPONSE_CACHE_KEY, String.class, ResponseState.class);
}

private static <K, V> Cache<K, V> getOrCreate(CacheManager cacheManager, String name,
Class<K> kclass, Class<V> vclass) {

final Cache<K, V> cache = cacheManager.getCache(name, kclass, vclass);
if (cache != null) {
return cache;
}

final MutableConfiguration<K, V> cacheConfiguration = new MutableConfiguration<>();
cacheConfiguration.setTypes(kclass, vclass);

return cacheManager.createCache(name, cacheConfiguration);
}

private URL getConfigFileURL(URL suppliedConfigFileURL) {
if (suppliedConfigFileURL == null) {
//using the default
try {
URL configFileURL = Loader.getResource(DEFAULT_CONFIG_URL);
if (configFileURL == null) {
configFileURL = new URL(DEFAULT_CONFIG_URL);
}
return configFileURL;
} catch (IOException e) {
// Do nothing
LOG.debug(e.getMessage());
}
}
return suppliedConfigFileURL;
}

public ResponseState getResponseState(String securityContextKey) {
return responseCache.get(securityContextKey);
}

public ResponseState removeResponseState(String securityContextKey) {
ResponseState responseState = getResponseState(securityContextKey);
if (responseState != null) {
responseCache.remove(securityContextKey);
}
return responseState;
}

public void setResponseState(String securityContextKey, ResponseState state) {
if (securityContextKey == null || "".equals(securityContextKey)) {
return;
}

responseCache.put(securityContextKey, state);
}

public void setRequestState(String relayState, RequestState state) {
if (relayState == null || "".equals(relayState)) {
return;
}

requestCache.put(relayState, state);
}

public RequestState removeRequestState(String relayState) {
RequestState state = requestCache.get(relayState);
if (state != null) {
requestCache.remove(relayState);
}
return state;
}

public synchronized void close() throws IOException {
if (!cacheManager.isClosed()) {
cacheManager.destroyCache(REQUEST_CACHE_KEY);
cacheManager.destroyCache(RESPONSE_CACHE_KEY);
cacheManager.close();
}
}

}
56 changes: 56 additions & 0 deletions rt/rs/security/sso/saml/src/main/resources/cxf-samlp-jcache.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?xml version="1.0"?>
<config
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns='http://www.ehcache.org/v3'
xmlns:jsr107='http://www.ehcache.org/v3/jsr107'
xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.8.xsd
http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">

<service>
<jsr107:defaults>
<jsr107:cache name="cxf.samlp.response.state.cache" template="cxf.samlp.response.state.cache.template"/>
<jsr107:cache name="cxf.samlp.request.state.cache" template="cxf.samlp.request.state.cache.template"/>
<jsr107:cache name="cxf.samlp.replay.cache" template="cxf.samlp.replay.cache.template"/>
</jsr107:defaults>
</service>

<persistence directory="${java.io.tmpdir}/ehcache/" />

<cache-template name="cxf.samlp.replay.cache.template">
<key-type>java.lang.String</key-type>
<value-type>org.apache.wss4j.common.cache.EHCacheValue</value-type>
<expiry>
<class>org.apache.wss4j.common.cache.EHCacheExpiry</class>
</expiry>
<resources>
<heap unit="entries">5000</heap>
<disk unit="MB" persistent="false">10</disk>
</resources>
</cache-template>

<cache-template name="cxf.samlp.request.state.cache.template">
<key-type>java.lang.String</key-type>
<value-type>org.apache.cxf.rs.security.saml.sso.state.RequestState</value-type>
<expiry>
<ttl unit="seconds">300</ttl>
</expiry>
<resources>
<heap unit="entries">5000</heap>
<disk unit="MB" persistent="false">10</disk>
</resources>
</cache-template>

<cache-template name="cxf.samlp.response.state.cache.template">
<key-type>java.lang.String</key-type>
<value-type>org.apache.cxf.rs.security.saml.sso.state.ResponseState</value-type>
<expiry>
<ttl unit="seconds">300</ttl>
</expiry>
<resources>
<heap unit="entries">5000</heap>
<disk unit="MB" persistent="false">10</disk>
</resources>
</cache-template>

</config>

Loading

0 comments on commit 9b1cdfd

Please sign in to comment.