Sunil Mohan Adapa 5ff7339c19
sso, users: Redirect to home page after logout
Closes: #2178.

- Don't bother with the redirection to the next page using the ?next= URL
parameter. Always redirect to the home (index) page.

- Show a message that logout was successful.

- Ensure that SSO cookie is removed.

Tests:

- Logout and notice that redirection has been performed to the home page.

- "Logged out successfully." message is shown.

- When logged as a user with a language set, logging out preserves the language
of the user who was just logged out.

- Login. Click logout while having browser developer tool open. Notice that
Logout request has SSO cookie. The response does not have the cookie set. The
next request is to the home page and it does not have SSO cookie in the request.

- Login to tt-rss app that needs SSO to work. Logout from FreedomBox interface
using another page. Refresh the tt-rss page and notice that user was logged out
and redirect to FreedomBox login page.

- Logout. Again, manually visit the URL
https://10.42.0.203/plinth/accounts/logout/. The page is still required to home
page and success is still shown even though the user is already logged out.

- Repeat the logout test as non-admin user.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2022-01-31 17:23:32 -05:00

113 lines
3.7 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-or-later
"""
Views for the Single Sign On app of FreedomBox.
"""
import logging
import os
import urllib
import axes.utils
from axes.decorators import axes_form_invalid
from django import shortcuts
from django.contrib import messages
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth import logout as auth_logout
from django.contrib.auth.views import LoginView
from django.http import HttpResponseRedirect
from django.utils.translation import gettext as _
from plinth import actions, translation, utils, web_framework
from .forms import AuthenticationForm, CaptchaAuthenticationForm
PRIVATE_KEY_FILE_NAME = 'privkey.pem'
SSO_COOKIE_NAME = 'auth_pubtkt'
KEYS_DIRECTORY = '/etc/apache2/auth-pubtkt-keys'
logger = logging.getLogger(__name__)
def set_ticket_cookie(user, response):
"""Generate and set a mod_auth_pubtkt as a cookie in the provided
response.
"""
tokens = list(map(lambda g: g.name, user.groups.all()))
private_key_file = os.path.join(KEYS_DIRECTORY, PRIVATE_KEY_FILE_NAME)
ticket = actions.superuser_run('auth-pubtkt', [
'generate-ticket', '--uid', user.username, '--private-key-file',
private_key_file, '--tokens', ','.join(tokens)
])
response.set_cookie(SSO_COOKIE_NAME, urllib.parse.quote(ticket))
return response
class SSOLoginView(LoginView):
"""View to login to FreedomBox and set a auth_pubtkt cookie.
Cookie will be used to provide Single Sign On for some other applications.
"""
redirect_authenticated_user = True
template_name = 'login.html'
form_class = AuthenticationForm
def dispatch(self, request, *args, **kwargs):
response = super(SSOLoginView, self).dispatch(request, *args, **kwargs)
if request.user.is_authenticated:
translation.set_language(request, response,
request.user.userprofile.language)
return set_ticket_cookie(request.user, response)
return response
# XXX: Use axes middleware and authentication backend instead of
# axes_form_invalid when axes >= 5.0.0 becomes available in Debian stable.
@axes_form_invalid
def form_invalid(self, *args, **kwargs):
return super(SSOLoginView, self).form_invalid(*args, **kwargs)
class CaptchaLoginView(LoginView):
redirect_authenticated_user = True
template_name = 'login.html'
form_class = CaptchaAuthenticationForm
def dispatch(self, request, *args, **kwargs):
response = super(CaptchaLoginView,
self).dispatch(request, *args, **kwargs)
if not request.POST:
return response
if not request.user.is_authenticated:
return response
# Successful authentication
if utils.is_axes_old():
ip_address = web_framework.get_ip_address_from_request(request)
axes.utils.reset(ip=ip_address)
logger.info(
'Login attempts reset for IP after successful login: %s',
ip_address)
return set_ticket_cookie(request.user, response)
def logout(request):
"""Logout an authenticated user, remove SSO cookie and redirect to home."""
auth_logout(request)
response = shortcuts.redirect('index')
response.delete_cookie(SSO_COOKIE_NAME)
messages.success(request, _('Logged out successfully.'))
return response
def refresh(request):
"""Simulate cookie refresh - redirect logged in user with a new cookie"""
redirect_url = request.GET.get(REDIRECT_FIELD_NAME, '')
response = HttpResponseRedirect(redirect_url)
response.delete_cookie(SSO_COOKIE_NAME)
# Redirect with cookie doesn't work with 300 series
response.status_code = 200
return set_ticket_cookie(request.user, response)