Add javascript login function allow service A to log user to service B via javascript

CORS need to be correctly configured if not this can lead to security issues.
Please do not put Access-Control-Allow-Origin: "*".
You can use django-cors-headers to properly configure CORS
This commit is contained in:
Valentin Samir 2015-11-17 14:50:16 +01:00
parent ee987f6d00
commit 9df1cd2e31
3 changed files with 149 additions and 25 deletions

View file

@ -0,0 +1,53 @@
function cas_login(cas_server_login, service, login_service){
url = cas_server_login + '?service=' + encodeURIComponent(service);
$.ajax({
type: 'GET',
url:url,
beforeSend: function (request) {
request.setRequestHeader("X-AJAX", "1");
},
xhrFields: {
withCredentials: true
},
success: function(data, textStatus, request){
if(data.status == 'success'){
$.ajax({
type: 'GET',
url: data.url,
xhrFields: {
withCredentials: true
},
});
} else {
if(data.detail == "login required"){
window.location.href = cas_server_login + '?service=' + encodeURIComponent(login_service);
} else {
alert('error: ' + data.messages[1].message);
}
}
},
error: function (request, textStatus, errorThrown) {},
});
}
function cas_logout(cas_server_logout){
$.ajax({
type: 'GET',
url:cas_server_logout,
beforeSend: function (request) {
request.setRequestHeader("X-AJAX", "1");
},
xhrFields: {
withCredentials: true
},
error: function (request, textStatus, errorThrown) {},
success: function(data, textStatus, request){
if(data.status == 'error'){
alert('error: ' + data.messages[1].message);
}
},
});
}

View file

@ -14,11 +14,12 @@ 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, HttpResponse
from django.contrib import messages
import random import random
import string import string
import json
try: try:
from urlparse import urlparse, urlunparse, parse_qsl from urlparse import urlparse, urlunparse, parse_qsl
@ -27,6 +28,13 @@ except ImportError:
from urllib.parse import urlparse, urlunparse, parse_qsl, urlencode from urllib.parse import urlparse, urlunparse, parse_qsl, urlencode
def JsonResponse(request, data):
data["messages"] = []
for msg in messages.get_messages(request):
data["messages"].append({'message': msg.message, 'level': msg.level_tag})
return HttpResponse(json.dumps(data), content_type="application/json")
def import_attr(path): def import_attr(path):
"""transform a python module.attr path to the attr""" """transform a python module.attr path to the attr"""
if not isinstance(path, str): if not isinstance(path, str):
@ -42,6 +50,12 @@ def redirect_params(url_name, params=None):
return HttpResponseRedirect(url + "?%s" % params) return HttpResponseRedirect(url + "?%s" % params)
def reverse_params(url_name, params=None, **kwargs):
url = reverse(url_name, **kwargs)
params = urlencode(params if params else {})
return url + "?%s" % params
def update_url(url, params): def update_url(url, params):
"""update params in the `url` query string""" """update params in the `url` query string"""
if not isinstance(url, bytes): if not isinstance(url, bytes):

View file

