Replace the oauth library with oauthlib
Because the oauth library we were using doesn't support Python 3, cloudbase-init could not work on Python 3 for the MaaS metadata service, the only place in the code base where OAuth is needed. This patch replaces oauth with oauthlib. oauthlib is better maintained and has support for Python 2.6-3.4. Change-Id: Iae2995420697bc305f2724ce038db2f2b3ab51e3 Closes-Bug: #1382572
This commit is contained in:
parent
433d4093c7
commit
a4f4a3c4e0
@ -13,9 +13,8 @@
|
||||
# under the License.
|
||||
|
||||
import posixpath
|
||||
import time
|
||||
|
||||
from oauth import oauth
|
||||
from oauthlib import oauth1
|
||||
from oslo.config import cfg
|
||||
from six.moves.urllib import error
|
||||
from six.moves.urllib import request
|
||||
@ -43,6 +42,17 @@ CONF.register_opts(opts)
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class _Realm(str):
|
||||
# There's a bug in oauthlib which ignores empty realm strings,
|
||||
# by checking that the given realm is always True.
|
||||
# This string class always returns True in a boolean context,
|
||||
# making sure that an empty realm can be used by oauthlib.
|
||||
def __bool__(self):
|
||||
return True
|
||||
|
||||
__nonzero__ = __bool__
|
||||
|
||||
|
||||
class MaaSHttpService(base.BaseMetadataService):
|
||||
_METADATA_2012_03_01 = '2012-03-01'
|
||||
|
||||
@ -76,22 +86,15 @@ class MaaSHttpService(base.BaseMetadataService):
|
||||
raise
|
||||
|
||||
def _get_oauth_headers(self, url):
|
||||
consumer = oauth.OAuthConsumer(CONF.maas_oauth_consumer_key,
|
||||
CONF.maas_oauth_consumer_secret)
|
||||
token = oauth.OAuthToken(CONF.maas_oauth_token_key,
|
||||
CONF.maas_oauth_token_secret)
|
||||
|
||||
parameters = {'oauth_version': "1.0",
|
||||
'oauth_nonce': oauth.generate_nonce(),
|
||||
'oauth_timestamp': int(time.time()),
|
||||
'oauth_token': token.key,
|
||||
'oauth_consumer_key': consumer.key}
|
||||
|
||||
req = oauth.OAuthRequest(http_url=url, parameters=parameters)
|
||||
req.sign_request(oauth.OAuthSignatureMethod_PLAINTEXT(), consumer,
|
||||
token)
|
||||
|
||||
return req.to_header()
|
||||
client = oauth1.Client(
|
||||
CONF.maas_oauth_consumer_key,
|
||||
client_secret=CONF.maas_oauth_consumer_secret,
|
||||
resource_owner_key=CONF.maas_oauth_token_key,
|
||||
resource_owner_secret=CONF.maas_oauth_token_secret,
|
||||
signature_method=oauth1.SIGNATURE_PLAINTEXT)
|
||||
realm = _Realm("")
|
||||
headers = client.sign(url, realm=realm)[1]
|
||||
return headers
|
||||
|
||||
def _get_data(self, path):
|
||||
norm_path = posixpath.join(CONF.maas_metadata_url, path)
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
import os
|
||||
import posixpath
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
try:
|
||||
@ -25,12 +24,10 @@ from oslo.config import cfg
|
||||
from six.moves.urllib import error
|
||||
|
||||
from cloudbaseinit.metadata.services import base
|
||||
from cloudbaseinit.metadata.services import maasservice
|
||||
from cloudbaseinit.tests import testutils
|
||||
from cloudbaseinit.utils import x509constants
|
||||
|
||||
if sys.version_info < (3, 0):
|
||||
# TODO(alexpilotti) replace oauth with a Python 3 compatible module
|
||||
from cloudbaseinit.metadata.services import maasservice
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
@ -38,12 +35,7 @@ CONF = cfg.CONF
|
||||
class MaaSHttpServiceTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
if sys.version_info < (3, 0):
|
||||
self.mock_oauth = mock.MagicMock()
|
||||
maasservice.oauth = self.mock_oauth
|
||||
self._maasservice = maasservice.MaaSHttpService()
|
||||
else:
|
||||
self.skipTest("Python 3 is not yet supported for maasservice")
|
||||
self._maasservice = maasservice.MaaSHttpService()
|
||||
|
||||
@mock.patch("cloudbaseinit.metadata.services.maasservice.MaaSHttpService"
|
||||
"._get_data")
|
||||
@ -91,32 +83,32 @@ class MaaSHttpServiceTest(unittest.TestCase):
|
||||
'test other error', {}, None)
|
||||
self._test_get_response(ret_val=err)
|
||||
|
||||
@mock.patch('time.time')
|
||||
def test_get_oauth_headers(self, mock_time):
|
||||
mock_token = mock.MagicMock()
|
||||
mock_consumer = mock.MagicMock()
|
||||
mock_req = mock.MagicMock()
|
||||
self.mock_oauth.OAuthConsumer.return_value = mock_consumer
|
||||
self.mock_oauth.OAuthToken.return_value = mock_token
|
||||
self.mock_oauth.OAuthRequest.return_value = mock_req
|
||||
mock_time.return_value = 0
|
||||
self.mock_oauth.generate_nonce.return_value = 'fake nounce'
|
||||
@testutils.ConfPatcher('maas_oauth_consumer_key', 'consumer_key')
|
||||
@testutils.ConfPatcher('maas_oauth_consumer_secret', 'consumer_secret')
|
||||
@testutils.ConfPatcher('maas_oauth_token_key', 'token_key')
|
||||
@testutils.ConfPatcher('maas_oauth_token_secret', 'token_secret')
|
||||
def test_get_oauth_headers(self):
|
||||
response = self._maasservice._get_oauth_headers(url='196.254.196.254')
|
||||
self.mock_oauth.OAuthConsumer.assert_called_once_with(
|
||||
CONF.maas_oauth_consumer_key, CONF.maas_oauth_consumer_secret)
|
||||
self.mock_oauth.OAuthToken.assert_called_once_with(
|
||||
CONF.maas_oauth_token_key, CONF.maas_oauth_token_secret)
|
||||
parameters = {'oauth_version': "1.0",
|
||||
'oauth_nonce': 'fake nounce',
|
||||
'oauth_timestamp': int(0),
|
||||
'oauth_token': mock_token.key,
|
||||
'oauth_consumer_key': mock_consumer.key}
|
||||
self.mock_oauth.OAuthRequest.assert_called_once_with(
|
||||
http_url='196.254.196.254', parameters=parameters)
|
||||
mock_req.sign_request.assert_called_once_with(
|
||||
self.mock_oauth.OAuthSignatureMethod_PLAINTEXT(), mock_consumer,
|
||||
mock_token)
|
||||
self.assertEqual(mock_req.to_header.return_value, response)
|
||||
self.assertIsInstance(response, dict)
|
||||
self.assertIn('Authorization', response)
|
||||
|
||||
auth = response['Authorization']
|
||||
self.assertTrue(auth.startswith('OAuth'))
|
||||
|
||||
auth = auth[6:]
|
||||
parts = [item.strip() for item in auth.split(",")]
|
||||
auth_parts = dict(part.split("=") for part in parts)
|
||||
|
||||
required_headers = {
|
||||
'oauth_token',
|
||||
'oauth_consumer_key',
|
||||
'oauth_signature',
|
||||
}
|
||||
self.assertTrue(required_headers.issubset(set(auth_parts)))
|
||||
self.assertEqual('"token_key"', auth_parts['oauth_token'])
|
||||
self.assertEqual('"consumer_key"', auth_parts['oauth_consumer_key'])
|
||||
self.assertEqual('"consumer_secret%26token_secret"',
|
||||
auth_parts['oauth_signature'])
|
||||
|
||||
@mock.patch("cloudbaseinit.metadata.services.maasservice.MaaSHttpService"
|
||||
"._get_oauth_headers")
|
||||
|
@ -6,6 +6,6 @@ pyserial
|
||||
oslo.config
|
||||
six>=1.7.0
|
||||
Babel>=1.3
|
||||
oauth
|
||||
oauthlib
|
||||
netifaces
|
||||
PyYAML
|
Loading…
x
Reference in New Issue
Block a user