332
335
if tcs.auth_scheme != 'basic':
335
auth_header = self.headers.get(tcs.auth_header_recv)
336
if auth_header and auth_header.lower().startswith('basic '):
337
raw_auth = auth_header[len('Basic '):]
338
user, password = raw_auth.decode('base64').split(':')
339
return tcs.authorized(user, password)
338
auth_header = self.headers.get(tcs.auth_header_recv, None)
340
scheme, raw_auth = auth_header.split(' ', 1)
341
if scheme.lower() == tcs.auth_scheme:
342
user, password = raw_auth.decode('base64').split(':')
343
return tcs.authorized(user, password)
343
347
def send_header_auth_reqed(self):
344
self.send_header(self.server.test_case_server.auth_header_sent,
345
'Basic realm="Thou should not pass"')
348
tcs = self.server.test_case_server
349
self.send_header(tcs.auth_header_sent,
350
'Basic realm="%s"' % tcs.auth_realm)
353
# FIXME: We should send an Authentication-Info header too when
354
# the autheticaion is succesful
348
356
class DigestAuthRequestHandler(AuthRequestHandler):
349
"""Implements the digest authentication of a request"""
357
"""Implements the digest authentication of a request.
359
We need persistence for some attributes and that can't be
360
achieved here since we get instantiated for each request. We
361
rely on the DigestAuthServer to take care of them.
351
364
def authorized(self):
352
365
tcs = self.server.test_case_server
353
366
if tcs.auth_scheme != 'digest':
356
auth_header = self.headers.get(tcs.auth_header_recv)
357
if auth_header and auth_header.lower().startswith('digest '):
358
raw_auth = auth_header[len('Basic '):]
359
user, password = raw_auth.decode('base64').split(':')
360
return tcs.authorized(user, password)
369
auth_header = self.headers.get(tcs.auth_header_recv, None)
370
if auth_header is None:
372
scheme, auth = auth_header.split(None, 1)
373
if scheme.lower() == tcs.auth_scheme:
374
auth_dict = urllib2.parse_keqv_list(urllib2.parse_http_list(auth))
376
return tcs.digest_authorized(auth_dict, self.command)
364
380
def send_header_auth_reqed(self):
365
self.send_header(self.server.test_case_server.auth_header_sent,
366
'Basic realm="Thou should not pass"')
381
tcs = self.server.test_case_server
382
header = 'Digest realm="%s", ' % tcs.auth_realm
383
header += 'nonce="%s", algorithm=%s, qop=auth' % (tcs.auth_nonce, 'MD5')
384
self.send_header(tcs.auth_header_sent,header)
368
387
class AuthServer(HttpServer):
369
388
"""Extends HttpServer with a dictionary of passwords.
374
393
Note that no users are defined by default, so add_user should
375
394
be called before issuing the first request.
378
397
# The following attributes should be set dy daughter classes
379
398
# and are used by AuthRequestHandler.
380
399
auth_header_sent = None
381
400
auth_header_recv = None
382
401
auth_error_code = None
402
auth_realm = "Thou should not pass"
384
404
def __init__(self, request_handler, auth_scheme):
385
405
HttpServer.__init__(self, request_handler)
396
416
self.password_of[user] = password
398
418
def authorized(self, user, password):
419
"""Check that the given user provided the right password"""
399
420
expected_password = self.password_of.get(user, None)
400
421
return expected_password is not None and password == expected_password
424
class DigestAuthServer(AuthServer):
425
"""A digest authentication server"""
427
auth_nonce = 'rRQ+Lp4uBAA=301b77beb156b6158b73dee026b8be23302292b4'
429
def __init__(self, request_handler, auth_scheme):
430
AuthServer.__init__(self, request_handler, auth_scheme)
432
def digest_authorized(self, auth, command):
433
realm = auth['realm']
434
if realm != self.auth_realm:
436
user = auth['username']
437
if not self.password_of.has_key(user):
439
algorithm= auth['algorithm']
440
if algorithm != 'MD5':
446
password = self.password_of[user]
448
# Recalculate the response_digest to compare with the one
450
A1 = '%s:%s:%s' % (user, realm, password)
451
A2 = '%s:%s' % (command, auth['uri'])
453
H = lambda x: md5.new(x).hexdigest()
454
KD = lambda secret, data: H("%s:%s" % (secret, data))
456
nonce = auth['nonce']
457
nonce_count = int(auth['nc'], 16)
459
ncvalue = '%08x' % nonce_count
461
cnonce = auth['cnonce']
462
noncebit = '%s:%s:%s:%s:%s' % (nonce, ncvalue, cnonce, qop, H(A2))
463
response_digest = KD(H(A1), noncebit)
465
return response_digest == auth['response']
403
467
class HTTPAuthServer(AuthServer):
404
468
"""An HTTP server requiring authentication"""
406
auth_header_sent = 'WWW-Authenticate'
407
auth_header_recv = 'Authorization'
408
auth_error_code = 401
470
def init_http_auth(self):
471
self.auth_header_sent = 'WWW-Authenticate'
472
self.auth_header_recv = 'Authorization'
473
self.auth_error_code = 401
411
476
class ProxyAuthServer(AuthServer):
412
477
"""A proxy server requiring authentication"""
414
proxy_requests = True
415
auth_header_sent = 'Proxy-Authenticate'
416
auth_header_recv = 'Proxy-Authorization'
417
auth_error_code = 407
479
def init_proxy_auth(self):
480
self.proxy_requests = True
481
self.auth_header_sent = 'Proxy-Authenticate'
482
self.auth_header_recv = 'Proxy-Authorization'
483
self.auth_error_code = 407
420
486
class HTTPBasicAuthServer(HTTPAuthServer):
423
489
def __init__(self):
424
490
HTTPAuthServer.__init__(self, BasicAuthRequestHandler, 'basic')
427
class HTTPDigestAuthServer(HTTPAuthServer):
491
self.init_http_auth()
494
class HTTPDigestAuthServer(DigestAuthServer, HTTPAuthServer):
428
495
"""An HTTP server requiring digest authentication"""
430
497
def __init__(self):
431
HTTPAuthServer.__init__(self, DigestAuthRequestHandler, 'digest')
498
DigestAuthServer.__init__(self, DigestAuthRequestHandler, 'digest')
499
self.init_http_auth()
434
502
class ProxyBasicAuthServer(ProxyAuthServer):
435
"""An proxy server requiring basic authentication"""
503
"""A proxy server requiring basic authentication"""
437
505
def __init__(self):
438
506
ProxyAuthServer.__init__(self, BasicAuthRequestHandler, 'basic')
441
class ProxyDigestAuthServer(ProxyAuthServer):
442
"""An proxy server requiring basic authentication"""
507
self.init_proxy_auth()
510
class ProxyDigestAuthServer(DigestAuthServer, ProxyAuthServer):
511
"""A proxy server requiring basic authentication"""
444
513
def __init__(self):
445
514
ProxyAuthServer.__init__(self, DigestAuthRequestHandler, 'digest')
515
self.init_proxy_auth()