from django.contrib import messages from django.contrib.auth import authenticate, get_user_model, login from django.contrib.auth.decorators import permission_required from django.contrib.auth.views import redirect_to_login from django.core.urlresolvers import reverse from django.db.models import Prefetch from django.http import QueryDict from django.shortcuts import get_object_or_404, redirect, render from django.utils.decorators import method_decorator from django.utils.translation import ugettext_lazy as _ from django.views.generic import View from django.views.decorators.http import require_http_methods from kfet.cms.views import get_kfetcms_group_formview_extra from .forms import GroupForm from .models import GenericTeamToken, Group User = get_user_model() class GenericLoginView(View): """ View to authenticate as kfet generic user. It is a 2-step view. First, issue a token if user is a team member and send him to the logout view (for proper disconnect) with callback url to here. Then authenticate the token to log in as the kfet generic user. Token is stored in COOKIES to avoid share it with the authentication provider, which can be external. Session is unusable as it will be cleared on logout. """ TOKEN_COOKIE_NAME = 'kfettoken' @method_decorator(require_http_methods(['GET', 'POST'])) def dispatch(self, request, *args, **kwargs): token = request.get_signed_cookie(self.TOKEN_COOKIE_NAME, None) if not token: if not request.user.has_perm('kfet.is_team'): return redirect_to_login(request.get_full_path()) if request.method == 'POST': # Step 1: set token and logout user. return self.prepare_auth() else: # GET request should not change server/client states. Send a # confirmation template to emit a POST request. return render(request, 'kfet/confirm_form.html', { 'title': _("Ouvrir une session partagée"), 'text': _( "Êtes-vous sûr·e de vouloir ouvrir une session " "partagée ?" ), }) else: # Step 2: validate token. return self.validate_auth(token) def prepare_auth(self): # Issue token. token = GenericTeamToken.objects.create_token() # Prepare callback of logout. here_url = reverse(login_generic) if 'next' in self.request.GET: # Keep given next page. here_qd = QueryDict(mutable=True) here_qd['next'] = self.request.GET['next'] here_url += '?{}'.format(here_qd.urlencode()) logout_url = reverse('cof-logout') logout_qd = QueryDict(mutable=True) logout_qd['next'] = here_url logout_url += '?{}'.format(logout_qd.urlencode(safe='/')) resp = redirect(logout_url) resp.set_signed_cookie( self.TOKEN_COOKIE_NAME, token.token, httponly=True) return resp def validate_auth(self, token): # Authenticate with GenericBackend. user = authenticate(request=self.request, kfet_token=token) if user: # Log in generic user. login(self.request, user) messages.success(self.request, _( "K-Fêt — Ouverture d'une session partagée." )) resp = redirect(self.get_next_url()) else: # Try again. resp = redirect(self.request.get_full_path()) # Prevents blocking due to an invalid COOKIE. resp.delete_cookie(self.TOKEN_COOKIE_NAME) return resp def get_next_url(self): return self.request.GET.get('next', reverse('kfet.kpsul')) login_generic = GenericLoginView.as_view() @permission_required('kfetauth.view_group') def group_index(request): user_pre = Prefetch( 'user_set', queryset=User.objects.select_related('profile__account_kfet'), ) groups = Group.objects.prefetch_related(user_pre) return render(request, 'kfet/group_list.html', { 'groups': groups, }) _group_formview_extras = None def get_group_formview_extras(): global _group_formview_extras if _group_formview_extras is None: _group_formview_extras = [] # Register additional group forms below. _group_formview_extras.append( get_kfetcms_group_formview_extra()) return [extra.copy() for extra in _group_formview_extras] @permission_required('kfetauth.add_group') def group_create(request): group = Group() extras = get_group_formview_extras() if request.method == 'POST': form = GroupForm(request.POST, instance=group) for extra in extras: extra['forms'] = [ form_cls( request.POST, request.FILES, instance=group, **form_kwargs) for form_cls, form_kwargs in extra['form_classes'] ] extra_forms = sum((extra['forms'] for extra in extras), []) if form.is_valid() and all(form.is_valid() for form in extra_forms): group = form.save() for extra_form in extra_forms: extra_form.save() messages.success(request, _("Nouveau groupe : {}").format(group)) return redirect('kfet.group') else: form = GroupForm(instance=group) for extra in extras: extra['forms'] = [ form_cls(instance=group, **form_kwargs) for form_cls, form_kwargs in extra['form_classes'] ] return render(request, 'kfet/group_form.html', { 'form': form, 'extras': extras, }) @permission_required('kfetauth.change_group') def group_update(request, pk): group = get_object_or_404(Group, pk=pk) extras = get_group_formview_extras() if request.method == 'POST': form = GroupForm(request.POST, instance=group) for extra in extras: extra['forms'] = [ form_cls( request.POST, request.FILES, instance=group, **form_kwargs) for form_cls, form_kwargs in extra['form_classes'] ] extra_forms = sum((extra['forms'] for extra in extras), []) if form.is_valid() and all(form.is_valid() for form in extra_forms): group = form.save() for extra_form in extra_forms: extra_form.save() messages.success(request, _("Groupe modifié : {}").format(group)) return redirect('kfet.group') else: form = GroupForm(instance=group) for extra in extras: extra['forms'] = [ form_cls(instance=group, **form_kwargs) for form_cls, form_kwargs in extra['form_classes'] ] return render(request, 'kfet/group_form.html', { 'form': form, 'extras': extras, })