diff options
author | Damien Churchill <damoc@gmail.com> | 2009-08-11 23:39:58 +0000 |
---|---|---|
committer | Damien Churchill <damoc@gmail.com> | 2009-08-11 23:39:58 +0000 |
commit | 3e8c17b071e131aa6ff050e6a5785bd7240e53ae (patch) | |
tree | 7f0e8c37a4287eb5116e7918c65e9e4fd6c0bf1e /deluge/ui/web/auth.py | |
parent | 1b17d66576ec692562e501af54b489b2fb258ede (diff) | |
download | deluge-3e8c17b071e131aa6ff050e6a5785bd7240e53ae.tar.gz deluge-3e8c17b071e131aa6ff050e6a5785bd7240e53ae.tar.bz2 deluge-3e8c17b071e131aa6ff050e6a5785bd7240e53ae.zip |
complete the new auth system using cookies & sessions
Diffstat (limited to 'deluge/ui/web/auth.py')
-rw-r--r-- | deluge/ui/web/auth.py | 130 |
1 files changed, 114 insertions, 16 deletions
diff --git a/deluge/ui/web/auth.py b/deluge/ui/web/auth.py index 6422625c5..9a8aee94b 100644 --- a/deluge/ui/web/auth.py +++ b/deluge/ui/web/auth.py @@ -40,18 +40,47 @@ AUTH_LEVEL_ADMIN = 10 AUTH_LEVEL_DEFAULT = AUTH_LEVEL_NORMAL +class AuthError(Exception): + """ + An exception that might be raised when checking a request for + authentication. + """ + pass + import time import random import hashlib import logging from twisted.internet.defer import Deferred +from twisted.internet.task import LoopingCall from deluge import component from deluge.ui.web.json_api import JSONComponent, export log = logging.getLogger(__name__) +def make_checksum(session_id): + return reduce(lambda x,y:x+y, map(ord, session_id)) + +def get_session_id(session_id): + """ + Checks a session id against its checksum + """ + if not session_id: + return None + + try: + checksum = int(session_id[-4:]) + session_id = session_id[:-4] + + if checksum == make_checksum(session_id): + return session_id + return None + except Exception, e: + log.exception(e) + return None + class Auth(JSONComponent): """ The component that implements authentification into the JSON interface. @@ -59,13 +88,31 @@ class Auth(JSONComponent): def __init__(self): super(Auth, self).__init__("Auth") + self.worker = LoopingCall(self._clean_sessions) + self.worker.start(5) - def _create_session(self, login='admin'): + def _clean_sessions(self): + config = component.get("DelugeWeb").config + session_ids = config["sessions"].keys() + + now = time.gmtime() + for session_id in session_ids: + session = config["sessions"][session_id] + + if "expires" not in session: + del config["sessions"][session_id] + continue + + if time.gmtime(session["expires"]) < now: + del config["sessions"][session_id] + continue + + def _create_session(self, request, login='admin'): """ Creates a new session. :keyword login: the username of the user logging in, currently \ - only for future use. + only for future use currently. :type login: string """ m = hashlib.md5() @@ -75,6 +122,16 @@ class Auth(JSONComponent): m.update(m.hexdigest()) session_id = m.hexdigest() + expires = int(time.time()) + 3600 + expires_str = time.strftime('%a, %d %b %Y %H:%M:%S UTC', + time.gmtime(expires)) + + checksum = str(make_checksum(session_id)) + + print 'Adding cookie' + request.addCookie('_session_id', session_id + checksum, + path="/json", expires=expires_str) + log.debug("Creating session for %s", login) config = component.get("DelugeWeb").config @@ -82,9 +139,54 @@ class Auth(JSONComponent): config.config["sessions"] = {} config["sessions"][session_id] = { - "login": login + "login": login, + "level": AUTH_LEVEL_ADMIN, + "expires": expires } - return session_id + return True + + def check_request(self, request, method=None, level=None): + """ + Check to ensure that a request is authorised to call the specified + method of authentication level. + + :param request: The HTTP request in question + :type request: twisted.web.http.Request + :keyword method: Check the specified method + :type method: function + :keyword level: Check the specified auth level + :type level: integer + + :raises: Exception + """ + + config = component.get("DelugeWeb").config + session_id = get_session_id(request.getCookie("_session_id")) + + if session_id not in config["sessions"]: + auth_level = AUTH_LEVEL_NONE + session_id = None + else: + auth_level = config["sessions"][session_id]["level"] + + if method: + if not hasattr(method, "_json_export"): + raise Exception("Not an exported method") + + method_level = getattr(method, "_json_auth_level") + if method_level is None: + raise Exception("Method has no auth level") + + level = method_level + + if level is None: + raise Exception("No level specified to check against") + + request.auth_level = auth_level + request.session_id = session_id + + if auth_level < level: + raise AuthError("Not authenticated") @export def change_password(self, new_password): @@ -104,25 +206,21 @@ class Auth(JSONComponent): config["pwd_sha1"] = s.hexdigest() d.callback(True) return d - - @export - def check_session(self, session_id): + @export(AUTH_LEVEL_NONE) + def check_session(self, session_id=None): """ Check a session to see if it's still valid. - :param session_id: the id for the session to remove - :type session_id: string :returns: True if the session is valid, False if not. :rtype: booleon """ d = Deferred() - config = component.get("DelugeWeb").config - d.callback(session_id in config["sessions"]) + d.callback(__request__.session_id is not None) return d @export - def delete_session(self, session_id): + def delete_session(self): """ Removes a session. @@ -131,11 +229,11 @@ class Auth(JSONComponent): """ d = Deferred() config = component.get("DelugeWeb").config - del config["sessions"][session_id] + del config["sessions"][__request__.session_id] d.callback(True) return d - @export + @export(AUTH_LEVEL_NONE) def login(self, password): """ Test a password to see if it's valid. @@ -177,7 +275,7 @@ class Auth(JSONComponent): m.update(password) if m.digest() == decodestring(config["old_pwd_md5"]): # We have a match, so we can create and return a session id. - d.callback(self._create_session()) + d.callback(self._create_session(__request__)) # We also want to move the password over to sha1 and remove # the old passwords from the config file. @@ -193,7 +291,7 @@ class Auth(JSONComponent): s.update(password) if s.hexdigest() == config["pwd_sha1"]: # We have a match, so we can create and return a session id. - d.callback(self._create_session()) + d.callback(self._create_session(__request__)) else: # Can't detect which method we should be using so just deny |