Drop deprecated Keystone V2 API support

Keystone V2 API was deprecated in Stein release in Horizon and
removed from Keystone in Queens release.

Change-Id: I917e273d3174adf0874e516b3d635ccb8ba58a27
This commit is contained in:
Ivan Kolodyazhny 2019-07-31 16:13:01 +03:00 committed by Vishal Manchanda
parent 7c897b677c
commit f33e5fd8ac
22 changed files with 106 additions and 820 deletions

View File

@ -492,7 +492,7 @@ OpenStack dashboard to use a specific API version for a given service API.
The version should be formatted as it appears in the URL for the
service API. For example, the identity service APIs have inconsistent
use of the decimal point, so valid options would be "2.0" or "3".
use of the decimal point, so valid options would be "3".
For example:
.. code-block:: python
@ -1313,7 +1313,7 @@ Default: ``["admin"]``
The list of roles that have administrator privileges in this OpenStack
installation. This check is very basic and essentially only works with
keystone v2.0 and v3 with the default policy file. The setting assumes there
keystone v3 with the default policy file. The setting assumes there
is a common ``admin`` like role(s) across services. Example uses of this
setting are:

View File

@ -149,9 +149,7 @@ class KeystoneBackend(object):
scoped_auth = domain_auth
scoped_auth_ref = domain_auth_ref
elif not scoped_auth_ref and not domain_auth_ref:
msg = _('You are not authorized for any projects.')
if utils.get_keystone_version() >= 3:
msg = _('You are not authorized for any projects or domains.')
msg = _('You are not authorized for any projects or domains.')
raise exceptions.KeystoneAuthException(msg)
# Check expiry for our new scoped token.
@ -170,16 +168,10 @@ class KeystoneBackend(object):
interface = settings.OPENSTACK_ENDPOINT_TYPE
endpoint, url_fixed = utils.fix_auth_url_version_prefix(
scoped_auth_ref.service_catalog.url_for(
service_type='identity',
interface=interface,
region_name=region_name))
if url_fixed:
LOG.warning("The Keystone URL in service catalog points to a v2.0 "
"Keystone endpoint, but v3 is specified as the API "
"version to use by Horizon. Using v3 endpoint for "
"authentication.")
endpoint = scoped_auth_ref.service_catalog.url_for(
service_type='identity',
interface=interface,
region_name=region_name)
# If we made it here we succeeded. Create our User!
unscoped_token = unscoped_auth_ref.auth_token

View File

