Move tests in keystoneclient

This is the suggested location for tests and is adopted by most
projects. As part of this change relative imports to package imports.

Fix all the test running and coverage code to point to the new location.

Change-Id: I01264aed14f396ab9a7242e3e72b71e1bc332675
This commit is contained in:
Jamie Lennox
2013-09-23 12:07:40 +10:00
commit 691ec36670
49 changed files with 8387 additions and 0 deletions

0
__init__.py Normal file
View File

View File

@@ -0,0 +1,66 @@
# Copyright 2012 OpenStack LLC.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from keystoneclient.apiclient import exceptions
from keystoneclient.tests import utils
class FakeResponse(object):
json_data = {}
def __init__(self, **kwargs):
for key, value in kwargs.iteritems():
setattr(self, key, value)
def json(self):
return self.json_data
class ExceptionsArgsTest(utils.TestCase):
def assert_exception(self, ex_cls, method, url, status_code, json_data):
ex = exceptions.from_response(
FakeResponse(status_code=status_code,
headers={"Content-Type": "application/json"},
json_data=json_data),
method,
url)
self.assertTrue(isinstance(ex, ex_cls))
self.assertEqual(ex.message, json_data["error"]["message"])
self.assertEqual(ex.details, json_data["error"]["details"])
self.assertEqual(ex.method, method)
self.assertEqual(ex.url, url)
self.assertEqual(ex.http_status, status_code)
def test_from_response_known(self):
method = "GET"
url = "/fake"
status_code = 400
json_data = {"error": {"message": "fake message",
"details": "fake details"}}
self.assert_exception(
exceptions.BadRequest, method, url, status_code, json_data)
def test_from_response_unknown(self):
method = "POST"
url = "/fake-unknown"
status_code = 499
json_data = {"error": {"message": "fake unknown message",
"details": "fake unknown details"}}
self.assert_exception(
exceptions.HTTPClientError, method, url, status_code, json_data)
status_code = 600
self.assert_exception(
exceptions.HTTPError, method, url, status_code, json_data)

298
client_fixtures.py Normal file
View File

@@ -0,0 +1,298 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2013 OpenStack LLC
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
from keystoneclient.common import cms
from keystoneclient.openstack.common import jsonutils
from keystoneclient.openstack.common import timeutils
from keystoneclient import utils
CLIENTDIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
ROOTDIR = os.path.dirname(CLIENTDIR)
CERTDIR = os.path.join(ROOTDIR, 'examples', 'pki', 'certs')
CMSDIR = os.path.join(ROOTDIR, 'examples', 'pki', 'cms')
# @TODO(mordred) This should become a testresources resource attached to the
# class
# The data for these tests are signed using openssl and are stored in files
# in the signing subdirectory. In order to keep the values consistent between
# the tests and the signed documents, we read them in for use in the tests.
with open(os.path.join(CMSDIR, 'auth_token_scoped.pem')) as f:
SIGNED_TOKEN_SCOPED = cms.cms_to_token(f.read())
with open(os.path.join(CMSDIR, 'auth_token_unscoped.pem')) as f:
SIGNED_TOKEN_UNSCOPED = cms.cms_to_token(f.read())
with open(os.path.join(CMSDIR, 'auth_v3_token_scoped.pem')) as f:
SIGNED_v3_TOKEN_SCOPED = cms.cms_to_token(f.read())
with open(os.path.join(CMSDIR, 'auth_token_revoked.pem')) as f:
REVOKED_TOKEN = cms.cms_to_token(f.read())
with open(os.path.join(CMSDIR, 'auth_token_scoped_expired.pem')) as f:
SIGNED_TOKEN_SCOPED_EXPIRED = cms.cms_to_token(f.read())
with open(os.path.join(CMSDIR, 'auth_v3_token_revoked.pem')) as f:
REVOKED_v3_TOKEN = cms.cms_to_token(f.read())
with open(os.path.join(CMSDIR, 'revocation_list.json')) as f:
REVOCATION_LIST = jsonutils.loads(f.read())
with open(os.path.join(CMSDIR, 'revocation_list.pem')) as f:
SIGNED_REVOCATION_LIST = jsonutils.dumps({'signed': f.read()})
with open(os.path.join(CERTDIR, 'signing_cert.pem')) as f:
SIGNING_CERT = f.read()
with open(os.path.join(CERTDIR, 'cacert.pem')) as f:
SIGNING_CA = f.read()
UUID_TOKEN_DEFAULT = "ec6c0710ec2f471498484c1b53ab4f9d"
UUID_TOKEN_NO_SERVICE_CATALOG = '8286720fbe4941e69fa8241723bb02df'
UUID_TOKEN_UNSCOPED = '731f903721c14827be7b2dc912af7776'
VALID_DIABLO_TOKEN = 'b0cf19b55dbb4f20a6ee18e6c6cf1726'
v3_UUID_TOKEN_DEFAULT = '5603457654b346fdbb93437bfe76f2f1'
v3_UUID_TOKEN_UNSCOPED = 'd34835fdaec447e695a0a024d84f8d79'
v3_UUID_TOKEN_DOMAIN_SCOPED = 'e8a7b63aaa4449f38f0c5c05c3581792'
REVOKED_TOKEN_HASH = utils.hash_signed_token(REVOKED_TOKEN)
REVOKED_TOKEN_LIST = {'revoked': [{'id': REVOKED_TOKEN_HASH,
'expires': timeutils.utcnow()}]}
REVOKED_TOKEN_LIST_JSON = jsonutils.dumps(REVOKED_TOKEN_LIST)
REVOKED_v3_TOKEN_HASH = utils.hash_signed_token(REVOKED_v3_TOKEN)
REVOKED_v3_TOKEN_LIST = {'revoked': [{'id': REVOKED_v3_TOKEN_HASH,
'expires': timeutils.utcnow()}]}
REVOKED_v3_TOKEN_LIST_JSON = jsonutils.dumps(REVOKED_v3_TOKEN_LIST)
SIGNED_TOKEN_SCOPED_KEY = cms.cms_hash_token(SIGNED_TOKEN_SCOPED)
SIGNED_TOKEN_UNSCOPED_KEY = cms.cms_hash_token(SIGNED_TOKEN_UNSCOPED)
SIGNED_v3_TOKEN_SCOPED_KEY = cms.cms_hash_token(SIGNED_v3_TOKEN_SCOPED)
INVALID_SIGNED_TOKEN = \
"MIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" \
"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC" \
"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" \
"EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" \
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" \
"0000000000000000000000000000000000000000000000000000000000000000" \
"1111111111111111111111111111111111111111111111111111111111111111" \
"2222222222222222222222222222222222222222222222222222222222222222" \
"3333333333333333333333333333333333333333333333333333333333333333" \
"4444444444444444444444444444444444444444444444444444444444444444" \
"5555555555555555555555555555555555555555555555555555555555555555" \
"6666666666666666666666666666666666666666666666666666666666666666" \
"7777777777777777777777777777777777777777777777777777777777777777" \
"8888888888888888888888888888888888888888888888888888888888888888" \
"9999999999999999999999999999999999999999999999999999999999999999" \
"0000000000000000000000000000000000000000000000000000000000000000" \
# JSON responses keyed by token ID
TOKEN_RESPONSES = {
UUID_TOKEN_DEFAULT: {
'access': {
'token': {
'id': UUID_TOKEN_DEFAULT,
'expires': '2020-01-01T00:00:10.000123Z',
'tenant': {
'id': 'tenant_id1',
'name': 'tenant_name1',
},
},
'user': {
'id': 'user_id1',
'name': 'user_name1',
'roles': [
{'name': 'role1'},
{'name': 'role2'},
],
},
'serviceCatalog': {}
},
},
VALID_DIABLO_TOKEN: {
'access': {
'token': {
'id': VALID_DIABLO_TOKEN,
'expires': '2020-01-01T00:00:10.000123Z',
'tenantId': 'tenant_id1',
},
'user': {
'id': 'user_id1',
'name': 'user_name1',
'roles': [
{'name': 'role1'},
{'name': 'role2'},
],
},
},
},
UUID_TOKEN_UNSCOPED: {
'access': {
'token': {
'id': UUID_TOKEN_UNSCOPED,
'expires': '2020-01-01T00:00:10.000123Z',
},
'user': {
'id': 'user_id1',
'name': 'user_name1',
'roles': [
{'name': 'role1'},
{'name': 'role2'},
],
},
},
},
UUID_TOKEN_NO_SERVICE_CATALOG: {
'access': {
'token': {
'id': 'valid-token',
'expires': '2020-01-01T00:00:10.000123Z',
'tenant': {
'id': 'tenant_id1',
'name': 'tenant_name1',
},
},
'user': {
'id': 'user_id1',
'name': 'user_name1',
'roles': [
{'name': 'role1'},
{'name': 'role2'},
],
}
},
},
v3_UUID_TOKEN_DEFAULT: {
'token': {
'expires_at': '2020-01-01T00:00:10.000123Z',
'user': {
'id': 'user_id1',
'name': 'user_name1',
'domain': {
'id': 'domain_id1',
'name': 'domain_name1'
}
},
'project': {
'id': 'tenant_id1',
'name': 'tenant_name1',
'domain': {
'id': 'domain_id1',
'name': 'domain_name1'
}
},
'roles': [
{'name': 'role1', 'id': 'Role1'},
{'name': 'role2', 'id': 'Role2'},
],
'catalog': {}
}
},
v3_UUID_TOKEN_UNSCOPED: {
'token': {
'expires_at': '2020-01-01T00:00:10.000123Z',
'user': {
'id': 'user_id1',
'name': 'user_name1',
'domain': {
'id': 'domain_id1',
'name': 'domain_name1'
}
}
}
},
v3_UUID_TOKEN_DOMAIN_SCOPED: {
'token': {
'expires_at': '2020-01-01T00:00:10.000123Z',
'user': {
'id': 'user_id1',
'name': 'user_name1',
'domain': {
'id': 'domain_id1',
'name': 'domain_name1'
}
},
'domain': {
'id': 'domain_id1',
'name': 'domain_name1',
},
'roles': [
{'name': 'role1', 'id': 'Role1'},
{'name': 'role2', 'id': 'Role2'},
],
'catalog': {}
}
},
SIGNED_TOKEN_SCOPED_KEY: {
'access': {
'token': {
'id': SIGNED_TOKEN_SCOPED_KEY,
},
'user': {
'id': 'user_id1',
'name': 'user_name1',
'tenantId': 'tenant_id1',
'tenantName': 'tenant_name1',
'roles': [
{'name': 'role1'},
{'name': 'role2'},
],
},
},
},
SIGNED_TOKEN_UNSCOPED_KEY: {
'access': {
'token': {
'id': SIGNED_TOKEN_UNSCOPED_KEY,
},
'user': {
'id': 'user_id1',
'name': 'user_name1',
'roles': [
{'name': 'role1'},
{'name': 'role2'},
],
},
},
},
SIGNED_v3_TOKEN_SCOPED_KEY: {
'token': {
'expires': '2020-01-01T00:00:10.000123Z',
'user': {
'id': 'user_id1',
'name': 'user_name1',
'domain': {
'id': 'domain_id1',
'name': 'domain_name1'
}
},
'project': {
'id': 'tenant_id1',
'name': 'tenant_name1',
'domain': {
'id': 'domain_id1',
'name': 'domain_name1'
}
},
'roles': [
{'name': 'role1'},
{'name': 'role2'}
],
'catalog': {}
}
},
}
JSON_TOKEN_RESPONSES = dict([(k, jsonutils.dumps(v)) for k, v in
TOKEN_RESPONSES.iteritems()])

120
fakes.py Normal file
View File

