~ubuntu-branches/ubuntu/precise/juju/precise-updates

« back to all changes in this revision

Viewing changes to juju/providers/orchestra/digestauth.py

  • Committer: Package Import Robot
  • Author(s): Clint Byrum
  • Date: 2011-12-01 13:15:51 UTC
  • mfrom: (1.1.3)
  • Revision ID: package-import@ubuntu.com-20111201131551-by7xfvgv16rdkmtl
Tags: 0.5+bzr424-0ubuntu1
* New upstream snapshot.
* d/control: No longer build-dep or depend on python-argparse, as
  it seems to cause fits with pkg_resources.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from hashlib import md5
 
2
from urllib2 import parse_http_list, parse_keqv_list
 
3
from uuid import uuid4
 
4
 
 
5
from twisted.internet import reactor
 
6
from twisted.internet.ssl import ClientContextFactory
 
7
from twisted.python.failure import Failure
 
8
from twisted.web.client import HTTPClientFactory, HTTPPageGetter
 
9
from twisted.web.error import Error
 
10
 
 
11
from juju.errors import ProviderError
 
12
 
 
13
 
 
14
def _parse_auth_info(auth_info):
 
15
    method, info_str = auth_info.split(' ', 1)
 
16
    if method != "Digest":
 
17
        raise ProviderError("Unknown authentication method: %s" % method)
 
18
    items = parse_http_list(info_str)
 
19
    info = parse_keqv_list(items)
 
20
 
 
21
    try:
 
22
        qop = info["qop"]
 
23
        realm = info["realm"]
 
24
        nonce = info["nonce"]
 
25
    except KeyError as e:
 
26
        raise ProviderError(
 
27
            "Authentication request missing required key: %s" % e)
 
28
    algorithm = info.get("algorithm", "MD5")
 
29
    if algorithm != "MD5":
 
30
        raise ProviderError("Unsupported digest algorithm: %s" % algorithm)
 
31
    if "auth" not in qop.split(","):
 
32
        raise ProviderError("Unsupported quality-of-protection: %s" % qop)
 
33
    return realm, nonce, "auth", algorithm
 
34
 
 
35
 
 
36
def _digest_squish(*fields):
 
37
    return md5(":".join(fields)).hexdigest()
 
38
 
 
39
 
 
40
class DigestAuthenticator(object):
 
41
 
 
42
    def __init__(self, username, password):
 
43
        self._user = username
 
44
        self._pass = password
 
45
        self._nonce_count = 0
 
46
 
 
47
    def authenticate(self, method, url, auth_info):
 
48
        realm, nonce, qop, algorithm = _parse_auth_info(auth_info)
 
49
        ha1 = _digest_squish(self._user, realm, self._pass)
 
50
        ha2 = _digest_squish(method, url)
 
51
        cnonce = str(uuid4())
 
52
        self._nonce_count += 1
 
53
        nc = "%08x" % self._nonce_count
 
54
        response = _digest_squish(ha1, nonce, nc, cnonce, qop, ha2)
 
55
        return (
 
56
            'Digest username="%s", realm="%s", nonce="%s", uri="%s", '
 
57
            'algorithm="%s", response="%s", qop="%s", nc="%s", cnonce="%s"'
 
58
            % (self._user, realm, nonce, url, algorithm, response, qop,
 
59
               nc, cnonce))
 
60
 
 
61
 
 
62
def _connect(factory):
 
63
    if factory.scheme == 'https':
 
64
        reactor.connectSSL(
 
65
            factory.host, factory.port, factory, ClientContextFactory())
 
66
    else:
 
67
        reactor.connectTCP(factory.host, factory.port, factory)
 
68
 
 
69
 
 
70
class _AuthPageGetter(HTTPPageGetter):
 
71
 
 
72
    handleStatus_204 = lambda self: self.handleStatus_200()
 
73
 
 
74
    def handleStatus_401(self):
 
75
        if not self.factory.authenticated:
 
76
            (auth_info,) = self.headers["www-authenticate"]
 
77
            self.factory.authenticate(auth_info)
 
78
            _connect(self.factory)
 
79
        else:
 
80
            self.handleStatusDefault()
 
81
            self.factory.noPage(Failure(Error(self.status, self.message)))
 
82
        self.quietLoss = True
 
83
        self.transport.loseConnection()
 
84
 
 
85
 
 
86
class _AuthClientFactory(HTTPClientFactory):
 
87
 
 
88
    protocol = _AuthPageGetter
 
89
    authenticated = False
 
90
 
 
91
    def __init__(self, url, authenticator, **kwargs):
 
92
        HTTPClientFactory.__init__(self, url, **kwargs)
 
93
        self._authenticator = authenticator
 
94
 
 
95
    def authenticate(self, auth_info):
 
96
        self.headers["authorization"] = self._authenticator.authenticate(
 
97
            self.method, self.url, auth_info)
 
98
        self.authenticated = True
 
99
 
 
100
 
 
101
def get_page_auth(url, authenticator, method="GET", postdata=None):
 
102
    factory = _AuthClientFactory(
 
103
        url, authenticator, method=method, postdata=postdata)
 
104
    _connect(factory)
 
105
    return factory.deferred