Skip to content

Commit dcb43d7

Browse files
author
Richard Jones
committed
Finished implementation of session and one-time-key stores for RDBMS backends.
Refactored the API of sessions and their interaction with the backend database a fair bit too. Added some session tests. Nothing testing ageing yet, 'cos that's a pain inna ass to test :) Note: metakit backend still uses the *dbm implementation. It might want to implement its own session store some day, as it'll be faster than the *dbm one.
1 parent 304d199 commit dcb43d7

21 files changed

+318
-97
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Feature:
1313
- added postgresql backend (originally from sf patch 761740, many changes
1414
since)
1515
- all RDBMS backends now have indexes on several columns
16+
- RDBMS backends implement their session and one-time-key stores
1617
- change nosymessage and send_message to accept msgid=None (RFE #707235).
1718
- handle Resent-From: headers (sf bug 841151)
1819
- always sort MultilinkHTMLProperty in the correct order, usually

roundup/backends/back_anydbm.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
#$Id: back_anydbm.py,v 1.137 2004-03-15 05:50:20 richard Exp $
18+
#$Id: back_anydbm.py,v 1.138 2004-03-18 01:58:45 richard Exp $
1919
'''This module defines a backend that saves the hyperdatabase in a
2020
database chosen by anydbm. It is guaranteed to always be available in python
2121
versions >2.1.1 (the dumbdbm fallback in 2.1.1 and earlier has several
@@ -36,7 +36,7 @@
3636
import whichdb, os, marshal, re, weakref, string, copy
3737
from roundup import hyperdb, date, password, roundupdb, security
3838
from blobfiles import FileStorage
39-
from sessions import Sessions, OneTimeKeys
39+
from sessions_dbm import Sessions, OneTimeKeys
4040
from roundup.indexer import Indexer
4141
from roundup.backends import locking
4242
from roundup.hyperdb import String, Password, Date, Interval, Link, \
@@ -79,8 +79,6 @@ def __init__(self, config, journaltag=None):
7979
self.destroyednodes = {}# keep track of the destroyed nodes by class
8080
self.transactions = []
8181
self.indexer = Indexer(self.dir)
82-
self.sessions = Sessions(self.config)
83-
self.otks = OneTimeKeys(self.config)
8482
self.security = security.Security(self)
8583
# ensure files are group readable and writable
8684
os.umask(0002)
@@ -103,6 +101,12 @@ def refresh_database(self):
103101
"""
104102
self.reindex()
105103

104+
def getSessionManager(self):
105+
return Sessions(self)
106+
107+
def getOTKManager(self):
108+
return OneTimeKeys(self)
109+
106110
def reindex(self):
107111
for klass in self.classes.values():
108112
for nodeid in klass.list():

roundup/backends/back_metakit.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: back_metakit.py,v 1.61 2004-03-12 05:36:26 richard Exp $
1+
# $Id: back_metakit.py,v 1.62 2004-03-18 01:58:45 richard Exp $
22
'''Metakit backend for Roundup, originally by Gordon McMillan.
33
44
Known Current Bugs:
@@ -43,7 +43,7 @@
4343

4444
from roundup import hyperdb, date, password, roundupdb, security
4545
import metakit
46-
from sessions import Sessions, OneTimeKeys
46+
from sessions_dbm import Sessions, OneTimeKeys
4747
import re, marshal, os, sys, time, calendar
4848
from roundup import indexer
4949
import locking
@@ -81,8 +81,6 @@ def __init__(self, config, journaltag=None):
8181
self.lockfile = None
8282
self._db = self.__open()
8383
self.indexer = Indexer(self.config.DATABASE, self._db)
84-
self.sessions = Sessions(self.config)
85-
self.otks = OneTimeKeys(self.config)
8684
self.security = security.Security(self)
8785

8886
os.umask(0002)
@@ -101,6 +99,12 @@ def reindex(self):
10199
klass.index(nodeid)
102100
self.indexer.save_index()
103101

102+
def getSessionManager(self):
103+
return Sessions(self)
104+
105+
def getOTKManager(self):
106+
return OneTimeKeys(self)
107+
104108
# --- defined in ping's spec
105109
def __getattr__(self, classname):
106110
if classname == 'transactions':

roundup/backends/back_mysql.py

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -86,22 +86,25 @@ class Database(Database):
8686
# use BDB to pass all unit tests.
8787
mysql_backend = 'InnoDB'
8888
#mysql_backend = 'BDB'
89-
90-
def sql_open_connection(self):
91-
# make sure the database actually exists
92-
if not db_exists(self.config):
93-
db_create(self.config)
9489

90+
def sql_open_connection(self):
9591
db = getattr(self.config, 'MYSQL_DATABASE')
9692
try:
97-
self.conn = MySQLdb.connect(*db)
93+
conn = MySQLdb.connect(*db)
9894
except MySQLdb.OperationalError, message:
9995
raise DatabaseError, message
96+
cursor = conn.cursor()
97+
cursor.execute("SET AUTOCOMMIT=0")
98+
cursor.execute("BEGIN")
99+
return (conn, cursor)
100+
101+
def open_connection(self):
102+
# make sure the database actually exists
103+
if not db_exists(self.config):
104+
db_create(self.config)
105+
106+
self.conn, self.cursor = self.sql_open_connection()
100107

101-
self.cursor = self.conn.cursor()
102-
# start transaction
103-
self.sql("SET AUTOCOMMIT=0")
104-
self.sql("BEGIN")
105108
try:
106109
self.load_dbschema()
107110
except MySQLdb.OperationalError, message:
@@ -124,9 +127,10 @@ def create_version_2_tables(self):
124127
self.cursor.execute('CREATE TABLE otks (otk_key VARCHAR(255), '
125128
'otk_value VARCHAR(255), otk_time FLOAT(20))')
126129
self.cursor.execute('CREATE INDEX otks_key_idx ON otks(otk_key)')
127-
self.cursor.execute('CREATE TABLE sessions (s_key VARCHAR(255), '
128-
's_last_use FLOAT(20), s_user VARCHAR(255))')
129-
self.cursor.execute('CREATE INDEX sessions_key_idx ON sessions(s_key)')
130+
self.cursor.execute('CREATE TABLE sessions (session_key VARCHAR(255), '
131+
'session_time FLOAT(20), session_value VARCHAR(255))')
132+
self.cursor.execute('CREATE INDEX sessions_key_idx ON '
133+
'sessions(session_key)')
130134

131135
def add_actor_column(self):
132136
# update existing tables to have the new actor column

roundup/backends/back_postgresql.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -83,19 +83,24 @@ class Database(rdbms_common.Database):
8383
arg = '%s'
8484

8585
def sql_open_connection(self):
86+
db = getattr(self.config, 'POSTGRESQL_DATABASE')
87+
try:
88+
conn = psycopg.connect(**db)
89+
except psycopg.OperationalError, message:
90+
raise hyperdb.DatabaseError, message
91+
92+
cursor = conn.cursor()
93+
94+
return (conn, cursor)
95+
96+
def open_connection(self):
8697
if not db_exists(self.config):
8798
db_create(self.config)
8899

89100
if __debug__:
90101
print >>hyperdb.DEBUG, '+++ open database connection +++'
91102

92-
db = getattr(self.config, 'POSTGRESQL_DATABASE')
93-
try:
94-
self.conn = psycopg.connect(**db)
95-
except psycopg.OperationalError, message:
96-
raise hyperdb.DatabaseError, message
97-
98-
self.cursor = self.conn.cursor()
103+
self.conn, self.cursor = self.sql_open_connection()
99104

100105
try:
101106
self.load_dbschema()
@@ -111,9 +116,10 @@ def create_version_2_tables(self):
111116
self.cursor.execute('CREATE TABLE otks (otk_key VARCHAR(255), '
112117
'otk_value VARCHAR(255), otk_time FLOAT(20))')
113118
self.cursor.execute('CREATE INDEX otks_key_idx ON otks(otk_key)')
114-
self.cursor.execute('CREATE TABLE sessions (s_key VARCHAR(255), '
115-
's_last_use FLOAT(20), s_user VARCHAR(255))')
116-
self.cursor.execute('CREATE INDEX sessions_key_idx ON sessions(s_key)')
119+
self.cursor.execute('CREATE TABLE sessions (session_key VARCHAR(255), '
120+
'session_time FLOAT(20), session_value VARCHAR(255))')
121+
self.cursor.execute('CREATE INDEX sessions_key_idx ON '
122+
'sessions(session_key)')
117123

118124
def add_actor_column(self):
119125
# update existing tables to have the new actor column

roundup/backends/back_sqlite.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: back_sqlite.py,v 1.16 2004-03-15 05:50:20 richard Exp $
1+
# $Id: back_sqlite.py,v 1.17 2004-03-18 01:58:45 richard Exp $
22
'''Implements a backend for SQLite.
33
44
See https://pysqlite.sourceforge.net/ for pysqlite info
@@ -17,18 +17,24 @@ class Database(rdbms_common.Database):
1717
arg = '%s'
1818

1919
def sql_open_connection(self):
20+
db = os.path.join(self.config.DATABASE, 'db')
21+
conn = sqlite.connect(db=db)
22+
cursor = conn.cursor()
23+
return (conn, cursor)
24+
25+
def open_connection(self):
2026
# ensure files are group readable and writable
2127
os.umask(0002)
22-
db = os.path.join(self.config.DATABASE, 'db')
2328

24-
# lock it
29+
# lock the database
30+
db = os.path.join(self.config.DATABASE, 'db')
2531
lockfilenm = db[:-3] + 'lck'
2632
self.lockfile = locking.acquire_lock(lockfilenm)
2733
self.lockfile.write(str(os.getpid()))
2834
self.lockfile.flush()
2935

30-
self.conn = sqlite.connect(db=db)
31-
self.cursor = self.conn.cursor()
36+
(self.conn, self.cursor) = self.sql_open_connection()
37+
3238
try:
3339
self.load_dbschema()
3440
except sqlite.DatabaseError, error:
@@ -40,13 +46,24 @@ def sql_open_connection(self):
4046
self.cursor.execute('create index ids_name_idx on ids(name)')
4147
self.create_version_2_tables()
4248

49+
def close(self):
50+
''' Close off the connection.
51+
'''
52+
self.sql_close()
53+
if self.lockfile is not None:
54+
locking.release_lock(self.lockfile)
55+
if self.lockfile is not None:
56+
self.lockfile.close()
57+
self.lockfile = None
58+
4359
def create_version_2_tables(self):
4460
self.cursor.execute('create table otks (otk_key varchar, '
45-
'otk_value varchar, otk_time varchar)')
61+
'otk_value varchar, otk_time integer)')
4662
self.cursor.execute('create index otks_key_idx on otks(otk_key)')
47-
self.cursor.execute('create table sessions (s_key varchar, '
48-
's_last_use varchar, s_user varchar)')
49-
self.cursor.execute('create index sessions_key_idx on sessions(s_key)')
63+
self.cursor.execute('create table sessions (session_key varchar, '
64+
'session_time integer, session_value varchar)')
65+
self.cursor.execute('create index sessions_key_idx on '
66+
'sessions(session_key)')
5067

5168
def add_actor_column(self):
5269
# update existing tables to have the new actor column

roundup/backends/rdbms_common.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: rdbms_common.py,v 1.80 2004-03-17 22:01:37 richard Exp $
1+
# $Id: rdbms_common.py,v 1.81 2004-03-18 01:58:45 richard Exp $
22
''' Relational database (SQL) backend common code.
33
44
Basics:
@@ -40,7 +40,7 @@
4040
# support
4141
from blobfiles import FileStorage
4242
from roundup.indexer import Indexer
43-
from sessions import Sessions, OneTimeKeys
43+
from sessions_rdbms import Sessions, OneTimeKeys
4444
from roundup.date import Range
4545

4646
# number of rows to keep in memory
@@ -60,8 +60,6 @@ def __init__(self, config, journaltag=None):
6060
self.dir = config.DATABASE
6161
self.classes = {}
6262
self.indexer = Indexer(self.dir)
63-
self.sessions = Sessions(self.config)
64-
self.otks = OneTimeKeys(self.config)
6563
self.security = security.Security(self)
6664

6765
# additional transaction support for external files and the like
@@ -76,13 +74,19 @@ def __init__(self, config, journaltag=None):
7674
self.lockfile = None
7775

7876
# open a connection to the database, creating the "conn" attribute
79-
self.sql_open_connection()
77+
self.open_connection()
8078

8179
def clearCache(self):
8280
self.cache = {}
8381
self.cache_lru = []
8482

85-
def sql_open_connection(self):
83+
def getSessionManager(self):
84+
return Sessions(self)
85+
86+
def getOTKManager(self):
87+
return OneTimeKeys(self)
88+
89+
def open_connection(self):
8690
''' Open a connection to the database, creating it if necessary.
8791
8892
Must call self.load_dbschema()
@@ -1069,11 +1073,6 @@ def close(self):
10691073
''' Close off the connection.
10701074
'''
10711075
self.sql_close()
1072-
if self.lockfile is not None:
1073-
locking.release_lock(self.lockfile)
1074-
if self.lockfile is not None:
1075-
self.lockfile.close()
1076-
self.lockfile = None
10771076

10781077
#
10791078
# The base Class class
Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#$Id: sessions.py,v 1.10 2004-02-26 04:20:45 drkorg Exp $
1+
#$Id: sessions_dbm.py,v 1.1 2004-03-18 01:58:45 richard Exp $
22
"""This module defines a very basic store that's used by the CGI interface
33
to store session and one-time-key information.
44
@@ -16,9 +16,9 @@ class BasicDatabase:
1616
'''
1717
_db_type = None
1818

19-
def __init__(self, config):
20-
self.config = config
21-
self.dir = config.DATABASE
19+
def __init__(self, db):
20+
self.config = db.config
21+
self.dir = db.config.DATABASE
2222
# ensure files are group readable and writable
2323
os.umask(0002)
2424

@@ -45,13 +45,16 @@ def cache_db_type(self, path):
4545
db_type = 'dbm'
4646
self.__class__._db_type = db_type
4747

48-
def get(self, infoid, value):
48+
_marker = []
49+
def get(self, infoid, value, default=_marker):
4950
db = self.opendb('c')
5051
try:
5152
if db.has_key(infoid):
5253
values = marshal.loads(db[infoid])
5354
else:
54-
return None
55+
if default != self._marker:
56+
return default
57+
raise KeyError, 'No such %s "%s"'%(self.name, infoid)
5558
return values.get(value, None)
5659
finally:
5760
db.close()
@@ -62,7 +65,7 @@ def getall(self, infoid):
6265
try:
6366
return marshal.loads(db[infoid])
6467
except KeyError:
65-
raise KeyError, 'No such One Time Key "%s"'%infoid
68+
raise KeyError, 'No such %s "%s"'%(self.name, infoid)
6669
finally:
6770
db.close()
6871

@@ -72,7 +75,7 @@ def set(self, infoid, **newvalues):
7275
if db.has_key(infoid):
7376
values = marshal.loads(db[infoid])
7477
else:
75-
values = {}
78+
values = {'__timestamp': time.time()}
7679
values.update(newvalues)
7780
db[infoid] = marshal.dumps(values)
7881
finally:
@@ -115,23 +118,24 @@ def opendb(self, mode):
115118
def commit(self):
116119
pass
117120

121+
def close(self):
122+
pass
123+
118124
def updateTimestamp(self, sessid):
119-
self.set(sessid, **{self.timestamp: time.time()})
125+
self.set(sessid, __timestamp=time.time())
120126

121127
def clean(self, now):
122128
"""Age sessions, remove when they haven't been used for a week.
123129
"""
124130
week = 60*60*24*7
125131
for sessid in self.list():
126-
interval = now - self.get(sessid, self.timestamp)
132+
interval = now - self.get(sessid, '__timestamp')
127133
if interval > week:
128134
self.destroy(sessid)
129135

130136
class Sessions(BasicDatabase):
131137
name = 'sessions'
132-
timestamp = 'last_use'
133138

134139
class OneTimeKeys(BasicDatabase):
135140
name = 'otks'
136-
timestamp = '__time'
137141

0 commit comments

Comments
 (0)