From cc7c3e24f827cec30ef1242f54e7852dd87b46ab Mon Sep 17 00:00:00 2001 From: Roland Hedberg Date: Fri, 11 Dec 2009 08:05:48 +0100 Subject: [PATCH] Added Lorenzo's Django branch --- src/djangosaml2/__init__.py | 43 +++++++++++++++++++++++++ src/djangosaml2/backends.py | 49 +++++++++++++++++++++++++++++ src/djangosaml2/urls.py | 21 +++++++++++++ src/djangosaml2/views.py | 62 +++++++++++++++++++++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 src/djangosaml2/__init__.py create mode 100644 src/djangosaml2/backends.py create mode 100644 src/djangosaml2/urls.py create mode 100644 src/djangosaml2/views.py diff --git a/src/djangosaml2/__init__.py b/src/djangosaml2/__init__.py new file mode 100644 index 0000000..611c7f8 --- /dev/null +++ b/src/djangosaml2/__init__.py @@ -0,0 +1,43 @@ +# Copyright (C) 2009 Lorenzo Gil Sanchez +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Quick Intructions +# +# 1. Configure the authentication backend in the settings.py file: +# +# AUTHENTICATION_BACKENDS = ( +# 'djangosaml2.backends.Saml2Backend', +# 'django.contrib.auth.backends.ModelBackend', +#) +# +# 2. Set the login url in the settings.py and include the urls: +# +# settings.py: +# ... +# LOGIN_URL = '/saml2/login/' +# ... +# +# urls.py: +# ... +# (r'^saml2/', include('djangosaml2.urls')), +# ... +# +# 3. Set the SAML config file (see pysaml2 docs for more information +# about this file) +# +# SAML_CONFIG_FILE = path.join(BASEDIR, 'sp.config') +# +# 4. Set the attribute that links the saml identity with the Django username +# +# SAML_USERNAME_ATTRIBUTE = 'uid' diff --git a/src/djangosaml2/backends.py b/src/djangosaml2/backends.py new file mode 100644 index 0000000..ace0b7b --- /dev/null +++ b/src/djangosaml2/backends.py @@ -0,0 +1,49 @@ +# Copyright (C) 2009 Lorenzo Gil Sanchez +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from django.conf import settings +from django.contrib.auth.backends import ModelBackend +from django.contrib.auth.models import User + + +class Saml2Backend(ModelBackend): + + def authenticate(self, session_info=None): + if session_info is None: + return None + + if not session_info.has_key('ava'): + return None + + ava = session_info['ava'] + username = ava[settings.SAML_USERNAME_ATTRIBUTE][0] + + modified = False + try: + user = User.objects.get(username=username) + except User.DoesNotExist: + user = User(username=username, password='') + modified = True + + modified = modified or self._update_user_attributes(user, ava) + + if modified: + user.save() + + return user + + def _update_user_attributes(self, user, attributes): + """TODO""" + + diff --git a/src/djangosaml2/urls.py b/src/djangosaml2/urls.py new file mode 100644 index 0000000..1d7a3ab --- /dev/null +++ b/src/djangosaml2/urls.py @@ -0,0 +1,21 @@ +# Copyright (C) 2009 Lorenzo Gil Sanchez +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from django.conf.urls.defaults import patterns + +urlpatterns = patterns( + 'djangosaml2.views', + (r'^login/$', 'login'), + (r'^acs/$', 'assertion_consumer_service'), +) diff --git a/src/djangosaml2/views.py b/src/djangosaml2/views.py new file mode 100644 index 0000000..4fc5333 --- /dev/null +++ b/src/djangosaml2/views.py @@ -0,0 +1,62 @@ +# Copyright (C) 2009 Lorenzo Gil Sanchez +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import cgi + +from django.conf import settings +from django.contrib import auth +from django.http import HttpResponse, HttpResponseRedirect + +from saml2.client import Saml2Client +from saml2.config import Config + + +def _load_conf(): + conf = Config() + conf.load_file(settings.SAML_CONFIG_FILE) + return conf + + +def login(request): + next = request.GET.get('next', '/') + conf = _load_conf() + srv = conf['service']['sp'] + idp_url = srv['idp'].values()[0] + client = Saml2Client(None, conf) + (session_id, result) = client.authenticate( + conf['entityid'], + idp_url, + srv['url'], + srv['name'], + relay_state=next) + + redirect_url = result[1] + return HttpResponseRedirect(redirect_url) + + +def assertion_consumer_service(request): + conf = _load_conf() + response = cgi.MiniFieldStorage('SAMLResponse', + request.POST['SAMLResponse']) + post = {'SAMLResponse': response} + client = Saml2Client(None, conf) + session_info = client.response(post, conf['entityid'], None) + + user = auth.authenticate(session_info=session_info) + if user is None: + return HttpResponse("user not valid") + + auth.login(request, user) + relay_state = request.POST.get('RelayState', '/') + return HttpResponseRedirect(relay_state)