Ticket #3067: session3.patch
| File session3.patch, 23.3 kB (added by jonas, 2 years ago) |
|---|
-
trac/env.py
273 273 if not cnx: 274 274 cnx = self.get_db_cnx() 275 275 cursor = cnx.cursor() 276 cursor.execute("SELECT DISTINCT s.sid, n.va r_value, e.var_value "276 cursor.execute("SELECT DISTINCT s.sid, n.value, e.value " 277 277 "FROM session AS s " 278 " LEFT JOIN session AS n ON (n.sid=s.sid "279 " AND n.authenticated=1 AND n.var_name = 'name') "280 " LEFT JOIN session AS e ON (e.sid=s.sid "281 " AND e.authenticated=1 AND e. var_name = 'email') "278 " LEFT JOIN session_attribute AS n ON (n.sid=s.sid " 279 " and n.authenticated=1 AND n.name = 'name') " 280 " LEFT JOIN session_attribute AS e ON (e.sid=s.sid " 281 " AND e.authenticated=1 AND e.name = 'email') " 282 282 "WHERE s.authenticated=1 ORDER BY s.sid") 283 283 for username,name,email in cursor: 284 284 yield username, name, email -
trac/db_default.py
17 17 from trac.db import Table, Column, Index 18 18 19 19 # Database version identifier. Used for automatic upgrades. 20 db_version = 1 720 db_version = 18 21 21 22 22 def __mkreports(reports): 23 23 """Utility function used to create report data in same syntax as the … … 46 46 Column('name'), 47 47 Column('ipnr'), 48 48 Column('time', type='int')], 49 Table('session', key=('sid', 'authenticated' , 'var_name'))[49 Table('session', key=('sid', 'authenticated'))[ 50 50 Column('sid'), 51 51 Column('authenticated', type='int'), 52 Column('var_name'), 53 Column('var_value')], 52 Column('last_visit', type='int'), 53 Index(['last_visit']), 54 Index(['authenticated'])], 55 Table('session_attribute', key=('sid', 'authenticated', 'name'))[ 56 Column('sid'), 57 Column('authenticated', type='int'), 58 Column('name'), 59 Column('value')], 54 60 55 61 # Attachments 56 62 Table('attachment', key=('type', 'id', 'filename'))[ … … 119 125 Column('field'), 120 126 Column('oldvalue'), 121 127 Column('newvalue'), 122 Index(['ticket', 'time'])], 128 Index(['ticket']), 129 Index(['time'])], 123 130 Table('ticket_custom', key=('ticket', 'name'))[ 124 131 Column('ticket', type='int'), 125 132 Column('name'), -
trac/tests/env.py
26 26 def test_get_known_users(self): 27 27 """Testing env.get_known_users""" 28 28 cursor = self.db.cursor() 29 cursor.execute("INSERT INTO session " 30 "VALUES ('123',0,'email','a@example.com')") 31 cursor.executemany("INSERT INTO session VALUES (%s,1,%s,%s)", 32 [('tom', 'name', 'Tom'), 33 ('tom', 'email', 'tom@example.com'), 34 ('joe', 'email', 'joe@example.com'), 35 ('jane', 'name', 'Jane')]) 29 cursor.executemany("INSERT INTO session VALUES (%s,%s,0)", 30 [('123', 0),('tom', 1), ('joe', 1), ('jane', 1)]) 31 cursor.executemany("INSERT INTO session_attribute VALUES (%s,%s,%s,%s)", 32 [('123', 0, 'email', 'a@example.com'), 33 ('tom', 1, 'name', 'Tom'), 34 ('tom', 1, 'email', 'tom@example.com'), 35 ('joe', 1, 'email', 'joe@example.com'), 36 ('jane', 1, 'name', 'Jane')]) 36 37 users = {} 37 38 for username,name,email in self.env.get_known_users(self.db): 38 39 users[username] = (name, email) -
trac/upgrades/db18.py
1 from trac.db import Table, Column, Index, DatabaseManager 2 3 def do_upgrade(env, ver, cursor): 4 cursor.execute("CREATE TEMP TABLE session_old AS SELECT * FROM session") 5 cursor.execute("DROP TABLE session") 6 cursor.execute("CREATE TEMP TABLE ticket_change_old AS SELECT * FROM ticket_change") 7 cursor.execute("DROP TABLE ticket_change") 8 9 # A slightly more denormalized session schema where the 'last_visit' values are 10 # stored in a column for performance reasons 11 tables = [Table('session', key=('sid', 'authenticated'))[ 12 Column('sid'), 13 Column('authenticated', type='int'), 14 Column('last_visit', type='int'), 15 Index(['last_visit']), 16 Index(['authenticated'])], 17 Table('session_attribute', key=('sid', 'authenticated', 'name'))[ 18 Column('sid'), 19 Column('authenticated', type='int'), 20 Column('name'), 21 Column('value')], 22 Table('ticket_change', key=('ticket', 'time', 'field'))[ 23 Column('ticket', type='int'), 24 Column('time', type='int'), 25 Column('author'), 26 Column('field'), 27 Column('oldvalue'), 28 Column('newvalue'), 29 Index(['ticket']), 30 Index(['time'])]] 31 32 db_connector, _ = DatabaseManager(env)._get_connector() 33 for table in tables: 34 for stmt in db_connector.to_sql(table): 35 cursor.execute(stmt) 36 37 # Add an index to the temporary table to speed up the conversion 38 cursor.execute("CREATE INDEX session_old_sid_idx ON session_old(sid)") 39 # Insert the sessions into the new table 40 db = env.get_db_cnx() 41 cursor.execute("INSERT INTO session (sid, last_visit, authenticated) " 42 "SELECT distinct s.sid,COALESCE(%s,0),s.authenticated " 43 "FROM session_old AS s LEFT JOIN session_old AS s2 " 44 "ON (s.sid=s2.sid AND s2.var_name='last_visit') " 45 "WHERE s.sid IS NOT NULL" 46 % db.cast('s2.var_value', 'int')) 47 cursor.execute("INSERT INTO session_attribute " 48 "(sid, authenticated, name, value) " 49 "SELECT s.sid, s.authenticated, s.var_name, s.var_value " 50 "FROM session_old s " 51 "WHERE s.var_name <> 'last_visit' AND s.sid IS NOT NULL") 52 53 # Insert ticket change data into the new table 54 cursor.execute("INSERT INTO ticket_change " 55 "(ticket, time, author, field, oldvalue, newvalue) " 56 "SELECT ticket, time, author, field, oldvalue, newvalue " 57 "FROM ticket_change_old") 58 59 -
trac/web/tests/session.py
65 65 authenticated session when the user logs in. 66 66 """ 67 67 cursor = self.db.cursor() 68 cursor.execute("INSERT INTO session VALUES ('123456', 0, 'foo', 'bar')") 69 68 cursor.execute("INSERT INTO session VALUES ('123456', 0, 0)") 70 69 incookie = Cookie() 71 70 incookie['trac_session'] = '123456' 72 71 outcookie = Cookie() … … 93 92 session['foo'] = 'bar' 94 93 session.save() 95 94 cursor = self.db.cursor() 96 cursor.execute("SELECT var_value FROM session WHERE sid='123456' AND " 97 "authenticated=0 AND var_name='foo'") 95 cursor.execute("SELECT value FROM session_attribute WHERE sid='123456'") 98 96 self.assertEqual('bar', cursor.fetchone()[0]) 99 97 100 98 def test_modify_anonymous_session_var(self): … … 103 101 accordingly for an anonymous session. 104 102 """ 105 103 cursor = self.db.cursor() 106 cursor.execute("INSERT INTO session VALUES ('123456', 0, 'foo', 'bar')") 107 104 cursor.execute("INSERT INTO session VALUES ('123456', 0, 0)") 105 cursor.execute("INSERT INTO session_attribute VALUES " 106 "('123456', 0, 'foo', 'bar')") 108 107 incookie = Cookie() 109 108 incookie['trac_session'] = '123456' 110 109 req = Mock(authname='anonymous', base_path='/', incookie=incookie, … … 113 112 self.assertEqual('bar', session['foo']) 114 113 session['foo'] = 'baz' 115 114 session.save() 116 cursor.execute("SELECT var_value FROM session WHERE sid='123456' AND " 117 "authenticated=0 AND var_name='foo'") 115 cursor.execute("SELECT value FROM session_attribute WHERE sid='123456'") 118 116 self.assertEqual('baz', cursor.fetchone()[0]) 119 117 120 118 def test_delete_anonymous_session_var(self): … … 123 121 for an anonymous session. 124 122 """ 125 123 cursor = self.db.cursor() 126 cursor.execute("INSERT INTO session VALUES ('123456', 0, 'foo', 'bar')") 127 124 cursor.execute("INSERT INTO session VALUES ('123456', 0, 0)") 125 cursor.execute("INSERT INTO session_attribute VALUES " 126 "('123456', 0, 'foo', 'bar')") 128 127 incookie = Cookie() 129 128 incookie['trac_session'] = '123456' 130 129 req = Mock(authname='anonymous', base_path='/', incookie=incookie, … … 133 132 self.assertEqual('bar', session['foo']) 134 133 del session['foo'] 135 134 session.save() 136 cursor.execute("SELECT COUNT(*) FROM session WHERE sid='123456' AND"137 " authenticated=0 AND var_name='foo'")135 cursor.execute("SELECT COUNT(*) FROM session_attribute " 136 "WHERE sid='123456' AND name='foo'") 138 137 self.assertEqual(0, cursor.fetchone()[0]) 139 138 140 139 def test_purge_anonymous_session(self): … … 143 142 """ 144 143 cursor = self.db.cursor() 145 144 cursor.execute("INSERT INTO session " 146 "VALUES ('987654', 0, 'last_visit',%s)",145 "VALUES ('987654', 0, %s)", 147 146 (time.time() - PURGE_AGE - 3600,)) 147 cursor.execute("INSERT INTO session_attribute VALUES " 148 "('987654', 0, 'foo', 'bar')") 148 149 149 150 # We need to modify a different session to trigger the purging 150 151 incookie = Cookie() … … 169 170 # Make sure the session has data so that it doesn't get dropped 170 171 cursor = self.db.cursor() 171 172 cursor.execute("INSERT INTO session " 172 "VALUES ('123456', 0, 'last_visit',%s)",173 "VALUES ('123456', 0, %s)", 173 174 (int(now - UPDATE_INTERVAL - 3600),)) 175 cursor.execute("INSERT INTO session_attribute VALUES " 176 "('123456', 0, 'foo', 'bar')") 174 177 175 178 incookie = Cookie() 176 179 incookie['trac_session'] = '123456' 177 180 req = Mock(authname='anonymous', base_path='/', incookie=incookie, 178 181 outcookie=Cookie()) 179 182 session = Session(self.env, req) 183 del session['foo'] 180 184 session.save() 181 185 182 186 cursor.execute("SELECT COUNT(*) FROM session WHERE sid='123456' AND " … … 193 197 session['foo'] = 'bar' 194 198 session.save() 195 199 cursor = self.db.cursor() 196 cursor.execute("SELECT va r_value FROM session WHERE sid='john' AND"197 " authenticated=1 AND var_name='foo'")200 cursor.execute("SELECT value FROM session_attribute WHERE sid='john'" 201 "AND name='foo'") 198 202 self.assertEqual('bar', cursor.fetchone()[0]) 199 203 200 204 def test_modify_authenticated_session_var(self): … … 203 207 accordingly for an authenticated session. 204 208 """ 205 209 cursor = self.db.cursor() 206 cursor.execute("INSERT INTO session VALUES ('john', 1, 'foo', 'bar')") 210 cursor.execute("INSERT INTO session VALUES ('john', 1, 0)") 211 cursor.execute("INSERT INTO session_attribute VALUES " 212 "('john', 1, 'foo', 'bar')") 207 213 208 214 req = Mock(authname='john', base_path='/', incookie=Cookie()) 209 215 session = Session(self.env, req) 210 216 self.assertEqual('bar', session['foo']) 211 217 session['foo'] = 'baz' 212 218 session.save() 213 cursor.execute("SELECT va r_value FROM session WHERE sid='john' AND"214 " authenticated=1 AND var_name='foo'")219 cursor.execute("SELECT value FROM session_attribute " 220 "WHERE sid='john' AND name='foo'") 215 221 self.assertEqual('baz', cursor.fetchone()[0]) 216 222 217 223 def test_delete_authenticated_session_var(self): … … 220 226 for an authenticated session. 221 227 """ 222 228 cursor = self.db.cursor() 223 cursor.execute("INSERT INTO session VALUES ('john', 1, 'foo', 'bar')") 229 cursor.execute("INSERT INTO session VALUES ('john', 1, 0)") 230 cursor.execute("INSERT INTO session_attribute VALUES " 231 "('john', 1, 'foo', 'bar')") 224 232 225 233 req = Mock(authname='john', base_path='/', incookie=Cookie()) 226 234 session = Session(self.env, req) 227 235 self.assertEqual('bar', session['foo']) 228 236 del session['foo'] 229 237 session.save() 230 cursor.execute("SELECT COUNT(*) FROM session WHERE sid='john' AND"231 " authenticated=1 AND var_name='foo'")238 cursor.execute("SELECT COUNT(*) FROM session_attribute " 239 "WHERE sid='john' AND name='foo'") 232 240 self.assertEqual(0, cursor.fetchone()[0]) 233 241 234 242 def test_update_session(self): … … 240 248 241 249 # Make sure the session has data so that it doesn't get dropped 242 250 cursor = self.db.cursor() 243 cursor.execute many("INSERT INTO session VALUES ('123456', 0, %s, %s)",244 [('last_visit', int(now - UPDATE_INTERVAL - 3600)),245 ('foo', 'bar')])251 cursor.execute("INSERT INTO session VALUES ('123456', 0, 1)") 252 cursor.execute("INSERT INTO session_attribute VALUES " 253 "('123456', 0, 'foo', 'bar')") 246 254 247 255 incookie = Cookie() 248 256 incookie['trac_session'] = '123456' … … 254 262 255 263 self.assertEqual(PURGE_AGE, outcookie['trac_session']['expires']) 256 264 257 cursor.execute("SELECT var_valueFROM session WHERE sid='123456' AND "258 "authenticated=0 AND var_name='last_visit'")265 cursor.execute("SELECT last_visit FROM session WHERE sid='123456' AND " 266 "authenticated=0") 259 267 self.assertAlmostEqual(now, int(cursor.fetchone()[0]), -1) 260 268 261 269 -
trac/web/session.py
3 3 # Copyright (C) 2004-2005 Edgewall Software 4 4 # Copyright (C) 2004 Daniel Lundin <daniel@edgewall.com> 5 5 # Copyright (C) 2004-2005 Christopher Lenz <cmlenz@gmx.de> 6 # Copyright (C) 2006 Jonas Borgström <jonas@edgewall.com> 6 7 # All rights reserved. 7 8 # 8 9 # This software is licensed as described in the file COPYING, which … … 34 35 self.env = env 35 36 self.req = req 36 37 self.sid = None 38 self.last_visit = 0 39 self._new = True 37 40 self._old = {} 38 41 if req.authname == 'anonymous': 39 42 if not req.incookie.has_key(COOKIE_KEY): … … 58 61 db = self.env.get_db_cnx() 59 62 cursor = db.cursor() 60 63 self.sid = sid 61 cursor.execute("SELECT var_name,var_valueFROM session "64 cursor.execute("SELECT last_visit FROM session " 62 65 "WHERE sid=%s AND authenticated=%s", 63 66 (sid, int(authenticated))) 67 row = cursor.fetchone() 68 if not row: 69 return 70 self._new = False 71 self.last_visit = int(row[0]) 72 cursor.execute("SELECT name,value FROM session_attribute " 73 "WHERE sid=%s and authenticated=%s", 74 (sid, int(authenticated))) 64 75 for name, value in cursor: 65 76 self[name] = value 66 77 self._old.update(self) 67 78 68 79 # Refresh the session cookie if this is the first visit since over a day 69 if not authenticated and self. has_key('last_visit'):70 if time.time() - int(self['last_visit'])> UPDATE_INTERVAL:80 if not authenticated and self.last_visit: 81 if time.time() - self.last_visit > UPDATE_INTERVAL: 71 82 self.bake_cookie() 72 83 73 84 def change_sid(self, new_sid): … … 78 89 return 79 90 db = self.env.get_db_cnx() 80 91 cursor = db.cursor() 81 cursor.execute("SELECT sid FROM session WHERE sid=%s " 82 "AND authenticated=0", (new_sid,)) 92 cursor.execute("SELECT sid FROM session WHERE sid=%s", (new_sid,)) 83 93 if cursor.fetchone(): 84 94 raise TracError(Markup('Session "%s" already exists.<br />' 85 95 'Please choose a different session ID.', … … 87 97 self.env.log.debug('Changing session ID %s to %s' % (self.sid, new_sid)) 88 98 cursor.execute("UPDATE session SET sid=%s WHERE sid=%s " 89 99 "AND authenticated=0", (new_sid, self.sid)) 100 cursor.execute("UPDATE session_attribute SET sid=%s " 101 "WHERE sid=%s and authenticated=0", 102 (new_sid, self.sid)) 90 103 db.commit() 91 104 self.sid = new_sid 92 105 self.bake_cookie() … … 107 120 # simply delete the anonymous session 108 121 cursor.execute("DELETE FROM session WHERE sid=%s " 109 122 "AND authenticated=0", (sid,)) 123 cursor.execute("DELETE FROM session_attribute WHERE sid=%s " 124 "AND authenticated=0", (sid,)) 110 125 else: 111 126 # Otherwise, update the session records so that the session ID is 112 127 # the user name, and the authenticated flag is set … … 116 131 cursor.execute("UPDATE session SET sid=%s,authenticated=1 " 117 132 "WHERE sid=%s AND authenticated=0", 118 133 (self.req.authname, sid)) 134 cursor.execute("UPDATE session_attribute SET sid=%s," 135 "authenticated=1 WHERE sid=%s", 136 (self.req.authname, sid)) 137 self._new = False 119 138 db.commit() 120 139 121 140 self.sid = sid … … 127 146 # persist it 128 147 return 129 148 130 changed = False131 now = int(time.time())132 133 if self.req.authname == 'anonymous':134 # Update the session last visit time if it is over an hour old,135 # so that session doesn't get purged136 last_visit = int(self.get('last_visit', 0))137 if now - last_visit > UPDATE_INTERVAL:138 self.env.log.info("Refreshing session %s" % self.sid)139 self['last_visit'] = now140 141 # If the only data in the session is the last_visit time, it makes142 # no sense to keep the session around143 if len(self.items()) == 1:144 del self['last_visit']145 146 149 db = self.env.get_db_cnx() 147 150 cursor = db.cursor() 148 151 authenticated = int(self.req.authname != 'anonymous') 149 152 150 # Find all new or modified session variables and persist their values to 151 # the database 152 for k,v in self.items(): 153 if not self._old.has_key(k): 154 self.env.log.debug('Adding variable %s with value "%s" to ' 155 'session %s' % (k, v, 156 self.sid or self.req.authname)) 157 cursor.execute("INSERT INTO session VALUES(%s,%s,%s,%s)", 158 (self.sid, authenticated, k, v)) 159 changed = True 160 elif v != self._old[k]: 161 self.env.log.debug('Changing variable %s from "%s" to "%s" in ' 162 'session %s' % (k, self._old[k], v, 163 self.sid)) 164 cursor.execute("UPDATE session SET var_value=%s " 165 "WHERE sid=%s AND authenticated=%s " 166 "AND var_name=%s", (v, self.sid, authenticated, 167 k)) 168 changed = True 153 if self._new: 154 self._new = False 155 cursor.execute("INSERT INTO session (sid,last_visit,authenticated) " 156 "VALUES(%s,%s,%s)", (self.sid, 157 self.last_visit, 158 authenticated)) 159 if self._old.items() != self.items(): 160 attrs = [(self.sid, authenticated, k, v) for k, v in self.items()] 161 cursor.execute("DELETE FROM session_attribute WHERE sid=%s", 162 (self.sid,)) 163 self._old = dict(self.items()) 164 if attrs: 165 cursor.executemany("INSERT INTO session_attribute " 166 "(sid,authenticated,name,value) " 167 "VALUES(%s,%s,%s,%s)", attrs) 168 elif not authenticated: 169 # No need to keep around empty unauthenticated sessions 170 cursor.execute("DELETE FROM session " 171 "WHERE sid=%s AND authenticated=0" % (self.sid,)) 172 return 169 173 170 # Find all variables that have been deleted and also remove them from 171 # the database 172 for k in [k for k in self._old.keys() if not self.has_key(k)]: 173 self.env.log.debug('Deleting variable %s from session %s' 174 % (k, self.sid or self.req.authname)) 175 cursor.execute("DELETE FROM session WHERE sid=%s AND " 176 "authenticated=%s AND var_name=%s", 177 (self.sid, authenticated, k)) 178 changed = True 179 180 if changed: 174 now = int(time.time()) 175 # Update the session last visit time if it is over an hour old, 176 # so that session doesn't get purged 177 if now - self.last_visit > UPDATE_INTERVAL: 178 self.last_visit = now 179 self.env.log.info("Refreshing session %s" % self.sid) 180 cursor.execute('UPDATE session SET last_visit=%s ' 181 'WHERE sid=%s AND authenticated=%s', 182 (self.last_visit, self.sid, authenticated)) 181 183 # Purge expired sessions. We do this only when the session was 182 184 # changed as to minimize the purging. 183 185 mintime = now - PURGE_AGE 184 186 self.env.log.debug('Purging old, expired, sessions.') 185 cursor.execute("DELETE FROM session WHERE authenticated=0 AND " 186 "sid IN (SELECT sid FROM session WHERE " 187 "var_name='last_visit' AND var_value < %s)", 187 cursor.execute("DELETE FROM session_attribute " 188 "WHERE authenticated=0 AND sid " 189 "IN (SELECT sid FROM session WHERE " 190 "authenticated=0 AND last_visit < %s)", 188 191 (mintime,)) 189 190 db.commit() 192 cursor.execute("DELETE FROM session WHERE " 193 "authenticated=0 AND last_visit < %s", 194 (mintime,)) 195 db.commit()
