Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(62)

Side by Side Diff: third_party/twisted_8_1/twisted/web/woven/guard.py

Issue 12261012: Remove third_party/twisted_8_1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # -*- test-case-name: twisted.web.test.test_woven -*-
2
3 # Copyright (c) 2001-2004 Twisted Matrix Laboratories.
4 # See LICENSE for details.
5
6 #
7
8 """Resource protection for Woven. If you wish to use twisted.cred to protect
9 your Woven application, you are probably most interested in
10 L{UsernamePasswordWrapper}.
11 """
12
13 from __future__ import nested_scopes
14
15 __version__ = "$Revision: 1.34 $"[11:-2]
16
17 import random
18 import time
19 import md5
20 import urllib
21
22 # Twisted Imports
23
24 from twisted.python import log, components
25 from twisted.web.resource import Resource, IResource
26 from twisted.web.util import redirectTo, Redirect, DeferredResource
27 from twisted.web.static import addSlash
28 from twisted.internet import reactor
29 from twisted.cred.error import LoginFailed, UnauthorizedLogin
30
31 def _sessionCookie():
32 return md5.new("%s_%s" % (str(random.random()) , str(time.time()))).hexdiges t()
33
34 class GuardSession(components.Componentized):
35 """A user's session with a system.
36
37 This utility class contains no functionality, but is used to
38 represent a session.
39 """
40 def __init__(self, guard, uid):
41 """Initialize a session with a unique ID for that session.
42 """
43 components.Componentized.__init__(self)
44 self.guard = guard
45 self.uid = uid
46 self.expireCallbacks = []
47 self.checkExpiredID = None
48 self.setLifetime(60)
49 self.services = {}
50 self.portals = {}
51 self.touch()
52
53 def _getSelf(self, interface=None):
54 self.touch()
55 if interface is None:
56 return self
57 else:
58 return self.getComponent(interface)
59
60 # Old Guard interfaces
61
62 def clientForService(self, service):
63 x = self.services.get(service)
64 if x:
65 return x[1]
66 else:
67 return x
68
69 def setClientForService(self, ident, perspective, client, service):
70 if self.services.has_key(service):
71 p, c, i = self.services[service]
72 p.detached(c, ident)
73 del self.services[service]
74 else:
75 self.services[service] = perspective, client, ident
76 perspective.attached(client, ident)
77 # this return value is useful for services that need to do asynchronous
78 # stuff.
79 return client
80
81 # New Guard Interfaces
82
83 def resourceForPortal(self, port):
84 return self.portals.get(port)
85
86 def setResourceForPortal(self, rsrc, port, logout):
87 self.portalLogout(port)
88 self.portals[port] = rsrc, logout
89 return rsrc
90
91 def portalLogout(self, port):
92 p = self.portals.get(port)
93 if p:
94 r, l = p
95 try: l()
96 except: log.err()
97 del self.portals[port]
98
99 # timeouts and expiration
100
101 def setLifetime(self, lifetime):
102 """Set the approximate lifetime of this session, in seconds.
103
104 This is highly imprecise, but it allows you to set some general
105 parameters about when this session will expire. A callback will be
106 scheduled each 'lifetime' seconds, and if I have not been 'touch()'ed
107 in half a lifetime, I will be immediately expired.
108 """
109 self.lifetime = lifetime
110
111 def notifyOnExpire(self, callback):
112 """Call this callback when the session expires or logs out.
113 """
114 self.expireCallbacks.append(callback)
115
116 def expire(self):
117 """Expire/logout of the session.
118 """
119 log.msg("expired session %s" % self.uid)
120 del self.guard.sessions[self.uid]
121 for c in self.expireCallbacks:
122 try:
123 c()
124 except:
125 log.err()
126 self.expireCallbacks = []
127 if self.checkExpiredID:
128 self.checkExpiredID.cancel()
129 self.checkExpiredID = None
130
131 def touch(self):
132 self.lastModified = time.time()
133
134 def checkExpired(self):
135 self.checkExpiredID = None
136 # If I haven't been touched in 15 minutes:
137 if time.time() - self.lastModified > self.lifetime / 2:
138 if self.guard.sessions.has_key(self.uid):
139 self.expire()
140 else:
141 log.msg("no session to expire: %s" % self.uid)
142 else:
143 log.msg("session given the will to live for %s more seconds" % self. lifetime)
144 self.checkExpiredID = reactor.callLater(self.lifetime,
145 self.checkExpired)
146 def __getstate__(self):
147 d = self.__dict__.copy()
148 if d.has_key('checkExpiredID'):
149 del d['checkExpiredID']
150 return d
151
152 def __setstate__(self, d):
153 self.__dict__.update(d)
154 self.touch()
155 self.checkExpired()
156
157 INIT_SESSION = 'session-init'
158
159 def _setSession(wrap, req, cook):
160 req.session = wrap.sessions[cook]
161 req.getSession = req.session._getSelf
162
163 def urlToChild(request, *ar, **kw):
164 pp = request.prepath.pop()
165 orig = request.prePathURL()
166 request.prepath.append(pp)
167 c = '/'.join(ar)
168 if orig[-1] == '/':
169 # this SHOULD only happen in the case where the URL is just the hostname
170 ret = orig + c
171 else:
172 ret = orig + '/' + c
173 args = request.args.copy()
174 args.update(kw)
175 if args:
176 ret += '?'+urllib.urlencode(args)
177 return ret
178
179 def redirectToSession(request, garbage):
180 rd = Redirect(urlToChild(request, *request.postpath, **{garbage:1}))
181 rd.isLeaf = 1
182 return rd
183
184 SESSION_KEY='__session_key__'
185
186 class SessionWrapper(Resource):
187
188 sessionLifetime = 1800
189
190 def __init__(self, rsrc, cookieKey=None):
191 Resource.__init__(self)
192 self.resource = rsrc
193 if cookieKey is None:
194 cookieKey = "woven_session_" + _sessionCookie()
195 self.cookieKey = cookieKey
196 self.sessions = {}
197
198 def render(self, request):
199 return redirectTo(addSlash(request), request)
200
201 def getChild(self, path, request):
202 if not request.prepath:
203 return None
204 cookie = request.getCookie(self.cookieKey)
205 setupURL = urlToChild(request, INIT_SESSION, *([path]+request.postpath))
206 request.setupSessionURL = setupURL
207 request.setupSession = lambda: Redirect(setupURL)
208 if path.startswith(SESSION_KEY):
209 key = path[len(SESSION_KEY):]
210 if key not in self.sessions:
211 return redirectToSession(request, '__start_session__')
212 self.sessions[key].setLifetime(self.sessionLifetime)
213 if cookie == key:
214 # /sessionized-url/${SESSION_KEY}aef9c34aecc3d9148/foo
215 # ^
216 # we are this getChild
217 # with a matching cookie
218 return redirectToSession(request, '__session_just_started__')
219 else:
220 # We attempted to negotiate the session but failed (the user
221 # probably has cookies disabled): now we're going to return the
222 # resource we contain. In general the getChild shouldn't stop
223 # there.
224 # /sessionized-url/${SESSION_KEY}aef9c34aecc3d9148/foo
225 # ^ we are this getChild
226 # without a cookie (or with a mismatched cookie)
227 _setSession(self, request, key)
228 return self.resource
229 elif cookie in self.sessions:
230 # /sessionized-url/foo
231 # ^ we are this getChild
232 # with a session
233 _setSession(self, request, cookie)
234 return getResource(self.resource, path, request)
235 elif path == INIT_SESSION:
236 # initialize the session
237 # /sessionized-url/session-init
238 # ^ this getChild
239 # without a session
240 newCookie = _sessionCookie()
241 request.addCookie(self.cookieKey, newCookie, path="/")
242 sz = self.sessions[newCookie] = GuardSession(self, newCookie)
243 sz.checkExpired()
244 rd = Redirect(urlToChild(request, SESSION_KEY+newCookie,
245 *request.postpath))
246 rd.isLeaf = 1
247 return rd
248 else:
249 # /sessionized-url/foo
250 # ^ we are this getChild
251 # without a session
252 request.getSession = lambda interface=None: None
253 return getResource(self.resource, path, request)
254
255 def getResource(resource, path, request):
256 if resource.isLeaf:
257 request.postpath.insert(0, request.prepath.pop())
258 return resource
259 else:
260 return resource.getChildWithDefault(path, request)
261
262 INIT_PERSPECTIVE = 'perspective-init'
263 DESTROY_PERSPECTIVE = 'perspective-destroy'
264
265 from twisted.python import formmethod as fm
266 from twisted.web.woven import form
267
268
269 newLoginSignature = fm.MethodSignature(
270 fm.String("username", "",
271 "Username", "Your user name."),
272 fm.Password("password", "",
273 "Password", "Your password."),
274 fm.Submit("submit", choices=[("Login", "", "")], allowNone=1),
275 )
276
277 from twisted.cred.credentials import UsernamePassword, Anonymous
278
279 class UsernamePasswordWrapper(Resource):
280 """I bring a C{twisted.cred} Portal to the web. Use me to provide different Resources
281 (usually entire pages) based on a user's authentication details.
282
283 A C{UsernamePasswordWrapper} is a
284 L{Resource<twisted.web.resource.Resource>}, and is usually wrapped in a
285 L{SessionWrapper} before being inserted into the site tree.
286
287 The L{Realm<twisted.cred.portal.IRealm>} associated with your
288 L{Portal<twisted.cred.portal.Portal>} should be prepared to accept a
289 request for an avatar that implements the L{twisted.web.resource.IResource}
290 interface. This avatar should probably be something like a Woven
291 L{Page<twisted.web.woven.page.Page>}. That is, it should represent a whole
292 web page. Once you return this avatar, requests for it's children do not go
293 through guard.
294
295 If you want to determine what unauthenticated users see, make sure your
296 L{Portal<twisted.cred.portal.Portal>} has a checker associated that allows
297 anonymous access. (See L{twisted.cred.checkers.AllowAnonymousAccess})
298
299 """
300
301 def __init__(self, portal, callback=None, errback=None):
302 """Constructs a UsernamePasswordWrapper around the given portal.
303
304 @param portal: A cred portal for your web application. The checkers
305 associated with this portal must be able to accept username/password
306 credentials.
307 @type portal: L{twisted.cred.portal.Portal}
308
309 @param callback: Gets called after a successful login attempt.
310 A resource that redirects to "." will display the avatar resource.
311 If this parameter isn't provided, defaults to a standard Woven
312 "Thank You" page.
313 @type callback: A callable that accepts a Woven
314 L{model<twisted.web.woven.interfaces.IModel>} and returns a
315 L{IResource<twisted.web.resource.Resource>}.
316
317 @param errback: Gets called after a failed login attempt.
318 If this parameter is not provided, defaults to a the standard Woven
319 form error (i.e. The original form on a page of its own, with
320 errors noted.)
321 @type errback: A callable that accepts a Woven
322 L{model<twisted.web.woven.interfaces.IModel>} and returns a
323 L{IResource<twisted.web.resource.Resource>}.
324 """
325 Resource.__init__(self)
326 self.portal = portal
327 self.callback = callback
328 self.errback = errback
329
330 def _ebFilter(self, f):
331 f.trap(LoginFailed, UnauthorizedLogin)
332 raise fm.FormException(password="Login failed, please enter correct user name and password.")
333
334 def getChild(self, path, request):
335 s = request.getSession()
336 if s is None:
337 return request.setupSession()
338 if path == INIT_PERSPECTIVE:
339 def loginSuccess(result):
340 interface, avatarAspect, logout = result
341 s.setResourceForPortal(avatarAspect, self.portal, logout)
342
343 def triggerLogin(username, password, submit=None):
344 return self.portal.login(
345 UsernamePassword(username, password),
346 None,
347 IResource
348 ).addCallback(
349 loginSuccess
350 ).addErrback(
351 self._ebFilter
352 )
353
354 return form.FormProcessor(
355 newLoginSignature.method(
356 triggerLogin
357 ),
358 callback=self.callback,
359 errback=self.errback
360 )
361 elif path == DESTROY_PERSPECTIVE:
362 s.portalLogout(self.portal)
363 return Redirect(".")
364 else:
365 r = s.resourceForPortal(self.portal)
366 if r:
367 ## Delegate our getChild to the resource our portal says is the right one.
368 return getResource(r[0], path, request)
369 else:
370 return DeferredResource(
371 self.portal.login(Anonymous(), None, IResource
372 ).addCallback(
373 lambda (interface, avatarAspect, logout):
374 getResource(s.setResourceForPortal(avatarAspect,
375 self.portal, logout),
376 path, request)))
377
378
379
380 from twisted.web.woven import interfaces, utils
381 ## Dumb hack until we have an ISession and use interface-to-interface adaption
382 components.registerAdapter(utils.WovenLivePage, GuardSession, interfaces.IWovenL ivePage)
383
OLDNEW
« no previous file with comments | « third_party/twisted_8_1/twisted/web/woven/form.py ('k') | third_party/twisted_8_1/twisted/web/woven/input.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698