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:
0
__init__.py
Normal file
0
__init__.py
Normal file
66
apiclient/test_exceptions.py
Normal file
66
apiclient/test_exceptions.py
Normal 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
298
client_fixtures.py
Normal 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
120
fakes.py
Normal 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"
|
||||
}
|
||||
}
|
||||
})
|
||||
1220
test_auth_token_middleware.py
Normal file
1220
test_auth_token_middleware.py
Normal file
File diff suppressed because it is too large
Load Diff
163
test_base.py
Normal file
163
test_base.py
Normal 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
257
test_ec2utils.py
Normal 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
209
test_http.py
Normal 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
110
test_https.py
Normal 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
188
test_keyring.py
Normal 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
88
test_memcache_crypt.py
Normal 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
497
test_shell.py
Normal 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
93
test_utils.py
Normal 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
117
utils.py
Normal 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
0
v2_0/__init__.py
Normal file
180
v2_0/client_fixtures.py
Normal file
180
v2_0/client_fixtures.py
Normal 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
494
v2_0/fakes.py
Normal 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
125
v2_0/test_access.py
Normal 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
190
v2_0/test_auth.py
Normal 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
106
v2_0/test_client.py
Normal 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
85
v2_0/test_discovery.py
Normal 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
115
v2_0/test_ec2.py
Normal 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
88
v2_0/test_endpoints.py
Normal 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
124
v2_0/test_roles.py
Normal 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')
|
||||
79
v2_0/test_service_catalog.py
Normal file
79
v2_0/test_service_catalog.py
Normal 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
98
v2_0/test_services.py
Normal 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
347
v2_0/test_shell.py
Normal 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
287
v2_0/test_tenants.py
Normal 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
24
v2_0/test_tokens.py
Normal 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
191
v2_0/test_users.py
Normal 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
90
v2_0/utils.py
Normal 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
0
v3/__init__.py
Normal file
290
v3/client_fixtures.py
Normal file
290
v3/client_fixtures.py
Normal 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
142
v3/test_access.py
Normal 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
303
v3/test_auth.py
Normal 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
136
v3/test_client.py
Normal 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
35
v3/test_credentials.py
Normal 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
87
v3/test_discover.py
Normal 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
33
v3/test_domains.py
Normal 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
91
v3/test_endpoints.py
Normal 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
65
v3/test_groups.py
Normal 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
33
v3/test_policies.py
Normal 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
64
v3/test_projects.py
Normal 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
352
v3/test_roles.py
Normal 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)
|
||||
84
v3/test_service_catalog.py
Normal file
84
v3/test_service_catalog.py
Normal 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
34
v3/test_services.py
Normal 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
102
v3/test_trusts.py
Normal 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
201
v3/test_users.py
Normal 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
286
v3/utils.py
Normal 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'])
|
||||
Reference in New Issue
Block a user