From 77fc5b59885fcba2e9a02b96789cff09fde9806a Mon Sep 17 00:00:00 2001 From: Valentin Samir Date: Thu, 11 Jun 2015 23:04:26 +0200 Subject: [PATCH] Compatibility with different session backend + admin layout --- cas_server/admin.py | 10 +++++++ cas_server/forms.py | 15 ++++++---- .../management/commands/cas_clean_sessions.py | 11 ++++++++ .../migrations/0021_auto_20150611_2102.py | 28 +++++++++++++++++++ cas_server/models.py | 28 +++++++++++++------ cas_server/utils.py | 6 ---- cas_server/views.py | 8 +++--- 7 files changed, 83 insertions(+), 23 deletions(-) create mode 100644 cas_server/management/commands/cas_clean_sessions.py create mode 100644 cas_server/migrations/0021_auto_20150611_2102.py diff --git a/cas_server/admin.py b/cas_server/admin.py index f2e391f..a74e51a 100644 --- a/cas_server/admin.py +++ b/cas_server/admin.py @@ -14,25 +14,35 @@ from .models import ServiceTicket, ProxyTicket, ProxyGrantingTicket, User, Servi from .models import Username, ReplaceAttributName, ReplaceAttributValue, FilterAttributValue from .forms import TicketForm +tickets_readonly_fields=('validate', 'service', 'service_pattern', 'creation', 'renew', 'single_log_out', 'value') +tickets_fields = ('validate', 'service', 'service_pattern', 'creation', 'renew', 'single_log_out') class ServiceTicketInline(admin.TabularInline): """`ServiceTicket` in admin interface""" model = ServiceTicket extra = 0 form = TicketForm + readonly_fields = tickets_readonly_fields + fields = tickets_fields class ProxyTicketInline(admin.TabularInline): """`ProxyTicket` in admin interface""" model = ProxyTicket extra = 0 form = TicketForm + readonly_fields = tickets_readonly_fields + fields = tickets_fields class ProxyGrantingInline(admin.TabularInline): """`ProxyGrantingTicket` in admin interface""" model = ProxyGrantingTicket extra = 0 form = TicketForm + readonly_fields = tickets_readonly_fields + fields = tickets_fields[1:] class UserAdmin(admin.ModelAdmin): """`User` in admin interface""" inlines = (ServiceTicketInline, ProxyTicketInline, ProxyGrantingInline) + readonly_fields=('username', 'date', "session_key") + fields = ('username', 'date', "session_key") class UsernamesInline(admin.TabularInline): """`Username` in admin interface""" diff --git a/cas_server/forms.py b/cas_server/forms.py index 8ea90a9..ef12873 100644 --- a/cas_server/forms.py +++ b/cas_server/forms.py @@ -20,7 +20,7 @@ import models class UserCredential(forms.Form): """Form used on the login page to retrive user credentials""" username = forms.CharField(label=_('login')) - service = forms.CharField(widget=forms.HiddenInput(), required=False) + service = forms.CharField(label=_('service'), widget=forms.HiddenInput(), required=False) password = forms.CharField(label=_('password'), widget=forms.PasswordInput) lt = forms.CharField(widget=forms.HiddenInput(), required=False) method = forms.CharField(widget=forms.HiddenInput(), required=False) @@ -34,12 +34,17 @@ class UserCredential(forms.Form): cleaned_data = super(UserCredential, self).clean() auth = utils.import_attr(settings.CAS_AUTH_CLASS)(cleaned_data.get("username")) if auth.test_password(cleaned_data.get("password")): - session = utils.get_session(self.request) try: - user = models.User.objects.get(username=auth.username, session=session) + user = models.User.objects.get( + username=auth.username, + session_key=self.request.session_key + ) user.save() except models.User.DoesNotExist: - user = models.User.objects.create(username=auth.username, session=session) + user = models.User.objects.create( + username=auth.username, + session_key=self.request.session_key + ) user.save() else: raise forms.ValidationError(_(u"Bad user")) @@ -50,4 +55,4 @@ class TicketForm(forms.ModelForm): class Meta: model = models.Ticket exclude = [] - service = forms.CharField(widget=forms.TextInput) + service = forms.CharField(label=_('service'), widget=forms.TextInput) diff --git a/cas_server/management/commands/cas_clean_sessions.py b/cas_server/management/commands/cas_clean_sessions.py new file mode 100644 index 0000000..95594be --- /dev/null +++ b/cas_server/management/commands/cas_clean_sessions.py @@ -0,0 +1,11 @@ +from django.core.management.base import BaseCommand, CommandError +from django.utils.translation import ugettext_lazy as _ + +from ... import models + +class Command(BaseCommand): + args = '' + help = _(u"Clean deleted sessions") + + def handle(self, *args, **options): + models.User.clean_deleted_sessions() diff --git a/cas_server/migrations/0021_auto_20150611_2102.py b/cas_server/migrations/0021_auto_20150611_2102.py new file mode 100644 index 0000000..c1c0aa1 --- /dev/null +++ b/cas_server/migrations/0021_auto_20150611_2102.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('cas_server', '0020_auto_20150609_1917'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='session_key', + field=models.CharField(max_length=40, null=True, blank=True), + preserve_default=True, + ), + migrations.AlterUniqueTogether( + name='user', + unique_together=set([('username', 'session_key')]), + ), + migrations.RemoveField( + model_name='user', + name='session', + ), + ] diff --git a/cas_server/models.py b/cas_server/models.py index e9399e7..75d4814 100644 --- a/cas_server/models.py +++ b/cas_server/models.py @@ -17,33 +17,44 @@ from django.db.models import Q from django.contrib import messages from django.utils.translation import ugettext_lazy as _ from django.utils import timezone -from django.contrib.sessions.models import Session from picklefield.fields import PickledObjectField import re import os import sys +from importlib import import_module from datetime import timedelta from concurrent.futures import ThreadPoolExecutor from requests_futures.sessions import FuturesSession import utils +SessionStore = import_module(settings.SESSION_ENGINE).SessionStore + class User(models.Model): """A user logged into the CAS""" class Meta: - unique_together = ("username", "session") - session = models.OneToOneField(Session, related_name="cas_server_user", blank=True, null=True, on_delete=models.SET_NULL) + unique_together = ("username", "session_key") + session_key = models.CharField(max_length=40, blank=True, null=True) username = models.CharField(max_length=30) date = models.DateTimeField(auto_now_add=True, auto_now=True) @classmethod def clean_old_entries(cls): - users = cls.objects.filter(session=None) + users = cls.objects.filter( + date__lt=(timezone.now() - timedelta(seconds=settings.SESSION_COOKIE_AGE)) + ) for user in users: user.logout() users.delete() + @classmethod + def clean_deleted_sessions(cls): + for user in cls.objects.all(): + if not SessionStore(session_key=user.session_key).get('authenticated'): + user.logout() + user.delete() + @property def attributs(self): """return a fresh dict for the user attributs""" @@ -108,6 +119,7 @@ class User(models.Model): single_log_out=service_pattern.single_log_out ) ticket.save() + self.save() return ticket def get_service_url(self, service, service_pattern, renew): @@ -321,7 +333,7 @@ class Ticket(models.Model): TIMEOUT = settings.CAS_TICKET_TIMEOUT def __unicode__(self): - return u"Ticket(%s, %s)" % (self.user, self.service) + return u"Ticket-%s" % self.pk @classmethod def clean_old_entries(cls): @@ -394,13 +406,13 @@ class ServiceTicket(Ticket): PREFIX = settings.CAS_SERVICE_TICKET_PREFIX value = models.CharField(max_length=255, default=utils.gen_st, unique=True) def __unicode__(self): - return u"ServiceTicket(%s, %s, %s)" % (self.user, self.value, self.service) + return u"ServiceTicket-%s" % self.pk class ProxyTicket(Ticket): """A Proxy Ticket""" PREFIX = settings.CAS_PROXY_TICKET_PREFIX value = models.CharField(max_length=255, default=utils.gen_pt, unique=True) def __unicode__(self): - return u"ProxyTicket(%s, %s, %s)" % (self.user, self.value, self.service) + return u"ProxyTicket-%s" % self.pk class ProxyGrantingTicket(Ticket): """A Proxy Granting Ticket""" PREFIX = settings.CAS_PROXY_GRANTING_TICKET_PREFIX @@ -409,7 +421,7 @@ class ProxyGrantingTicket(Ticket): def __unicode__(self): - return u"ProxyGrantingTicket(%s, %s, %s)" % (self.user, self.value, self.service) + return u"ProxyGrantingTicket-%s" % self.pk class Proxy(models.Model): """A list of proxies on `ProxyTicket`""" diff --git a/cas_server/utils.py b/cas_server/utils.py index ee5fa5e..8e76271 100644 --- a/cas_server/utils.py +++ b/cas_server/utils.py @@ -15,7 +15,6 @@ from .default_settings import settings from django.utils.importlib import import_module from django.core.urlresolvers import reverse from django.http import HttpResponseRedirect -from django.contrib.sessions.models import Session import urlparse import urllib @@ -102,8 +101,3 @@ def gen_pgtiou(): def gen_saml_id(): """Generate an saml id""" return _gen_ticket('_') - -def get_session(request): - if not request.session.exists(request.session.session_key): - request.session.create() - return Session.objects.get(session_key=request.session.session_key) diff --git a/cas_server/views.py b/cas_server/views.py index 044fe6a..8a396ee 100644 --- a/cas_server/views.py +++ b/cas_server/views.py @@ -71,7 +71,7 @@ class LogoutMixin(object): try: user = models.User.objects.get( username=self.request.session.get("username"), - session=utils.get_session(self.request) + session_key=self.request.session_key ) user.logout(self.request) user.delete() @@ -156,7 +156,7 @@ class LoginView(View, LogoutMixin): if self.form.is_valid(): self.user = models.User.objects.get( username=self.form.cleaned_data['username'], - session=utils.get_session(self.request) + session_key=self.request.session_key ) request.session.set_expiry(0) request.session["username"] = self.form.cleaned_data['username'] @@ -263,7 +263,7 @@ class LoginView(View, LogoutMixin): try: self.user = models.User.objects.get( username=self.request.session.get("username"), - session=utils.get_session(self.request) + session_key=self.request.session_key ) except models.User.DoesNotExist: self.logout() @@ -351,7 +351,7 @@ class Auth(View): try: user = models.User.objects.get( username=form.cleaned_data['username'], - session=utils.get_session(request) + session_key=self.request.session_key ) # is the service allowed service_pattern = ServicePattern.validate(service)