removed main.cfg, not used anywhere
[limo.git] / session.py
blob3444b0252559505fed58d122842123a835e08086
2 import sys, os, re, zlib, hashlib, base64, datetime, random
3 from db import Query, Model, safe, FastQuery
4 from model import LimoModel
5 from template import urldecode
6 from errors import *
7 from limoutil import *
8 from settings import Settings
10 class Session():
11 def __init__(self, request, response):
12 self.sha = None
13 sha = request.cookies.get('sha','').value
14 if len(sha) == 40 and sha == safe(sha):
15 self._loadsha(sha)
16 if self.sha is None:
17 self._newsha()
18 assert self.sha is not None
19 response.cookies['sha'] = self.sha
20 # response.cookies['sha'].expires = datetime.datetime.utcnow()+datetime.timedelta(minutes=Settings.sessionTimeout)
21 response.cookies['sha'].max_age = Settings.sessionTimeout*60
22 response.cookies['sha'].path = "/"
23 # log the visit
24 visit = self.logVisit(url=request['REQUEST_URI'], remote=request["REMOTE_ADDR"]+":"+request.get("REMOTE_PORT","-1"))
25 self._dirty = False
27 def _newsha(self):
28 sha = hashlib.sha1()
29 sha.update(str(datetime.datetime.now()))
30 sha.update(str(os.urandom(8192)))
31 sha = sha.hexdigest()
32 FastQuery("begin work; insert into sessions (sha, data) values ('%s', '{}'); commit;" % sha)
33 if not self._loadsha(sha):
34 raise Exception("Critical Error: I ran an insert/commit, and then couldnt find the data I committed. Database corruption?")
35 assert self.sha is not None
36 self._dirty = True
37 return sha
39 def _loadsha(self, sha):
40 for row in FastQuery("select sid,sha,uid,data from sessions where sha = '%s'" % sha):
41 try:
42 assert row[3][:4] == 'eJyr', "Session data does not start with zlib magic bytes: %s" % row[3][:4]
43 self.data = eval(zlib.decompress(base64.b64decode(row[3])))
44 except Exception, e:
45 self.data = {}
46 self._dirty = True
47 self.sid = int(row[0])
48 self.sha = str(row[1])
49 if row[2] == 0:
50 self.user = None
51 else:
52 self.user = User(row[2])
53 return True
54 return False
56 def logVisit(self, url='', remote=''):
57 if Settings.detectSessionHijack:
58 for row in FastQuery("select remote from visits where sid = %d order by entered desc limit 1" % self.sid):
59 if row[0].split(':')[0] != remote.split(':')[0]:
60 raise HTTPStatusError(500, "Session Hijack detected, session token '%s' was used on '%s' originally and now on '%s'"
61 % (self.sha, row[0], remote))
62 FastQuery("begin work; insert into visits (sid, url, remote) values (%d, '%s', '%s'); commit"
63 % (self.sid, safe(url), safe(remote)))
65 def login(self, login, passwd, save=False):
66 assert len(login) > 0, "Login required"
67 assert len(passwd) > 0, "Password required"
68 for row in FastQuery("select uid from users where login = '%s' or email = '%s' and passwd = '%s'" % (login, login, passwd)):
69 self.uid = row[0]
70 self._dirty = True
71 if save:
72 self.save()
73 return
74 raise Exception("User not found")
76 def logout(self, save=False):
77 self.uid = 0
78 self._dirty = True
79 if save:
80 self.save()
82 def save(self):
83 if self.user is None:
84 uid = 0
85 else:
86 uid = self.user.uid
87 if self._dirty:
88 data = base64.b64encode(zlib.compress(str(self.data)))
89 FastQuery("begin work; update sessions set data = '%s', uid = %d where sid = %d; commit" % (data, uid, self.sid))
90 self._dirty = False
92 def hasRole(self, name):
93 if self.user is None:
94 return False;
95 return self.user.hasRole(name)
97 def getMessages(self):
98 return self.get('messages','')
99 def addMessage(self, msg):
100 self['messages'] = self.getMessages() + msg
101 def clearMessages(self):
102 del self['messages']
104 # Session objects behave like a hash table
105 def get(self, key, default):
106 return self.data.get(key, default)
107 def __getitem__(self, key):
108 return self.data.__getitem__(key)
109 def __setitem__(self, key, val):
110 self.data.__setitem__(key, val)
111 self._dirty = True
112 def __delitem__(self, key):
113 self.data.__delitem__(key)
114 self._dirty = True
116 def __str__(self):
117 return "{sid: %s, sha: %s, user: %s, data: %s}" % (self.sid, self.sha, str(self.user), str(self.data))
118 def __unicode__(self):
119 return unicode(self.__str__())
121 class User():
122 def __init__(self, uid):
123 getuser=Query("""
124 select u.*, r1.name as ur_name, r2.name as gr_name from users u
125 left join user_roles ur on ur.uid = u.uid
126 left join roles r1 on r1.rid = ur.rid
127 left join group_users ug on ug.uid = u.uid
128 left join group_roles gr on gr.gid = ug.gid
129 left join roles r2 on r2.rid = gr.rid
130 where u.uid = %d
131 """ % uid)
132 if len(getuser) > 0:
133 self.uid = int(getuser['uid'][0])
134 (self.name, self.login, self.email, self.theme)\
135 = [urldecode(x) for x in getuser['name, login, email, theme'][0]]
136 self.roles\
137 = [str(r.role_name) for r in getuser.Query("""
138 select distinct role_name from (
139 select ur_name as role_name from __self__
140 union
141 select gr_name as role_name from __self__
142 ) where role_name <> 'None'
143 """)]
144 else:
145 raise Exception("No such user id: %d" % int(uid))
147 def setPassword(self, passwd):
148 sha = Model.uid(passwd)
149 Query("update users set passwd = '%s' where uid = %d" % (passwd, self.uid))
151 def setTheme(self, theme):
152 self.theme = theme
153 Query("update users set theme = '%s' where uid = %d" % (theme, self.uid))
155 def hasRole(self, role):
156 role = str(role)
157 for r in self.roles:
158 if r == role:
159 return True
160 return False
162 def __str__(self):
163 return "{uid: %s, name: %s, login: %s, email: %s, roles: %s}" % (self.uid, self.name, self.login, self.email, str(self.roles))
165 @staticmethod
166 def getUser(login, password):
167 password = Model.uid(password)
168 q = Query("select uid from users where login = '%s' and passwd = '%s'" % (login, password))
169 if len(q) is 1:
170 return User(q[0].uid)
171 else:
172 return None
174 @staticmethod
175 def createUser(name, login, passwd, email, active=1):
176 LimoModel()\
177 .defaultUser(safe(name), safe(login), safe(passwd), safe(email), int(active))\
178 .assignUserToGroup(login, 'users')\
179 .execute()
180 for row in Query("select uid from users where name = '%s' and login = '%s'" % (name, login)):
181 return row.uid
182 return False