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:
parent
181e43fe00
commit
38dfe3d907
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue