From a0013ea1f8a2dd374d3afd6349b89d7c7ab01bce Mon Sep 17 00:00:00 2001 From: Mike Crute Date: Mon, 15 Feb 2010 22:12:10 -0500 Subject: Cleaning up authenticator and adding error checking. --- exchange/authenticators.py | 121 ++++++++++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 55 deletions(-) diff --git a/exchange/authenticators.py b/exchange/authenticators.py index bcc2e38..4cc4c18 100644 --- a/exchange/authenticators.py +++ b/exchange/authenticators.py @@ -8,70 +8,81 @@ Exchange Server Authenticators """ import urllib - -from copy import copy -from httplib import HTTPSConnection from Cookie import SimpleCookie +from httplib import HTTPSConnection +from datetime import datetime, timedelta +from exchange import AuthenticationException -class ExchangeAuthenticator(object): - - _auth_cookie = None - authenticated = False - username = None - - def __init__(self, web_server): - self.web_server = web_server - - def authenticate(self, username, password): - """ - Authenticate the user and cache the authentication so we aren't - hammering the auth server. Should hanlde expiration eventually. - """ - self.username = username - - if self._auth_cookie: - return self._auth_cookie - - self._auth_cookie = self._do_authentication(username, password) - return self._auth_cookie - - def _do_authentication(self, username, password): - raise NotImplemented - - def patch_headers(self, headers): - raise NotImplemented - - -class CookieAuthenticator(ExchangeAuthenticator): +class CookieSession(object): + """ + CookieSession implmenents the authentication protocol for the + Exchange web interface. + """ AUTH_DLL = "/exchweb/bin/auth/owaauth.dll" - def _do_authentication(self, username, password): - """ - Does a post to the authentication DLL to fetch a cookie for the session - this can then be passed back to the exchange API for servers that don't - support basicc HTTP auth. - """ - params = urllib.urlencode({ "destination": "https://%s/exchange" % (self.web_server), - "flags": "0", - "username": username, - "password": password, - "SubmitCreds": "Log On", - "trusted": "4" - }) - - conn = HTTPSConnection(self.web_server) + def __init__(self, server, username=None, password=None): + self.server = server + self.username = username + self.password = password + self.cache_timeout = timedelta(minutes=15) + self._token = None + self._last_modified = None + + @property + def has_expired(self): + if not self._last_modified: + return False + + update_delta = datetime.now() - self._last_modified + return (update_delta >= self.cache_timeout) + + @property + def is_authenticated(self): + return bool(self._token) and not self.has_expired + + @property + def token(self): + if not self.is_authenticated: + self._authenticate() + self._check_auth() + + return self._token + + @token.setter + def token(self, token): + self._last_modified = datetime.now() + self._token = token.strip() + + def _authenticate(self): + # Another idiotic Exchange issue, you MUST pass the redirect + # destination, and what's more if you don't pass it the name + # of the current server + /echange it will fail to auth + + params = urllib.urlencode({ + "destination": "https://{0}/exchange".format(self.server), + "username": self.username, + "password": self.password, + }) + + conn = HTTPSConnection(self.server) conn.request("POST", self.AUTH_DLL, params) response = conn.getresponse() cookie = SimpleCookie(response.getheader("set-cookie")) - cookie = ("sessionid=%s" % cookie["sessionid"].value, "cadata=%s" % cookie["cadata"].value) + self.token = cookie.output(attrs=[], header='', sep=';') + + def _check_auth(self): + # Grrr... Exchange is idiotic, instead of returning the correct error + # code with the auth they force you to follow a redirect. If you + # don't get a 200 (you'll get a 302) then you can rest assured that + # your authentication failed. - self.authenticated = True - return "; ".join(cookie) + conn = HTTPSConnection(self.server) + conn.request("GET", '/exchange/', + headers=dict(Cookie=self.token)) + resp = conn.getresponse() - def patch_headers(self, headers): - out_headers = copy(headers) - out_headers["Cookie"] = self._auth_cookie - return out_headers + if not resp.status == 200: + raise AuthenticationException -- cgit v1.2.3