Make login through CAS and local auth available

So far, CAS-created users don't get any privilege based on rhosts
This commit is contained in:
Théophile Bastian 2017-10-14 16:22:23 +02:00
parent a62272d82e
commit 17e71451fe
9 changed files with 193 additions and 0 deletions

View file

@ -29,6 +29,7 @@ INSTALLED_APPS = [
'django.contrib.staticfiles',
'solo',
'markdownx',
'django_cas_ng',
'mainsite',
'api',
]
@ -41,6 +42,7 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django_cas_ng.middleware.CASMiddleware',
]
ROOT_URLCONF = 'bocal.urls'
@ -83,6 +85,15 @@ AUTH_PASSWORD_VALIDATORS = [
},
]
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'django_cas_ng.backends.CASBackend',
]
CAS_ADMIN_PREFIX = '/yaes5eiS' # we don't want CAS to take over /admin auth
LOGIN_URL = '/accounts/login'
LOGIN_REDIRECT_URL = '/'
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/

View file

@ -33,3 +33,13 @@ USE_TZ = True
STATIC_ROOT = os.path.join(PUBLIC_DIR, 'static')
MEDIA_ROOT = os.path.join(PUBLIC_DIR, 'media')
# Cas
CAS_SERVER_URL = 'https://cas.eleves.ens.fr/'
CAS_VERIFY_URL = 'https://cas.eleves.ens.fr/'
CAS_VERSION = 'CAS_2_SAML_1_0'
CAS_IGNORE_REFERER = True
CAS_FORCE_CHANGE_USERNAME_CASE = 'lower'
CAS_REDIRECT_URL = '/'
CAS_EMAIL_FORMAT = "%s@clipper.ens.fr"
CAS_LOGOUT_COMPLETELY = False

View file

@ -44,3 +44,13 @@ USE_TZ = True
STATIC_ROOT = os.path.join(PUBLIC_DIR, 'static')
MEDIA_ROOT = os.path.join(PUBLIC_DIR, 'media')
# Cas
CAS_SERVER_URL = 'https://example.com/' # FIXME
CAS_VERIFY_URL = 'https://example.com/' # FIXME
CAS_VERSION = 'CAS_2_SAML_1_0' # FIXME
CAS_IGNORE_REFERER = True
CAS_FORCE_CHANGE_USERNAME_CASE = 'lower'
CAS_REDIRECT_URL = '/'
CAS_EMAIL_FORMAT = "%s@clipper.ens.fr" # FIXME
CAS_LOGOUT_COMPLETELY = False

View file

@ -15,14 +15,39 @@ Including another URLconf
"""
from django.conf.urls import url, include
from django.contrib import admin
from django.contrib.auth.decorators import login_required
import django.contrib.auth.views as dj_auth_views
import mainsite.urls
import mainsite.auth_views as auth_views
import markdownx.urls
import api.urls
import django_cas_ng.views
# Force the user to login through the custom login page
admin.site.login = login_required(admin.site.login)
cas_patterns = [
url(r'^login$', django_cas_ng.views.login, name='cas_ng_login'),
url(r'^logout$', django_cas_ng.views.logout, name='cas_ng_logout'),
url(r'^callback$', django_cas_ng.views.callback,
name='cas_ng_proxy_callback'),
]
accounts_patterns = [
url(r'^cas/', include(cas_patterns)),
url(r'^login$', auth_views.login, name='login'),
url(r'^logout$', auth_views.logout, name='logout'),
url(r'^password_login$', dj_auth_views.LoginView.as_view(),
name='password_login'),
]
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^markdownx/', include(markdownx.urls)),
url(r'^api/', include(api.urls)),
url(r'^accounts/', include(accounts_patterns)),
url(r'^', include(mainsite.urls)),
]

42
mainsite/auth_views.py Normal file
View file

@ -0,0 +1,42 @@
from django.shortcuts import render, redirect
from django.urls import reverse
from django.contrib.auth import logout as auth_logout
from django.contrib.auth.decorators import login_required
from urllib.parse import quote as urlquote
def login(req):
if req.user.is_authenticated():
return redirect('homepage')
if req.method == 'GET':
reqDict = req.GET
elif req.method == 'POST':
reqDict = req.POST
if 'next' in reqDict:
nextUrl = reqDict['next']
context = {
'pass_url': '{}?next={}'.format(
reverse('password_login'),
urlquote(nextUrl, safe='')),
'cas_url': '{}?next={}'.format(
reverse('cas_ng_login'),
urlquote(nextUrl, safe='')),
}
else:
context = {
'pass_url': reverse('password_login'),
'cas_url': reverse('cas_ng_login'),
}
return render(req, 'mainsite/login.html', context=context)
@login_required
def logout(req):
CAS_BACKEND_NAME = 'django_cas_ng.backends.CASBackend'
if req.session['_auth_user_backend'] != CAS_BACKEND_NAME:
auth_logout(req)
return redirect('homepage')
return redirect('cas_ng_logout')

View file

@ -0,0 +1,33 @@
body {
background-color: #eba367;
margin-top: 50px;
}
@media (min-height: 480px) {
body {
margin-top: 150px;
}
}
#main {
text-align: center;
}
.login-btn {
padding: 20px;
border-radius: 15px;
background-color: white;
border: 3px solid #9c0635;
color: black;
font-size: 24px;
width: 200px;
height: 100px;
margin: 30px;
}
#content-area {
background-color: white;
width: 500px;
padding: 50px;
margin: auto;
}

View file

@ -0,0 +1,10 @@
{% extends "plain_base.html" %}
{% block content %}
<a href="{{ cas_url }}">
<button class="login-btn">Clipper</button>
</a>
<a href="{{ pass_url }}">
<button class="login-btn">Mot de passe</button>
</a>
{% endblock content %}

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="keywords" lang="fr" content="COF,BOcal,ENS,AEENS">
<title>Le BOcal</title>
<link rel="stylesheet" href="/static/css/plain-site.css">
</head>
<body>
<div id="main">
{% block content %}
{% endblock content %}
</div>
</body>
</html>

View file

@ -0,0 +1,35 @@
{% extends "plain_base.html" %}
{% block content %}
<div id="content-area">
{% if form.errors %}
<p>Login ou mot de passe incorrect</p>
{% endif %}
{% if next %}
{% if user.is_authenticated %}
<p>Accès non autorisé.</p>
{% else %}
<p>Merci de vous connecter.</p>
{% endif %}
{% endif %}
<form method="post" action="{% url 'password_login' %}">
{% csrf_token %}
<table>
<tr>
<td>{{ form.username.label_tag }}</td>
<td>{{ form.username }}</td>
</tr>
<tr>
<td>{{ form.password.label_tag }}</td>
<td>{{ form.password }}</td>
</tr>
</table>
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
</div>
{% endblock %}