Move the auth stuff to gestion/

- The login views are in `gestion/`
- The templates are under `gestion/templates/gestion/`
- `cof/shared.py` moves to `gestion/` and is splitted into 3 files:
    - The auth backends are in `backends.py`.
    - The context_processor is in `context_processor.py`
    - The LOCK/UNLOCK functions remain in `shared.py`
This commit is contained in:
Martin Pépin 2017-02-12 14:14:08 +01:00
parent 50b667993f
commit a28c00e474
16 changed files with 156 additions and 147 deletions

View file

@ -15,9 +15,9 @@ from django.core.validators import MinLengthValidator
from .models import CofProfile, EventCommentValue, \
CalendarSubscription, Club
from .widgets import TriStateCheckbox
from .shared import lock_table, unlock_table
from gestion.models import Profile
from gestion.shared import lock_table, unlock_table
from bda.models import Spectacle

View file

@ -18,9 +18,10 @@ from .petits_cours_models import (
PetitCoursAbility, PetitCoursSubject
)
from .decorators import buro_required
from .shared import lock_table, unlock_tables
from .petits_cours_forms import DemandeForm, MatieresFormSet
from gestion.shared import lock_table, unlock_tables
class DemandeListView(ListView):
model = PetitCoursDemande

View file

@ -11,7 +11,9 @@
</a>
<div class="secondary">
<span class="hidden-xxs">&nbsp;&nbsp;|&nbsp; </span>
<span><a href="{% url "cof.views.logout" %}">Se déconnecter&nbsp;<span class="glyphicon glyphicon-log-out"></span></a></span>
<span><a href="{% url "gestion:logout" %}">
Se déconnecter&nbsp;<span class="glyphicon glyphicon-log-out"></span>
</a></span>
</div>
<h2 class="member-status">{% if user.first_name %}{{ user.first_name }}{% else %}<tt>{{ user.username }}</tt>{% endif %}, {% if user.profile.is_cof %}<tt class="user-is-cof">au COF{% else %}<tt class="user-is-not-cof">non-COF{% endif %}</tt></h2>
</div><!-- /.container -->

View file

@ -1,14 +0,0 @@
{% extends "base_title.html" %}
{% block realcontent %}
{% if error_type == "use_clipper_login" %}
<h2><strong>Votre identifiant est lié à un compte <tt>clipper</tt></strong></h2>
<p>Veuillez vous connecter à l'aide de votre <a href="{% url 'cas_login_view' %}">compte <tt>clipper</tt></a></p>
{% elif error_type == "no_password" %}
<h2><strong>Votre compte n'a pas de mot de passe associé</strong></h2>
<p>Veuillez <a href="mailto:cof@clipper.ens.fr">nous contacter</a> pour que nous en définissions un et que nous vous le transmettions !</p>
{% else %}
<h1><strong>{{ error_title }}</strong></h1>
<p>{{ error_description }}</p>
{% endif %}
{% endblock %}

View file

@ -9,7 +9,6 @@ from custommail.shortcuts import send_custom_mail
from django.shortcuts import redirect, get_object_or_404, render
from django.http import Http404, HttpResponse, HttpResponseForbidden
from django.contrib.auth.decorators import login_required
from django.contrib.auth.views import login as django_login_view
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.utils import timezone
@ -50,50 +49,6 @@ def home(request):
return render(request, "home.html", data)
def login(request):
if request.user.is_authenticated():
return redirect("cof.views.home")
context = {}
if request.method == "GET" and 'next' in request.GET:
context['next'] = request.GET['next']
return render(request, "login_switch.html", context)
def login_ext(request):
if request.method == "POST" and "username" in request.POST:
try:
user = User.objects.get(username=request.POST["username"])
if not user.has_usable_password() or user.password in ("", "!"):
profile, created = CofProfile.objects.get_or_create(user=user)
if profile.login_clipper:
return render(request, "error.html",
{"error_type": "use_clipper_login"})
else:
return render(request, "error.html",
{"error_type": "no_password"})
except User.DoesNotExist:
pass
context = {}
if request.method == "GET" and 'next' in request.GET:
context['next'] = request.GET['next']
if request.method == "POST" and 'next' in request.POST:
context['next'] = request.POST['next']
return django_login_view(request, template_name='login.html',
extra_context=context)
@login_required
def logout(request):
try:
profile = request.user.profile
except CofProfile.DoesNotExist:
profile, created = CofProfile.objects.get_or_create(user=request.user)
if profile.login_clipper:
return redirect("django_cas_ng.views.logout")
else:
return redirect("django.contrib.auth.views.logout")
@login_required
def survey(request, survey_id):
survey = get_object_or_404(Survey, id=survey_id)