@ -15,7 +15,6 @@ import logging
from django.utils.translation import ugettext_lazy as _
from keystoneauth1 import exceptions as keystone_exceptions
from keystoneclient.v2_0 import client as v2_client
from keystoneclient.v3 import client as v3_client
import six
@ -82,16 +81,11 @@ class BasePlugin(object):
or v3 keystoneclient projects objects.
"""
try:
if self.keystone_version >= 3:
client = v3_client.Client(session=session, auth=auth_plugin)
if auth_ref.is_federated:
return client.federation.projects.list()
else:
return client.projects.list(user=auth_ref.user_id)
client = v3_client.Client(session=session, auth=auth_plugin)
if auth_ref.is_federated:
return client.federation.projects.list()
else:
client = v2_client.Client(session=session, auth=auth_plugin)
return client.tenants.list()
return client.projects.list(user=auth_ref.user_id)
except (keystone_exceptions.ClientException,
keystone_exceptions.AuthorizationFailure):
@ -100,11 +94,8 @@ class BasePlugin(object):
def list_domains(self, session, auth_plugin, auth_ref=None):
try:
if self.keystone_version >= 3:
client = v3_client.Client(session=session, auth=auth_plugin)
return client.auth.domains()
else:
return []
client = v3_client.Client(session=session, auth=auth_plugin)
return client.auth.domains()
except (keystone_exceptions.ClientException,
keystone_exceptions.AuthorizationFailure):
msg = _('Unable to retrieve authorized domains.')
@ -203,8 +194,6 @@ class BasePlugin(object):
session = utils.get_session()
auth_url = unscoped_auth.auth_url
if utils.get_keystone_version() < 3:
return None, None
if domain_name:
domains = [domain_name]
else:

View File

@ -44,10 +44,8 @@ class K2KAuthPlugin(base.BasePlugin):
# Avoid mutable default arg for plugins
plugins = plugins or []
# service_provider being None prevents infinite recursion
if utils.get_keystone_version() < 3 or not service_provider:
return None
if not service_provider:
return
keystone_idp_id = settings.KEYSTONE_PROVIDER_IDP_ID
if service_provider == keystone_idp_id:

View File

@ -12,11 +12,9 @@
import logging
from keystoneauth1.identity import v2 as v2_auth
from keystoneauth1.identity import v3 as v3_auth
from openstack_auth.plugin import base
from openstack_auth import utils
LOG = logging.getLogger(__name__)
@ -38,14 +36,8 @@ class PasswordPlugin(base.BasePlugin):
LOG.debug('Attempting to authenticate for %s', username)
if utils.get_keystone_version() >= 3:
return v3_auth.Password(auth_url=auth_url,
username=username,
password=password,
user_domain_name=user_domain_name,
unscoped=True)
else:
return v2_auth.Password(auth_url=auth_url,
username=username,
password=password)
return v3_auth.Password(auth_url=auth_url,
username=username,
password=password,
user_domain_name=user_domain_name,
unscoped=True)

View File

@ -10,11 +10,9 @@
# License for the specific language governing permissions and limitations
# under the License.
from keystoneauth1.identity import v2 as v2_auth
from keystoneauth1.identity import v3 as v3_auth
from openstack_auth.plugin import base
from openstack_auth import utils
__all__ = ['TokenPlugin']
@ -28,14 +26,7 @@ class TokenPlugin(base.BasePlugin):
if not all((auth_url, token)):
return None
if utils.get_keystone_version() >= 3:
return v3_auth.Token(auth_url=auth_url,
token=token,
project_id=project_id,
reauthenticate=False)
else:
return v2_auth.Token(auth_url=auth_url,
token=token,
tenant_id=project_id,
reauthenticate=False)
return v3_auth.Token(auth_url=auth_url,
token=token,
project_id=project_id,
reauthenticate=False)

View File

@ -1,143 +0,0 @@
# 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 uuid
from django.utils import datetime_safe
from keystoneauth1.access import access
from keystoneauth1.access import service_catalog
from keystoneclient.v2_0 import roles
from keystoneclient.v2_0 import tenants
from keystoneclient.v2_0 import users
class TestDataContainer(object):
"""Arbitrary holder for test data in an object-oriented fashion."""
pass
def generate_test_data():
'''Builds a set of test_data data as returned by Keystone V2.'''
test_data = TestDataContainer()
keystone_service = {
'type': 'identity',
'name': 'keystone',
'endpoints_links': [],
'endpoints': [
{
'region': 'RegionOne',
'adminURL': 'http://admin.localhost:35357/v2.0',
'internalURL': 'http://internal.localhost:5000/v2.0',
'publicURL': 'http://public.localhost:5000/v2.0'
}
]
}
# Users
user_dict = {'id': uuid.uuid4().hex,
'name': 'gabriel',
'email': 'gabriel@example.com',
'password': 'swordfish',
'token': '',
'enabled': True}
test_data.user = users.User(None, user_dict, loaded=True)
# Tenants
tenant_dict_1 = {'id': uuid.uuid4().hex,
'name': 'tenant_one',
'description': '',
'enabled': True}
tenant_dict_2 = {'id': uuid.uuid4().hex,
'name': 'tenant_two',
'description': '',
'enabled': False}
test_data.tenant_one = tenants.Tenant(None, tenant_dict_1, loaded=True)
test_data.tenant_two = tenants.Tenant(None, tenant_dict_2, loaded=True)
nova_service = {
'type': 'compute',
'name': 'nova',
'endpoint_links': [],
'endpoints': [
{
'region': 'RegionOne',
'adminURL': ('http://nova-admin.localhost:8774/v2.0/%s'
% (tenant_dict_1['id'])),
'internalURL': ('http://nova-internal.localhost:8774/v2.0/%s'
% (tenant_dict_1['id'])),
'publicURL': ('http://nova-public.localhost:8774/v2.0/%s'
% (tenant_dict_1['id']))
},
{
'region': 'RegionTwo',
'adminURL': ('http://nova2-admin.localhost:8774/v2.0/%s'
% (tenant_dict_1['id'])),
'internalURL': ('http://nova2-internal.localhost:8774/v2.0/%s'
% (tenant_dict_1['id'])),
'publicURL': ('http://nova2-public.localhost:8774/v2.0/%s'
% (tenant_dict_1['id']))
}
]
}
# Roles
role_dict = {'id': uuid.uuid4().hex,
'name': 'Member'}
test_data.role = roles.Role(roles.RoleManager, role_dict)
# Tokens
tomorrow = datetime_safe.datetime.now() + datetime.timedelta(days=1)
expiration = datetime_safe.datetime.isoformat(tomorrow)
scoped_token_dict = {
'access': {
'token': {
'id': uuid.uuid4().hex,
'expires': expiration,
'tenant': tenant_dict_1,
'tenants': [tenant_dict_1, tenant_dict_2]},
'user': {
'id': user_dict['id'],
'name': user_dict['name'],
'roles': [role_dict]},
'serviceCatalog': [keystone_service, nova_service]
}
}
test_data.scoped_access_info = access.create(
resp=None,
body=scoped_token_dict)
unscoped_token_dict = {
'access': {
'token': {
'id': uuid.uuid4().hex,
'expires': expiration},
'user': {
'id': user_dict['id'],
'name': user_dict['name'],
'roles': [role_dict]},
'serviceCatalog': [keystone_service]
}
}
test_data.unscoped_access_info = access.create(
resp=None,
body=unscoped_token_dict)
# Service Catalog
test_data.service_catalog = service_catalog.ServiceCatalogV2(
[keystone_service, nova_service])
return test_data

View File

@ -19,16 +19,13 @@ from django import test
from django.test.utils import override_settings
from django.urls import reverse
from keystoneauth1 import exceptions as keystone_exceptions
from keystoneauth1.identity import v2 as v2_auth
from keystoneauth1.identity import v3 as v3_auth
from keystoneauth1 import session
from keystoneauth1 import token_endpoint
from keystoneclient.v2_0 import client as client_v2
from keystoneclient.v3 import client as client_v3
from mox3 import mox
from testscenarios import load_tests_apply_scenarios
from openstack_auth.tests import data_v2
from openstack_auth.tests import data_v3
from openstack_auth import utils
@ -143,333 +140,6 @@ class OpenStackAuthFederatedTestsMixin(object):
self._mock_unscoped_list_domains(client, domains)
class OpenStackAuthTestsV2(OpenStackAuthTestsMixin, test.TestCase):
def setUp(self):
super(OpenStackAuthTestsV2, self).setUp()
if getattr(self, 'interface', None):
override = self.settings(OPENSTACK_ENDPOINT_TYPE=self.interface)
override.enable()
self.addCleanup(override.disable)
self.mox = mox.Mox()
self.addCleanup(self.mox.VerifyAll)
self.addCleanup(self.mox.UnsetStubs)
self.data = data_v2.generate_test_data()
self.ks_client_module = client_v2
settings.OPENSTACK_API_VERSIONS['identity'] = 2.0
settings.OPENSTACK_KEYSTONE_URL = "http://localhost:5000/v2.0"
self.mox.StubOutClassWithMocks(token_endpoint, 'Token')
self.mox.StubOutClassWithMocks(v2_auth, 'Token')
self.mox.StubOutClassWithMocks(v2_auth, 'Password')
self.mox.StubOutClassWithMocks(client_v2, 'Client')
def _mock_unscoped_list_tenants(self, client, tenants):
client.tenants = self.mox.CreateMockAnything()
client.tenants.list().AndReturn(tenants)
def _mock_unscoped_client_list_tenants(self, user, tenants):
client = self._mock_unscoped_client(user)
self._mock_unscoped_list_tenants(client, tenants)
def _create_password_auth(self, username=None, password=None, url=None):
if not username:
username = self.data.user.name
if not password:
password = self.data.user.password
if not url:
url = settings.OPENSTACK_KEYSTONE_URL
return v2_auth.Password(auth_url=url,
password=password,
username=username)
def _create_token_auth(self, project_id, token=None, url=None):
if not token:
token = self.data.unscoped_access_info.auth_token
if not url:
url = settings.OPENSTACK_KEYSTONE_URL
return v2_auth.Token(auth_url=url,
token=token,
tenant_id=project_id,
reauthenticate=False)
def _login(self):
tenants = [self.data.tenant_one, self.data.tenant_two]
user = self.data.user
unscoped = self.data.unscoped_access_info
form_data = self.get_form_data(user)
self._mock_unscoped_client_list_tenants(user, tenants)
self._mock_scoped_client_for_tenant(unscoped, self.data.tenant_one.id)
self.mox.ReplayAll()
url = reverse('login')
# GET the page to set the test cookie.
response = self.client.get(url, form_data)
self.assertEqual(response.status_code, 200)
# POST to the page to log in.
response = self.client.post(url, form_data)
self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
def test_login(self):
self._login()
def test_login_with_disabled_tenant(self):
# Test to validate that authentication will not try to get
# scoped token for disabled project.
tenants = [self.data.tenant_two, self.data.tenant_one]
user = self.data.user
unscoped = self.data.unscoped_access_info
form_data = self.get_form_data(user)
self._mock_unscoped_client_list_tenants(user, tenants)
self._mock_scoped_client_for_tenant(unscoped, self.data.tenant_one.id)
self.mox.ReplayAll()
url = reverse('login')
# GET the page to set the test cookie.
response = self.client.get(url, form_data)
self.assertEqual(response.status_code, 200)
# POST to the page to log in.
response = self.client.post(url, form_data)
self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
def test_login_w_bad_region_cookie(self):
self.client.cookies['services_region'] = "bad_region"
self._login()
self.assertNotEqual("bad_region",
self.client.session['services_region'])
self.assertEqual("RegionOne",
self.client.session['services_region'])
def test_no_enabled_tenants(self):
tenants = [self.data.tenant_two]
user = self.data.user
form_data = self.get_form_data(user)
self._mock_unscoped_client_list_tenants(user, tenants)
self.mox.ReplayAll()
url = reverse('login')
# GET the page to set the test cookie.
response = self.client.get(url, form_data)
self.assertEqual(response.status_code, 200)
# POST to the page to log in.
response = self.client.post(url, form_data)
self.assertTemplateUsed(response, 'auth/login.html')
self.assertContains(response,
'You are not authorized for any projects.')
def test_no_tenants(self):
user = self.data.user
form_data = self.get_form_data(user)
self._mock_unscoped_client_list_tenants(user, [])
self.mox.ReplayAll()
url = reverse('login')
# GET the page to set the test cookie.
response = self.client.get(url, form_data)
self.assertEqual(response.status_code, 200)
# POST to the page to log in.
response = self.client.post(url, form_data)
self.assertTemplateUsed(response, 'auth/login.html')
self.assertContains(response,
'You are not authorized for any projects.')
def test_invalid_credentials(self):
user = self.data.user
form_data = self.get_form_data(user)
form_data['password'] = "invalid"
exc = keystone_exceptions.Unauthorized(401)
self._mock_client_password_auth_failure(user.name, "invalid", exc)
self.mox.ReplayAll()
url = reverse('login')
# GET the page to set the test cookie.
response = self.client.get(url, form_data)
self.assertEqual(response.status_code, 200)
# POST to the page to log in.
response = self.client.post(url, form_data)
self.assertTemplateUsed(response, 'auth/login.html')
self.assertContains(response, "Invalid credentials.")
def test_exception(self):
user = self.data.user
form_data = self.get_form_data(user)
exc = keystone_exceptions.ClientException(500)
self._mock_client_password_auth_failure(user.name, user.password, exc)
self.mox.ReplayAll()
url = reverse('login')
# GET the page to set the test cookie.
response = self.client.get(url, form_data)
self.assertEqual(response.status_code, 200)
# POST to the page to log in.
response = self.client.post(url, form_data)
self.assertTemplateUsed(response, 'auth/login.html')
self.assertContains(response,
("An error occurred authenticating. Please try "
"again later."))
def test_redirect_when_already_logged_in(self):
self._login()
response = self.client.get(reverse('login'))
self.assertEqual(response.status_code, 302)
self.assertNotIn(reverse('login'), response['location'])
def test_dont_redirect_when_already_logged_in_if_next_is_set(self):
self._login()
expected_url = "%s?%s=/%s/" % (reverse('login'),
auth.REDIRECT_FIELD_NAME,
'special')
response = self.client.get(expected_url)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'auth/login.html')
def test_switch(self, next=None):
tenant = self.data.tenant_two
tenants = [self.data.tenant_one, self.data.tenant_two]
user = self.data.user
unscoped = self.data.unscoped_access_info
scoped = self.data.scoped_access_info
sc = self.data.service_catalog
et = getattr(settings, 'OPENSTACK_ENDPOINT_TYPE', 'publicURL')
endpoint = sc.url_for(service_type='identity', interface=et)
form_data = self.get_form_data(user)
self._mock_unscoped_client_list_tenants(user, tenants)
self._mock_scoped_client_for_tenant(unscoped, self.data.tenant_one.id)
self._mock_scoped_client_for_tenant(scoped, tenant.id, url=endpoint,
client=False)
self.mox.ReplayAll()
url = reverse('login')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
response = self.client.post(url, form_data)
self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
url = reverse('switch_tenants', args=[tenant.id])
scoped._token['tenant']['id'] = self.data.tenant_two.id
if next:
form_data.update({auth.REDIRECT_FIELD_NAME: next})
response = self.client.get(url, form_data)
if next:
expected_url = next
self.assertEqual(response['location'], expected_url)
else:
self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
self.assertEqual(self.client.session['token'].tenant['id'],
scoped.tenant_id)
def test_switch_with_next(self):
self.test_switch(next='/next_url')
def test_switch_region(self, next=None):
tenants = [self.data.tenant_one, self.data.tenant_two]
user = self.data.user
scoped = self.data.scoped_access_info
sc = self.data.service_catalog
form_data = self.get_form_data(user)
self._mock_unscoped_client_list_tenants(user, tenants)
self._mock_scoped_client_for_tenant(scoped, self.data.tenant_one.id)
self.mox.ReplayAll()
url = reverse('login')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
response = self.client.post(url, form_data)
self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
old_region = sc.get_endpoints()['compute'][0]['region']
self.assertEqual(self.client.session['services_region'], old_region)
region = sc.get_endpoints()['compute'][1]['region']
url = reverse('switch_services_region', args=[region])
form_data['region_name'] = region
if next:
form_data.update({auth.REDIRECT_FIELD_NAME: next})
response = self.client.get(url, form_data)
if next:
expected_url = next
self.assertEqual(response['location'], expected_url)
else:
self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
self.assertEqual(self.client.session['services_region'], region)
self.assertEqual(self.client.cookies['services_region'].value, region)
def test_switch_region_with_next(self, next=None):
self.test_switch_region(next='/next_url')
def test_tenant_sorting(self):
tenants = [self.data.tenant_two, self.data.tenant_one]
expected_tenants = [self.data.tenant_one, self.data.tenant_two]
user = self.data.user
unscoped = self.data.unscoped_access_info
client = self._mock_unscoped_client_with_token(user, unscoped)
self._mock_unscoped_list_tenants(client, tenants)
self.mox.ReplayAll()
tenant_list = utils.get_project_list(
user_id=user.id,
auth_url=settings.OPENSTACK_KEYSTONE_URL,
token=unscoped.auth_token)
self.assertEqual(tenant_list, expected_tenants)
class OpenStackAuthTestsV3(OpenStackAuthTestsMixin,
OpenStackAuthFederatedTestsMixin,
test.TestCase):

View File

@ -11,7 +11,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from django.conf import settings
from django import http
from django import test
from django.test import client
@ -55,41 +54,6 @@ class RoleTestCaseAdmin(test.TestCase):
class UtilsTestCase(test.TestCase):
def test_fix_auth_url_version_v20(self):
settings.OPENSTACK_API_VERSIONS['identity'] = 2.0
test_urls = [
("http://a/", ("http://a/v2.0", False)),
("http://a", ("http://a/v2.0", False)),
("http://a:8080/", ("http://a:8080/v2.0", False)),
("http://a/v2.0", ("http://a/v2.0", False)),
("http://a/v2.0/", ("http://a/v2.0/", False)),
("http://a/identity", ("http://a/identity/v2.0", False)),
("http://a/identity/", ("http://a/identity/v2.0", False)),
("http://a:5000/identity/v2.0",
("http://a:5000/identity/v2.0", False)),
("http://a/identity/v2.0/", ("http://a/identity/v2.0/", False))
]
for src, expected in test_urls:
self.assertEqual(expected, utils.fix_auth_url_version_prefix(src))
def test_fix_auth_url_version_v3(self):
settings.OPENSTACK_API_VERSIONS['identity'] = 3
test_urls = [
("http://a/", ("http://a/v3", False)),
("http://a", ("http://a/v3", False)),
("http://a:8080/", ("http://a:8080/v3", False)),
("http://a/v3", ("http://a/v3", False)),
("http://a/v3/", ("http://a/v3/", False)),
("http://a/v2.0/", ("http://a/v3/", True)),
("http://a/v2.0", ("http://a/v3", True)),
("http://a/identity", ("http://a/identity/v3", False)),
("http://a:5000/identity/", ("http://a:5000/identity/v3", False)),
("http://a/identity/v3", ("http://a/identity/v3", False)),
("http://a/identity/v3/", ("http://a/identity/v3/", False))
]
for src, expected in test_urls:
self.assertEqual(expected, utils.fix_auth_url_version_prefix(src))
@override_settings(DEFAULT_SERVICE_REGIONS={
'http://example.com': 'RegionThree', '*': 'RegionFour'})
def test_default_services_region_precedence(self):

View File

@ -19,11 +19,9 @@ from django.conf import settings
from django.contrib import auth
from django.contrib.auth import models
from django.utils import timezone
from keystoneauth1.identity import v2 as v2_auth
from keystoneauth1.identity import v3 as v3_auth
from keystoneauth1 import session
from keystoneauth1 import token_endpoint
from keystoneclient.v2_0 import client as client_v2
from keystoneclient.v3 import client as client_v3
from six.moves.urllib import parse as urlparse
@ -116,17 +114,12 @@ def get_session():
def get_keystone_client():
if get_keystone_version() < 3:
return client_v2
else:
return client_v3
return client_v3
def is_websso_enabled():
"""Websso is supported in Keystone version 3."""
websso_enabled = settings.WEBSSO_ENABLED
keystonev3_plus = (get_keystone_version() >= 3)
return websso_enabled and keystonev3_plus
return settings.WEBSSO_ENABLED
def is_websso_default_redirect():
@ -275,26 +268,24 @@ def _augment_url_with_version(auth_url):
the identity URLs returned by Keystone might no longer contain API
versions, leaving the version choice up to the user.
"""
if has_in_url_path(auth_url, ["/v2.0", "/v3"]):
if has_in_url_path(auth_url, ["/v3"]):
return auth_url
if get_keystone_version() >= 3:
return url_path_append(auth_url, "/v3")
else:
return url_path_append(auth_url, "/v2.0")
return url_path_append(auth_url, "/v3")
def fix_auth_url_version_prefix(auth_url):
"""Fix up the auth url if an invalid or no version prefix was given.
People still give a v2 auth_url even when they specify that they want v3
authentication. Fix the URL to say v3 in this case and add version if it is
Fix the URL to say v3 in this case and add version if it is
missing entirely. This should be smarter and use discovery.
Until version discovery is implemented we need this method to get
everything working.
"""
auth_url = _augment_url_with_version(auth_url)
url_fixed = False
if get_keystone_version() >= 3 and has_in_url_path(auth_url, ["/v2.0"]):
if has_in_url_path(auth_url, ["/v2.0"]):
url_fixed = True
auth_url = url_path_replace(auth_url, "/v2.0", "/v3", 1)
@ -312,21 +303,15 @@ def clean_up_auth_url(auth_url):
def get_token_auth_plugin(auth_url, token, project_id=None, domain_name=None):
if get_keystone_version() >= 3:
if domain_name:
return v3_auth.Token(auth_url=auth_url,
token=token,
domain_name=domain_name,
reauthenticate=False)
else:
return v3_auth.Token(auth_url=auth_url,
token=token,
project_id=project_id,
reauthenticate=False)
else:
return v2_auth.Token(auth_url=auth_url,
if domain_name:
return v3_auth.Token(auth_url=auth_url,
token=token,
tenant_id=project_id,
domain_name=domain_name,
reauthenticate=False)
else:
return v3_auth.Token(auth_url=auth_url,
token=token,
project_id=project_id,
reauthenticate=False)

View File

@ -33,12 +33,10 @@ from openstack_auth import backend
from openstack_auth import utils as auth_utils
from horizon import exceptions
from horizon import messages
from openstack_dashboard.api import base
from openstack_dashboard.contrib.developer.profiler import api as profiler
from openstack_dashboard import policy
from openstack_dashboard.utils import settings as utils
LOG = logging.getLogger(__name__)
@ -56,25 +54,13 @@ class IdentityAPIVersionManager(base.APIVersionManager):
return user
def get_project_manager(self, *args, **kwargs):
if VERSIONS.active < 3:
manager = keystoneclient(*args, **kwargs).tenants
else:
manager = keystoneclient(*args, **kwargs).projects
return manager
return keystoneclient(*args, **kwargs).projects
VERSIONS = IdentityAPIVersionManager(
"identity", preferred_version=auth_utils.get_keystone_version())
# Import from oldest to newest so that "preferred" takes correct precedence.
try:
# pylint: disable=ungrouped-imports
from keystoneclient.v2_0 import client as keystone_client_v2
VERSIONS.load_supported_version(2.0, {"client": keystone_client_v2})
except ImportError:
pass
try:
# pylint: disable=ungrouped-imports
from keystoneclient.v3 import client as keystone_client_v3
@ -266,12 +252,9 @@ def tenant_create(request, name, description=None, enabled=None,
domain=None, **kwargs):
manager = VERSIONS.get_project_manager(request, admin=True)
try:
if VERSIONS.active < 3:
return manager.create(name, description, enabled, **kwargs)
else:
return manager.create(name, domain,
description=description,
enabled=enabled, **kwargs)
return manager.create(name, domain,
description=description,
enabled=enabled, **kwargs)
except keystone_exceptions.Conflict:
raise exceptions.Conflict()
@ -364,25 +347,13 @@ def tenant_delete(request, project):
def tenant_list(request, paginate=False, marker=None, domain=None, user=None,
admin=True, filters=None):
manager = VERSIONS.get_project_manager(request, admin=admin)
page_size = utils.get_page_size(request)
tenants = []
limit = None
if paginate:
limit = page_size + 1
has_more_data = False
# if requesting the projects for the current user,
# return the list from the cache
if user == request.user.id:
tenants = request.user.authorized_tenants
elif VERSIONS.active < 3:
tenants = manager.list(limit, marker)
if paginate and len(tenants) > page_size:
tenants.pop(-1)
has_more_data = True
# V3 API
else:
domain_id = get_effective_domain_id(request)
kwargs = {
@ -408,12 +379,8 @@ def tenant_update(request, project, name=None, description=None,
enabled=None, domain=None, **kwargs):
manager = VERSIONS.get_project_manager(request, admin=True)
try:
if VERSIONS.active < 3:
return manager.update(project, name, description, enabled,
**kwargs)
else:
return manager.update(project, name=name, description=description,
enabled=enabled, domain=domain, **kwargs)
return manager.update(project, name=name, description=description,
enabled=enabled, domain=domain, **kwargs)
except keystone_exceptions.Conflict:
raise exceptions.Conflict()
@ -421,16 +388,13 @@ def tenant_update(request, project, name=None, description=None,
@profiler.trace
def user_list(request, project=None, domain=None, group=None, filters=None):
users = []
if VERSIONS.active < 3:
kwargs = {"tenant_id": project}
else:
kwargs = {
"project": project,
"domain": domain,
"group": group
}
if filters is not None:
kwargs.update(filters)
kwargs = {
"project": project,
"domain": domain,
"group": group
}
if filters is not None:
kwargs.update(filters)
if 'id' in kwargs:
try:
users = [user_get(request, kwargs['id'])]
@ -446,14 +410,10 @@ def user_create(request, name=None, email=None, password=None, project=None,
enabled=None, domain=None, description=None, **data):
manager = keystoneclient(request, admin=True).users
try:
if VERSIONS.active < 3:
user = manager.create(name, password, email, project, enabled)
return VERSIONS.upgrade_v2_user(user)
else:
return manager.create(name, password=password, email=email,
default_project=project, enabled=enabled,
domain=domain, description=description,
**data)
return manager.create(name, password=password, email=email,
default_project=project, enabled=enabled,
domain=domain, description=description,
**data)
except keystone_exceptions.Conflict:
raise exceptions.Conflict()
@ -472,59 +432,20 @@ def user_get(request, user_id, admin=True):
@profiler.trace
def user_update(request, user, **data):
manager = keystoneclient(request, admin=True).users
error = None
if not keystone_can_edit_user():
raise keystone_exceptions.ClientException(
405, _("Identity service does not allow editing user data."))
# The v2 API updates user model and default project separately
if VERSIONS.active < 3:
# Update user details
try:
user = manager.update(user, **data)
except keystone_exceptions.Conflict:
raise exceptions.Conflict()
except Exception:
error = exceptions.handle(request, ignore=True)
if "project" in data:
project = data.pop('project')
# Update default tenant
try:
user_update_tenant(request, user, project)
user.tenantId = project
except Exception:
error = exceptions.handle(request, ignore=True)
# Check for existing roles
# Show a warning if no role exists for the project
user_roles = roles_for_user(request, user, project)
if not user_roles:
messages.warning(request,
_('User %s has no role defined for '
'that project.')
% data.get('name', None))
if error is not None:
raise error
# v3 API is so much simpler...
else:
try:
user = manager.update(user, **data)
except keystone_exceptions.Conflict:
raise exceptions.Conflict()
try:
user = manager.update(user, **data)
except keystone_exceptions.Conflict:
raise exceptions.Conflict()
@profiler.trace
def user_update_enabled(request, user, enabled):
manager = keystoneclient(request, admin=True).users
if VERSIONS.active < 3:
manager.update_enabled(user, enabled)
else:
manager.update(user, enabled=enabled)
manager.update(user, enabled=enabled)
@profiler.trace
@ -535,16 +456,13 @@ def user_update_password(request, user, password, admin=True):
405, _("Identity service does not allow editing user password."))
manager = keystoneclient(request, admin=admin).users
if VERSIONS.active < 3:
manager.update_password(user, password)
else:
manager.update(user, password=password)
manager.update(user, password=password)
def user_verify_admin_password(request, admin_password):
# attempt to create a new client instance with admin password to
# verify if it's correct.
client = keystone_client_v2 if VERSIONS.active < 3 else keystone_client_v3
client = keystone_client_v3
try:
endpoint = _get_endpoint_url(request, 'publicURL')
insecure = settings.OPENSTACK_SSL_NO_VERIFY
@ -565,21 +483,14 @@ def user_verify_admin_password(request, admin_password):
@profiler.trace
def user_update_own_password(request, origpassword, password):
client = keystoneclient(request, admin=False)
if VERSIONS.active < 3:
client.user_id = request.user.id
return client.users.update_own_password(origpassword, password)
else:
client.users.client.session.auth.user_id = request.user.id
return client.users.update_password(origpassword, password)
client.users.client.session.auth.user_id = request.user.id
return client.users.update_password(origpassword, password)
@profiler.trace
def user_update_tenant(request, user, project, admin=True):
manager = keystoneclient(request, admin=admin).users
if VERSIONS.active < 3:
return manager.update_tenant(user, project)
else:
return manager.update(user, project=project)
return manager.update(user, project=project)
@profiler.trace
@ -689,9 +600,6 @@ def get_project_groups_roles(request, project):
def role_assignments_list(request, project=None, user=None, role=None,
group=None, domain=None, effective=False,
include_subtree=True, include_names=False):
if VERSIONS.active < 3:
raise exceptions.NotAvailable
if include_subtree:
domain = None
@ -751,10 +659,7 @@ def role_list(request, filters=None):
def roles_for_user(request, user, project=None, domain=None):
"""Returns a list of user roles scoped to a project or domain."""
manager = keystoneclient(request, admin=True).roles
if VERSIONS.active < 3:
return manager.roles_for_user(user, project)
else:
return manager.list(user=user, domain=domain, project=project)
return manager.list(user=user, domain=domain, project=project)
@profiler.trace
@ -793,26 +698,18 @@ def remove_domain_user_role(request, user, role, domain=None):
@profiler.trace
def get_project_users_roles(request, project):
users_roles = collections.defaultdict(list)
if VERSIONS.active < 3:
project_users = user_list(request, project=project)
project_role_assignments = role_assignments_list(request,
project=project)
for role_assignment in project_role_assignments:
if not hasattr(role_assignment, 'user'):
continue
user_id = role_assignment.user['id']
role_id = role_assignment.role['id']
for user in project_users:
roles = roles_for_user(request, user.id, project)
roles_ids = [role.id for role in roles]
users_roles[user.id].extend(roles_ids)
else:
project_role_assignments = role_assignments_list(request,
project=project)
for role_assignment in project_role_assignments:
if not hasattr(role_assignment, 'user'):
continue
user_id = role_assignment.user['id']
role_id = role_assignment.role['id']
# filter by project_id
if ('project' in role_assignment.scope and
role_assignment.scope['project']['id'] == project):
users_roles[user_id].append(role_id)
# filter by project_id
if ('project' in role_assignment.scope and
role_assignment.scope['project']['id'] == project):
users_roles[user_id].append(role_id)
return users_roles
@ -821,11 +718,8 @@ def add_tenant_user_role(request, project=None, user=None, role=None,
group=None, domain=None):
"""Adds a role for a user on a tenant."""
manager = keystoneclient(request, admin=True).roles
if VERSIONS.active < 3:
manager.add_user_role(user, role, project)
else:
manager.grant(role, user=user, project=project,
group=group, domain=domain)
manager.grant(role, user=user, project=project,
group=group, domain=domain)
@profiler.trace
@ -833,11 +727,8 @@ def remove_tenant_user_role(request, project=None, user=None, role=None,
group=None, domain=None):
"""Removes a given single role for a user from a tenant."""
manager = keystoneclient(request, admin=True).roles
if VERSIONS.active < 3:
return manager.remove_user_role(user, role, project)
else:
return manager.revoke(role, user=user, project=project,
group=group, domain=domain)
return manager.revoke(role, user=user, project=project,
group=group, domain=domain)
def remove_tenant_user(request, project=None, user=None, domain=None):
@ -907,9 +798,8 @@ def ec2_manager(request):
if hasattr(client, 'ec2'):
return client.ec2
# Keystoneclient 4.0 was released without the ec2 creds manager.
from keystoneclient.v2_0 import ec2
return ec2.CredentialsManager(client)
from keystoneclient.v3 import ec2
return ec2.EC2Manager(client)
@profiler.trace

View File

@ -128,7 +128,7 @@ def swift_api(request):
preauthurl=endpoint,
cacert=cacert,
insecure=insecure,
auth_version="2.0")
auth_version="3")
@profiler.trace

