Add OpenIDStore to store associations and nonces
authorPatrick Uiterwijk <puiterwijk@redhat.com>
Fri, 5 Dec 2014 17:28:21 +0000 (12:28 -0500)
committerPatrick Uiterwijk <puiterwijk@redhat.com>
Fri, 5 Dec 2014 20:44:17 +0000 (15:44 -0500)
Signed-off-by: Patrick Uiterwijk <puiterwijk@redhat.com>
Reviewed-by: Simo Sorce <simo@redhat.com>
ipsilon/providers/openid/store.py [new file with mode: 0755]
ipsilon/providers/openidp.py

diff --git a/ipsilon/providers/openid/store.py b/ipsilon/providers/openid/store.py
new file mode 100755 (executable)
index 0000000..9b426bc
--- /dev/null
@@ -0,0 +1,80 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2014  Ipsilon project Contributors, for licensee see COPYING
+
+from ipsilon.util.data import Store
+
+from openid import oidutil
+from openid.association import Association
+from openid.store.nonce import SKEW as NonceSKEW
+from openid.store.interface import OpenIDStore as OpenIDStoreInterface
+
+from time import time
+
+class OpenIDStore(Store, OpenIDStoreInterface):
+    def __init__(self, database_url):
+        Store.__init__(self, database_url=database_url)
+
+    def storeAssociation(self, server_url, assoc):
+        iden = '%s-%s' % (server_url, assoc.handle)
+        datum = {'secret': oidutil.toBase64(assoc.secret),
+                 'issued': str(assoc.issued),
+                 'lifetime': str(assoc.lifetime),
+                 'assoc_type': assoc.assoc_type}
+
+        data = {iden: datum}
+        self.save_unique_data('association', data)
+
+    def getAssociation(self, server_url, handle=None):
+        iden = '%s-%s' % (server_url, handle)
+        data = self.get_unique_data('association', iden)
+
+        if len(data) < 1:
+            return None
+
+        datum = data[iden]
+        assoc = Association(handle,
+                            oidutil.fromBase64(datum['secret']),
+                            int(datum['issued']),
+                            int(datum['lifetime']),
+                            datum['assoc_type'])
+
+        if assoc.expiresIn == 0:
+            self.del_unique_data('association', iden)
+            return None
+
+        return assoc
+
+    def removeAssociation(self, server_url, handle):
+        iden = '%s-%s' % (server_url, handle)
+        self.del_unique_data('association', iden)
+
+    def useNonce(self, server_url, timestamp, salt):
+        if abs(timestamp - time()) > NonceSKEW:
+            return False
+
+        iden = '%s-%s-%s' % (server_url, timestamp, salt)
+        data = self.get_unique_data('nonce', iden)
+
+        if len(data) > 0:
+            # This server_url, timestamp, salt combination is already seen
+            return False
+
+        datum = {'timestamp': timestamp}
+        data = {iden: datum}
+        self.save_unique_data('nonce', data)
+
+        return True
+
+    def cleanupNonces(self):
+        nonces = self.get_unique_data('nonce')
+        for iden in nonces:
+            if nonces[iden]['timestamp'] < (time() - NonceSKEW):
+                self.del_unique_data('nonce', iden)
+
+    def cleanupAssociations(self):
+        assocs = self.get_unique_data('association')
+        for iden in assocs:
+            if ((int(assocs[iden]['issued']) + int(assocs[iden]['lifetime']))
+                    < time()):
+                self.del_unique_data('association', iden)
index 7b53f78..08890a2 100755 (executable)
@@ -5,6 +5,7 @@
 from __future__ import absolute_import
 
 from ipsilon.providers.common import ProviderBase
+from ipsilon.providers.openid.store import OpenIDStore
 from ipsilon.providers.openid.auth import OpenID
 from ipsilon.providers.openid.extensions.common import LoadExtensions
 from ipsilon.util.plugin import PluginObject
@@ -12,8 +13,6 @@ from ipsilon.util import config as pconfig
 from ipsilon.info.common import InfoMapping
 
 from openid.server.server import Server
-# TODO: Move this to the database
-from openid.store.memstore import MemoryStore
 
 
 class IdpProvider(ProviderBase):
@@ -32,6 +31,10 @@ Provides OpenID 2.0 authentication infrastructure. """
 
         self.new_config(
             self.name,
+            pconfig.String(
+                'database url',
+                'Database URL for OpenID temp storage',
+                'openid.sqlite'),
             pconfig.String(
                 'default email domain',
                 'Used for users missing the email property.',
@@ -96,7 +99,9 @@ Provides OpenID 2.0 authentication infrastructure. """
         return self.page
 
     def init_idp(self):
-        self.server = Server(MemoryStore(), op_endpoint=self.endpoint_url)
+        self.server = Server(
+            OpenIDStore(self.get_config_value('database url')),
+            op_endpoint=self.endpoint_url)
 
         # Expose OpenID presence in the root
         headers = self._root.default_headers