Some refactoring

This commit is contained in:
Valentin Samir 2015-05-27 21:56:39 +02:00
parent d173cd6190
commit ad434a113f
9 changed files with 575 additions and 170 deletions

View File

@ -1,42 +1,57 @@
"""module for the admin interface of the app"""
from django.contrib import admin from django.contrib import admin
from models import * from .models import ServiceTicket, ProxyTicket, ProxyGrantingTicket, User, ServicePattern
from forms import * from .models import Username, ReplaceAttributName, ReplaceAttributValue, FilterAttributValue
from .forms import TicketForm
# Register your models here. # Register your models here.
class ServiceTicketInline(admin.TabularInline): class ServiceTicketInline(admin.TabularInline):
"""`ServiceTicket` in admin interface"""
model = ServiceTicket model = ServiceTicket
extra = 0 extra = 0
form = TicketForm form = TicketForm
class ProxyTicketInline(admin.TabularInline): class ProxyTicketInline(admin.TabularInline):
"""`ProxyTicket` in admin interface"""
model = ProxyTicket model = ProxyTicket
extra = 0 extra = 0
form = TicketForm form = TicketForm
class ProxyGrantingInline(admin.TabularInline): class ProxyGrantingInline(admin.TabularInline):
"""`ProxyGrantingTicket` in admin interface"""
model = ProxyGrantingTicket model = ProxyGrantingTicket
extra = 0 extra = 0
form = TicketForm form = TicketForm
class UserAdmin(admin.ModelAdmin): class UserAdmin(admin.ModelAdmin):
"""`User` in admin interface"""
inlines = (ServiceTicketInline, ProxyTicketInline, ProxyGrantingInline) inlines = (ServiceTicketInline, ProxyTicketInline, ProxyGrantingInline)
class UsernamesInline(admin.TabularInline): class UsernamesInline(admin.TabularInline):
model = Usernames """`Username` in admin interface"""
model = Username
extra = 0 extra = 0
class ReplaceAttributNameInline(admin.TabularInline): class ReplaceAttributNameInline(admin.TabularInline):
"""`ReplaceAttributName` in admin interface"""
model = ReplaceAttributName model = ReplaceAttributName
extra = 0 extra = 0
class ReplaceAttributValueInline(admin.TabularInline): class ReplaceAttributValueInline(admin.TabularInline):
"""`ReplaceAttributValue` in admin interface"""
model = ReplaceAttributValue model = ReplaceAttributValue
extra = 0 extra = 0
class FilterAttributValueInline(admin.TabularInline): class FilterAttributValueInline(admin.TabularInline):
"""`FilterAttributValue` in admin interface"""
model = FilterAttributValue model = FilterAttributValue
extra = 0 extra = 0
class ServicePatternAdmin(admin.ModelAdmin): class ServicePatternAdmin(admin.ModelAdmin):
inlines = (UsernamesInline, ReplaceAttributNameInline, ReplaceAttributValueInline, FilterAttributValueInline) """`ServicePattern` in admin interface"""
inlines = (
UsernamesInline,
ReplaceAttributNameInline,
ReplaceAttributValueInline,
FilterAttributValueInline
)
list_display = ('pos', 'name', 'pattern', 'proxy') list_display = ('pos', 'name', 'pattern', 'proxy')
admin.site.register(User, UserAdmin) admin.site.register(User, UserAdmin)
admin.site.register(ServicePattern, ServicePatternAdmin) admin.site.register(ServicePattern, ServicePatternAdmin)
#admin.site.register(ProxyGrantingTicketIOU, admin.ModelAdmin)

View File

@ -1,4 +1,5 @@
# ⁻*- coding: utf-8 -*- # ⁻*- coding: utf-8 -*-
"""Some authentication classes for the CAS"""
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
try: try:
@ -7,38 +8,47 @@ try:
import crypt import crypt
except ImportError: except ImportError:
MySQLdb = None MySQLdb = None
class DummyAuthUser(object): class DummyAuthUser(object):
"""A Dummy authentication class"""
def __init__(self, username): def __init__(self, username):
self.username = username self.username = username
def test_password(self, password): def test_password(self, password):
"""test `password` agains the user"""
return False return False
def attributs(self): def attributs(self):
"""return a dict of user attributes"""
return {} return {}
class TestAuthUser(DummyAuthUser): class TestAuthUser(DummyAuthUser):
"""A test authentication class with one user test having
alose test as password and some attributes"""
def __init__(self, username): def __init__(self, username):
self.username = username super(TestAuthUser, self).__init__(username)
def test_password(self, password): def test_password(self, password):
"""test `password` agains the user"""
return self.username == "test" and password == "test" return self.username == "test" and password == "test"
def attributs(self): def attributs(self):
"""return a dict of user attributes"""
return {'nom':'Nymous', 'prenom':'Ano', 'email':'anonymous@example.net'} return {'nom':'Nymous', 'prenom':'Ano', 'email':'anonymous@example.net'}
class MysqlAuthUser(DummyAuthUser): class MysqlAuthUser(DummyAuthUser):
"""A mysql auth class: authentication user agains a mysql database"""
user = None user = None
def __init__(self, username): def __init__(self, username):
mysql_config = { mysql_config = {
"user": settings.CAS_SQL_USERNAME, "user": settings.CAS_SQL_USERNAME,
"passwd": settings.CAS_SQL_PASSWORD, "passwd": settings.CAS_SQL_PASSWORD,
"db": settings.CAS_SQL_DBNAME, "db": settings.CAS_SQL_DBNAME,
"host": settings.CAS_SQL_HOST, "host": settings.CAS_SQL_HOST,
"charset":settings.CAS_SQL_DBCHARSET, "charset":settings.CAS_SQL_DBCHARSET,
"cursorclass":MySQLdb.cursors.DictCursor "cursorclass":MySQLdb.cursors.DictCursor
} }
if not MySQLdb: if not MySQLdb:
raise RuntimeError("Please install MySQLdb before using the MysqlAuthUser backend") raise RuntimeError("Please install MySQLdb before using the MysqlAuthUser backend")
@ -49,6 +59,7 @@ class MysqlAuthUser(DummyAuthUser):
super(MysqlAuthUser, self).__init__(username) super(MysqlAuthUser, self).__init__(username)
def test_password(self, password): def test_password(self, password):
"""test `password` agains the user"""
if not self.user: if not self.user:
return False return False
else: else:
@ -62,13 +73,14 @@ class MysqlAuthUser(DummyAuthUser):
return crypt.crypt(password, self.user["password"][:2]) == self.user["password"] return crypt.crypt(password, self.user["password"][:2]) == self.user["password"]
def attributs(self): def attributs(self):
"""return a dict of user attributes"""
if not self.user: if not self.user:
return {} return {}
else: else:
return self.user return self.user
class DjangoAuthUser(DummyAuthUser): class DjangoAuthUser(DummyAuthUser):
"""A django auth class: authenticate user agains django internal users"""
user = None user = None
def __init__(self, username): def __init__(self, username):
try: try:
@ -79,16 +91,18 @@ class DjangoAuthUser(DummyAuthUser):
def test_password(self, password): def test_password(self, password):
"""test `password` agains the user"""
if not self.user: if not self.user:
return False return False
else: else:
return self.user.check_password(password) return self.user.check_password(password)
def attributs(self): def attributs(self):
"""return a dict of user attributes"""
if not self.user: if not self.user:
return {} return {}
else: else:
attr = {} attr = {}
for field in self.user._meta.fields: for field in self.user._meta.fields:
attr[field.attname]=getattr(self.user, field.attname) attr[field.attname] = getattr(self.user, field.attname)
return attr return attr

