Make it possible to use PluginLoader without store
[cascardo/ipsilon.git] / ipsilon / providers / openid / store.py
1 # Copyright (C) 2014 Ipsilon project Contributors, for license see COPYING
2
3 from ipsilon.util.data import Store, UNIQUE_DATA_TABLE, OPTIONS_TABLE
4
5 from openid import oidutil
6 from openid.association import Association
7 from openid.store.nonce import SKEW as NonceSKEW
8 from openid.store.interface import OpenIDStore as OpenIDStoreInterface
9
10 from time import time
11
12
13 class OpenIDStore(Store, OpenIDStoreInterface):
14     def __init__(self, database_url):
15         Store.__init__(self, database_url=database_url)
16
17     def storeAssociation(self, server_url, assoc):
18         iden = '%s-%s' % (server_url, assoc.handle)
19         datum = {'secret': oidutil.toBase64(assoc.secret),
20                  'issued': str(assoc.issued),
21                  'lifetime': str(assoc.lifetime),
22                  'assoc_type': assoc.assoc_type}
23
24         data = {iden: datum}
25         self.save_unique_data('association', data)
26
27     def getAssociation(self, server_url, handle=None):
28         iden = '%s-%s' % (server_url, handle)
29         data = self.get_unique_data('association', iden)
30
31         if len(data) < 1:
32             return None
33
34         datum = data[iden]
35         assoc = Association(handle,
36                             oidutil.fromBase64(datum['secret']),
37                             int(datum['issued']),
38                             int(datum['lifetime']),
39                             datum['assoc_type'])
40
41         if assoc.expiresIn == 0:
42             self.del_unique_data('association', iden)
43             return None
44
45         return assoc
46
47     def removeAssociation(self, server_url, handle):
48         iden = '%s-%s' % (server_url, handle)
49         self.del_unique_data('association', iden)
50
51     def useNonce(self, server_url, timestamp, salt):
52         if abs(timestamp - time()) > NonceSKEW:
53             return False
54
55         iden = '%s-%s-%s' % (server_url, timestamp, salt)
56         data = self.get_unique_data('nonce', iden)
57
58         if len(data) > 0:
59             # This server_url, timestamp, salt combination is already seen
60             return False
61
62         datum = {'timestamp': timestamp}
63         data = {iden: datum}
64         self.save_unique_data('nonce', data)
65
66         return True
67
68     def _cleanup(self):
69         res1 = self.cleanupNonces()
70         res2 = self.cleanupAssociations()
71         return res1 + res2
72
73     def cleanupNonces(self):
74         nonces = self.get_unique_data('nonce')
75         cleaned = 0
76         for iden in nonces:
77             if nonces[iden]['timestamp'] < (time() - NonceSKEW):
78                 cleaned += 1
79                 self.del_unique_data('nonce', iden)
80         return cleaned
81
82     def cleanupAssociations(self):
83         assocs = self.get_unique_data('association')
84         cleaned = 0
85         for iden in assocs:
86             if ((int(assocs[iden]['issued']) + int(assocs[iden]['lifetime']))
87                     < time()):
88                 cleaned += 1
89                 self.del_unique_data('association', iden)
90         return cleaned
91
92     def _initialize_schema(self):
93         q = self._query(self._db, 'association', UNIQUE_DATA_TABLE,
94                         trans=False)
95         q.create()
96         q._con.close()  # pylint: disable=protected-access
97
98     def _upgrade_schema(self, old_version):
99         if old_version == 1:
100             # In schema version 2, we added indexes and primary keys
101             # pylint: disable=protected-access
102             table = self._query(self._db, 'association', UNIQUE_DATA_TABLE,
103                                 trans=False)._table
104             self._db.add_constraint(table.primary_key)
105             for index in table.indexes:
106                 self._db.add_index(index)
107             table = self._query(self._db, 'openid_extensions', OPTIONS_TABLE,
108                                 trans=False)._table
109             self._db.add_constraint(table.primary_key)
110             for index in table.indexes:
111                 self._db.add_index(index)
112             return 2
113         else:
114             raise NotImplementedError()