Spring SAML - Reading and refreshing IdP metadata at runtime

前端 未结 1 1377
鱼传尺愫
鱼传尺愫 2021-01-03 10:55

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

相关标签:
1条回答
  • 2021-01-03 11:15

    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.

    1. The initial reading of all IdP metadata providers from a database table
    2. Expiring and re-reading the IdP metadata XML data
    3. Dynamically adding and removing a provider without config changes or server restart

    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.

    0 讨论(0)
提交回复
热议问题