Announcement Announcement Module
Collapse
No announcement yet.
SAMLMessageStorage implementation for DB? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • SAMLMessageStorage implementation for DB?

    Hi,

    in my case, the system contains multiple tomcats, each one of them runs a WAR that contains the sprin-sec-saml. so i'm not sure that the tomcat which sent the saml-request is the one to handle the saml-response. hence, i need to store the messages in a db, and not in-memory, counting on the session (like HttpSessionStorage does)...
    does it sound reasonable?

    is there an implementation of SAMLMessageStorage that suits read/writes from a DB?

  • #2
    Hi,

    The SAMLMessageStorage interface was created for exactly the case you have - need for a custom way to replicate SAML state across multiple nodes. Implementing it in your own way definitely sounds reasonable.

    I haven't yet written any additional implementations on top of the default HttpSessionStorage, but planned to do so in the future - although it's more likely to be using a distributed cache instead of a database. I'm not aware of any other existing implementations done by someone else.

    Btw one possible solution is to enable session replication in Tomcat.

    Vladimir Schafer

    Comment


    • #3
      So i've implemented a class called JdbcMessageStorage implements SAMLMessageStorage.
      The problem now is that AuthnRequestImpl is not Serializable... Any ideas??
      (p.s. why AuthnRequestImpl is not Serializable?)

      Comment


      • #4
        You can see how to serialize AuthnRequest objects in the HttpSessionStorage implementation - just wrap them in the SAMLObject which is serializable.

        The low-level SAML components come from the OpenSAML project and therefore I'm not able to tell why isn't the AuthnRequestImpl serializable. You'd need to ask OpenSAML authors.

        Comment


        • #5
          Well, I found another way to de/serialize... :-) anyways, I'd love to contribute it, if you find it interesting.
          I know that the relevant classes are from opemsaml, so it cannot be changed by you. So I've created a new type,
          Code:
          class SerializableAuthnRequestImpl 
          	implements Serializable, AuthnRequest
          and i de/serialize it.
          also, I've created the factory, etc. let me know if you find it interesting...

          Comment


          • #6
            I'm glad you found a solution which works for you, but make sure to test it carefully - making the AuthnRequest wrapped in a Serializable class or extending it and declaring the extended class as Serializable won't do the trick. Declaring something as Serializable only works as long as all the properties in the complete class hierarchy are either Serializable, primitive types, or declared as volatile (which makes them effectively skipped during serialization). The AuthnRequest hierarchy doesn't fit the bill, see e.g. property Issuer, Extensions (in RequestAbstractTypeImpl). The org.springframework.security.saml.parser.SAMLObjec t solves this problem by marhalling SAML messages into strings and back and works on any object implementing the org.opensaml.common.SAMLObject interface without need for additional boilerplate code.

            Comment


            • #7
              1. I looked carefully into the implementation and saw that after the XMLObject is restored, only 5 items are checked: ID, assertionConsumerServiceIndex, assertionConsumerServiceUrl, protocolBinding, and requestAuthnContect. So in my impl, I took care only for these 5. I know it will break easily if the implementation of the spring-saml will change in the future and check for another item, for example.
              However, I needed something fast, and something that will work...

              2. Hope I understood correctly; my storeMessage looks like this now:
              Code:
                          
              	@Override
              	public void storeMessage(String messageId, XMLObject message)
              	{
              		SAMLObject object = new SAMLObject<XMLObject>(message);
              
              		jdbcTemplate.update(insertRefreshTokenSql,
              				new Object[] { messageId, new SqlLobValue(serialize(object)) },
              				new int[] { Types.VARCHAR, Types.BLOB });
              	}
              it does the trick. Let me know if you want me to contribute this class... ;-)
              Last edited by OhadR; May 16th, 2013, 08:34 AM.

              Comment


              • #8
                I use memcached for distributed storage, hope it helps.

                By the way, is it safe to remove message in SAMLMessageStorageImpl.retrieveMessage() ?

                Code:
                 <!-- Override SAML storage factory -->
                    <bean id="samlMessageStorageFactory" class="com.mycompany.SAMLMessageStorageFactoryImpl"/>
                Code:
                public class SAMLMessageStorageFactoryImpl implements SAMLMessageStorageFactory {
                
                    @Autowired
                    private Cache cache;
                
                    @Override
                    public SAMLMessageStorage getMessageStorage(HttpServletRequest request) {
                        return new SAMLMessageStorageImpl(cache);
                    }
                }


                Code:
                public class SAMLMessageStorageImpl implements SAMLMessageStorage {
                
                    private static final String CACHE_KEY_PREFIX = "SAML:MSG:";
                    private static final int cacheExpirationSec = 60 * 60; // 1h
                
                    private Cache cache;
                
                    public SAMLMessageStorageImpl(Cache cache) {
                        this.cache = cache;
                    }
                
                    @Override
                    public void storeMessage(String messageId, XMLObject message) {
                        String key = cacheKey(messageId);
                        SAMLObject value = new SAMLObject<XMLObject>(message);
                
                        try {
                            cache.add(key, value, cacheExpirationSec);
                        } catch (CacheException e) {
                            throw new RuntimeException("Unable to save SAML message to cache", e);
                        }
                    }
                
                    @Override
                    public XMLObject retrieveMessage(String messageID) {
                        String key = cacheKey(messageID);
                
                        SAMLObject value = cache.get(key);
                
                        if (value == null){
                            return null;
                        }
                
                        // looks like it's only required once, so cleanup
                        cache.remove(key);
                
                        return value.getObject();
                    }
                
                    private String cacheKey(String messageId){
                        return CACHE_KEY_PREFIX + messageId;
                    }
                }

                Comment

                Working...
                X