Commit 5abedfdb authored by Xavier Perseguers's avatar Xavier Perseguers
Browse files

[FEATURE] DirectSSO package for typo3.org

Change-Id: I646152d1b94d7f83d258a553c33b3828d7943f6e
Reviewed-on: http://review.typo3.org/41570

Reviewed-by: Xavier Perseguers's avatarXavier Perseguers <xavier@typo3.org>
Tested-by: Xavier Perseguers's avatarXavier Perseguers <xavier@typo3.org>
parent 4893ca29
Install the DirectSSO authentication back-end
---------------------------------------------
0. Install the requirements::
pip install m2crypto
or:
easy_install m2crypto
1. Place the ``directsso`` folder somewhere in the Python path (e.g., ``external_apps``)
2. Add an URLconf entry in :file:`pootle/urls.py`::
url('^auth/', include('directsso.urls')),
Note: the ``/auth/`` prefix can be anything you want, but it has to match the path path configured on the SSO server.
Note: the URLconf line should be high in the list, e.g. right after the Django admin lines.
3. Add the ``directsso.auth.DirectSSOAuthBackend`` back-end to ``AUTHENTICATION_BACKENDS`` in :file:`localsettings.py`, e.g.::
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'directsso.auth.DirectSSOAuthBackend',
)
4. At the end of :file:`localsettings.py` import the directsso settings::
from directsso.settings import *
5. Edit :file:`directsso/settings.py` to suit your needs.
6. Restart your server to take the changes into account
from django.contrib.auth.models import User
class DirectSSOAuthBackend:
def authenticate(self, direct_sso_username=None):
if not direct_sso_username:
return None
try:
user = User.objects.get(username=direct_sso_username)
# we only want to authenticate SSO users,
# not local, valid users (i.e. admins)
if user.has_usable_password():
return None
return user
except:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
from django.db import models
# Create your models here.
import os
# The login URL
DIRECT_SSO_LOGIN_URL = 'http://typo3.org/login/translation'
# Technical contact email, this should be an admin on the local server
DIRECT_SSO_CONTACT_EMAIL = 'xavier@typo3.org'
# Path to the file or ASCII content of the key.
DIRECT_SSO_PUBLIC_KEY = """
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAu+x5xKoCGxc/I4jB5soN
IqklkDH/jvLER0enJd1jIeOJ8jOjo7kMkMnt/5qfpIb4tnCYVLXR7OrhQ16TQfty
wuaeeVothdthc3SWFCB9j3k7983RJOgHsQzFNLpGzokMbI0PIs4xZVEimegS3ItR
1xWINdRkK/R6Y4oCx/SgR1hO5uqnYJNOytHRAe4JxKwNN2LSuvkNob15dHqTKU9M
CwztPTtoHK2KwkgGHq6lLFpGLeKBa7r98Mm1TXV+VsD2M1sT1vgJ6ocXEdf153xY
1zQmUecexTlS/oQnHGdtSWvNdPQBnfD6qADiC9YZwA15WHG2fEqsLuxiVVfIURNo
IXqKpVuS96a3vkAqi/AECd+FzlIgeTOFLf+SZmRhsxECXesdoc2ZL8exBKK05Wqt
YcnIuXhTsiqwDmsr9hhpaXHDFI/7aZFaV0C+Is3z67m/vfK8IRAVv9J5tAyIwqKz
JV75+Wj/Uww0AqulHXm8Ayf+yS7MHwePDA/Sh2cE7Yv6unta4o3D/vhZg0QQlDKJ
IZtldY+0HbgHJqEKxfipcA7gO1GpRlj6HVnwjHwJfcFfRwKdnDy+tWziUJQL50TV
pJvAzTUnrt7k1xomc87kW7CFk58I6f1GH5PhDJnxhPM0i0nSRaLfsDsHv7k310co
5Vpe3Ym6JZF7Nm/cLr/OAfsCAwEAAQ==
-----END PUBLIC KEY-----
"""
# Configured on the Typo3 server
DIRECT_SSO_TPA_ID = "Pootle Server"
# Must be True on production: prevents the same URL to be reused to re-authenticate a user
DIRECT_SSO_ENFORCE_NO_REPEAT_ATTACK = True
# Redirect to this URL after a successfull login
DIRECT_SSO_SUCCESS_REDIRECT_URL = '/accounts/personal/edit/'
"""
This file demonstrates two different styles of tests (one doctest and one
unittest). These will both pass when you run "manage.py test".
Replace these with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.failUnlessEqual(1 + 1, 2)
__test__ = {"doctest": """
Another way to test that 1 + 1 is equal to 2.
>>> 1 + 1 == 2
True
"""}
from django.conf.urls.defaults import *
urlpatterns = patterns('directsso.views',
url(r'^$', 'handle_auth', name="sso-handle-auth"),
)
from M2Crypto import BIO, RSA, EVP
from django.conf import settings
from django.contrib.auth import login, authenticate
from django.contrib.auth.models import User
from django.core.cache import cache
from django.http import HttpResponseRedirect
from django.utils.translation import ugettext_lazy as _
import os, datetime, base64, hashlib
def handle_auth(request):
"""
Main entry point of the Direct SSO agent.
"""
def verify_sig(payload, signature):
"Verify the signature digest using the configured public key"
# Load the public key
if os.path.exists(settings.DIRECT_SSO_PUBLIC_KEY):
ascii_pub_key = open(settings.DIRECT_SSO_PUBLIC_KEY,'r').read()
else:
ascii_pub_key = settings.DIRECT_SSO_PUBLIC_KEY
bio = BIO.MemoryBuffer(ascii_pub_key)
rsa = RSA.load_pub_key_bio(bio)
pubkey = EVP.PKey()
pubkey.assign_rsa(rsa)
pubkey.reset_context(md='sha1')
pubkey.verify_init()
pubkey.verify_update(payload)
return pubkey.verify_final(signature) == 1
# Calculate the hash of the querystring, check if it was
# used before
sso_link_hash = hashlib.new('sha1', request.META.get('QUERY_STRING')).hexdigest()
cache_key = 'sso_hash_%s' %sso_link_hash
if cache.get(cache_key) is None:
cache.set(cache_key,True, 3600)
else:
if settings.DIRECT_SSO_ENFORCE_NO_REPEAT_ATTACK:
raise Exception(_('This single-signon query string was used before. Please try the login process again. If the problem persists, please contact %s.') %settings.DIRECT_SSO_CONTACT_EMAIL)
# Sanitize all passed data
version, user, tpa_id, expires, action, flags, userdata, signature = \
request.GET.get('version',''), \
request.GET.get('user',''), \
request.GET.get('tpa_id',''), \
request.GET.get('expires',''), \
request.GET.get('action',''), \
request.GET.get('flags',''), \
request.GET.get('userdata',''), \
request.GET.get('signature','').decode('hex')
# TPA ID must match the configured one, just in case.
if tpa_id != settings.DIRECT_SSO_TPA_ID:
raise Exception(_('An invalid Application ID key was used. Please report this problem to %s.') %settings.DIRECT_SSO_CONTACT_EMAIL)
# Verify the signature digest
payload = 'version=%s&user=%s&tpa_id=%s&expires=%s&action=%s&flags=%s&userdata=%s' %(
version, user, tpa_id, expires, action, flags, userdata
)
payload = str(payload)
if not verify_sig(payload, signature):
raise Exception(_('Could not validate the SSO signature. Please report this problem to %s.') %settings.DIRECT_SSO_CONTACT_EMAIL)
# Make sure the URL is still valid
if datetime.datetime.fromtimestamp(float(expires)) < datetime.datetime.now():
raise Exception(_('The given single-signon URL has expired. Please try the login process again, if the problem persists, please contact %s.') %settings.DIRECT_SSO_CONTACT_EMAIL)
# All good, extract the userdata, stash it away in a dict
try:
udata = dict()
for pair in base64.b64decode(userdata).split('|'):
try:
k,v = pair.split('=')
udata[k] = v
except:
pass
userdata = udata
except:
raise Exception(_('The given userdata is invalid. Please try the login process again, if the problem persists, please contact %s.') %settings.DIRECT_SSO_CONTACT_EMAIL)
# Check whether the user exists, if not, create him
try:
user = User.objects.get(username=userdata.get('username'))
except User.DoesNotExist:
user = User.objects.create_user(
userdata.get('username'),
userdata.get('email', None)
)
user.is_active = True
user.set_unusable_password()
# Update the user data
if ' ' in userdata.get('name'):
user.first_name, user.last_name = userdata.get('name').rsplit(' ',1)
else:
user.first_name = userdata.get('name')
user.email = userdata.get('email', '')
user.save()
# Log 'em in
user = authenticate(direct_sso_username=userdata.get('username'))
if user is not None and user.is_active:
login(request, user)
else:
raise Exception(_('Could not authenticate you, sorry. Please contact %s') %settings.DIRECT_SSO_CONTACT_EMAIL)
# Success, redirect to the configured URL
return HttpResponseRedirect(settings.DIRECT_SSO_SUCCESS_REDIRECT_URL)
......@@ -2,6 +2,18 @@
- name: Create web directory for localisation packages
file: dest=/var/www/{{ domain }}/public/l10n_ter mode=755 state=directory owner=pootle group=www-data recurse=yes
- name: Install swig (directSSO)
apt: pkg=swig state=installed
- name: Install libssl-dev (directSSO)
apt: pkg=libssl-dev state=installed
- name: Install m2crypto for Python (directSSO)
pip: name=m2crypto virtualenv={{ pootle_virtualenv }}
- name: Install directSSO
copy: src=directsso dest={{ pootle_virtualenv }}/lib/python2.7/site-packages
- name: Install git
apt: pkg=git state=installed update_cache=true
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment