summaryrefslogtreecommitdiff
path: root/exchange/authenticators.py
blob: 4cc4c18c86765d852ef540c696f567ec6d9bff9c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# vim: set filencoding=utf8
"""
Exchange Server Authenticators

@author: Mike Crute (mcrute@gmail.com)
@organization: SoftGroup Interactive, Inc.
@date: April 26, 2009
"""

import urllib
from Cookie import SimpleCookie
from httplib import HTTPSConnection
from datetime import datetime, timedelta
from exchange import AuthenticationException


class CookieSession(object):
    """
    CookieSession implmenents the authentication protocol for the
    Exchange web interface.
    """

    AUTH_DLL = "/exchweb/bin/auth/owaauth.dll"

    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"))
        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.

        conn = HTTPSConnection(self.server)
        conn.request("GET", '/exchange/',
                        headers=dict(Cookie=self.token))
        resp = conn.getresponse()

        if not resp.status == 200:
            raise AuthenticationException