Remove v2.0 auth APIs
This was originally staged to be removed in the T release. Discussions from the Queens PTG resulted in the ability to remove it sooner since everything else in v2.0 is gone except the ec2 APIs. This patch just removes the v2.0 authentication API and the tests that failed as a result. A subsequent patch will go through and start removing all the plumbing, fixtures, and testing infrastructure that is no longer needed. bp removed-as-of-queens Change-Id: I4c3e35f3565b4b60ae4d00cc2490bd04aba1a800
This commit is contained in:
parent
139aa015d2
commit
8e85cb1a4d
File diff suppressed because it is too large
Load Diff
|
@ -27,26 +27,14 @@ from keystone.tests import unit
|
|||
from keystone.tests.unit import default_fixtures
|
||||
from keystone.tests.unit import ksfixtures
|
||||
from keystone.tests.unit.ksfixtures import database
|
||||
from keystone.tests.unit import rest
|
||||
from keystone.tests.unit import test_v3 as rest
|
||||
|
||||
CRED_TYPE_EC2 = controllers.CRED_TYPE_EC2
|
||||
|
||||
|
||||
class V2CredentialEc2TestCase(rest.RestfulTestCase):
|
||||
def setUp(self):
|
||||
super(V2CredentialEc2TestCase, self).setUp()
|
||||
self.user_id = self.user_foo['id']
|
||||
self.project_id = self.tenant_bar['id']
|
||||
self.useFixture(
|
||||
ksfixtures.KeyRepository(
|
||||
self.config_fixture,
|
||||
'credential',
|
||||
credential_fernet.MAX_ACTIVE_KEYS
|
||||
)
|
||||
)
|
||||
|
||||
def _get_token_id(self, r):
|
||||
return r.result['access']['token']['id']
|
||||
return r.headers.get('X-Subject-Token')
|
||||
|
||||
def _get_ec2_cred(self):
|
||||
uri = self._get_ec2_cred_uri()
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
import uuid
|
||||
|
||||
import six
|
||||
from six.moves import http_client
|
||||
|
||||
from keystone.common import extension as keystone_extension
|
||||
|
@ -22,7 +21,6 @@ import keystone.conf
|
|||
from keystone.tests import unit
|
||||
from keystone.tests.unit import ksfixtures
|
||||
from keystone.tests.unit import rest
|
||||
from keystone.tests.unit.schema import v2
|
||||
|
||||
CONF = keystone.conf.CONF
|
||||
|
||||
|
@ -116,37 +114,6 @@ class CoreApiTests(object):
|
|||
self.assertValidExtensionResponse(
|
||||
r, keystone_extension.ADMIN_EXTENSIONS)
|
||||
|
||||
def test_authenticate(self):
|
||||
r = self.public_request(
|
||||
method='POST',
|
||||
path='/v2.0/tokens',
|
||||
body={
|
||||
'auth': {
|
||||
'passwordCredentials': {
|
||||
'username': self.user_foo['name'],
|
||||
'password': self.user_foo['password'],
|
||||
},
|
||||
'tenantId': self.tenant_bar['id'],
|
||||
},
|
||||
},
|
||||
expected_status=http_client.OK)
|
||||
self.assertValidAuthenticationResponse(r, require_service_catalog=True)
|
||||
|
||||
def test_authenticate_unscoped(self):
|
||||
r = self.public_request(
|
||||
method='POST',
|
||||
path='/v2.0/tokens',
|
||||
body={
|
||||
'auth': {
|
||||
'passwordCredentials': {
|
||||
'username': self.user_foo['name'],
|
||||
'password': self.user_foo['password'],
|
||||
},
|
||||
},
|
||||
},
|
||||
expected_status=http_client.OK)
|
||||
self.assertValidAuthenticationResponse(r)
|
||||
|
||||
def test_error_response(self):
|
||||
"""Trigger assertValidErrorResponse by convention."""
|
||||
self.public_request(path='/v2.0/tenants',
|
||||
|
@ -197,38 +164,6 @@ class CoreApiTests(object):
|
|||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def test_authenticating_a_user_with_no_password(self):
|
||||
token = self.get_scoped_token()
|
||||
|
||||
username = uuid.uuid4().hex
|
||||
|
||||
# create the user
|
||||
self.admin_request(
|
||||
method='POST',
|
||||
path='/v3/users',
|
||||
body={
|
||||
'user': {
|
||||
'name': username,
|
||||
'enabled': True,
|
||||
},
|
||||
},
|
||||
token=token)
|
||||
|
||||
# fail to authenticate
|
||||
r = self.public_request(
|
||||
method='POST',
|
||||
path='/v2.0/tokens',
|
||||
body={
|
||||
'auth': {
|
||||
'passwordCredentials': {
|
||||
'username': username,
|
||||
'password': 'password',
|
||||
},
|
||||
},
|
||||
},
|
||||
expected_status=http_client.UNAUTHORIZED)
|
||||
self.assertValidErrorResponse(r)
|
||||
|
||||
def test_www_authenticate_header(self):
|
||||
r = self.public_request(
|
||||
path='/v2.0/tenants',
|
||||
|
@ -454,136 +389,3 @@ class V2TestCaseFernet(V2TestCase, RestfulTestCase, CoreApiTests):
|
|||
CONF.fernet_tokens.max_active_keys
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class TestFernetTokenProviderV2(RestfulTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestFernetTokenProviderV2, self).setUp()
|
||||
# Add catalog data
|
||||
self.region = unit.new_region_ref()
|
||||
self.region_id = self.region['id']
|
||||
self.catalog_api.create_region(self.region)
|
||||
|
||||
self.service = unit.new_service_ref()
|
||||
self.service_id = self.service['id']
|
||||
self.catalog_api.create_service(self.service_id, self.service)
|
||||
|
||||
self.endpoint = unit.new_endpoint_ref(service_id=self.service_id,
|
||||
interface='public',
|
||||
region_id=self.region_id)
|
||||
self.endpoint_id = self.endpoint['id']
|
||||
self.catalog_api.create_endpoint(self.endpoint_id, self.endpoint)
|
||||
|
||||
def assertValidUnscopedTokenResponse(self, r):
|
||||
v2.unscoped_validator.validate(r.json['access'])
|
||||
|
||||
def assertValidScopedTokenResponse(self, r):
|
||||
v2.scoped_validator.validate(r.json['access'])
|
||||
|
||||
# Used by RestfulTestCase
|
||||
def _get_token_id(self, r):
|
||||
return r.result['access']['token']['id']
|
||||
|
||||
def new_project_ref(self):
|
||||
return {'id': uuid.uuid4().hex,
|
||||
'name': uuid.uuid4().hex,
|
||||
'description': uuid.uuid4().hex,
|
||||
'domain_id': 'default',
|
||||
'enabled': True}
|
||||
|
||||
def config_overrides(self):
|
||||
super(TestFernetTokenProviderV2, self).config_overrides()
|
||||
self.config_fixture.config(group='token', provider='fernet')
|
||||
self.useFixture(
|
||||
ksfixtures.KeyRepository(
|
||||
self.config_fixture,
|
||||
'fernet_tokens',
|
||||
CONF.fernet_tokens.max_active_keys
|
||||
)
|
||||
)
|
||||
|
||||
def test_authenticate_unscoped_token(self):
|
||||
unscoped_token = self.get_unscoped_token()
|
||||
# Fernet token must be of length 255 per usability requirements
|
||||
self.assertLess(len(unscoped_token), 255)
|
||||
|
||||
def test_authenticate_scoped_token(self):
|
||||
project_ref = self.new_project_ref()
|
||||
self.resource_api.create_project(project_ref['id'], project_ref)
|
||||
self.assignment_api.add_role_to_user_and_project(
|
||||
self.user_foo['id'], project_ref['id'], self.role_service['id'])
|
||||
token = self.get_scoped_token(tenant_id=project_ref['id'])
|
||||
# Fernet token must be of length 255 per usability requirements
|
||||
self.assertLess(len(token), 255)
|
||||
|
||||
def test_token_authentication_and_validation(self):
|
||||
"""Test token authentication for Fernet token provider.
|
||||
|
||||
Verify that token authentication returns validate response code and
|
||||
valid token belongs to project.
|
||||
"""
|
||||
project_ref = self.new_project_ref()
|
||||
self.resource_api.create_project(project_ref['id'], project_ref)
|
||||
unscoped_token = self.get_unscoped_token()
|
||||
self.assignment_api.add_role_to_user_and_project(self.user_foo['id'],
|
||||
project_ref['id'],
|
||||
self.role_admin['id'])
|
||||
token_id = unscoped_token
|
||||
if six.PY2:
|
||||
token_id = token_id.encode('ascii')
|
||||
resp = self.public_request(
|
||||
method='POST',
|
||||
path='/v2.0/tokens',
|
||||
body={
|
||||
'auth': {
|
||||
'tenantName': project_ref['name'],
|
||||
'token': {
|
||||
'id': token_id,
|
||||
}
|
||||
}
|
||||
},
|
||||
expected_status=http_client.OK)
|
||||
self.assertValidScopedTokenResponse(resp)
|
||||
|
||||
def test_rescoped_tokens_maintain_original_expiration(self):
|
||||
project_ref = self.new_project_ref()
|
||||
self.resource_api.create_project(project_ref['id'], project_ref)
|
||||
self.assignment_api.add_role_to_user_and_project(self.user_foo['id'],
|
||||
project_ref['id'],
|
||||
self.role_admin['id'])
|
||||
resp = self.public_request(
|
||||
method='POST',
|
||||
path='/v2.0/tokens',
|
||||
body={
|
||||
'auth': {
|
||||
'tenantName': project_ref['name'],
|
||||
'passwordCredentials': {
|
||||
'username': self.user_foo['name'],
|
||||
'password': self.user_foo['password']
|
||||
}
|
||||
}
|
||||
},
|
||||
# NOTE(lbragstad): This test may need to be refactored if Keystone
|
||||
# decides to disallow rescoping using a scoped token.
|
||||
expected_status=http_client.OK)
|
||||
original_token = resp.result['access']['token']['id']
|
||||
original_expiration = resp.result['access']['token']['expires']
|
||||
|
||||
resp = self.public_request(
|
||||
method='POST',
|
||||
path='/v2.0/tokens',
|
||||
body={
|
||||
'auth': {
|
||||
'tenantName': project_ref['name'],
|
||||
'token': {
|
||||
'id': original_token,
|
||||
}
|
||||
}
|
||||
},
|
||||
expected_status=http_client.OK)
|
||||
rescoped_token = resp.result['access']['token']['id']
|
||||
rescoped_expiration = resp.result['access']['token']['expires']
|
||||
self.assertNotEqual(original_token, rescoped_token)
|
||||
self.assertEqual(original_expiration, rescoped_expiration)
|
||||
self.assertValidScopedTokenResponse(resp)
|
||||
|
|
|
@ -388,14 +388,6 @@ class TokenAPITests(object):
|
|||
expected_status=expected_status
|
||||
)
|
||||
|
||||
def _validate_token_v2(self, token, expected_status=http_client.OK):
|
||||
return self.admin_request(
|
||||
path='/v2.0/tokens/%s' % token,
|
||||
headers={'X-Auth-Token': self.get_scoped_token()},
|
||||
method='GET',
|
||||
expected_status=expected_status
|
||||
)
|
||||
|
||||
def _revoke_token(self, token, expected_status=http_client.NO_CONTENT):
|
||||
return self.delete(
|
||||
'/auth/tokens',
|
||||
|
@ -1445,110 +1437,6 @@ class TokenAPITests(object):
|
|||
def test_default_fixture_scope_token(self):
|
||||
self.assertIsNotNone(self.get_scoped_token())
|
||||
|
||||
def test_v2_v3_unscoped_token_intermix(self):
|
||||
r = self.admin_request(
|
||||
method='POST',
|
||||
path='/v2.0/tokens',
|
||||
body={
|
||||
'auth': {
|
||||
'passwordCredentials': {
|
||||
'userId': self.default_domain_user['id'],
|
||||
'password': self.default_domain_user['password']
|
||||
}
|
||||
}
|
||||
})
|
||||
v2_token_data = r.result
|
||||
v2_token = v2_token_data['access']['token']['id']
|
||||
|
||||
r = self.get('/auth/tokens', headers={'X-Subject-Token': v2_token})
|
||||
self.assertValidUnscopedTokenResponse(r)
|
||||
v3_token_data = r.result
|
||||
|
||||
self.assertEqual(v2_token_data['access']['user']['id'],
|
||||
v3_token_data['token']['user']['id'])
|
||||
self.assertTimestampEqual(v2_token_data['access']['token']['expires'],
|
||||
v3_token_data['token']['expires_at'])
|
||||
|
||||
def test_v2_v3_token_intermix(self):
|
||||
r = self.admin_request(
|
||||
path='/v2.0/tokens',
|
||||
method='POST',
|
||||
body={
|
||||
'auth': {
|
||||
'passwordCredentials': {
|
||||
'userId': self.default_domain_user['id'],
|
||||
'password': self.default_domain_user['password']
|
||||
},
|
||||
'tenantId': self.default_domain_project['id']
|
||||
}
|
||||
})
|
||||
v2_token_data = r.result
|
||||
v2_token = v2_token_data['access']['token']['id']
|
||||
|
||||
r = self.get('/auth/tokens', headers={'X-Subject-Token': v2_token})
|
||||
self.assertValidProjectScopedTokenResponse(r)
|
||||
v3_token_data = r.result
|
||||
|
||||
self.assertEqual(v2_token_data['access']['user']['id'],
|
||||
v3_token_data['token']['user']['id'])
|
||||
self.assertTimestampEqual(v2_token_data['access']['token']['expires'],
|
||||
v3_token_data['token']['expires_at'])
|
||||
self.assertEqual(v2_token_data['access']['user']['roles'][0]['name'],
|
||||
v3_token_data['token']['roles'][0]['name'])
|
||||
|
||||
v2_issued_at = timeutils.parse_isotime(
|
||||
v2_token_data['access']['token']['issued_at'])
|
||||
v3_issued_at = timeutils.parse_isotime(
|
||||
v3_token_data['token']['issued_at'])
|
||||
|
||||
self.assertEqual(v2_issued_at, v3_issued_at)
|
||||
|
||||
def test_create_v3_project_token_from_v2_project_token(self):
|
||||
r = self.admin_request(
|
||||
path='/v2.0/tokens',
|
||||
method='POST',
|
||||
body={
|
||||
'auth': {
|
||||
'passwordCredentials': {
|
||||
'userId': self.default_domain_user['id'],
|
||||
'password': self.default_domain_user['password']
|
||||
},
|
||||
'tenantId': self.default_domain_project['id']
|
||||
}
|
||||
})
|
||||
v2_token_data = r.result
|
||||
v2_token = v2_token_data['access']['token']['id']
|
||||
|
||||
auth_data = self.build_authentication_request(
|
||||
token=v2_token,
|
||||
project_id=self.default_domain_project['id'])
|
||||
r = self.v3_create_token(auth_data)
|
||||
self.assertValidScopedTokenResponse(r)
|
||||
|
||||
def test_v2_token_deleted_on_v3(self):
|
||||
# Create a v2 token.
|
||||
body = {
|
||||
'auth': {
|
||||
'passwordCredentials': {
|
||||
'userId': self.default_domain_user['id'],
|
||||
'password': self.default_domain_user['password']
|
||||
},
|
||||
'tenantId': self.default_domain_project['id']
|
||||
}
|
||||
}
|
||||
r = self.admin_request(
|
||||
path='/v2.0/tokens', method='POST', body=body)
|
||||
v2_token = r.result['access']['token']['id']
|
||||
|
||||
# Delete the v2 token using v3.
|
||||
self.delete(
|
||||
'/auth/tokens', headers={'X-Subject-Token': v2_token})
|
||||
|
||||
# Attempting to use the deleted token on v2 should fail.
|
||||
self._validate_token_v2(
|
||||
v2_token, expected_status=http_client.NOT_FOUND
|
||||
)
|
||||
|
||||
def test_rescoping_token(self):
|
||||
expires = self.v3_token_data['token']['expires_at']
|
||||
|
||||
|
@ -2239,38 +2127,6 @@ class TokenAPITests(object):
|
|||
# the bind information should be carried over from the original token
|
||||
self.assertEqual(remote_user, token['bind']['kerberos'])
|
||||
|
||||
def test_v2_v3_bind_token_intermix(self):
|
||||
self.config_fixture.config(group='token', bind='kerberos')
|
||||
|
||||
# we need our own user registered to the default domain because of
|
||||
# the way external auth works.
|
||||
remote_user = self.default_domain_user['name']
|
||||
self.admin_app.extra_environ.update({'REMOTE_USER': remote_user,
|
||||
'AUTH_TYPE': 'Negotiate'})
|
||||
body = {'auth': {}}
|
||||
resp = self.admin_request(path='/v2.0/tokens',
|
||||
method='POST',
|
||||
body=body)
|
||||
|
||||
v2_token_data = resp.result
|
||||
|
||||
bind = v2_token_data['access']['token']['bind']
|
||||
self.assertEqual(self.default_domain_user['name'], bind['kerberos'])
|
||||
|
||||
v2_token_id = v2_token_data['access']['token']['id']
|
||||
# NOTE(gyee): self.get() will try to obtain an auth token if one
|
||||
# is not provided. When REMOTE_USER is present in the request
|
||||
# environment, the external user auth plugin is used in conjunction
|
||||
# with the password auth for the admin user. Therefore, we need to
|
||||
# cleanup the REMOTE_USER information from the previous call.
|
||||
del self.admin_app.extra_environ['REMOTE_USER']
|
||||
headers = {'X-Subject-Token': v2_token_id}
|
||||
resp = self.get('/auth/tokens', headers=headers)
|
||||
token_data = resp.result
|
||||
|
||||
self.assertDictEqual(v2_token_data['access']['token']['bind'],
|
||||
token_data['token']['bind'])
|
||||
|
||||
def test_fetch_expired_allow_expired(self):
|
||||
self.config_fixture.config(group='token',
|
||||
expiration=10,
|
||||
|
@ -2405,48 +2261,6 @@ class AllowRescopeScopedTokenDisabledTests(test_v3.RestfulTestCase):
|
|||
project_id=self.project_id),
|
||||
expected_status=http_client.FORBIDDEN)
|
||||
|
||||
def _v2_token(self):
|
||||
body = {
|
||||
'auth': {
|
||||
"tenantId": self.default_domain_project['id'],
|
||||
'passwordCredentials': {
|
||||
'userId': self.default_domain_user['id'],
|
||||
'password': self.default_domain_user['password']
|
||||
}
|
||||
}}
|
||||
resp = self.admin_request(path='/v2.0/tokens',
|
||||
method='POST',
|
||||
body=body)
|
||||
v2_token_data = resp.result
|
||||
return v2_token_data
|
||||
|
||||
def _v2_token_from_token(self, token):
|
||||
body = {
|
||||
'auth': {
|
||||
"tenantId": self.project['id'],
|
||||
"token": token
|
||||
}}
|
||||
self.admin_request(path='/v2.0/tokens',
|
||||
method='POST',
|
||||
body=body,
|
||||
expected_status=http_client.FORBIDDEN)
|
||||
|
||||
def test_rescoping_v2_to_v3_disabled(self):
|
||||
token = self._v2_token()
|
||||
self.v3_create_token(
|
||||
self.build_authentication_request(
|
||||
token=token['access']['token']['id'],
|
||||
project_id=self.project_id),
|
||||
expected_status=http_client.FORBIDDEN)
|
||||
|
||||
def test_rescoping_v3_to_v2_disabled(self):
|
||||
token = {'id': self.get_scoped_token()}
|
||||
self._v2_token_from_token(token)
|
||||
|
||||
def test_rescoping_v2_to_v2_disabled(self):
|
||||
token = self._v2_token()
|
||||
self._v2_token_from_token(token['access']['token'])
|
||||
|
||||
def test_rescoped_domain_token_disabled(self):
|
||||
|
||||
self.domainA = unit.new_domain_ref()
|
||||
|
@ -2546,21 +2360,6 @@ class TestFernetTokenAPIs(test_v3.RestfulTestCase, TokenAPITests,
|
|||
self.v3_create_token(auth_data,
|
||||
expected_status=http_client.NOT_IMPLEMENTED)
|
||||
|
||||
def test_v2_v3_bind_token_intermix(self):
|
||||
self.config_fixture.config(group='token', bind='kerberos')
|
||||
|
||||
# we need our own user registered to the default domain because of
|
||||
# the way external auth works.
|
||||
remote_user = self.default_domain_user['name']
|
||||
self.admin_app.extra_environ.update({'REMOTE_USER': remote_user,
|
||||
'AUTH_TYPE': 'Negotiate'})
|
||||
body = {'auth': {}}
|
||||
# Bind not current supported by Fernet, see bug 1433311.
|
||||
self.admin_request(path='/v2.0/tokens',
|
||||
method='POST',
|
||||
body=body,
|
||||
expected_status=http_client.NOT_IMPLEMENTED)
|
||||
|
||||
def test_auth_with_bind_token(self):
|
||||
self.config_fixture.config(group='token', bind=['kerberos'])
|
||||
|
||||
|
@ -3323,37 +3122,6 @@ class TestTokenRevokeById(test_v3.RestfulTestCase):
|
|||
# Make sure that we get a 404 Not Found when heading that role.
|
||||
self.head(role_path, expected_status=http_client.NOT_FOUND)
|
||||
|
||||
def get_v2_token(self, token=None, project_id=None):
|
||||
body = {'auth': {}, }
|
||||
|
||||
if token:
|
||||
body['auth']['token'] = {
|
||||
'id': token
|
||||
}
|
||||
else:
|
||||
body['auth']['passwordCredentials'] = {
|
||||
'username': self.default_domain_user['name'],
|
||||
'password': self.default_domain_user['password'],
|
||||
}
|
||||
|
||||
if project_id:
|
||||
body['auth']['tenantId'] = project_id
|
||||
|
||||
r = self.admin_request(method='POST', path='/v2.0/tokens', body=body)
|
||||
return r.json_body['access']['token']['id']
|
||||
|
||||
def test_revoke_v2_token_no_check(self):
|
||||
# Test that a V2 token can be revoked without validating it first.
|
||||
|
||||
token = self.get_v2_token()
|
||||
|
||||
self.delete('/auth/tokens',
|
||||
headers={'X-Subject-Token': token})
|
||||
|
||||
self.head('/auth/tokens',
|
||||
headers={'X-Subject-Token': token},
|
||||
expected_status=http_client.NOT_FOUND)
|
||||
|
||||
def test_revoke_token_from_token(self):
|
||||
# Test that a scoped token can be requested from an unscoped token,
|
||||
# the scoped token can be revoked, and the unscoped token remains
|
||||
|
@ -3409,31 +3177,6 @@ class TestTokenRevokeById(test_v3.RestfulTestCase):
|
|||
headers={'X-Subject-Token': unscoped_token},
|
||||
expected_status=http_client.OK)
|
||||
|
||||
def test_revoke_token_from_token_v2(self):
|
||||
# Test that a scoped token can be requested from an unscoped token,
|
||||
# the scoped token can be revoked, and the unscoped token remains
|
||||
# valid.
|
||||
|
||||
unscoped_token = self.get_v2_token()
|
||||
|
||||
# Get a project-scoped token from the unscoped token
|
||||
project_scoped_token = self.get_v2_token(
|
||||
token=unscoped_token, project_id=self.default_domain_project['id'])
|
||||
|
||||
# revoke the project-scoped token.
|
||||
self.delete('/auth/tokens',
|
||||
headers={'X-Subject-Token': project_scoped_token})
|
||||
|
||||
# The project-scoped token is invalidated.
|
||||
self.head('/auth/tokens',
|
||||
headers={'X-Subject-Token': project_scoped_token},
|
||||
expected_status=http_client.NOT_FOUND)
|
||||
|
||||
# The unscoped token should still be valid.
|
||||
self.head('/auth/tokens',
|
||||
headers={'X-Subject-Token': unscoped_token},
|
||||
expected_status=http_client.OK)
|
||||
|
||||
|
||||
class TestTokenRevokeByAssignment(TestTokenRevokeById):
|
||||
|
||||
|
@ -3556,20 +3299,6 @@ class TestTokenRevokeApi(TestTokenRevokeById):
|
|||
self.assertValidRevokedTokenResponse(events_response,
|
||||
audit_id=response['audit_ids'][0])
|
||||
|
||||
def test_revoke_v2_token(self):
|
||||
token = self.get_v2_token()
|
||||
headers = {'X-Subject-Token': token}
|
||||
response = self.get('/auth/tokens',
|
||||
headers=headers).json_body['token']
|
||||
self.delete('/auth/tokens', headers=headers)
|
||||
self.head('/auth/tokens', headers=headers,
|
||||
expected_status=http_client.NOT_FOUND)
|
||||
events_response = self.get('/OS-REVOKE/events').json_body
|
||||
|
||||
self.assertValidRevokedTokenResponse(
|
||||
events_response,
|
||||
audit_id=response['audit_ids'][0])
|
||||
|
||||
def test_get_revoke_by_id_false_returns_gone(self):
|
||||
self.get('/auth/tokens/OS-PKI/revoked',
|
||||
expected_status=http_client.GONE)
|
||||
|
|
|
@ -239,18 +239,6 @@ class ResourceTestCase(test_v3.RestfulTestCase,
|
|||
user2['id'])
|
||||
|
||||
# First check a user in that domain can authenticate..
|
||||
body = {
|
||||
'auth': {
|
||||
'passwordCredentials': {
|
||||
'userId': user2['id'],
|
||||
'password': user2['password']
|
||||
},
|
||||
'tenantId': project2['id']
|
||||
}
|
||||
}
|
||||
self.admin_request(
|
||||
path='/v2.0/tokens', method='POST', body=body)
|
||||
|
||||
auth_data = self.build_authentication_request(
|
||||
user_id=user2['id'],
|
||||
password=user2['password'],
|
||||
|
@ -264,21 +252,6 @@ class ResourceTestCase(test_v3.RestfulTestCase,
|
|||
body={'domain': {'enabled': False}})
|
||||
self.assertValidDomainResponse(r, domain2)
|
||||
|
||||
# Make sure the user can no longer authenticate, via
|
||||
# either API
|
||||
body = {
|
||||
'auth': {
|
||||
'passwordCredentials': {
|
||||
'userId': user2['id'],
|
||||
'password': user2['password']
|
||||
},
|
||||
'tenantId': project2['id']
|
||||
}
|
||||
}
|
||||
self.admin_request(
|
||||
path='/v2.0/tokens', method='POST', body=body,
|
||||
expected_status=http_client.UNAUTHORIZED)
|
||||
|
||||
# Try looking up in v3 by name and id
|
||||
auth_data = self.build_authentication_request(
|
||||
user_id=user2['id'],
|
||||
|
|
|
@ -45,6 +45,35 @@ class TestTrustOperations(test_v3.RestfulTestCase):
|
|||
self.post('/OS-TRUST/trusts', body={'trust': {}},
|
||||
expected_status=http_client.FORBIDDEN)
|
||||
|
||||
def test_create_trust_with_invalid_expiration_fails(self):
|
||||
# create a new trust
|
||||
ref = unit.new_trust_ref(
|
||||
trustor_user_id=self.user_id,
|
||||
trustee_user_id=self.trustee_user_id,
|
||||
project_id=self.project_id,
|
||||
role_ids=[self.role_id])
|
||||
|
||||
ref['expires_at'] = 'bad'
|
||||
self.post(
|
||||
'/OS-TRUST/trusts',
|
||||
body={'trust': ref},
|
||||
expected_status=http_client.BAD_REQUEST
|
||||
)
|
||||
|
||||
ref['expires_at'] = ''
|
||||
self.post(
|
||||
'/OS-TRUST/trusts',
|
||||
body={'trust': ref},
|
||||
expected_status=http_client.BAD_REQUEST
|
||||
)
|
||||
|
||||
ref['expires_at'] = 'Z'
|
||||
self.post(
|
||||
'/OS-TRUST/trusts',
|
||||
body={'trust': ref},
|
||||
expected_status=http_client.BAD_REQUEST
|
||||
)
|
||||
|
||||
def test_trust_crud(self):
|
||||
# create a new trust
|
||||
ref = unit.new_trust_ref(
|
||||
|
@ -151,6 +180,22 @@ class TestTrustOperations(test_v3.RestfulTestCase):
|
|||
expected_status=http_client.FORBIDDEN
|
||||
)
|
||||
|
||||
def test_create_trust_with_expiration_in_the_past_fails(self):
|
||||
ref = unit.new_trust_ref(
|
||||
trustor_user_id=self.user_id,
|
||||
trustee_user_id=self.trustee_user_id,
|
||||
project_id=self.project_id,
|
||||
impersonation=False,
|
||||
expires='2010-06-04T08:44:31.999999Z',
|
||||
role_ids=[self.role_id]
|
||||
)
|
||||
|
||||
self.post(
|
||||
'/OS-TRUST/trusts',
|
||||
body={'trust': ref},
|
||||
expected_status=http_client.BAD_REQUEST
|
||||
)
|
||||
|
||||
def test_delete_trust(self):
|
||||
# create a trust
|
||||
ref = unit.new_trust_ref(
|
||||
|
|
|
@ -12,10 +12,6 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import sys
|
||||
|
||||
import six
|
||||
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone.common import utils
|
||||
|
@ -62,84 +58,6 @@ class ExternalAuthNotApplicable(Exception):
|
|||
pass
|
||||
|
||||
|
||||
@dependency.requires('catalog_api', 'identity_api', 'resource_api',
|
||||
'token_provider_api', 'trust_api')
|
||||
class Auth(controller.V2Controller):
|
||||
|
||||
@controller.v2_auth_deprecated
|
||||
def authenticate(self, request, auth=None):
|
||||
"""Authenticate credentials and return a token.
|
||||
|
||||
Accept auth as a dict that looks like::
|
||||
|
||||
{
|
||||
"auth":{
|
||||
"passwordCredentials":{
|
||||
"username":"test_user",
|
||||
"password":"mypass"
|
||||
},
|
||||
"tenantName":"customer-x"
|
||||
}
|
||||
}
|
||||
|
||||
In this case, tenant is optional, if not provided the token will be
|
||||
considered "unscoped" and can later be used to get a scoped token.
|
||||
|
||||
Alternatively, this call accepts auth with only a token and tenant
|
||||
that will return a token that is scoped to that tenant.
|
||||
"""
|
||||
method = authentication_method_generator(request, auth)
|
||||
user_ref, project_id, expiry, bind, audit_id = (
|
||||
method.authenticate(request, auth)
|
||||
)
|
||||
|
||||
# Ensure the entities provided in the authentication information are
|
||||
# valid and not disabled.
|
||||
try:
|
||||
self.identity_api.assert_user_enabled(
|
||||
user_id=user_ref['id'], user=user_ref)
|
||||
if project_id:
|
||||
try:
|
||||
self.resource_api.get_project(project_id)
|
||||
except exception.ProjectNotFound:
|
||||
msg = _(
|
||||
'Project ID not found: %(p_id)s'
|
||||
) % {'p_id': project_id}
|
||||
raise exception.Unauthorized(msg)
|
||||
self.resource_api.assert_project_enabled(project_id)
|
||||
except AssertionError as e:
|
||||
six.reraise(exception.Unauthorized, exception.Unauthorized(e),
|
||||
sys.exc_info()[2])
|
||||
# NOTE(morganfainberg): Make sure the data is in correct form since it
|
||||
# might be consumed external to Keystone and this is a v2.0 controller.
|
||||
# The user_ref is encoded into the auth_token_data which is returned as
|
||||
# part of the token data. The token provider doesn't care about the
|
||||
# format.
|
||||
user_ref = self.v3_to_v2_user(user_ref)
|
||||
|
||||
auth_context = {}
|
||||
if bind:
|
||||
auth_context['bind'] = bind
|
||||
|
||||
trust_ref = None
|
||||
if CONF.trust.enabled and 'trust_id' in auth:
|
||||
trust_ref = self.trust_api.get_trust(auth['trust_id'])
|
||||
|
||||
(token_id, token_data) = self.token_provider_api.issue_token(
|
||||
user_ref['id'], ['password'], expires_at=expiry,
|
||||
project_id=project_id, trust=trust_ref, parent_audit_id=audit_id,
|
||||
auth_context=auth_context)
|
||||
v2_helper = V2TokenDataHelper()
|
||||
token_data = v2_helper.v3_to_v2_token(token_data, token_id)
|
||||
|
||||
# NOTE(wanghong): We consume a trust use only when we are using trusts
|
||||
# and have successfully issued a token.
|
||||
if CONF.trust.enabled and 'trust_id' in auth:
|
||||
self.trust_api.consume_use(auth['trust_id'])
|
||||
|
||||
return token_data
|
||||
|
||||
|
||||
@dependency.requires('resource_api', 'identity_api')
|
||||
class BaseAuthenticationMethod(object):
|
||||
"""Common utilities/dependencies for all authentication method classes."""
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
# Copyright 2012 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.
|
||||
from keystone.common import wsgi
|
||||
from keystone.token import controllers
|
||||
|
||||
|
||||
class Router(wsgi.ComposableRouter):
|
||||
def add_routes(self, mapper):
|
||||
token_controller = controllers.Auth()
|
||||
mapper.connect('/tokens',
|
||||
controller=token_controller,
|
||||
action='authenticate',
|
||||
conditions=dict(method=['POST']))
|
|
@ -33,7 +33,6 @@ from keystone.policy import routers as policy_routers
|
|||
from keystone.resource import routers as resource_routers
|
||||
from keystone.revoke import routers as revoke_routers
|
||||
from keystone.token import _simple_cert as simple_cert_ext
|
||||
from keystone.token import routers as token_routers
|
||||
from keystone.trust import routers as trust_routers
|
||||
from keystone.v2_crud import admin_crud
|
||||
from keystone.version import controllers
|
||||
|
@ -83,7 +82,6 @@ def public_app_factory(global_conf, **local_conf):
|
|||
controllers.register_version('v2.0')
|
||||
return wsgi.ComposingRouter(routes.Mapper(),
|
||||
[assignment_routers.Public(),
|
||||
token_routers.Router(),
|
||||
routers.VersionV2('public'),
|
||||
routers.Extension(False)])
|
||||
|
||||
|
@ -93,8 +91,7 @@ def public_app_factory(global_conf, **local_conf):
|
|||
def admin_app_factory(global_conf, **local_conf):
|
||||
controllers.register_version('v2.0')
|
||||
return wsgi.ComposingRouter(routes.Mapper(),
|
||||
[token_routers.Router(),
|
||||
admin_crud.Router(),
|
||||
[admin_crud.Router(),
|
||||
routers.VersionV2('admin'),
|
||||
routers.Extension()])
|
||||
|
||||
|
|
Loading…
Reference in New Issue