commit | author | age
|
299b4c
|
1 |
import datetime |
b95a59
|
2 |
from codecs import utf_8_decode |
CM |
3 |
from codecs import utf_8_encode |
0ee58d
|
4 |
import os |
cd1198
|
5 |
import time |
b95a59
|
6 |
|
a5b033
|
7 |
from paste.request import get_cookies |
CM |
8 |
from paste.auth import auth_tkt |
|
9 |
|
|
10 |
from zope.interface import implements |
|
11 |
|
cb5426
|
12 |
from repoze.who.interfaces import IIdentifier |
a5b033
|
13 |
|
798feb
|
14 |
_NOW_TESTING = None # unit tests can replace |
TS |
15 |
def _now(): #pragma NO COVERAGE |
|
16 |
if _NOW_TESTING is not None: |
|
17 |
return _NOW_TESTING |
|
18 |
return datetime.datetime.now() |
|
19 |
|
a5b033
|
20 |
class AuthTktCookiePlugin(object): |
CM |
21 |
|
|
22 |
implements(IIdentifier) |
779caf
|
23 |
|
CM |
24 |
userid_type_decoders = { |
|
25 |
'int':int, |
b95a59
|
26 |
'unicode':lambda x: utf_8_decode(x)[0], |
779caf
|
27 |
} |
CM |
28 |
|
|
29 |
userid_type_encoders = { |
|
30 |
int: ('int', str), |
a2c030
|
31 |
long: ('int', str), |
b95a59
|
32 |
unicode: ('unicode', lambda x: utf_8_encode(x)[0]), |
779caf
|
33 |
} |
a5b033
|
34 |
|
CM |
35 |
def __init__(self, secret, cookie_name='auth_tkt', |
cd1198
|
36 |
secure=False, include_ip=False, |
a6f6dc
|
37 |
timeout=None, reissue_time=None, userid_checker=None): |
a5b033
|
38 |
self.secret = secret |
CM |
39 |
self.cookie_name = cookie_name |
|
40 |
self.include_ip = include_ip |
|
41 |
self.secure = secure |
cd1198
|
42 |
if timeout and ( (not reissue_time) or (reissue_time > timeout) ): |
CM |
43 |
raise ValueError('When timeout is specified, reissue_time must ' |
|
44 |
'be set to a lower value') |
|
45 |
self.timeout = timeout |
|
46 |
self.reissue_time = reissue_time |
a6f6dc
|
47 |
self.userid_checker = userid_checker |
a5b033
|
48 |
|
CM |
49 |
# IIdentifier |
|
50 |
def identify(self, environ): |
|
51 |
cookies = get_cookies(environ) |
|
52 |
cookie = cookies.get(self.cookie_name) |
|
53 |
|
|
54 |
if cookie is None or not cookie.value: |
40a968
|
55 |
return None |
a5b033
|
56 |
|
CM |
57 |
if self.include_ip: |
|
58 |
remote_addr = environ['REMOTE_ADDR'] |
|
59 |
else: |
|
60 |
remote_addr = '0.0.0.0' |
|
61 |
|
|
62 |
try: |
|
63 |
timestamp, userid, tokens, user_data = auth_tkt.parse_ticket( |
|
64 |
self.secret, cookie.value, remote_addr) |
|
65 |
except auth_tkt.BadTicket: |
a6f6dc
|
66 |
return None |
CM |
67 |
|
|
68 |
if self.userid_checker and not self.userid_checker(userid): |
cd1198
|
69 |
return None |
CM |
70 |
|
|
71 |
if self.timeout and ( (timestamp + self.timeout) < time.time() ): |
40a968
|
72 |
return None |
779caf
|
73 |
|
CM |
74 |
userid_typename = 'userid_type:' |
|
75 |
user_data_info = user_data.split('|') |
|
76 |
for datum in filter(None, user_data_info): |
|
77 |
if datum.startswith(userid_typename): |
|
78 |
userid_type = datum[len(userid_typename):] |
|
79 |
decoder = self.userid_type_decoders.get(userid_type) |
|
80 |
if decoder: |
|
81 |
userid = decoder(userid) |
a5b033
|
82 |
|
CM |
83 |
environ['REMOTE_USER_TOKENS'] = tokens |
|
84 |
environ['REMOTE_USER_DATA'] = user_data |
|
85 |
environ['AUTH_TYPE'] = 'cookie' |
56d0c5
|
86 |
|
a5b033
|
87 |
identity = {} |
CM |
88 |
identity['timestamp'] = timestamp |
cb5426
|
89 |
identity['repoze.who.userid'] = userid |
a5b033
|
90 |
identity['tokens'] = tokens |
CM |
91 |
identity['userdata'] = user_data |
|
92 |
return identity |
|
93 |
|
299b4c
|
94 |
def _get_cookies(self, environ, value, max_age=None): |
CM |
95 |
if max_age is not None: |
798feb
|
96 |
later = _now() + datetime.timedelta(seconds=int(max_age)) |
299b4c
|
97 |
# Wdy, DD-Mon-YY HH:MM:SS GMT |
CM |
98 |
expires = later.strftime('%a, %d %b %Y %H:%M:%S') |
|
99 |
# the Expires header is *required* at least for IE7 (IE7 does |
|
100 |
# not respect Max-Age) |
|
101 |
max_age = "; Max-Age=%s; Expires=%s" % (max_age, expires) |
|
102 |
else: |
|
103 |
max_age = '' |
|
104 |
|
a5b033
|
105 |
cur_domain = environ.get('HTTP_HOST', environ.get('SERVER_NAME')) |
CM |
106 |
wild_domain = '.' + cur_domain |
|
107 |
cookies = [ |
299b4c
|
108 |
('Set-Cookie', '%s="%s"; Path=/%s' % ( |
CM |
109 |
self.cookie_name, value, max_age)), |
|
110 |
('Set-Cookie', '%s="%s"; Path=/; Domain=%s%s' % ( |
|
111 |
self.cookie_name, value, cur_domain, max_age)), |
|
112 |
('Set-Cookie', '%s="%s"; Path=/; Domain=%s%s' % ( |
|
113 |
self.cookie_name, value, wild_domain, max_age)) |
a5b033
|
114 |
] |
CM |
115 |
return cookies |
519300
|
116 |
|
CM |
117 |
# IIdentifier |
|
118 |
def forget(self, environ, identity): |
|
119 |
# return a set of expires Set-Cookie headers |
798feb
|
120 |
return self._get_cookies(environ, 'INVALID', 0) |
a5b033
|
121 |
|
CM |
122 |
# IIdentifier |
|
123 |
def remember(self, environ, identity): |
|
124 |
if self.include_ip: |
|
125 |
remote_addr = environ['REMOTE_ADDR'] |
|
126 |
else: |
|
127 |
remote_addr = '0.0.0.0' |
|
128 |
|
|
129 |
cookies = get_cookies(environ) |
|
130 |
old_cookie = cookies.get(self.cookie_name) |
|
131 |
existing = cookies.get(self.cookie_name) |
|
132 |
old_cookie_value = getattr(existing, 'value', None) |
299b4c
|
133 |
max_age = identity.get('max_age', None) |
a5b033
|
134 |
|
CM |
135 |
timestamp, userid, tokens, userdata = None, '', '', '' |
|
136 |
|
|
137 |
if old_cookie_value: |
|
138 |
try: |
|
139 |
timestamp,userid,tokens,userdata = auth_tkt.parse_ticket( |
|
140 |
self.secret, old_cookie_value, remote_addr) |
|
141 |
except auth_tkt.BadTicket: |
|
142 |
pass |
|
143 |
|
cb5426
|
144 |
who_userid = identity['repoze.who.userid'] |
CM |
145 |
who_tokens = identity.get('tokens', '') |
|
146 |
who_userdata = identity.get('userdata', '') |
779caf
|
147 |
|
CM |
148 |
encoding_data = self.userid_type_encoders.get(type(who_userid)) |
|
149 |
if encoding_data: |
|
150 |
encoding, encoder = encoding_data |
|
151 |
who_userid = encoder(who_userid) |
|
152 |
who_userdata = 'userid_type:%s' % encoding |
a5b033
|
153 |
|
CM |
154 |
if not isinstance(tokens, basestring): |
|
155 |
tokens = ','.join(tokens) |
cb5426
|
156 |
if not isinstance(who_tokens, basestring): |
CM |
157 |
who_tokens = ','.join(who_tokens) |
a5b033
|
158 |
old_data = (userid, tokens, userdata) |
cb5426
|
159 |
new_data = (who_userid, who_tokens, who_userdata) |
a5b033
|
160 |
|
cd1198
|
161 |
if old_data != new_data or (self.reissue_time and |
CM |
162 |
( (timestamp + self.reissue_time) < time.time() )): |
a5b033
|
163 |
ticket = auth_tkt.AuthTicket( |
CM |
164 |
self.secret, |
cb5426
|
165 |
who_userid, |
a5b033
|
166 |
remote_addr, |
cb5426
|
167 |
tokens=who_tokens, |
CM |
168 |
user_data=who_userdata, |
a5b033
|
169 |
cookie_name=self.cookie_name, |
CM |
170 |
secure=self.secure) |
|
171 |
new_cookie_value = ticket.cookie_value() |
519300
|
172 |
|
CM |
173 |
cur_domain = environ.get('HTTP_HOST', environ.get('SERVER_NAME')) |
|
174 |
wild_domain = '.' + cur_domain |
a5b033
|
175 |
if old_cookie_value != new_cookie_value: |
519300
|
176 |
# return a set of Set-Cookie headers |
299b4c
|
177 |
return self._get_cookies(environ, new_cookie_value, max_age) |
a5b033
|
178 |
|
CM |
179 |
def __repr__(self): |
394ea6
|
180 |
return '<%s %s>' % (self.__class__.__name__, |
TS |
181 |
id(self)) #pragma NO COVERAGE |
a5b033
|
182 |
|
515c69
|
183 |
def _bool(value): |
TS |
184 |
if isinstance(value, basestring): |
|
185 |
return value.lower() in ('yes', 'true', '1') |
|
186 |
return value |
|
187 |
|
|
188 |
def make_plugin(secret=None, |
0ee58d
|
189 |
secretfile=None, |
a5b033
|
190 |
cookie_name='auth_tkt', |
0ee58d
|
191 |
secure=False, |
TS |
192 |
include_ip=False, |
cd1198
|
193 |
timeout=None, |
CM |
194 |
reissue_time=None, |
a6f6dc
|
195 |
userid_checker=None, |
0ee58d
|
196 |
): |
a6f6dc
|
197 |
from repoze.who.utils import resolveDotted |
0ee58d
|
198 |
if (secret is None and secretfile is None): |
TS |
199 |
raise ValueError("One of 'secret' or 'secretfile' must not be None.") |
|
200 |
if (secret is not None and secretfile is not None): |
|
201 |
raise ValueError("Specify only one of 'secret' or 'secretfile'.") |
|
202 |
if secretfile: |
|
203 |
secretfile = os.path.abspath(os.path.expanduser(secretfile)) |
|
204 |
if not os.path.exists(secretfile): |
|
205 |
raise ValueError("No such 'secretfile': %s" % secretfile) |
|
206 |
secret = open(secretfile).read().strip() |
cd1198
|
207 |
if timeout: |
CM |
208 |
timeout = int(timeout) |
|
209 |
if reissue_time: |
|
210 |
reissue_time = int(reissue_time) |
a6f6dc
|
211 |
if userid_checker is not None: |
CM |
212 |
userid_checker = resolveDotted(userid_checker) |
cd1198
|
213 |
plugin = AuthTktCookiePlugin(secret, |
CM |
214 |
cookie_name, |
|
215 |
_bool(secure), |
|
216 |
_bool(include_ip), |
|
217 |
timeout, |
|
218 |
reissue_time, |
a6f6dc
|
219 |
userid_checker, |
cd1198
|
220 |
) |
a5b033
|
221 |
return plugin |
CM |
222 |
|