def __init__(self, header_val): data = parse_http_dict(header_val) self.realm = data.get('realm') self.username = data.get('username') self.nonce = data.get('nonce') self.uri = data.get('uri') self.method = data.get('method') self.response = data.get('response') self.algorithm = data.get('algorithm', 'MD5').upper() self.cnonce = data.get('cnonce') self.opaque = data.get('opaque') self.qop = data.get('qop', '').lower() self.nonce_count = data.get('nc') if self.algorithm not in self.valid_algorithms: raise HTTPSimpleResponse(httplib.BAD_REQUEST, 'Unsupported digest algorithm') if not (self.username and self.realm and self.nonce and self.uri and self.response): raise HTTPSimpleResponse(httplib.BAD_REQUEST, 'Digest algorithm required fields missing') if self.qop: if self.qop not in self.valid_qops: raise HTTPSimpleResponse(httplib.BAD_REQUEST, 'Unsupported digest qop') if not (self.cnonce and self.nonce_count): raise HTTPSimpleResponse(httplib.BAD_REQUEST, 'qop present, but cnonce and nonce_count absent') else: if self.cnonce or self.nonce_count: raise HTTPSimpleResponse(httplib.BAD_REQUEST, 'qop missing')
def __init__(self, header_val): data = parse_http_dict(header_val) self.realm = data.get('realm') self.username = data.get('username') self.nonce = data.get('nonce') self.uri = data.get('uri') self.method = data.get('method') self.response = data.get('response') self.algorithm = data.get('algorithm', 'MD5').upper() self.cnonce = data.get('cnonce') self.opaque = data.get('opaque') self.qop = data.get('qop', '').lower() self.nonce_count = data.get('nc') if self.algorithm not in self.valid_algorithms: raise HTTPSimpleResponse(httplib.BAD_REQUEST, 'Unsupported digest algorithm') if not (self.username and self.realm and self.nonce and self.uri and self.response): raise HTTPSimpleResponse( httplib.BAD_REQUEST, 'Digest algorithm required fields missing') if self.qop: if self.qop not in self.valid_qops: raise HTTPSimpleResponse(httplib.BAD_REQUEST, 'Unsupported digest qop') if not (self.cnonce and self.nonce_count): raise HTTPSimpleResponse( httplib.BAD_REQUEST, 'qop present, but cnonce and nonce_count absent') else: if self.cnonce or self.nonce_count: raise HTTPSimpleResponse(httplib.BAD_REQUEST, 'qop missing')
def test_digest_auth(self): 'Test HTTP Digest auth' from calibre.srv.http_request import normalize_header_name from calibre.srv.utils import parse_http_dict r = router() with TestServer(r.dispatch) as server: r.auth_controller.log = server.log def test(conn, path, headers={}, status=httplib.OK, body=b'', request_body=b''): conn.request('GET', path, request_body, headers) r = conn.getresponse() self.ae(r.status, status) self.ae(r.read(), body) return {normalize_header_name(k):v for k, v in r.getheaders()} conn = server.connect() test(conn, '/open', body=b'open') auth = parse_http_dict(test(conn, '/closed', status=httplib.UNAUTHORIZED)['WWW-Authenticate']) self.ae(auth[b'Digest realm'], bytes(REALM)), self.ae(auth[b'algorithm'], b'MD5'), self.ae(auth[b'qop'], b'auth') self.assertNotIn('stale', auth) self.ae(urlopen(server).read(), b'closed')
def test_digest_auth(self): # {{{ 'Test HTTP Digest auth' from calibre.srv.http_request import normalize_header_name from calibre.srv.utils import parse_http_dict r = router() with TestServer(r.dispatch) as server: r.auth_controller.log = server.log def test(conn, path, headers={}, status=httplib.OK, body=b'', request_body=b''): conn.request('GET', path, request_body, headers) r = conn.getresponse() self.ae(r.status, status) self.ae(r.read(), body) return {normalize_header_name(k): v for k, v in r.getheaders()} conn = server.connect() test(conn, '/open', body=b'open') auth = parse_http_dict( test(conn, '/closed', status=httplib.UNAUTHORIZED) ['WWW-Authenticate'].partition(b' ')[2]) nonce = auth['nonce'] auth = parse_http_dict( test(conn, '/closed', status=httplib.UNAUTHORIZED) ['WWW-Authenticate'].partition(b' ')[2]) self.assertNotEqual(nonce, auth['nonce'], 'nonce was re-used') self.ae(auth[b'realm'], bytes(REALM)), self.ae( auth[b'algorithm'], b'MD5'), self.ae(auth[b'qop'], b'auth') self.assertNotIn('stale', auth) args = auth.copy() args['un'], args['pw'], args[ 'uri'] = 'testuser', 'testpw', '/closed' def ok_test(conn, dh, **args): args['body'] = args.get('body', b'closed') return test(conn, '/closed', headers={'Authorization': dh}, **args) ok_test(conn, digest(**args)) # Check that server ignores repeated nc values ok_test(conn, digest(**args)) warnings = [] server.loop.log.warn = lambda *args, **kwargs: warnings.append( ' '.join(args)) # Check stale nonces orig, r.auth_controller.max_age_seconds = r.auth_controller.max_age_seconds, -1 auth = parse_http_dict( test(conn, '/closed', headers={'Authorization': digest(**args)}, status=httplib.UNAUTHORIZED) ['WWW-Authenticate'].partition(b' ')[2]) self.assertIn('stale', auth) r.auth_controller.max_age_seconds = orig ok_test(conn, digest(**args)) def fail_test(conn, modify, **kw): kw['body'] = kw.get('body', b'') kw['status'] = kw.get('status', httplib.UNAUTHORIZED) args['modify'] = modify return test(conn, '/closed', headers={'Authorization': digest(**args)}, **kw) # Check modified nonce fails fail_test(conn, lambda da: setattr(da, 'nonce', 'xyz')) fail_test(conn, lambda da: setattr(da, 'nonce', 'x' + da.nonce)) # Check mismatched uri fails fail_test(conn, lambda da: setattr(da, 'uri', '/')) fail_test(conn, lambda da: setattr(da, 'uri', '/closed2')) fail_test(conn, lambda da: setattr(da, 'uri', '/closed/2')) # Check that incorrect user/password fails fail_test(conn, lambda da: setattr(da, 'pw', '/')) fail_test(conn, lambda da: setattr(da, 'username', '/')) fail_test(conn, lambda da: setattr(da, 'username', '')) fail_test(conn, lambda da: setattr(da, 'pw', '')) fail_test( conn, lambda da: (setattr(da, 'pw', ''), setattr(da, 'username', ''))) # Check against python's stdlib self.ae(urlopen(server).read(), b'closed') # Check using curl curl = find_executable('curl') if curl: def docurl(data, *args): cmd = [curl] + list(args) + [ 'http://localhost:%d/closed' % server.address[1] ] p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=open(os.devnull, 'wb')) x = p.stdout.read() p.wait() self.ae(x, data) docurl(b'') docurl(b'', '--digest', '--user', 'xxxx:testpw') docurl(b'', '--digest', '--user', 'testuser:xtestpw') docurl(b'closed', '--digest', '--user', 'testuser:testpw')
def test_digest_auth(self): # {{{ 'Test HTTP Digest auth' from calibre.srv.http_request import normalize_header_name from calibre.srv.utils import parse_http_dict r = router() with TestServer(r.dispatch) as server: r.auth_controller.log = server.log def test(conn, path, headers={}, status=httplib.OK, body=b'', request_body=b''): conn.request('GET', path, request_body, headers) r = conn.getresponse() self.ae(r.status, status) self.ae(r.read(), body) return {normalize_header_name(k):v for k, v in r.getheaders()} conn = server.connect() test(conn, '/open', body=b'open') auth = parse_http_dict(test(conn, '/closed', status=httplib.UNAUTHORIZED)['WWW-Authenticate'].partition(b' ')[2]) nonce = auth['nonce'] auth = parse_http_dict(test(conn, '/closed', status=httplib.UNAUTHORIZED)['WWW-Authenticate'].partition(b' ')[2]) self.assertNotEqual(nonce, auth['nonce'], 'nonce was re-used') self.ae(auth[b'realm'], bytes(REALM)), self.ae(auth[b'algorithm'], b'MD5'), self.ae(auth[b'qop'], b'auth') self.assertNotIn('stale', auth) args = auth.copy() args['un'], args['pw'], args['uri'] = 'testuser', 'testpw', '/closed' def ok_test(conn, dh, **args): args['body'] = args.get('body', b'closed') return test(conn, '/closed', headers={'Authorization':dh}, **args) ok_test(conn, digest(**args)) # Check that server ignores repeated nc values ok_test(conn, digest(**args)) warnings = [] server.loop.log.warn = lambda *args, **kwargs: warnings.append(' '.join(args)) # Check stale nonces orig, r.auth_controller.max_age_seconds = r.auth_controller.max_age_seconds, -1 auth = parse_http_dict(test(conn, '/closed', headers={ 'Authorization':digest(**args)},status=httplib.UNAUTHORIZED)['WWW-Authenticate'].partition(b' ')[2]) self.assertIn('stale', auth) r.auth_controller.max_age_seconds = orig ok_test(conn, digest(**args)) def fail_test(conn, modify, **kw): kw['body'] = kw.get('body', b'') kw['status'] = kw.get('status', httplib.UNAUTHORIZED) args['modify'] = modify return test(conn, '/closed', headers={'Authorization':digest(**args)}, **kw) # Check modified nonce fails fail_test(conn, lambda da:setattr(da, 'nonce', 'xyz')) fail_test(conn, lambda da:setattr(da, 'nonce', 'x' + da.nonce)) # Check mismatched uri fails fail_test(conn, lambda da:setattr(da, 'uri', '/')) fail_test(conn, lambda da:setattr(da, 'uri', '/closed2')) fail_test(conn, lambda da:setattr(da, 'uri', '/closed/2')) # Check that incorrect user/password fails fail_test(conn, lambda da:setattr(da, 'pw', '/')) fail_test(conn, lambda da:setattr(da, 'username', '/')) fail_test(conn, lambda da:setattr(da, 'username', '')) fail_test(conn, lambda da:setattr(da, 'pw', '')) fail_test(conn, lambda da:(setattr(da, 'pw', ''), setattr(da, 'username', ''))) # Check against python's stdlib self.ae(urlopen(server).read(), b'closed') # Check using curl curl = find_executable('curl') if curl: def docurl(data, *args): cmd = [curl] + list(args) + ['http://localhost:%d/closed' % server.address[1]] p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=open(os.devnull, 'wb')) x = p.stdout.read() p.wait() self.ae(x, data) docurl(b'') docurl(b'', '--digest', '--user', 'xxxx:testpw') docurl(b'', '--digest', '--user', 'testuser:xtestpw') docurl(b'closed', '--digest', '--user', 'testuser:testpw')