summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--exchange/authenticators.py121
1 files 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
8""" 8"""
9 9
10import urllib 10import urllib
11
12from copy import copy
13from httplib import HTTPSConnection
14from Cookie import SimpleCookie 11from Cookie import SimpleCookie
12from httplib import HTTPSConnection
13from datetime import datetime, timedelta
14from exchange import AuthenticationException
15 15
16 16
17class ExchangeAuthenticator(object): 17class CookieSession(object):
18 18 """
19 _auth_cookie = None 19 CookieSession implmenents the authentication protocol for the
20 authenticated = False 20 Exchange web interface.
21 username = None 21 """
22
23 def __init__(self, web_server):
24 self.web_server = web_server
25
26 def authenticate(self, username, password):
27 """
28 Authenticate the user and cache the authentication so we aren't
29 hammering the auth server. Should hanlde expiration eventually.
30 """
31 self.username = username
32
33 if self._auth_cookie:
34 return self._auth_cookie
35
36 self._auth_cookie = self._do_authentication(username, password)
37 return self._auth_cookie
38
39 def _do_authentication(self, username, password):
40 raise NotImplemented
41
42 def patch_headers(self, headers):
43 raise NotImplemented
44
45
46class CookieAuthenticator(ExchangeAuthenticator):
47 22
48 AUTH_DLL = "/exchweb/bin/auth/owaauth.dll" 23 AUTH_DLL = "/exchweb/bin/auth/owaauth.dll"
49 24
50 def _do_authentication(self, username, password): 25 def __init__(self, server, username=None, password=None):
51 """ 26 self.server = server
52 Does a post to the authentication DLL to fetch a cookie for the session 27 self.username = username
53 this can then be passed back to the exchange API for servers that don't 28 self.password = password
54 support basicc HTTP auth. 29 self.cache_timeout = timedelta(minutes=15)
55 """ 30 self._token = None
56 params = urllib.urlencode({ "destination": "https://%s/exchange" % (self.web_server), 31 self._last_modified = None
57 "flags": "0", 32
58 "username": username, 33 @property
59 "password": password, 34 def has_expired(self):
60 "SubmitCreds": "Log On", 35 if not self._last_modified:
61 "trusted": "4" 36 return False
62 }) 37
63 38 update_delta = datetime.now() - self._last_modified
64 conn = HTTPSConnection(self.web_server) 39 return (update_delta >= self.cache_timeout)
40
41 @property
42 def is_authenticated(self):
43 return bool(self._token) and not self.has_expired
44
45 @property
46 def token(self):
47 if not self.is_authenticated:
48 self._authenticate()
49 self._check_auth()
50
51 return self._token
52
53 @token.setter
54 def token(self, token):
55 self._last_modified = datetime.now()
56 self._token = token.strip()
57
58 def _authenticate(self):
59 # Another idiotic Exchange issue, you MUST pass the redirect
60 # destination, and what's more if you don't pass it the name
61 # of the current server + /echange it will fail to auth
62
63 params = urllib.urlencode({
64 "destination": "https://{0}/exchange".format(self.server),
65 "username": self.username,
66 "password": self.password,
67 })
68
69 conn = HTTPSConnection(self.server)
65 conn.request("POST", self.AUTH_DLL, params) 70 conn.request("POST", self.AUTH_DLL, params)
66 response = conn.getresponse() 71 response = conn.getresponse()
67 72
68 cookie = SimpleCookie(response.getheader("set-cookie")) 73 cookie = SimpleCookie(response.getheader("set-cookie"))
69 cookie = ("sessionid=%s" % cookie["sessionid"].value, "cadata=%s" % cookie["cadata"].value) 74 self.token = cookie.output(attrs=[], header='', sep=';')
75
76 def _check_auth(self):
77 # Grrr... Exchange is idiotic, instead of returning the correct error
78 # code with the auth they force you to follow a redirect. If you
79 # don't get a 200 (you'll get a 302) then you can rest assured that
80 # your authentication failed.
70 81
71 self.authenticated = True 82 conn = HTTPSConnection(self.server)
72 return "; ".join(cookie) 83 conn.request("GET", '/exchange/',
84 headers=dict(Cookie=self.token))
85 resp = conn.getresponse()
73 86
74 def patch_headers(self, headers): 87 if not resp.status == 200:
75 out_headers = copy(headers) 88 raise AuthenticationException
76 out_headers["Cookie"] = self._auth_cookie
77 return out_headers