View File

@ -19,8 +19,6 @@ from django.urls import reverse
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from openstack_auth import utils
from horizon import exceptions
from horizon import forms
from horizon import tables
@ -121,7 +119,6 @@ def _get_context(request):
auth_url = api.base.url_for(request,
'identity',
endpoint_type='publicURL')
auth_url, url_fixed = utils.fix_auth_url_version_prefix(auth_url)
interface = 'public'
region = getattr(request.user, 'services_region', '')
app_cred = request.session['application_credential']

View File

@ -30,9 +30,6 @@ class Domains(horizon.Panel):
return keystone.VERSIONS.active >= 3
def can_access(self, context):
if keystone.VERSIONS.active < 3:
return super(Domains, self).can_access(context)
request = context['request']
domain_token = request.session.get('domain_token')
return super(Domains, self).can_access(context) and domain_token

View File

@ -149,11 +149,8 @@ class ModifyQuotas(tables.LinkAction):
policy_rules = (('compute', "os_compute_api:os-quota-sets:update"),)
def allowed(self, request, datum):
if api.keystone.VERSIONS.active < 3:
return True
else:
return (api.keystone.is_cloud_admin(request) and
quotas.enabled_quotas(request))
return (api.keystone.is_cloud_admin(request) and
quotas.enabled_quotas(request))
def get_link_url(self, project):
step = 'update_quotas'
@ -198,13 +195,10 @@ class DeleteTenantsAction(policy.PolicyTargetMixin, tables.DeleteAction):
class TenantFilterAction(tables.FilterAction):
if api.keystone.VERSIONS.active < 3:
filter_type = "query"
else:
filter_type = "server"
filter_choices = (('name', _("Project Name ="), True),
('id', _("Project ID ="), True),
('enabled', _("Enabled ="), True, _('e.g. Yes/No')))
filter_type = "server"
filter_choices = (('name', _("Project Name ="), True),
('id', _("Project ID ="), True),
('enabled', _("Enabled ="), True, _('e.g. Yes/No')))
class UpdateRow(tables.Row):

