Logout user if he has no valid tokens

Before this patch, if user's rights were changed
or revoked - there would be "Unauthorized" errors
on every page since user had no rights to view them
because he had no valid tokens in that case.

Now user will be logged out if he has no valid tokens.
Set `escalate` to True (for unauthorized-error)
to always log user out.

Also, now horizon.exceptions.NotAuthorized is a part of
UNAUTHORIZED tuple in the exceptions.py, because this type
of exception is re-raised after handling services unauthorized errors.
Looks like it was missing. Now the horizon.exceptions.NotAuthorized
is handled like all NotAuthorized exceptions.

And horizon_middleware.py in process_exception now generates
logout_reason for cases if user is not authorized.

Conflicts:
	openstack_dashboard/dashboards/project/overview/tests.py

Closes-Bug: #1528967
Closes-Bug: #1252341
Closes-Bug: #1407105
Co-Authored-By: Paul Karikh <pkarikh@mirantis.com>
Change-Id: I417cad936ea80c0569c2f442fc87cbd58745757e
(cherry picked from commit 878c703fd0)
This commit is contained in:
Vlad Okhrimenko 2014-12-17 16:47:16 +02:00 committed by Itxaka
parent 181e43fe00
commit 38dfe3d907
4 changed files with 66 additions and 24 deletions

View File

