source: sage/server/notebook/guard.py @ 4985:66bd0a306506

Revision 4985:66bd0a306506, 10.4 KB checked in by William Stein <wstein@…>, 6 years ago (diff)

fix some bugs.

Line 
1#####################################################################
2# Copyright (C) 2007 Alex Clemesha <clemesha@gmail.com>
3#
4#  Distributed under the terms of the GNU General Public License (GPL)
5#                  http://www.gnu.org/licenses/
6#####################################################################
7#
8# This code was inspired by the following sources:
9# * The file 'guard.py' included in the Nevow Web Framework http://divmod.org/trac/wiki/DivmodNevow
10# * The file 'guard.py' used in the Stiq website code http://www.stiq.it
11
12#twisted modules
13from twisted.python import log, components
14from twisted.internet import task, defer
15from twisted.cred.error import UnauthorizedLogin
16from twisted.cred import credentials
17from zope.interface import Interface, implements
18from twisted.web2 import iweb
19from twisted.web2 import server
20
21#standard library
22import random
23import time
24import md5
25
26
27class Session(object):
28    """A single users session, defined by the uid.
29    """
30    def __init__(self, uid, sessionManager):
31        self.uid = uid #the cookie, a unique identifier (md5 hash)
32        self.creationTime = self.lastAccessed = time.time()
33        self.sessionManager = sessionManager
34        self.authenticatedAs = None
35        #self.setLifetime(60)
36   
37    def set_authCreds(self, creds):
38        self.authenticatedAs = creds
39
40    def get_authCreds(self):
41        return self.authenticatedAs
42
43    def get_uid(self):
44        return self.uid
45
46    def touch(self):
47        self.lastAccessed = time.time()
48
49    def expire(self):
50        return defer.maybeDeferred(self.sessionManager.expiredSession, self)
51
52
53class SessionsManager(object):
54    """Manages all logged in users' sessions.
55    """
56    tickTime = 10000 #SESSION_CLEAN_FREQUENCY
57    sessionLifetime = 1000000 #TRANSIENT_SESSION_LIFETIME
58    sessionPersistentLifetime = 1000000 #PERSISTENT_SESSION_LIFETIME
59   
60    sessionFactory = Session
61   
62    def __init__(self):
63        self.sessions = {}
64        self.tick = task.LoopingCall(self._tick)
65
66    def createSession(self):
67        session = self.sessionFactory(self.createSessionID(), self)
68        uid = session.get_uid()
69        self.sessions[uid] = session
70        log.msg('Session %r created' % uid)
71        #log.msg('All sessions %r' % self.sessions)
72        if not self.tick.running and len(self.sessions) > 0:
73            self.tick.start(self.tickTime)
74        return session, uid
75   
76    def expiredSession(self, session):
77        uid = session.get_uid()
78        if uid in self.sessions:
79            log.msg('Session %r expired' % uid)
80            self.sessions.pop(uid)
81        if self.tick.running and len(self.sessions) == 0:
82            self.tick.stop()
83
84    def getSession(self, uid):
85        session = self.sessions.get(uid)
86        if session:
87            session.touch()
88        return session
89
90    def createSessionID(self):
91        """Generate a new session ID."""
92        data = "%s_%s" % (str(random.random()) , str(time.time()))
93        return md5.new(data).hexdigest()
94
95    def _tick(self):
96        """Remove expired sessions.
97        """
98        now = time.time()
99        for uid, session in self.sessions.items():
100            age = now - session.lastAccessed
101            max = self.sessionLifetime
102            #if session.persistent:
103            #    max = self.sessionPersistentLifetime
104            if age > max:
105                log.msg('Session %r expired' % uid)
106                self.sessions[uid].expire()
107        if not self.sessions and self.tick.running:
108            self.tick.stop()
109
110class MindManager(object):
111    """Might want to use this"""
112    def __init__(self, uid): 
113        self.uid = uid #uid is the session id (the cookie)
114
115class MySessionWrapper(object):
116    implements(iweb.IResource)
117   
118    cookieManager = None
119    mindFactory = MindManager
120    sessionManager = SessionsManager()
121
122    # The interface to cred for when logging into the portal
123    credInterface = iweb.IResource
124
125    def __init__(self, portal):
126        self.portal = portal
127       
128    def renderHTTP(self, request):
129        """When, if ever, would this get called?
130        """
131        log.msg("=== renderHTTP ===")
132        d = defer.maybeDeferred(self._delegate, request, [])
133        def _cb(resource, request):
134            res = iweb.IResource(resource)
135            return res.renderHTTP(request)
136        d.addCallback(_cb, request)
137        return d
138
139    def locateChild(self, request, segments):
140        """Serve Resources depending on users Authentication status.
141       
142        Inital logic occurs here to decide the
143        authentication status of a given user.
144        """
145        log.msg("=== locateChild 'guard.py' ===")
146        log.msg("=== %s ==" % request.args)
147        #log.msg("request.args: %s, segments: %s" % (str(request.args), segments))
148        #see if the user already has a session going
149        if segments and segments[0] == "login":
150            #get the username and password in the postdata
151            #the callback function needs no args because the parsing
152            #of the POSTData just updates the request args.
153            l = server.parsePOSTData(request)
154            l.addCallback(lambda _: self.requestPasswordAuthentication(request, segments))
155            return l
156        session = self.getSession(request)
157        if session is None:
158            return self.requestAnonymousAuthentication(request, segments)
159        else:
160            if segments and segments[0] == "logout":
161                log.msg("=== logout ===")
162                return self.logout(session, request, segments)
163            else:
164                log.msg("session found ... locateResource")
165                creds = session.get_authCreds()
166                return self.locateResource(request, segments, session, creds)
167
168    def locateResource(self, request, segments, session, creds):
169        """Locate the resource for an authenticated session.
170
171        This method is used to actually get the resource that
172        was requested after the request is passed through locateChild.
173
174        It is in locateChild where the users session is checked.
175        """
176        log.msg("=== locateResource 'myguard.py' ===")
177        def _success(avatar, request, segments):
178            iface, rsrc, logout = avatar
179            return rsrc, segments
180        #mind = self.mindFactory(request, creds)
181        mind = [session.get_uid(), request.args, segments]
182        d = self.portal.login(creds, mind, self.credInterface)
183        d.addCallback(_success, request, segments)
184        d.addErrback(self._loginFailure, request, segments, "Incorrect login.")
185        return d
186
187    def _loginFailure(self, avatar, request, segments, reason): 
188        return self, ()
189
190    def _delegate(self, request, segments):
191        """Identify session by the http cookie.
192
193        If no session exists, create a new session defined
194        by a uid, and then set that uid as the cookie.
195        """
196        cookie = request.headers.getHeader('cookie')
197        if cookie in self.sessions:
198            session = self.sessions[cookie]
199            return self.checkLogin(request, session, segments)
200        else:
201            return self.sessionManager.createSession(request, segments), ()
202
203    def requestPasswordAuthentication(self, request, segments):
204        """
205        Try to athenticate with a username and password from
206        a web login form.  Depending on the given credentials,
207        return a custom 'view' of protected resources.
208
209        """
210        log.msg("=== requestPasswordAuthentication ===")
211        creds = self.getCredentials(request)
212        session, newCookie = self.sessionManager.createSession()
213        mind = [newCookie, request.args, segments] 
214        d = self.portal.login(creds, mind, self.credInterface)
215        d.addCallback(self._loginSuccess, session, creds, segments)
216        return d
217
218    def requestAnonymousAuthentication(self, request, segments):
219        """
220        Anonymous authentication, the user can only see
221        non-protected resources.
222        """
223        log.msg("=== requestAnonymousAuthentication ===")
224        def _success(avatar, request, segments):
225            iface, resource, logout = avatar
226            return resource, segments
227        creds = credentials.Anonymous() #anonymous user.
228        session, newCookie = self.sessionManager.createSession()
229        session.set_authCreds(creds)
230        mind = [newCookie, None, None] 
231        d = self.portal.login(creds, mind, self.credInterface)
232        d.addCallback(_success, request, segments)
233        #d.addErrback(self._loginFailure, request, segments, 'Anonymous access not allowed.')
234        return d
235
236    def getSession(self, request):
237        """Get a user's session defined by the requests cookie.
238
239        Pull the cookie out of the http header and
240        pass it to the session manager, will handles
241        find the users actuall session object.
242        """
243        cookie = request.headers.getHeader('cookie')
244        if cookie:
245            cookie = cookie[0].value
246        log.msg("cookie from header: %s"%cookie) 
247        session = self.sessionManager.getSession(cookie)
248        return session
249 
250    def getCredentials(self, request):
251        """Get username, password from post args
252        or if they dont exist we have an anonymous
253        session
254        """
255        username = request.args.get('email', [''])[0]
256        password = request.args.get('password', [''])[0]
257        return credentials.UsernamePassword(username, password)
258
259    def _loginSuccess(self, (iface, rsrc, logout), session, creds, segments):
260        """Return the Root Page after log in success.
261       
262        Also saved the credentials that the user used to log in
263        to later associate these credentials with the users session.
264        """
265        log.msg("=== _loginSuccess ===")
266        log.msg("resource: %s " % rsrc)
267        log.msg("segments: %s " % segments)
268        #put the users creds in the session object
269        session.set_authCreds(creds)
270        return rsrc, () #segments
271   
272    def _loginFailure(self, *x): #TODO   
273        print x
274                 
275    def incorrectLoginError(self, error, ctx, segments, loginFailure):
276        pass
277
278    def logout(self, session, request, segments):
279        """Destroy the users session.
280
281        This pops the uid that defines the users session
282        out of the sessions dict.  A new anonymous session
283        is immediatly created for this user.
284        """
285        log.msg("=== logout ===")
286        self.sessionManager.expiredSession(session)
287        return self.requestAnonymousAuthentication(request, segments)
288
289
Note: See TracBrowser for help on using the repository browser.