View File

@ -21,7 +21,6 @@ from openstack_dashboard import policy
ENABLE = 0
DISABLE = 1
KEYSTONE_V2_ENABLED = api.keystone.VERSIONS.active < 3
class CreateUserLink(tables.LinkAction):
@ -186,7 +185,6 @@ class UsersTable(tables.DataTable):
form_field=forms.CharField(required=False))
description = tables.Column(lambda obj: getattr(obj, 'description', None),
verbose_name=_('Description'),
hidden=KEYSTONE_V2_ENABLED,
form_field=forms.CharField(
widget=forms.Textarea(attrs={'rows': 4}),
required=False))

View File

@ -16,8 +16,6 @@ from django.conf import settings
from django.template.defaultfilters import title
from django.utils.translation import ugettext_lazy as _
from openstack_auth import utils
from horizon import tables
from openstack_dashboard import api
from openstack_dashboard.dashboards.project.api_access import forms
@ -63,8 +61,7 @@ class DownloadOpenRC(tables.LinkAction):
url = "horizon:project:api_access:openrc"
def allowed(self, request, datum=None):
return (settings.SHOW_OPENRC_FILE and
utils.get_keystone_version() >= 3)
return settings.SHOW_OPENRC_FILE
class ViewCredentials(tables.LinkAction):

