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,38 +1,50 @@
# ⁻*- coding: utf-8 -*-
import default_settings
"""views for the app"""
import cas_server.default_settings
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.contrib import messages
from django.views.decorators.csrf import csrf_exempt
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 urllib
from datetime import datetime, timedelta
from lxml import etree
from datetime import timedelta
import utils
import forms
import models
from . import utils
from . import forms
from . import models
def _logout(request):
try: del request.session["authenticated"]
except KeyError: pass
try: del request.session["username"]
except KeyError: pass
try: del request.session["warn"]
except KeyError: pass
"""Clean sessions variables"""
try:
del request.session["authenticated"]
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={}):
url = reverse(url_name, args = args)
params = urllib.urlencode(params)
def redirect_params(url_name, params=None):
"""Redirect to `url_name` with `params` as querystring"""
url = reverse(url_name)
params = urllib.urlencode(params if params else {})
return HttpResponseRedirect(url + "?%s" % params)
def login(request):
"""credential requestor / acceptor"""
user = None
form = None
service_pattern = None
@ -45,7 +57,10 @@ def login(request):
method = request.POST.get('method')
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():
user = models.User.objects.get(username=form.cleaned_data['username'])
request.session.set_expiry(0)
@ -63,10 +78,13 @@ def login(request):
method = request.GET.get('method')
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 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:
user = models.User.objects.get(username=request.session["username"])
except models.User.DoesNotExist:
@ -80,20 +98,51 @@ def login(request):
service_pattern = models.ServicePattern.validate(service)
# is the current user allowed on this service
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:
messages.add_message(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)})
messages.add_message(
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:
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:
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:
messages.add_message(request, messages.ERROR, _(u"Username non allowed"))
messages.add_message(
request,
messages.ERROR,
_(u"Username non allowed")
)
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:
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:
@ -106,17 +155,32 @@ def login(request):
try:
service_pattern = models.ServicePattern.validate(service)
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)
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:
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:
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})
def logout(request):
"""destroy CAS session (logout)"""
service = request.GET.get('service')
if request.session.get("authenticated"):
user = models.User.objects.get(username=request.session["username"])
@ -133,12 +197,19 @@ def logout(request):
return redirect("login")
def validate(request):
"""service ticket validation"""
service = request.GET.get('service')
ticket = request.GET.get('ticket')
renew = True if request.GET.get('renew') else False
if service and ticket:
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.save()
return HttpResponse("yes\n", content_type="text/plain")
@ -147,119 +218,270 @@ def validate(request):
else:
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')
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
if service and ticket:
for t in typ:
if ticket.startswith(t):
for typ in ticket_type:
if ticket.startswith(typ):
break
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:
proxies = []
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"):
ticket = models.ProxyTicket.objects.get(value=ticket, service=service, validate=False, renew=renew, creation__gt=(datetime.now() - timedelta(seconds=settings.CAS_TICKET_VALIDITY)))
for p in ticket.proxies.all():
proxies.append(p.url)
ticket = models.ProxyTicket.objects.get(
value=ticket,
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.save()
attributes = []
for key, value in ticket.attributs.items():
if isinstance(value, list):
for v in value:
attributes.append((key, v))
for elt in value:
attributes.append((key, elt))
else:
attributes.append((key, value))
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)
if pgtUrl and pgtUrl.startswith("https://"):
pattern = models.ServicePattern.validate(pgtUrl)
if pgt_url and pgt_url.startswith("https://"):
pattern = models.ServicePattern.validate(pgt_url)
if pattern.proxy:
proxyid = models._gen_ticket('PGTIOU')
pticket = models.ProxyGrantingTicket.objects.create(user=ticket.user, service=pgtUrl, service_pattern=pattern)
url = utils.update_url(pgtUrl, {'pgtIou':proxyid, 'pgtId':pticket.value})
proxyid = models.gen_pgtiou()
pticket = models.ProxyGrantingTicket.objects.create(
user=ticket.user,
service=pgt_url,
service_pattern=pattern
)
url = utils.update_url(pgt_url, {'pgtIou':proxyid, 'pgtId':pticket.value})
try:
r = requests.get(url, verify=settings.CAS_PROXY_CA_CERTIFICATE_PATH)
if r.status_code == 200:
ret = requests.get(url, verify=settings.CAS_PROXY_CA_CERTIFICATE_PATH)
if ret.status_code == 200:
params['proxyGrantingTicket'] = proxyid
else:
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:
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:
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:
return render(request, "cas_server/serviceValidate.xml", params, content_type="text/xml; charset=utf-8")
except (models.ServiceTicket.DoesNotExist, models.ProxyTicket.DoesNotExist, models.ServicePattern.DoesNotExist):
return render(request, "cas_server/serviceValidateError.xml", {'code':'INVALID_TICKET'}, content_type="text/xml; charset=utf-8")
return render(
request,
"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:
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):
return psValidate(request)
def proxyValidate(request):
return psValidate(request, ["ST", "PT"])
def service_validate(request):
"""service ticket validation CAS 2.0 (also work for CAS 3.0)"""
return ps_validate(request)
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):
"""proxy ticket service"""
pgt = request.GET.get('pgt')
targetService = request.GET.get('targetService')
if pgt and targetService:
target_service = request.GET.get('targetService')
if pgt and target_service:
try:
pattern = models.ServicePattern.validate(targetService)
ticket = models.ProxyGrantingTicket.objects.get(value=pgt, creation__gt=(datetime.now() - timedelta(seconds=settings.CAS_TICKET_VALIDITY)))
# is the target service allowed
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)
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)
return render(request, "cas_server/proxy.xml", {'ticket':pticket.value}, content_type="text/xml; charset=utf-8")
except (models.ProxyGrantingTicket.DoesNotExist, models.ServicePattern.DoesNotExist, models.BadUsername, models.BadFilter):
return render(request, "cas_server/serviceValidateError.xml", {'code':'INVALID_TICKET'}, content_type="text/xml; charset=utf-8")
return render(
request,
"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:
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):
return serviceValidate(request)
def p3_service_validate(request):
"""service ticket validation CAS 3.0"""
return service_validate(request)
def p3_proxyValidate(request):
return proxyValidate(request)
def p3_proxy_validate(request):
"""service/proxy ticket validation CAS 3.0"""
return proxy_validate(request)
@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':
target = request.GET.get('TARGET')
root = etree.fromstring(request.body)
try:
auth_req = root.getchildren()[1].getchildren()[0]
IssueInstant = auth_req.attrib['IssueInstant']
RequestID = auth_req.attrib['RequestID']
issue_instant = auth_req.attrib['IssueInstant']
request_id = auth_req.attrib['RequestID']
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.save()
expireInstant = (ticket.creation + timedelta(seconds=settings.CAS_TICKET_VALIDITY)).isoformat()
expire_instant = (ticket.creation + \
timedelta(seconds=settings.CAS_TICKET_VALIDITY)).isoformat()
attributes = []
for key, value in ticket.attributs.items():
if isinstance(value, list):
for v in value:
attributes.append((key, v))
for elt in value:
attributes.append((key, elt))
else:
attributes.append((key, value))
params = {'IssueInstant':IssueInstant, 'expireInstant':expireInstant,'Recipient':target, 'ResponseID':RequestID, 'username':ticket.user.username, 'attributes':attributes}
if ticket.service_pattern.user_field and ticket.user.attributs.get(ticket.service_pattern.user_field):
params = {
'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)
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:
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:
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:
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:
return redirect("login")