federations sync command

This commit is contained in:
Serghei Mihai 2015-03-23 22:06:00 +01:00
parent 43725c91f7
commit e2c0dff2e3
6 changed files with 136 additions and 16 deletions

View File

@ -2,3 +2,5 @@ django
django-mellon
python-ldap
gadjo
requests
unidecode

View File

@ -74,7 +74,9 @@ setup(
install_requires=['django>=1.7, <1.8',
'django-mellon',
'python-ldap',
'gadjo'
'gadjo',
'requests',
'unidecode'
],
zip_safe=False,
cmdclass={

View File

View File

@ -0,0 +1,119 @@
import os
import json
import subprocess
import xml.etree.ElementTree as etree
import requests
import urlparse
import unidecode
import re
import lasso
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
def slugify(text):
text = unidecode.unidecode(text).lower()
return re.sub(r'\W+', '-', text)
def md_element_name(tag_name):
return '{%s}%s' % (lasso.SAML2_METADATA_HREF, tag_name)
def mdui_element_name(tag_name):
return '{%s}%s' % (SAML2_METADATA_UI_HREF, tag_name)
SAML2_METADATA_UI_HREF = 'urn:oasis:names:tc:SAML:metadata:ui'
ENTITY_DESCRIPTOR_TN = md_element_name('EntityDescriptor')
ENTITIES_DESCRIPTOR_TN = md_element_name('EntitiesDescriptor')
IDP_SSO_DESCRIPTOR_TN = md_element_name('IDPSSODescriptor')
SP_SSO_DESCRIPTOR_TN = md_element_name('SPSSODescriptor')
ORGANIZATION_DISPLAY_NAME = md_element_name('OrganizationDisplayName')
ORGANIZATION_NAME = md_element_name('OrganizationName')
ORGANIZATION = md_element_name('Organization')
EXTENSIONS = md_element_name('Extensions')
UI_INFO = mdui_element_name('UIInfo')
DISPLAY_NAME = mdui_element_name('DisplayName')
ENTITY_ID = 'entityID'
PROTOCOL_SUPPORT_ENUMERATION = 'protocolSupportEnumeration'
def check_support_saml2(tree):
if tree is not None and lasso.SAML2_PROTOCOL_HREF in tree.get(PROTOCOL_SUPPORT_ENUMERATION):
return True
return False
def verify_metadata(codename, signcert):
metadata = metadata_filename(codename, 'downloaded')
if not signcert:
print 'warn: do not verify %s metadata (no certificate provided)' % codename
ret = True
else:
signcert_pem = metadata_filename(codename, 'signcert.pem')
dir = os.path.join(METADATAS_DIR, codename)
f = open(signcert_pem, 'wb')
f.write(signcert)
f.close()
ret = 0 == subprocess.call(['xmlsec1', '--verify',
'--id-attr:ID', 'EntitiesDescriptor',
'--pubkey-cert-pem', signcert_pem,
'--enabled-key-data', 'key-name',
metadata])
if ret:
os.rename(metadata, metadata_filename(codename))
else:
print 'warn: bad signature for %s metadata' % codename
os.rename(metadata, metadata_filename(codename, 'bad_signature'))
return ret
class Command(BaseCommand):
def handle(self, *args, **options):
idps = []
if not os.path.exists(settings.METADATAS_DIR):
os.mkdir(settings.METADATAS_DIR)
for metadata_uri in settings.METADATA_URIS:
metadata = requests.get(metadata_uri)
url = urlparse.urlparse(metadata_uri)
metadata_file_path = os.path.join(settings.METADATAS_DIR,
url.path.split('/')[-1])
with open(metadata_file_path, 'w') as metadata_file:
metadata_file.write(metadata.content)
idp_dir, ext = os.path.splitext(metadata_file_path)
if not os.path.exists(idp_dir):
os.mkdir(os.path.join(settings.METADATAS_DIR, idp_dir))
doc = etree.parse(metadata_file_path)
if doc.getroot().tag == ENTITIES_DESCRIPTOR_TN:
entity_descriptors = doc.getroot().findall(ENTITY_DESCRIPTOR_TN)
for entity_descriptor in entity_descriptors:
idp = check_support_saml2(entity_descriptor.find(IDP_SSO_DESCRIPTOR_TN))
if not idp:
continue
entity_id = entity_descriptor.get(ENTITY_ID)
name = None
display_name = entity_descriptor.find('.//%s/%s/%s' % (EXTENSIONS, UI_INFO, DISPLAY_NAME))
if display_name is not None:
name = display_name.text
if not name:
organization = entity_descriptor.find(ORGANIZATION)
if organization is not None:
organization_display_name = organization.find(ORGANIZATION_DISPLAY_NAME)
organization_name = organization.find(ORGANIZATION_NAME)
if organization_display_name is not None:
name = organization_display_name.text
elif organization_name is not None:
name = organization_name.text
if not name:
name = entity_id
idp = entity_descriptor.find(IDP_SSO_DESCRIPTOR_TN)
entity_id = entity_descriptor.get(ENTITY_ID)
entities = etree.tostring(entity_descriptor)
metadata_dest_filename = os.path.join(idp_dir, '%s.xml' % slugify(name))
with open(metadata_dest_filename, 'w') as metadata:
metadata.write(entities)
idps.append({'METADATA': os.path.abspath(metadata_dest_filename), 'ENTITY_ID': entity_id,
'NAME': name})
with open('uauth/idps.json', 'w') as idps_file:
json.dump(idps, idps_file)

View File

@ -1,15 +1,8 @@
"""
Django settings for uauth project.
For more information on this file, see
https://docs.djangoproject.com/en/1.7/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.7/ref/settings/
"""
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
import json
from django.conf import global_settings
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
@ -38,6 +31,14 @@ INSTALLED_APPS = (
'django.contrib.staticfiles',
)
METADATA_URIS = (
'https://federation.renater.fr/test/renater-test-metadata.xml',
# 'https://federation.renater.fr/renater/idps-renater-metadata.xml',
# 'https://federation.renater.fr/edugain/idps-edugain-metadata.xml',
)
METADATAS_DIR = os.path.join(BASE_DIR, 'metadatas')
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
@ -52,10 +53,6 @@ ROOT_URLCONF = 'uauth.urls'
WSGI_APPLICATION = 'uauth.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',