View File

@ -24,8 +24,6 @@ from django.template.loader import render_to_string
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from openstack_auth import utils
from horizon import exceptions
from horizon import forms
from horizon import messages
@ -131,9 +129,6 @@ def download_rc_file(request):
except KeyError:
project_domain_id = ''
context['project_domain_id'] = project_domain_id
# sanity fix for removing v2.0 from the url if present
context['auth_url'], _ = utils.fix_auth_url_version_prefix(
context['auth_url'])
context['os_identity_api_version'] = 3
context['os_auth_version'] = 3
return _download_rc_file_for_template(request, context, template)
@ -150,14 +145,9 @@ def download_clouds_yaml_file(request):
region_tuple[1] for region_tuple in settings.AVAILABLE_REGIONS
]
if utils.get_keystone_version() >= 3:
# make v3 specific changes
context['user_domain_name'] = request.user.user_domain_name
# sanity fix for removing v2.0 from the url if present
context['auth_url'], _ = utils.fix_auth_url_version_prefix(
context['auth_url'])
context['os_identity_api_version'] = 3
context['os_auth_version'] = 3
context['user_domain_name'] = request.user.user_domain_name
context['os_identity_api_version'] = 3
context['os_auth_version'] = 3
return _download_rc_file_for_template(request, context, template,
'clouds.yaml')

