summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: d181a76)
raw | patch | inline | side by side (parent: d181a76)
author | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Thu, 18 Mar 2004 01:58:46 +0000 (01:58 +0000) | ||
committer | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Thu, 18 Mar 2004 01:58:46 +0000 (01:58 +0000) |
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.
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@2151 57a73879-2fb5-44c3-a270-3262357dd7e2
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.
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@2151 57a73879-2fb5-44c3-a270-3262357dd7e2
22 files changed:
diff --git a/CHANGES.txt b/CHANGES.txt
index 710b1046440ff7321adde6fc19d28f01a933969a..f0c79f6082e056cdfb7035a830f73f03ceabb0d3 100644 (file)
--- a/CHANGES.txt
+++ b/CHANGES.txt
- added postgresql backend (originally from sf patch 761740, many changes
since)
- all RDBMS backends now have indexes on several columns
+- RDBMS backends implement their session and one-time-key stores
- change nosymessage and send_message to accept msgid=None (RFE #707235).
- handle Resent-From: headers (sf bug 841151)
- always sort MultilinkHTMLProperty in the correct order, usually
index 64c6236aa3cb8ea66e40b5c8242bcfd48baf9750..7091d19bd218995e1bd78a529f6c06b49bb207eb 100644 (file)
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-#$Id: back_anydbm.py,v 1.137 2004-03-15 05:50:20 richard Exp $
+#$Id: back_anydbm.py,v 1.138 2004-03-18 01:58:45 richard Exp $
'''This module defines a backend that saves the hyperdatabase in a
database chosen by anydbm. It is guaranteed to always be available in python
versions >2.1.1 (the dumbdbm fallback in 2.1.1 and earlier has several
import whichdb, os, marshal, re, weakref, string, copy
from roundup import hyperdb, date, password, roundupdb, security
from blobfiles import FileStorage
-from sessions import Sessions, OneTimeKeys
+from sessions_dbm import Sessions, OneTimeKeys
from roundup.indexer import Indexer
from roundup.backends import locking
from roundup.hyperdb import String, Password, Date, Interval, Link, \
self.destroyednodes = {}# keep track of the destroyed nodes by class
self.transactions = []
self.indexer = Indexer(self.dir)
- self.sessions = Sessions(self.config)
- self.otks = OneTimeKeys(self.config)
self.security = security.Security(self)
# ensure files are group readable and writable
os.umask(0002)
"""
self.reindex()
+ def getSessionManager(self):
+ return Sessions(self)
+
+ def getOTKManager(self):
+ return OneTimeKeys(self)
+
def reindex(self):
for klass in self.classes.values():
for nodeid in klass.list():
index 055782ea49c4fcbd66095d1ead40ab5765347c0c..cf3f59f99c594947f43e5771f2d5deb94f7c2093 100755 (executable)
-# $Id: back_metakit.py,v 1.61 2004-03-12 05:36:26 richard Exp $
+# $Id: back_metakit.py,v 1.62 2004-03-18 01:58:45 richard Exp $
'''Metakit backend for Roundup, originally by Gordon McMillan.
Known Current Bugs:
from roundup import hyperdb, date, password, roundupdb, security
import metakit
-from sessions import Sessions, OneTimeKeys
+from sessions_dbm import Sessions, OneTimeKeys
import re, marshal, os, sys, time, calendar
from roundup import indexer
import locking
self.lockfile = None
self._db = self.__open()
self.indexer = Indexer(self.config.DATABASE, self._db)
- self.sessions = Sessions(self.config)
- self.otks = OneTimeKeys(self.config)
self.security = security.Security(self)
os.umask(0002)
klass.index(nodeid)
self.indexer.save_index()
+ def getSessionManager(self):
+ return Sessions(self)
+
+ def getOTKManager(self):
+ return OneTimeKeys(self)
+
# --- defined in ping's spec
def __getattr__(self, classname):
if classname == 'transactions':
index a02b445ec99b427811742f8c40e877fbe0f6ad22..3afd3d36a16847ba89f5f26756837086352a2820 100644 (file)
# use BDB to pass all unit tests.
mysql_backend = 'InnoDB'
#mysql_backend = 'BDB'
-
- def sql_open_connection(self):
- # make sure the database actually exists
- if not db_exists(self.config):
- db_create(self.config)
+ def sql_open_connection(self):
db = getattr(self.config, 'MYSQL_DATABASE')
try:
- self.conn = MySQLdb.connect(*db)
+ conn = MySQLdb.connect(*db)
except MySQLdb.OperationalError, message:
raise DatabaseError, message
+ cursor = conn.cursor()
+ cursor.execute("SET AUTOCOMMIT=0")
+ cursor.execute("BEGIN")
+ return (conn, cursor)
+
+ def open_connection(self):
+ # make sure the database actually exists
+ if not db_exists(self.config):
+ db_create(self.config)
+
+ self.conn, self.cursor = self.sql_open_connection()
- self.cursor = self.conn.cursor()
- # start transaction
- self.sql("SET AUTOCOMMIT=0")
- self.sql("BEGIN")
try:
self.load_dbschema()
except MySQLdb.OperationalError, message:
self.cursor.execute('CREATE TABLE otks (otk_key VARCHAR(255), '
'otk_value VARCHAR(255), otk_time FLOAT(20))')
self.cursor.execute('CREATE INDEX otks_key_idx ON otks(otk_key)')
- self.cursor.execute('CREATE TABLE sessions (s_key VARCHAR(255), '
- 's_last_use FLOAT(20), s_user VARCHAR(255))')
- self.cursor.execute('CREATE INDEX sessions_key_idx ON sessions(s_key)')
+ self.cursor.execute('CREATE TABLE sessions (session_key VARCHAR(255), '
+ 'session_time FLOAT(20), session_value VARCHAR(255))')
+ self.cursor.execute('CREATE INDEX sessions_key_idx ON '
+ 'sessions(session_key)')
def add_actor_column(self):
# update existing tables to have the new actor column
index fa9ba09e9de69ecda1807bd56ae4450f7a2c3d79..a09086b50db3bdb992cc9e91876d338eba21d39b 100644 (file)
arg = '%s'
def sql_open_connection(self):
+ db = getattr(self.config, 'POSTGRESQL_DATABASE')
+ try:
+ conn = psycopg.connect(**db)
+ except psycopg.OperationalError, message:
+ raise hyperdb.DatabaseError, message
+
+ cursor = conn.cursor()
+
+ return (conn, cursor)
+
+ def open_connection(self):
if not db_exists(self.config):
db_create(self.config)
if __debug__:
print >>hyperdb.DEBUG, '+++ open database connection +++'
- db = getattr(self.config, 'POSTGRESQL_DATABASE')
- try:
- self.conn = psycopg.connect(**db)
- except psycopg.OperationalError, message:
- raise hyperdb.DatabaseError, message
-
- self.cursor = self.conn.cursor()
+ self.conn, self.cursor = self.sql_open_connection()
try:
self.load_dbschema()
self.cursor.execute('CREATE TABLE otks (otk_key VARCHAR(255), '
'otk_value VARCHAR(255), otk_time FLOAT(20))')
self.cursor.execute('CREATE INDEX otks_key_idx ON otks(otk_key)')
- self.cursor.execute('CREATE TABLE sessions (s_key VARCHAR(255), '
- 's_last_use FLOAT(20), s_user VARCHAR(255))')
- self.cursor.execute('CREATE INDEX sessions_key_idx ON sessions(s_key)')
+ self.cursor.execute('CREATE TABLE sessions (session_key VARCHAR(255), '
+ 'session_time FLOAT(20), session_value VARCHAR(255))')
+ self.cursor.execute('CREATE INDEX sessions_key_idx ON '
+ 'sessions(session_key)')
def add_actor_column(self):
# update existing tables to have the new actor column
index 04100d09316daac2229e5979670148f0cbdc4b30..e1105d3488484e85709872189091dbdd7ac09dac 100644 (file)
-# $Id: back_sqlite.py,v 1.16 2004-03-15 05:50:20 richard Exp $
+# $Id: back_sqlite.py,v 1.17 2004-03-18 01:58:45 richard Exp $
'''Implements a backend for SQLite.
See https://pysqlite.sourceforge.net/ for pysqlite info
arg = '%s'
def sql_open_connection(self):
+ db = os.path.join(self.config.DATABASE, 'db')
+ conn = sqlite.connect(db=db)
+ cursor = conn.cursor()
+ return (conn, cursor)
+
+ def open_connection(self):
# ensure files are group readable and writable
os.umask(0002)
- db = os.path.join(self.config.DATABASE, 'db')
- # lock it
+ # lock the database
+ db = os.path.join(self.config.DATABASE, 'db')
lockfilenm = db[:-3] + 'lck'
self.lockfile = locking.acquire_lock(lockfilenm)
self.lockfile.write(str(os.getpid()))
self.lockfile.flush()
- self.conn = sqlite.connect(db=db)
- self.cursor = self.conn.cursor()
+ (self.conn, self.cursor) = self.sql_open_connection()
+
try:
self.load_dbschema()
except sqlite.DatabaseError, error:
self.cursor.execute('create index ids_name_idx on ids(name)')
self.create_version_2_tables()
+ def close(self):
+ ''' Close off the connection.
+ '''
+ self.sql_close()
+ if self.lockfile is not None:
+ locking.release_lock(self.lockfile)
+ if self.lockfile is not None:
+ self.lockfile.close()
+ self.lockfile = None
+
def create_version_2_tables(self):
self.cursor.execute('create table otks (otk_key varchar, '
- 'otk_value varchar, otk_time varchar)')
+ 'otk_value varchar, otk_time integer)')
self.cursor.execute('create index otks_key_idx on otks(otk_key)')
- self.cursor.execute('create table sessions (s_key varchar, '
- 's_last_use varchar, s_user varchar)')
- self.cursor.execute('create index sessions_key_idx on sessions(s_key)')
+ self.cursor.execute('create table sessions (session_key varchar, '
+ 'session_time integer, session_value varchar)')
+ self.cursor.execute('create index sessions_key_idx on '
+ 'sessions(session_key)')
def add_actor_column(self):
# update existing tables to have the new actor column
index 2d030365750679496c7a8925c1e5468814cbfc81..2894cc63e35a8374b17ed070bd0fd0f6955353a9 100644 (file)
-# $Id: rdbms_common.py,v 1.80 2004-03-17 22:01:37 richard Exp $
+# $Id: rdbms_common.py,v 1.81 2004-03-18 01:58:45 richard Exp $
''' Relational database (SQL) backend common code.
Basics:
# support
from blobfiles import FileStorage
from roundup.indexer import Indexer
-from sessions import Sessions, OneTimeKeys
+from sessions_rdbms import Sessions, OneTimeKeys
from roundup.date import Range
# number of rows to keep in memory
self.dir = config.DATABASE
self.classes = {}
self.indexer = Indexer(self.dir)
- self.sessions = Sessions(self.config)
- self.otks = OneTimeKeys(self.config)
self.security = security.Security(self)
# additional transaction support for external files and the like
self.lockfile = None
# open a connection to the database, creating the "conn" attribute
- self.sql_open_connection()
+ self.open_connection()
def clearCache(self):
self.cache = {}
self.cache_lru = []
- def sql_open_connection(self):
+ def getSessionManager(self):
+ return Sessions(self)
+
+ def getOTKManager(self):
+ return OneTimeKeys(self)
+
+ def open_connection(self):
''' Open a connection to the database, creating it if necessary.
Must call self.load_dbschema()
''' Close off the connection.
'''
self.sql_close()
- if self.lockfile is not None:
- locking.release_lock(self.lockfile)
- if self.lockfile is not None:
- self.lockfile.close()
- self.lockfile = None
#
# The base Class class
diff --git a/roundup/backends/sessions.py b/roundup/backends/sessions.py
+++ /dev/null
@@ -1,137 +0,0 @@
-#$Id: sessions.py,v 1.10 2004-02-26 04:20:45 drkorg Exp $
-"""This module defines a very basic store that's used by the CGI interface
-to store session and one-time-key information.
-
-Yes, it's called "sessions" - because originally it only defined a session
-class. It's now also used for One Time Key handling too.
-"""
-__docformat__ = 'restructuredtext'
-
-import anydbm, whichdb, os, marshal, time
-
-class BasicDatabase:
- ''' Provide a nice encapsulation of an anydbm store.
-
- Keys are id strings, values are automatically marshalled data.
- '''
- _db_type = None
-
- def __init__(self, config):
- self.config = config
- self.dir = config.DATABASE
- # ensure files are group readable and writable
- os.umask(0002)
-
- def clear(self):
- path = os.path.join(self.dir, self.name)
- if os.path.exists(path):
- os.remove(path)
- elif os.path.exists(path+'.db'): # dbm appends .db
- os.remove(path+'.db')
-
- def cache_db_type(self, path):
- ''' determine which DB wrote the class file, and cache it as an
- attribute of __class__ (to allow for subclassed DBs to be
- different sorts)
- '''
- db_type = ''
- if os.path.exists(path):
- db_type = whichdb.whichdb(path)
- if not db_type:
- raise hyperdb.DatabaseError, "Couldn't identify database type"
- elif os.path.exists(path+'.db'):
- # if the path ends in '.db', it's a dbm database, whether
- # anydbm says it's dbhash or not!
- db_type = 'dbm'
- self.__class__._db_type = db_type
-
- def get(self, infoid, value):
- db = self.opendb('c')
- try:
- if db.has_key(infoid):
- values = marshal.loads(db[infoid])
- else:
- return None
- return values.get(value, None)
- finally:
- db.close()
-
- def getall(self, infoid):
- db = self.opendb('c')
- try:
- try:
- return marshal.loads(db[infoid])
- except KeyError:
- raise KeyError, 'No such One Time Key "%s"'%infoid
- finally:
- db.close()
-
- def set(self, infoid, **newvalues):
- db = self.opendb('c')
- try:
- if db.has_key(infoid):
- values = marshal.loads(db[infoid])
- else:
- values = {}
- values.update(newvalues)
- db[infoid] = marshal.dumps(values)
- finally:
- db.close()
-
- def list(self):
- db = self.opendb('r')
- try:
- return db.keys()
- finally:
- db.close()
-
- def destroy(self, infoid):
- db = self.opendb('c')
- try:
- if db.has_key(infoid):
- del db[infoid]
- finally:
- db.close()
-
- def opendb(self, mode):
- '''Low-level database opener that gets around anydbm/dbm
- eccentricities.
- '''
- # figure the class db type
- path = os.path.join(os.getcwd(), self.dir, self.name)
- if self._db_type is None:
- self.cache_db_type(path)
-
- db_type = self._db_type
-
- # new database? let anydbm pick the best dbm
- if not db_type:
- return anydbm.open(path, 'c')
-
- # open the database with the correct module
- dbm = __import__(db_type)
- return dbm.open(path, mode)
-
- def commit(self):
- pass
-
- def updateTimestamp(self, sessid):
- self.set(sessid, **{self.timestamp: time.time()})
-
- def clean(self, now):
- """Age sessions, remove when they haven't been used for a week.
- """
- week = 60*60*24*7
- for sessid in self.list():
- interval = now - self.get(sessid, self.timestamp)
- if interval > week:
- self.destroy(sessid)
-
-class Sessions(BasicDatabase):
- name = 'sessions'
- timestamp = 'last_use'
-
-class OneTimeKeys(BasicDatabase):
- name = 'otks'
- timestamp = '__time'
-
diff --git a/roundup/backends/sessions_dbm.py b/roundup/backends/sessions_dbm.py
--- /dev/null
@@ -0,0 +1,141 @@
+#$Id: sessions_dbm.py,v 1.1 2004-03-18 01:58:45 richard Exp $
+"""This module defines a very basic store that's used by the CGI interface
+to store session and one-time-key information.
+
+Yes, it's called "sessions" - because originally it only defined a session
+class. It's now also used for One Time Key handling too.
+"""
+__docformat__ = 'restructuredtext'
+
+import anydbm, whichdb, os, marshal, time
+
+class BasicDatabase:
+ ''' Provide a nice encapsulation of an anydbm store.
+
+ Keys are id strings, values are automatically marshalled data.
+ '''
+ _db_type = None
+
+ def __init__(self, db):
+ self.config = db.config
+ self.dir = db.config.DATABASE
+ # ensure files are group readable and writable
+ os.umask(0002)
+
+ def clear(self):
+ path = os.path.join(self.dir, self.name)
+ if os.path.exists(path):
+ os.remove(path)
+ elif os.path.exists(path+'.db'): # dbm appends .db
+ os.remove(path+'.db')
+
+ def cache_db_type(self, path):
+ ''' determine which DB wrote the class file, and cache it as an
+ attribute of __class__ (to allow for subclassed DBs to be
+ different sorts)
+ '''
+ db_type = ''
+ if os.path.exists(path):
+ db_type = whichdb.whichdb(path)
+ if not db_type:
+ raise hyperdb.DatabaseError, "Couldn't identify database type"
+ elif os.path.exists(path+'.db'):
+ # if the path ends in '.db', it's a dbm database, whether
+ # anydbm says it's dbhash or not!
+ db_type = 'dbm'
+ self.__class__._db_type = db_type
+
+ _marker = []
+ def get(self, infoid, value, default=_marker):
+ db = self.opendb('c')
+ try:
+ if db.has_key(infoid):
+ values = marshal.loads(db[infoid])
+ else:
+ if default != self._marker:
+ return default
+ raise KeyError, 'No such %s "%s"'%(self.name, infoid)
+ return values.get(value, None)
+ finally:
+ db.close()
+
+ def getall(self, infoid):
+ db = self.opendb('c')
+ try:
+ try:
+ return marshal.loads(db[infoid])
+ except KeyError:
+ raise KeyError, 'No such %s "%s"'%(self.name, infoid)
+ finally:
+ db.close()
+
+ def set(self, infoid, **newvalues):
+ db = self.opendb('c')
+ try:
+ if db.has_key(infoid):
+ values = marshal.loads(db[infoid])
+ else:
+ values = {'__timestamp': time.time()}
+ values.update(newvalues)
+ db[infoid] = marshal.dumps(values)
+ finally:
+ db.close()
+
+ def list(self):
+ db = self.opendb('r')
+ try:
+ return db.keys()
+ finally:
+ db.close()
+
+ def destroy(self, infoid):
+ db = self.opendb('c')
+ try:
+ if db.has_key(infoid):
+ del db[infoid]
+ finally:
+ db.close()
+
+ def opendb(self, mode):
+ '''Low-level database opener that gets around anydbm/dbm
+ eccentricities.
+ '''
+ # figure the class db type
+ path = os.path.join(os.getcwd(), self.dir, self.name)
+ if self._db_type is None:
+ self.cache_db_type(path)
+
+ db_type = self._db_type
+
+ # new database? let anydbm pick the best dbm
+ if not db_type:
+ return anydbm.open(path, 'c')
+
+ # open the database with the correct module
+ dbm = __import__(db_type)
+ return dbm.open(path, mode)
+
+ def commit(self):
+ pass
+
+ def close(self):
+ pass
+
+ def updateTimestamp(self, sessid):
+ self.set(sessid, __timestamp=time.time())
+
+ def clean(self, now):
+ """Age sessions, remove when they haven't been used for a week.
+ """
+ week = 60*60*24*7
+ for sessid in self.list():
+ interval = now - self.get(sessid, '__timestamp')
+ if interval > week:
+ self.destroy(sessid)
+
+class Sessions(BasicDatabase):
+ name = 'sessions'
+
+class OneTimeKeys(BasicDatabase):
+ name = 'otks'
+
diff --git a/roundup/backends/sessions_rdbms.py b/roundup/backends/sessions_rdbms.py
--- /dev/null
@@ -0,0 +1,90 @@
+#$Id: sessions_rdbms.py,v 1.1 2004-03-18 01:58:45 richard Exp $
+"""This module defines a very basic store that's used by the CGI interface
+to store session and one-time-key information.
+
+Yes, it's called "sessions" - because originally it only defined a session
+class. It's now also used for One Time Key handling too.
+"""
+__docformat__ = 'restructuredtext'
+
+import os, time
+
+class BasicDatabase:
+ ''' Provide a nice encapsulation of an RDBMS table.
+
+ Keys are id strings, values are automatically marshalled data.
+ '''
+ def __init__(self, db):
+ self.db = db
+ self.cursor = self.db.cursor
+
+ def clear(self):
+ self.cursor.execute('delete from %ss'%self.name)
+
+ _marker = []
+ def get(self, infoid, value, default=_marker):
+ n = self.name
+ self.cursor.execute('select %s_value from %ss where %s_key=%s'%(n,
+ n, n, self.db.arg), (infoid,))
+ res = self.cursor.fetchone()
+ if not res:
+ if default != self._marker:
+ return default
+ raise KeyError, 'No such %s "%s"'%(self.name, infoid)
+ values = eval(res[0])
+ return values.get(value, None)
+
+ def getall(self, infoid):
+ n = self.name
+ self.cursor.execute('select %s_value from %ss where %s_key=%s'%(n,
+ n, n, self.db.arg), (infoid,))
+ res = self.cursor.fetchone()
+ if not res:
+ raise KeyError, 'No such %s "%s"'%(self.name, infoid)
+ return eval(res[0])
+
+ def set(self, infoid, **newvalues):
+ c = self.cursor
+ n = self.name
+ a = self.db.arg
+ c.execute('select %s_value from %ss where %s_key=%s'%(n, n, n, a),
+ (infoid,))
+ res = c.fetchone()
+ if res:
+ values = eval(res[0])
+ else:
+ values = {}
+ values.update(newvalues)
+
+ if res:
+ sql = 'update %ss set %s_value=%s where %s_key=%s'%(n, n,
+ a, n, a)
+ args = (repr(values), infoid)
+ else:
+ sql = 'insert into %ss (%s_key, %s_time, %s_value) '\
+ 'values (%s, %s, %s)'%(n, n, n, n, a, a, a)
+ args = (infoid, time.time(), repr(values))
+ c.execute(sql, args)
+
+ def destroy(self, infoid):
+ self.cursor.execute('delete from %ss where %s_key=%s'%(self.name,
+ self.name, self.db.arg), (infoid,))
+
+ def updateTimestamp(self, infoid):
+ self.cursor.execute('update %ss set %s_time=%s where %s_key=%s'%(
+ self.name, self.name, self.db.arg, self.name, self.db.arg),
+ (time.time(), infoid))
+
+ def clean(self, now):
+ """Age sessions, remove when they haven't been used for a week.
+ """
+ old = now - 60*60*24*7
+ self.cursor.execute('delete from %ss where %s_time < %s'%(self.name,
+ self.name, self.db.arg), (old, ))
+
+class Sessions(BasicDatabase):
+ name = 'session'
+
+class OneTimeKeys(BasicDatabase):
+ name = 'otk'
+
diff --git a/roundup/cgi/actions.py b/roundup/cgi/actions.py
index 46ff0e8b81d6689158d0b6098497e7994ffecbcc..3859f968b30eb29850dd10d7a12cebf51995a172 100755 (executable)
--- a/roundup/cgi/actions.py
+++ b/roundup/cgi/actions.py
if self.form.has_key('otk'):
# pull the rego information out of the otk database
otk = self.form['otk'].value
- uid = self.db.otks.get(otk, 'uid')
+ otks = self.db.getOTKManager()
+ uid = otks.get(otk, 'uid')
if uid is None:
self.client.error_message.append("""Invalid One Time Key!
(a Mozilla bug may cause this message to show up erroneously,
newpw = password.generatePassword()
cl = self.db.user
-# XXX we need to make the "default" page be able to display errors!
+ # XXX we need to make the "default" page be able to display errors!
try:
# set the password
cl.set(uid, password=password.Password(newpw))
# clear the props from the otk database
- self.db.otks.destroy(otk)
+ otks.destroy(otk)
self.db.commit()
except (ValueError, KeyError), message:
self.client.error_message.append(str(message))
if not self.client.standard_message([address], subject, body):
return
- self.client.ok_message.append('Password reset and email sent to %s' %
- address)
+ self.client.ok_message.append(
+ 'Password reset and email sent to %s'%address)
return
# no OTK, so now figure the user
# generate the one-time-key and store the props for later
otk = ''.join([random.choice(chars) for x in range(32)])
- d = {'uid': uid, self.db.otks.timestamp: time.time()}
- self.db.otks.set(otk, **d)
+ while otks.exists(otk):
+ otk = ''.join([random.choice(chars) for x in range(32)])
+ otks.set(otk, uid=uid)
+ self.db.commit()
# send the email
tracker_name = self.db.config.TRACKER_NAME
pass
# generate the one-time-key and store the props for later
- otk = ''.join([random.choice(chars) for x in range(32)])
for propname, proptype in self.db.user.getprops().items():
value = props.get(propname, None)
if value is None:
props[propname] = str(value)
elif isinstance(proptype, hyperdb.Password):
props[propname] = str(value)
- props[self.db.otks.timestamp] = time.time()
- self.db.otks.set(otk, **props)
+ otks = self.db.getOTKManager()
+ while otks.exists(otk):
+ otk = ''.join([random.choice(chars) for x in range(32)])
+ otks.set(otk, **props)
# send the email
tracker_name = self.db.config.TRACKER_NAME
diff --git a/roundup/cgi/client.py b/roundup/cgi/client.py
index d96aa168258a13532244194fd88ef6eabc16a394..03cfa642eab575652f69200029b2a043a61ddd21 100644 (file)
--- a/roundup/cgi/client.py
+++ b/roundup/cgi/client.py
-# $Id: client.py,v 1.165 2004-02-25 23:27:54 richard Exp $
+# $Id: client.py,v 1.166 2004-03-18 01:58:46 richard Exp $
"""WWW request handler (also used in the stand-alone server).
"""
Note: also cleans One Time Keys, and other "session" based stuff.
"""
- sessions = self.db.sessions
- last_clean = self.db.sessions.get('last_clean', 'last_use') or 0
+ sessions = self.db.getSessionManager()
+ last_clean = sessions.get('last_clean', 'last_use', 0)
# time to clean?
week = 60*60*24*7
if now - last_clean < hour:
return
- self.db.sessions.clean(now)
- self.db.otks.clean(now)
- self.db.sessions.set('last_clean', last_use=time.time())
+ sessions.clean(now)
+ self.db.getOTKManager().clean(now)
+ sessions.set('last_clean', last_use=time.time())
+ self.db.commit()
def determine_user(self):
''' Determine who the user is
# make sure we have the session Class
self.clean_sessions()
- sessions = self.db.sessions
+ sessions = self.db.getSessionManager()
# first up, try the REMOTE_USER var (from HTTP Basic Auth handled
# by a front-end HTTP server)
try:
# update the lifetime datestamp
sessions.updateTimestamp(self.session)
- sessions.commit()
user = sessions.get(self.session, 'user')
except KeyError:
# not valid, ignore id
self.session = self.session[:-1]
# insert the session in the sessiondb
- self.db.sessions.set(self.session, user=user, last_use=time.time())
-
- # and commit immediately
- self.db.sessions.commit()
+ sessions = self.db.getSessionManager()
+ sessions.set(self.session, user=user)
+ self.db.commit()
# expire us in a long, long time
expire = Cookie._getdate(86400*365)
index 7560e6a185e662c0341d3c42fca370aad4b13e8a..b14f63bab2923b599e453b3d7623f857c11107e3 100644 (file)
margin: 0;
}
a[href]:hover {
- background-color: white;
color:blue;
text-decoration: underline;
}
a[href], a[href]:link {
- background-color: white;
color:blue;
text-decoration: none;
}
diff --git a/test/db_test_base.py b/test/db_test_base.py
index b2ffcd1e4656aff7c50f8df65f6456d98049506a..2405c54d425734aca23976db511a77d02b5614c7 100644 (file)
--- a/test/db_test_base.py
+++ b/test/db_test_base.py
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: db_test_base.py,v 1.16 2004-03-15 05:50:20 richard Exp $
+# $Id: db_test_base.py,v 1.17 2004-03-18 01:58:46 richard Exp $
import unittest, os, shutil, errno, imp, sys, time, pprint
def tearDown(self):
if hasattr(self, 'db'):
self.db.close()
- if os.path.exists('_test_dir'):
- shutil.rmtree('_test_dir')
+ if os.path.exists(config.DATABASE):
+ shutil.rmtree(config.DATABASE)
class config:
DATABASE='_test_dir'
diff --git a/test/session_common.py b/test/session_common.py
--- /dev/null
+++ b/test/session_common.py
@@ -0,0 +1,46 @@
+import os, shutil, unittest
+
+from db_test_base import config
+
+class SessionTest(unittest.TestCase):
+ def setUp(self):
+ # remove previous test, ignore errors
+ if os.path.exists(config.DATABASE):
+ shutil.rmtree(config.DATABASE)
+ os.makedirs(config.DATABASE + '/files')
+ self.db = self.module.Database(config, 'admin')
+ self.sessions = self.sessions_module.Sessions(self.db)
+ self.otks = self.sessions_module.OneTimeKeys(self.db)
+
+ def tearDown(self):
+ del self.otks
+ del self.sessions
+ if hasattr(self, 'db'):
+ self.db.close()
+ if os.path.exists(config.DATABASE):
+ shutil.rmtree(config.DATABASE)
+
+ def testSetSession(self):
+ self.sessions.set('random_key', text='hello, world!')
+ self.assertEqual(self.sessions.get('random_key', 'text'),
+ 'hello, world!')
+
+ def testUpdateSession(self):
+ self.sessions.set('random_key', text='hello, world!')
+ self.assertEqual(self.sessions.get('random_key', 'text'),
+ 'hello, world!')
+ self.sessions.set('random_key', text='nope')
+ self.assertEqual(self.sessions.get('random_key', 'text'), 'nope')
+
+ def testSetOTK(self):
+ assert 0, 'not implemented'
+
+ def testExpiry(self):
+ assert 0, 'not implemented'
+
+class DBMTest(SessionTest):
+ import roundup.backends.sessions_dbm as sessions_module
+
+class RDBMSTest(SessionTest):
+ import roundup.backends.sessions_rdbms as sessions_module
+
diff --git a/test/test_anydbm.py b/test/test_anydbm.py
index 77c57b8744cf965da4ed77c292f9c1b6ba72c746..cb91c975db008eead4f7c4bdc80f6ce5f48bfe59 100644 (file)
--- a/test/test_anydbm.py
+++ b/test/test_anydbm.py
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: test_anydbm.py,v 1.2 2003-11-14 00:11:19 richard Exp $
+# $Id: test_anydbm.py,v 1.3 2004-03-18 01:58:46 richard Exp $
import unittest, os, shutil, time
class anydbmClassicInitTest(ClassicInitTest):
backend = 'anydbm'
+from session_common import DBMTest
+class anydbmSessionTest(anydbmOpener, DBMTest):
+ pass
+
def test_suite():
suite = unittest.TestSuite()
print 'Including anydbm tests'
suite.addTest(unittest.makeSuite(anydbmROTest))
suite.addTest(unittest.makeSuite(anydbmSchemaTest))
suite.addTest(unittest.makeSuite(anydbmClassicInitTest))
+ suite.addTest(unittest.makeSuite(anydbmSessionTest))
return suite
if __name__ == '__main__':
diff --git a/test/test_bsddb.py b/test/test_bsddb.py
index 24cde202a96188b731a79e3af630a2924cfab477..ef81a3b37612a4186823a17d4e22970f0cc1ecfa 100644 (file)
--- a/test/test_bsddb.py
+++ b/test/test_bsddb.py
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: test_bsddb.py,v 1.2 2003-11-14 00:11:19 richard Exp $
+# $Id: test_bsddb.py,v 1.3 2004-03-18 01:58:46 richard Exp $
import unittest, os, shutil, time
class bsddbClassicInitTest(ClassicInitTest):
backend = 'bsddb'
+from session_common import DBMTest
+class bsddbSessionTest(bsddbOpener, DBMTest):
+ pass
+
def test_suite():
suite = unittest.TestSuite()
if not hasattr(backends, 'bsddb'):
suite.addTest(unittest.makeSuite(bsddbROTest))
suite.addTest(unittest.makeSuite(bsddbSchemaTest))
suite.addTest(unittest.makeSuite(bsddbClassicInitTest))
+ suite.addTest(unittest.makeSuite(bsddbSessionTest))
return suite
if __name__ == '__main__':
diff --git a/test/test_bsddb3.py b/test/test_bsddb3.py
index 2387cda04aa6aec674b09becb3b3904146060a7d..3469fdda6d56c86a0df79fd618cf2a4c70903a78 100644 (file)
--- a/test/test_bsddb3.py
+++ b/test/test_bsddb3.py
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: test_bsddb3.py,v 1.2 2003-11-14 00:11:19 richard Exp $
+# $Id: test_bsddb3.py,v 1.3 2004-03-18 01:58:46 richard Exp $
import unittest, os, shutil, time
class bsddb3ClassicInitTest(ClassicInitTest):
backend = 'bsddb3'
+from session_common import DBMTest
+class bsddb3SessionTest(bsddb3Opener, DBMTest):
+ pass
+
def test_suite():
suite = unittest.TestSuite()
if not hasattr(backends, 'bsddb3'):
suite.addTest(unittest.makeSuite(bsddb3ROTest))
suite.addTest(unittest.makeSuite(bsddb3SchemaTest))
suite.addTest(unittest.makeSuite(bsddb3ClassicInitTest))
+ suite.addTest(unittest.makeSuite(bsddb3SessionTest))
return suite
if __name__ == '__main__':
diff --git a/test/test_metakit.py b/test/test_metakit.py
index 0610b79c540f17b450f6f63959b922f93f30ec26..845f4c291cc40172beda73008539c5e3e428d51c 100644 (file)
--- a/test/test_metakit.py
+++ b/test/test_metakit.py
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: test_metakit.py,v 1.3 2004-01-27 18:16:50 wc2so1 Exp $
+# $Id: test_metakit.py,v 1.4 2004-03-18 01:58:46 richard Exp $
import unittest, os, shutil, time, weakref
from db_test_base import DBTest, ROTest, SchemaTest, ClassicInitTest, config, password
class metakitClassicInitTest(ClassicInitTest):
backend = 'metakit'
+from session_common import DBMTest
+class metakitSessionTest(metakitOpener, DBMTest):
+ pass
+
def test_suite():
suite = unittest.TestSuite()
if not hasattr(backends, 'metakit'):
suite.addTest(unittest.makeSuite(metakitROTest))
suite.addTest(unittest.makeSuite(metakitSchemaTest))
suite.addTest(unittest.makeSuite(metakitClassicInitTest))
+ suite.addTest(unittest.makeSuite(metakitSessionTest))
return suite
if __name__ == '__main__':
diff --git a/test/test_mysql.py b/test/test_mysql.py
index 54f7dcf6ffb7a739390585c640f228acef401303..1fdc9417dfc628fa6f05eb5a32c3b9d415450eb9 100644 (file)
--- a/test/test_mysql.py
+++ b/test/test_mysql.py
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: test_mysql.py,v 1.7 2004-03-12 04:09:00 richard Exp $
+# $Id: test_mysql.py,v 1.8 2004-03-18 01:58:46 richard Exp $
import unittest, os, shutil, time, imp
ClassicInitTest.tearDown(self)
self.nuke_database()
+from session_common import RDBMSTest
+class mysqlSessionTest(mysqlOpener, RDBMSTest):
+ def setUp(self):
+ mysqlOpener.setUp(self)
+ RDBMSTest.setUp(self)
+ def tearDown(self):
+ RDBMSTest.tearDown(self)
+ mysqlOpener.tearDown(self)
+
def test_suite():
suite = unittest.TestSuite()
if not hasattr(backends, 'mysql'):
suite.addTest(unittest.makeSuite(mysqlROTest))
suite.addTest(unittest.makeSuite(mysqlSchemaTest))
suite.addTest(unittest.makeSuite(mysqlClassicInitTest))
+ suite.addTest(unittest.makeSuite(mysqlSessionTest))
return suite
if __name__ == '__main__':
index ac628be2e6ef16269c81cfcf411d2fef3e633919..81ce5dcb5b4b439c697231ddb778ca839c4e0882 100644 (file)
--- a/test/test_postgresql.py
+++ b/test/test_postgresql.py
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: test_postgresql.py,v 1.5 2004-03-12 04:09:00 richard Exp $
+# $Id: test_postgresql.py,v 1.6 2004-03-18 01:58:46 richard Exp $
import unittest
ClassicInitTest.tearDown(self)
postgresqlOpener.tearDown(self)
+from session_common import RDBMSTest
+class postgresqlSessionTest(postgresqlOpener, RDBMSTest):
+ def setUp(self):
+ postgresqlOpener.setUp(self)
+ RDBMSTest.setUp(self)
+ def tearDown(self):
+ RDBMSTest.tearDown(self)
+ postgresqlOpener.tearDown(self)
+
def test_suite():
suite = unittest.TestSuite()
if not hasattr(backends, 'postgresql'):
suite.addTest(unittest.makeSuite(postgresqlROTest))
suite.addTest(unittest.makeSuite(postgresqlSchemaTest))
suite.addTest(unittest.makeSuite(postgresqlClassicInitTest))
+ suite.addTest(unittest.makeSuite(postgresqlSessionTest))
return suite
diff --git a/test/test_sqlite.py b/test/test_sqlite.py
index 19e4277459ce349160ae44857ec6edd8c651cf59..85f5184eaa0f8c308373aa9d4611b16a4cf9124f 100644 (file)
--- a/test/test_sqlite.py
+++ b/test/test_sqlite.py
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: test_sqlite.py,v 1.3 2003-11-14 00:11:19 richard Exp $
+# $Id: test_sqlite.py,v 1.4 2004-03-18 01:58:46 richard Exp $
import unittest, os, shutil, time
class sqliteClassicInitTest(ClassicInitTest):
backend = 'sqlite'
+from session_common import RDBMSTest
+class sqliteSessionTest(sqliteOpener, RDBMSTest):
+ pass
+
def test_suite():
suite = unittest.TestSuite()
from roundup import backends
suite.addTest(unittest.makeSuite(sqliteROTest))
suite.addTest(unittest.makeSuite(sqliteSchemaTest))
suite.addTest(unittest.makeSuite(sqliteClassicInitTest))
+ suite.addTest(unittest.makeSuite(sqliteSessionTest))
return suite
if __name__ == '__main__':