@ -13,6 +13,7 @@
from .default_settings import settings from .default_settings import settings
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.core.urlresolvers import reverse
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponse, HttpResponseRedirect
from django.contrib import messages from django.contrib import messages
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
@ -30,6 +31,7 @@ import cas_server.utils as utils
import cas_server.forms as forms import cas_server.forms as forms
import cas_server.models as models import cas_server.models as models
from utils import JsonResponse
from .models import ServiceTicket, ProxyTicket, ProxyGrantingTicket from .models import ServiceTicket, ProxyTicket, ProxyGrantingTicket
from .models import ServicePattern from .models import ServicePattern
@ -93,6 +95,7 @@ class LogoutView(View, LogoutMixin):
self.request = request self.request = request
self.service = request.GET.get('service') self.service = request.GET.get('service')
self.url = request.GET.get('url') self.url = request.GET.get('url')
self.ajax = 'HTTP_X_AJAX' in request.META
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
"""methode called on GET request on this view""" """methode called on GET request on this view"""
@ -108,9 +111,17 @@ class LogoutView(View, LogoutMixin):
# else redirect to login page # else redirect to login page
else: else:
if settings.CAS_REDIRECT_TO_LOGIN_AFTER_LOGOUT: if settings.CAS_REDIRECT_TO_LOGIN_AFTER_LOGOUT:
messages.add_message(request, messages.SUCCESS, _(u'Successfully logout')) messages.add_message(request, messages.SUCCESS, _(u'Successfully logout'))
if self.ajax:
url = reverse("cas_server:login")
data = {'status': 'success', 'detail': 'logout', 'url': url}
return JsonResponse(request, data)
else:
return redirect("cas_server:login") return redirect("cas_server:login")
else:
if self.ajax:
data = {'status': 'success', 'detail': 'logout'}
return JsonResponse(request, data)
else: else:
return render(request, settings.CAS_LOGOUT_TEMPLATE) return render(request, settings.CAS_LOGOUT_TEMPLATE)
@ -129,6 +140,7 @@ class LoginView(View, LogoutMixin):
renew = None renew = None
gateway = None gateway = None
method = None method = None
ajax = None
renewed = False renewed = False
warned = False warned = False
@ -146,6 +158,7 @@ class LoginView(View, LogoutMixin):
self.renew = True if request.POST.get('renew') else False self.renew = True if request.POST.get('renew') else False
self.gateway = request.POST.get('gateway') self.gateway = request.POST.get('gateway')
self.method = request.POST.get('method') self.method = request.POST.get('method')
self.ajax = 'HTTP_X_AJAX' in request.META
def check_lt(self): def check_lt(self):
# save LT for later check # save LT for later check
@ -223,6 +236,7 @@ class LoginView(View, LogoutMixin):
self.renew = True if request.GET.get('renew') else False self.renew = True if request.GET.get('renew') else False
self.gateway = request.GET.get('gateway') self.gateway = request.GET.get('gateway')
self.method = request.GET.get('method') self.method = request.GET.get('method')
self.ajax = 'HTTP_X_AJAX' in request.META
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
"""methode called on GET request on this view""" """methode called on GET request on this view"""
@ -265,6 +279,10 @@ class LoginView(View, LogoutMixin):
_(u"Authentication has been required by service %(name)s (%(url)s)") % _(u"Authentication has been required by service %(name)s (%(url)s)") %
{'name': service_pattern.name, 'url': self.service} {'name': service_pattern.name, 'url': self.service}
) )
if self.ajax:
data = {"status": "error", "detail": "confirmation needed"}
return JsonResponse(request, data)
else:
return render( return render(
self.request, self.request,
settings.CAS_WARN_TEMPLATE, settings.CAS_WARN_TEMPLATE,
@ -277,28 +295,39 @@ class LoginView(View, LogoutMixin):
else: else:
# redirect, using method ? # redirect, using method ?
list(messages.get_messages(self.request)) # clean messages before leaving django list(messages.get_messages(self.request)) # clean messages before leaving django
return HttpResponseRedirect( redirect_url = self.user.get_service_url(
self.user.get_service_url(self.service, service_pattern, renew=self.renew) self.service,
service_pattern,
renew=self.renew
) )
if not self.ajax:
return HttpResponseRedirect(redirect_url)
else:
data = {"status": "success", "detail": "auth", "url": redirect_url}
return JsonResponse(self.request, data)
except ServicePattern.DoesNotExist: except ServicePattern.DoesNotExist:
error = 1
messages.add_message( messages.add_message(
self.request, self.request,
messages.ERROR, messages.ERROR,
_(u'Service %(url)s non allowed.') % {'url': self.service} _(u'Service %(url)s non allowed.') % {'url': self.service}
) )
except models.BadUsername: except models.BadUsername:
error = 2
messages.add_message( messages.add_message(
self.request, self.request,
messages.ERROR, messages.ERROR,
_(u"Username non allowed") _(u"Username non allowed")
) )
except models.BadFilter: except models.BadFilter:
error = 3
messages.add_message( messages.add_message(
self.request, self.request,
messages.ERROR, messages.ERROR,
_(u"User charateristics non allowed") _(u"User charateristics non allowed")
) )
except models.UserFieldNotDefined: except models.UserFieldNotDefined:
error = 4
messages.add_message( messages.add_message(
self.request, self.request,
messages.ERROR, messages.ERROR,
@ -307,11 +336,19 @@ class LoginView(View, LogoutMixin):
) )
# 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 self.gateway: if self.gateway and not self.ajax:
list(messages.get_messages(self.request)) # clean messages before leaving django list(messages.get_messages(self.request)) # clean messages before leaving django
return HttpResponseRedirect(self.service) return HttpResponseRedirect(self.service)
return render(self.request, settings.CAS_LOGGED_TEMPLATE, {'session': self.request.session}) if not self.ajax:
return render(
self.request,
settings.CAS_LOGGED_TEMPLATE,
{'session': self.request.session}
)
else:
data = {"status": "error", "detail": "auth", "code": error}
return JsonResponse(self.request, data)
def authenticated(self): def authenticated(self):
"""Processing authenticated users""" """Processing authenticated users"""
@ -322,11 +359,23 @@ class LoginView(View, LogoutMixin):
) )
except models.User.DoesNotExist: except models.User.DoesNotExist:
self.logout() self.logout()
if self.ajax:
data = {
"status": "error",
"detail": "login required",
"url": utils.reverse_params("cas_server:login", params=self.request.GET)
}
return JsonResponse(self.request, data)
else:
return utils.redirect_params("cas_server:login", params=self.request.GET) return utils.redirect_params("cas_server:login", params=self.request.GET)
# if login agains a service is self.requestest # if login agains a service is self.requestest
if self.service: if self.service:
return self.service_login() return self.service_login()
else:
if self.ajax:
data = {"status": "success", "detail": "logged"}
return JsonResponse(self.request, data)
else: else:
return render( return render(
self.request, self.request,
@ -339,7 +388,7 @@ class LoginView(View, LogoutMixin):
if self.service: if self.service:
try: try:
service_pattern = ServicePattern.validate(self.service) service_pattern = ServicePattern.validate(self.service)
if self.gateway: if self.gateway and not self.ajax:
# clean messages before leaving django # clean messages before leaving django
list(messages.get_messages(self.request)) list(messages.get_messages(self.request))
return HttpResponseRedirect(self.service) return HttpResponseRedirect(self.service)
@ -363,6 +412,14 @@ class LoginView(View, LogoutMixin):
messages.ERROR, messages.ERROR,
_(u'Service %s non allowed') % self.service _(u'Service %s non allowed') % self.service
) )
if self.ajax:
data = {
"status": "error",
"detail": "login required",
"url": utils.reverse_params("cas_server:login", params=self.request.GET)
}
return JsonResponse(self.request, data)
else:
return render(self.request, settings.CAS_LOGIN_TEMPLATE, {'form': self.form}) return render(self.request, settings.CAS_LOGIN_TEMPLATE, {'form': self.form})
def common(self): def common(self):