View file

@ -33,7 +33,6 @@ INSTALLED_APPS = (
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'grappelli',
'django.contrib.admin',
'django.contrib.admindocs',
'autocomplete_light',
@ -80,7 +79,7 @@ TEMPLATES = [
'django.core.context_processors.i18n',
'django.core.context_processors.media',
'django.core.context_processors.static',
'cof.shared.context_processor',
'gestion.context_processors.context_processor',
'kfet.context_processors.auth',
],
},
@ -130,9 +129,6 @@ MEDIA_URL = '/media/'
# Various additional settings
SITE_ID = 1
GRAPPELLI_ADMIN_HEADLINE = "GestioCOF"
GRAPPELLI_ADMIN_TITLE = "<a href=\"/\">GestioCOF</a>"
MAIL_DATA = {
'petits_cours': {
'FROM': "Le COF <cof@ens.fr>",
@ -146,7 +142,7 @@ MAIL_DATA = {
'REPLYTO': 'BdA-Revente <bda-revente@ens.fr>'},
}
LOGIN_URL = "cof-login"
LOGIN_URL = "gestion:login"
LOGIN_REDIRECT_URL = "home"
CAS_SERVER_URL = 'https://cas.eleves.ens.fr/'
@ -155,7 +151,7 @@ CAS_REDIRECT_URL = '/'
CAS_EMAIL_FORMAT = "%s@clipper.ens.fr"
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'cof.shared.COFCASBackend',
'gestion.backends.COFCASBackend',
'kfet.backends.GenericTeamBackend',
)

View file

