Add transactions support
authorSimo Sorce <simo@redhat.com>
Wed, 10 Sep 2014 21:19:55 +0000 (17:19 -0400)
committerPatrick Uiterwijk <puiterwijk@redhat.com>
Wed, 24 Sep 2014 18:51:24 +0000 (20:51 +0200)
In some cases a user may end up having multiple login pags in diffeent tabs in
the borwser (session restore after a crash, or simply opening multiple urls
which all redirect to the same IdP).
Without transactions multiple authentication requests in fly may step on each
other causing potentially all of them to fail to properly authenticate and
redirect back to the original web site.

Signed-off-by: Simo Sorce <simo@redhat.com>
Reviewed-by: Patrick Uiterwijk <puiterwijk@redhat.com>
ipsilon/util/data.py
ipsilon/util/trans.py [new file with mode: 0755]

index bdf93e7..5a144a4 100755 (executable)
@@ -348,3 +348,18 @@ class UserStore(Store):
 
     def save_user_preferences(self, user, options):
         return self.save_options('users', user, options)
+
+
+class TranStore(Store):
+
+    def __init__(self, path=None):
+        if path is None:
+            self._path = os.getcwd()
+        else:
+            self._path = path
+        self._name = None
+        if 'transactions.db' in cherrypy.config:
+            self._name = cherrypy.config['transactions.db']
+        if not self._name:
+            self._name = os.path.join(self._path, 'transactions.sqlite')
+        super(TranStore, self).__init__(self._name)
diff --git a/ipsilon/util/trans.py b/ipsilon/util/trans.py
new file mode 100755 (executable)
index 0000000..4d2f887
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2014  Ipsilon project Contributors, for licensee see COPYING
+
+from ipsilon.util.data import TranStore
+from ipsilon.util.log import Log
+from datetime import datetime
+from ipsilon.util.cookies import SecureCookie
+
+
+TRANSTABLE = 'transactions'
+TRANSID = "ipsilon_transaction_id"
+
+
+class Transaction(Log):
+
+    def __init__(self, provider, **kwargs):
+        self.debug('Transaction: %s' % repr(kwargs))
+        self.provider = provider
+        self.transaction_id = None
+        self._ts = TranStore()
+        self.cookie = None
+        tid = kwargs.get(TRANSID)
+        if tid:
+            self.transaction_id = tid
+            data = self._ts.get_unique_data(TRANSTABLE, tid)
+            self._get_cookie()
+        else:
+            data = {'provider': self.provider,
+                    'origintime': str(datetime.now())}
+            self.transaction_id = self._ts.new_unique_data(TRANSTABLE, data)
+            self._set_cookie()
+        self.debug('Transaction id: %s' % self.transaction_id)
+
+    def _set_cookie(self):
+        self.cookie = SecureCookie(name=None, value=self.provider)
+        self.cookie.send()
+        cookiedata = {'cookie': self.cookie.name}
+        data = {self.transaction_id: cookiedata}
+        self._ts.save_unique_data(TRANSTABLE, data)
+
+    def _get_cookie(self):
+        data = self.retrieve()
+        if 'cookie' not in data:
+            raise ValueError('Cookie name not available')
+        self.cookie = SecureCookie(data['cookie'])
+        self.cookie.receive()
+        if self.cookie.value is None:
+            raise ValueError('Missing or invalid cookie')
+
+    def _del_cookie(self):
+        self.cookie.delete()
+
+    def wipe(self):
+        if not self.transaction_id:
+            return
+        self._ts.del_unique_data(TRANSTABLE, self.transaction_id)
+        self._del_cookie()
+        self.transaction_id = None
+
+    def store(self, data):
+        savedata = {self.transaction_id: data}
+        self._ts.save_unique_data(TRANSTABLE, savedata)
+
+    def retrieve(self):
+        data = self._ts.get_unique_data(TRANSTABLE,
+                                        uuidval=self.transaction_id)
+        return data.get(self.transaction_id)
+
+    def get_GET_arg(self):
+        return "%s=%s" % (TRANSID, self.transaction_id)
+
+    def get_POST_tuple(self):
+        return (TRANSID, self.transaction_id)