f01c2c4f05
Use the configuration fixture instead of the old opt_in_group and opt methods. Where possible the config_fixture usage done from within setUp has been moved to config_overrides() method. Notably a number of the Policy.json tests cannot be moved into config_overrides due to the need to control how and when policy data is loaded. Change-Id: I1662627cca30890cc3f3da83772132cec7bd58e0
381 lines
15 KiB
Python
381 lines
15 KiB
Python
# Copyright 2013 OpenStack Foundation
|
|
#
|
|
# 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 hashlib
|
|
import json
|
|
import uuid
|
|
|
|
from keystoneclient.contrib.ec2 import utils as ec2_utils
|
|
|
|
from keystone import config
|
|
from keystone import exception
|
|
from keystone.tests import test_v3
|
|
|
|
|
|
CONF = config.CONF
|
|
|
|
|
|
class CredentialBaseTestCase(test_v3.RestfulTestCase):
|
|
def _create_dict_blob_credential(self):
|
|
blob = {"access": uuid.uuid4().hex,
|
|
"secret": uuid.uuid4().hex}
|
|
credential_id = hashlib.sha256(blob['access']).hexdigest()
|
|
credential = self.new_credential_ref(
|
|
user_id=self.user['id'],
|
|
project_id=self.project_id)
|
|
credential['id'] = credential_id
|
|
|
|
# Store the blob as a dict *not* JSON ref bug #1259584
|
|
# This means we can test the dict->json workaround, added
|
|
# as part of the bugfix for backwards compatibility works.
|
|
credential['blob'] = blob
|
|
credential['type'] = 'ec2'
|
|
# Create direct via the DB API to avoid validation failure
|
|
self.credential_api.create_credential(
|
|
credential_id,
|
|
credential)
|
|
expected_blob = json.dumps(blob)
|
|
return expected_blob, credential_id
|
|
|
|
|
|
class CredentialTestCase(CredentialBaseTestCase):
|
|
"""Test credential CRUD."""
|
|
def setUp(self):
|
|
|
|
super(CredentialTestCase, self).setUp()
|
|
|
|
self.credential_id = uuid.uuid4().hex
|
|
self.credential = self.new_credential_ref(
|
|
user_id=self.user['id'],
|
|
project_id=self.project_id)
|
|
self.credential['id'] = self.credential_id
|
|
self.credential_api.create_credential(
|
|
self.credential_id,
|
|
self.credential)
|
|
|
|
def test_credential_api_delete_credentials_for_project(self):
|
|
self.credential_api.delete_credentials_for_project(self.project_id)
|
|
# Test that the credential that we created in .setUp no longer exists
|
|
# once we delete all credentials for self.project_id
|
|
self.assertRaises(exception.CredentialNotFound,
|
|
self.credential_api.get_credential,
|
|
credential_id=self.credential_id)
|
|
|
|
def test_credential_api_delete_credentials_for_user(self):
|
|
self.credential_api.delete_credentials_for_user(self.user_id)
|
|
# Test that the credential that we created in .setUp no longer exists
|
|
# once we delete all credentials for self.user_id
|
|
self.assertRaises(exception.CredentialNotFound,
|
|
self.credential_api.get_credential,
|
|
credential_id=self.credential_id)
|
|
|
|
def test_list_credentials(self):
|
|
"""Call ``GET /credentials``."""
|
|
r = self.get('/credentials')
|
|
self.assertValidCredentialListResponse(r, ref=self.credential)
|
|
|
|
def test_list_credentials_xml(self):
|
|
"""Call ``GET /credentials`` (xml data)."""
|
|
r = self.get('/credentials', content_type='xml')
|
|
self.assertValidCredentialListResponse(r, ref=self.credential)
|
|
|
|
def test_create_credential(self):
|
|
"""Call ``POST /credentials``."""
|
|
ref = self.new_credential_ref(user_id=self.user['id'])
|
|
r = self.post(
|
|
'/credentials',
|
|
body={'credential': ref})
|
|
self.assertValidCredentialResponse(r, ref)
|
|
|
|
def test_get_credential(self):
|
|
"""Call ``GET /credentials/{credential_id}``."""
|
|
r = self.get(
|
|
'/credentials/%(credential_id)s' % {
|
|
'credential_id': self.credential_id})
|
|
self.assertValidCredentialResponse(r, self.credential)
|
|
|
|
def test_update_credential(self):
|
|
"""Call ``PATCH /credentials/{credential_id}``."""
|
|
ref = self.new_credential_ref(
|
|
user_id=self.user['id'],
|
|
project_id=self.project_id)
|
|
del ref['id']
|
|
r = self.patch(
|
|
'/credentials/%(credential_id)s' % {
|
|
'credential_id': self.credential_id},
|
|
body={'credential': ref})
|
|
self.assertValidCredentialResponse(r, ref)
|
|
|
|
def test_delete_credential(self):
|
|
"""Call ``DELETE /credentials/{credential_id}``."""
|
|
self.delete(
|
|
'/credentials/%(credential_id)s' % {
|
|
'credential_id': self.credential_id})
|
|
|
|
def test_create_ec2_credential(self):
|
|
"""Call ``POST /credentials`` for creating ec2 credential."""
|
|
ref = self.new_credential_ref(user_id=self.user['id'])
|
|
blob = {"access": uuid.uuid4().hex,
|
|
"secret": uuid.uuid4().hex}
|
|
ref['blob'] = json.dumps(blob)
|
|
ref['type'] = 'ec2'
|
|
r = self.post(
|
|
'/credentials',
|
|
body={'credential': ref})
|
|
self.assertValidCredentialResponse(r, ref)
|
|
# Assert credential id is same as hash of access key id for
|
|
# ec2 credentials
|
|
self.assertEqual(r.result['credential']['id'],
|
|
hashlib.sha256(blob['access']).hexdigest())
|
|
# Create second ec2 credential with the same access key id and check
|
|
# for conflict.
|
|
self.post(
|
|
'/credentials',
|
|
body={'credential': ref}, expected_status=409)
|
|
|
|
def test_get_ec2_dict_blob(self):
|
|
"""Ensure non-JSON blob data is correctly converted."""
|
|
expected_blob, credential_id = self._create_dict_blob_credential()
|
|
|
|
r = self.get(
|
|
'/credentials/%(credential_id)s' % {
|
|
'credential_id': credential_id})
|
|
self.assertEqual(expected_blob, r.result['credential']['blob'])
|
|
|
|
def test_list_ec2_dict_blob(self):
|
|
"""Ensure non-JSON blob data is correctly converted."""
|
|
expected_blob, credential_id = self._create_dict_blob_credential()
|
|
|
|
list_r = self.get('/credentials')
|
|
list_creds = list_r.result['credentials']
|
|
list_ids = [r['id'] for r in list_creds]
|
|
self.assertIn(credential_id, list_ids)
|
|
for r in list_creds:
|
|
if r['id'] == credential_id:
|
|
self.assertEqual(expected_blob, r['blob'])
|
|
|
|
def test_create_non_ec2_credential(self):
|
|
"""Call ``POST /credentials`` for creating non-ec2 credential."""
|
|
ref = self.new_credential_ref(user_id=self.user['id'])
|
|
blob = {"access": uuid.uuid4().hex,
|
|
"secret": uuid.uuid4().hex}
|
|
ref['blob'] = json.dumps(blob)
|
|
r = self.post(
|
|
'/credentials',
|
|
body={'credential': ref})
|
|
self.assertValidCredentialResponse(r, ref)
|
|
# Assert credential id is not same as hash of access key id for
|
|
# non-ec2 credentials
|
|
self.assertNotEqual(r.result['credential']['id'],
|
|
hashlib.sha256(blob['access']).hexdigest())
|
|
|
|
def test_create_ec2_credential_with_invalid_blob(self):
|
|
"""Call ``POST /credentials`` for creating ec2
|
|
credential with invalid blob.
|
|
"""
|
|
ref = self.new_credential_ref(user_id=self.user['id'])
|
|
ref['blob'] = '{"abc":"def"d}'
|
|
ref['type'] = 'ec2'
|
|
# Assert 400 status for bad request containing invalid
|
|
# blob
|
|
response = self.post(
|
|
'/credentials',
|
|
body={'credential': ref}, expected_status=400)
|
|
self.assertValidErrorResponse(response)
|
|
|
|
def test_create_credential_with_admin_token(self):
|
|
# Make sure we can create credential with the static admin token
|
|
ref = self.new_credential_ref(user_id=self.user['id'])
|
|
r = self.post(
|
|
'/credentials',
|
|
body={'credential': ref},
|
|
token=CONF.admin_token)
|
|
self.assertValidCredentialResponse(r, ref)
|
|
|
|
|
|
class TestCredentialTrustScoped(test_v3.RestfulTestCase):
|
|
"""Test credential with trust scoped token."""
|
|
def setUp(self):
|
|
super(TestCredentialTrustScoped, self).setUp()
|
|
|
|
self.trustee_user_id = uuid.uuid4().hex
|
|
self.trustee_user = self.new_user_ref(domain_id=self.domain_id)
|
|
self.trustee_user['id'] = self.trustee_user_id
|
|
self.identity_api.create_user(self.trustee_user_id, self.trustee_user)
|
|
|
|
def config_overrides(self):
|
|
super(TestCredentialTrustScoped, self).config_overrides()
|
|
self.config_fixture.config(group='trust', enabled=True)
|
|
|
|
def test_trust_scoped_ec2_credential(self):
|
|
"""Call ``POST /credentials`` for creating ec2 credential."""
|
|
# Create the trust
|
|
ref = self.new_trust_ref(
|
|
trustor_user_id=self.user_id,
|
|
trustee_user_id=self.trustee_user_id,
|
|
project_id=self.project_id,
|
|
impersonation=True,
|
|
expires=dict(minutes=1),
|
|
role_ids=[self.role_id])
|
|
del ref['id']
|
|
r = self.post('/OS-TRUST/trusts', body={'trust': ref})
|
|
trust = self.assertValidTrustResponse(r)
|
|
|
|
# Get a trust scoped token
|
|
auth_data = self.build_authentication_request(
|
|
user_id=self.trustee_user['id'],
|
|
password=self.trustee_user['password'],
|
|
trust_id=trust['id'])
|
|
r = self.post('/auth/tokens', body=auth_data)
|
|
self.assertValidProjectTrustScopedTokenResponse(r, self.user)
|
|
trust_id = r.result['token']['OS-TRUST:trust']['id']
|
|
token_id = r.headers.get('X-Subject-Token')
|
|
|
|
# Create the credential with the trust scoped token
|
|
ref = self.new_credential_ref(user_id=self.user['id'])
|
|
blob = {"access": uuid.uuid4().hex,
|
|
"secret": uuid.uuid4().hex}
|
|
ref['blob'] = json.dumps(blob)
|
|
ref['type'] = 'ec2'
|
|
r = self.post(
|
|
'/credentials',
|
|
body={'credential': ref},
|
|
token=token_id)
|
|
|
|
# We expect the response blob to contain the trust_id
|
|
ret_ref = ref.copy()
|
|
ret_blob = blob.copy()
|
|
ret_blob['trust_id'] = trust_id
|
|
ret_ref['blob'] = json.dumps(ret_blob)
|
|
self.assertValidCredentialResponse(r, ref=ret_ref)
|
|
|
|
# Assert credential id is same as hash of access key id for
|
|
# ec2 credentials
|
|
self.assertEqual(r.result['credential']['id'],
|
|
hashlib.sha256(blob['access']).hexdigest())
|
|
|
|
# Create second ec2 credential with the same access key id and check
|
|
# for conflict.
|
|
self.post(
|
|
'/credentials',
|
|
body={'credential': ref},
|
|
token=token_id,
|
|
expected_status=409)
|
|
|
|
|
|
class TestCredentialEc2(CredentialBaseTestCase):
|
|
"""Test v3 credential compatibility with ec2tokens."""
|
|
def setUp(self):
|
|
super(TestCredentialEc2, self).setUp()
|
|
|
|
def _validate_signature(self, access, secret):
|
|
"""Test signature validation with the access/secret provided."""
|
|
signer = ec2_utils.Ec2Signer(secret)
|
|
params = {'SignatureMethod': 'HmacSHA256',
|
|
'SignatureVersion': '2',
|
|
'AWSAccessKeyId': access}
|
|
request = {'host': 'foo',
|
|
'verb': 'GET',
|
|
'path': '/bar',
|
|
'params': params}
|
|
signature = signer.generate(request)
|
|
|
|
# Now make a request to validate the signed dummy request via the
|
|
# ec2tokens API. This proves the v3 ec2 credentials actually work.
|
|
sig_ref = {'access': access,
|
|
'signature': signature,
|
|
'host': 'foo',
|
|
'verb': 'GET',
|
|
'path': '/bar',
|
|
'params': params}
|
|
r = self.post(
|
|
'/ec2tokens',
|
|
body={'ec2Credentials': sig_ref},
|
|
expected_status=200)
|
|
self.assertValidTokenResponse(r)
|
|
|
|
def test_ec2_credential_signature_validate(self):
|
|
"""Test signature validation with a v3 ec2 credential."""
|
|
ref = self.new_credential_ref(
|
|
user_id=self.user['id'],
|
|
project_id=self.project_id)
|
|
blob = {"access": uuid.uuid4().hex,
|
|
"secret": uuid.uuid4().hex}
|
|
ref['blob'] = json.dumps(blob)
|
|
ref['type'] = 'ec2'
|
|
r = self.post(
|
|
'/credentials',
|
|
body={'credential': ref})
|
|
self.assertValidCredentialResponse(r, ref)
|
|
# Assert credential id is same as hash of access key id
|
|
self.assertEqual(r.result['credential']['id'],
|
|
hashlib.sha256(blob['access']).hexdigest())
|
|
|
|
cred_blob = json.loads(r.result['credential']['blob'])
|
|
self.assertEqual(blob, cred_blob)
|
|
self._validate_signature(access=cred_blob['access'],
|
|
secret=cred_blob['secret'])
|
|
|
|
def test_ec2_credential_signature_validate_legacy(self):
|
|
"""Test signature validation with a legacy v3 ec2 credential."""
|
|
cred_json, credential_id = self._create_dict_blob_credential()
|
|
cred_blob = json.loads(cred_json)
|
|
self._validate_signature(access=cred_blob['access'],
|
|
secret=cred_blob['secret'])
|
|
|
|
def _get_ec2_cred_uri(self):
|
|
return '/users/%s/credentials/OS-EC2' % self.user_id
|
|
|
|
def _get_ec2_cred(self):
|
|
uri = self._get_ec2_cred_uri()
|
|
r = self.post(uri, body={'tenant_id': self.project_id})
|
|
return r.result['credential']
|
|
|
|
def test_ec2_create_credential(self):
|
|
"""Test ec2 credential creation."""
|
|
ec2_cred = self._get_ec2_cred()
|
|
self.assertEqual(self.user_id, ec2_cred['user_id'])
|
|
self.assertEqual(self.project_id, ec2_cred['tenant_id'])
|
|
self.assertIsNone(ec2_cred['trust_id'])
|
|
self._validate_signature(access=ec2_cred['access'],
|
|
secret=ec2_cred['secret'])
|
|
|
|
return ec2_cred
|
|
|
|
def test_ec2_get_credential(self):
|
|
ec2_cred = self._get_ec2_cred()
|
|
uri = '/'.join([self._get_ec2_cred_uri(), ec2_cred['access']])
|
|
r = self.get(uri)
|
|
self.assertDictEqual(ec2_cred, r.result['credential'])
|
|
|
|
def test_ec2_list_credentials(self):
|
|
"""Test ec2 credential listing."""
|
|
self._get_ec2_cred_uri()
|
|
uri = self._get_ec2_cred_uri()
|
|
r = self.get(uri)
|
|
cred_list = r.result
|
|
self.assertEqual(1, len(cred_list))
|
|
|
|
def test_ec2_delete_credential(self):
|
|
"""Test ec2 credential deletion."""
|
|
ec2_cred = self._get_ec2_cred()
|
|
uri = '/'.join([self._get_ec2_cred_uri(), ec2_cred['access']])
|
|
cred_from_credential_api = self.credential_api.list_credentials(
|
|
user_id=self.user_id)
|
|
self.assertEqual(1, len(cred_from_credential_api))
|
|
self.delete(uri)
|
|
self.assertRaises(exception.CredentialNotFound,
|
|
self.credential_api.get_credential,
|
|
cred_from_credential_api[0]['id'])
|