Compatibility with different session backend + admin layout

This commit is contained in:
Valentin Samir 2015-06-11 23:04:26 +02:00
parent 245086f6ef
commit 77fc5b5988
7 changed files with 83 additions and 23 deletions

View File

@ -14,25 +14,35 @@ from .models import ServiceTicket, ProxyTicket, ProxyGrantingTicket, User, Servi
from .models import Username, ReplaceAttributName, ReplaceAttributValue, FilterAttributValue from .models import Username, ReplaceAttributName, ReplaceAttributValue, FilterAttributValue
from .forms import TicketForm 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): class ServiceTicketInline(admin.TabularInline):
"""`ServiceTicket` in admin interface""" """`ServiceTicket` in admin interface"""
model = ServiceTicket model = ServiceTicket
extra = 0 extra = 0
form = TicketForm form = TicketForm
readonly_fields = tickets_readonly_fields
fields = tickets_fields
class ProxyTicketInline(admin.TabularInline): class ProxyTicketInline(admin.TabularInline):
"""`ProxyTicket` in admin interface""" """`ProxyTicket` in admin interface"""
model = ProxyTicket model = ProxyTicket
extra = 0 extra = 0
form = TicketForm form = TicketForm
readonly_fields = tickets_readonly_fields
fields = tickets_fields
class ProxyGrantingInline(admin.TabularInline): class ProxyGrantingInline(admin.TabularInline):
"""`ProxyGrantingTicket` in admin interface""" """`ProxyGrantingTicket` in admin interface"""
model = ProxyGrantingTicket model = ProxyGrantingTicket
extra = 0 extra = 0
form = TicketForm form = TicketForm
readonly_fields = tickets_readonly_fields
fields = tickets_fields[1:]
class UserAdmin(admin.ModelAdmin): class UserAdmin(admin.ModelAdmin):
"""`User` in admin interface""" """`User` in admin interface"""
inlines = (ServiceTicketInline, ProxyTicketInline, ProxyGrantingInline) inlines = (ServiceTicketInline, ProxyTicketInline, ProxyGrantingInline)
readonly_fields=('username', 'date', "session_key")
fields = ('username', 'date', "session_key")
class UsernamesInline(admin.TabularInline): class UsernamesInline(admin.TabularInline):
"""`Username` in admin interface""" """`Username` in admin interface"""

View File

@ -20,7 +20,7 @@ import models
class UserCredential(forms.Form): class UserCredential(forms.Form):
"""Form used on the login page to retrive user credentials""" """Form used on the login page to retrive user credentials"""
username = forms.CharField(label=_('login')) 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) password = forms.CharField(label=_('password'), widget=forms.PasswordInput)
lt = forms.CharField(widget=forms.HiddenInput(), required=False) lt = forms.CharField(widget=forms.HiddenInput(), required=False)
method = 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() cleaned_data = super(UserCredential, self).clean()
auth = utils.import_attr(settings.CAS_AUTH_CLASS)(cleaned_data.get("username")) auth = utils.import_attr(settings.CAS_AUTH_CLASS)(cleaned_data.get("username"))
if auth.test_password(cleaned_data.get("password")): if auth.test_password(cleaned_data.get("password")):
session = utils.get_session(self.request)
try: 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() user.save()
except models.User.DoesNotExist: 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() user.save()
else: else:
raise forms.ValidationError(_(u"Bad user")) raise forms.ValidationError(_(u"Bad user"))
@ -50,4 +55,4 @@ class TicketForm(forms.ModelForm):
class Meta: class Meta:
model = models.Ticket model = models.Ticket
exclude = [] exclude = []
service = forms.CharField(widget=forms.TextInput) service = forms.CharField(label=_('service'), widget=forms.TextInput)

View File

@ -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()

View File

@ -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',
),
]

View File

