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. Closes-Bug: #1252341 Closes-Bug: #1407105 Co-Authored-By: Paul Karikh <pkarikh@mirantis.com> Change-Id: I417cad936ea80c0569c2f442fc87cbd58745757e
This commit is contained in:
parent
e69a1c2868
commit
878c703fd0
@ -201,6 +201,7 @@ class HandledException(HorizonException):
|
|||||||
|
|
||||||
|
|
||||||
UNAUTHORIZED = tuple(HORIZON_CONFIG['exceptions']['unauthorized'])
|
UNAUTHORIZED = tuple(HORIZON_CONFIG['exceptions']['unauthorized'])
|
||||||
|
UNAUTHORIZED += (NotAuthorized,)
|
||||||
NOT_FOUND = tuple(HORIZON_CONFIG['exceptions']['not_found'])
|
NOT_FOUND = tuple(HORIZON_CONFIG['exceptions']['not_found'])
|
||||||
RECOVERABLE = (AlreadyExists, Conflict, NotAvailable, ServiceCatalogException)
|
RECOVERABLE = (AlreadyExists, Conflict, NotAvailable, ServiceCatalogException)
|
||||||
RECOVERABLE += tuple(HORIZON_CONFIG['exceptions']['recoverable'])
|
RECOVERABLE += tuple(HORIZON_CONFIG['exceptions']['recoverable'])
|
||||||
@ -279,7 +280,8 @@ def handle_recoverable(request, message, redirect, ignore, escalate, handled,
|
|||||||
|
|
||||||
|
|
||||||
HANDLE_EXC_METHODS = [
|
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': NOT_FOUND, 'handler': handle_notfound, 'set_wrap': True},
|
||||||
{'exc': RECOVERABLE, 'handler': handle_recoverable, 'set_wrap': True},
|
{'exc': RECOVERABLE, 'handler': handle_recoverable, 'set_wrap': True},
|
||||||
]
|
]
|
||||||
@ -346,7 +348,8 @@ def handle(request, message=None, redirect=None, ignore=False,
|
|||||||
if exc_handler['set_wrap']:
|
if exc_handler['set_wrap']:
|
||||||
wrap = True
|
wrap = True
|
||||||
handler = exc_handler['handler']
|
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,
|
handled, force_silence, force_log,
|
||||||
log_method, log_entry, log_level)
|
log_method, log_entry, log_level)
|
||||||
if ret:
|
if ret:
|
||||||
|
@ -158,6 +158,12 @@ class HorizonMiddleware(object):
|
|||||||
login_url = request.build_absolute_uri(auth_url)
|
login_url = request.build_absolute_uri(auth_url)
|
||||||
response = redirect_to_login(next_url, login_url=login_url,
|
response = redirect_to_login(next_url, login_url=login_url,
|
||||||
redirect_field_name=field_name)
|
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():
|
if request.is_ajax():
|
||||||
response_401 = http.HttpResponse(status=401)
|
response_401 = http.HttpResponse(status=401)
|
||||||
|
@ -17,10 +17,12 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import django
|
import django
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib.auth import REDIRECT_FIELD_NAME # noqa
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.forms import widgets
|
from django.forms import widgets
|
||||||
from django import http
|
from django import http
|
||||||
@ -889,9 +891,18 @@ class InstanceTests(helpers.TestCase):
|
|||||||
|
|
||||||
url = reverse('horizon:project:instances:detail',
|
url = reverse('horizon:project:instances:detail',
|
||||||
args=[server.id])
|
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):
|
def test_instance_details_flavor_not_found(self):
|
||||||
server = self.servers.first()
|
server = self.servers.first()
|
||||||
|
@ -17,7 +17,10 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import datetime
|
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.core.urlresolvers import reverse
|
||||||
from django import http
|
from django import http
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
@ -140,18 +143,32 @@ class UsageViewTests(test.TestCase):
|
|||||||
|
|
||||||
self._common_assertions(nova_stu_enabled, maxTotalFloatingIps=10)
|
self._common_assertions(nova_stu_enabled, maxTotalFloatingIps=10)
|
||||||
|
|
||||||
|
@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)
|
||||||
|
self._nova_stu_enabled(exception)
|
||||||
|
|
||||||
def test_unauthorized(self):
|
def test_unauthorized(self):
|
||||||
self._stub_nova_api_calls(
|
self._stub_nova_api_calls_unauthorized(
|
||||||
stu_exception=self.exceptions.nova_unauthorized)
|
self.exceptions.nova_unauthorized)
|
||||||
self._stub_neutron_api_calls()
|
|
||||||
self._stub_cinder_api_calls()
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
url = reverse('horizon:project:overview:index')
|
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)
|
res = self.client.get(url)
|
||||||
self.assertTemplateUsed(res, 'project/overview/usage.html')
|
logging.disable(logging.NOTSET)
|
||||||
self.assertMessageCount(res, error=1)
|
|
||||||
self.assertContains(res, 'Unauthorized:')
|
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):
|
def test_usage_csv(self):
|
||||||
self._test_usage_csv(nova_stu_enabled=True)
|
self._test_usage_csv(nova_stu_enabled=True)
|
||||||
|
Loading…
Reference in New Issue
Block a user