diff --git a/bocal/settings_base.py b/bocal/settings_base.py index 5a3657e..a5f217c 100644 --- a/bocal/settings_base.py +++ b/bocal/settings_base.py @@ -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/ diff --git a/bocal/settings_dev.py b/bocal/settings_dev.py index 5c88c03..94e76f0 100644 --- a/bocal/settings_dev.py +++ b/bocal/settings_dev.py @@ -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 diff --git a/bocal/settings_prod.py b/bocal/settings_prod.py index 4521cd6..766b6ac 100644 --- a/bocal/settings_prod.py +++ b/bocal/settings_prod.py @@ -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 diff --git a/bocal/urls.py b/bocal/urls.py index 1431243..0645196 100644 --- a/bocal/urls.py +++ b/bocal/urls.py @@ -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)), ] diff --git a/mainsite/auth_views.py b/mainsite/auth_views.py new file mode 100644 index 0000000..e73c892 --- /dev/null +++ b/mainsite/auth_views.py @@ -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') diff --git a/mainsite/static/css/plain-site.css b/mainsite/static/css/plain-site.css new file mode 100644 index 0000000..dd9295a --- /dev/null +++ b/mainsite/static/css/plain-site.css @@ -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; +} diff --git a/mainsite/templates/mainsite/login.html b/mainsite/templates/mainsite/login.html new file mode 100644 index 0000000..bcb0508 --- /dev/null +++ b/mainsite/templates/mainsite/login.html @@ -0,0 +1,10 @@ +{% extends "plain_base.html" %} + +{% block content %} + + + + + + +{% endblock content %} diff --git a/mainsite/templates/plain_base.html b/mainsite/templates/plain_base.html new file mode 100644 index 0000000..735971b --- /dev/null +++ b/mainsite/templates/plain_base.html @@ -0,0 +1,17 @@ + + +
+ + + +Login ou mot de passe incorrect
+ {% endif %} + + {% if next %} + {% if user.is_authenticated %} +Accès non autorisé.
+ {% else %} +Merci de vous connecter.
+ {% endif %} + {% endif %} + + +