I am using WSO2 and SSOCircle with the Spring-SAML extension. We are testing configurations at this time and have defined 2 IdP's and 2 SP's within our applicationContext. So, currently, we have 2 statically defined IdP's within our spring xml config and this is working. For testing purpose we are using the combination of CachingMetadataManager and ResourceBackedMetadataProvider so the IdP metadata is built inside of our WAR archive. Sample:
<bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
<constructor-arg>
<list>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<constructor-arg>
<bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
<constructor-arg>
<bean class="java.util.Timer"/>
</constructor-arg>
<constructor-arg>
<bean class="org.opensaml.util.resource.ClasspathResource">
<constructor-arg value="/metadata/wso2idp_metadata.xml"/>
</bean>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
</bean>
</constructor-arg>
</bean>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<constructor-arg>
<bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
<constructor-arg>
<bean class="java.util.Timer"/>
</constructor-arg>
<constructor-arg>
<bean class="org.opensaml.util.resource.ClasspathResource">
<constructor-arg value="/metadata/ssocircleidp_metadata.xml"/>
</bean>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
</bean>
</constructor-arg>
</bean>
</list>
</constructor-arg>
For production, we want to be able to store our IdP metadata in a database (centrally located). I want to be able to add, remove and modify the metadata without redeploying the WAR or restarting the server(s). Initially, I thought I could override the CachingMetadataManager and define a noarg constructor that could load all Metadata providers dynamically but this is not possible because the CachingMetadataManager only defines a single constructor that must take in a MetadataProvider List. I ended up doing the following:
<bean id="metadataList" class="org.arbfile.util.security.saml.DBMetadataProviderList">
<constructor-arg ref="parserPool" />
<constructor-arg>
<bean class="java.util.Timer"/>
</constructor-arg>
</bean>
<bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
<constructor-arg ref="metadataList" />
</bean>
Bean metadataList can be defined simply as:
public final class DBMetadataProviderList extends ArrayList<MetadataProvider>
{
private final static Logger log = LoggerFactory.getLogger(DBMetadataProviderList.class);
private ParserPool parser;
public DBMetadataProviderList(ParserPool _parser, Timer _timer) throws MetadataProviderException
{
this.parser = _parser;
// Lookup metadata from DB
}
}
This does allow me to read in the IdP metadata dynamically. My logic falls down when it comes to refreshing though. I found this post on the spring forum, however it is 3 to 4 years old. What is the best way to dynamically read, add and update IdP metadata, cache it and have the cache refresh at some interval? In my case 1 row in a DB table would equate to a single IdP metadata definition.
After reading several more posts and scanning source code, I discovered the answer to this question is more complex than I thought. There are really 3 different scenarios to address.
I'll take on each of these one at a time. Item 1: There are probably several ways to solve this but review the
DBMetadataProviderList
class (in my OP) above as a quick and dirty solution. Here is more complete constructor code:To solve item #2 I used the
FilesystemMetadataProvider
as a guide and created aDBMetadataProvider
class. By extending theAbstractReloadingMetadataProvider
class and implementing thefetchMetadata()
method we have built-in cache refreshing thanks to opensaml. Below are the important parts (example code only):This resource helped me get to the right technique for a cache reloading metadata provider class. Finally, item #3 can be solved by implementing this post.