@ -17,33 +17,44 @@ from django.db.models import Q
from django.contrib import messages from django.contrib import messages
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils import timezone from django.utils import timezone
from django.contrib.sessions.models import Session
from picklefield.fields import PickledObjectField from picklefield.fields import PickledObjectField
import re import re
import os import os
import sys import sys
from importlib import import_module
from datetime import timedelta from datetime import timedelta
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from requests_futures.sessions import FuturesSession from requests_futures.sessions import FuturesSession
import utils import utils
SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
class User(models.Model): class User(models.Model):
"""A user logged into the CAS""" """A user logged into the CAS"""
class Meta: class Meta:
unique_together = ("username", "session") unique_together = ("username", "session_key")
session = models.OneToOneField(Session, related_name="cas_server_user", blank=True, null=True, on_delete=models.SET_NULL) session_key = models.CharField(max_length=40, blank=True, null=True)
username = models.CharField(max_length=30) username = models.CharField(max_length=30)
date = models.DateTimeField(auto_now_add=True, auto_now=True) date = models.DateTimeField(auto_now_add=True, auto_now=True)
@classmethod @classmethod
def clean_old_entries(cls): 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: for user in users:
user.logout() user.logout()
users.delete() 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 @property
def attributs(self): def attributs(self):
"""return a fresh dict for the user attributs""" """return a fresh dict for the user attributs"""
@ -108,6 +119,7 @@ class User(models.Model):
single_log_out=service_pattern.single_log_out single_log_out=service_pattern.single_log_out
) )
ticket.save() ticket.save()
self.save()
return ticket return ticket
def get_service_url(self, service, service_pattern, renew): def get_service_url(self, service, service_pattern, renew):
@ -321,7 +333,7 @@ class Ticket(models.Model):
TIMEOUT = settings.CAS_TICKET_TIMEOUT TIMEOUT = settings.CAS_TICKET_TIMEOUT
def __unicode__(self): def __unicode__(self):
return u"Ticket(%s, %s)" % (self.user, self.service) return u"Ticket-%s" % self.pk
@classmethod @classmethod
def clean_old_entries(cls): def clean_old_entries(cls):
@ -394,13 +406,13 @@ class ServiceTicket(Ticket):
PREFIX = settings.CAS_SERVICE_TICKET_PREFIX PREFIX = settings.CAS_SERVICE_TICKET_PREFIX
value = models.CharField(max_length=255, default=utils.gen_st, unique=True) value = models.CharField(max_length=255, default=utils.gen_st, unique=True)
def __unicode__(self): def __unicode__(self):
return u"ServiceTicket(%s, %s, %s)" % (self.user, self.value, self.service) return u"ServiceTicket-%s" % self.pk
class ProxyTicket(Ticket): class ProxyTicket(Ticket):
"""A Proxy Ticket""" """A Proxy Ticket"""
PREFIX = settings.CAS_PROXY_TICKET_PREFIX PREFIX = settings.CAS_PROXY_TICKET_PREFIX
value = models.CharField(max_length=255, default=utils.gen_pt, unique=True) value = models.CharField(max_length=255, default=utils.gen_pt, unique=True)
def __unicode__(self): def __unicode__(self):
return u"ProxyTicket(%s, %s, %s)" % (self.user, self.value, self.service) return u"ProxyTicket-%s" % self.pk
class ProxyGrantingTicket(Ticket): class ProxyGrantingTicket(Ticket):
"""A Proxy Granting Ticket""" """A Proxy Granting Ticket"""
PREFIX = settings.CAS_PROXY_GRANTING_TICKET_PREFIX PREFIX = settings.CAS_PROXY_GRANTING_TICKET_PREFIX
@ -409,7 +421,7 @@ class ProxyGrantingTicket(Ticket):
def __unicode__(self): 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): class Proxy(models.Model):
"""A list of proxies on `ProxyTicket`""" """A list of proxies on `ProxyTicket`"""

View File

@ -15,7 +15,6 @@ from .default_settings import settings
from django.utils.importlib import import_module from django.utils.importlib import import_module
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.contrib.sessions.models import Session
import urlparse import urlparse
import urllib import urllib
@ -102,8 +101,3 @@ def gen_pgtiou():
def gen_saml_id(): def gen_saml_id():
"""Generate an saml id""" """Generate an saml id"""
return _gen_ticket('_') 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)

View File

@ -71,7 +71,7 @@ class LogoutMixin(object):
try: try:
user = models.User.objects.get( user = models.User.objects.get(
username=self.request.session.get("username"), username=self.request.session.get("username"),
session=utils.get_session(self.request) session_key=self.request.session_key
) )
user.logout(self.request) user.logout(self.request)
user.delete() user.delete()
@ -156,7 +156,7 @@ class LoginView(View, LogoutMixin):
if self.form.is_valid(): if self.form.is_valid():
self.user = models.User.objects.get( self.user = models.User.objects.get(
username=self.form.cleaned_data['username'], 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.set_expiry(0)
request.session["username"] = self.form.cleaned_data['username'] request.session["username"] = self.form.cleaned_data['username']
@ -263,7 +263,7 @@ class LoginView(View, LogoutMixin):
try: try:
self.user = models.User.objects.get( self.user = models.User.objects.get(
username=self.request.session.get("username"), username=self.request.session.get("username"),
session=utils.get_session(self.request) session_key=self.request.session_key
) )
except models.User.DoesNotExist: except models.User.DoesNotExist:
self.logout() self.logout()
@ -351,7 +351,7 @@ class Auth(View):
try: try:
user = models.User.objects.get( user = models.User.objects.get(
username=form.cleaned_data['username'], username=form.cleaned_data['username'],
session=utils.get_session(request) session_key=self.request.session_key
) )
# is the service allowed # is the service allowed
service_pattern = ServicePattern.validate(service) service_pattern = ServicePattern.validate(service)