@ -203,6 +203,7 @@ class HandledException(HorizonException):
UNAUTHORIZED = tuple(HORIZON_CONFIG['exceptions']['unauthorized'])
UNAUTHORIZED += (NotAuthorized,)
NOT_FOUND = tuple(HORIZON_CONFIG['exceptions']['not_found'])
RECOVERABLE = (AlreadyExists, Conflict, NotAvailable, ServiceCatalogException)
RECOVERABLE += tuple(HORIZON_CONFIG['exceptions']['recoverable'])
@ -281,7 +282,8 @@ def handle_recoverable(request, message, redirect, ignore, escalate, handled,
HANDLE_EXC_METHODS = [
{'exc': UNAUTHORIZED, 'handler': handle_unauthorized, 'set_wrap': False},
{'exc': UNAUTHORIZED, 'handler': handle_unauthorized,
'set_wrap': False, 'escalate': True},
{'exc': NOT_FOUND, 'handler': handle_notfound, 'set_wrap': True},
{'exc': RECOVERABLE, 'handler': handle_recoverable, 'set_wrap': True},
]
@ -351,7 +353,8 @@ def handle(request, message=None, redirect=None, ignore=False,
if exc_handler['set_wrap']:
wrap = True
handler = exc_handler['handler']
ret = handler(request, message, redirect, ignore, escalate,
ret = handler(request, message, redirect, ignore,
exc_handler.get('escalate', escalate),
handled, force_silence, force_log,
log_method, log_entry, log_level)
if ret:

View File

@ -158,6 +158,12 @@ class HorizonMiddleware(object):
login_url = request.build_absolute_uri(auth_url)
response = redirect_to_login(next_url, login_url=login_url,
redirect_field_name=field_name)
if isinstance(exception, exceptions.NotAuthorized):
logout_reason = _("Unauthorized. Please try logging in again.")
utils.add_logout_reason(request, response, logout_reason)
# delete messages, created in get_data() method
# since we are going to redirect user to the login page
response.delete_cookie('messages')
if request.is_ajax():
response_401 = http.HttpResponse(status=401)

View File

@ -17,9 +17,11 @@
# under the License.
import json
import logging
import sys
from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME # noqa
from django.core.urlresolvers import reverse
from django.forms import widgets
from django import http
@ -886,9 +888,18 @@ class InstanceTests(helpers.TestCase):
url = reverse('horizon:project:instances:detail',
args=[server.id])
res = self.client.get(url)
self.assertRedirectsNoFollow(res, INDEX_URL)
# Avoid the log message in the test
# when unauthorized exception will be logged
logging.disable(logging.ERROR)
res = self.client.get(url)
logging.disable(logging.NOTSET)
self.assertEqual(302, res.status_code)
self.assertEqual(('Location', settings.TESTSERVER +
settings.LOGIN_URL + '?' +
REDIRECT_FIELD_NAME + '=' + url),
res._headers.get('location', None),)
def test_instance_details_flavor_not_found(self):
server = self.servers.first()

View File

@ -17,7 +17,10 @@
# under the License.
import datetime
import logging
from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME # noqa
from django.core.urlresolvers import reverse
from django import http
from django.utils import timezone
@ -34,7 +37,8 @@ INDEX_URL = reverse('horizon:project:overview:index')
class UsageViewTests(test.TestCase):
def _stub_nova_api_calls(self, nova_stu_enabled=True):
def _stub_nova_api_calls(self, nova_stu_enabled=True,
stu_exception=False):
self.mox.StubOutWithMock(api.nova, 'usage_get')
self.mox.StubOutWithMock(api.nova, 'tenant_absolute_limits')
self.mox.StubOutWithMock(api.nova, 'extension_supported')
@ -42,6 +46,9 @@ class UsageViewTests(test.TestCase):
'SimpleTenantUsage', IsA(http.HttpRequest)) \
.AndReturn(nova_stu_enabled)
if nova_stu_enabled and stu_exception:
self._nova_stu_enabled(stu_exception)
def _stub_cinder_api_calls(self):
self.mox.StubOutWithMock(api.cinder, 'tenant_absolute_limits')
api.cinder.tenant_absolute_limits(IsA(http.HttpRequest)) \
@ -64,6 +71,20 @@ class UsageViewTests(test.TestCase):
api.network.security_group_list(IsA(http.HttpRequest)) \
.AndReturn(self.q_secgroups.list())
def _nova_stu_enabled(self, exception=False):
now = timezone.now()
start = datetime.datetime(now.year, now.month, 1, 0, 0, 0, 0)
end = datetime.datetime(now.year, now.month, now.day, 23, 59, 59, 0)
if exception:
api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id,
start, end) \
.AndRaise(exception)
else:
api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id,
start, end) \
.AndReturn(api.nova.NovaUsage(self.usages.first()))
def test_usage(self):
self._test_usage(nova_stu_enabled=True)
@ -150,32 +171,33 @@ class UsageViewTests(test.TestCase):
self.assertNotContains(res, 'form-inline')
self.assertEqual(usages.limits['maxTotalFloatingIps'], 10)
def test_unauthorized(self):
exc = self.exceptions.nova_unauthorized
now = timezone.now()
self._stub_nova_api_calls()
@test.create_stubs({api.nova: ('usage_get',
'extension_supported')})
def _stub_nova_api_calls_unauthorized(self, exception):
api.nova.extension_supported(
'SimpleTenantUsage', IsA(http.HttpRequest)) \
.AndReturn(True)
api.nova.usage_get(IsA(http.HttpRequest), self.tenant.id,
datetime.datetime(now.year,
now.month,
1, 0, 0, 0, 0),
datetime.datetime(now.year,
now.month,
now.day, 23, 59, 59, 0)) \
.AndRaise(exc)
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
.AndReturn(self.limits['absolute'])
self._stub_neutron_api_calls()
self._stub_cinder_api_calls()
self._nova_stu_enabled(exception)
def test_unauthorized(self):
self._stub_nova_api_calls_unauthorized(
self.exceptions.nova_unauthorized)
self.mox.ReplayAll()
url = reverse('horizon:project:overview:index')
# Avoid the log message in the test
# when unauthorized exception will be logged
logging.disable(logging.ERROR)
res = self.client.get(url)
self.assertTemplateUsed(res, 'project/overview/usage.html')
self.assertMessageCount(res, error=1)
self.assertContains(res, 'Unauthorized:')
logging.disable(logging.NOTSET)
self.assertEqual(302, res.status_code)
self.assertEqual((
'Location', settings.TESTSERVER +
settings.LOGIN_URL + '?' +
REDIRECT_FIELD_NAME + '=' + url),
res._headers.get('location', None),)
def test_usage_csv(self):
self._test_usage_csv(nova_stu_enabled=True)