From 8bec5607eab8ca5515cc1d843b2e47b33f853e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Michael=20O=2E=20Hegg=C3=B8?= Date: Sun, 16 Nov 2014 16:27:22 +0100 Subject: [PATCH] Use Basic/Digest Auth from Requests As suggested in #52 Original auth mechanism introduced in #27 This change should retain backwards compability --- RELEASE-NOTES.md | 1 + mwclient/client.py | 14 +++++++++----- tests/test_client.py | 17 +++++++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 8311eb51..0b271a33 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -19,6 +19,7 @@ This is the development version of mwclient. Use 'simplified' continuation [4262786](https://github.com/mwclient/mwclient/commit/4262786), [#66](https://github.com/mwclient/mwclient/issues/66). +* [2014-11-16] Use Basic/Digest Auth from Requests ## Changes in version 0.7.0 diff --git a/mwclient/client.py b/mwclient/client.py index be4457b6..f9b3bee1 100644 --- a/mwclient/client.py +++ b/mwclient/client.py @@ -6,7 +6,6 @@ import random import sys import weakref -import base64 try: # Python 2.7+ @@ -20,6 +19,7 @@ except ImportError: import simplejson as json import requests +from requests.auth import HTTPBasicAuth, AuthBase import errors import listing @@ -62,13 +62,19 @@ def __init__(self, host, path='/w/', ext='.php', pool=None, retry_timeout=30, self.ext = ext self.credentials = None self.compress = compress - self.httpauth = httpauth self.retry_timeout = retry_timeout self.max_retries = max_retries self.wait_callback = wait_callback self.max_lag = str(max_lag) self.force_login = force_login + if isinstance(httpauth, (list, tuple)): + self.httpauth = HTTPBasicAuth(*httpauth) + elif httpauth is None or isinstance(httpauth, (AuthBase,)): + self.httpauth = httpauth + else: + raise RuntimeError('Authentication is not a tuple or an instance of AuthBase') + # The token string => token object mapping self.wait_tokens = weakref.WeakKeyDictionary() @@ -86,6 +92,7 @@ def __init__(self, host, path='/w/', ext='.php', pool=None, retry_timeout=30, # Setup connection if pool is None: self.connection = requests.Session() + self.connection.auth = self.httpauth self.connection.headers['User-Agent'] = 'MwClient/' + __ver__ + ' (https://github.com/mwclient/mwclient)' if clients_useragent: self.connection.headers['User-Agent'] = clients_useragent + ' - ' + self.connection.headers['User-Agent'] @@ -235,9 +242,6 @@ def raw_call(self, script, data, files=None): headers = {} if self.compress and gzip: headers['Accept-Encoding'] = 'gzip' - if self.httpauth is not None: - credentials = base64.encodestring('%s:%s' % self.httpauth).replace('\n', '') - headers['Authorization'] = 'Basic %s' % credentials token = self.wait_token((script, data)) while True: scheme = 'http' # Should we move to 'https' as default? diff --git a/tests/test_client.py b/tests/test_client.py index 15281d60..d16584c4 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -140,6 +140,23 @@ def test_basic_request(self): assert 'action=query' in responses.calls[0].request.body assert 'meta=siteinfo%7Cuserinfo' in responses.calls[0].request.body + @responses.activate + def test_httpauth_defaults_to_basic_auth(self): + + self.httpShouldReturn(self.makeMetaResponse()) + + site = mwclient.Site('test.wikipedia.org', httpauth=('me', 'verysecret')) + + assert isinstance(site.httpauth, requests.auth.HTTPBasicAuth) + + @responses.activate + def test_httpauth_raise_error_on_invalid_type(self): + + self.httpShouldReturn(self.makeMetaResponse()) + + with pytest.raises(RuntimeError): + site = mwclient.Site('test.wikipedia.org', httpauth=1) + @responses.activate def test_api_disabled(self): # Should raise APIDisabledError if API is not enabled