View File

@ -1,7 +1,10 @@
"""Default values for the app's settings"""
from django.conf import settings from django.conf import settings
import auth from . import auth
def setting_default(name, default_value): def setting_default(name, default_value):
"""if the config `name` is not set, set it the `default_value`"""
value = getattr(settings, name, default_value) value = getattr(settings, name, default_value)
setattr(settings, name, value) setattr(settings, name, value)
@ -18,6 +21,7 @@ setting_default('CAS_SQL_USERNAME', '')
setting_default('CAS_SQL_PASSWORD', '') setting_default('CAS_SQL_PASSWORD', '')
setting_default('CAS_SQL_DBNAME', '') setting_default('CAS_SQL_DBNAME', '')
setting_default('CAS_SQL_DBCHARSET', 'utf8') setting_default('CAS_SQL_DBCHARSET', 'utf8')
setting_default('CAS_SQL_USER_QUERY', 'SELECT user AS usersame, pass AS password, users.* FROM users WHERE user = %s') setting_default('CAS_SQL_USER_QUERY', 'SELECT user AS usersame, pass AS ' \
'password, users.* FROM users WHERE user = %s')
setting_default('CAS_SQL_PASSWORD_CHECK', 'crypt') # crypt or plain setting_default('CAS_SQL_PASSWORD_CHECK', 'crypt') # crypt or plain

View File

