Display a message on the login page
In some cases, particularly when having to log the user out after performing some action (e.g. password change), we want to display a friendly message on the login screen to explain to the user why they have been redirected to the login page. This adds a function to do so, and uses it in a couple of places: - When updating one's own password using the Settings panel - Session time out - HTTP 401 Change-Id: Ie53c5552159304e1f1304ac6211b3accfd9aa623 Implements: blueprint messages-on-login-page
This commit is contained in:
parent
a6f39bc74a
commit
85f4c8b473
|
@ -34,9 +34,10 @@ from django.http import HttpResponseRedirect # noqa
|
||||||
from django import shortcuts
|
from django import shortcuts
|
||||||
from django.utils.encoding import iri_to_uri # noqa
|
from django.utils.encoding import iri_to_uri # noqa
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.utils.translation import ugettext_lazy as _ # noqa
|
||||||
|
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
|
from horizon.utils import functions as utils
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -62,7 +63,10 @@ class HorizonMiddleware(object):
|
||||||
timestamp = datetime.datetime.now()
|
timestamp = datetime.datetime.now()
|
||||||
if last_activity and (timestamp - last_activity).seconds > timeout:
|
if last_activity and (timestamp - last_activity).seconds > timeout:
|
||||||
request.session.pop('last_activity')
|
request.session.pop('last_activity')
|
||||||
return HttpResponseRedirect(settings.LOGOUT_URL)
|
response = HttpResponseRedirect(settings.LOGOUT_URL)
|
||||||
|
reason = _("Session timed out.")
|
||||||
|
utils.add_logout_reason(request, response, reason)
|
||||||
|
return response
|
||||||
request.session['last_activity'] = timestamp
|
request.session['last_activity'] = timestamp
|
||||||
|
|
||||||
request.horizon = {'dashboard': None,
|
request.horizon = {'dashboard': None,
|
||||||
|
@ -86,12 +90,13 @@ class HorizonMiddleware(object):
|
||||||
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)
|
||||||
|
|
||||||
# TODO(gabriel): Find a way to display an appropriate message to
|
|
||||||
# the user *on* the login form...
|
|
||||||
if request.is_ajax():
|
if request.is_ajax():
|
||||||
response_401 = http.HttpResponse(status=401)
|
response_401 = http.HttpResponse(status=401)
|
||||||
response_401['X-Horizon-Location'] = response['location']
|
response_401['X-Horizon-Location'] = response['location']
|
||||||
return response_401
|
return response_401
|
||||||
|
else:
|
||||||
|
utils.add_logout_reason(request, response, _("Unauthorized."))
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
# If an internal "NotFound" error gets this far, return a real 404.
|
# If an internal "NotFound" error gets this far, return a real 404.
|
||||||
|
|
|
@ -18,6 +18,11 @@
|
||||||
<a href="{% url 'horizon:user_home' %}">{% trans "home page" %}</a></p>
|
<a href="{% url 'horizon:user_home' %}">{% trans "home page" %}</a></p>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if request.COOKIES.logout_reason %}
|
||||||
|
<div class="control-group clearfix error">
|
||||||
|
<span class="help-inline"><p>{{ request.COOKIES.logout_reason }}</p></span>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if next %}<input type="hidden" name="{{ redirect_field_name }}" value="{{ next }}" />{% endif %}
|
{% if next %}<input type="hidden" name="{{ redirect_field_name }}" value="{{ next }}" />{% endif %}
|
||||||
{% include "horizon/common/_form_fields.html" %}
|
{% include "horizon/common/_form_fields.html" %}
|
||||||
|
|
|
@ -60,14 +60,14 @@ class RequestFactoryWithMessages(RequestFactory):
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
req = super(RequestFactoryWithMessages, self).get(*args, **kwargs)
|
req = super(RequestFactoryWithMessages, self).get(*args, **kwargs)
|
||||||
req.user = User()
|
req.user = User()
|
||||||
req.session = []
|
req.session = {}
|
||||||
req._messages = default_storage(req)
|
req._messages = default_storage(req)
|
||||||
return req
|
return req
|
||||||
|
|
||||||
def post(self, *args, **kwargs):
|
def post(self, *args, **kwargs):
|
||||||
req = super(RequestFactoryWithMessages, self).post(*args, **kwargs)
|
req = super(RequestFactoryWithMessages, self).post(*args, **kwargs)
|
||||||
req.user = User()
|
req.user = User()
|
||||||
req.session = []
|
req.session = {}
|
||||||
req._messages = default_storage(req)
|
req._messages = default_storage(req)
|
||||||
return req
|
return req
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import math
|
||||||
|
|
||||||
from django.utils.encoding import force_unicode # noqa
|
from django.utils.encoding import force_unicode # noqa
|
||||||
from django.utils.functional import lazy # noqa
|
from django.utils.functional import lazy # noqa
|
||||||
|
from django.utils import translation
|
||||||
|
|
||||||
|
|
||||||
def _lazy_join(separator, strings):
|
def _lazy_join(separator, strings):
|
||||||
|
@ -15,3 +16,11 @@ def bytes_to_gigabytes(bytes):
|
||||||
# Converts the number of bytes to the next highest number of Gigabytes
|
# Converts the number of bytes to the next highest number of Gigabytes
|
||||||
# For example 5000000 (5 Meg) would return '1'
|
# For example 5000000 (5 Meg) would return '1'
|
||||||
return int(math.ceil(float(bytes) / 1024 ** 3))
|
return int(math.ceil(float(bytes) / 1024 ** 3))
|
||||||
|
|
||||||
|
|
||||||
|
def add_logout_reason(request, response, reason):
|
||||||
|
# Store the translated string in the cookie
|
||||||
|
lang = translation.get_language_from_request(request)
|
||||||
|
with translation.override(lang):
|
||||||
|
reason = unicode(reason).encode('utf-8')
|
||||||
|
response.set_cookie('logout_reason', reason, max_age=30)
|
||||||
|
|
|
@ -14,13 +14,16 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from django.conf import settings # noqa
|
||||||
from django.forms import ValidationError # noqa
|
from django.forms import ValidationError # noqa
|
||||||
|
from django import http
|
||||||
from django.utils.translation import ugettext_lazy as _ # noqa
|
from django.utils.translation import ugettext_lazy as _ # noqa
|
||||||
from django.views.decorators.debug import sensitive_variables # noqa
|
from django.views.decorators.debug import sensitive_variables # noqa
|
||||||
|
|
||||||
from horizon import exceptions
|
from horizon import exceptions
|
||||||
from horizon import forms
|
from horizon import forms
|
||||||
from horizon import messages
|
from horizon import messages
|
||||||
|
from horizon.utils import functions as utils
|
||||||
from horizon.utils import validators
|
from horizon.utils import validators
|
||||||
|
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
|
@ -56,7 +59,10 @@ class PasswordForm(forms.SelfHandlingForm):
|
||||||
api.keystone.user_update_own_password(request,
|
api.keystone.user_update_own_password(request,
|
||||||
data['current_password'],
|
data['current_password'],
|
||||||
data['new_password'])
|
data['new_password'])
|
||||||
messages.success(request, _('Password changed.'))
|
response = http.HttpResponseRedirect(settings.LOGOUT_URL)
|
||||||
|
msg = _("Password changed. Please log in again to continue.")
|
||||||
|
utils.add_logout_reason(request, response, msg)
|
||||||
|
return response
|
||||||
except Exception:
|
except Exception:
|
||||||
exceptions.handle(request,
|
exceptions.handle(request,
|
||||||
_('Unable to change password.'))
|
_('Unable to change password.'))
|
||||||
|
|
|
@ -31,27 +31,39 @@ class ChangePasswordTests(test.TestCase):
|
||||||
@test.create_stubs({api.keystone: ('user_update_own_password', )})
|
@test.create_stubs({api.keystone: ('user_update_own_password', )})
|
||||||
def test_change_password(self):
|
def test_change_password(self):
|
||||||
api.keystone.user_update_own_password(IsA(http.HttpRequest),
|
api.keystone.user_update_own_password(IsA(http.HttpRequest),
|
||||||
'oldpwd',
|
'oldpwd',
|
||||||
'normalpwd',).AndReturn(None)
|
'normalpwd',).AndReturn(None)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
formData = {'method': 'PasswordForm',
|
formData = {'method': 'PasswordForm',
|
||||||
'current_password': 'oldpwd',
|
'current_password': 'oldpwd',
|
||||||
'new_password': 'normalpwd',
|
'new_password': 'normalpwd',
|
||||||
'confirm_password': 'normalpwd'}
|
'confirm_password': 'normalpwd'}
|
||||||
|
|
||||||
res = self.client.post(INDEX_URL, formData)
|
res = self.client.post(INDEX_URL, formData)
|
||||||
|
|
||||||
self.assertNoFormErrors(res)
|
self.assertNoFormErrors(res)
|
||||||
|
|
||||||
def test_change_validation_passwords_not_matching(self):
|
def test_change_validation_passwords_not_matching(self):
|
||||||
|
|
||||||
formData = {'method': 'PasswordForm',
|
formData = {'method': 'PasswordForm',
|
||||||
'current_password': 'currpasswd',
|
'current_password': 'currpasswd',
|
||||||
'new_password': 'testpassword',
|
'new_password': 'testpassword',
|
||||||
'confirm_password': 'doesnotmatch'}
|
'confirm_password': 'doesnotmatch'}
|
||||||
|
|
||||||
res = self.client.post(INDEX_URL, formData)
|
res = self.client.post(INDEX_URL, formData)
|
||||||
|
|
||||||
self.assertFormError(res, "form", None, ['Passwords do not match.'])
|
self.assertFormError(res, "form", None, ['Passwords do not match.'])
|
||||||
|
|
||||||
|
@test.create_stubs({api.keystone: ('user_update_own_password', )})
|
||||||
|
def test_change_password_shows_message_on_login_page(self):
|
||||||
|
api.keystone.user_update_own_password(IsA(http.HttpRequest),
|
||||||
|
'oldpwd',
|
||||||
|
'normalpwd').AndReturn(None)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
formData = {'method': 'PasswordForm',
|
||||||
|
'current_password': 'oldpwd',
|
||||||
|
'new_password': 'normalpwd',
|
||||||
|
'confirm_password': 'normalpwd'}
|
||||||
|
res = self.client.post(INDEX_URL, formData, follow=True)
|
||||||
|
|
||||||
|
info_msg = "Password changed. Please log in again to continue."
|
||||||
|
self.assertContains(res, info_msg)
|
||||||
|
|
Loading…
Reference in New Issue