@ -10,16 +10,16 @@ from django.conf import settings
from django.conf.urls import include, url
from django.conf.urls.static import static
from django.contrib import admin
from django.views.generic.base import TemplateView
from django.contrib.auth import views as django_views
from django_cas_ng import views as django_cas_views
from cof import views as cof_views, csv_views
from cof import views as cof_views
from cof.urls import export_patterns, petitcours_patterns, \
surveys_patterns, events_patterns, calendar_patterns, \
clubs_patterns
from cof.autocomplete import autocomplete
from gestion import views as gestion_views
autocomplete_light.autodiscover()
admin.autodiscover()
@ -28,6 +28,10 @@ urlpatterns = [
url(r'^$', cof_views.home, name='home'),
# The common views
url(r"^", include("gestion.urls", namespace='gestion')),
# Admin urls
url(r'^admin/logout/', gestion_views.logout),
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)),
# Le BdA
url(r'^bda/', include('bda.urls')),
# Les exports
@ -42,15 +46,6 @@ urlpatterns = [
url(r'^calendar/', include(calendar_patterns)),
# Clubs
url(r'^clubs/', include(clubs_patterns)),
# Authentification
url(r'^cof/denied$', TemplateView.as_view(template_name='cof-denied.html'),
name="cof-denied"),
url(r'^cas/login$', django_cas_views.login, name="cas_login_view"),
url(r'^cas/logout$', django_cas_views.logout),
url(r'^outsider/login$', cof_views.login_ext),
url(r'^outsider/logout$', django_views.logout, {'next_page': 'home'}),
url(r'^login$', cof_views.login, name="cof-login"),
url(r'^logout$', cof_views.logout),
# Infos persos
url(r'^outsider/password-change$', django_views.password_change),
url(r'^outsider/password-change-done$',
@ -67,14 +62,6 @@ urlpatterns = [
# Autocompletion
url(r'^autocomplete/registration$', autocomplete),
url(r'^autocomplete/', include('autocomplete_light.urls')),
# Interface admin
url(r'^admin/logout/', cof_views.logout),
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/(?P<app_label>[\d\w]+)/(?P<model_name>[\d\w]+)/csv/',
csv_views.admin_list_export,
{'fields': ['username', ]}),
url(r'^admin/', include(admin.site.urls)),
url(r'^grappelli/', include('grappelli.urls')),
# Liens utiles du COF et du BdA
url(r'^utile_cof$', cof_views.utile_cof),
url(r'^utile_bda$', cof_views.utile_bda),

View file

@ -1,17 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from django.contrib.sites.models import Site
from django.conf import settings
from django_cas_ng.backends import CASBackend
from django_cas_ng.utils import get_cas_client
from django.contrib.auth import get_user_model
from django.db import connection
from .models import CofProfile
from gestion.models import Profile
User = get_user_model()
@ -32,9 +26,12 @@ class COFCASBackend(CASBackend):
# éviter les doublons.
username = username.strip().lower()
profiles = CofProfile.objects.filter(login_clipper=username)
profiles = Profile.objects.filter(login_clipper=username)
if len(profiles) > 0:
profile = profiles.order_by('-is_cof')[0]
# XXX. We have to deal with multiple profiles, this should not
# happen
# profile = profiles.order_by('-is_cof')[0]
profile = profiles.first()
user = profile.user
return user
try:
@ -50,49 +47,11 @@ class COFCASBackend(CASBackend):
user = self.authenticate_cas(ticket, service, request)
if user is None:
return user
try:
profile = user.profile
except CofProfile.DoesNotExist:
profile, created = CofProfile.objects.get_or_create(user=user)
profile.save()
if not profile.login_clipper:
profile.login_clipper = user.username
profile.save()
if not user.email:
user.email = settings.CAS_EMAIL_FORMAT % profile.login_clipper
user.save()
if profile.is_buro and not user.is_staff:
user.is_staff = True
user.save()
return user
def context_processor(request):
'''Append extra data to the context of the given request'''
data = {
"user": request.user,
"site": Site.objects.get_current(),
}
return data
def lock_table(*models):
query = "LOCK TABLES "
for i, model in enumerate(models):
table = model._meta.db_table
if i > 0:
query += ", "
query += "%s WRITE" % table
cursor = connection.cursor()
cursor.execute(query)
row = cursor.fetchone()
return row
def unlock_tables(*models):
cursor = connection.cursor()
cursor.execute("UNLOCK TABLES")
row = cursor.fetchone()
return row
unlock_table = unlock_tables

View file

@ -0,0 +1,10 @@
from django.contrib.sites.models import Site
def context_processor(request):
'''Append extra data to the context of the given request'''
data = {
"user": request.user,
"site": Site.objects.get_current(),
}
return data

27
gestion/shared.py Normal file
View file

@ -0,0 +1,27 @@
"""
Locking/unlocking tools to prevent tables to be corrupted
"""
from django.db import connection
def lock_table(*models):
query = "LOCK TABLES "
for i, model in enumerate(models):
table = model._meta.db_table
if i > 0:
query += ", "
query += "%s WRITE" % table
cursor = connection.cursor()
cursor.execute(query)
row = cursor.fetchone()
return row
def unlock_tables(*models):
cursor = connection.cursor()
cursor.execute("UNLOCK TABLES")
row = cursor.fetchone()
return row
unlock_table = unlock_tables

View file

@ -0,0 +1,20 @@
{% extends "base_title.html" %}
{% block realcontent %}
{% if error_type == "use_clipper_login" %}
<h2><strong>Votre identifiant est lié à un compte <tt>clipper</tt></strong></h2>
<p>
Veuillez vous connecter à l'aide de votre
<a href="{% url 'gestion:cas_login' %}">compte <tt>clipper</tt></a>
</p>
{% elif error_type == "no_password" %}
<h2><strong>Votre compte n'a pas de mot de passe associé</strong></h2>
<p>
Veuillez <a href="mailto:cof@clipper.ens.fr">nous contacter</a> pour que
nous en définissions un et que nous vous le transmettions !
</p>
{% else %}
<h1><strong>{{ error_title }}</strong></h1>
<p>{{ error_description }}</p>
{% endif %}
{% endblock %}

View file

@ -15,7 +15,7 @@
<p class="error">Identifiants incorrects.</p>
{% endif %}
<form class="form-horizontal" method="post"
action="{% url 'cof.views.login_ext' %}?next={{ next|urlencode }}">
action="{% url 'gestion:login_ext' %}?next={{ next|urlencode }}">
{% csrf_token %}
<div class="form-group">
<input class="form-control" id="id_username" maxlength="254" name="username" type="text" placeholder="Nom d'utilisateur">

View file

@ -12,13 +12,13 @@
<div class="container-fluid">
<div class="row" style="margin:0;">
<a aria-label="Compte clipper"
href="{% url 'django_cas_ng.views.login' %}?next={{ next|urlencode }}">
href="{% url 'gestion:cas_login' %}?next={{ next|urlencode }}">
<div class="col-xs-12 col-sm-6" id="login_clipper">
Compte clipper
</div>
</a>
<a aria-label="Extérieur"
href="{% url 'cof.views.login_ext' %}?next={{ next|urlencode }}">
href="{% url 'gestion:login_ext' %}?next={{ next|urlencode }}">
<div class="col-xs-12 col-sm-6" id="login_outsider">
Extérieur
</div>

View file

@ -1,8 +1,24 @@
from django.conf.urls import url
from django.conf.urls import url, include
from django.views.generic.base import TemplateView
from django.contrib.auth import views as django_views
from django.contrib import admin
from django_cas_ng import views as django_cas_views
from . import views
urlpatterns = [
# Profile edition
url(r"^profile/?$", views.profile, name="profile"),
# Authentication
url(r'^cof/denied$',
TemplateView.as_view(template_name='cof-denied.html'),
name="denied"),
url(r'^cas/login$', django_cas_views.login, name="cas_login"),
url(r'^cas/logout$', django_cas_views.logout, name="cas_logout"),
url(r'^outsider/login$', views.login_ext, name="login_ext"),
url(r'^outsider/logout$', django_views.logout, {'next_page': 'home'}),
url(r'^login$', views.login, name="login"),
url(r'^logout$', views.logout, name="logout"),
]

View file

@ -1,9 +1,59 @@
from django.shortcuts import render
"""
The common views of the different organisations.
- Authentication
- Profile edition
"""
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.contrib.auth.views import (
login as django_login, logout as django_logout
)
from .forms import ProfileForm, UserForm
def login(request):
if request.user.is_authenticated():
return redirect("cof.views.home")
context = {}
# Fetch the next page from the request data
if request.method == "GET" and 'next' in request.GET:
context['next'] = request.GET['next']
return render(request, "gestion/login_switch.html", context)
def login_ext(request):
if request.method == "POST" and "username" in request.POST:
try:
user = User.objects.get(username=request.POST["username"])
if user.profile.login_clipper:
return render(request, "gestion/error.html",
{"error_type": "use_clipper_login"})
if not user.has_usable_password() or user.password in ("", "!"):
return render(request, "gestion/error.html",
{"error_type": "no_password"})
except User.DoesNotExist:
pass
context = {}
# Fetch the next page from the request data
if request.method == "GET" and 'next' in request.GET:
context['next'] = request.GET['next']
if request.method == "POST" and 'next' in request.POST:
context['next'] = request.POST['next']
return django_login(request, template_name='gestion/login.html',
extra_context=context)
@login_required
def logout(request):
if request.user.profile.login_clipper:
return redirect("gestion:cas_logout")
else:
return django_logout(request)
@login_required
def profile(request):
success = False

View file

@ -51,7 +51,7 @@
</li>
{% endif %}
{% if user.is_authenticated %}
<li><a href="{% url 'cof.views.logout' %}?next=/k-fet/" title="Déconnexion"><span class="glyphicon glyphicon-log-out"></span></a></li>
<li><a href="{% url 'gestion:logout' %}?next=/k-fet/" title="Déconnexion"><span class="glyphicon glyphicon-log-out"></span></a></li>
{% endif %}
</ul>
</div>