summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Hurley <gabriel@strikeawe.com>2011-11-08 12:20:30 -0800
committerGabriel Hurley <gabriel@strikeawe.com>2011-11-08 14:22:50 -0800
commit67a979ae99fcba2b0f97789ae7a4dc5de8fa084e (patch)
treed1c911f27b9a830c7afc9fb9bd63db60784a987a
parent193e59f8ff041410901cd73f30edb74ca17ed1e3 (diff)
downloadtuskar-ui-67a979ae99fcba2b0f97789ae7a4dc5de8fa084e.tar.gz
Four modest bug fixes.
Sorry these are in one commit, but they all ran one into the other when I discovered them. Fixed 887770 -- corrects an instance of "dash" -> "nova" renaming that was missed in openstack-dashboard/dashboard/views.py Fixed 887768 -- removes duplicate code and adds docstrings to auth_views.py. These changes were previously made but somehow didn't get staged and committed. Fixed 887767 -- the list of tenants in the tenant switcher no longer changes when viewing the Tenants or Users syspanel pages. Fixed 887766 -- corrects a broken bit of if/else logic so that the tenant switch list isn't empty. Change-Id: I0cb6fff83d7279b17233c50ef2d4af2586cca829
-rw-r--r--horizon/horizon/api/keystone.py32
-rw-r--r--horizon/horizon/context_processors.py19
-rw-r--r--horizon/horizon/test.py12
-rw-r--r--horizon/horizon/tests/context_processor_tests.py18
-rw-r--r--horizon/horizon/views/auth.py123
-rw-r--r--openstack-dashboard/dashboard/settings.py1
-rw-r--r--openstack-dashboard/dashboard/templates/_topbar.html2
-rw-r--r--openstack-dashboard/dashboard/views.py2
8 files changed, 87 insertions, 122 deletions
diff --git a/horizon/horizon/api/keystone.py b/horizon/horizon/api/keystone.py
index cdc45516..3c54123c 100644
--- a/horizon/horizon/api/keystone.py
+++ b/horizon/horizon/api/keystone.py
@@ -23,7 +23,9 @@ import logging
from django.conf import settings
from keystoneclient import exceptions as keystone_exceptions
+from keystoneclient import service_catalog
from keystoneclient.v2_0 import client as keystone_client
+from keystoneclient.v2_0 import tokens
from horizon.api.base import *
from horizon.api.deprecated import admin_api
@@ -59,7 +61,7 @@ class Services(APIResourceWrapper):
def keystoneclient(request, username=None, password=None, tenant_id=None,
- token_id=None, endpoint=None):
+ token_id=None, endpoint=None, endpoint_type=None):
"""Returns a client connected to the Keystone backend.
Several forms of authentication are supported:
@@ -82,8 +84,11 @@ def keystoneclient(request, username=None, password=None, tenant_id=None,
user = request.user
if hasattr(request, '_keystone') and \
request._keystone.auth_token == user.token:
+ LOG.debug("Using cached client for token: %s" % user.token)
conn = request._keystone
else:
+ LOG.debug("Creating a new client connection with endpoint: %s."
+ % endpoint)
conn = keystone_client.Client(username=username or user.username,
password=password,
project_id=tenant_id or user.tenant_id,
@@ -95,7 +100,10 @@ def keystoneclient(request, username=None, password=None, tenant_id=None,
# Fetch the correct endpoint for the user type
catalog = getattr(conn, 'service_catalog', None)
if catalog and "serviceCatalog" in catalog.catalog.keys():
- if user.is_admin():
+ if endpoint_type:
+ endpoint = catalog.url_for(service_type='identity',
+ endpoint_type=endpoint_type)
+ elif user.is_admin():
endpoint = catalog.url_for(service_type='identity',
endpoint_type='adminURL')
else:
@@ -137,9 +145,11 @@ def tenant_delete(request, tenant_id):
keystoneclient(request).tenants.delete(tenant_id)
-def tenant_list_for_token(request, token):
- c = keystoneclient(request, token_id=token,
- endpoint=settings.OPENSTACK_KEYSTONE_URL)
+def tenant_list_for_token(request, token, endpoint_type=None):
+ c = keystoneclient(request,
+ token_id=token,
+ endpoint=settings.OPENSTACK_KEYSTONE_URL,
+ endpoint_type=endpoint_type)
return [Tenant(t) for t in c.tenants.list()]
@@ -170,7 +180,17 @@ def token_create_scoped(request, tenant, token):
del request._keystone
c = keystoneclient(request, tenant_id=tenant, token_id=token,
endpoint=settings.OPENSTACK_KEYSTONE_URL)
- scoped_token = c.tokens.authenticate(tenant=tenant, token=token)
+ raw_token = c.tokens.authenticate(tenant=tenant,
+ token=token,
+ return_raw=True)
+ c.service_catalog = service_catalog.ServiceCatalog(raw_token)
+ if request.user.is_admin():
+ c.management_url = c.service_catalog.url_for(service_type='identity',
+ endpoint_type='adminURL')
+ else:
+ c.management_url = c.service_catalog.url_for(service_type='identity',
+ endpoint_type='publicURL')
+ scoped_token = tokens.Token(tokens.TokenManager, raw_token)
return Token(scoped_token)
diff --git a/horizon/horizon/context_processors.py b/horizon/horizon/context_processors.py
index 9f0a9878..eccb0452 100644
--- a/horizon/horizon/context_processors.py
+++ b/horizon/horizon/context_processors.py
@@ -21,12 +21,17 @@
Context processors used by Horizon.
"""
+import logging
+
from django.conf import settings
from django.contrib import messages
from horizon import api
+LOG = logging.getLogger(__name__)
+
+
def horizon(request):
""" The main Horizon context processor. Required for Horizon to function.
@@ -45,15 +50,19 @@ def horizon(request):
context = {}
# Auth/Keystone context
+ context.setdefault('authorized_tenants', [])
if request.user.is_authenticated():
try:
- tenants = api.tenant_list_for_token(request, request.user.token)
- context['tenants'] = tenants
+ tenants = api.tenant_list_for_token(request,
+ request.user.token,
+ endpoint_type='internalURL')
+ context['authorized_tenants'] = tenants
except Exception, e:
if hasattr(request.user, 'message_set'):
- messages.error(request, _("Unable to retrieve tenant list from\
- keystone: %s") % e.message)
- context['tenants'] = []
+ messages.error(request, _("Unable to retrieve tenant list: %s")
+ % e.message)
+ else:
+ LOG.exception('Could not retrieve tenant list.')
# Object Store/Swift context
catalog = getattr(request.user, 'service_catalog', [])
diff --git a/horizon/horizon/test.py b/horizon/horizon/test.py
index 97f512af..969718b9 100644
--- a/horizon/horizon/test.py
+++ b/horizon/horizon/test.py
@@ -66,6 +66,12 @@ class TestCase(django_test.TestCase):
TEST_TOKEN = 'aToken'
TEST_USER = 'test'
TEST_ROLES = [{'name': 'admin', 'id': '1'}]
+ TEST_CONTEXT = {'authorized_tenants': [{'enabled': True,
+ 'name': 'aTenant',
+ 'id': '1',
+ 'description': "None"}],
+ 'object_store_configured': False,
+ 'network_configured': False}
TEST_SERVICE_CATALOG = [
{"endpoints": [{
@@ -107,12 +113,8 @@ class TestCase(django_test.TestCase):
def setUp(self):
self.mox = mox.Mox()
- context_dict = {'tenants': [],
- 'object_store_configured': False,
- 'network_configured': False}
-
self._real_horizon_context_processor = context_processors.horizon
- context_processors.horizon = lambda request: context_dict
+ context_processors.horizon = lambda request: self.TEST_CONTEXT
self._real_get_user_from_request = users.get_user_from_request
self.setActiveUser(token=self.TEST_TOKEN,
diff --git a/horizon/horizon/tests/context_processor_tests.py b/horizon/horizon/tests/context_processor_tests.py
index b4e507d1..06b7e70f 100644
--- a/horizon/horizon/tests/context_processor_tests.py
+++ b/horizon/horizon/tests/context_processor_tests.py
@@ -18,8 +18,12 @@
# License for the specific language governing permissions and limitations
# under the License.
+from django import http
+from mox import IsA
+
from horizon import context_processors
from horizon import test
+from horizon import api
class ContextProcessorTests(test.TestCase):
@@ -32,6 +36,20 @@ class ContextProcessorTests(test.TestCase):
super(ContextProcessorTests, self).tearDown()
self.request.user.service_catalog = self._prev_catalog
+ def test_authorized_tenants(self):
+ tenant_list = self.TEST_CONTEXT['authorized_tenants']
+ self.mox.StubOutWithMock(api, 'tenant_list_for_token')
+ api.tenant_list_for_token(IsA(http.HttpRequest),
+ self.TEST_TOKEN,
+ endpoint_type='internalURL') \
+ .AndReturn(tenant_list)
+ self.mox.ReplayAll()
+
+ context = context_processors.horizon(self.request)
+ self.assertEqual(len(context['authorized_tenants']), 1)
+ tenant = context['authorized_tenants'].pop()
+ self.assertEqual(tenant['id'], self.TEST_TENANT)
+
def test_object_store(self):
# Returns the object store service data when it's in the catalog
context = context_processors.horizon(self.request)
diff --git a/horizon/horizon/views/auth.py b/horizon/horizon/views/auth.py
index 9322b049..6269e221 100644
--- a/horizon/horizon/views/auth.py
+++ b/horizon/horizon/views/auth.py
@@ -29,123 +29,36 @@ from openstackx.api import exceptions as api_exceptions
from horizon import api
from horizon import exceptions
-from horizon import forms
+from horizon import users
+from horizon.base import Horizon
+from horizon.views.auth_forms import Login, LoginWithTenant, _set_session_data
LOG = logging.getLogger(__name__)
-def _is_admin(token):
- for role in token.user['roles']:
- if role['name'].lower() == 'admin':
- return True
- return False
-
-
-def _set_session_data(request, token):
- request.session['admin'] = _is_admin(token)
- request.session['serviceCatalog'] = token.serviceCatalog
- request.session['tenant'] = token.tenant['name']
- request.session['tenant_id'] = token.tenant['id']
- request.session['token'] = token.id
- request.session['user'] = token.user['name']
- request.session['roles'] = token.user['roles']
-
-
-class Login(forms.SelfHandlingForm):
- username = forms.CharField(max_length="20", label=_("User Name"))
- password = forms.CharField(max_length="20", label=_("Password"),
- widget=forms.PasswordInput(render_value=False))
-
- def handle(self, request, data):
- try:
- if data.get('tenant'):
- token = api.token_create(request,
- data.get('tenant'),
- data['username'],
- data['password'])
-
- tenants = api.tenant_list_for_token(request, token.id)
- tenant = None
- for t in tenants:
- if t.id == data.get('tenant'):
- tenant = t
- else:
- token = api.token_create(request,
- '',
- data['username'],
- data['password'])
-
- # Unscoped token
- request.session['unscoped_token'] = token.id
- request.user.username = data['username']
-
- # Get the tenant list, and log in using first tenant
- # FIXME (anthony): add tenant chooser here?
- tenants = api.tenant_list_for_token(request, token.id)
-
- # Abort if there are no valid tenants for this user
- if not tenants:
- messages.error(request,
- _('No tenants present for user: %(user)s') %
- {"user": data['username']})
- return
-
- # Create a token.
- # NOTE(gabriel): Keystone can return tenants that you're
- # authorized to administer but not to log into as a user, so in
- # the case of an Unauthorized error we should iterate through
- # the tenants until one succeeds or we've failed them all.
- while tenants:
- tenant = tenants.pop()
- try:
- token = api.token_create_scoped(request,
- tenant.id,
- token.id)
- break
- except exceptions.Unauthorized as e:
- token = None
- if token is None:
- raise exceptions.Unauthorized(
- _("You are not authorized for any available tenants."))
-
- LOG.info('Login form for user "%s". Service Catalog data:\n%s' %
- (data['username'], token.serviceCatalog))
- _set_session_data(request, token)
-
- return shortcuts.redirect('horizon:nova:overview:index')
-
- except api_exceptions.Unauthorized as e:
- msg = _('Error authenticating: %s') % e.message
- LOG.exception(msg)
- messages.error(request, msg)
- except api_exceptions.ApiException as e:
- messages.error(request,
- _('Error authenticating with keystone: %s') %
- e.message)
-
-
-class LoginWithTenant(Login):
- username = forms.CharField(max_length="20",
- widget=forms.TextInput(attrs={'readonly': 'readonly'}))
- tenant = forms.CharField(widget=forms.HiddenInput())
-
-
def login(request):
- if request.user and request.user.is_authenticated():
- if request.user.is_admin():
- return shortcuts.redirect('horizon:syspanel:overview:index')
- else:
- return shortcuts.redirect('horizon:nova:overview:index')
+ """
+ Logs in a user and redirects them to the URL specified by
+ :func:`horizon.get_user_home`.
+ """
+ if request.user.is_authenticated():
+ user = users.User(users.get_user_from_request(request))
+ return shortcuts.redirect(Horizon.get_user_home(user))
form, handled = Login.maybe_handle(request)
if handled:
return handled
+ # FIXME(gabriel): we don't ship a view named splash
return shortcuts.render(request, 'splash.html', {'form': form})
def switch_tenants(request, tenant_id):
+ """
+ Swaps a user from one tenant to another using the unscoped token from
+ Keystone to exchange scoped tokens for the new tenant.
+ """
form, handled = LoginWithTenant.maybe_handle(
request, initial={'tenant': tenant_id,
'username': request.user.username})
@@ -159,10 +72,12 @@ def switch_tenants(request, tenant_id):
tenant_id,
unscoped_token)
_set_session_data(request, token)
- return shortcuts.redirect('horizon:nova:overview:index')
+ user = users.User(users.get_user_from_request(request))
+ return shortcuts.redirect(Horizon.get_user_home(user))
except exceptions.Unauthorized as e:
messages.error(_("You are not authorized for that tenant."))
+ # FIXME(gabriel): we don't ship switch_tenants.html
return shortcuts.render(request,
'switch_tenants.html', {
'to_tenant': tenant_id,
@@ -170,5 +85,7 @@ def switch_tenants(request, tenant_id):
def logout(request):
+ """ Clears the session and logs the current user out. """
request.session.clear()
+ # FIXME(gabriel): we don't ship a view named splash
return shortcuts.redirect('splash')
diff --git a/openstack-dashboard/dashboard/settings.py b/openstack-dashboard/dashboard/settings.py
index 1b5100be..e61865f7 100644
--- a/openstack-dashboard/dashboard/settings.py
+++ b/openstack-dashboard/dashboard/settings.py
@@ -138,4 +138,3 @@ if DEBUG:
'debug_toolbar.middleware.DebugToolbarMiddleware',)
except ImportError:
logging.info('Running in debug mode without debug_toolbar.')
-
diff --git a/openstack-dashboard/dashboard/templates/_topbar.html b/openstack-dashboard/dashboard/templates/_topbar.html
index b75d38a5..c246cb7e 100644
--- a/openstack-dashboard/dashboard/templates/_topbar.html
+++ b/openstack-dashboard/dashboard/templates/_topbar.html
@@ -15,7 +15,7 @@
<a id="drop_btn" href="#">&nbsp;</a>
<ul id="user_tenant_list">
<li class="title"><h4>{% trans "Available Tenants"%}</h4></li>
- {% for tenant in tenants %}
+ {% for tenant in authorized_tenants %}
{% if tenant.enabled %}
<li><a href="{% url horizon:auth_switch tenant.id %}">{{tenant.name}}</a></li>
{% endif %}
diff --git a/openstack-dashboard/dashboard/views.py b/openstack-dashboard/dashboard/views.py
index fb06c62d..cef66324 100644
--- a/openstack-dashboard/dashboard/views.py
+++ b/openstack-dashboard/dashboard/views.py
@@ -32,7 +32,7 @@ from horizon.views import auth as auth_views
def user_home(user):
if user.admin:
return horizon.get_dashboard('syspanel').get_absolute_url()
- return horizon.get_dashboard('dash').get_absolute_url()
+ return horizon.get_dashboard('nova').get_absolute_url()
@vary.vary_on_cookie