@ -1,12 +1,14 @@
import default_settings """forms for the app"""
import cas_server.default_settings
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import models from . import models
class UserCredential(forms.Form): class UserCredential(forms.Form):
"""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(widget=forms.HiddenInput(), required=False)
password = forms.CharField(label=_('password'), widget=forms.PasswordInput) password = forms.CharField(label=_('password'), widget=forms.PasswordInput)
@ -22,17 +24,20 @@ class UserCredential(forms.Form):
if auth.test_password(cleaned_data.get("password")): if auth.test_password(cleaned_data.get("password")):
try: try:
user = models.User.objects.get(username=auth.username) user = models.User.objects.get(username=auth.username)
user.attributs=auth.attributs() user.attributs = auth.attributs()
user.save() user.save()
except models.User.DoesNotExist: except models.User.DoesNotExist:
user = models.User.objects.create(username=auth.username, attributs=auth.attributs()) user = models.User.objects.create(
username=auth.username,
attributs=auth.attributs()
)
user.save() user.save()
self.user = user
else: else:
raise forms.ValidationError(_(u"Bad user")) raise forms.ValidationError(_(u"Bad user"))
class TicketForm(forms.ModelForm): class TicketForm(forms.ModelForm):
"""Form for Tickets in the admin interface"""
class Meta: class Meta:
model = models.Ticket model = models.Ticket
exclude = [] exclude = []

View File

@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('cas_server', '0011_auto_20150523_1731'),
]
operations = [
migrations.RenameModel(
old_name='Usernames',
new_name='Username',
),
]

View File

@ -1,5 +1,6 @@
# ⁻*- coding: utf-8 -*- # ⁻*- coding: utf-8 -*-
import default_settings """models for the app"""
import cas_server.default_settings
from django.conf import settings from django.conf import settings
from django.db import models from django.db import models
@ -16,21 +17,37 @@ import string
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from requests_futures.sessions import FuturesSession from requests_futures.sessions import FuturesSession
import utils from . import utils
def _gen_ticket(prefix): def _gen_ticket(prefix):
return '%s-%s' % (prefix, ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(settings.CAS_ST_LEN))) """Generate a ticket with prefix `prefix`"""
return '%s-%s' % (
prefix,
''.join(
random.choice(
string.ascii_letters + string.digits
) for _ in range(settings.CAS_ST_LEN)
)
)
def _gen_st(): def _gen_st():
"""Generate a Service Ticket"""
return _gen_ticket('ST') return _gen_ticket('ST')
def _gen_pt(): def _gen_pt():
"""Generate a Proxy Ticket"""
return _gen_ticket('PT') return _gen_ticket('PT')
def _gen_pgt(): def _gen_pgt():
"""Generate a Proxy Granting Ticket"""
return _gen_ticket('PGT') return _gen_ticket('PGT')
def gen_pgtiou():
"""Generate a Proxy Granting Ticket IOU"""
return _gen_ticket('PGTIOU')
class User(models.Model): class User(models.Model):
"""A user logged into the CAS"""
username = models.CharField(max_length=30, unique=True) username = models.CharField(max_length=30, unique=True)
attributs = PickledObjectField() attributs = PickledObjectField()
date = models.DateTimeField(auto_now_add=True, auto_now=True) date = models.DateTimeField(auto_now_add=True, auto_now=True)
@ -39,6 +56,7 @@ class User(models.Model):
return self.username return self.username
def logout(self, request): def logout(self, request):
"""Sending SSO request to all services the user logged in"""
async_list = [] async_list = []
session = FuturesSession(executor=ThreadPoolExecutor(max_workers=10)) session = FuturesSession(executor=ThreadPoolExecutor(max_workers=10))
for ticket in ServiceTicket.objects.filter(user=self, validate=True): for ticket in ServiceTicket.objects.filter(user=self, validate=True):
@ -53,67 +71,112 @@ class User(models.Model):
for future in async_list: for future in async_list:
try: try:
future.result() future.result()
except Exception as e: except Exception as error:
messages.add_message(request, messages.WARNING, _(u'Error during service logout %s') % e) messages.add_message(
request,
messages.WARNING,
_(u'Error during service logout %r') % error
)
def delete(self): def get_ticket(self, ticket_class, service, service_pattern, renew):
super(User, self).delete() """
Generate a ticket using `ticket_class` for the service
`service` matching `service_pattern` and asking or not for
def get_ticket(self, TicketClass, service, service_pattern, renew): authentication renewal with `renew
attributs = dict((a.name, a.replace if a.replace else a.name) for a in service_pattern.attributs.all()) """
replacements = dict((a.name, (a.pattern, a.replace)) for a in service_pattern.replacements.all()) attributs = dict(
(a.name, a.replace if a.replace else a.name) for a in service_pattern.attributs.all()
)
replacements = dict(
(a.name, (a.pattern, a.replace)) for a in service_pattern.replacements.all()
)
service_attributs = {} service_attributs = {}
for (k,v) in self.attributs.items(): for (key, value) in self.attributs.items():
if k in attributs: if key in attributs:
if k in replacements: if key in replacements:
v = re.sub(replacements[k][0], replacements[k][1], v) value = re.sub(replacements[key][0], replacements[key][1], value)
service_attributs[attributs[k]] = v service_attributs[attributs[key]] = value
ticket = TicketClass.objects.create(user=self, attributs = service_attributs, service=service, renew=renew, service_pattern=service_pattern) ticket = ticket_class.objects.create(
user=self,
attributs=service_attributs,
service=service,
renew=renew,
service_pattern=service_pattern
)
ticket.save() ticket.save()
return ticket return ticket
def get_service_url(self, service, service_pattern, renew): def get_service_url(self, service, service_pattern, renew):
"""Return the url to which the user must be redirected to
after a Service Ticket has been generated"""
ticket = self.get_ticket(ServiceTicket, service, service_pattern, renew) ticket = self.get_ticket(ServiceTicket, service, service_pattern, renew)
url = utils.update_url(service, {'ticket':ticket.value}) url = utils.update_url(service, {'ticket':ticket.value})
return url return url
class BadUsername(Exception): class BadUsername(Exception):
"""Exception raised then an non allowed username
try to get a ticket for a service"""
pass pass
class BadFilter(Exception): class BadFilter(Exception):
""""Exception raised then a user try
to get a ticket for a service and do not reach a condition"""
pass pass
class UserFieldNotDefined(Exception): class UserFieldNotDefined(Exception):
"""Exception raised then a user try to get a ticket for a service
using as username an attribut not present on this user"""
pass pass
class ServicePattern(models.Model): class ServicePattern(models.Model):
"""Allowed services pattern agains services are tested to"""
class Meta: class Meta:
ordering = ("pos", ) ordering = ("pos", )
pos = models.IntegerField(default=100) pos = models.IntegerField(default=100)
name = models.CharField(max_length=255, unique=True, blank=True, null=True, help_text="Un nom pour le service") name = models.CharField(
max_length=255,
unique=True,
blank=True,
null=True,
help_text="Un nom pour le service"
)
pattern = models.CharField(max_length=255, unique=True) pattern = models.CharField(max_length=255, unique=True)
user_field = models.CharField(max_length=255, default="", blank=True, help_text="Nom de l'attribut transmit comme username, vide = login") user_field = models.CharField(
#usernames = models.CharField(max_length=255, default="", blank=True, help_text="Liste d'utilisateurs acceptés séparé par des virgules, vide = tous les utilisateur") max_length=255,
#attributs = models.CharField(max_length=255, default="", blank=True, help_text="Liste des nom d'attributs à transmettre au service, séparé par une virgule. vide = aucun") default="",
restrict_users = models.BooleanField(default=False, help_text="Limiter les utilisateur autorisé a se connecté a ce service à celle ci-dessous") blank=True,
proxy = models.BooleanField(default=False, help_text="Un ProxyGrantingTicket peut être délivré au service pour s'authentifier en temps que l'utilisateur sur d'autres services") help_text="Nom de l'attribut transmit comme username, vide = login"
#filter = models.CharField(max_length=255, default="", blank=True, help_text="Une lambda fonction pour filtrer sur les utilisateur où leurs attribut, arg1: username, arg2:attrs_dict. vide = pas de filtre") )
restrict_users = models.BooleanField(
default=False,
help_text="Limiter les utilisateur autorisé a se connecté a ce service à celle ci-dessous"
)
proxy = models.BooleanField(
default=False,
help_text="Un ProxyGrantingTicket peut être délivré au service pour " \
"s'authentifier en temps que l'utilisateur sur d'autres services"
)
def __unicode__(self): def __unicode__(self):
return u"%s: %s" % (self.pos, self.pattern) return u"%s: %s" % (self.pos, self.pattern)
def check_user(self, user): def check_user(self, user):
"""Check if `user` if allowed to use theses services"""
if self.restrict_users and not self.usernames.filter(value=user.username): if self.restrict_users and not self.usernames.filter(value=user.username):
raise BadUsername() raise BadUsername()
for f in self.filters.all(): for filtre in self.filters.all():
if isinstance(user.attributs[f.attribut], list): if isinstance(user.attributs[filtre.attribut], list):
l = user.attributs[f.attribut] attrs = user.attributs[filtre.attribut]
else: else:
l = [user.attributs[f.attribut]] attrs = [user.attributs[filtre.attribut]]
for v in l: for value in attrs:
if re.match(f.pattern, str(v)): if re.match(filtre.pattern, str(value)):
break break
else: else:
raise BadFilter('%s do not match %s %s' % (f.pattern, f.attribut, user.attributs[f.attribut]) ) raise BadFilter('%s do not match %s %s' % (
filtre.pattern,
filtre.attribut,
user.attributs[filtre.attribut]
))
if self.user_field and not user.attributs.get(self.user_field): if self.user_field and not user.attributs.get(self.user_field):
raise UserFieldNotDefined() raise UserFieldNotDefined()
return True return True
@ -121,20 +184,35 @@ class ServicePattern(models.Model):
@classmethod @classmethod
def validate(cls, service): def validate(cls, service):
for s in cls.objects.all().order_by('pos'): """Check if a Service Patern match `service` and
if re.match(s.pattern, service): return it, else raise `ServicePattern.DoesNotExist`"""
return s for service_pattern in cls.objects.all().order_by('pos'):
if re.match(service_pattern.pattern, service):
return service_pattern
raise cls.DoesNotExist() raise cls.DoesNotExist()
class Usernames(models.Model): class Username(models.Model):
"""A list of allowed usernames on a service pattern"""
value = models.CharField(max_length=255) value = models.CharField(max_length=255)
service_pattern = models.ForeignKey(ServicePattern, related_name="usernames") service_pattern = models.ForeignKey(ServicePattern, related_name="usernames")
def __unicode__(self):
return self.value
class ReplaceAttributName(models.Model): class ReplaceAttributName(models.Model):
"""A list of replacement of attributs name for a service pattern"""
class Meta: class Meta:
unique_together = ('name', 'replace', 'service_pattern') unique_together = ('name', 'replace', 'service_pattern')
name = models.CharField(max_length=255, help_text=u"nom d'un attributs à transmettre au service") name = models.CharField(
replace = models.CharField(max_length=255, blank=True, help_text=u"nom sous lequel l'attribut sera présenté au service. vide = inchangé") max_length=255,
help_text=u"nom d'un attributs à transmettre au service"
)
replace = models.CharField(
max_length=255,
blank=True,
help_text=u"nom sous lequel l'attribut sera présenté " \
u"au service. vide = inchangé"
)
service_pattern = models.ForeignKey(ServicePattern, related_name="attributs") service_pattern = models.ForeignKey(ServicePattern, related_name="attributs")
def __unicode__(self): def __unicode__(self):
@ -144,17 +222,35 @@ class ReplaceAttributName(models.Model):
return u"%s%s" % (self.name, self.replace) return u"%s%s" % (self.name, self.replace)
class FilterAttributValue(models.Model): class FilterAttributValue(models.Model):
attribut = models.CharField(max_length=255, help_text=u"Nom de l'attribut devant vérifier pattern") """A list of filter on attributs for a service pattern"""
pattern = models.CharField(max_length=255, help_text=u"Une expression régulière") attribut = models.CharField(
max_length=255,
help_text=u"Nom de l'attribut devant vérifier pattern"
)
pattern = models.CharField(
max_length=255,
help_text=u"Une expression régulière"
)
service_pattern = models.ForeignKey(ServicePattern, related_name="filters") service_pattern = models.ForeignKey(ServicePattern, related_name="filters")
def __unicode__(self): def __unicode__(self):
return u"%s %s" % (self.attribut, self.pattern) return u"%s %s" % (self.attribut, self.pattern)
class ReplaceAttributValue(models.Model): class ReplaceAttributValue(models.Model):
attribut = models.CharField(max_length=255, help_text=u"Nom de l'attribut dont la valeur doit être modifié") """Replacement to apply on attributs values for a service pattern"""
pattern = models.CharField(max_length=255, help_text=u"Une expression régulière de ce qui doit être modifié") attribut = models.CharField(
replace = models.CharField(max_length=255, blank=True, help_text=u"Par quoi le remplacer, les groupes sont capturé par \\1, \\2 …") max_length=255,
help_text=u"Nom de l'attribut dont la valeur doit être modifié"
)
pattern = models.CharField(
max_length=255,
help_text=u"Une expression régulière de ce qui doit être modifié"
)
replace = models.CharField(
max_length=255,
blank=True,
help_text=u"Par quoi le remplacer, les groupes sont capturé par \\1, \\2 …"
)
service_pattern = models.ForeignKey(ServicePattern, related_name="replacements") service_pattern = models.ForeignKey(ServicePattern, related_name="replacements")
def __unicode__(self): def __unicode__(self):
@ -162,44 +258,71 @@ class ReplaceAttributValue(models.Model):
class Ticket(models.Model): class Ticket(models.Model):
"""Generic class for a Ticket"""
class Meta: class Meta:
abstract = True abstract = True
user = models.ForeignKey(User, related_name="%(class)s") user = models.ForeignKey(User, related_name="%(class)s")
attributs = PickledObjectField() attributs = PickledObjectField()
validate = models.BooleanField(default=False) validate = models.BooleanField(default=False)
service = models.TextField() service = models.TextField()
service_pattern = models.ForeignKey(ServicePattern, related_name="%(class)s") service_pattern = models.ForeignKey(ServicePattern, related_name="%(class)s")
creation = models.DateTimeField(auto_now_add=True) creation = models.DateTimeField(auto_now_add=True)
renew = models.BooleanField(default=False) renew = models.BooleanField(default=False)
def __unicode__(self): def __unicode__(self):
return u"%s: %s %s" % (self.user, self.value, self.service) return u"Ticket(%s, %s)" % (self.user, self.service)
def logout(self, request, session): def logout(self, request, session):
#if self.validate: """Send a SSO request to the ticket service"""
if self.validate:
xml = """<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xml = """<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
ID="%(id)s" Version="2.0" IssueInstant="%(datetime)s"> ID="%(id)s" Version="2.0" IssueInstant="%(datetime)s">
<saml:NameID xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"></saml:NameID> <saml:NameID xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"></saml:NameID>
<samlp:SessionIndex>%(ticket)s</samlp:SessionIndex> <samlp:SessionIndex>%(ticket)s</samlp:SessionIndex>
</samlp:LogoutRequest>""" % {'id' : os.urandom(20).encode("hex"), 'datetime' : int(time.time()), 'ticket': self.value} </samlp:LogoutRequest>""" % \
{
'id' : os.urandom(20).encode("hex"),
'datetime' : int(time.time()),
'ticket': self.value
}
headers = {'Content-Type': 'text/xml'} headers = {'Content-Type': 'text/xml'}
try: try:
return session.post(self.service.encode('utf-8'), data=xml.encode('utf-8'), headers=headers) return session.post(
except Exception as e: self.service.encode('utf-8'),
messages.add_message(request, messages.WARNING, _(u'Error during service logout %(service)s:\n%(error)s') % {'service': self.service, 'error':e}) data=xml.encode('utf-8'),
headers=headers
)
except Exception as error:
messages.add_message(
request,
messages.WARNING,
_(u'Error during service logout %(service)s:\n%(error)s') %
{'service': self.service, 'error':error}
)
class ServiceTicket(Ticket): class ServiceTicket(Ticket):
"""A Service Ticket"""
value = models.CharField(max_length=255, default=_gen_st, unique=True) value = models.CharField(max_length=255, default=_gen_st, unique=True)
def __unicode__(self):
return u"ServiceTicket(%s, %s, %s)" % (self.user, self.value, self.service)
class ProxyTicket(Ticket): class ProxyTicket(Ticket):
"""A Proxy Ticket"""
value = models.CharField(max_length=255, default=_gen_pt, unique=True) value = models.CharField(max_length=255, default=_gen_pt, unique=True)
def __unicode__(self):
return u"ProxyTicket(%s, %s, %s)" % (self.user, self.value, self.service)
class ProxyGrantingTicket(Ticket): class ProxyGrantingTicket(Ticket):
"""A Proxy Granting Ticket"""
value = models.CharField(max_length=255, default=_gen_pgt, unique=True) value = models.CharField(max_length=255, default=_gen_pgt, unique=True)
#class ProxyGrantingTicketIOU(Ticket): def __unicode__(self):
# value = models.CharField(max_length=255, default=lambda:_gen_ticket('PGTIOU'), unique=True) return u"ProxyGrantingTicket(%s, %s, %s)" % (self.user, self.value, self.service)
class Proxy(models.Model): class Proxy(models.Model):
"""A list of proxies on `ProxyTicket`"""
class Meta: class Meta:
ordering = ("-pk", ) ordering = ("-pk", )
url = models.CharField(max_length=255) url = models.CharField(max_length=255)
proxy_ticket = models.ForeignKey(ProxyTicket, related_name="proxies") proxy_ticket = models.ForeignKey(ProxyTicket, related_name="proxies")
def __unicode__(self):
return self.url

View File

@ -1,19 +1,21 @@
# ⁻*- coding: utf-8 -*- # ⁻*- coding: utf-8 -*-
"""urls for the app"""
from django.conf.urls import patterns, url from django.conf.urls import patterns, url
from django.views.generic import RedirectView from django.views.generic import RedirectView
import views from . import views
urlpatterns = patterns('', urlpatterns = patterns(
'',
url(r'^$', RedirectView.as_view(pattern_name="login")), url(r'^$', RedirectView.as_view(pattern_name="login")),
url('^login$', views.login, name='login'), url('^login$', views.login, name='login'),
url('^logout$', views.logout, name='logout'), url('^logout$', views.logout, name='logout'),
url('^validate$', views.validate, name='validate'), url('^validate$', views.validate, name='validate'),
url('^serviceValidate$', views.serviceValidate, name='serviceValidate'), url('^serviceValidate$', views.service_validate, name='serviceValidate'),
url('^proxyValidate$', views.proxyValidate, name='proxyValidate'), url('^proxyValidate$', views.proxy_validate, name='proxyValidate'),
url('^proxy$', views.proxy, name='proxy'), url('^proxy$', views.proxy, name='proxy'),
url('^p3/serviceValidate$', views.p3_serviceValidate, name='p3_serviceValidate'), url('^p3/serviceValidate$', views.p3_service_validate, name='p3_serviceValidate'),
url('^p3/proxyValidate$', views.p3_proxyValidate, name='p3_proxyValidate'), url('^p3/proxyValidate$', views.p3_proxy_validate, name='p3_proxyValidate'),
url('^samlValidate$', views.samlValidate, name='samlValidate'), url('^samlValidate$', views.saml_validate, name='samlValidate'),
) )

View File

@ -1,7 +1,9 @@
"""Some util function for the app"""
import urlparse import urlparse
import urllib import urllib
def update_url(url, params): def update_url(url, params):
"""update params in the `url` query string"""
url_parts = list(urlparse.urlparse(url)) url_parts = list(urlparse.urlparse(url))
query = dict(urlparse.parse_qsl(url_parts[4])) query = dict(urlparse.parse_qsl(url_parts[4]))
query.update(params) query.update(params)

View File

@ -1,38 +1,50 @@
# ⁻*- coding: utf-8 -*- # ⁻*- coding: utf-8 -*-
import default_settings """views for the app"""
import cas_server.default_settings
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.http import HttpResponse, StreamingHttpResponse from django.http import HttpResponse, HttpResponseRedirect
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils import timezone
import requests import requests
import urllib import urllib
from datetime import datetime, timedelta
from lxml import etree from lxml import etree
from datetime import timedelta
import utils from . import utils
import forms from . import forms
import models from . import models
def _logout(request): def _logout(request):
try: del request.session["authenticated"] """Clean sessions variables"""
except KeyError: pass try:
try: del request.session["username"] del request.session["authenticated"]
except KeyError: pass except KeyError:
try: del request.session["warn"] pass
except KeyError: pass try:
del request.session["username"]
except KeyError:
pass
try:
del request.session["warn"]
except KeyError:
pass
def redirect_params(url_name, params={}): def redirect_params(url_name, params=None):
url = reverse(url_name, args = args) """Redirect to `url_name` with `params` as querystring"""
params = urllib.urlencode(params) url = reverse(url_name)
params = urllib.urlencode(params if params else {})
return HttpResponseRedirect(url + "?%s" % params) return HttpResponseRedirect(url + "?%s" % params)
def login(request): def login(request):
"""credential requestor / acceptor"""
user = None user = None
form = None form = None
service_pattern = None service_pattern = None
@ -45,7 +57,10 @@ def login(request):
method = request.POST.get('method') method = request.POST.get('method')
if not request.session.get("authenticated") or renew: if not request.session.get("authenticated") or renew:
form = forms.UserCredential(request.POST, initial={'service':service,'method':method,'warn':request.session.get("warn")}) form = forms.UserCredential(
request.POST,
initial={'service':service, 'method':method, 'warn':request.session.get("warn")}
)
if form.is_valid(): if form.is_valid():
user = models.User.objects.get(username=form.cleaned_data['username']) user = models.User.objects.get(username=form.cleaned_data['username'])
request.session.set_expiry(0) request.session.set_expiry(0)
@ -63,10 +78,13 @@ def login(request):
method = request.GET.get('method') method = request.GET.get('method')
if not request.session.get("authenticated") or renew: if not request.session.get("authenticated") or renew:
form = forms.UserCredential(initial={'service':service,'method':method,'warn':request.session.get("warn")}) form = forms.UserCredential(
initial={'service':service, 'method':method, 'warn':request.session.get("warn")}
)
# if authenticated and successfully renewed authentication if needed # if authenticated and successfully renewed authentication if needed
if request.session.get("authenticated") and request.session.get("username") and (not renew or renewed): if request.session.get("authenticated") and \
request.session.get("username") and (not renew or renewed):
try: try:
user = models.User.objects.get(username=request.session["username"]) user = models.User.objects.get(username=request.session["username"])
except models.User.DoesNotExist: except models.User.DoesNotExist:
@ -80,20 +98,51 @@ def login(request):
service_pattern = models.ServicePattern.validate(service) service_pattern = models.ServicePattern.validate(service)
# is the current user allowed on this service # is the current user allowed on this service
service_pattern.check_user(user) service_pattern.check_user(user)
# if the user has asked to be warned before any login to a service (no transparent SSO) # if the user has asked to be warned before any login to a service
if request.session.get("warn", True) and not warned: if request.session.get("warn", True) and not warned:
messages.add_message(request, messages.WARNING, _(u"Authentication has been required by service %(name)s (%(url)s)") % {'name':service_pattern.name, 'url':service}) messages.add_message(
return render(request, settings.CAS_WARN_TEMPLATE, {'service_ticket_url':user.get_service_url(service, service_pattern, renew=renew)}) request,
messages.WARNING,
_(u"Authentication has been required by service %(name)s (%(url)s)") % \
{'name':service_pattern.name, 'url':service}
)
return render(
request,
settings.CAS_WARN_TEMPLATE,
{'service_ticket_url':user.get_service_url(
service,
service_pattern,
renew=renew
)}
)
else: else:
return redirect(user.get_service_url(service, service_pattern, renew=renew)) # redirect, using method ? # redirect, using method ?
return redirect(user.get_service_url(service, service_pattern, renew=renew))
except models.ServicePattern.DoesNotExist: except models.ServicePattern.DoesNotExist:
messages.add_message(request, messages.ERROR, _(u'Service %(url)s non allowed.') % {'url' : service}) messages.add_message(
request,
messages.ERROR,
_(u'Service %(url)s non allowed.') % {'url' : service}
)
except models.BadUsername: except models.BadUsername:
messages.add_message(request, messages.ERROR, _(u"Username non allowed")) messages.add_message(
request,
messages.ERROR,
_(u"Username non allowed")
)
except models.BadFilter: except models.BadFilter:
messages.add_message(request, messages.ERROR, _(u"User charateristics non allowed")) messages.add_message(
request,
messages.ERROR,
_(u"User charateristics non allowed")
)
except models.UserFieldNotDefined: except models.UserFieldNotDefined:
messages.add_message(request, messages.ERROR, _(u"The attribut %(field)s is needed to use that service") % {'field':service_pattern.user_field}) messages.add_message(
request,
messages.ERROR,
_(u"The attribut %(field)s is needed to use" \
" that service") % {'field':service_pattern.user_field}
)
# if gateway is set and auth failed redirect to the service without authentication # if gateway is set and auth failed redirect to the service without authentication
if gateway: if gateway:
@ -106,17 +155,32 @@ def login(request):
try: try:
service_pattern = models.ServicePattern.validate(service) service_pattern = models.ServicePattern.validate(service)
if gateway: if gateway:
list(messages.get_messages(request)) # clean messages before leaving the django app list(messages.get_messages(request)) # clean messages before leaving django
return redirect(service) return redirect(service)
if request.session.get("authenticated") and renew: if request.session.get("authenticated") and renew:
messages.add_message(request, messages.WARNING, _(u"Authentication renewal required by service %(name)s (%(url)s).") % {'name':service_pattern.name, 'url':service}) messages.add_message(
request,
messages.WARNING,
_(u"Authentication renewal required by service" \
" %(name)s (%(url)s).") % {'name':service_pattern.name, 'url':service}
)
else: else:
messages.add_message(request, messages.WARNING, _(u"Authentication required by service %(name)s (%(url)s).") % {'name':service_pattern.name, 'url':service}) messages.add_message(
request,
messages.WARNING,
_(u"Authentication required by service" \
" %(name)s (%(url)s).") % {'name':service_pattern.name, 'url':service}
)
except models.ServicePattern.DoesNotExist: except models.ServicePattern.DoesNotExist:
messages.add_message(request, messages.ERROR, _(u'Service %s non allowed') % service) messages.add_message(
request,
messages.ERROR,
_(u'Service %s non allowed') % service
)
return render(request, settings.CAS_LOGIN_TEMPLATE, {'form':form}) return render(request, settings.CAS_LOGIN_TEMPLATE, {'form':form})
def logout(request): def logout(request):
"""destroy CAS session (logout)"""
service = request.GET.get('service') service = request.GET.get('service')
if request.session.get("authenticated"): if request.session.get("authenticated"):
user = models.User.objects.get(username=request.session["username"]) user = models.User.objects.get(username=request.session["username"])
@ -133,12 +197,19 @@ def logout(request):
return redirect("login") return redirect("login")
def validate(request): def validate(request):
"""service ticket validation"""
service = request.GET.get('service') service = request.GET.get('service')
ticket = request.GET.get('ticket') ticket = request.GET.get('ticket')
renew = True if request.GET.get('renew') else False renew = True if request.GET.get('renew') else False
if service and ticket: if service and ticket:
try: try:
ticket = models.ServiceTicket.objects.get(value=ticket, service=service, validate=False, renew=renew, creation__gt=(datetime.now() - timedelta(seconds=settings.CAS_TICKET_VALIDITY))) ticket = models.ServiceTicket.objects.get(
value=ticket,
service=service,
validate=False,
renew=renew,
creation__gt=(timezone.now() - timedelta(seconds=settings.CAS_TICKET_VALIDITY))
)
ticket.validate = True ticket.validate = True
ticket.save() ticket.save()
return HttpResponse("yes\n", content_type="text/plain") return HttpResponse("yes\n", content_type="text/plain")
@ -147,119 +218,270 @@ def validate(request):
else: else:
return HttpResponse("no\n", content_type="text/plain") return HttpResponse("no\n", content_type="text/plain")
def psValidate(request, typ=['ST']): def ps_validate(request, ticket_type=None):
"""factorization for serviceValidate and proxyValidate"""
if ticket_type is None:
ticket_type = ['ST']
service = request.GET.get('service') service = request.GET.get('service')
ticket = request.GET.get('ticket') ticket = request.GET.get('ticket')
pgtUrl = request.GET.get('pgtUrl') pgt_url = request.GET.get('pgtUrl')
renew = True if request.GET.get('renew') else False renew = True if request.GET.get('renew') else False
if service and ticket: if service and ticket:
for t in typ: for typ in ticket_type:
if ticket.startswith(t): if ticket.startswith(typ):
break break
else: else:
return render(request, "cas_server/serviceValidateError.xml", {'code':'INVALID_TICKET'}, content_type="text/xml; charset=utf-8") return render(
request,
"cas_server/serviceValidateError.xml",
{'code':'INVALID_TICKET'},
content_type="text/xml; charset=utf-8"
)
try: try:
proxies = [] proxies = []
if ticket.startswith("ST"): if ticket.startswith("ST"):
ticket = models.ServiceTicket.objects.get(value=ticket, service=service, validate=False, renew=renew, creation__gt=(datetime.now() - timedelta(seconds=settings.CAS_TICKET_VALIDITY))) ticket = models.ServiceTicket.objects.get(
value=ticket,
service=service,
validate=False,
renew=renew,
creation__gt=(timezone.now() - timedelta(seconds=settings.CAS_TICKET_VALIDITY))
)
elif ticket.startswith("PT"): elif ticket.startswith("PT"):
ticket = models.ProxyTicket.objects.get(value=ticket, service=service, validate=False, renew=renew, creation__gt=(datetime.now() - timedelta(seconds=settings.CAS_TICKET_VALIDITY))) ticket = models.ProxyTicket.objects.get(
for p in ticket.proxies.all(): value=ticket,
proxies.append(p.url) service=service,
validate=False,
renew=renew,
creation__gt=(timezone.now() - timedelta(seconds=settings.CAS_TICKET_VALIDITY))
)
for prox in ticket.proxies.all():
proxies.append(prox.url)
ticket.validate = True ticket.validate = True
ticket.save() ticket.save()
attributes = [] attributes = []
for key, value in ticket.attributs.items(): for key, value in ticket.attributs.items():
if isinstance(value, list): if isinstance(value, list):
for v in value: for elt in value:
attributes.append((key, v)) attributes.append((key, elt))
else: else:
attributes.append((key, value)) attributes.append((key, value))
params = {'username':ticket.user.username, 'attributes':attributes, 'proxies':proxies} params = {'username':ticket.user.username, 'attributes':attributes, 'proxies':proxies}
if ticket.service_pattern.user_field and ticket.user.attributs.get(ticket.service_pattern.user_field): if ticket.service_pattern.user_field and \
ticket.user.attributs.get(ticket.service_pattern.user_field):
params['username'] = ticket.user.attributs.get(ticket.service_pattern.user_field) params['username'] = ticket.user.attributs.get(ticket.service_pattern.user_field)
if pgtUrl and pgtUrl.startswith("https://"): if pgt_url and pgt_url.startswith("https://"):
pattern = models.ServicePattern.validate(pgtUrl) pattern = models.ServicePattern.validate(pgt_url)
if pattern.proxy: if pattern.proxy:
proxyid = models._gen_ticket('PGTIOU') proxyid = models.gen_pgtiou()
pticket = models.ProxyGrantingTicket.objects.create(user=ticket.user, service=pgtUrl, service_pattern=pattern) pticket = models.ProxyGrantingTicket.objects.create(
url = utils.update_url(pgtUrl, {'pgtIou':proxyid, 'pgtId':pticket.value}) user=ticket.user,
service=pgt_url,
service_pattern=pattern
)
url = utils.update_url(pgt_url, {'pgtIou':proxyid, 'pgtId':pticket.value})
try: try:
r = requests.get(url, verify=settings.CAS_PROXY_CA_CERTIFICATE_PATH) ret = requests.get(url, verify=settings.CAS_PROXY_CA_CERTIFICATE_PATH)
if r.status_code == 200: if ret.status_code == 200:
params['proxyGrantingTicket'] = proxyid params['proxyGrantingTicket'] = proxyid
else: else:
pticket.delete() pticket.delete()
return render(request, "cas_server/serviceValidate.xml", params, content_type="text/xml; charset=utf-8") return render(
request,
"cas_server/serviceValidate.xml",
params,
content_type="text/xml; charset=utf-8"
)
except requests.exceptions.SSLError: except requests.exceptions.SSLError:
return render(request, "cas_server/serviceValidateError.xml", {'code':'INVALID_PROXY_CALLBACK'}, content_type="text/xml; charset=utf-8") return render(
request,
"cas_server/serviceValidateError.xml",
{'code':'INVALID_PROXY_CALLBACK'},
content_type="text/xml; charset=utf-8"
)
else: else:
return render(request, "cas_server/serviceValidateError.xml", {'code':'INVALID_PROXY_CALLBACK'}, content_type="text/xml; charset=utf-8") return render(
request,
"cas_server/serviceValidateError.xml",
{'code':'INVALID_PROXY_CALLBACK'},
content_type="text/xml; charset=utf-8"
)
else: else:
return render(request, "cas_server/serviceValidate.xml", params, content_type="text/xml; charset=utf-8") return render(
except (models.ServiceTicket.DoesNotExist, models.ProxyTicket.DoesNotExist, models.ServicePattern.DoesNotExist): request,
return render(request, "cas_server/serviceValidateError.xml", {'code':'INVALID_TICKET'}, content_type="text/xml; charset=utf-8") "cas_server/serviceValidate.xml",
params,
content_type="text/xml; charset=utf-8"
)
except (models.ServiceTicket.DoesNotExist, models.ProxyTicket.DoesNotExist):
return render(
request,
"cas_server/serviceValidateError.xml",
{'code':'INVALID_TICKET'},
content_type="text/xml; charset=utf-8"
)
except models.ServicePattern.DoesNotExist:
return render(
request,
"cas_server/serviceValidateError.xml",
{'code':'INVALID_TICKET'},
content_type="text/xml; charset=utf-8"
)
else: else:
return render(request, "cas_server/serviceValidateError.xml", {'code':'INVALID_REQUEST'}, content_type="text/xml; charset=utf-8") return render(
request,
"cas_server/serviceValidateError.xml",
{'code':'INVALID_REQUEST'},
content_type="text/xml; charset=utf-8"
)
def serviceValidate(request): def service_validate(request):
return psValidate(request) """service ticket validation CAS 2.0 (also work for CAS 3.0)"""
def proxyValidate(request): return ps_validate(request)
return psValidate(request, ["ST", "PT"]) def proxy_validate(request):
"""service/proxy ticket validation CAS 2.0 (also work for CAS 3.0)"""
return ps_validate(request, ["ST", "PT"])
def proxy(request): def proxy(request):
"""proxy ticket service"""
pgt = request.GET.get('pgt') pgt = request.GET.get('pgt')
targetService = request.GET.get('targetService') target_service = request.GET.get('targetService')
if pgt and targetService: if pgt and target_service:
try: try:
pattern = models.ServicePattern.validate(targetService) # is the target service allowed
ticket = models.ProxyGrantingTicket.objects.get(value=pgt, creation__gt=(datetime.now() - timedelta(seconds=settings.CAS_TICKET_VALIDITY))) pattern = models.ServicePattern.validate(target_service)
# is the proxy granting ticket valid
ticket = models.ProxyGrantingTicket.objects.get(
value=pgt,
creation__gt=(timezone.now() - timedelta(seconds=settings.CAS_TICKET_VALIDITY))
)
# is the pgt user allowed on the target service
pattern.check_user(ticket.user) pattern.check_user(ticket.user)
pticket = ticket.user.get_ticket(models.ProxyTicket, targetService, pattern, False) pticket = ticket.user.get_ticket(models.ProxyTicket, target_service, pattern, False)
pticket.proxies.create(url=ticket.service) pticket.proxies.create(url=ticket.service)
return render(request, "cas_server/proxy.xml", {'ticket':pticket.value}, content_type="text/xml; charset=utf-8") return render(
except (models.ProxyGrantingTicket.DoesNotExist, models.ServicePattern.DoesNotExist, models.BadUsername, models.BadFilter): request,
return render(request, "cas_server/serviceValidateError.xml", {'code':'INVALID_TICKET'}, content_type="text/xml; charset=utf-8") "cas_server/proxy.xml",
{'ticket':pticket.value},
content_type="text/xml; charset=utf-8"
)
except models.ProxyGrantingTicket.DoesNotExist:
return render(
request,
"cas_server/serviceValidateError.xml",
{'code':'INVALID_TICKET'},
content_type="text/xml; charset=utf-8"
)
except models.ServicePattern.DoesNotExist:
return render(
request,
"cas_server/serviceValidateError.xml",
{'code':'INVALID_TICKET'},
content_type="text/xml; charset=utf-8"
)
except models.BadUsername:
return render(
request,
"cas_server/serviceValidateError.xml",
{'code':'INVALID_TICKET'},
content_type="text/xml; charset=utf-8"
)
except models.BadFilter:
return render(
request,
"cas_server/serviceValidateError.xml",
{'code':'INVALID_TICKET'},
content_type="text/xml; charset=utf-8"
)
except models.UserFieldNotDefined:
return render(
request,
"cas_server/serviceValidateError.xml",
{'code':'INVALID_TICKET'},
content_type="text/xml; charset=utf-8"
)
else: else:
return render(request, "cas_server/serviceValidateError.xml", {'code':'INVALID_REQUEST'}, content_type="text/xml; charset=utf-8") return render(
request,
"cas_server/serviceValidateError.xml",
{'code':'INVALID_REQUEST'},
content_type="text/xml; charset=utf-8"
)
def p3_serviceValidate(request): def p3_service_validate(request):
return serviceValidate(request) """service ticket validation CAS 3.0"""
return service_validate(request)
def p3_proxyValidate(request): def p3_proxy_validate(request):
return proxyValidate(request) """service/proxy ticket validation CAS 3.0"""
return proxy_validate(request)
@csrf_exempt @csrf_exempt
def samlValidate(request): def saml_validate(request):
"""checks the validity of a Service Ticket by a SAML 1.1 request"""
if request.method == 'POST': if request.method == 'POST':
target = request.GET.get('TARGET') target = request.GET.get('TARGET')
root = etree.fromstring(request.body) root = etree.fromstring(request.body)
try: try:
auth_req = root.getchildren()[1].getchildren()[0] auth_req = root.getchildren()[1].getchildren()[0]
IssueInstant = auth_req.attrib['IssueInstant'] issue_instant = auth_req.attrib['IssueInstant']
RequestID = auth_req.attrib['RequestID'] request_id = auth_req.attrib['RequestID']
ticket = auth_req.getchildren()[0].text ticket = auth_req.getchildren()[0].text
ticket = models.ServiceTicket.objects.get(value=ticket, service=target, validate=False, creation__gt=(datetime.now() - timedelta(seconds=settings.CAS_TICKET_VALIDITY))) ticket = models.ServiceTicket.objects.get(
value=ticket,
service=target,
validate=False,
creation__gt=(timezone.now() - timedelta(seconds=settings.CAS_TICKET_VALIDITY))
)
ticket.validate = True ticket.validate = True
ticket.save() ticket.save()
expireInstant = (ticket.creation + timedelta(seconds=settings.CAS_TICKET_VALIDITY)).isoformat() expire_instant = (ticket.creation + \
timedelta(seconds=settings.CAS_TICKET_VALIDITY)).isoformat()
attributes = [] attributes = []
for key, value in ticket.attributs.items(): for key, value in ticket.attributs.items():
if isinstance(value, list): if isinstance(value, list):
for v in value: for elt in value:
attributes.append((key, v)) attributes.append((key, elt))
else: else:
attributes.append((key, value)) attributes.append((key, value))
params = {'IssueInstant':IssueInstant, 'expireInstant':expireInstant,'Recipient':target, 'ResponseID':RequestID, 'username':ticket.user.username, 'attributes':attributes} params = {
if ticket.service_pattern.user_field and ticket.user.attributs.get(ticket.service_pattern.user_field): 'IssueInstant':issue_instant,
'expireInstant':expire_instant,
'Recipient':target,
'ResponseID':request_id,
'username':ticket.user.username,
'attributes':attributes
}
if ticket.service_pattern.user_field and \
ticket.user.attributs.get(ticket.service_pattern.user_field):
params['username'] = ticket.user.attributs.get(ticket.service_pattern.user_field) params['username'] = ticket.user.attributs.get(ticket.service_pattern.user_field)
return render(request, "cas_server/samlValidate.xml", params, content_type="text/xml; charset=utf-8") return render(
request,
"cas_server/samlValidate.xml",
params,
content_type="text/xml; charset=utf-8"
)
except IndexError: except IndexError:
return render(request, "cas_server/samlValidateError.xml", {'code':'VersionMismatch'}, content_type="text/xml; charset=utf-8") return render(
request,
"cas_server/samlValidateError.xml",
{'code':'VersionMismatch'},
content_type="text/xml; charset=utf-8"
)
except KeyError: except KeyError:
return render(request, "cas_server/samlValidateError.xml", {'code':'VersionMismatch'}, content_type="text/xml; charset=utf-8") return render(
request,
"cas_server/samlValidateError.xml",
{'code':'VersionMismatch'},
content_type="text/xml; charset=utf-8"
)
except models.ServiceTicket.DoesNotExist: except models.ServiceTicket.DoesNotExist:
return render(request, "cas_server/samlValidateError.xml", {'code':'AuthnFailed'}, content_type="text/xml; charset=utf-8") return render(
request,
"cas_server/samlValidateError.xml",
{'code':'AuthnFailed'},
content_type="text/xml; charset=utf-8"
)
else: else:
return redirect("login") return redirect("login")