1
|
import os
|
2
|
import json
|
3
|
import subprocess
|
4
|
import xml.etree.ElementTree as etree
|
5
|
import requests
|
6
|
import urlparse
|
7
|
|
8
|
import lasso
|
9
|
from slugify import slugify
|
10
|
|
11
|
from django.conf import settings
|
12
|
from django.core.management.base import BaseCommand, CommandError
|
13
|
|
14
|
def md_element_name(tag_name):
|
15
|
return '{%s}%s' % (lasso.SAML2_METADATA_HREF, tag_name)
|
16
|
|
17
|
def mdui_element_name(tag_name):
|
18
|
return '{%s}%s' % (SAML2_METADATA_UI_HREF, tag_name)
|
19
|
|
20
|
SAML2_METADATA_UI_HREF = 'urn:oasis:names:tc:SAML:metadata:ui'
|
21
|
ENTITY_DESCRIPTOR_TN = md_element_name('EntityDescriptor')
|
22
|
ENTITIES_DESCRIPTOR_TN = md_element_name('EntitiesDescriptor')
|
23
|
IDP_SSO_DESCRIPTOR_TN = md_element_name('IDPSSODescriptor')
|
24
|
SP_SSO_DESCRIPTOR_TN = md_element_name('SPSSODescriptor')
|
25
|
ORGANIZATION_DISPLAY_NAME = md_element_name('OrganizationDisplayName')
|
26
|
ORGANIZATION_NAME = md_element_name('OrganizationName')
|
27
|
ORGANIZATION = md_element_name('Organization')
|
28
|
EXTENSIONS = md_element_name('Extensions')
|
29
|
UI_INFO = mdui_element_name('UIInfo')
|
30
|
DISPLAY_NAME = mdui_element_name('DisplayName')
|
31
|
ENTITY_ID = 'entityID'
|
32
|
PROTOCOL_SUPPORT_ENUMERATION = 'protocolSupportEnumeration'
|
33
|
|
34
|
def check_support_saml2(tree):
|
35
|
if tree is not None and lasso.SAML2_PROTOCOL_HREF in tree.get(PROTOCOL_SUPPORT_ENUMERATION):
|
36
|
return True
|
37
|
return False
|
38
|
|
39
|
def verify_metadata(codename, signcert):
|
40
|
metadata = metadata_filename(codename, 'downloaded')
|
41
|
if not signcert:
|
42
|
print 'warn: do not verify %s metadata (no certificate provided)' % codename
|
43
|
ret = True
|
44
|
else:
|
45
|
signcert_pem = metadata_filename(codename, 'signcert.pem')
|
46
|
dir = os.path.join(METADATAS_DIR, codename)
|
47
|
f = open(signcert_pem, 'wb')
|
48
|
f.write(signcert)
|
49
|
f.close()
|
50
|
ret = 0 == subprocess.call(['xmlsec1', '--verify',
|
51
|
'--id-attr:ID', 'EntitiesDescriptor',
|
52
|
'--pubkey-cert-pem', signcert_pem,
|
53
|
'--enabled-key-data', 'key-name',
|
54
|
metadata])
|
55
|
if ret:
|
56
|
os.rename(metadata, metadata_filename(codename))
|
57
|
else:
|
58
|
print 'warn: bad signature for %s metadata' % codename
|
59
|
os.rename(metadata, metadata_filename(codename, 'bad_signature'))
|
60
|
return ret
|
61
|
|
62
|
class Command(BaseCommand):
|
63
|
|
64
|
def handle(self, *args, **options):
|
65
|
|
66
|
idps = []
|
67
|
if not os.path.exists(settings.METADATAS_DIR):
|
68
|
os.mkdir(settings.METADATAS_DIR)
|
69
|
|
70
|
for metadata_uri in settings.METADATA_URIS:
|
71
|
metadata = requests.get(metadata_uri)
|
72
|
url = urlparse.urlparse(metadata_uri)
|
73
|
metadata_file_path = os.path.join(settings.METADATAS_DIR,
|
74
|
url.path.split('/')[-1])
|
75
|
with open(metadata_file_path, 'w') as metadata_file:
|
76
|
metadata_file.write(metadata.content)
|
77
|
|
78
|
idp_dir, ext = os.path.splitext(metadata_file_path)
|
79
|
if not os.path.exists(idp_dir):
|
80
|
os.mkdir(os.path.join(settings.METADATAS_DIR, idp_dir))
|
81
|
doc = etree.parse(metadata_file_path)
|
82
|
if doc.getroot().tag == ENTITIES_DESCRIPTOR_TN:
|
83
|
entity_descriptors = doc.getroot().findall(ENTITY_DESCRIPTOR_TN)
|
84
|
for entity_descriptor in entity_descriptors:
|
85
|
idp = check_support_saml2(entity_descriptor.find(IDP_SSO_DESCRIPTOR_TN))
|
86
|
if not idp:
|
87
|
continue
|
88
|
entity_id = entity_descriptor.get(ENTITY_ID)
|
89
|
name = None
|
90
|
display_name = entity_descriptor.find('.//%s/%s/%s' % (EXTENSIONS, UI_INFO, DISPLAY_NAME))
|
91
|
if display_name is not None:
|
92
|
name = display_name.text
|
93
|
if not name:
|
94
|
organization = entity_descriptor.find(ORGANIZATION)
|
95
|
if organization is not None:
|
96
|
organization_display_name = organization.find(ORGANIZATION_DISPLAY_NAME)
|
97
|
organization_name = organization.find(ORGANIZATION_NAME)
|
98
|
if organization_display_name is not None:
|
99
|
name = organization_display_name.text
|
100
|
elif organization_name is not None:
|
101
|
name = organization_name.text
|
102
|
if not name:
|
103
|
name = entity_id
|
104
|
idp = entity_descriptor.find(IDP_SSO_DESCRIPTOR_TN)
|
105
|
entity_id = entity_descriptor.get(ENTITY_ID)
|
106
|
entities = etree.tostring(entity_descriptor)
|
107
|
metadata_dest_filename = os.path.join(idp_dir, '%s.xml' % slugify(name))
|
108
|
with open(metadata_dest_filename, 'w') as metadata:
|
109
|
metadata.write(entities)
|
110
|
|
111
|
idps.append({'METADATA': os.path.abspath(metadata_dest_filename),
|
112
|
'ENTITY_ID': entity_id,
|
113
|
'NAME': name})
|
114
|
|
115
|
with open(os.path.join(settings.BASE_DIR, 'idps.json'), 'w') as idps_file:
|
116
|
json.dump(idps, idps_file)
|