@@ -0,0 +1,120 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
A fake server that "responds" to API methods with pre-canned responses.
All of these responses come from the spec, so if for some reason the spec's
wrong the tests might raise AssertionError. I've indicated in comments the
places where actual behavior differs from the spec.
"""
from keystoneclient import access
def assert_has_keys(dict, required=[], optional=[]):
keys = dict.keys()
for k in required:
try:
assert k in keys
except AssertionError:
extra_keys = set(keys).difference(set(required + optional))
raise AssertionError("found unexpected keys: %s" %
list(extra_keys))
class FakeClient(object):
def assert_called(self, method, url, body=None, pos=-1):
"""Assert than an API method was just called."""
expected = (method, url)
called = self.callstack[pos][0:2]
assert self.callstack, ("Expected %s %s but no calls were made." %
expected)
assert expected == called, ("Expected %s %s; got %s %s" %
(expected + called))
if body is not None:
assert self.callstack[pos][2] == body
def assert_called_anytime(self, method, url, body=None):
"""Assert than an API method was called anytime in the test."""
expected = (method, url)
assert self.callstack, ("Expected %s %s but no calls were made." %
expected)
found = False
for entry in self.callstack:
if expected == entry[0:2]:
found = True
break
assert found, ('Expected %s; got %s' %
(expected, self.callstack))
if body is not None:
if entry[2] != body:
raise AssertionError('%s != %s' % (entry[2], body))
self.callstack = []
def clear_callstack(self):
self.callstack = []
def authenticate(self, cl_obj):
cl_obj.user_id = '1'
cl_obj.auth_user_id = '1'
cl_obj.project_id = '1'
cl_obj.auth_tenant_id = '1'
cl_obj.auth_ref = access.AccessInfo.factory(None, {
"access": {
"token": {
"expires": "2012-02-05T00:00:00",
"id": "887665443383838",
"tenant": {
"id": "1",
"name": "customer-x"
}
},
"serviceCatalog": [{
"endpoints": [{
"adminURL": "http://swift.admin-nets.local:8080/",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:8080/v1/AUTH_1",
"publicURL":
"http://swift.publicinternets.com/v1/AUTH_1"
}],
"type": "object-store",
"name": "swift"
}, {
"endpoints": [{
"adminURL": "http://cdn.admin-nets.local/v1.1/1",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:7777/v1.1/1",
"publicURL": "http://cdn.publicinternets.com/v1.1/1"
}],
"type": "object-store",
"name": "cdn"
}],
"user": {
"id": "1",
"roles": [{
"tenantId": "1",
"id": "3",
"name": "Member"
}],
"name": "joeuser"
}
}
})

File diff suppressed because it is too large Load Diff

163
test_base.py Normal file
View File

@@ -0,0 +1,163 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from keystoneclient import base
from keystoneclient.tests import utils
from keystoneclient.v2_0 import client
from keystoneclient.v2_0 import roles
class HumanReadable(base.Resource):
HUMAN_ID = True
class BaseTest(utils.TestCase):
def test_resource_repr(self):
r = base.Resource(None, dict(foo="bar", baz="spam"))
self.assertEqual(repr(r), "<Resource baz=spam, foo=bar>")
def test_getid(self):
self.assertEqual(base.getid(4), 4)
class TmpObject(object):
id = 4
self.assertEqual(base.getid(TmpObject), 4)
def test_resource_lazy_getattr(self):
self.client = client.Client(username=self.TEST_USER,
token=self.TEST_TOKEN,
tenant_name=self.TEST_TENANT_NAME,
auth_url='http://127.0.0.1:5000',
endpoint='http://127.0.0.1:5000')
self.client.get = self.mox.CreateMockAnything()
self.client.get('/OS-KSADM/roles/1').AndRaise(AttributeError)
self.mox.ReplayAll()
f = roles.Role(self.client.roles, {'id': 1, 'name': 'Member'})
self.assertEqual(f.name, 'Member')
# Missing stuff still fails after a second get
self.assertRaises(AttributeError, getattr, f, 'blahblah')
def test_eq(self):
# Two resources of the same type with the same id: equal
r1 = base.Resource(None, {'id': 1, 'name': 'hi'})
r2 = base.Resource(None, {'id': 1, 'name': 'hello'})
self.assertEqual(r1, r2)
# Two resoruces of different types: never equal
r1 = base.Resource(None, {'id': 1})
r2 = roles.Role(None, {'id': 1})
self.assertNotEqual(r1, r2)
# Two resources with no ID: equal if their info is equal
r1 = base.Resource(None, {'name': 'joe', 'age': 12})
r2 = base.Resource(None, {'name': 'joe', 'age': 12})
self.assertEqual(r1, r2)
r1 = base.Resource(None, {'id': 1})
self.assertNotEqual(r1, object())
self.assertNotEqual(r1, {'id': 1})
def test_human_id(self):
r = base.Resource(None, {"name": "1 of !"})
self.assertEqual(r.human_id, None)
r = HumanReadable(None, {"name": "1 of !"})
self.assertEqual(r.human_id, "1-of")
class ManagerTest(utils.TestCase):
body = {"hello": {"hi": 1}}
url = "/test-url"
def setUp(self):
super(ManagerTest, self).setUp()
self.client = client.Client(username=self.TEST_USER,
token=self.TEST_TOKEN,
tenant_name=self.TEST_TENANT_NAME,
auth_url='http://127.0.0.1:5000',
endpoint='http://127.0.0.1:5000')
self.mgr = base.Manager(self.client)
self.mgr.resource_class = base.Resource
def test_api(self):
self.assertEqual(self.mgr.api, self.client)
def test_get(self):
self.client.get = self.mox.CreateMockAnything()
self.client.get(self.url).AndReturn((None, self.body))
self.mox.ReplayAll()
rsrc = self.mgr._get(self.url, "hello")
self.assertEqual(rsrc.hi, 1)
def test_post(self):
self.client.post = self.mox.CreateMockAnything()
self.client.post(self.url, body=self.body).AndReturn((None, self.body))
self.client.post(self.url, body=self.body).AndReturn((None, self.body))
self.mox.ReplayAll()
rsrc = self.mgr._post(self.url, self.body, "hello")
self.assertEqual(rsrc.hi, 1)
rsrc = self.mgr._post(self.url, self.body, "hello", return_raw=True)
self.assertEqual(rsrc["hi"], 1)
def test_put(self):
self.client.put = self.mox.CreateMockAnything()
self.client.put(self.url, body=self.body).AndReturn((None, self.body))
self.client.put(self.url, body=self.body).AndReturn((None, self.body))
self.mox.ReplayAll()
rsrc = self.mgr._put(self.url, self.body, "hello")
self.assertEqual(rsrc.hi, 1)
rsrc = self.mgr._put(self.url, self.body)
self.assertEqual(rsrc.hello["hi"], 1)
def test_patch(self):
self.client.patch = self.mox.CreateMockAnything()
self.client.patch(self.url, body=self.body).AndReturn(
(None, self.body))
self.client.patch(self.url, body=self.body).AndReturn(
(None, self.body))
self.mox.ReplayAll()
rsrc = self.mgr._patch(self.url, self.body, "hello")
self.assertEqual(rsrc.hi, 1)
rsrc = self.mgr._patch(self.url, self.body)
self.assertEqual(rsrc.hello["hi"], 1)
def test_update(self):
self.client.patch = self.mox.CreateMockAnything()
self.client.put = self.mox.CreateMockAnything()
self.client.patch(
self.url, body=self.body, management=False).AndReturn(
(None, self.body))
self.client.put(self.url, body=None, management=True).AndReturn(
(None, self.body))
self.mox.ReplayAll()
rsrc = self.mgr._update(
self.url, body=self.body, response_key="hello", method="PATCH",
management=False)
self.assertEqual(rsrc.hi, 1)
rsrc = self.mgr._update(
self.url, body=None, response_key="hello", method="PUT",
management=True)
self.assertEqual(rsrc.hi, 1)

257
test_ec2utils.py Normal file
View File

@@ -0,0 +1,257 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 OpenStack LLC
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import unicode_literals
import testtools
from keystoneclient.contrib.ec2 import utils
class Ec2SignerTest(testtools.TestCase):
def setUp(self):
super(Ec2SignerTest, self).setUp()
self.access = '966afbde20b84200ae4e62e09acf46b2'
self.secret = '89cdf9e94e2643cab35b8b8ac5a51f83'
self.signer = utils.Ec2Signer(self.secret)
def tearDown(self):
super(Ec2SignerTest, self).tearDown()
def test_v4_creds_header(self):
auth_str = 'AWS4-HMAC-SHA256 blah'
credentials = {'host': '127.0.0.1',
'verb': 'GET',
'path': '/v1/',
'params': {},
'headers': {'Authorization': auth_str}}
self.assertTrue(self.signer._v4_creds(credentials))
def test_v4_creds_param(self):
credentials = {'host': '127.0.0.1',
'verb': 'GET',
'path': '/v1/',
'params': {'X-Amz-Algorithm': 'AWS4-HMAC-SHA256'},
'headers': {}}
self.assertTrue(self.signer._v4_creds(credentials))
def test_v4_creds_false(self):
credentials = {'host': '127.0.0.1',
'verb': 'GET',
'path': '/v1/',
'params': {'SignatureVersion': '0',
'AWSAccessKeyId': self.access,
'Timestamp': '2012-11-27T11:47:02Z',
'Action': 'Foo'}}
self.assertFalse(self.signer._v4_creds(credentials))
def test_generate_0(self):
"""Test generate function for v0 signature."""
credentials = {'host': '127.0.0.1',
'verb': 'GET',
'path': '/v1/',
'params': {'SignatureVersion': '0',
'AWSAccessKeyId': self.access,
'Timestamp': '2012-11-27T11:47:02Z',
'Action': 'Foo'}}
signature = self.signer.generate(credentials)
expected = 'SmXQEZAUdQw5glv5mX8mmixBtas='
self.assertEqual(signature, expected)
def test_generate_1(self):
"""Test generate function for v1 signature."""
credentials = {'host': '127.0.0.1',
'verb': 'GET',
'path': '/v1/',
'params': {'SignatureVersion': '1',
'AWSAccessKeyId': self.access}}
signature = self.signer.generate(credentials)
expected = 'VRnoQH/EhVTTLhwRLfuL7jmFW9c='
self.assertEqual(signature, expected)
def test_generate_v2_SHA256(self):
"""Test generate function for v2 signature, SHA256."""
credentials = {'host': '127.0.0.1',
'verb': 'GET',
'path': '/v1/',
'params': {'SignatureVersion': '2',
'AWSAccessKeyId': self.access}}
signature = self.signer.generate(credentials)
expected = 'odsGmT811GffUO0Eu13Pq+xTzKNIjJ6NhgZU74tYX/w='
self.assertEqual(signature, expected)
def test_generate_v2_SHA1(self):
"""Test generate function for v2 signature, SHA1."""
credentials = {'host': '127.0.0.1',
'verb': 'GET',
'path': '/v1/',
'params': {'SignatureVersion': '2',
'AWSAccessKeyId': self.access}}
self.signer.hmac_256 = None
signature = self.signer.generate(credentials)
expected = 'ZqCxMI4ZtTXWI175743mJ0hy/Gc='
self.assertEqual(signature, expected)
def test_generate_v4(self):
"""Test v4 generator with data from AWS docs example.
see:
http://docs.aws.amazon.com/general/latest/gr/
sigv4-create-canonical-request.html
and
http://docs.aws.amazon.com/general/latest/gr/
sigv4-signed-request-examples.html
"""
# Create a new signer object with the AWS example key
secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
signer = utils.Ec2Signer(secret)
body_hash = ('b6359072c78d70ebee1e81adcbab4f0'
'1bf2c23245fa365ef83fe8f1f955085e2')
auth_str = ('AWS4-HMAC-SHA256 '
'Credential=AKIAIOSFODNN7EXAMPLE/20110909/'
'us-east-1/iam/aws4_request,'
'SignedHeaders=content-type;host;x-amz-date,')
headers = {'Content-type':
'application/x-www-form-urlencoded; charset=utf-8',
'X-Amz-Date': '20110909T233600Z',
'Host': 'iam.amazonaws.com',
'Authorization': auth_str}
# Note the example in the AWS docs is inconsistent, previous
# examples specify no query string, but the final POST example
# does, apparently incorrectly since an empty parameter list
# aligns all steps and the final signature with the examples
params = {}
credentials = {'host': 'iam.amazonaws.com',
'verb': 'POST',
'path': '/',
'params': params,
'headers': headers,
'body_hash': body_hash}
signature = signer.generate(credentials)
expected = ('ced6826de92d2bdeed8f846f0bf508e8'
'559e98e4b0199114b84c54174deb456c')
self.assertEqual(signature, expected)
def test_generate_v4_port(self):
"""Test v4 generator with host:port format."""
# Create a new signer object with the AWS example key
secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
signer = utils.Ec2Signer(secret)
body_hash = ('b6359072c78d70ebee1e81adcbab4f0'
'1bf2c23245fa365ef83fe8f1f955085e2')
auth_str = ('AWS4-HMAC-SHA256 '
'Credential=AKIAIOSFODNN7EXAMPLE/20110909/'
'us-east-1/iam/aws4_request,'
'SignedHeaders=content-type;host;x-amz-date,')
headers = {'Content-type':
'application/x-www-form-urlencoded; charset=utf-8',
'X-Amz-Date': '20110909T233600Z',
'Host': 'foo:8000',
'Authorization': auth_str}
# Note the example in the AWS docs is inconsistent, previous
# examples specify no query string, but the final POST example
# does, apparently incorrectly since an empty parameter list
# aligns all steps and the final signature with the examples
params = {}
credentials = {'host': 'foo:8000',
'verb': 'POST',
'path': '/',
'params': params,
'headers': headers,
'body_hash': body_hash}
signature = signer.generate(credentials)
expected = ('26dd92ea79aaa49f533d13b1055acdc'
'd7d7321460d64621f96cc79c4f4d4ab2b')
self.assertEqual(signature, expected)
def test_generate_v4_port_strip(self):
"""Test v4 generator with host:port format, but for an old
(<2.9.3) version of boto, where the port should be stripped
to match boto behavior.
"""
# Create a new signer object with the AWS example key
secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
signer = utils.Ec2Signer(secret)
body_hash = ('b6359072c78d70ebee1e81adcbab4f0'
'1bf2c23245fa365ef83fe8f1f955085e2')
auth_str = ('AWS4-HMAC-SHA256 '
'Credential=AKIAIOSFODNN7EXAMPLE/20110909/'
'us-east-1/iam/aws4_request,'
'SignedHeaders=content-type;host;x-amz-date,')
headers = {'Content-type':
'application/x-www-form-urlencoded; charset=utf-8',
'X-Amz-Date': '20110909T233600Z',
'Host': 'foo:8000',
'Authorization': auth_str,
'User-Agent': 'Boto/2.9.2 (linux2)'}
# Note the example in the AWS docs is inconsistent, previous
# examples specify no query string, but the final POST example
# does, apparently incorrectly since an empty parameter list
# aligns all steps and the final signature with the examples
params = {}
credentials = {'host': 'foo:8000',
'verb': 'POST',
'path': '/',
'params': params,
'headers': headers,
'body_hash': body_hash}
signature = signer.generate(credentials)
expected = ('9a4b2276a5039ada3b90f72ea8ec1745'
'14b92b909fb106b22ad910c5d75a54f4')
self.assertEqual(expected, signature)
def test_generate_v4_port_nostrip(self):
"""Test v4 generator with host:port format, but for an new
(>=2.9.3) version of boto, where the port should not be stripped.
"""
# Create a new signer object with the AWS example key
secret = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
signer = utils.Ec2Signer(secret)
body_hash = ('b6359072c78d70ebee1e81adcbab4f0'
'1bf2c23245fa365ef83fe8f1f955085e2')
auth_str = ('AWS4-HMAC-SHA256 '
'Credential=AKIAIOSFODNN7EXAMPLE/20110909/'
'us-east-1/iam/aws4_request,'
'SignedHeaders=content-type;host;x-amz-date,')
headers = {'Content-type':
'application/x-www-form-urlencoded; charset=utf-8',
'X-Amz-Date': '20110909T233600Z',
'Host': 'foo:8000',
'Authorization': auth_str,
'User-Agent': 'Boto/2.9.3 (linux2)'}
# Note the example in the AWS docs is inconsistent, previous
# examples specify no query string, but the final POST example
# does, apparently incorrectly since an empty parameter list
# aligns all steps and the final signature with the examples
params = {}
credentials = {'host': 'foo:8000',
'verb': 'POST',
'path': '/',
'params': params,
'headers': headers,
'body_hash': body_hash}
signature = signer.generate(credentials)
expected = ('26dd92ea79aaa49f533d13b1055acdc'
'd7d7321460d64621f96cc79c4f4d4ab2b')
self.assertEqual(expected, signature)

209
test_http.py Normal file
View File

@@ -0,0 +1,209 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2013 OpenStack LLC
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
import testtools
from testtools import matchers
from keystoneclient import exceptions
from keystoneclient import httpclient
from keystoneclient.tests import utils
RESPONSE_BODY = '{"hi": "there"}'
def get_client():
cl = httpclient.HTTPClient(username="username", password="password",
tenant_id="tenant", auth_url="auth_test")
return cl
def get_authed_client():
cl = get_client()
cl.management_url = "http://127.0.0.1:5000"
cl.auth_token = "token"
return cl
class FakeLog(object):
def __init__(self):
self.warn_log = str()
self.debug_log = str()
def warn(self, msg=None, *args, **kwargs):
self.warn_log = "%s\n%s" % (self.warn_log, (msg % args))
def debug(self, msg=None, *args, **kwargs):
self.debug_log = "%s\n%s" % (self.debug_log, (msg % args))
class ClientTest(utils.TestCase):
TEST_URL = 'http://127.0.0.1:5000/hi'
def test_unauthorized_client_requests(self):
cl = get_client()
self.assertRaises(exceptions.AuthorizationFailure, cl.get, '/hi')
self.assertRaises(exceptions.AuthorizationFailure, cl.post, '/hi')
self.assertRaises(exceptions.AuthorizationFailure, cl.put, '/hi')
self.assertRaises(exceptions.AuthorizationFailure, cl.delete, '/hi')
@httpretty.activate
def test_get(self):
cl = get_authed_client()
self.stub_url(httpretty.GET, body=RESPONSE_BODY)
resp, body = cl.get("/hi")
self.assertEqual(httpretty.last_request().method, 'GET')
self.assertEqual(httpretty.last_request().path, '/hi')
req_headers = httpretty.last_request().headers
self.assertEqual(req_headers.getheader('X-Auth-Token'), 'token')
self.assertEqual(req_headers.getheader('User-Agent'),
httpclient.USER_AGENT)
# Automatic JSON parsing
self.assertEqual(body, {"hi": "there"})
@httpretty.activate
def test_get_error_with_plaintext_resp(self):
cl = get_authed_client()
self.stub_url(httpretty.GET, status=400,
body='Some evil plaintext string')
self.assertRaises(exceptions.BadRequest, cl.get, '/hi')
@httpretty.activate
def test_get_error_with_json_resp(self):
cl = get_authed_client()
err_response = {
"error": {
"code": 400,
"title": "Error title",
"message": "Error message string"
}
}
self.stub_url(httpretty.GET, status=400, json=err_response)
exc_raised = False
try:
cl.get('/hi')
except exceptions.BadRequest as exc:
exc_raised = True
self.assertEqual(exc.message, "Error message string")
self.assertTrue(exc_raised, 'Exception not raised.')
@httpretty.activate
def test_post(self):
cl = get_authed_client()
self.stub_url(httpretty.POST)
cl.post("/hi", body=[1, 2, 3])
self.assertEqual(httpretty.last_request().method, 'POST')
self.assertEqual(httpretty.last_request().body, '[1, 2, 3]')
req_headers = httpretty.last_request().headers
self.assertEqual(req_headers.getheader('X-Auth-Token'), 'token')
self.assertEqual(req_headers.getheader('Content-Type'),
'application/json')
self.assertEqual(req_headers.getheader('User-Agent'),
httpclient.USER_AGENT)
@httpretty.activate
def test_forwarded_for(self):
ORIGINAL_IP = "10.100.100.1"
cl = httpclient.HTTPClient(username="username", password="password",
tenant_id="tenant", auth_url="auth_test",
original_ip=ORIGINAL_IP)
self.stub_url(httpretty.GET)
cl.request(self.TEST_URL, 'GET')
self.assertEqual(httpretty.last_request().headers['Forwarded'],
"for=%s;by=%s" % (ORIGINAL_IP, httpclient.USER_AGENT))
def test_client_deprecated(self):
# Can resolve symbols from the keystoneclient.client module.
# keystoneclient.client was deprecated and renamed to
# keystoneclient.httpclient. This tests that keystoneclient.client
# can still be used.
from keystoneclient import client
# These statements will raise an AttributeError if the symbol isn't
# defined in the module.
client.HTTPClient
class BasicRequestTests(testtools.TestCase):
url = 'http://keystone.test.com/'
def setUp(self):
super(BasicRequestTests, self).setUp()
self.logger = FakeLog()
def request(self, method='GET', response='Test Response', status=200,
url=None, **kwargs):
if not url:
url = self.url
httpretty.register_uri(method, url, body=response, status=status)
return httpclient.request(url, method, debug=True,
logger=self.logger, **kwargs)
@httpretty.activate
def test_basic_params(self):
method = 'GET'
response = 'Test Response'
status = 200
self.request(method=method, status=status, response=response)
self.assertEqual(httpretty.last_request().method, method)
self.assertThat(self.logger.debug_log, matchers.Contains('curl'))
self.assertThat(self.logger.debug_log, matchers.Contains('-X %s' %
method))
self.assertThat(self.logger.debug_log, matchers.Contains(self.url))
self.assertThat(self.logger.debug_log, matchers.Contains(str(status)))
self.assertThat(self.logger.debug_log, matchers.Contains(response))
@httpretty.activate
def test_headers(self):
headers = {'key': 'val', 'test': 'other'}
self.request(headers=headers)
for k, v in headers.iteritems():
self.assertEqual(httpretty.last_request().headers[k], v)
for header in headers.iteritems():
self.assertThat(self.logger.debug_log,
matchers.Contains('-H "%s: %s"' % header))
@httpretty.activate
def test_body(self):
data = "BODY DATA"
self.request(response=data)
self.assertThat(self.logger.debug_log, matchers.Contains('BODY:'))
self.assertThat(self.logger.debug_log, matchers.Contains(data))

110
test_https.py Normal file
View File

@@ -0,0 +1,110 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
import requests
from keystoneclient import httpclient
from keystoneclient.tests import utils
FAKE_RESPONSE = utils.TestResponse({
"status_code": 200,
"text": '{"hi": "there"}',
})
MOCK_REQUEST = mock.Mock(return_value=(FAKE_RESPONSE))
REQUEST_URL = 'https://127.0.0.1:5000/hi'
RESPONSE_BODY = '{"hi": "there"}'
def get_client():
cl = httpclient.HTTPClient(username="username", password="password",
tenant_id="tenant", auth_url="auth_test",
cacert="ca.pem", key="key.pem", cert="cert.pem")
return cl
def get_authed_client():
cl = get_client()
cl.management_url = "https://127.0.0.1:5000"
cl.auth_token = "token"
return cl
class ClientTest(utils.TestCase):
def setUp(self):
super(ClientTest, self).setUp()
self.request_patcher = mock.patch.object(requests, 'request',
self.mox.CreateMockAnything())
self.request_patcher.start()
def tearDown(self):
self.request_patcher.stop()
super(ClientTest, self).tearDown()
def test_get(self):
cl = get_authed_client()
with mock.patch.object(requests, "request", MOCK_REQUEST):
resp, body = cl.get("/hi")
# this may become too tightly couple later
mock_args, mock_kwargs = MOCK_REQUEST.call_args
self.assertEqual(mock_args[0], 'GET')
self.assertEqual(mock_args[1], REQUEST_URL)
self.assertEqual(mock_kwargs['headers']['X-Auth-Token'], 'token')
self.assertEqual(mock_kwargs['cert'], ('cert.pem', 'key.pem'))
self.assertEqual(mock_kwargs['verify'], 'ca.pem')
# Automatic JSON parsing
self.assertEqual(body, {"hi": "there"})
def test_post(self):
cl = get_authed_client()
with mock.patch.object(requests, "request", MOCK_REQUEST):
cl.post("/hi", body=[1, 2, 3])
# this may become too tightly couple later
mock_args, mock_kwargs = MOCK_REQUEST.call_args
self.assertEqual(mock_args[0], 'POST')
self.assertEqual(mock_args[1], REQUEST_URL)
self.assertEqual(mock_kwargs['data'], '[1, 2, 3]')
self.assertEqual(mock_kwargs['headers']['X-Auth-Token'], 'token')
self.assertEqual(mock_kwargs['cert'], ('cert.pem', 'key.pem'))
self.assertEqual(mock_kwargs['verify'], 'ca.pem')
def test_post_auth(self):
with mock.patch.object(requests, "request", MOCK_REQUEST):
cl = httpclient.HTTPClient(
username="username", password="password", tenant_id="tenant",
auth_url="auth_test", cacert="ca.pem", key="key.pem",
cert="cert.pem")
cl.management_url = "https://127.0.0.1:5000"
cl.auth_token = "token"
cl.post("/hi", body=[1, 2, 3])
# this may become too tightly couple later
mock_args, mock_kwargs = MOCK_REQUEST.call_args
self.assertEqual(mock_args[0], 'POST')
self.assertEqual(mock_args[1], REQUEST_URL)
self.assertEqual(mock_kwargs['data'], '[1, 2, 3]')
self.assertEqual(mock_kwargs['headers']['X-Auth-Token'], 'token')
self.assertEqual(mock_kwargs['cert'], ('cert.pem', 'key.pem'))
self.assertEqual(mock_kwargs['verify'], 'ca.pem')

188
test_keyring.py Normal file
View File

@@ -0,0 +1,188 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import datetime
import mock
from keystoneclient import access
from keystoneclient import httpclient
from keystoneclient.openstack.common import timeutils
from keystoneclient.tests import utils
from keystoneclient.tests.v2_0 import client_fixtures
try:
import keyring # noqa
import pickle # noqa
except ImportError:
keyring = None
PROJECT_SCOPED_TOKEN = client_fixtures.PROJECT_SCOPED_TOKEN
# These mirror values from PROJECT_SCOPED_TOKEN
USERNAME = 'exampleuser'
AUTH_URL = 'http://public.com:5000/v2.0'
TOKEN = '04c7d5ffaeef485f9dc69c06db285bdb'
PASSWORD = 'password'
TENANT = 'tenant'
TENANT_ID = 'tenant_id'
class KeyringTest(utils.TestCase):
def setUp(self):
if keyring is None:
self.skipTest(
'optional package keyring or pickle is not installed')
class MemoryKeyring(keyring.backend.KeyringBackend):
"""A Simple testing keyring.
This class supports stubbing an initial password to be returned by
setting password, and allows easy password and key retrieval. Also
records if a password was retrieved.
"""
def __init__(self):
self.key = None
self.password = None
self.fetched = False
self.get_password_called = False
self.set_password_called = False
def supported(self):
return 1
def get_password(self, service, username):
self.get_password_called = True
key = username + '@' + service
# make sure we don't get passwords crossed if one is enforced.
if self.key and self.key != key:
return None
if self.password:
self.fetched = True
return self.password
def set_password(self, service, username, password):
self.set_password_called = True
self.key = username + '@' + service
self.password = password
super(KeyringTest, self).setUp()
self.memory_keyring = MemoryKeyring()
keyring.set_keyring(self.memory_keyring)
def test_no_keyring_key(self):
"""Ensure that if we don't have use_keyring set in the client that
the keyring is never accessed.
"""
cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD,
tenant_id=TENANT_ID, auth_url=AUTH_URL)
# stub and check that a new token is received
with mock.patch.object(cl, 'get_raw_token_from_identity_service') \
as meth:
meth.return_value = (True, PROJECT_SCOPED_TOKEN)
self.assertTrue(cl.authenticate())
meth.assert_called_once()
# make sure that we never touched the keyring
self.assertFalse(self.memory_keyring.get_password_called)
self.assertFalse(self.memory_keyring.set_password_called)
def test_build_keyring_key(self):
cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD,
tenant_id=TENANT_ID, auth_url=AUTH_URL)
keyring_key = cl._build_keyring_key(auth_url=AUTH_URL,
username=USERNAME,
tenant_name=TENANT,
tenant_id=TENANT_ID,
token=TOKEN)
self.assertEqual(keyring_key,
'%s/%s/%s/%s/%s' %
(AUTH_URL, TENANT_ID, TENANT, TOKEN, USERNAME))
def test_set_and_get_keyring_expired(self):
cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD,
tenant_id=TENANT_ID, auth_url=AUTH_URL,
use_keyring=True)
# set an expired token into the keyring
auth_ref = access.AccessInfo.factory(body=PROJECT_SCOPED_TOKEN)
expired = timeutils.utcnow() - datetime.timedelta(minutes=30)
auth_ref['token']['expires'] = timeutils.isotime(expired)
self.memory_keyring.password = pickle.dumps(auth_ref)
# stub and check that a new token is received, so not using expired
with mock.patch.object(cl, 'get_raw_token_from_identity_service') \
as meth:
meth.return_value = (True, PROJECT_SCOPED_TOKEN)
self.assertTrue(cl.authenticate())
meth.assert_called_once()
# check that a value was returned from the keyring
self.assertTrue(self.memory_keyring.fetched)
# check that the new token has been loaded into the keyring
new_auth_ref = pickle.loads(self.memory_keyring.password)
self.assertEqual(new_auth_ref['token']['expires'],
PROJECT_SCOPED_TOKEN['access']['token']['expires'])
def test_get_keyring(self):
cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD,
tenant_id=TENANT_ID, auth_url=AUTH_URL,
use_keyring=True)
# set an token into the keyring
auth_ref = access.AccessInfo.factory(body=PROJECT_SCOPED_TOKEN)
future = timeutils.utcnow() + datetime.timedelta(minutes=30)
auth_ref['token']['expires'] = timeutils.isotime(future)
self.memory_keyring.password = pickle.dumps(auth_ref)
# don't stub get_raw_token so will fail if authenticate happens
self.assertTrue(cl.authenticate())
self.assertTrue(self.memory_keyring.fetched)
def test_set_keyring(self):
cl = httpclient.HTTPClient(username=USERNAME, password=PASSWORD,
tenant_id=TENANT_ID, auth_url=AUTH_URL,
use_keyring=True)
# stub and check that a new token is received
with mock.patch.object(cl, 'get_raw_token_from_identity_service') \
as meth:
meth.return_value = (True, PROJECT_SCOPED_TOKEN)
self.assertTrue(cl.authenticate())
meth.assert_called_once()
# we checked the keyring, but we didn't find anything
self.assertTrue(self.memory_keyring.get_password_called)
self.assertFalse(self.memory_keyring.fetched)
# check that the new token has been loaded into the keyring
self.assertTrue(self.memory_keyring.set_password_called)
new_auth_ref = pickle.loads(self.memory_keyring.password)
self.assertEqual(new_auth_ref.auth_token, TOKEN)
self.assertEqual(new_auth_ref['token'],
PROJECT_SCOPED_TOKEN['access']['token'])
self.assertEqual(new_auth_ref.username, USERNAME)

88
test_memcache_crypt.py Normal file
View File

@@ -0,0 +1,88 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import testtools
from keystoneclient.middleware import memcache_crypt
class MemcacheCryptPositiveTests(testtools.TestCase):
def _setup_keys(self, strategy):
return memcache_crypt.derive_keys('token', 'secret', strategy)
def test_constant_time_compare(self):
# make sure it works as a compare, the "constant time" aspect
# isn't appropriate to test in unittests
ctc = memcache_crypt.constant_time_compare
self.assertTrue(ctc('abcd', 'abcd'))
self.assertTrue(ctc('', ''))
self.assertFalse(ctc('abcd', 'efgh'))
self.assertFalse(ctc('abc', 'abcd'))
self.assertFalse(ctc('abc', 'abc\x00'))
self.assertFalse(ctc('', 'abc'))
def test_derive_keys(self):
keys = memcache_crypt.derive_keys('token', 'secret', 'strategy')
self.assertEqual(len(keys['ENCRYPTION']),
len(keys['CACHE_KEY']))
self.assertEqual(len(keys['CACHE_KEY']),
len(keys['MAC']))
self.assertNotEqual(keys['ENCRYPTION'],
keys['MAC'])
self.assertIn('strategy', keys.keys())
def test_key_strategy_diff(self):
k1 = self._setup_keys('MAC')
k2 = self._setup_keys('ENCRYPT')
self.assertNotEqual(k1, k2)
def test_sign_data(self):
keys = self._setup_keys('MAC')
sig = memcache_crypt.sign_data(keys['MAC'], 'data')
self.assertEqual(len(sig), memcache_crypt.DIGEST_LENGTH_B64)
def test_encryption(self):
keys = self._setup_keys('ENCRYPT')
# what you put in is what you get out
for data in ['data', '1234567890123456', '\x00\xFF' * 13
] + [chr(x % 256) * x for x in range(768)]:
crypt = memcache_crypt.encrypt_data(keys['ENCRYPTION'], data)
decrypt = memcache_crypt.decrypt_data(keys['ENCRYPTION'], crypt)
self.assertEqual(data, decrypt)
self.assertRaises(memcache_crypt.DecryptError,
memcache_crypt.decrypt_data,
keys['ENCRYPTION'], crypt[:-1])
def test_protect_wrappers(self):
data = 'My Pretty Little Data'
for strategy in ['MAC', 'ENCRYPT']:
keys = self._setup_keys(strategy)
protected = memcache_crypt.protect_data(keys, data)
self.assertNotEqual(protected, data)
if strategy == 'ENCRYPT':
self.assertNotIn(data, protected)
unprotected = memcache_crypt.unprotect_data(keys, protected)
self.assertEqual(data, unprotected)
self.assertRaises(memcache_crypt.InvalidMacError,
memcache_crypt.unprotect_data,
keys, protected[:-1])
self.assertIsNone(memcache_crypt.unprotect_data(keys, None))
def test_no_pycrypt(self):
aes = memcache_crypt.AES
memcache_crypt.AES = None
self.assertRaises(memcache_crypt.CryptoUnavailableError,
memcache_crypt.encrypt_data, 'token', 'secret',
'data')
memcache_crypt.AES = aes

497
test_shell.py Normal file
View File

@@ -0,0 +1,497 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import argparse
import cStringIO
import json
import os
import sys
import uuid
import fixtures
import mock
import testtools
from testtools import matchers
from keystoneclient import exceptions
from keystoneclient import shell as openstack_shell
from keystoneclient.tests import utils
from keystoneclient.v2_0 import shell as shell_v2_0
DEFAULT_USERNAME = 'username'
DEFAULT_PASSWORD = 'password'
DEFAULT_TENANT_ID = 'tenant_id'
DEFAULT_TENANT_NAME = 'tenant_name'
DEFAULT_AUTH_URL = 'http://127.0.0.1:5000/v2.0/'
class NoExitArgumentParser(argparse.ArgumentParser):
def error(self, message):
raise exceptions.CommandError(message)
class ShellTest(utils.TestCase):
FAKE_ENV = {
'OS_USERNAME': DEFAULT_USERNAME,
'OS_PASSWORD': DEFAULT_PASSWORD,
'OS_TENANT_ID': DEFAULT_TENANT_ID,
'OS_TENANT_NAME': DEFAULT_TENANT_NAME,
'OS_AUTH_URL': DEFAULT_AUTH_URL,
}
def _tolerant_shell(self, cmd):
t_shell = openstack_shell.OpenStackIdentityShell(NoExitArgumentParser)
t_shell.main(cmd.split())
# Patch os.environ to avoid required auth info.
def setUp(self):
super(ShellTest, self).setUp()
for var in self.FAKE_ENV:
self.useFixture(fixtures.EnvironmentVariable(var,
self.FAKE_ENV[var]))
# Make a fake shell object, a helping wrapper to call it, and a quick
# way of asserting that certain API calls were made.
global shell, _shell, assert_called, assert_called_anytime
_shell = openstack_shell.OpenStackIdentityShell()
shell = lambda cmd: _shell.main(cmd.split())
def test_help_unknown_command(self):
self.assertRaises(exceptions.CommandError, shell, 'help %s'
% uuid.uuid4().hex)
def shell(self, argstr):
orig = sys.stdout
clean_env = {}
_old_env, os.environ = os.environ, clean_env.copy()
try:
sys.stdout = cStringIO.StringIO()
_shell = openstack_shell.OpenStackIdentityShell()
_shell.main(argstr.split())
except SystemExit:
exc_type, exc_value, exc_traceback = sys.exc_info()
self.assertEqual(exc_value.code, 0)
finally:
out = sys.stdout.getvalue()
sys.stdout.close()
sys.stdout = orig
os.environ = _old_env
return out
def test_help_no_args(self):
do_tenant_mock = mock.MagicMock()
with mock.patch('keystoneclient.shell.OpenStackIdentityShell.do_help',
do_tenant_mock):
self.shell('')
assert do_tenant_mock.called
def test_help(self):
required = 'usage:'
help_text = self.shell('help')
self.assertThat(help_text,
matchers.MatchesRegex(required))
def test_help_command(self):
required = 'usage: keystone user-create'
help_text = self.shell('help user-create')
self.assertThat(help_text,
matchers.MatchesRegex(required))
def test_auth_no_credentials(self):
with testtools.ExpectedException(
exceptions.CommandError, 'Expecting'):
self.shell('user-list')
def test_auth_password_authurl_no_username(self):
with testtools.ExpectedException(
exceptions.CommandError,
'Expecting a username provided via either'):
self.shell('--os-password=%s --os-auth-url=%s user-list'
% (uuid.uuid4().hex, uuid.uuid4().hex))
def test_auth_username_password_no_authurl(self):
with testtools.ExpectedException(
exceptions.CommandError, 'Expecting an auth URL via either'):
self.shell('--os-password=%s --os-username=%s user-list'
% (uuid.uuid4().hex, uuid.uuid4().hex))
def test_token_no_endpoint(self):
with testtools.ExpectedException(
exceptions.CommandError, 'Expecting an endpoint provided'):
self.shell('--token=%s user-list' % uuid.uuid4().hex)
def test_endpoint_no_token(self):
with testtools.ExpectedException(
exceptions.CommandError, 'Expecting a token provided'):
self.shell('--endpoint=http://10.0.0.1:5000/v2.0/ user-list')
def test_shell_args(self):
do_tenant_mock = mock.MagicMock()
with mock.patch('keystoneclient.v2_0.shell.do_user_list',
do_tenant_mock):
shell('user-list')
assert do_tenant_mock.called
((a, b), c) = do_tenant_mock.call_args
actual = (b.os_auth_url, b.os_password, b.os_tenant_id,
b.os_tenant_name, b.os_username,
b.os_identity_api_version)
expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, DEFAULT_TENANT_ID,
DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
# Old_style options
shell('--os_auth_url http://0.0.0.0:5000/ --os_password xyzpdq '
'--os_tenant_id 1234 --os_tenant_name fred '
'--os_username barney '
'--os_identity_api_version 2.0 user-list')
assert do_tenant_mock.called
((a, b), c) = do_tenant_mock.call_args
actual = (b.os_auth_url, b.os_password, b.os_tenant_id,
b.os_tenant_name, b.os_username,
b.os_identity_api_version)
expect = ('http://0.0.0.0:5000/', 'xyzpdq', '1234',
'fred', 'barney', '2.0')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
# New-style options
shell('--os-auth-url http://1.1.1.1:5000/ --os-password xyzpdq '
'--os-tenant-id 4321 --os-tenant-name wilma '
'--os-username betty '
'--os-identity-api-version 2.0 user-list')
assert do_tenant_mock.called
((a, b), c) = do_tenant_mock.call_args
actual = (b.os_auth_url, b.os_password, b.os_tenant_id,
b.os_tenant_name, b.os_username,
b.os_identity_api_version)
expect = ('http://1.1.1.1:5000/', 'xyzpdq', '4321',
'wilma', 'betty', '2.0')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
# Test keyring options
shell('--os-auth-url http://1.1.1.1:5000/ --os-password xyzpdq '
'--os-tenant-id 4321 --os-tenant-name wilma '
'--os-username betty '
'--os-identity-api-version 2.0 '
'--os-cache '
'--stale-duration 500 '
'--force-new-token user-list')
assert do_tenant_mock.called
((a, b), c) = do_tenant_mock.call_args
actual = (b.os_auth_url, b.os_password, b.os_tenant_id,
b.os_tenant_name, b.os_username,
b.os_identity_api_version, b.os_cache,
b.stale_duration, b.force_new_token)
expect = ('http://1.1.1.1:5000/', 'xyzpdq', '4321',
'wilma', 'betty', '2.0', True, '500', True)
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
def test_shell_user_create_args(self):
"""Test user-create args."""
do_uc_mock = mock.MagicMock()
# grab the decorators for do_user_create
uc_func = getattr(shell_v2_0, 'do_user_create')
do_uc_mock.arguments = getattr(uc_func, 'arguments', [])
with mock.patch('keystoneclient.v2_0.shell.do_user_create',
do_uc_mock):
# Old_style options
# Test case with one --tenant_id args present: ec2 creds
shell('user-create --name=FOO '
'--pass=secrete --tenant_id=barrr --enabled=true')
assert do_uc_mock.called
((a, b), c) = do_uc_mock.call_args
actual = (b.os_auth_url, b.os_password, b.os_tenant_id,
b.os_tenant_name, b.os_username,
b.os_identity_api_version)
expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, DEFAULT_TENANT_ID,
DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
actual = (b.tenant_id, b.name, b.passwd, b.enabled)
expect = ('barrr', 'FOO', 'secrete', 'true')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
# New-style options
# Test case with one --tenant args present: ec2 creds
shell('user-create --name=foo '
'--pass=secrete --tenant=BARRR --enabled=true')
assert do_uc_mock.called
((a, b), c) = do_uc_mock.call_args
actual = (b.os_auth_url, b.os_password, b.os_tenant_id,
b.os_tenant_name, b.os_username,
b.os_identity_api_version)
expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, DEFAULT_TENANT_ID,
DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
actual = (b.tenant, b.name, b.passwd, b.enabled)
expect = ('BARRR', 'foo', 'secrete', 'true')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
# New-style options
# Test case with one --tenant-id args present: ec2 creds
shell('user-create --name=foo '
'--pass=secrete --tenant-id=BARRR --enabled=true')
assert do_uc_mock.called
((a, b), c) = do_uc_mock.call_args
actual = (b.os_auth_url, b.os_password, b.os_tenant_id,
b.os_tenant_name, b.os_username,
b.os_identity_api_version)
expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, DEFAULT_TENANT_ID,
DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
actual = (b.tenant, b.name, b.passwd, b.enabled)
expect = ('BARRR', 'foo', 'secrete', 'true')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
# Old_style options
# Test case with --os_tenant_id and --tenant_id args present
shell('--os_tenant_id=os-tenant user-create --name=FOO '
'--pass=secrete --tenant_id=barrr --enabled=true')
assert do_uc_mock.called
((a, b), c) = do_uc_mock.call_args
actual = (b.os_auth_url, b.os_password, b.os_tenant_id,
b.os_tenant_name, b.os_username,
b.os_identity_api_version)
expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, 'os-tenant',
DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
actual = (b.tenant_id, b.name, b.passwd, b.enabled)
expect = ('barrr', 'FOO', 'secrete', 'true')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
# New-style options
# Test case with --os-tenant-id and --tenant-id args present
shell('--os-tenant-id=ostenant user-create --name=foo '
'--pass=secrete --tenant-id=BARRR --enabled=true')
assert do_uc_mock.called
((a, b), c) = do_uc_mock.call_args
actual = (b.os_auth_url, b.os_password, b.os_tenant_id,
b.os_tenant_name, b.os_username,
b.os_identity_api_version)
expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, 'ostenant',
DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
actual = (b.tenant, b.name, b.passwd, b.enabled)
expect = ('BARRR', 'foo', 'secrete', 'true')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
def test_do_tenant_create(self):
do_tenant_mock = mock.MagicMock()
with mock.patch('keystoneclient.v2_0.shell.do_tenant_create',
do_tenant_mock):
shell('tenant-create')
assert do_tenant_mock.called
# FIXME(dtroyer): how do you test the decorators?
#shell('tenant-create --tenant-name wilma '
# '--description "fred\'s wife"')
#assert do_tenant_mock.called
def test_do_tenant_list(self):
do_tenant_mock = mock.MagicMock()
with mock.patch('keystoneclient.v2_0.shell.do_tenant_list',
do_tenant_mock):
shell('tenant-list')
assert do_tenant_mock.called
def test_shell_tenant_id_args(self):
"""Test a corner case where --tenant_id appears on the
command-line twice.
"""
do_ec2_mock = mock.MagicMock()
# grab the decorators for do_ec2_create_credentials
ec2_func = getattr(shell_v2_0, 'do_ec2_credentials_create')
do_ec2_mock.arguments = getattr(ec2_func, 'arguments', [])
with mock.patch('keystoneclient.v2_0.shell.do_ec2_credentials_create',
do_ec2_mock):
# Old_style options
# Test case with one --tenant_id args present: ec2 creds
shell('ec2-credentials-create '
'--tenant_id=ec2-tenant --user_id=ec2-user')
assert do_ec2_mock.called
((a, b), c) = do_ec2_mock.call_args
actual = (b.os_auth_url, b.os_password, b.os_tenant_id,
b.os_tenant_name, b.os_username,
b.os_identity_api_version)
expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, DEFAULT_TENANT_ID,
DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
actual = (b.tenant_id, b.user_id)
expect = ('ec2-tenant', 'ec2-user')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
# New-style options
# Test case with one --tenant-id args present: ec2 creds
shell('ec2-credentials-create '
'--tenant-id=dash-tenant --user-id=dash-user')
assert do_ec2_mock.called
((a, b), c) = do_ec2_mock.call_args
actual = (b.os_auth_url, b.os_password, b.os_tenant_id,
b.os_tenant_name, b.os_username,
b.os_identity_api_version)
expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, DEFAULT_TENANT_ID,
DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
actual = (b.tenant_id, b.user_id)
expect = ('dash-tenant', 'dash-user')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
# Old_style options
# Test case with two --tenant_id args present
shell('--os_tenant_id=os-tenant ec2-credentials-create '
'--tenant_id=ec2-tenant --user_id=ec2-user')
assert do_ec2_mock.called
((a, b), c) = do_ec2_mock.call_args
actual = (b.os_auth_url, b.os_password, b.os_tenant_id,
b.os_tenant_name, b.os_username,
b.os_identity_api_version)
expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, 'os-tenant',
DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
actual = (b.tenant_id, b.user_id)
expect = ('ec2-tenant', 'ec2-user')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
# New-style options
# Test case with two --tenant-id args present
shell('--os-tenant-id=ostenant ec2-credentials-create '
'--tenant-id=dash-tenant --user-id=dash-user')
assert do_ec2_mock.called
((a, b), c) = do_ec2_mock.call_args
actual = (b.os_auth_url, b.os_password, b.os_tenant_id,
b.os_tenant_name, b.os_username,
b.os_identity_api_version)
expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, 'ostenant',
DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
actual = (b.tenant_id, b.user_id)
expect = ('dash-tenant', 'dash-user')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
def test_do_ec2_get(self):
do_shell_mock = mock.MagicMock()
with mock.patch('keystoneclient.v2_0.shell.do_ec2_credentials_create',
do_shell_mock):
shell('ec2-credentials-create')
assert do_shell_mock.called
with mock.patch('keystoneclient.v2_0.shell.do_ec2_credentials_get',
do_shell_mock):
shell('ec2-credentials-get')
assert do_shell_mock.called
with mock.patch('keystoneclient.v2_0.shell.do_ec2_credentials_list',
do_shell_mock):
shell('ec2-credentials-list')
assert do_shell_mock.called
with mock.patch('keystoneclient.v2_0.shell.do_ec2_credentials_delete',
do_shell_mock):
shell('ec2-credentials-delete')
assert do_shell_mock.called
def test_timeout_parse_invalid_type(self):
for f in ['foobar', 'xyz']:
cmd = '--timeout %s endpoint-create' % (f)
self.assertRaises(exceptions.CommandError,
self._tolerant_shell, cmd)
def test_timeout_parse_invalid_number(self):
for f in [-1, 0]:
cmd = '--timeout %s endpoint-create' % (f)
self.assertRaises(exceptions.CommandError,
self._tolerant_shell, cmd)
def test_do_timeout(self):
response_mock = mock.MagicMock()
response_mock.status_code = 200
response_mock.text = json.dumps({
'endpoints': [],
})
request_mock = mock.MagicMock(return_value=response_mock)
with mock.patch('requests.request', request_mock):
shell(('--timeout 2 --os-token=blah --os-endpoint=blah'
' --os-auth-url=blah.com endpoint-list'))
request_mock.assert_called_with(mock.ANY, mock.ANY,
timeout=2,
headers=mock.ANY,
verify=mock.ANY)
def test_do_endpoints(self):
do_shell_mock = mock.MagicMock()
# grab the decorators for do_endpoint_create
shell_func = getattr(shell_v2_0, 'do_endpoint_create')
do_shell_mock.arguments = getattr(shell_func, 'arguments', [])
with mock.patch('keystoneclient.v2_0.shell.do_endpoint_create',
do_shell_mock):
# Old_style options
# Test create args
shell('endpoint-create '
'--service_id=2 --publicurl=http://example.com:1234/go '
'--adminurl=http://example.com:9876/adm')
assert do_shell_mock.called
((a, b), c) = do_shell_mock.call_args
actual = (b.os_auth_url, b.os_password, b.os_tenant_id,
b.os_tenant_name, b.os_username,
b.os_identity_api_version)
expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, DEFAULT_TENANT_ID,
DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
actual = (b.service, b.publicurl, b.adminurl)
expect = ('2',
'http://example.com:1234/go',
'http://example.com:9876/adm')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
# New-style options
# Test create args
shell('endpoint-create '
'--service-id=3 --publicurl=http://example.com:4321/go '
'--adminurl=http://example.com:9876/adm')
assert do_shell_mock.called
((a, b), c) = do_shell_mock.call_args
actual = (b.os_auth_url, b.os_password, b.os_tenant_id,
b.os_tenant_name, b.os_username,
b.os_identity_api_version)
expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, DEFAULT_TENANT_ID,
DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
actual = (b.service, b.publicurl, b.adminurl)
expect = ('3',
'http://example.com:4321/go',
'http://example.com:9876/adm')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
# New-style options
# Test create args
shell('endpoint-create '
'--service=3 --publicurl=http://example.com:4321/go '
'--adminurl=http://example.com:9876/adm')
assert do_shell_mock.called
((a, b), c) = do_shell_mock.call_args
actual = (b.os_auth_url, b.os_password, b.os_tenant_id,
b.os_tenant_name, b.os_username,
b.os_identity_api_version)
expect = (DEFAULT_AUTH_URL, DEFAULT_PASSWORD, DEFAULT_TENANT_ID,
DEFAULT_TENANT_NAME, DEFAULT_USERNAME, '')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))
actual = (b.service, b.publicurl, b.adminurl)
expect = ('3',
'http://example.com:4321/go',
'http://example.com:9876/adm')
self.assertTrue(all([x == y for x, y in zip(actual, expect)]))

93
test_utils.py Normal file
View File

@@ -0,0 +1,93 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from keystoneclient import exceptions
from keystoneclient.tests import utils as test_utils
from keystoneclient import utils
class FakeResource(object):
pass
class FakeManager(object):
resource_class = FakeResource
resources = {
'1234': {'name': 'entity_one'},
'8e8ec658-c7b0-4243-bdf8-6f7f2952c0d0': {'name': 'entity_two'},
'\xe3\x82\xbdtest': {'name': u'\u30bdtest'},
'5678': {'name': '9876'}
}
def get(self, resource_id):
try:
return self.resources[str(resource_id)]
except KeyError:
raise exceptions.NotFound(resource_id)
def find(self, name=None):
if name == '9999':
# NOTE(morganfainberg): special case that raises NoUniqueMatch.
raise exceptions.NoUniqueMatch()
for resource_id, resource in self.resources.items():
if resource['name'] == str(name):
return resource
raise exceptions.NotFound(name)
class FindResourceTestCase(test_utils.TestCase):
def setUp(self):
super(FindResourceTestCase, self).setUp()
self.manager = FakeManager()
def test_find_none(self):
self.assertRaises(exceptions.CommandError,
utils.find_resource,
self.manager,
'asdf')
def test_find_by_integer_id(self):
output = utils.find_resource(self.manager, 1234)
self.assertEqual(output, self.manager.resources['1234'])
def test_find_by_str_id(self):
output = utils.find_resource(self.manager, '1234')
self.assertEqual(output, self.manager.resources['1234'])
def test_find_by_uuid(self):
uuid = '8e8ec658-c7b0-4243-bdf8-6f7f2952c0d0'
output = utils.find_resource(self.manager, uuid)
self.assertEqual(output, self.manager.resources[uuid])
def test_find_by_unicode(self):
name = '\xe3\x82\xbdtest'
output = utils.find_resource(self.manager, name)
self.assertEqual(output, self.manager.resources[name])
def test_find_by_str_name(self):
output = utils.find_resource(self.manager, 'entity_one')
self.assertEqual(output, self.manager.resources['1234'])
def test_find_by_int_name(self):
output = utils.find_resource(self.manager, 9876)
self.assertEqual(output, self.manager.resources['5678'])
def test_find_no_unique_match(self):
self.assertRaises(exceptions.CommandError,
utils.find_resource,
self.manager,
9999)

117
utils.py Normal file
View File

@@ -0,0 +1,117 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import sys
import time
import httpretty
import mock
from mox3 import mox
import requests
import testtools
from keystoneclient.openstack.common import jsonutils
class TestCase(testtools.TestCase):
TEST_DOMAIN_ID = '1'
TEST_DOMAIN_NAME = 'aDomain'
TEST_TENANT_ID = '1'
TEST_TENANT_NAME = 'aTenant'
TEST_TOKEN = 'aToken'
TEST_USER = 'test'
TEST_ROOT_URL = 'http://127.0.0.1:5000/'
def setUp(self):
super(TestCase, self).setUp()
self.mox = mox.Mox()
self.time_patcher = mock.patch.object(time, 'time', lambda: 1234)
self.time_patcher.start()
def tearDown(self):
self.time_patcher.stop()
self.mox.UnsetStubs()
self.mox.VerifyAll()
super(TestCase, self).tearDown()
def stub_url(self, method, parts=None, base_url=None, json=None, **kwargs):
if not base_url:
base_url = self.TEST_URL
if json:
kwargs['body'] = jsonutils.dumps(json)
kwargs['content_type'] = 'application/json'
if parts:
url = '/'.join([p.strip('/') for p in [base_url] + parts])
else:
url = base_url
httpretty.register_uri(method, url, **kwargs)
def assertRequestBodyIs(self, body=None, json=None):
if json:
val = jsonutils.loads(httpretty.last_request().body)
self.assertEqual(json, val)
elif body:
self.assertEqual(body, httpretty.last_request().body)
def assertQueryStringIs(self, val):
self.assertEqual(httpretty.last_request().querystring, val)
if tuple(sys.version_info)[0:2] < (2, 7):
def assertDictEqual(self, d1, d2, msg=None):
# Simple version taken from 2.7
self.assertIsInstance(d1, dict,
'First argument is not a dictionary')
self.assertIsInstance(d2, dict,
'Second argument is not a dictionary')
if d1 != d2:
if msg:
self.fail(msg)
else:
standardMsg = '%r != %r' % (d1, d2)
self.fail(standardMsg)
TestCase.assertDictEqual = assertDictEqual
class TestResponse(requests.Response):
"""Class used to wrap requests.Response and provide some
convenience to initialize with a dict.
"""
def __init__(self, data):
self._text = None
super(TestResponse, self).__init__()
if isinstance(data, dict):
self.status_code = data.get('status_code', None)
headers = data.get('headers')
if headers:
self.headers.update(headers)
# Fake the text attribute to streamline Response creation
# _content is defined by requests.Response
self._content = data.get('text', None)
else:
self.status_code = data
def __eq__(self, other):
return self.__dict__ == other.__dict__
@property
def text(self):
return self.content

0
v2_0/__init__.py Normal file
View File

180
v2_0/client_fixtures.py Normal file
View File

@@ -0,0 +1,180 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import unicode_literals
UNSCOPED_TOKEN = {
'access': {'serviceCatalog': {},
'token': {'expires': '2012-10-03T16:58:01Z',
'id': '3e2813b7ba0b4006840c3825860b86ed'},
'user': {'id': 'c4da488862bd435c9e6c0275a0d0e49a',
'name': 'exampleuser',
'roles': [],
'roles_links': [],
'username': 'exampleuser'}
}
}
PROJECT_SCOPED_TOKEN = {
'access': {
'serviceCatalog': [{
'endpoints': [{
'adminURL': 'http://admin:8776/v1/225da22d3ce34b15877ea70b2a575f58',
'internalURL':
'http://internal:8776/v1/225da22d3ce34b15877ea70b2a575f58',
'publicURL':
'http://public.com:8776/v1/225da22d3ce34b15877ea70b2a575f58',
'region': 'RegionOne'
}],
'endpoints_links': [],
'name': 'Volume Service',
'type': 'volume'},
{'endpoints': [{
'adminURL': 'http://admin:9292/v1',
'internalURL': 'http://internal:9292/v1',
'publicURL': 'http://public.com:9292/v1',
'region': 'RegionOne'}],
'endpoints_links': [],
'name': 'Image Service',
'type': 'image'},
{'endpoints': [{
'adminURL': 'http://admin:8774/v2/225da22d3ce34b15877ea70b2a575f58',
'internalURL': 'http://internal:8774/v2/225da22d3ce34b15877ea70b2a575f58',
'publicURL': 'http://public.com:8774/v2/225da22d3ce34b15877ea70b2a575f58',
'region': 'RegionOne'}],
'endpoints_links': [],
'name': 'Compute Service',
'type': 'compute'},
{'endpoints': [{
'adminURL': 'http://admin:8773/services/Admin',
'internalURL': 'http://internal:8773/services/Cloud',
'publicURL': 'http://public.com:8773/services/Cloud',
'region': 'RegionOne'}],
'endpoints_links': [],
'name': 'EC2 Service',
'type': 'ec2'},
{'endpoints': [{
'adminURL': 'http://admin:35357/v2.0',
'internalURL': 'http://internal:5000/v2.0',
'publicURL': 'http://public.com:5000/v2.0',
'region': 'RegionOne'}],
'endpoints_links': [],
'name': 'Identity Service',
'type': 'identity'}],
'token': {'expires': '2012-10-03T16:53:36Z',
'id': '04c7d5ffaeef485f9dc69c06db285bdb',
'tenant': {'description': '',
'enabled': True,
'id': '225da22d3ce34b15877ea70b2a575f58',
'name': 'exampleproject'}},
'user': {'id': 'c4da488862bd435c9e6c0275a0d0e49a',
'name': 'exampleuser',
'roles': [{'id': 'edc12489faa74ee0aca0b8a0b4d74a74',
'name': 'Member'}],
'roles_links': [],
'username': 'exampleuser'}
}
}
AUTH_RESPONSE_BODY = {
'access': {
'token': {
'id': 'ab48a9efdfedb23ty3494',
'expires': '2010-11-01T03:32:15-05:00',
'tenant': {
'id': '345',
'name': 'My Project'
}
},
'user': {
'id': '123',
'name': 'jqsmith',
'roles': [{
'id': '234',
'name': 'compute:admin'
}, {
'id': '235',
'name': 'object-store:admin',
'tenantId': '1'
}],
'roles_links': []
},
'serviceCatalog': [{
'name': 'Cloud Servers',
'type': 'compute',
'endpoints': [{
'tenantId': '1',
'publicURL': 'https://compute.north.host/v1/1234',
'internalURL': 'https://compute.north.host/v1/1234',
'region': 'North',
'versionId': '1.0',
'versionInfo': 'https://compute.north.host/v1.0/',
'versionList': 'https://compute.north.host/'
}, {
'tenantId': '2',
'publicURL': 'https://compute.north.host/v1.1/3456',
'internalURL': 'https://compute.north.host/v1.1/3456',
'region': 'North',
'versionId': '1.1',
'versionInfo': 'https://compute.north.host/v1.1/',
'versionList': 'https://compute.north.host/'
}],
'endpoints_links': []
}, {
'name': 'Cloud Files',
'type': 'object-store',
'endpoints': [{
'tenantId': '11',
'publicURL': 'https://swift.north.host/v1/blah',
'internalURL': 'https://swift.north.host/v1/blah',
'region': 'South',
'versionId': '1.0',
'versionInfo': 'uri',
'versionList': 'uri'
}, {
'tenantId': '2',
'publicURL': 'https://swift.north.host/v1.1/blah',
'internalURL': 'https://compute.north.host/v1.1/blah',
'region': 'South',
'versionId': '1.1',
'versionInfo': 'https://swift.north.host/v1.1/',
'versionList': 'https://swift.north.host/'
}],
'endpoints_links': [{
'rel': 'next',
'href': 'https://identity.north.host/v2.0/'
'endpoints?marker=2'
}]
}, {
'name': 'Image Servers',
'type': 'image',
'endpoints': [{
'publicURL': 'https://image.north.host/v1/',
'internalURL': 'https://image-internal.north.host/v1/',
'region': 'North'
}, {
'publicURL': 'https://image.south.host/v1/',
'internalURL': 'https://image-internal.south.host/v1/',
'region': 'South'
}],
'endpoints_links': []
}],
'serviceCatalog_links': [{
'rel': 'next',
'href': ('https://identity.host/v2.0/endpoints?'
'session=2hfh8Ar&marker=2')
}]
}
}

494
v2_0/fakes.py Normal file
View File

@@ -0,0 +1,494 @@
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# Copyright 2011 OpenStack, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import urlparse
from keystoneclient.tests import fakes
from keystoneclient.tests.v2_0 import utils
class FakeHTTPClient(fakes.FakeClient):
def __init__(self, **kwargs):
self.username = 'username'
self.password = 'password'
self.auth_url = 'auth_url'
self.callstack = []
def _cs_request(self, url, method, **kwargs):
# Check that certain things are called correctly
if method in ['GET', 'DELETE']:
assert 'body' not in kwargs
elif method == 'PUT':
kwargs.setdefault('body', None)
# Call the method
args = urlparse.parse_qsl(urlparse.urlparse(url)[4])
kwargs.update(args)
munged_url = url.rsplit('?', 1)[0]
munged_url = munged_url.strip('/').replace('/', '_').replace('.', '_')
munged_url = munged_url.replace('-', '_')
callback = "%s_%s" % (method.lower(), munged_url)
if not hasattr(self, callback):
raise AssertionError('Called unknown API method: %s %s, '
'expected fakes method name: %s' %
(method, url, callback))
# Note the call
self.callstack.append((method, url, kwargs.get('body', None)))
if not hasattr(self, callback):
raise AssertionError('Called unknown API method: %s %s, '
'expected fakes method name: %s' %
(method, url, callback))
# Note the call
self.callstack.append((method, url, kwargs.get('body', None)))
status, body = getattr(self, callback)(**kwargs)
r = utils.TestResponse({
"status_code": status,
"text": body})
return r, body
#
# List all extensions
#
def post_tokens(self, **kw):
body = [
{"access":
{"token":
{"expires": "2012-02-05T00:00:00",
"id": "887665443383838",
"tenant":
{"id": "1",
"name": "customer-x"}},
"serviceCatalog": [
{"endpoints": [
{"adminURL": "http://swift.admin-nets.local:8080/",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:8080/v1/AUTH_1",
"publicURL":
"http://swift.publicinternets.com/v1/AUTH_1"}],
"type": "object-store",
"name": "swift"},
{"endpoints": [
{"adminURL": "http://cdn.admin-nets.local/v1.1/1",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:7777/v1.1/1",
"publicURL":
"http://cdn.publicinternets.com/v1.1/1"}],
"type": "object-store",
"name": "cdn"}],
"user":
{"id": "1",
"roles": [
{"tenantId": "1",
"id": "3",
"name": "Member"}],
"name": "joeuser"}}
}
]
return (200, body)
def get_tokens_887665443383838(self, **kw):
body = [
{"access":
{"token":
{"expires": "2012-02-05T00:00:00",
"id": "887665443383838",
"tenant": {"id": "1",
"name": "customer-x"}},
"user":
{"name": "joeuser",
"tenantName": "customer-x",
"id": "1",
"roles": [
{"serviceId": "1",
"id": "3",
"name": "Member"}],
"tenantId": "1"}}
}
]
return (200, body)
def get_tokens_887665443383838_endpoints(self, **kw):
body = [
{"endpoints_links": [
{"href":
"http://127.0.0.1:35357/tokens/887665443383838"
"/endpoints?'marker=5&limit=10'",
"rel": "next"}],
"endpoints": [
{"internalURL": "http://127.0.0.1:8080/v1/AUTH_1",
"name": "swift",
"adminURL": "http://swift.admin-nets.local:8080/",
"region": "RegionOne",
"tenantId": 1,
"type": "object-store",
"id": 1,
"publicURL": "http://swift.publicinternets.com/v1/AUTH_1"},
{"internalURL": "http://localhost:8774/v1.0",
"name": "nova_compat",
"adminURL": "http://127.0.0.1:8774/v1.0",
"region": "RegionOne",
"tenantId": 1,
"type": "compute",
"id": 2,
"publicURL": "http://nova.publicinternets.com/v1.0/"},
{"internalURL": "http://localhost:8774/v1.1",
"name": "nova",
"adminURL": "http://127.0.0.1:8774/v1.1",
"region": "RegionOne",
"tenantId": 1,
"type": "compute",
"id": 3,
"publicURL": "http://nova.publicinternets.com/v1.1/"},
{"internalURL": "http://127.0.0.1:9292/v1.1/",
"name": "glance",
"adminURL": "http://nova.admin-nets.local/v1.1/",
"region": "RegionOne",
"tenantId": 1,
"type": "image",
"id": 4,
"publicURL": "http://glance.publicinternets.com/v1.1/"},
{"internalURL": "http://127.0.0.1:7777/v1.1/1",
"name": "cdn",
"adminURL": "http://cdn.admin-nets.local/v1.1/1",
"region": "RegionOne",
"tenantId": 1,
"versionId": "1.1",
"versionList": "http://127.0.0.1:7777/",
"versionInfo": "http://127.0.0.1:7777/v1.1",
"type": "object-store",
"id": 5,
"publicURL": "http://cdn.publicinternets.com/v1.1/1"}]
}
]
return (200, body)
def get(self, **kw):
body = {
"version": {
"id": "v2.0",
"status": "beta",
"updated": "2011-11-19T00:00:00Z",
"links": [
{"rel": "self",
"href": "http://127.0.0.1:35357/v2.0/"},
{"rel": "describedby",
"type": "text/html",
"href": "http://docs.openstack.org/"
"api/openstack-identity-service/2.0/content/"},
{"rel": "describedby",
"type": "application/pdf",
"href": "http://docs.openstack.org/api/"
"openstack-identity-service/2.0/identity-dev-guide-2.0.pdf"},
{"rel": "describedby",
"type": "application/vnd.sun.wadl+xml",
"href": "http://127.0.0.1:35357/v2.0/identity-admin.wadl"}],
"media-types": [
{"base": "application/xml",
"type": "application/vnd.openstack.identity-v2.0+xml"},
{"base": "application/json",
"type": "application/vnd.openstack.identity-v2.0+json"}]
}
}
return (200, body)
def get_extensions(self, **kw):
body = {
"extensions": {"values": []}
}
return (200, body)
def post_tenants(self, **kw):
body = {"tenant":
{"enabled": True,
"description": None,
"name": "new-tenant",
"id": "1"}}
return (200, body)
def post_tenants_2(self, **kw):
body = {"tenant":
{"enabled": False,
"description": "desc",
"name": "new-tenant1",
"id": "2"}}
return (200, body)
def get_tenants(self, **kw):
body = {
"tenants_links": [],
"tenants": [
{"enabled": False,
"description": None,
"name": "project-y",
"id": "1"},
{"enabled": True,
"description": None,
"name": "new-tenant",
"id": "2"},
{"enabled": True,
"description": None,
"name": "customer-x",
"id": "1"}]
}
return (200, body)
def get_tenants_1(self, **kw):
body = {"tenant":
{"enabled": True,
"description": None,
"name": "new-tenant",
"id": "1"}}
return (200, body)
def get_tenants_2(self, **kw):
body = {"tenant":
{"enabled": True,
"description": None,
"name": "new-tenant",
"id": "2"}}
return (200, body)
def delete_tenants_2(self, **kw):
body = {}
return (200, body)
def get_tenants_1_users_1_roles(self, **kw):
body = {
"roles": [
{"id": "1",
"name": "Admin"},
{"id": "2",
"name": "Member"},
{"id": "3",
"name": "new-role"}]
}
return (200, body)
def put_users_1_roles_OS_KSADM_1(self, **kw):
body = {
"roles":
{"id": "1",
"name": "Admin"}}
return (200, body)
def delete_users_1_roles_OS_KSADM_1(self, **kw):
body = {}
return (200, body)
def put_tenants_1_users_1_roles_OS_KSADM_1(self, **kw):
body = {
"role":
{"id": "1",
"name": "Admin"}}
return (200, body)
def get_users(self, **kw):
body = {
"users": [
{"name": self.username,
"enabled": "true",
"email": "sdfsdf@sdfsd.sdf",
"id": "1",
"tenantId": "1"},
{"name": "user2",
"enabled": "true",
"email": "sdfsdf@sdfsd.sdf",
"id": "2",
"tenantId": "1"}]
}
return (200, body)
def get_users_1(self, **kw):
body = {
"user": {
"tenantId": "1",
"enabled": "true",
"id": "1",
"name": self.username}
}
return (200, body)
def put_users_1(self, **kw):
body = {
"user": {
"tenantId": "1",
"enabled": "true",
"id": "1",
"name": "new-user1",
"email": "user@email.com"}
}
return (200, body)
def put_users_1_OS_KSADM_password(self, **kw):
body = {
"user": {
"tenantId": "1",
"enabled": "true",
"id": "1",
"name": "new-user1",
"email": "user@email.com"}
}
return (200, body)
def post_users(self, **kw):
body = {
"user": {
"tenantId": "1",
"enabled": "true",
"id": "1",
"name": self.username}
}
return (200, body)
def delete_users_1(self, **kw):
body = []
return (200, body)
def get_users_1_roles(self, **kw):
body = [
{"roles_links": [],
"roles":[
{"id": "2",
"name": "KeystoneServiceAdmin"}]
}
]
return (200, body)
def post_OS_KSADM_roles(self, **kw):
body = {"role":
{"name": "new-role",
"id": "1"}}
return (200, body)
def get_OS_KSADM_roles(self, **kw):
body = {"roles": [
{"id": "10", "name": "admin"},
{"id": "20", "name": "member"},
{"id": "1", "name": "new-role"}]
}
return (200, body)
def get_OS_KSADM_roles_1(self, **kw):
body = {"role":
{"name": "new-role",
"id": "1"}
}
return (200, body)
def delete_OS_KSADM_roles_1(self, **kw):
body = {}
return (200, body)
def post_OS_KSADM_services(self, **kw):
body = {"OS-KSADM:service":
{"id": "1",
"type": "compute",
"name": "service1",
"description": None}
}
return (200, body)
def get_OS_KSADM_services_1(self, **kw):
body = {"OS-KSADM:service":
{"description": None,
"type": "compute",
"id": "1",
"name": "service1"}
}
return (200, body)
def get_OS_KSADM_services(self, **kw):
body = {
"OS-KSADM:services": [
{"description": None,
"type": "compute",
"id": "1",
"name": "service1"},
{"description": None,
"type": "identity",
"id": "2",
"name": "service2"}]
}
return (200, body)
def delete_OS_KSADM_services_1(self, **kw):
body = {}
return (200, body)
def post_users_1_credentials_OS_EC2(self, **kw):
body = {"credential":
{"access": "1",
"tenant_id": "1",
"secret": "1",
"user_id": "1"}
}
return (200, body)
def get_users_1_credentials_OS_EC2(self, **kw):
body = {"credentials": [
{"access": "1",
"tenant_id": "1",
"secret": "1",
"user_id": "1"}]
}
return (200, body)
def get_users_1_credentials_OS_EC2_2(self, **kw):
body = {
"credential":
{"access": "2",
"tenant_id": "1",
"secret": "1",
"user_id": "1"}
}
return (200, body)
def delete_users_1_credentials_OS_EC2_2(self, **kw):
body = {}
return (200, body)
def patch_OS_KSCRUD_users_1(self, **kw):
body = {}
return (200, body)
def get_endpoints(self, **kw):
body = {
'endpoints': [
{'adminURL': 'http://cdn.admin-nets.local/v1.1/1',
'region': 'RegionOne',
'internalURL': 'http://127.0.0.1:7777/v1.1/1',
'publicURL': 'http://cdn.publicinternets.com/v1.1/1'}],
'type': 'compute',
'name': 'nova-compute'
}
return (200, body)
def post_endpoints(self, **kw):
body = {
"endpoint":
{"adminURL": "http://swift.admin-nets.local:8080/",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:8080/v1/AUTH_1",
"publicURL": "http://swift.publicinternets.com/v1/AUTH_1"},
"type": "compute",
"name": "nova-compute"
}
return (200, body)

125
v2_0/test_access.py Normal file
View File

@@ -0,0 +1,125 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import datetime
from keystoneclient import access
from keystoneclient.openstack.common import timeutils
from keystoneclient.tests import client_fixtures as token_data
from keystoneclient.tests.v2_0 import client_fixtures
from keystoneclient.tests.v2_0 import utils
UNSCOPED_TOKEN = client_fixtures.UNSCOPED_TOKEN
PROJECT_SCOPED_TOKEN = client_fixtures.PROJECT_SCOPED_TOKEN
DIABLO_TOKEN = token_data.TOKEN_RESPONSES[token_data.VALID_DIABLO_TOKEN]
GRIZZLY_TOKEN = token_data.TOKEN_RESPONSES[token_data.SIGNED_TOKEN_SCOPED_KEY]
class AccessInfoTest(utils.TestCase):
def test_building_unscoped_accessinfo(self):
auth_ref = access.AccessInfo.factory(body=UNSCOPED_TOKEN)
self.assertTrue(auth_ref)
self.assertIn('token', auth_ref)
self.assertIn('serviceCatalog', auth_ref)
self.assertFalse(auth_ref['serviceCatalog'])
self.assertEquals(auth_ref.auth_token,
'3e2813b7ba0b4006840c3825860b86ed')
self.assertEquals(auth_ref.username, 'exampleuser')
self.assertEquals(auth_ref.user_id, 'c4da488862bd435c9e6c0275a0d0e49a')
self.assertEquals(auth_ref.tenant_name, None)
self.assertEquals(auth_ref.tenant_id, None)
self.assertEquals(auth_ref.auth_url, None)
self.assertEquals(auth_ref.management_url, None)
self.assertFalse(auth_ref.scoped)
self.assertFalse(auth_ref.domain_scoped)
self.assertFalse(auth_ref.project_scoped)
self.assertFalse(auth_ref.trust_scoped)
self.assertIsNone(auth_ref.project_domain_id)
self.assertIsNone(auth_ref.project_domain_name)
self.assertEqual(auth_ref.user_domain_id, 'default')
self.assertEqual(auth_ref.user_domain_name, 'Default')
self.assertEquals(auth_ref.expires, timeutils.parse_isotime(
UNSCOPED_TOKEN['access']['token']['expires']))
def test_will_expire_soon(self):
expires = timeutils.utcnow() + datetime.timedelta(minutes=5)
UNSCOPED_TOKEN['access']['token']['expires'] = expires.isoformat()
auth_ref = access.AccessInfo.factory(body=UNSCOPED_TOKEN)
self.assertFalse(auth_ref.will_expire_soon(stale_duration=120))
self.assertTrue(auth_ref.will_expire_soon(stale_duration=300))
self.assertFalse(auth_ref.will_expire_soon())
def test_building_scoped_accessinfo(self):
auth_ref = access.AccessInfo.factory(body=PROJECT_SCOPED_TOKEN)
self.assertTrue(auth_ref)
self.assertIn('token', auth_ref)
self.assertIn('serviceCatalog', auth_ref)
self.assertTrue(auth_ref['serviceCatalog'])
self.assertEquals(auth_ref.auth_token,
'04c7d5ffaeef485f9dc69c06db285bdb')
self.assertEquals(auth_ref.username, 'exampleuser')
self.assertEquals(auth_ref.user_id, 'c4da488862bd435c9e6c0275a0d0e49a')
self.assertEquals(auth_ref.tenant_name, 'exampleproject')
self.assertEquals(auth_ref.tenant_id,
'225da22d3ce34b15877ea70b2a575f58')
self.assertEquals(auth_ref.tenant_name, auth_ref.project_name)
self.assertEquals(auth_ref.tenant_id, auth_ref.project_id)
self.assertEquals(auth_ref.auth_url,
('http://public.com:5000/v2.0',))
self.assertEquals(auth_ref.management_url,
('http://admin:35357/v2.0',))
self.assertEqual(auth_ref.project_domain_id, 'default')
self.assertEqual(auth_ref.project_domain_name, 'Default')
self.assertEqual(auth_ref.user_domain_id, 'default')
self.assertEqual(auth_ref.user_domain_name, 'Default')
self.assertTrue(auth_ref.scoped)
self.assertTrue(auth_ref.project_scoped)
self.assertFalse(auth_ref.domain_scoped)
def test_diablo_token(self):
auth_ref = access.AccessInfo.factory(body=DIABLO_TOKEN)
self.assertTrue(auth_ref)
self.assertEquals(auth_ref.username, 'user_name1')
self.assertEquals(auth_ref.project_id, 'tenant_id1')
self.assertEquals(auth_ref.project_name, 'tenant_id1')
self.assertEquals(auth_ref.project_domain_id, 'default')
self.assertEquals(auth_ref.project_domain_name, 'Default')
self.assertEquals(auth_ref.user_domain_id, 'default')
self.assertEquals(auth_ref.user_domain_name, 'Default')
self.assertFalse(auth_ref.scoped)
def test_grizzly_token(self):
auth_ref = access.AccessInfo.factory(body=GRIZZLY_TOKEN)
self.assertEquals(auth_ref.project_id, 'tenant_id1')
self.assertEquals(auth_ref.project_name, 'tenant_name1')
self.assertEquals(auth_ref.project_domain_id, 'default')
self.assertEquals(auth_ref.project_domain_name, 'Default')
self.assertEquals(auth_ref.user_domain_id, 'default')
self.assertEquals(auth_ref.user_domain_name, 'Default')

190
v2_0/test_auth.py Normal file
View File

@@ -0,0 +1,190 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import datetime
import json
import httpretty
from keystoneclient import exceptions
from keystoneclient.openstack.common import timeutils
from keystoneclient.tests.v2_0 import utils
from keystoneclient.v2_0 import client
class AuthenticateAgainstKeystoneTests(utils.TestCase):
def setUp(self):
super(AuthenticateAgainstKeystoneTests, self).setUp()
self.TEST_RESPONSE_DICT = {
"access": {
"token": {
"expires": "2020-01-01T00:00:10.000123Z",
"id": self.TEST_TOKEN,
"tenant": {
"id": self.TEST_TENANT_ID
},
},
"user": {
"id": self.TEST_USER
},
"serviceCatalog": self.TEST_SERVICE_CATALOG,
},
}
self.TEST_REQUEST_BODY = {
"auth": {
"passwordCredentials": {
"username": self.TEST_USER,
"password": self.TEST_TOKEN,
},
"tenantId": self.TEST_TENANT_ID,
},
}
@httpretty.activate
def test_authenticate_success_expired(self):
# Build an expired token
self.TEST_RESPONSE_DICT['access']['token']['expires'] = \
(timeutils.utcnow() - datetime.timedelta(1)).isoformat()
exp_resp = httpretty.Response(body=json.dumps(self.TEST_RESPONSE_DICT),
content_type='application/json')
# Build a new response
TEST_TOKEN = "abcdef"
self.TEST_RESPONSE_DICT['access']['token']['expires'] = \
'2020-01-01T00:00:10.000123Z'
self.TEST_RESPONSE_DICT['access']['token']['id'] = TEST_TOKEN
new_resp = httpretty.Response(body=json.dumps(self.TEST_RESPONSE_DICT),
content_type='application/json')
# return expired first, and then the new response
self.stub_auth(responses=[exp_resp, new_resp])
cs = client.Client(tenant_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL,
username=self.TEST_USER,
password=self.TEST_TOKEN)
self.assertEqual(cs.management_url,
self.TEST_RESPONSE_DICT["access"]["serviceCatalog"][3]
['endpoints'][0]["adminURL"])
self.assertEqual(cs.auth_token, TEST_TOKEN)
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY)
@httpretty.activate
def test_authenticate_failure(self):
_auth = 'auth'
_cred = 'passwordCredentials'
_pass = 'password'
self.TEST_REQUEST_BODY[_auth][_cred][_pass] = 'bad_key'
error = {"unauthorized": {"message": "Unauthorized",
"code": "401"}}
self.stub_auth(status=401, json=error)
# Workaround for issue with assertRaises on python2.6
# where with assertRaises(exceptions.Unauthorized): doesn't work
# right
def client_create_wrapper():
client.Client(username=self.TEST_USER,
password="bad_key",
tenant_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL)
self.assertRaises(exceptions.Unauthorized, client_create_wrapper)
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY)
@httpretty.activate
def test_auth_redirect(self):
self.stub_auth(status=305, body='Use Proxy',
location=self.TEST_ADMIN_URL + "/tokens")
self.stub_auth(base_url=self.TEST_ADMIN_URL,
json=self.TEST_RESPONSE_DICT)
cs = client.Client(username=self.TEST_USER,
password=self.TEST_TOKEN,
tenant_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL)
self.assertEqual(cs.management_url,
self.TEST_RESPONSE_DICT["access"]["serviceCatalog"][3]
['endpoints'][0]["adminURL"])
self.assertEqual(cs.auth_token,
self.TEST_RESPONSE_DICT["access"]["token"]["id"])
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY)
@httpretty.activate
def test_authenticate_success_password_scoped(self):
self.stub_auth(json=self.TEST_RESPONSE_DICT)
cs = client.Client(username=self.TEST_USER,
password=self.TEST_TOKEN,
tenant_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL)
self.assertEqual(cs.management_url,
self.TEST_RESPONSE_DICT["access"]["serviceCatalog"][3]
['endpoints'][0]["adminURL"])
self.assertEqual(cs.auth_token,
self.TEST_RESPONSE_DICT["access"]["token"]["id"])
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY)
@httpretty.activate
def test_authenticate_success_password_unscoped(self):
del self.TEST_RESPONSE_DICT['access']['serviceCatalog']
del self.TEST_REQUEST_BODY['auth']['tenantId']
self.stub_auth(json=self.TEST_RESPONSE_DICT)
cs = client.Client(username=self.TEST_USER,
password=self.TEST_TOKEN,
auth_url=self.TEST_URL)
self.assertEqual(cs.auth_token,
self.TEST_RESPONSE_DICT["access"]["token"]["id"])
self.assertFalse('serviceCatalog' in cs.service_catalog.catalog)
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY)
@httpretty.activate
def test_authenticate_success_token_scoped(self):
del self.TEST_REQUEST_BODY['auth']['passwordCredentials']
self.TEST_REQUEST_BODY['auth']['token'] = {'id': self.TEST_TOKEN}
self.stub_auth(json=self.TEST_RESPONSE_DICT)
cs = client.Client(token=self.TEST_TOKEN,
tenant_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL)
self.assertEqual(cs.management_url,
self.TEST_RESPONSE_DICT["access"]["serviceCatalog"][3]
['endpoints'][0]["adminURL"])
self.assertEqual(cs.auth_token,
self.TEST_RESPONSE_DICT["access"]["token"]["id"])
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY)
@httpretty.activate
def test_authenticate_success_token_unscoped(self):
del self.TEST_REQUEST_BODY['auth']['passwordCredentials']
del self.TEST_REQUEST_BODY['auth']['tenantId']
del self.TEST_RESPONSE_DICT['access']['serviceCatalog']
self.TEST_REQUEST_BODY['auth']['token'] = {'id': self.TEST_TOKEN}
self.stub_auth(json=self.TEST_RESPONSE_DICT)
cs = client.Client(token=self.TEST_TOKEN,
auth_url=self.TEST_URL)
self.assertEqual(cs.auth_token,
self.TEST_RESPONSE_DICT["access"]["token"]["id"])
self.assertFalse('serviceCatalog' in cs.service_catalog.catalog)
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY)

106
v2_0/test_client.py Normal file
View File

@@ -0,0 +1,106 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import httpretty
from keystoneclient import exceptions
from keystoneclient.tests.v2_0 import client_fixtures
from keystoneclient.tests.v2_0 import utils
from keystoneclient.v2_0 import client
class KeystoneClientTest(utils.TestCase):
@httpretty.activate
def test_unscoped_init(self):
self.stub_auth(json=client_fixtures.UNSCOPED_TOKEN)
c = client.Client(username='exampleuser',
password='password',
auth_url=self.TEST_URL)
self.assertIsNotNone(c.auth_ref)
self.assertFalse(c.auth_ref.scoped)
self.assertFalse(c.auth_ref.domain_scoped)
self.assertFalse(c.auth_ref.project_scoped)
self.assertIsNone(c.auth_ref.trust_id)
self.assertFalse(c.auth_ref.trust_scoped)
@httpretty.activate
def test_scoped_init(self):
self.stub_auth(json=client_fixtures.PROJECT_SCOPED_TOKEN)
c = client.Client(username='exampleuser',
password='password',
tenant_name='exampleproject',
auth_url=self.TEST_URL)
self.assertIsNotNone(c.auth_ref)
self.assertTrue(c.auth_ref.scoped)
self.assertTrue(c.auth_ref.project_scoped)
self.assertFalse(c.auth_ref.domain_scoped)
self.assertIsNone(c.auth_ref.trust_id)
self.assertFalse(c.auth_ref.trust_scoped)
@httpretty.activate
def test_auth_ref_load(self):
self.stub_auth(json=client_fixtures.PROJECT_SCOPED_TOKEN)
cl = client.Client(username='exampleuser',
password='password',
tenant_name='exampleproject',
auth_url=self.TEST_URL)
cache = json.dumps(cl.auth_ref)
new_client = client.Client(auth_ref=json.loads(cache))
self.assertIsNotNone(new_client.auth_ref)
self.assertTrue(new_client.auth_ref.scoped)
self.assertTrue(new_client.auth_ref.project_scoped)
self.assertFalse(new_client.auth_ref.domain_scoped)
self.assertIsNone(new_client.auth_ref.trust_id)
self.assertFalse(new_client.auth_ref.trust_scoped)
self.assertEquals(new_client.username, 'exampleuser')
self.assertIsNone(new_client.password)
self.assertEqual(new_client.management_url,
'http://admin:35357/v2.0')
@httpretty.activate
def test_auth_ref_load_with_overridden_arguments(self):
self.stub_auth(json=client_fixtures.PROJECT_SCOPED_TOKEN)
cl = client.Client(username='exampleuser',
password='password',
tenant_name='exampleproject',
auth_url=self.TEST_URL)
cache = json.dumps(cl.auth_ref)
new_auth_url = "http://new-public:5000/v2.0"
new_client = client.Client(auth_ref=json.loads(cache),
auth_url=new_auth_url)
self.assertIsNotNone(new_client.auth_ref)
self.assertTrue(new_client.auth_ref.scoped)
self.assertTrue(new_client.auth_ref.scoped)
self.assertTrue(new_client.auth_ref.project_scoped)
self.assertFalse(new_client.auth_ref.domain_scoped)
self.assertIsNone(new_client.auth_ref.trust_id)
self.assertFalse(new_client.auth_ref.trust_scoped)
self.assertEquals(new_client.auth_url, new_auth_url)
self.assertEquals(new_client.username, 'exampleuser')
self.assertIsNone(new_client.password)
self.assertEqual(new_client.management_url,
'http://admin:35357/v2.0')
def test_init_err_no_auth_url(self):
self.assertRaises(exceptions.AuthorizationFailure,
client.Client,
username='exampleuser',
password='password')

85
v2_0/test_discovery.py Normal file
View File

@@ -0,0 +1,85 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from keystoneclient.generic import client
from keystoneclient.tests.v2_0 import utils
class DiscoverKeystoneTests(utils.UnauthenticatedTestCase):
def setUp(self):
super(DiscoverKeystoneTests, self).setUp()
self.TEST_RESPONSE_DICT = {
"versions": {
"values": [{
"id": "v2.0",
"status": "beta",
"updated": "2011-11-19T00:00:00Z",
"links": [
{"rel": "self",
"href": "http://127.0.0.1:5000/v2.0/", },
{"rel": "describedby",
"type": "text/html",
"href": "http://docs.openstack.org/api/"
"openstack-identity-service/2.0/content/", },
{"rel": "describedby",
"type": "application/pdf",
"href": "http://docs.openstack.org/api/"
"openstack-identity-service/2.0/"
"identity-dev-guide-2.0.pdf", },
{"rel": "describedby",
"type": "application/vnd.sun.wadl+xml",
"href": "http://127.0.0.1:5000/v2.0/identity.wadl", }
],
"media-types": [{
"base": "application/xml",
"type": "application/vnd.openstack.identity-v2.0+xml",
}, {
"base": "application/json",
"type": "application/vnd.openstack.identity-v2.0+json",
}],
}],
},
}
@httpretty.activate
def test_get_versions(self):
self.stub_url(httpretty.GET, base_url=self.TEST_ROOT_URL,
json=self.TEST_RESPONSE_DICT)
cs = client.Client()
versions = cs.discover(self.TEST_ROOT_URL)
self.assertIsInstance(versions, dict)
self.assertIn('message', versions)
self.assertIn('v2.0', versions)
self.assertEquals(
versions['v2.0']['url'],
self.TEST_RESPONSE_DICT['versions']['values'][0]['links'][0]
['href'])
@httpretty.activate
def test_get_version_local(self):
self.stub_url(httpretty.GET, base_url="http://localhost:35357/",
json=self.TEST_RESPONSE_DICT)
cs = client.Client()
versions = cs.discover()
self.assertIsInstance(versions, dict)
self.assertIn('message', versions)
self.assertIn('v2.0', versions)
self.assertEquals(
versions['v2.0']['url'],
self.TEST_RESPONSE_DICT['versions']['values'][0]['links'][0]
['href'])

115
v2_0/test_ec2.py Normal file
View File

@@ -0,0 +1,115 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from keystoneclient.tests.v2_0 import utils
from keystoneclient.v2_0 import ec2
class EC2Tests(utils.TestCase):
@httpretty.activate
def test_create(self):
user_id = 'usr'
tenant_id = 'tnt'
req_body = {
"tenant_id": tenant_id,
}
resp_body = {
"credential": {
"access": "access",
"secret": "secret",
"tenant_id": tenant_id,
"created": "12/12/12",
"enabled": True,
}
}
self.stub_url(httpretty.POST, ['users', user_id, 'credentials',
'OS-EC2'], json=resp_body)
cred = self.client.ec2.create(user_id, tenant_id)
self.assertTrue(isinstance(cred, ec2.EC2))
self.assertEqual(cred.tenant_id, tenant_id)
self.assertEqual(cred.enabled, True)
self.assertEqual(cred.access, 'access')
self.assertEqual(cred.secret, 'secret')
self.assertRequestBodyIs(json=req_body)
@httpretty.activate
def test_get(self):
user_id = 'usr'
tenant_id = 'tnt'
resp_body = {
"credential": {
"access": "access",
"secret": "secret",
"tenant_id": tenant_id,
"created": "12/12/12",
"enabled": True,
}
}
self.stub_url(httpretty.GET, ['users', user_id, 'credentials',
'OS-EC2', 'access'], json=resp_body)
cred = self.client.ec2.get(user_id, 'access')
self.assertTrue(isinstance(cred, ec2.EC2))
self.assertEqual(cred.tenant_id, tenant_id)
self.assertEqual(cred.enabled, True)
self.assertEqual(cred.access, 'access')
self.assertEqual(cred.secret, 'secret')
@httpretty.activate
def test_list(self):
user_id = 'usr'
tenant_id = 'tnt'
resp_body = {
"credentials": {
"values": [
{
"access": "access",
"secret": "secret",
"tenant_id": tenant_id,
"created": "12/12/12",
"enabled": True,
},
{
"access": "another",
"secret": "key",
"tenant_id": tenant_id,
"created": "12/12/31",
"enabled": True,
}
]
}
}
self.stub_url(httpretty.GET, ['users', user_id, 'credentials',
'OS-EC2'], json=resp_body)
creds = self.client.ec2.list(user_id)
self.assertTrue(len(creds), 2)
cred = creds[0]
self.assertTrue(isinstance(cred, ec2.EC2))
self.assertEqual(cred.tenant_id, tenant_id)
self.assertEqual(cred.enabled, True)
self.assertEqual(cred.access, 'access')
self.assertEqual(cred.secret, 'secret')
@httpretty.activate
def test_delete(self):
user_id = 'usr'
access = 'access'
self.stub_url(httpretty.DELETE, ['users', user_id, 'credentials',
'OS-EC2', access], status=204)
self.client.ec2.delete(user_id, access)

88
v2_0/test_endpoints.py Normal file
View File

@@ -0,0 +1,88 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from keystoneclient.tests.v2_0 import utils
from keystoneclient.v2_0 import endpoints
class EndpointTests(utils.TestCase):
def setUp(self):
super(EndpointTests, self).setUp()
self.TEST_ENDPOINTS = {
'endpoints': [
{
'adminurl': 'http://host-1:8774/v1.1/$(tenant_id)s',
'id': '8f9531231e044e218824b0e58688d262',
'internalurl': 'http://host-1:8774/v1.1/$(tenant_id)s',
'publicurl': 'http://host-1:8774/v1.1/$(tenant_id)s',
'region': 'RegionOne',
},
{
'adminurl': 'http://host-1:8774/v1.1/$(tenant_id)s',
'id': '8f9531231e044e218824b0e58688d263',
'internalurl': 'http://host-1:8774/v1.1/$(tenant_id)s',
'publicurl': 'http://host-1:8774/v1.1/$(tenant_id)s',
'region': 'RegionOne',
}
]
}
@httpretty.activate
def test_create(self):
req_body = {
"endpoint": {
"region": "RegionOne",
"publicurl": "http://host-3:8774/v1.1/$(tenant_id)s",
"internalurl": "http://host-3:8774/v1.1/$(tenant_id)s",
"adminurl": "http://host-3:8774/v1.1/$(tenant_id)s",
"service_id": "e044e21",
}
}
resp_body = {
"endpoint": {
"adminurl": "http://host-3:8774/v1.1/$(tenant_id)s",
"region": "RegionOne",
"id": "1fd485b2ffd54f409a5ecd42cba11401",
"internalurl": "http://host-3:8774/v1.1/$(tenant_id)s",
"publicurl": "http://host-3:8774/v1.1/$(tenant_id)s",
}
}
self.stub_url(httpretty.POST, ['endpoints'], json=resp_body)
endpoint = self.client.endpoints.create(
region=req_body['endpoint']['region'],
publicurl=req_body['endpoint']['publicurl'],
adminurl=req_body['endpoint']['adminurl'],
internalurl=req_body['endpoint']['internalurl'],
service_id=req_body['endpoint']['service_id']
)
self.assertTrue(isinstance(endpoint, endpoints.Endpoint))
self.assertRequestBodyIs(json=req_body)
@httpretty.activate
def test_delete(self):
self.stub_url(httpretty.DELETE, ['endpoints', '8f953'], status=204)
self.client.endpoints.delete('8f953')
@httpretty.activate
def test_list(self):
self.stub_url(httpretty.GET, ['endpoints'], json=self.TEST_ENDPOINTS)
endpoint_list = self.client.endpoints.list()
[self.assertTrue(isinstance(r, endpoints.Endpoint))
for r in endpoint_list]

124
v2_0/test_roles.py Normal file
View File

@@ -0,0 +1,124 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from keystoneclient.tests.v2_0 import utils
from keystoneclient.v2_0 import roles
class RoleTests(utils.TestCase):
def setUp(self):
super(RoleTests, self).setUp()
self.TEST_ROLES = {
"roles": {
"values": [
{
"name": "admin",
"id": 1,
},
{
"name": "member",
"id": 2,
}
],
},
}
@httpretty.activate
def test_create(self):
req_body = {
"role": {
"name": "sysadmin",
}
}
resp_body = {
"role": {
"name": "sysadmin",
"id": 3,
}
}
self.stub_url(httpretty.POST, ['OS-KSADM', 'roles'], json=resp_body)
role = self.client.roles.create(req_body['role']['name'])
self.assertRequestBodyIs(json=req_body)
self.assertTrue(isinstance(role, roles.Role))
self.assertEqual(role.id, 3)
self.assertEqual(role.name, req_body['role']['name'])
@httpretty.activate
def test_delete(self):
self.stub_url(httpretty.DELETE, ['OS-KSADM', 'roles', '1'], status=204)
self.client.roles.delete(1)
@httpretty.activate
def test_get(self):
self.stub_url(httpretty.GET, ['OS-KSADM', 'roles', '1'],
json={'role': self.TEST_ROLES['roles']['values'][0]})
role = self.client.roles.get(1)
self.assertTrue(isinstance(role, roles.Role))
self.assertEqual(role.id, 1)
self.assertEqual(role.name, 'admin')
@httpretty.activate
def test_list(self):
self.stub_url(httpretty.GET, ['OS-KSADM', 'roles'],
json=self.TEST_ROLES)
role_list = self.client.roles.list()
[self.assertTrue(isinstance(r, roles.Role)) for r in role_list]
@httpretty.activate
def test_roles_for_user(self):
self.stub_url(httpretty.GET, ['users', 'foo', 'roles'],
json=self.TEST_ROLES)
role_list = self.client.roles.roles_for_user('foo')
[self.assertTrue(isinstance(r, roles.Role)) for r in role_list]
@httpretty.activate
def test_roles_for_user_tenant(self):
self.stub_url(httpretty.GET, ['tenants', 'barrr', 'users', 'foo',
'roles'], json=self.TEST_ROLES)
role_list = self.client.roles.roles_for_user('foo', 'barrr')
[self.assertTrue(isinstance(r, roles.Role)) for r in role_list]
@httpretty.activate
def test_add_user_role(self):
self.stub_url(httpretty.PUT, ['users', 'foo', 'roles', 'OS-KSADM',
'barrr'], status=204)
self.client.roles.add_user_role('foo', 'barrr')
@httpretty.activate
def test_add_user_role_tenant(self):
self.stub_url(httpretty.PUT, ['tenants', '4', 'users', 'foo', 'roles',
'OS-KSADM', 'barrr'], status=204)
self.client.roles.add_user_role('foo', 'barrr', '4')
@httpretty.activate
def test_remove_user_role(self):
self.stub_url(httpretty.DELETE, ['users', 'foo', 'roles', 'OS-KSADM',
'barrr'], status=204)
self.client.roles.remove_user_role('foo', 'barrr')
@httpretty.activate
def test_remove_user_role_tenant(self):
self.stub_url(httpretty.DELETE, ['tenants', '4', 'users', 'foo',
'roles', 'OS-KSADM', 'barrr'],
status=204)
self.client.roles.remove_user_role('foo', 'barrr', '4')

View File

@@ -0,0 +1,79 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import copy
from keystoneclient import access
from keystoneclient import exceptions
from keystoneclient.tests.v2_0 import client_fixtures
from keystoneclient.tests.v2_0 import utils
class ServiceCatalogTest(utils.TestCase):
def setUp(self):
super(ServiceCatalogTest, self).setUp()
self.AUTH_RESPONSE_BODY = client_fixtures.AUTH_RESPONSE_BODY
def test_building_a_service_catalog(self):
auth_ref = access.AccessInfo.factory(None, self.AUTH_RESPONSE_BODY)
sc = auth_ref.service_catalog
self.assertEquals(sc.url_for(service_type='compute'),
"https://compute.north.host/v1/1234")
self.assertEquals(sc.url_for('tenantId', '1', service_type='compute'),
"https://compute.north.host/v1/1234")
self.assertEquals(sc.url_for('tenantId', '2', service_type='compute'),
"https://compute.north.host/v1.1/3456")
self.assertRaises(exceptions.EndpointNotFound, sc.url_for, "region",
"South", service_type='compute')
def test_service_catalog_endpoints(self):
auth_ref = access.AccessInfo.factory(None, self.AUTH_RESPONSE_BODY)
sc = auth_ref.service_catalog
public_ep = sc.get_endpoints(service_type='compute',
endpoint_type='publicURL')
self.assertEquals(public_ep['compute'][1]['tenantId'], '2')
self.assertEquals(public_ep['compute'][1]['versionId'], '1.1')
self.assertEquals(public_ep['compute'][1]['internalURL'],
"https://compute.north.host/v1.1/3456")
def test_service_catalog_regions(self):
self.AUTH_RESPONSE_BODY['access']['region_name'] = "North"
auth_ref = access.AccessInfo.factory(None, self.AUTH_RESPONSE_BODY)
sc = auth_ref.service_catalog
url = sc.url_for(service_type='image', endpoint_type='publicURL')
self.assertEquals(url, "https://image.north.host/v1/")
self.AUTH_RESPONSE_BODY['access']['region_name'] = "South"
auth_ref = access.AccessInfo.factory(None, self.AUTH_RESPONSE_BODY)
sc = auth_ref.service_catalog
url = sc.url_for(service_type='image', endpoint_type='internalURL')
self.assertEquals(url, "https://image-internal.south.host/v1/")
def test_service_catalog_empty(self):
# We need to do a copy.deepcopy here since
# dict(self.AUTH_RESPONSE_BODY) or self.AUTH_RESPONSE_BODY.copy() will
# only do a shadowcopy and sc_empty['token']['catalog'] will still be a
# reference to self.AUTH_RESPONSE_BODY so setting it to empty will fail
# the other tests that needs a service catalog.
sc_empty = copy.deepcopy(self.AUTH_RESPONSE_BODY)
sc_empty['access']['serviceCatalog'] = []
auth_ref = access.AccessInfo.factory(None, sc_empty)
self.assertRaises(exceptions.EmptyCatalog,
auth_ref.service_catalog.url_for,
service_type='image',
endpoint_type='internalURL')

98
v2_0/test_services.py Normal file
View File

@@ -0,0 +1,98 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from keystoneclient.tests.v2_0 import utils
from keystoneclient.v2_0 import services
class ServiceTests(utils.TestCase):
def setUp(self):
super(ServiceTests, self).setUp()
self.TEST_SERVICES = {
"OS-KSADM:services": {
"values": [
{
"name": "nova",
"type": "compute",
"description": "Nova-compatible service.",
"id": 1
},
{
"name": "keystone",
"type": "identity",
"description": "Keystone-compatible service.",
"id": 2
},
],
},
}
@httpretty.activate
def test_create(self):
req_body = {
"OS-KSADM:service": {
"name": "swift",
"type": "object-store",
"description": "Swift-compatible service.",
}
}
resp_body = {
"OS-KSADM:service": {
"name": "swift",
"type": "object-store",
"description": "Swift-compatible service.",
"id": 3,
}
}
self.stub_url(httpretty.POST, ['OS-KSADM', 'services'], json=resp_body)
service = self.client.services.create(
req_body['OS-KSADM:service']['name'],
req_body['OS-KSADM:service']['type'],
req_body['OS-KSADM:service']['description'])
self.assertTrue(isinstance(service, services.Service))
self.assertEqual(service.id, 3)
self.assertEqual(service.name, req_body['OS-KSADM:service']['name'])
self.assertRequestBodyIs(json=req_body)
@httpretty.activate
def test_delete(self):
self.stub_url(httpretty.DELETE, ['OS-KSADM', 'services', '1'],
status=204)
self.client.services.delete(1)
@httpretty.activate
def test_get(self):
test_services = self.TEST_SERVICES['OS-KSADM:services']['values'][0]
self.stub_url(httpretty.GET, ['OS-KSADM', 'services', '1'],
json={'OS-KSADM:service': test_services})
service = self.client.services.get(1)
self.assertTrue(isinstance(service, services.Service))
self.assertEqual(service.id, 1)
self.assertEqual(service.name, 'nova')
self.assertEqual(service.type, 'compute')
@httpretty.activate
def test_list(self):
self.stub_url(httpretty.GET, ['OS-KSADM', 'services'],
json=self.TEST_SERVICES)
service_list = self.client.services.list()
[self.assertTrue(isinstance(r, services.Service))
for r in service_list]

347
v2_0/test_shell.py Normal file
View File

@@ -0,0 +1,347 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import cStringIO
import os
import sys
from mox3 import stubout
from testtools import matchers
from keystoneclient import httpclient
from keystoneclient.tests.v2_0 import fakes
from keystoneclient.tests.v2_0 import utils
DEFAULT_USERNAME = 'username'
DEFAULT_PASSWORD = 'password'
DEFAULT_TENANT_ID = 'tenant_id'
DEFAULT_TENANT_NAME = 'tenant_name'
DEFAULT_AUTH_URL = 'http://127.0.0.1:5000/v2.0/'
class ShellTests(utils.TestCase):
def setUp(self):
"""Patch os.environ to avoid required auth info."""
super(ShellTests, self).setUp()
self.stubs = stubout.StubOutForTesting()
self.fake_client = fakes.FakeHTTPClient()
self.stubs.Set(
httpclient.HTTPClient, "_cs_request",
lambda ign_self, *args, **kwargs:
self.fake_client._cs_request(*args, **kwargs))
self.stubs.Set(
httpclient.HTTPClient, "authenticate",
lambda cl_obj:
self.fake_client.authenticate(cl_obj))
self.old_environment = os.environ.copy()
os.environ = {
'OS_USERNAME': DEFAULT_USERNAME,
'OS_PASSWORD': DEFAULT_PASSWORD,
'OS_TENANT_ID': DEFAULT_TENANT_ID,
'OS_TENANT_NAME': DEFAULT_TENANT_NAME,
'OS_AUTH_URL': DEFAULT_AUTH_URL,
}
import keystoneclient.shell
self.shell = keystoneclient.shell.OpenStackIdentityShell()
def tearDown(self):
self.stubs.UnsetAll()
self.stubs.SmartUnsetAll()
os.environ = self.old_environment
self.fake_client.clear_callstack()
super(ShellTests, self).tearDown()
def run_command(self, cmd):
orig = sys.stdout
try:
sys.stdout = cStringIO.StringIO()
if isinstance(cmd, list):
self.shell.main(cmd)
else:
self.shell.main(cmd.split())
except SystemExit:
exc_type, exc_value, exc_traceback = sys.exc_info()
self.assertEqual(exc_value.code, 0)
finally:
out = sys.stdout.getvalue()
sys.stdout.close()
sys.stdout = orig
return out
def assert_called(self, method, url, body=None, **kwargs):
return self.fake_client.assert_called(method, url, body, **kwargs)
def assert_called_anytime(self, method, url, body=None):
return self.fake_client.assert_called_anytime(method, url, body)
def test_user_list(self):
self.run_command('user-list')
self.fake_client.assert_called_anytime('GET', '/users')
def test_user_create(self):
self.run_command('user-create --name new-user')
self.fake_client.assert_called_anytime(
'POST', '/users',
{'user':
{'email': None,
'password': None,
'enabled': True,
'name': 'new-user',
'tenantId': None}})
def test_user_get(self):
self.run_command('user-get 1')
self.fake_client.assert_called_anytime('GET', '/users/1')
def test_user_delete(self):
self.run_command('user-delete 1')
self.fake_client.assert_called_anytime('DELETE', '/users/1')
def test_user_password_update(self):
self.run_command('user-password-update --pass newpass 1')
self.fake_client.assert_called_anytime(
'PUT', '/users/1/OS-KSADM/password')
def test_user_update(self):
self.run_command('user-update --name new-user1'
' --email user@email.com --enabled true 1')
self.fake_client.assert_called_anytime(
'PUT', '/users/1',
{'user':
{'id': '1',
'email': 'user@email.com',
'enabled': True,
'name': 'new-user1'}
})
required = 'User not updated, no arguments present.'
out = self.run_command('user-update 1')
self.assertThat(out, matchers.MatchesRegex(required))
self.run_command(['user-update', '--email', '', '1'])
self.fake_client.assert_called_anytime(
'PUT', '/users/1',
{'user':
{'id': '1',
'email': ''}
})
def test_role_create(self):
self.run_command('role-create --name new-role')
self.fake_client.assert_called_anytime(
'POST', '/OS-KSADM/roles',
{"role": {"name": "new-role"}})
def test_role_get(self):
self.run_command('role-get 1')
self.fake_client.assert_called_anytime('GET', '/OS-KSADM/roles/1')
def test_role_list(self):
self.run_command('role-list')
self.fake_client.assert_called_anytime('GET', '/OS-KSADM/roles')
def test_role_delete(self):
self.run_command('role-delete 1')
self.fake_client.assert_called_anytime('DELETE', '/OS-KSADM/roles/1')
def test_user_role_add(self):
self.run_command('user-role-add --user_id 1 --role_id 1')
self.fake_client.assert_called_anytime(
'PUT', '/users/1/roles/OS-KSADM/1')
def test_user_role_list(self):
self.run_command('user-role-list --user_id 1 --tenant-id 1')
self.fake_client.assert_called_anytime(
'GET', '/tenants/1/users/1/roles')
self.run_command('user-role-list --user_id 1')
self.fake_client.assert_called_anytime(
'GET', '/tenants/1/users/1/roles')
self.run_command('user-role-list')
self.fake_client.assert_called_anytime(
'GET', '/tenants/1/users/1/roles')
def test_user_role_remove(self):
self.run_command('user-role-remove --user_id 1 --role_id 1')
self.fake_client.assert_called_anytime(
'DELETE', '/users/1/roles/OS-KSADM/1')
def test_tenant_create(self):
self.run_command('tenant-create --name new-tenant')
self.fake_client.assert_called_anytime(
'POST', '/tenants',
{"tenant": {"enabled": True,
"name": "new-tenant",
"description": None}})
def test_tenant_get(self):
self.run_command('tenant-get 2')
self.fake_client.assert_called_anytime('GET', '/tenants/2')
def test_tenant_list(self):
self.run_command('tenant-list')
self.fake_client.assert_called_anytime('GET', '/tenants')
def test_tenant_update(self):
self.run_command('tenant-update'
' --name new-tenant1 --enabled false'
' --description desc 2')
self.fake_client.assert_called_anytime(
'POST', '/tenants/2',
{"tenant":
{"enabled": False,
"id": "2",
"description": "desc",
"name": "new-tenant1"}})
required = 'Tenant not updated, no arguments present.'
out = self.run_command('tenant-update 1')
self.assertThat(out, matchers.MatchesRegex(required))
def test_tenant_delete(self):
self.run_command('tenant-delete 2')
self.fake_client.assert_called_anytime('DELETE', '/tenants/2')
def test_service_create(self):
self.run_command('service-create --name service1 --type compute')
self.fake_client.assert_called_anytime(
'POST', '/OS-KSADM/services',
{"OS-KSADM:service":
{"type": "compute",
"name": "service1",
"description": None}})
def test_service_get(self):
self.run_command('service-get 1')
self.fake_client.assert_called_anytime('GET', '/OS-KSADM/services/1')
def test_service_list(self):
self.run_command('service-list')
self.fake_client.assert_called_anytime('GET', '/OS-KSADM/services')
def test_service_delete(self):
self.run_command('service-delete 1')
self.fake_client.assert_called_anytime(
'DELETE', '/OS-KSADM/services/1')
def test_catalog(self):
self.run_command('catalog')
self.run_command('catalog --service compute')
def test_ec2_credentials_create(self):
self.run_command('ec2-credentials-create'
' --tenant-id 1 --user-id 1')
self.fake_client.assert_called_anytime(
'POST', '/users/1/credentials/OS-EC2',
{'tenant_id': '1'})
self.run_command('ec2-credentials-create --tenant-id 1')
self.fake_client.assert_called_anytime(
'POST', '/users/1/credentials/OS-EC2',
{'tenant_id': '1'})
self.run_command('ec2-credentials-create')
self.fake_client.assert_called_anytime(
'POST', '/users/1/credentials/OS-EC2',
{'tenant_id': '1'})
def test_ec2_credentials_delete(self):
self.run_command('ec2-credentials-delete --access 2 --user-id 1')
self.fake_client.assert_called_anytime(
'DELETE', '/users/1/credentials/OS-EC2/2')
self.run_command('ec2-credentials-delete --access 2')
self.fake_client.assert_called_anytime(
'DELETE', '/users/1/credentials/OS-EC2/2')
def test_ec2_credentials_list(self):
self.run_command('ec2-credentials-list --user-id 1')
self.fake_client.assert_called_anytime(
'GET', '/users/1/credentials/OS-EC2')
self.run_command('ec2-credentials-list')
self.fake_client.assert_called_anytime(
'GET', '/users/1/credentials/OS-EC2')
def test_ec2_credentials_get(self):
self.run_command('ec2-credentials-get --access 2 --user-id 1')
self.fake_client.assert_called_anytime(
'GET', '/users/1/credentials/OS-EC2/2')
def test_bootstrap(self):
self.run_command('bootstrap --user-name new-user'
' --pass 1 --role-name admin'
' --tenant-name new-tenant')
self.fake_client.assert_called_anytime(
'POST', '/users',
{'user':
{'email': None,
'password': '1',
'enabled': True,
'name': 'new-user',
'tenantId': None}})
self.run_command('bootstrap --user-name new-user'
' --pass 1 --role-name admin'
' --tenant-name new-tenant')
self.fake_client.assert_called_anytime(
'POST', '/tenants',
{"tenant": {"enabled": True,
"name": "new-tenant",
"description": None}})
self.run_command('bootstrap --user-name new-user'
' --pass 1 --role-name new-role'
' --tenant-name new-tenant')
self.fake_client.assert_called_anytime(
'POST', '/OS-KSADM/roles',
{"role": {"name": "new-role"}})
self.run_command('bootstrap --user-name'
' new-user --pass 1 --role-name admin'
' --tenant-name new-tenant')
self.fake_client.assert_called_anytime(
'PUT', '/tenants/1/users/1/roles/OS-KSADM/1')
def test_bash_completion(self):
self.run_command('bash-completion')
def test_help(self):
out = self.run_command('help')
required = 'usage: keystone'
self.assertThat(out, matchers.MatchesRegex(required))
def test_password_update(self):
self.run_command('password-update --current-password oldpass'
' --new-password newpass')
self.fake_client.assert_called_anytime(
'PATCH', '/OS-KSCRUD/users/1',
{'user':
{'original_password': 'oldpass',
'password': 'newpass'}})
def test_endpoint_create(self):
self.run_command('endpoint-create --service-id 1')
self.fake_client.assert_called_anytime(
'POST', '/endpoints',
{'endpoint':
{'adminurl': None,
'service_id': '1',
'region': 'regionOne',
'internalurl': None,
'publicurl': None}})
def test_endpoint_list(self):
self.run_command('endpoint-list')
self.fake_client.assert_called_anytime('GET', '/endpoints')

287
v2_0/test_tenants.py Normal file
View File

@@ -0,0 +1,287 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from keystoneclient import exceptions
from keystoneclient.tests.v2_0 import utils
from keystoneclient.v2_0 import tenants
class TenantTests(utils.TestCase):
def setUp(self):
super(TenantTests, self).setUp()
self.TEST_TENANTS = {
"tenants": {
"values": [
{
"enabled": True,
"description": "A description change!",
"name": "invisible_to_admin",
"id": 3,
},
{
"enabled": True,
"description": "None",
"name": "demo",
"id": 2,
},
{
"enabled": True,
"description": "None",
"name": "admin",
"id": 1,
},
{
"extravalue01": "metadata01",
"enabled": True,
"description": "For testing extras",
"name": "test_extras",
"id": 4,
}
],
"links": [],
},
}
@httpretty.activate
def test_create(self):
req_body = {
"tenant": {
"name": "tenantX",
"description": "Like tenant 9, but better.",
"enabled": True,
"extravalue01": "metadata01",
},
}
resp_body = {
"tenant": {
"name": "tenantX",
"enabled": True,
"id": 4,
"description": "Like tenant 9, but better.",
"extravalue01": "metadata01",
}
}
self.stub_url(httpretty.POST, ['tenants'], json=resp_body)
tenant = self.client.tenants.create(
req_body['tenant']['name'],
req_body['tenant']['description'],
req_body['tenant']['enabled'],
extravalue01=req_body['tenant']['extravalue01'],
name="dont overwrite priors")
self.assertTrue(isinstance(tenant, tenants.Tenant))
self.assertEqual(tenant.id, 4)
self.assertEqual(tenant.name, "tenantX")
self.assertEqual(tenant.description, "Like tenant 9, but better.")
self.assertEqual(tenant.extravalue01, "metadata01")
self.assertRequestBodyIs(json=req_body)
@httpretty.activate
def test_duplicate_create(self):
req_body = {
"tenant": {
"name": "tenantX",
"description": "The duplicate tenant.",
"enabled": True
},
}
resp_body = {
"error": {
"message": "Conflict occurred attempting to store project.",
"code": 409,
"title": "Conflict",
}
}
self.stub_url(httpretty.POST, ['tenants'], status=409, json=resp_body)
def create_duplicate_tenant():
self.client.tenants.create(req_body['tenant']['name'],
req_body['tenant']['description'],
req_body['tenant']['enabled'])
self.assertRaises(exceptions.Conflict, create_duplicate_tenant)
@httpretty.activate
def test_delete(self):
self.stub_url(httpretty.DELETE, ['tenants', '1'], status=204)
self.client.tenants.delete(1)
@httpretty.activate
def test_get(self):
resp = {'tenant': self.TEST_TENANTS['tenants']['values'][2]}
self.stub_url(httpretty.GET, ['tenants', '1'], json=resp)
t = self.client.tenants.get(1)
self.assertTrue(isinstance(t, tenants.Tenant))
self.assertEqual(t.id, 1)
self.assertEqual(t.name, 'admin')
@httpretty.activate
def test_list(self):
self.stub_url(httpretty.GET, ['tenants'], json=self.TEST_TENANTS)
tenant_list = self.client.tenants.list()
[self.assertTrue(isinstance(t, tenants.Tenant)) for t in tenant_list]
@httpretty.activate
def test_list_limit(self):
self.stub_url(httpretty.GET, ['tenants'], json=self.TEST_TENANTS)
tenant_list = self.client.tenants.list(limit=1)
self.assertQueryStringIs({'limit': ['1']})
[self.assertTrue(isinstance(t, tenants.Tenant)) for t in tenant_list]
@httpretty.activate
def test_list_marker(self):
self.stub_url(httpretty.GET, ['tenants'], json=self.TEST_TENANTS)
tenant_list = self.client.tenants.list(marker=1)
self.assertQueryStringIs({'marker': ['1']})
[self.assertTrue(isinstance(t, tenants.Tenant)) for t in tenant_list]
@httpretty.activate
def test_list_limit_marker(self):
self.stub_url(httpretty.GET, ['tenants'], json=self.TEST_TENANTS)
tenant_list = self.client.tenants.list(limit=1, marker=1)
self.assertQueryStringIs({'marker': ['1'], 'limit': ['1']})
[self.assertTrue(isinstance(t, tenants.Tenant)) for t in tenant_list]
@httpretty.activate
def test_update(self):
req_body = {
"tenant": {
"id": 4,
"name": "tenantX",
"description": "I changed you!",
"enabled": False,
"extravalue01": "metadataChanged",
#"extraname": "dontoverwrite!",
},
}
resp_body = {
"tenant": {
"name": "tenantX",
"enabled": False,
"id": 4,
"description": "I changed you!",
"extravalue01": "metadataChanged",
},
}
self.stub_url(httpretty.POST, ['tenants', '4'], json=resp_body)
tenant = self.client.tenants.update(
req_body['tenant']['id'],
req_body['tenant']['name'],
req_body['tenant']['description'],
req_body['tenant']['enabled'],
extravalue01=req_body['tenant']['extravalue01'],
name="dont overwrite priors")
self.assertTrue(isinstance(tenant, tenants.Tenant))
self.assertRequestBodyIs(json=req_body)
self.assertEqual(tenant.id, 4)
self.assertEqual(tenant.name, "tenantX")
self.assertEqual(tenant.description, "I changed you!")
self.assertFalse(tenant.enabled)
self.assertEqual(tenant.extravalue01, "metadataChanged")
@httpretty.activate
def test_update_empty_description(self):
req_body = {
"tenant": {
"id": 4,
"name": "tenantX",
"description": "",
"enabled": False,
},
}
resp_body = {
"tenant": {
"name": "tenantX",
"enabled": False,
"id": 4,
"description": "",
},
}
self.stub_url(httpretty.POST, ['tenants', '4'], json=resp_body)
tenant = self.client.tenants.update(req_body['tenant']['id'],
req_body['tenant']['name'],
req_body['tenant']['description'],
req_body['tenant']['enabled'])
self.assertTrue(isinstance(tenant, tenants.Tenant))
self.assertRequestBodyIs(json=req_body)
self.assertEqual(tenant.id, 4)
self.assertEqual(tenant.name, "tenantX")
self.assertEqual(tenant.description, "")
self.assertFalse(tenant.enabled)
@httpretty.activate
def test_add_user(self):
self.stub_url(httpretty.PUT, ['tenants', '4', 'users', 'foo', 'roles',
'OS-KSADM', 'barrr'], status=204)
self.client.tenants.add_user('4', 'foo', 'barrr')
@httpretty.activate
def test_remove_user(self):
self.stub_url(httpretty.DELETE, ['tenants', '4', 'users', 'foo',
'roles', 'OS-KSADM', 'barrr'],
status=204)
self.client.tenants.remove_user('4', 'foo', 'barrr')
@httpretty.activate
def test_tenant_add_user(self):
self.stub_url(httpretty.PUT, ['tenants', '4', 'users', 'foo', 'roles',
'OS-KSADM', 'barrr'],
status=204)
req_body = {
"tenant": {
"id": 4,
"name": "tenantX",
"description": "I changed you!",
"enabled": False,
},
}
# make tenant object with manager
tenant = self.client.tenants.resource_class(self.client.tenants,
req_body['tenant'])
tenant.add_user('foo', 'barrr')
self.assertTrue(isinstance(tenant, tenants.Tenant))
@httpretty.activate
def test_tenant_remove_user(self):
self.stub_url(httpretty.DELETE, ['tenants', '4', 'users', 'foo',
'roles', 'OS-KSADM', 'barrr'],
status=204)
req_body = {
"tenant": {
"id": 4,
"name": "tenantX",
"description": "I changed you!",
"enabled": False,
},
}
# make tenant object with manager
tenant = self.client.tenants.resource_class(self.client.tenants,
req_body['tenant'])
tenant.remove_user('foo', 'barrr')
self.assertTrue(isinstance(tenant, tenants.Tenant))

24
v2_0/test_tokens.py Normal file
View File

@@ -0,0 +1,24 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from keystoneclient.tests.v2_0 import utils
class TokenTests(utils.TestCase):
@httpretty.activate
def test_delete(self):
self.stub_url(httpretty.DELETE, ['tokens', '1'], status=204)
self.client.tokens.delete(1)

191
v2_0/test_users.py Normal file
View File

@@ -0,0 +1,191 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from keystoneclient.tests.v2_0 import utils
from keystoneclient.v2_0 import users
class UserTests(utils.TestCase):
def setUp(self):
super(UserTests, self).setUp()
self.TEST_USERS = {
"users": {
"values": [
{
"email": "None",
"enabled": True,
"id": 1,
"name": "admin",
},
{
"email": "None",
"enabled": True,
"id": 2,
"name": "demo",
},
]
}
}
@httpretty.activate
def test_create(self):
req_body = {
"user": {
"name": "gabriel",
"password": "test",
"tenantId": 2,
"email": "test@example.com",
"enabled": True,
}
}
resp_body = {
"user": {
"name": "gabriel",
"enabled": True,
"tenantId": 2,
"id": 3,
"password": "test",
"email": "test@example.com",
}
}
self.stub_url(httpretty.POST, ['users'], json=resp_body)
user = self.client.users.create(req_body['user']['name'],
req_body['user']['password'],
req_body['user']['email'],
tenant_id=req_body['user']['tenantId'],
enabled=req_body['user']['enabled'])
self.assertTrue(isinstance(user, users.User))
self.assertEqual(user.id, 3)
self.assertEqual(user.name, "gabriel")
self.assertEqual(user.email, "test@example.com")
self.assertRequestBodyIs(json=req_body)
@httpretty.activate
def test_delete(self):
self.stub_url(httpretty.DELETE, ['users', '1'], status=204)
self.client.users.delete(1)
@httpretty.activate
def test_get(self):
self.stub_url(httpretty.GET, ['users', '1'],
json={'user': self.TEST_USERS['users']['values'][0]})
u = self.client.users.get(1)
self.assertTrue(isinstance(u, users.User))
self.assertEqual(u.id, 1)
self.assertEqual(u.name, 'admin')
@httpretty.activate
def test_list(self):
self.stub_url(httpretty.GET, ['users'], json=self.TEST_USERS)
user_list = self.client.users.list()
[self.assertTrue(isinstance(u, users.User)) for u in user_list]
@httpretty.activate
def test_list_limit(self):
self.stub_url(httpretty.GET, ['users'], json=self.TEST_USERS)
user_list = self.client.users.list(limit=1)
self.assertEqual(httpretty.last_request().querystring,
{'limit': ['1']})
[self.assertTrue(isinstance(u, users.User)) for u in user_list]
@httpretty.activate
def test_list_marker(self):
self.stub_url(httpretty.GET, ['users'], json=self.TEST_USERS)
user_list = self.client.users.list(marker='foo')
self.assertDictEqual(httpretty.last_request().querystring,
{'marker': ['foo']})
[self.assertTrue(isinstance(u, users.User)) for u in user_list]
@httpretty.activate
def test_list_limit_marker(self):
self.stub_url(httpretty.GET, ['users'], json=self.TEST_USERS)
user_list = self.client.users.list(limit=1, marker='foo')
self.assertDictEqual(httpretty.last_request().querystring,
{'marker': ['foo'], 'limit': ['1']})
[self.assertTrue(isinstance(u, users.User)) for u in user_list]
@httpretty.activate
def test_update(self):
req_1 = {
"user": {
"id": 2,
"email": "gabriel@example.com",
"name": "gabriel",
}
}
req_2 = {
"user": {
"id": 2,
"password": "swordfish",
}
}
req_3 = {
"user": {
"id": 2,
"tenantId": 1,
}
}
req_4 = {
"user": {
"id": 2,
"enabled": False,
}
}
self.stub_url(httpretty.PUT, ['users', '2'], json=req_1)
self.stub_url(httpretty.PUT, ['users', '2', 'OS-KSADM', 'password'],
json=req_2)
self.stub_url(httpretty.PUT, ['users', '2', 'OS-KSADM', 'tenant'],
json=req_3)
self.stub_url(httpretty.PUT, ['users', '2', 'OS-KSADM', 'enabled'],
json=req_4)
self.client.users.update(2,
name='gabriel',
email='gabriel@example.com')
self.assertRequestBodyIs(json=req_1)
self.client.users.update_password(2, 'swordfish')
self.assertRequestBodyIs(json=req_2)
self.client.users.update_tenant(2, 1)
self.assertRequestBodyIs(json=req_3)
self.client.users.update_enabled(2, False)
self.assertRequestBodyIs(json=req_4)
@httpretty.activate
def test_update_own_password(self):
req_body = {
'user': {
'password': 'ABCD', 'original_password': 'DCBA'
}
}
resp_body = {
'access': {}
}
self.stub_url(httpretty.PATCH, ['OS-KSCRUD', 'users', '123'],
json=resp_body)
self.client.user_id = '123'
self.client.users.update_own_password('DCBA', 'ABCD')
self.assertRequestBodyIs(json=req_body)

90
v2_0/utils.py Normal file
View File

@@ -0,0 +1,90 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from keystoneclient.tests import utils
from keystoneclient.v2_0 import client
TestResponse = utils.TestResponse
class UnauthenticatedTestCase(utils.TestCase):
"""Class used as base for unauthenticated calls."""
TEST_ROOT_URL = 'http://127.0.0.1:5000/'
TEST_URL = '%s%s' % (TEST_ROOT_URL, 'v2.0')
TEST_ROOT_ADMIN_URL = 'http://127.0.0.1:35357/'
TEST_ADMIN_URL = '%s%s' % (TEST_ROOT_ADMIN_URL, 'v2.0')
class TestCase(UnauthenticatedTestCase):
TEST_SERVICE_CATALOG = [{
"endpoints": [{
"adminURL": "http://cdn.admin-nets.local:8774/v1.0",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:8774/v1.0",
"publicURL": "http://cdn.admin-nets.local:8774/v1.0/"
}],
"type": "nova_compat",
"name": "nova_compat"
}, {
"endpoints": [{
"adminURL": "http://nova/novapi/admin",
"region": "RegionOne",
"internalURL": "http://nova/novapi/internal",
"publicURL": "http://nova/novapi/public"
}],
"type": "compute",
"name": "nova"
}, {
"endpoints": [{
"adminURL": "http://glance/glanceapi/admin",
"region": "RegionOne",
"internalURL": "http://glance/glanceapi/internal",
"publicURL": "http://glance/glanceapi/public"
}],
"type": "image",
"name": "glance"
}, {
"endpoints": [{
"adminURL": "http://127.0.0.1:35357/v2.0",
"region": "RegionOne",
"internalURL": "http://127.0.0.1:5000/v2.0",
"publicURL": "http://127.0.0.1:5000/v2.0"
}],
"type": "identity",
"name": "keystone"
}, {
"endpoints": [{
"adminURL": "http://swift/swiftapi/admin",
"region": "RegionOne",
"internalURL": "http://swift/swiftapi/internal",
"publicURL": "http://swift/swiftapi/public"
}],
"type": "object-store",
"name": "swift"
}]
def setUp(self):
super(TestCase, self).setUp()
self.client = client.Client(username=self.TEST_USER,
token=self.TEST_TOKEN,
tenant_name=self.TEST_TENANT_NAME,
auth_url=self.TEST_URL,
endpoint=self.TEST_URL)
def stub_auth(self, **kwargs):
self.stub_url(httpretty.POST, ['tokens'], **kwargs)

0
v3/__init__.py Normal file
View File

290
v3/client_fixtures.py Normal file
View File

@@ -0,0 +1,290 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import unicode_literals
UNSCOPED_TOKEN = {
'token': {
'methods': [
'password'
],
'catalog': {},
'expires_at': '2010-11-01T03:32:15-05:00',
'user': {
'domain': {
'id': '4e6893b7ba0b4006840c3845660b86ed',
'name': 'exampledomain'
},
'id': 'c4da488862bd435c9e6c0275a0d0e49a',
'name': 'exampleuser',
}
}
}
DOMAIN_SCOPED_TOKEN = {
'token': {
'methods': [
'password'
],
'catalog': {},
'expires_at': '2010-11-01T03:32:15-05:00',
'user': {
'domain': {
'id': '4e6893b7ba0b4006840c3845660b86ed',
'name': 'exampledomain'
},
'id': 'c4da488862bd435c9e6c0275a0d0e49a',
'name': 'exampleuser',
},
'domain': {
'id': '8e9283b7ba0b1038840c3842058b86ab',
'name': 'anotherdomain'
},
}
}
PROJECT_SCOPED_TOKEN = {
'token': {
'methods': [
'password'
],
'catalog': [{
'endpoints': [{
'url':
'http://public.com:8776/v1/225da22d3ce34b15877ea70b2a575f58',
'region': 'RegionOne',
'interface': 'public'
}, {
'url':
'http://internal:8776/v1/225da22d3ce34b15877ea70b2a575f58',
'region': 'RegionOne',
'interface': 'internal'
}, {
'url':
'http://admin:8776/v1/225da22d3ce34b15877ea70b2a575f58',
'region': 'RegionOne',
'interface': 'admin'
}],
'type': 'volume'
}, {
'endpoints': [{
'url': 'http://public.com:9292/v1',
'region': 'RegionOne',
'interface': 'public'
}, {
'url': 'http://internal:9292/v1',
'region': 'RegionOne',
'interface': 'internal'
}, {
'url': 'http://admin:9292/v1',
'region': 'RegionOne',
'interface': 'admin'
}],
'type': 'image'
}, {
'endpoints': [{
'url':
'http://public.com:8774/v2/225da22d3ce34b15877ea70b2a575f58',
'region': 'RegionOne',
'interface': 'public'
}, {
'url':
'http://internal:8774/v2/225da22d3ce34b15877ea70b2a575f58',
'region': 'RegionOne',
'interface': 'internal'
}, {
'url':
'http://admin:8774/v2/225da22d3ce34b15877ea70b2a575f58',
'region': 'RegionOne',
'interface': 'admin'
}],
'type': 'compute'
}, {
'endpoints': [{
'url': 'http://public.com:8773/services/Cloud',
'region': 'RegionOne',
'interface': 'public'
}, {
'url': 'http://internal:8773/services/Cloud',
'region': 'RegionOne',
'interface': 'internal'
}, {
'url': 'http://admin:8773/services/Admin',
'region': 'RegionOne',
'interface': 'admin'
}],
'type': 'ec2'
}, {
'endpoints': [{
'url': 'http://public.com:5000/v3',
'region': 'RegionOne',
'interface': 'public'
}, {
'url': 'http://internal:5000/v3',
'region': 'RegionOne',
'interface': 'internal'
}, {
'url': 'http://admin:35357/v3',
'region': 'RegionOne',
'interface': 'admin'
}],
'type': 'identity'
}],
'expires_at': '2010-11-01T03:32:15-05:00',
'user': {
'domain': {
'id': '4e6893b7ba0b4006840c3845660b86ed',
'name': 'exampledomain'
},
'id': 'c4da488862bd435c9e6c0275a0d0e49a',
'name': 'exampleuser',
},
'project': {
'domain': {
'id': '4e6893b7ba0b4006840c3845660b86ed',
'name': 'exampledomain'
},
'id': '225da22d3ce34b15877ea70b2a575f58',
'name': 'exampleproject',
},
}
}
AUTH_SUBJECT_TOKEN = '3e2813b7ba0b4006840c3825860b86ed'
AUTH_RESPONSE_HEADERS = {
'X-Subject-Token': AUTH_SUBJECT_TOKEN
}
AUTH_RESPONSE_BODY = {
'token': {
'methods': [
'password'
],
'expires_at': '2010-11-01T03:32:15-05:00',
'project': {
'domain': {
'id': '123',
'name': 'aDomain'
},
'id': '345',
'name': 'aTenant'
},
'user': {
'domain': {
'id': '1',
'name': 'aDomain'
},
'id': '567',
'name': 'test'
},
'issued_at': '2010-10-31T03:32:15-05:00',
'catalog': [{
'endpoints': [{
'url': 'https://compute.north.host/novapi/public',
'region': 'North',
'interface': 'public'
}, {
'url': 'https://compute.north.host/novapi/internal',
'region': 'North',
'interface': 'internal'
}, {
'url': 'https://compute.north.host/novapi/admin',
'region': 'North',
'interface': 'admin'
}],
'type': 'compute'
}, {
'endpoints': [{
'url': 'http://swift.north.host/swiftapi/public',
'region': 'South',
'interface': 'public'
}, {
'url': 'http://swift.north.host/swiftapi/internal',
'region': 'South',
'interface': 'internal'
}, {
'url': 'http://swift.north.host/swiftapi/admin',
'region': 'South',
'interface': 'admin'
}],
'type': 'object-store'
}, {
'endpoints': [{
'url': 'http://glance.north.host/glanceapi/public',
'region': 'North',
'interface': 'public'
}, {
'url': 'http://glance.north.host/glanceapi/internal',
'region': 'North',
'interface': 'internal'
}, {
'url': 'http://glance.north.host/glanceapi/admin',
'region': 'North',
'interface': 'admin'
}, {
'url': 'http://glance.south.host/glanceapi/public',
'region': 'South',
'interface': 'public'
}, {
'url': 'http://glance.south.host/glanceapi/internal',
'region': 'South',
'interface': 'internal'
}, {
'url': 'http://glance.south.host/glanceapi/admin',
'region': 'South',
'interface': 'admin'
}],
'type': 'image'
}]
}
}
TRUST_TOKEN = {
'token': {
'methods': [
'password'
],
'catalog': {},
'expires_at': '2010-11-01T03:32:15-05:00',
"OS-TRUST:trust": {
"id": "fe0aef",
"impersonation": False,
"links": {
"self": "http://identity:35357/v3/trusts/fe0aef"
},
"trustee_user": {
"id": "0ca8f6",
"links": {
"self": "http://identity:35357/v3/users/0ca8f6"
}
},
"trustor_user": {
"id": "bd263c",
"links": {
"self": "http://identity:35357/v3/users/bd263c"
}
}
},
'user': {
'domain': {
'id': '4e6893b7ba0b4006840c3845660b86ed',
'name': 'exampledomain'
},
'id': '0ca8f6',
'name': 'exampleuser',
}
}
}

142
v3/test_access.py Normal file
View File

@@ -0,0 +1,142 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import datetime
from keystoneclient import access
from keystoneclient.openstack.common import timeutils
from keystoneclient.tests.v3 import client_fixtures
from keystoneclient.tests.v3 import utils
TOKEN_RESPONSE = utils.TestResponse({
"headers": client_fixtures.AUTH_RESPONSE_HEADERS
})
UNSCOPED_TOKEN = client_fixtures.UNSCOPED_TOKEN
DOMAIN_SCOPED_TOKEN = client_fixtures.DOMAIN_SCOPED_TOKEN
PROJECT_SCOPED_TOKEN = client_fixtures.PROJECT_SCOPED_TOKEN
class AccessInfoTest(utils.TestCase):
def test_building_unscoped_accessinfo(self):
auth_ref = access.AccessInfo.factory(resp=TOKEN_RESPONSE,
body=UNSCOPED_TOKEN)
self.assertTrue(auth_ref)
self.assertIn('methods', auth_ref)
self.assertIn('catalog', auth_ref)
self.assertFalse(auth_ref['catalog'])
self.assertEquals(auth_ref.auth_token,
'3e2813b7ba0b4006840c3825860b86ed')
self.assertEquals(auth_ref.username, 'exampleuser')
self.assertEquals(auth_ref.user_id, 'c4da488862bd435c9e6c0275a0d0e49a')
self.assertEquals(auth_ref.project_name, None)
self.assertEquals(auth_ref.project_id, None)
self.assertEquals(auth_ref.auth_url, None)
self.assertEquals(auth_ref.management_url, None)
self.assertFalse(auth_ref.domain_scoped)
self.assertFalse(auth_ref.project_scoped)
self.assertEquals(auth_ref.user_domain_id,
'4e6893b7ba0b4006840c3845660b86ed')
self.assertEquals(auth_ref.user_domain_name, 'exampledomain')
self.assertIsNone(auth_ref.project_domain_id)
self.assertIsNone(auth_ref.project_domain_name)
self.assertEquals(auth_ref.expires, timeutils.parse_isotime(
UNSCOPED_TOKEN['token']['expires_at']))
def test_will_expire_soon(self):
expires = timeutils.utcnow() + datetime.timedelta(minutes=5)
UNSCOPED_TOKEN['token']['expires_at'] = expires.isoformat()
auth_ref = access.AccessInfo.factory(resp=TOKEN_RESPONSE,
body=UNSCOPED_TOKEN)
self.assertFalse(auth_ref.will_expire_soon(stale_duration=120))
self.assertTrue(auth_ref.will_expire_soon(stale_duration=300))
self.assertFalse(auth_ref.will_expire_soon())
def test_building_domain_scoped_accessinfo(self):
auth_ref = access.AccessInfo.factory(resp=TOKEN_RESPONSE,
body=DOMAIN_SCOPED_TOKEN)
self.assertTrue(auth_ref)
self.assertIn('methods', auth_ref)
self.assertIn('catalog', auth_ref)
self.assertFalse(auth_ref['catalog'])
self.assertEquals(auth_ref.auth_token,
'3e2813b7ba0b4006840c3825860b86ed')
self.assertEquals(auth_ref.username, 'exampleuser')
self.assertEquals(auth_ref.user_id, 'c4da488862bd435c9e6c0275a0d0e49a')
self.assertEquals(auth_ref.domain_name, 'anotherdomain')
self.assertEquals(auth_ref.domain_id,
'8e9283b7ba0b1038840c3842058b86ab')
self.assertEquals(auth_ref.project_name, None)
self.assertEquals(auth_ref.project_id, None)
self.assertEquals(auth_ref.user_domain_id,
'4e6893b7ba0b4006840c3845660b86ed')
self.assertEquals(auth_ref.user_domain_name, 'exampledomain')
self.assertIsNone(auth_ref.project_domain_id)
self.assertIsNone(auth_ref.project_domain_name)
self.assertTrue(auth_ref.domain_scoped)
self.assertFalse(auth_ref.project_scoped)
def test_building_project_scoped_accessinfo(self):
auth_ref = access.AccessInfo.factory(resp=TOKEN_RESPONSE,
body=PROJECT_SCOPED_TOKEN)
self.assertTrue(auth_ref)
self.assertIn('methods', auth_ref)
self.assertIn('catalog', auth_ref)
self.assertTrue(auth_ref['catalog'])
self.assertEquals(auth_ref.auth_token,
'3e2813b7ba0b4006840c3825860b86ed')
self.assertEquals(auth_ref.username, 'exampleuser')
self.assertEquals(auth_ref.user_id, 'c4da488862bd435c9e6c0275a0d0e49a')
self.assertEquals(auth_ref.domain_name, None)
self.assertEquals(auth_ref.domain_id, None)
self.assertEquals(auth_ref.project_name, 'exampleproject')
self.assertEquals(auth_ref.project_id,
'225da22d3ce34b15877ea70b2a575f58')
self.assertEquals(auth_ref.tenant_name, auth_ref.project_name)
self.assertEquals(auth_ref.tenant_id, auth_ref.project_id)
self.assertEquals(auth_ref.auth_url,
('http://public.com:5000/v3',))
self.assertEquals(auth_ref.management_url,
('http://admin:35357/v3',))
self.assertEquals(auth_ref.project_domain_id,
'4e6893b7ba0b4006840c3845660b86ed')
self.assertEquals(auth_ref.project_domain_name, 'exampledomain')
self.assertEquals(auth_ref.user_domain_id,
'4e6893b7ba0b4006840c3845660b86ed')
self.assertEquals(auth_ref.user_domain_name, 'exampledomain')
self.assertFalse(auth_ref.domain_scoped)
self.assertTrue(auth_ref.project_scoped)

303
v3/test_auth.py Normal file
View File

@@ -0,0 +1,303 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import httpretty
from keystoneclient import exceptions
from keystoneclient.tests.v3 import utils
from keystoneclient.v3 import client
class AuthenticateAgainstKeystoneTests(utils.TestCase):
def setUp(self):
super(AuthenticateAgainstKeystoneTests, self).setUp()
self.TEST_RESPONSE_DICT = {
"token": {
"methods": [
"token",
"password"
],
"expires_at": "2020-01-01T00:00:10.000123Z",
"project": {
"domain": {
"id": self.TEST_DOMAIN_ID,
"name": self.TEST_DOMAIN_NAME
},
"id": self.TEST_TENANT_ID,
"name": self.TEST_TENANT_NAME
},
"user": {
"domain": {
"id": self.TEST_DOMAIN_ID,
"name": self.TEST_DOMAIN_NAME
},
"id": self.TEST_USER,
"name": self.TEST_USER
},
"issued_at": "2013-05-29T16:55:21.468960Z",
"catalog": self.TEST_SERVICE_CATALOG
},
}
self.TEST_REQUEST_BODY = {
"auth": {
"identity": {
"methods": ["password"],
"password": {
"user": {
"domain": {
"name": self.TEST_DOMAIN_NAME
},
"name": self.TEST_USER,
"password": self.TEST_TOKEN
}
}
},
"scope": {
"project": {
"id": self.TEST_TENANT_ID
},
}
}
}
self.TEST_REQUEST_HEADERS = {
'Content-Type': 'application/json',
'User-Agent': 'python-keystoneclient'
}
self.TEST_RESPONSE_HEADERS = {
'X-Subject-Token': self.TEST_TOKEN
}
@httpretty.activate
def test_authenticate_success(self):
TEST_TOKEN = "abcdef"
ident = self.TEST_REQUEST_BODY['auth']['identity']
del ident['password']['user']['domain']
del ident['password']['user']['name']
ident['password']['user']['id'] = self.TEST_USER
self.stub_auth(json=self.TEST_RESPONSE_DICT, subject_token=TEST_TOKEN)
cs = client.Client(user_id=self.TEST_USER,
password=self.TEST_TOKEN,
project_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL)
self.assertEqual(cs.auth_token, TEST_TOKEN)
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY)
@httpretty.activate
def test_authenticate_failure(self):
ident = self.TEST_REQUEST_BODY['auth']['identity']
ident['password']['user']['password'] = 'bad_key'
error = {"unauthorized": {"message": "Unauthorized",
"code": "401"}}
self.stub_auth(status=401, json=error)
# Workaround for issue with assertRaises on python2.6
# where with assertRaises(exceptions.Unauthorized): doesn't work
# right
def client_create_wrapper():
client.Client(user_domain_name=self.TEST_DOMAIN_NAME,
username=self.TEST_USER,
password="bad_key",
project_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL)
self.assertRaises(exceptions.Unauthorized, client_create_wrapper)
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY)
@httpretty.activate
def test_auth_redirect(self):
self.stub_auth(status=305, body='Use proxy',
location=self.TEST_ADMIN_URL + '/auth/tokens')
self.stub_auth(json=self.TEST_RESPONSE_DICT,
base_url=self.TEST_ADMIN_URL)
cs = client.Client(user_domain_name=self.TEST_DOMAIN_NAME,
username=self.TEST_USER,
password=self.TEST_TOKEN,
project_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL)
self.assertEqual(cs.management_url,
self.TEST_RESPONSE_DICT["token"]["catalog"][3]
['endpoints'][2]["url"])
self.assertEqual(cs.auth_token,
self.TEST_RESPONSE_HEADERS["X-Subject-Token"])
@httpretty.activate
def test_authenticate_success_domain_username_password_scoped(self):
self.stub_auth(json=self.TEST_RESPONSE_DICT)
cs = client.Client(user_domain_name=self.TEST_DOMAIN_NAME,
username=self.TEST_USER,
password=self.TEST_TOKEN,
project_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL)
self.assertEqual(cs.management_url,
self.TEST_RESPONSE_DICT["token"]["catalog"][3]
['endpoints'][2]["url"])
self.assertEqual(cs.auth_token,
self.TEST_RESPONSE_HEADERS["X-Subject-Token"])
@httpretty.activate
def test_authenticate_success_userid_password_domain_scoped(self):
ident = self.TEST_REQUEST_BODY['auth']['identity']
del ident['password']['user']['domain']
del ident['password']['user']['name']
ident['password']['user']['id'] = self.TEST_USER
scope = self.TEST_REQUEST_BODY['auth']['scope']
del scope['project']
scope['domain'] = {}
scope['domain']['id'] = self.TEST_DOMAIN_ID
token = self.TEST_RESPONSE_DICT['token']
del token['project']
token['domain'] = {}
token['domain']['id'] = self.TEST_DOMAIN_ID
token['domain']['name'] = self.TEST_DOMAIN_NAME
self.stub_auth(json=self.TEST_RESPONSE_DICT)
cs = client.Client(user_id=self.TEST_USER,
password=self.TEST_TOKEN,
domain_id=self.TEST_DOMAIN_ID,
auth_url=self.TEST_URL)
self.assertEqual(cs.auth_domain_id,
self.TEST_DOMAIN_ID)
self.assertEqual(cs.management_url,
self.TEST_RESPONSE_DICT["token"]["catalog"][3]
['endpoints'][2]["url"])
self.assertEqual(cs.auth_token,
self.TEST_RESPONSE_HEADERS["X-Subject-Token"])
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY)
@httpretty.activate
def test_authenticate_success_userid_password_project_scoped(self):
ident = self.TEST_REQUEST_BODY['auth']['identity']
del ident['password']['user']['domain']
del ident['password']['user']['name']
ident['password']['user']['id'] = self.TEST_USER
self.stub_auth(json=self.TEST_RESPONSE_DICT)
cs = client.Client(user_id=self.TEST_USER,
password=self.TEST_TOKEN,
project_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL)
self.assertEqual(cs.auth_tenant_id,
self.TEST_TENANT_ID)
self.assertEqual(cs.management_url,
self.TEST_RESPONSE_DICT["token"]["catalog"][3]
['endpoints'][2]["url"])
self.assertEqual(cs.auth_token,
self.TEST_RESPONSE_HEADERS["X-Subject-Token"])
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY)
@httpretty.activate
def test_authenticate_success_password_unscoped(self):
del self.TEST_RESPONSE_DICT['token']['catalog']
del self.TEST_REQUEST_BODY['auth']['scope']
self.stub_auth(json=self.TEST_RESPONSE_DICT)
cs = client.Client(user_domain_name=self.TEST_DOMAIN_NAME,
username=self.TEST_USER,
password=self.TEST_TOKEN,
auth_url=self.TEST_URL)
self.assertEqual(cs.auth_token,
self.TEST_RESPONSE_HEADERS["X-Subject-Token"])
self.assertFalse('catalog' in cs.service_catalog.catalog)
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY)
@httpretty.activate
def test_authenticate_success_token_domain_scoped(self):
ident = self.TEST_REQUEST_BODY['auth']['identity']
del ident['password']
ident['methods'] = ['token']
ident['token'] = {}
ident['token']['id'] = self.TEST_TOKEN
scope = self.TEST_REQUEST_BODY['auth']['scope']
del scope['project']
scope['domain'] = {}
scope['domain']['id'] = self.TEST_DOMAIN_ID
token = self.TEST_RESPONSE_DICT['token']
del token['project']
token['domain'] = {}
token['domain']['id'] = self.TEST_DOMAIN_ID
token['domain']['name'] = self.TEST_DOMAIN_NAME
self.TEST_REQUEST_HEADERS['X-Auth-Token'] = self.TEST_TOKEN
self.stub_auth(json=self.TEST_RESPONSE_DICT)
cs = client.Client(token=self.TEST_TOKEN,
domain_id=self.TEST_DOMAIN_ID,
auth_url=self.TEST_URL)
self.assertEqual(cs.auth_domain_id,
self.TEST_DOMAIN_ID)
self.assertEqual(cs.management_url,
self.TEST_RESPONSE_DICT["token"]["catalog"][3]
['endpoints'][2]["url"])
self.assertEqual(cs.auth_token,
self.TEST_RESPONSE_HEADERS["X-Subject-Token"])
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY)
@httpretty.activate
def test_authenticate_success_token_project_scoped(self):
ident = self.TEST_REQUEST_BODY['auth']['identity']
del ident['password']
ident['methods'] = ['token']
ident['token'] = {}
ident['token']['id'] = self.TEST_TOKEN
self.TEST_REQUEST_HEADERS['X-Auth-Token'] = self.TEST_TOKEN
self.stub_auth(json=self.TEST_RESPONSE_DICT)
cs = client.Client(token=self.TEST_TOKEN,
project_id=self.TEST_TENANT_ID,
auth_url=self.TEST_URL)
self.assertEqual(cs.auth_tenant_id,
self.TEST_TENANT_ID)
self.assertEqual(cs.management_url,
self.TEST_RESPONSE_DICT["token"]["catalog"][3]
['endpoints'][2]["url"])
self.assertEqual(cs.auth_token,
self.TEST_RESPONSE_HEADERS["X-Subject-Token"])
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY)
@httpretty.activate
def test_authenticate_success_token_unscoped(self):
ident = self.TEST_REQUEST_BODY['auth']['identity']
del ident['password']
ident['methods'] = ['token']
ident['token'] = {}
ident['token']['id'] = self.TEST_TOKEN
del self.TEST_REQUEST_BODY['auth']['scope']
del self.TEST_RESPONSE_DICT['token']['catalog']
self.TEST_REQUEST_HEADERS['X-Auth-Token'] = self.TEST_TOKEN
self.stub_auth(json=self.TEST_RESPONSE_DICT)
cs = client.Client(token=self.TEST_TOKEN,
auth_url=self.TEST_URL)
self.assertEqual(cs.auth_token,
self.TEST_RESPONSE_HEADERS["X-Subject-Token"])
self.assertFalse('catalog' in cs.service_catalog.catalog)
self.assertRequestBodyIs(json=self.TEST_REQUEST_BODY)

136
v3/test_client.py Normal file
View File

@@ -0,0 +1,136 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import httpretty
from keystoneclient import exceptions
from keystoneclient.tests.v3 import client_fixtures
from keystoneclient.tests.v3 import utils
from keystoneclient.v3 import client
class KeystoneClientTest(utils.TestCase):
@httpretty.activate
def test_unscoped_init(self):
self.stub_auth(json=client_fixtures.UNSCOPED_TOKEN)
c = client.Client(user_domain_name='exampledomain',
username='exampleuser',
password='password',
auth_url=self.TEST_URL)
self.assertIsNotNone(c.auth_ref)
self.assertFalse(c.auth_ref.domain_scoped)
self.assertFalse(c.auth_ref.project_scoped)
self.assertEquals(c.auth_user_id,
'c4da488862bd435c9e6c0275a0d0e49a')
@httpretty.activate
def test_domain_scoped_init(self):
self.stub_auth(json=client_fixtures.DOMAIN_SCOPED_TOKEN)
c = client.Client(user_id='c4da488862bd435c9e6c0275a0d0e49a',
password='password',
domain_name='exampledomain',
auth_url=self.TEST_URL)
self.assertIsNotNone(c.auth_ref)
self.assertTrue(c.auth_ref.domain_scoped)
self.assertFalse(c.auth_ref.project_scoped)
self.assertEquals(c.auth_user_id,
'c4da488862bd435c9e6c0275a0d0e49a')
self.assertEquals(c.auth_domain_id,
'8e9283b7ba0b1038840c3842058b86ab')
@httpretty.activate
def test_project_scoped_init(self):
self.stub_auth(json=client_fixtures.PROJECT_SCOPED_TOKEN),
c = client.Client(user_id='c4da488862bd435c9e6c0275a0d0e49a',
password='password',
user_domain_name='exampledomain',
project_name='exampleproject',
auth_url=self.TEST_URL)
self.assertIsNotNone(c.auth_ref)
self.assertFalse(c.auth_ref.domain_scoped)
self.assertTrue(c.auth_ref.project_scoped)
self.assertEquals(c.auth_user_id,
'c4da488862bd435c9e6c0275a0d0e49a')
self.assertEquals(c.auth_tenant_id,
'225da22d3ce34b15877ea70b2a575f58')
@httpretty.activate
def test_auth_ref_load(self):
self.stub_auth(json=client_fixtures.PROJECT_SCOPED_TOKEN)
c = client.Client(user_id='c4da488862bd435c9e6c0275a0d0e49a',
password='password',
project_id='225da22d3ce34b15877ea70b2a575f58',
auth_url=self.TEST_URL)
cache = json.dumps(c.auth_ref)
new_client = client.Client(auth_ref=json.loads(cache))
self.assertIsNotNone(new_client.auth_ref)
self.assertFalse(new_client.auth_ref.domain_scoped)
self.assertTrue(new_client.auth_ref.project_scoped)
self.assertEquals(new_client.username, 'exampleuser')
self.assertIsNone(new_client.password)
self.assertEqual(new_client.management_url,
'http://admin:35357/v3')
@httpretty.activate
def test_auth_ref_load_with_overridden_arguments(self):
new_auth_url = 'https://newkeystone.com/v3'
self.stub_auth(json=client_fixtures.PROJECT_SCOPED_TOKEN)
self.stub_auth(json=client_fixtures.PROJECT_SCOPED_TOKEN,
base_url=new_auth_url)
c = client.Client(user_id='c4da488862bd435c9e6c0275a0d0e49a',
password='password',
project_id='225da22d3ce34b15877ea70b2a575f58',
auth_url=self.TEST_URL)
cache = json.dumps(c.auth_ref)
new_client = client.Client(auth_ref=json.loads(cache),
auth_url=new_auth_url)
self.assertIsNotNone(new_client.auth_ref)
self.assertFalse(new_client.auth_ref.domain_scoped)
self.assertTrue(new_client.auth_ref.project_scoped)
self.assertEquals(new_client.auth_url, new_auth_url)
self.assertEquals(new_client.username, 'exampleuser')
self.assertIsNone(new_client.password)
self.assertEqual(new_client.management_url,
'http://admin:35357/v3')
@httpretty.activate
def test_trust_init(self):
self.stub_auth(json=client_fixtures.TRUST_TOKEN)
c = client.Client(user_domain_name='exampledomain',
username='exampleuser',
password='password',
auth_url=self.TEST_URL,
trust_id='fe0aef')
self.assertIsNotNone(c.auth_ref)
self.assertFalse(c.auth_ref.domain_scoped)
self.assertFalse(c.auth_ref.project_scoped)
self.assertEqual(c.auth_ref.trust_id, 'fe0aef')
self.assertTrue(c.auth_ref.trust_scoped)
self.assertEquals(c.auth_user_id, '0ca8f6')
def test_init_err_no_auth_url(self):
self.assertRaises(exceptions.AuthorizationFailure,
client.Client,
username='exampleuser',
password='password')

35
v3/test_credentials.py Normal file
View File

@@ -0,0 +1,35 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import uuid
from keystoneclient.tests.v3 import utils
from keystoneclient.v3 import credentials
class CredentialTests(utils.TestCase, utils.CrudTests):
def setUp(self):
super(CredentialTests, self).setUp()
self.key = 'credential'
self.collection_key = 'credentials'
self.model = credentials.Credential
self.manager = self.client.credentials
def new_ref(self, **kwargs):
kwargs = super(CredentialTests, self).new_ref(**kwargs)
kwargs.setdefault('data', uuid.uuid4().hex)
kwargs.setdefault('project_id', uuid.uuid4().hex)
kwargs.setdefault('type', uuid.uuid4().hex)
kwargs.setdefault('user_id', uuid.uuid4().hex)
return kwargs

87
v3/test_discover.py Normal file
View File

@@ -0,0 +1,87 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import httpretty
from keystoneclient.generic import client
from keystoneclient.tests.v3 import utils
class DiscoverKeystoneTests(utils.UnauthenticatedTestCase):
def setUp(self):
super(DiscoverKeystoneTests, self).setUp()
self.TEST_RESPONSE_DICT = {
"versions": {
"values": [{"id": "v3.0",
"status": "beta",
"updated": "2013-03-06T00:00:00Z",
"links": [
{"rel": "self",
"href": "http://127.0.0.1:5000/v3.0/", },
{"rel": "describedby",
"type": "text/html",
"href": "http://docs.openstack.org/api/"
"openstack-identity-service/3/"
"content/", },
{"rel": "describedby",
"type": "application/pdf",
"href": "http://docs.openstack.org/api/"
"openstack-identity-service/3/"
"identity-dev-guide-3.pdf", },
]},
{"id": "v2.0",
"status": "beta",
"updated": "2013-03-06T00:00:00Z",
"links": [
{"rel": "self",
"href": "http://127.0.0.1:5000/v2.0/", },
{"rel": "describedby",
"type": "text/html",
"href": "http://docs.openstack.org/api/"
"openstack-identity-service/2.0/"
"content/", },
{"rel": "describedby",
"type": "application/pdf",
"href": "http://docs.openstack.org/api/"
"openstack-identity-service/2.0/"
"identity-dev-guide-2.0.pdf", }
]}],
},
}
self.TEST_REQUEST_HEADERS = {
'User-Agent': 'python-keystoneclient',
'Accept': 'application/json',
}
@httpretty.activate
def test_get_version_local(self):
httpretty.register_uri(httpretty.GET, "http://localhost:35357/",
status=300,
body=json.dumps(self.TEST_RESPONSE_DICT))
cs = client.Client()
versions = cs.discover()
self.assertIsInstance(versions, dict)
self.assertIn('message', versions)
self.assertIn('v3.0', versions)
self.assertEquals(
versions['v3.0']['url'],
self.TEST_RESPONSE_DICT['versions']['values'][0]['links'][0]
['href'])
self.assertEquals(
versions['v2.0']['url'],
self.TEST_RESPONSE_DICT['versions']['values'][1]['links'][0]
['href'])

33
v3/test_domains.py Normal file
View File

@@ -0,0 +1,33 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import uuid
from keystoneclient.tests.v3 import utils
from keystoneclient.v3 import domains
class DomainTests(utils.TestCase, utils.CrudTests):
def setUp(self):
super(DomainTests, self).setUp()
self.key = 'domain'
self.collection_key = 'domains'
self.model = domains.Domain
self.manager = self.client.domains
def new_ref(self, **kwargs):
kwargs = super(DomainTests, self).new_ref(**kwargs)
kwargs.setdefault('enabled', True)
kwargs.setdefault('name', uuid.uuid4().hex)
return kwargs

91
v3/test_endpoints.py Normal file
View File

@@ -0,0 +1,91 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import uuid
from keystoneclient.tests.v3 import utils
from keystoneclient.v3 import endpoints
class EndpointTests(utils.TestCase, utils.CrudTests):
def setUp(self):
super(EndpointTests, self).setUp()
self.key = 'endpoint'
self.collection_key = 'endpoints'
self.model = endpoints.Endpoint
self.manager = self.client.endpoints
def new_ref(self, **kwargs):
kwargs = super(EndpointTests, self).new_ref(**kwargs)
kwargs.setdefault('interface', 'public')
kwargs.setdefault('region', uuid.uuid4().hex)
kwargs.setdefault('service_id', uuid.uuid4().hex)
kwargs.setdefault('url', uuid.uuid4().hex)
kwargs.setdefault('enabled', True)
return kwargs
def test_create_public_interface(self):
ref = self.new_ref(interface='public')
self.test_create(ref)
def test_create_admin_interface(self):
ref = self.new_ref(interface='admin')
self.test_create(ref)
def test_create_internal_interface(self):
ref = self.new_ref(interface='internal')
self.test_create(ref)
def test_create_invalid_interface(self):
ref = self.new_ref(interface=uuid.uuid4().hex)
self.assertRaises(Exception, self.manager.create,
**utils.parameterize(ref))
def test_update_public_interface(self):
ref = self.new_ref(interface='public')
self.test_update(ref)
def test_update_admin_interface(self):
ref = self.new_ref(interface='admin')
self.test_update(ref)
def test_update_internal_interface(self):
ref = self.new_ref(interface='internal')
self.test_update(ref)
def test_update_invalid_interface(self):
ref = self.new_ref(interface=uuid.uuid4().hex)
self.assertRaises(Exception, self.manager.update,
**utils.parameterize(ref))
def test_list_public_interface(self):
interface = 'public'
expected_path = 'v3/%s?interface=%s' % (self.collection_key, interface)
self.test_list(expected_path=expected_path, interface=interface)
def test_list_admin_interface(self):
interface = 'admin'
expected_path = 'v3/%s?interface=%s' % (self.collection_key, interface)
self.test_list(expected_path=expected_path, interface=interface)
def test_list_internal_interface(self):
interface = 'admin'
expected_path = 'v3/%s?interface=%s' % (self.collection_key, interface)
self.test_list(expected_path=expected_path, interface=interface)
def test_list_invalid_interface(self):
interface = uuid.uuid4().hex
expected_path = 'v3/%s?interface=%s' % (self.collection_key, interface)
self.assertRaises(Exception, self.manager.list,
expected_path=expected_path, interface=interface)

65
v3/test_groups.py Normal file
View File

@@ -0,0 +1,65 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 OpenStack LLC
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import uuid
import httpretty
from keystoneclient.tests.v3 import utils
from keystoneclient.v3 import groups
class GroupTests(utils.TestCase, utils.CrudTests):
def setUp(self):
super(GroupTests, self).setUp()
self.key = 'group'
self.collection_key = 'groups'
self.model = groups.Group
self.manager = self.client.groups
def new_ref(self, **kwargs):
kwargs = super(GroupTests, self).new_ref(**kwargs)
kwargs.setdefault('name', uuid.uuid4().hex)
return kwargs
@httpretty.activate
def test_list_groups_for_user(self):
user_id = uuid.uuid4().hex
ref_list = [self.new_ref(), self.new_ref()]
self.stub_entity(httpretty.GET,
['users', user_id, self.collection_key],
status=200, entity=ref_list)
returned_list = self.manager.list(user=user_id)
self.assertTrue(len(returned_list))
[self.assertTrue(isinstance(r, self.model)) for r in returned_list]
@httpretty.activate
def test_list_groups_for_domain(self):
ref_list = [self.new_ref(), self.new_ref()]
domain_id = uuid.uuid4().hex
self.stub_entity(httpretty.GET,
[self.collection_key],
status=200, entity=ref_list)
returned_list = self.manager.list(domain=domain_id)
self.assertTrue(len(returned_list))
[self.assertTrue(isinstance(r, self.model)) for r in returned_list]
self.assertEqual(httpretty.last_request().querystring,
{'domain_id': [domain_id]})

33
v3/test_policies.py Normal file
View File

@@ -0,0 +1,33 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import uuid
from keystoneclient.tests.v3 import utils
from keystoneclient.v3 import policies
class PolicyTests(utils.TestCase, utils.CrudTests):
def setUp(self):
super(PolicyTests, self).setUp()
self.key = 'policy'
self.collection_key = 'policies'
self.model = policies.Policy
self.manager = self.client.policies
def new_ref(self, **kwargs):
kwargs = super(PolicyTests, self).new_ref(**kwargs)
kwargs.setdefault('type', uuid.uuid4().hex)
kwargs.setdefault('blob', uuid.uuid4().hex)
return kwargs

64
v3/test_projects.py Normal file
View File

@@ -0,0 +1,64 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import uuid
import httpretty
from keystoneclient.tests.v3 import utils
from keystoneclient.v3 import projects
class ProjectTests(utils.TestCase, utils.CrudTests):
def setUp(self):
super(ProjectTests, self).setUp()
self.key = 'project'
self.collection_key = 'projects'
self.model = projects.Project
self.manager = self.client.projects
def new_ref(self, **kwargs):
kwargs = super(ProjectTests, self).new_ref(**kwargs)
kwargs.setdefault('domain_id', uuid.uuid4().hex)
kwargs.setdefault('enabled', True)
kwargs.setdefault('name', uuid.uuid4().hex)
return kwargs
@httpretty.activate
def test_list_projects_for_user(self):
ref_list = [self.new_ref(), self.new_ref()]
user_id = uuid.uuid4().hex
self.stub_entity(httpretty.GET,
['users', user_id, self.collection_key],
entity=ref_list)
returned_list = self.manager.list(user=user_id)
self.assertTrue(len(returned_list))
[self.assertTrue(isinstance(r, self.model)) for r in returned_list]
@httpretty.activate
def test_list_projects_for_domain(self):
ref_list = [self.new_ref(), self.new_ref()]
domain_id = uuid.uuid4().hex
self.stub_entity(httpretty.GET, [self.collection_key],
entity=ref_list)
returned_list = self.manager.list(domain=domain_id)
self.assertTrue(len(returned_list))
[self.assertTrue(isinstance(r, self.model)) for r in returned_list]
self.assertEqual(httpretty.last_request().querystring,
{'domain_id': [domain_id]})

352
v3/test_roles.py Normal file
View File

@@ -0,0 +1,352 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 OpenStack LLC
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import uuid
import httpretty
from keystoneclient import exceptions
from keystoneclient.tests.v3 import utils
from keystoneclient.v3 import roles
class RoleTests(utils.TestCase, utils.CrudTests):
def setUp(self):
super(RoleTests, self).setUp()
self.key = 'role'
self.collection_key = 'roles'
self.model = roles.Role
self.manager = self.client.roles
def new_ref(self, **kwargs):
kwargs = super(RoleTests, self).new_ref(**kwargs)
kwargs.setdefault('name', uuid.uuid4().hex)
return kwargs
@httpretty.activate
def test_domain_role_grant(self):
user_id = uuid.uuid4().hex
domain_id = uuid.uuid4().hex
ref = self.new_ref()
self.stub_url(httpretty.PUT,
['domains', domain_id, 'users', user_id,
self.collection_key, ref['id']],
status=201)
self.manager.grant(role=ref['id'], domain=domain_id, user=user_id)
@httpretty.activate
def test_domain_group_role_grant(self):
group_id = uuid.uuid4().hex
domain_id = uuid.uuid4().hex
ref = self.new_ref()
self.stub_url(httpretty.PUT,
['domains', domain_id, 'groups', group_id,
self.collection_key, ref['id']],
status=201)
self.manager.grant(role=ref['id'], domain=domain_id, group=group_id)
@httpretty.activate
def test_domain_role_list(self):
user_id = uuid.uuid4().hex
domain_id = uuid.uuid4().hex
ref_list = [self.new_ref(), self.new_ref()]
self.stub_entity(httpretty.GET,
['domains', domain_id, 'users', user_id,
self.collection_key], entity=ref_list)
self.manager.list(domain=domain_id, user=user_id)
@httpretty.activate
def test_domain_group_role_list(self):
group_id = uuid.uuid4().hex
domain_id = uuid.uuid4().hex
ref_list = [self.new_ref(), self.new_ref()]
self.stub_entity(httpretty.GET,
['domains', domain_id, 'groups', group_id,
self.collection_key], entity=ref_list)
self.manager.list(domain=domain_id, group=group_id)
@httpretty.activate
def test_domain_role_check(self):
user_id = uuid.uuid4().hex
domain_id = uuid.uuid4().hex
ref = self.new_ref()
self.stub_url(httpretty.HEAD,
['domains', domain_id, 'users', user_id,
self.collection_key, ref['id']],
status=204)
self.manager.check(role=ref['id'], domain=domain_id,
user=user_id)
@httpretty.activate
def test_domain_group_role_check(self):
return
group_id = uuid.uuid4().hex
domain_id = uuid.uuid4().hex
ref = self.new_ref()
self.stub_url(httpretty.HEAD,
['domains', domain_id, 'groups', group_id,
self.collection_key, ref['id']],
status=204)
self.manager.check(role=ref['id'], domain=domain_id, group=group_id)
@httpretty.activate
def test_domain_role_revoke(self):
user_id = uuid.uuid4().hex
domain_id = uuid.uuid4().hex
ref = self.new_ref()
self.stub_url(httpretty.DELETE,
['domains', domain_id, 'users', user_id,
self.collection_key, ref['id']],
status=204)
self.manager.revoke(role=ref['id'], domain=domain_id, user=user_id)
@httpretty.activate
def test_domain_group_role_revoke(self):
group_id = uuid.uuid4().hex
domain_id = uuid.uuid4().hex
ref = self.new_ref()
self.stub_url(httpretty.DELETE,
['domains', domain_id, 'groups', group_id,
self.collection_key, ref['id']],
status=204)
self.manager.revoke(role=ref['id'], domain=domain_id, group=group_id)
@httpretty.activate
def test_project_role_grant(self):
user_id = uuid.uuid4().hex
project_id = uuid.uuid4().hex
ref = self.new_ref()
self.stub_url(httpretty.PUT,
['projects', project_id, 'users', user_id,
self.collection_key, ref['id']],
status=201)
self.manager.grant(role=ref['id'], project=project_id, user=user_id)
@httpretty.activate
def test_project_group_role_grant(self):
group_id = uuid.uuid4().hex
project_id = uuid.uuid4().hex
ref = self.new_ref()
self.stub_url(httpretty.PUT,
['projects', project_id, 'groups', group_id,
self.collection_key, ref['id']],
status=201)
self.manager.grant(role=ref['id'], project=project_id, group=group_id)
@httpretty.activate
def test_project_role_list(self):
user_id = uuid.uuid4().hex
project_id = uuid.uuid4().hex
ref_list = [self.new_ref(), self.new_ref()]
self.stub_entity(httpretty.GET,
['projects', project_id, 'users', user_id,
self.collection_key], entity=ref_list)
self.manager.list(project=project_id, user=user_id)
@httpretty.activate
def test_project_group_role_list(self):
group_id = uuid.uuid4().hex
project_id = uuid.uuid4().hex
ref_list = [self.new_ref(), self.new_ref()]
self.stub_entity(httpretty.GET,
['projects', project_id, 'groups', group_id,
self.collection_key], entity=ref_list)
self.manager.list(project=project_id, group=group_id)
@httpretty.activate
def test_project_role_check(self):
user_id = uuid.uuid4().hex
project_id = uuid.uuid4().hex
ref = self.new_ref()
self.stub_url(httpretty.HEAD,
['projects', project_id, 'users', user_id,
self.collection_key, ref['id']],
status=200)
self.manager.check(role=ref['id'], project=project_id, user=user_id)
@httpretty.activate
def test_project_group_role_check(self):
group_id = uuid.uuid4().hex
project_id = uuid.uuid4().hex
ref = self.new_ref()
self.stub_url(httpretty.HEAD,
['projects', project_id, 'groups', group_id,
self.collection_key, ref['id']],
status=200)
self.manager.check(role=ref['id'], project=project_id, group=group_id)
@httpretty.activate
def test_project_role_revoke(self):
user_id = uuid.uuid4().hex
project_id = uuid.uuid4().hex
ref = self.new_ref()
self.stub_url(httpretty.DELETE,
['projects', project_id, 'users', user_id,
self.collection_key, ref['id']],
status=204)
self.manager.revoke(role=ref['id'], project=project_id, user=user_id)
@httpretty.activate
def test_project_group_role_revoke(self):
group_id = uuid.uuid4().hex
project_id = uuid.uuid4().hex
ref = self.new_ref()
self.stub_url(httpretty.DELETE,
['projects', project_id, 'groups', group_id,
self.collection_key, ref['id']],
status=204)
self.manager.revoke(role=ref['id'], project=project_id, group=group_id)
@httpretty.activate
def test_domain_project_role_grant_fails(self):
user_id = uuid.uuid4().hex
project_id = uuid.uuid4().hex
domain_id = uuid.uuid4().hex
ref = self.new_ref()
self.assertRaises(
exceptions.ValidationError,
self.manager.grant,
role=ref['id'],
domain=domain_id,
project=project_id,
user=user_id)
def test_domain_project_role_list_fails(self):
user_id = uuid.uuid4().hex
project_id = uuid.uuid4().hex
domain_id = uuid.uuid4().hex
self.assertRaises(
exceptions.ValidationError,
self.manager.list,
domain=domain_id,
project=project_id,
user=user_id)
def test_domain_project_role_check_fails(self):
user_id = uuid.uuid4().hex
project_id = uuid.uuid4().hex
domain_id = uuid.uuid4().hex
ref = self.new_ref()
self.assertRaises(
exceptions.ValidationError,
self.manager.check,
role=ref['id'],
domain=domain_id,
project=project_id,
user=user_id)
def test_domain_project_role_revoke_fails(self):
user_id = uuid.uuid4().hex
project_id = uuid.uuid4().hex
domain_id = uuid.uuid4().hex
ref = self.new_ref()
self.assertRaises(
exceptions.ValidationError,
self.manager.revoke,
role=ref['id'],
domain=domain_id,
project=project_id,
user=user_id)
def test_user_group_role_grant_fails(self):
user_id = uuid.uuid4().hex
group_id = uuid.uuid4().hex
project_id = uuid.uuid4().hex
ref = self.new_ref()
self.assertRaises(
exceptions.ValidationError,
self.manager.grant,
role=ref['id'],
project=project_id,
group=group_id,
user=user_id)
def test_user_group_role_list_fails(self):
user_id = uuid.uuid4().hex
group_id = uuid.uuid4().hex
project_id = uuid.uuid4().hex
self.assertRaises(
exceptions.ValidationError,
self.manager.list,
project=project_id,
group=group_id,
user=user_id)
def test_user_group_role_check_fails(self):
user_id = uuid.uuid4().hex
group_id = uuid.uuid4().hex
project_id = uuid.uuid4().hex
ref = self.new_ref()
self.assertRaises(
exceptions.ValidationError,
self.manager.check,
role=ref['id'],
project=project_id,
group=group_id,
user=user_id)
def test_user_group_role_revoke_fails(self):
user_id = uuid.uuid4().hex
group_id = uuid.uuid4().hex
project_id = uuid.uuid4().hex
ref = self.new_ref()
self.assertRaises(
exceptions.ValidationError,
self.manager.revoke,
role=ref['id'],
project=project_id,
group=group_id,
user=user_id)

View File

@@ -0,0 +1,84 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import copy
from keystoneclient import access
from keystoneclient import exceptions
from keystoneclient.tests.v3 import client_fixtures
from keystoneclient.tests.v3 import utils
class ServiceCatalogTest(utils.TestCase):
def setUp(self):
super(ServiceCatalogTest, self).setUp()
self.AUTH_RESPONSE_BODY = client_fixtures.AUTH_RESPONSE_BODY
self.RESPONSE = utils.TestResponse({
"headers": client_fixtures.AUTH_RESPONSE_HEADERS
})
def test_building_a_service_catalog(self):
auth_ref = access.AccessInfo.factory(self.RESPONSE,
self.AUTH_RESPONSE_BODY)
sc = auth_ref.service_catalog
self.assertEquals(sc.url_for(service_type='compute'),
"https://compute.north.host/novapi/public")
self.assertEquals(sc.url_for(service_type='compute',
endpoint_type='internal'),
"https://compute.north.host/novapi/internal")
self.assertRaises(exceptions.EndpointNotFound, sc.url_for, "region",
"South", service_type='compute')
def test_service_catalog_endpoints(self):
auth_ref = access.AccessInfo.factory(self.RESPONSE,
self.AUTH_RESPONSE_BODY)
sc = auth_ref.service_catalog
public_ep = sc.get_endpoints(service_type='compute',
endpoint_type='public')
self.assertEquals(public_ep['compute'][0]['region'], 'North')
self.assertEquals(public_ep['compute'][0]['url'],
"https://compute.north.host/novapi/public")
def test_service_catalog_regions(self):
self.AUTH_RESPONSE_BODY['token']['region_name'] = "North"
auth_ref = access.AccessInfo.factory(self.RESPONSE,
self.AUTH_RESPONSE_BODY)
sc = auth_ref.service_catalog
url = sc.url_for(service_type='image', endpoint_type='public')
self.assertEquals(url, "http://glance.north.host/glanceapi/public")
self.AUTH_RESPONSE_BODY['token']['region_name'] = "South"
auth_ref = access.AccessInfo.factory(self.RESPONSE,
self.AUTH_RESPONSE_BODY)
sc = auth_ref.service_catalog
url = sc.url_for(service_type='image', endpoint_type='internal')
self.assertEquals(url, "http://glance.south.host/glanceapi/internal")
def test_service_catalog_empty(self):
# We need to do a copy.deepcopy here since
# dict(self.AUTH_RESPONSE_BODY) or self.AUTH_RESPONSE_BODY.copy() will
# only do a shadowcopy and sc_empty['token']['catalog'] will still be a
# reference to self.AUTH_RESPONSE_BODY so setting it to empty will fail
# the other tests that needs a service catalog.
sc_empty = copy.deepcopy(self.AUTH_RESPONSE_BODY)
sc_empty['token']['catalog'] = []
auth_ref = access.AccessInfo.factory(self.RESPONSE, sc_empty)
self.assertRaises(exceptions.EmptyCatalog,
auth_ref.service_catalog.url_for,
service_type='image',
endpoint_type='internalURL')

34
v3/test_services.py Normal file
View File

@@ -0,0 +1,34 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import uuid
from keystoneclient.tests.v3 import utils
from keystoneclient.v3 import services
class ServiceTests(utils.TestCase, utils.CrudTests):
def setUp(self):
super(ServiceTests, self).setUp()
self.key = 'service'
self.collection_key = 'services'
self.model = services.Service
self.manager = self.client.services
def new_ref(self, **kwargs):
kwargs = super(ServiceTests, self).new_ref(**kwargs)
kwargs.setdefault('name', uuid.uuid4().hex)
kwargs.setdefault('type', uuid.uuid4().hex)
kwargs.setdefault('enabled', True)
return kwargs

102
v3/test_trusts.py Normal file
View File

@@ -0,0 +1,102 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import uuid
from keystoneclient import exceptions
from keystoneclient.openstack.common import timeutils
from keystoneclient.tests.v3 import utils
from keystoneclient.v3.contrib import trusts
class TrustTests(utils.TestCase, utils.CrudTests):
def setUp(self):
super(TrustTests, self).setUp()
self.key = 'trust'
self.collection_key = 'trusts'
self.model = trusts.Trust
self.manager = self.client.trusts
self.path_prefix = 'OS-TRUST'
def new_ref(self, **kwargs):
kwargs = super(TrustTests, self).new_ref(**kwargs)
kwargs.setdefault('project_id', uuid.uuid4().hex)
return kwargs
def test_create(self):
ref = self.new_ref()
ref['trustor_user_id'] = uuid.uuid4().hex
ref['trustee_user_id'] = uuid.uuid4().hex
ref['impersonation'] = False
super(TrustTests, self).test_create(ref=ref)
def test_create_roles(self):
ref = self.new_ref()
ref['trustor_user_id'] = uuid.uuid4().hex
ref['trustee_user_id'] = uuid.uuid4().hex
ref['impersonation'] = False
req_ref = ref.copy()
# Note the TrustManager takes a list of role_names, and converts
# internally to the slightly odd list-of-dict API format, so we
# have to pass the expected request data to allow correct stubbing
ref['role_names'] = ['atestrole']
req_ref['roles'] = [{'name': 'atestrole'}]
super(TrustTests, self).test_create(ref=ref, req_ref=req_ref)
def test_create_expires(self):
ref = self.new_ref()
ref['trustor_user_id'] = uuid.uuid4().hex
ref['trustee_user_id'] = uuid.uuid4().hex
ref['impersonation'] = False
ref['expires_at'] = timeutils.parse_isotime(
'2013-03-04T12:00:01.000000Z')
req_ref = ref.copy()
# Note the TrustManager takes a datetime.datetime object for
# expires_at, and converts it internally into an iso format datestamp
req_ref['expires_at'] = '2013-03-04T12:00:01.000000Z'
super(TrustTests, self).test_create(ref=ref, req_ref=req_ref)
def test_create_imp(self):
ref = self.new_ref()
ref['trustor_user_id'] = uuid.uuid4().hex
ref['trustee_user_id'] = uuid.uuid4().hex
ref['impersonation'] = True
super(TrustTests, self).test_create(ref=ref)
def test_create_roles_imp(self):
ref = self.new_ref()
ref['trustor_user_id'] = uuid.uuid4().hex
ref['trustee_user_id'] = uuid.uuid4().hex
ref['impersonation'] = True
req_ref = ref.copy()
ref['role_names'] = ['atestrole']
req_ref['roles'] = [{'name': 'atestrole'}]
super(TrustTests, self).test_create(ref=ref, req_ref=req_ref)
def test_list_filter_trustor(self):
ep = 'v3/OS-TRUST/trusts?trustor_user_id=12345'
super(TrustTests, self).test_list(expected_path=ep,
trustor_user='12345')
def test_list_filter_trustee(self):
ep = 'v3/OS-TRUST/trusts?trustee_user_id=12345'
super(TrustTests, self).test_list(expected_path=ep,
trustee_user='12345')
def test_update(self):
# Update not supported for the OS-TRUST API
self.assertRaises(exceptions.HTTPNotImplemented, self.manager.update)

201
v3/test_users.py Normal file
View File

@@ -0,0 +1,201 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 OpenStack LLC
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import uuid
import httpretty
from keystoneclient import exceptions
from keystoneclient.tests.v3 import utils
from keystoneclient.v3 import users
class UserTests(utils.TestCase, utils.CrudTests):
def setUp(self):
super(UserTests, self).setUp()
self.key = 'user'
self.collection_key = 'users'
self.model = users.User
self.manager = self.client.users
def new_ref(self, **kwargs):
kwargs = super(UserTests, self).new_ref(**kwargs)
kwargs.setdefault('description', uuid.uuid4().hex)
kwargs.setdefault('domain_id', uuid.uuid4().hex)
kwargs.setdefault('enabled', True)
kwargs.setdefault('name', uuid.uuid4().hex)
kwargs.setdefault('default_project_id', uuid.uuid4().hex)
return kwargs
@httpretty.activate
def test_add_user_to_group(self):
group_id = uuid.uuid4().hex
ref = self.new_ref()
self.stub_url(httpretty.PUT,
['groups', group_id, self.collection_key, ref['id']],
status=204)
self.manager.add_to_group(user=ref['id'], group=group_id)
self.assertRaises(exceptions.ValidationError,
self.manager.remove_from_group,
user=ref['id'],
group=None)
@httpretty.activate
def test_list_users_in_group(self):
group_id = uuid.uuid4().hex
ref_list = [self.new_ref(), self.new_ref()]
self.stub_entity(httpretty.GET,
['groups', group_id, self.collection_key],
entity=ref_list)
returned_list = self.manager.list(group=group_id)
self.assertTrue(len(returned_list))
[self.assertTrue(isinstance(r, self.model)) for r in returned_list]
@httpretty.activate
def test_check_user_in_group(self):
group_id = uuid.uuid4().hex
ref = self.new_ref()
self.stub_url(httpretty.HEAD,
['groups', group_id, self.collection_key, ref['id']],
status=204)
self.manager.check_in_group(user=ref['id'], group=group_id)
self.assertRaises(exceptions.ValidationError,
self.manager.check_in_group,
user=ref['id'],
group=None)
@httpretty.activate
def test_remove_user_from_group(self):
group_id = uuid.uuid4().hex
ref = self.new_ref()
self.stub_url(httpretty.DELETE,
['groups', group_id, self.collection_key, ref['id']],
status=204)
self.manager.remove_from_group(user=ref['id'], group=group_id)
self.assertRaises(exceptions.ValidationError,
self.manager.remove_from_group,
user=ref['id'],
group=None)
@httpretty.activate
def test_create_with_project(self):
# Can create a user with the deprecated project option rather than
# default_project_id.
ref = self.new_ref()
self.stub_entity(httpretty.POST, [self.collection_key],
status=201, entity=ref)
req_ref = ref.copy()
req_ref.pop('id')
param_ref = req_ref.copy()
# Use deprecated project_id rather than new default_project_id.
param_ref['project_id'] = param_ref.pop('default_project_id')
params = utils.parameterize(param_ref)
returned = self.manager.create(**params)
self.assertTrue(isinstance(returned, self.model))
for attr in ref:
self.assertEqual(
getattr(returned, attr),
ref[attr],
'Expected different %s' % attr)
self.assertEntityRequestBodyIs(req_ref)
@httpretty.activate
def test_create_with_project_and_default_project(self):
# Can create a user with the deprecated project and default_project_id.
# The backend call should only pass the default_project_id.
ref = self.new_ref()
self.stub_entity(httpretty.POST,
[self.collection_key],
status=201, entity=ref)
req_ref = ref.copy()
req_ref.pop('id')
param_ref = req_ref.copy()
# Add the deprecated project_id in the call, the value will be ignored.
param_ref['project_id'] = 'project'
params = utils.parameterize(param_ref)
returned = self.manager.create(**params)
self.assertTrue(isinstance(returned, self.model))
for attr in ref:
self.assertEqual(
getattr(returned, attr),
ref[attr],
'Expected different %s' % attr)
self.assertEntityRequestBodyIs(req_ref)
@httpretty.activate
def test_update_with_project(self):
# Can update a user with the deprecated project option rather than
# default_project_id.
ref = self.new_ref()
req_ref = ref.copy()
req_ref.pop('id')
param_ref = req_ref.copy()
self.stub_entity(httpretty.PATCH,
[self.collection_key, ref['id']],
status=200, entity=ref)
# Use deprecated project_id rather than new default_project_id.
param_ref['project_id'] = param_ref.pop('default_project_id')
params = utils.parameterize(param_ref)
returned = self.manager.update(ref['id'], **params)
self.assertTrue(isinstance(returned, self.model))
for attr in ref:
self.assertEqual(
getattr(returned, attr),
ref[attr],
'Expected different %s' % attr)
self.assertEntityRequestBodyIs(req_ref)
@httpretty.activate
def test_update_with_project_and_default_project(self, ref=None):
ref = self.new_ref()
req_ref = ref.copy()
req_ref.pop('id')
param_ref = req_ref.copy()
self.stub_entity(httpretty.PATCH,
[self.collection_key, ref['id']],
status=200, entity=ref)
# Add the deprecated project_id in the call, the value will be ignored.
param_ref['project_id'] = 'project'
params = utils.parameterize(param_ref)
returned = self.manager.update(ref['id'], **params)
self.assertTrue(isinstance(returned, self.model))
for attr in ref:
self.assertEqual(
getattr(returned, attr),
ref[attr],
'Expected different %s' % attr)
self.assertEntityRequestBodyIs(req_ref)

286
v3/utils.py Normal file
View File

@@ -0,0 +1,286 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import urlparse
import uuid
import httpretty
from keystoneclient.openstack.common import jsonutils
from keystoneclient.tests import utils
from keystoneclient.v3 import client
TestResponse = utils.TestResponse
def parameterize(ref):
"""Rewrites attributes to match the kwarg naming convention in client.
>>> parameterize({'project_id': 0})
{'project': 0}
"""
params = ref.copy()
for key in ref:
if key[-3:] == '_id':
params.setdefault(key[:-3], params.pop(key))
return params
class UnauthenticatedTestCase(utils.TestCase):
"""Class used as base for unauthenticated calls."""
TEST_ROOT_URL = 'http://127.0.0.1:5000/'
TEST_URL = '%s%s' % (TEST_ROOT_URL, 'v3')
TEST_ROOT_ADMIN_URL = 'http://127.0.0.1:35357/'
TEST_ADMIN_URL = '%s%s' % (TEST_ROOT_ADMIN_URL, 'v3')
class TestCase(UnauthenticatedTestCase):
TEST_SERVICE_CATALOG = [{
"endpoints": [{
"url": "http://cdn.admin-nets.local:8774/v1.0/",
"region": "RegionOne",
"interface": "public"
}, {
"url": "http://127.0.0.1:8774/v1.0",
"region": "RegionOne",
"interface": "internal"
}, {
"url": "http://cdn.admin-nets.local:8774/v1.0",
"region": "RegionOne",
"interface": "admin"
}],
"type": "nova_compat"
}, {
"endpoints": [{
"url": "http://nova/novapi/public",
"region": "RegionOne",
"interface": "public"
}, {
"url": "http://nova/novapi/internal",
"region": "RegionOne",
"interface": "internal"
}, {
"url": "http://nova/novapi/admin",
"region": "RegionOne",
"interface": "admin"
}],
"type": "compute"
}, {
"endpoints": [{
"url": "http://glance/glanceapi/public",
"region": "RegionOne",
"interface": "public"
}, {
"url": "http://glance/glanceapi/internal",
"region": "RegionOne",
"interface": "internal"
}, {
"url": "http://glance/glanceapi/admin",
"region": "RegionOne",
"interface": "admin"
}],
"type": "image",
"name": "glance"
}, {
"endpoints": [{
"url": "http://127.0.0.1:5000/v3",
"region": "RegionOne",
"interface": "public"
}, {
"url": "http://127.0.0.1:5000/v3",
"region": "RegionOne",
"interface": "internal"
}, {
"url": "http://127.0.0.1:35357/v3",
"region": "RegionOne",
"interface": "admin"
}],
"type": "identity"
}, {
"endpoints": [{
"url": "http://swift/swiftapi/public",
"region": "RegionOne",
"interface": "public"
}, {
"url": "http://swift/swiftapi/internal",
"region": "RegionOne",
"interface": "internal"
}, {
"url": "http://swift/swiftapi/admin",
"region": "RegionOne",
"interface": "admin"
}],
"type": "object-store"
}]
def setUp(self):
super(TestCase, self).setUp()
self.client = client.Client(username=self.TEST_USER,
token=self.TEST_TOKEN,
tenant_name=self.TEST_TENANT_NAME,
auth_url=self.TEST_URL,
endpoint=self.TEST_URL)
def stub_auth(self, subject_token=None, **kwargs):
if not subject_token:
subject_token = self.TEST_TOKEN
self.stub_url(httpretty.POST, ['auth', 'tokens'],
X_Subject_Token=subject_token, **kwargs)
class CrudTests(object):
key = None
collection_key = None
model = None
manager = None
path_prefix = None
def new_ref(self, **kwargs):
kwargs.setdefault('id', uuid.uuid4().hex)
return kwargs
def encode(self, entity):
if isinstance(entity, dict):
return {self.key: entity}
if isinstance(entity, list):
return {self.collection_key: entity}
raise NotImplementedError('Are you sure you want to encode that?')
def stub_entity(self, method, parts=None, entity=None, id=None, **kwargs):
if entity:
entity = self.encode(entity)
kwargs['json'] = entity
if not parts:
parts = [self.collection_key]
if self.path_prefix:
parts.insert(0, self.path_prefix)
if id:
if not parts:
parts = []
parts.append(id)
self.stub_url(method, parts=parts, **kwargs)
def assertEntityRequestBodyIs(self, entity):
self.assertRequestBodyIs(json=self.encode(entity))
@httpretty.activate
def test_create(self, ref=None, req_ref=None):
ref = ref or self.new_ref()
manager_ref = ref.copy()
manager_ref.pop('id')
# req_ref argument allows you to specify a different
# signature for the request when the manager does some
# conversion before doing the request (e.g converting
# from datetime object to timestamp string)
req_ref = req_ref or ref.copy()
req_ref.pop('id')
self.stub_entity(httpretty.POST, entity=req_ref, status=201)
returned = self.manager.create(**parameterize(manager_ref))
self.assertTrue(isinstance(returned, self.model))
for attr in req_ref:
self.assertEqual(
getattr(returned, attr),
req_ref[attr],
'Expected different %s' % attr)
self.assertEntityRequestBodyIs(req_ref)
@httpretty.activate
def test_get(self, ref=None):
ref = ref or self.new_ref()
self.stub_entity(httpretty.GET, id=ref['id'], entity=ref)
returned = self.manager.get(ref['id'])
self.assertTrue(isinstance(returned, self.model))
for attr in ref:
self.assertEqual(
getattr(returned, attr),
ref[attr],
'Expected different %s' % attr)
@httpretty.activate
def test_list(self, ref_list=None, expected_path=None, **filter_kwargs):
ref_list = ref_list or [self.new_ref(), self.new_ref()]
if not expected_path:
if self.path_prefix:
expected_path = 'v3/%s/%s' % (self.path_prefix,
self.collection_key)
else:
expected_path = 'v3/%s' % self.collection_key
httpretty.register_uri(httpretty.GET,
urlparse.urljoin(self.TEST_URL, expected_path),
body=jsonutils.dumps(self.encode(ref_list)))
returned_list = self.manager.list(**filter_kwargs)
self.assertTrue(len(returned_list))
[self.assertTrue(isinstance(r, self.model)) for r in returned_list]
@httpretty.activate
def test_find(self, ref=None):
ref = ref or self.new_ref()
ref_list = [ref]
self.stub_entity(httpretty.GET, entity=ref_list)
returned = self.manager.find(name=getattr(ref, 'name', None))
self.assertTrue(isinstance(returned, self.model))
for attr in ref:
self.assertEqual(
getattr(returned, attr),
ref[attr],
'Expected different %s' % attr)
if hasattr(ref, 'name'):
self.assertQueryStringIs({'name': ref['name']})
else:
self.assertQueryStringIs({})
@httpretty.activate
def test_update(self, ref=None):
ref = ref or self.new_ref()
self.stub_entity(httpretty.PATCH, id=ref['id'], entity=ref)
req_ref = ref.copy()
req_ref.pop('id')
returned = self.manager.update(ref['id'], **parameterize(req_ref))
self.assertTrue(isinstance(returned, self.model))
for attr in ref:
self.assertEqual(
getattr(returned, attr),
ref[attr],
'Expected different %s' % attr)
self.assertEntityRequestBodyIs(req_ref)
@httpretty.activate
def test_delete(self, ref=None):
ref = ref or self.new_ref()
self.stub_entity(httpretty.DELETE, id=ref['id'], status=204)
self.manager.delete(ref['id'])