View File

@ -15,7 +15,6 @@ from os.path import join
from os import remove
from horizon.test import firefox_binary
from openstack_dashboard.test.integration_tests import decorators
from openstack_dashboard.test.integration_tests import helpers
@ -41,27 +40,6 @@ class TestDownloadRCFile(helpers.AdminTestCase):
self.addCleanup(cleanup)
@decorators.skip_because(bugs=['1792028'])
def test_download_rc_v2_file(self):
"""This is a basic scenario test:
Steps:
1) Login to Horizon Dashboard as admin user
2) Navigate to Project > API Access tab
3) Click on "Download OpenStack RC File" dropdown button
4) Click on "OpenStack RC File (Identity API v2.0" button
5) File named by template "<tenant_name>-openrc.sh" must be downloaded
6) Check that username, tenant name and tenant id correspond to current
username, tenant name and tenant id
"""
api_access_page = self.home_pg.\
go_to_project_apiaccesspage()
api_access_page.download_openstack_rc_file(
2, self._directory, self._openrc_template)
cred_dict = api_access_page.get_credentials_from_file(
2, self._directory, self._openrc_template)
self.assertEqual(cred_dict, self.actual_dict)
def test_download_rc_v3_file(self):
"""This is a basic scenario test:

View File

@ -29,10 +29,10 @@ class KeystoneRestTestCase(test.TestCase):
@test.create_mocks({api.keystone: ['get_version']})
def test_version_get(self):
request = self.mock_rest_request()
self.mock_get_version.return_value = '2.0'
self.mock_get_version.return_value = '3'
response = keystone.Version().get(request)
self.assertStatusCode(response, 200)
self.assertEqual(response.json, {"version": "2.0"})
self.assertEqual(response.json, {"version": "3"})
self.mock_get_version.assert_called_once_with()
#

View File

@ -237,7 +237,7 @@ class ApiVersionTests(test.TestCase):
self.previous_settings = settings.OPENSTACK_API_VERSIONS
settings.OPENSTACK_API_VERSIONS = {
"data-processing": 1.1,
"identity": "2.0",
"identity": "3",
"volume": 1
}
# Make sure cached data from other tests doesn't interfere

View File

@ -0,0 +1,7 @@
---
upgrade:
- |
Keystone API V2 support has been dropped in Train release. Keystone V2 API
support was deprecated in Stein release. If you use Keystone V2 before, you
should update the `OPENSTACK_API_VERSIONS` configuration option to use
Keystone V3 API.