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.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.http import HttpResponseRedirect, HttpResponse
from django.contrib import messages
import random
import string
import json
try:
from urlparse import urlparse, urlunparse, parse_qsl
@ -27,6 +28,13 @@ except ImportError:
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):
"""transform a python module.attr path to the attr"""
if not isinstance(path, str):
@ -42,6 +50,12 @@ def redirect_params(url_name, params=None):
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):
"""update params in the `url` query string"""
if not isinstance(url, bytes):

View File

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