federations sync command
This commit is contained in:
parent
43725c91f7
commit
e2c0dff2e3
|
@ -2,3 +2,5 @@ django
|
|||
django-mellon
|
||||
python-ldap
|
||||
gadjo
|
||||
requests
|
||||
unidecode
|
||||
|
|
4
setup.py
4
setup.py
|
@ -74,7 +74,9 @@ setup(
|
|||
install_requires=['django>=1.7, <1.8',
|
||||
'django-mellon',
|
||||
'python-ldap',
|
||||
'gadjo'
|
||||
'gadjo',
|
||||
'requests',
|
||||
'unidecode'
|
||||
],
|
||||
zip_safe=False,
|
||||
cmdclass={
|
||||
|
|
|
@ -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)
|
|
@ -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',
|
||||
|
|
Reference in New Issue