Project

General

Profile

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

root / uauth / organization / management / commands / sync_federations.py @ 26f6a4aa

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)
(2-2/2)