Project

General

Profile

Download (4.96 KB) Statistics
| Branch: | Tag: | Revision:

root / uauth / organization / management / commands / sync_federations.py @ 03b73cdc

1
import os
2
import json
3
import subprocess
4
import xml.etree.ElementTree as etree
5
import requests
6
import urlparse
7
import re
8
import unidecode
9
import lasso
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
def slugify(text):
21
    text = unidecode.unidecode(text).lower()
22
    return re.sub(r'\W+', '-', text)
23

    
24
SAML2_METADATA_UI_HREF = 'urn:oasis:names:tc:SAML:metadata:ui'
25
ENTITY_DESCRIPTOR_TN = md_element_name('EntityDescriptor')
26
ENTITIES_DESCRIPTOR_TN = md_element_name('EntitiesDescriptor')
27
IDP_SSO_DESCRIPTOR_TN = md_element_name('IDPSSODescriptor')
28
SP_SSO_DESCRIPTOR_TN = md_element_name('SPSSODescriptor')
29
ORGANIZATION_DISPLAY_NAME = md_element_name('OrganizationDisplayName')
30
ORGANIZATION_NAME = md_element_name('OrganizationName')
31
ORGANIZATION = md_element_name('Organization')
32
EXTENSIONS = md_element_name('Extensions')
33
UI_INFO = mdui_element_name('UIInfo')
34
DISPLAY_NAME = mdui_element_name('DisplayName')
35
ENTITY_ID = 'entityID'
36
PROTOCOL_SUPPORT_ENUMERATION = 'protocolSupportEnumeration'
37

    
38
def check_support_saml2(tree):
39
    if tree is not None and lasso.SAML2_PROTOCOL_HREF in tree.get(PROTOCOL_SUPPORT_ENUMERATION):
40
        return True
41
    return False
42

    
43
def verify_metadata(codename, signcert):
44
    metadata = metadata_filename(codename, 'downloaded')
45
    if not signcert:
46
        print 'warn: do not verify %s metadata (no certificate provided)' % codename
47
        ret = True
48
    else:
49
        signcert_pem = metadata_filename(codename, 'signcert.pem')
50
        dir = os.path.join(METADATAS_DIR, codename)
51
        f = open(signcert_pem, 'wb')
52
        f.write(signcert)
53
        f.close()
54
        ret = 0 == subprocess.call(['xmlsec1', '--verify',
55
            '--id-attr:ID', 'EntitiesDescriptor',
56
            '--pubkey-cert-pem', signcert_pem,
57
            '--enabled-key-data', 'key-name',
58
            metadata])
59
    if ret:
60
        os.rename(metadata, metadata_filename(codename))
61
    else:
62
        print 'warn: bad signature for %s metadata' % codename
63
        os.rename(metadata, metadata_filename(codename, 'bad_signature'))
64
    return ret
65

    
66
class Command(BaseCommand):
67

    
68
    def handle(self, *args, **options):
69

    
70
        idps = []
71
        if not os.path.exists(settings.METADATAS_DIR):
72
            os.mkdir(settings.METADATAS_DIR)
73

    
74
        for metadata_uri in settings.METADATA_URIS:
75
            metadata = requests.get(metadata_uri)
76
            url = urlparse.urlparse(metadata_uri)
77
            metadata_file_path = os.path.join(settings.METADATAS_DIR,
78
                                              url.path.split('/')[-1])
79
            with open(metadata_file_path, 'w') as metadata_file:
80
                metadata_file.write(metadata.content)
81

    
82
            idp_dir, ext = os.path.splitext(metadata_file_path)
83
            if not os.path.exists(idp_dir):
84
                os.mkdir(os.path.join(settings.METADATAS_DIR, idp_dir))
85
            doc = etree.parse(metadata_file_path)
86
            if doc.getroot().tag == ENTITIES_DESCRIPTOR_TN:
87
                entity_descriptors = doc.getroot().findall(ENTITY_DESCRIPTOR_TN)
88
            for entity_descriptor in entity_descriptors:
89
                idp = check_support_saml2(entity_descriptor.find(IDP_SSO_DESCRIPTOR_TN))
90
                if not idp:
91
                    continue
92
                entity_id = entity_descriptor.get(ENTITY_ID)
93
                name = None
94
                display_name = entity_descriptor.find('.//%s/%s/%s' % (EXTENSIONS, UI_INFO, DISPLAY_NAME))
95
                if display_name is not None:
96
                    name = display_name.text
97
                if not name:
98
                    organization = entity_descriptor.find(ORGANIZATION)
99
                    if organization is not None:
100
                        organization_display_name = organization.find(ORGANIZATION_DISPLAY_NAME)
101
                        organization_name = organization.find(ORGANIZATION_NAME)
102
                        if organization_display_name is not None:
103
                            name = organization_display_name.text
104
                        elif organization_name is not None:
105
                            name = organization_name.text
106
                if not name:
107
                    name = entity_id
108
                idp = entity_descriptor.find(IDP_SSO_DESCRIPTOR_TN)
109
                entity_id = entity_descriptor.get(ENTITY_ID)
110
                entities = etree.tostring(entity_descriptor)
111
                metadata_dest_filename = os.path.join(idp_dir, '%s.xml' % slugify(name))
112
                with open(metadata_dest_filename, 'w') as metadata:
113
                    metadata.write(entities)
114

    
115
                idps.append({'METADATA': os.path.abspath(metadata_dest_filename),
116
                             'ENTITY_ID': entity_id,
117
                             'NAME': name})
118

    
119
        with open(os.path.join(settings.BASE_DIR, 'idps.json'), 'w') as idps_file:
120
            json.dump(idps, idps_file)
(2-2/2)