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
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:
//This constructor allows us to read in metadata stored in a database.
public DBMetadataProviderList(ParserPool _parser, Timer _timer) throws MetadataProviderException
{
this.parser = _parser;
List<String> metadataProviderIds = getUniqueEntityIdListFromDB();
for (final String mdprovId : metadataProviderIds)
{
DBMetadataProvider metadataProvider = new DBMetadataProvider(_timer, mdprovId);
metadataProvider.setParserPool(this.parser);
metadataProvider.setMaxRefreshDelay(480000); // 8 mins (set low for testing)
metadataProvider.setMinRefreshDelay(120000); // 2 mins
ExtendedMetadataDelegate md = new ExtendedMetadataDelegate(metadataProvider, new ExtendedMetadata());
add(md);
}
}
To solve item #2 I used the FilesystemMetadataProvider
as a guide and created a DBMetadataProvider
class. By extending the AbstractReloadingMetadataProvider
class and implementing the fetchMetadata()
method we have built-in cache refreshing thanks to opensaml. Below are the important parts (example code only):
public class DBMetadataProvider extends AbstractReloadingMetadataProvider
{
private String metaDataEntityId; // unique Id for DB lookups
/**
* Constructor.
* @param entityId the entity Id of the metadata. Use as key to identify a database row.
*/
public DBMetadataProvider(String entityId)
{
super();
setMetaDataEntityId(entityId);
}
/**
* Constructor.
* @param backgroundTaskTimer timer used to refresh metadata in the background
* @param entityId the entity Id of the metadata. Use as key to identify a database row.
*/
public DBMetadataProvider(Timer backgroundTaskTimer, String entityId)
{
super(backgroundTaskTimer);
setMetaDataEntityId(entityId);
}
public String getMetaDataEntityId() { return metaDataEntityId; }
public void setMetaDataEntityId(String metaDataEntityId){ this.metaDataEntityId = metaDataEntityId; }
@Override
protected String getMetadataIdentifier() { return getMetaDataEntityId(); }
// This example code simply does straight JDBC
@Override
protected byte[] fetchMetadata() throws MetadataProviderException
{
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try
{
conn = JDBCUtility.getConnection();
ps = conn.prepareStatement("select bla bla bla ENTITY_ID = ?");
ps.setString(1, getMetaDataEntityId());
rs = ps.executeQuery();
if (rs.next())
{
// include a modified date column in schema so that we know if row has changed
Timestamp sqldt = rs.getTimestamp("MOD_DATE"); // use TimeStamp here to get full datetime
DateTime metadataUpdateTime = new DateTime(sqldt.getTime(), ISOChronology.getInstanceUTC());
if (getLastRefresh() == null || getLastUpdate() == null || metadataUpdateTime.isAfter(getLastRefresh()))
{
log.info("Reading IdP metadata from database with entityId = " + getMetaDataEntityId());
Clob clob = rs.getClob("XML_IDP_METADATA");
return clob2Bytes(clob);
}
return null;
}
else
{
// row was not found
throw new MetadataProviderException("Metadata with entityId = '" + getMetaDataEntityId() + "' does not exist");
}
}
catch (Exception e)
{
String msg = "Unable to query metadata from database with entityId = " + getMetaDataEntityId();
log.error(msg, e);
throw new MetadataProviderException(msg, e);
}
finally
{
// clean up connections
}
}
}
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.