diff --git a/README.md b/README.md index e92bcfb713aee2411935eb141441d189059b62f8..79d0ef19e33702b7df7cfb8ff977f33807bd4549 100644 --- a/README.md +++ b/README.md @@ -33,3 +33,44 @@ This script will download the current edugain metadata aggregate XML and parse a identity providers found in order to derive a list of Organization names, domains and security contacts in CSV format. This list will be printed to stdout. +## Entities Details + +Script: `entity_details.py` + +This script will print out some details about a single entity or a list of entities +passed as a list of entityIDs. + +CSV Format: + +``` +entityID,FederationName,RegistrationAuthority,OrganizationName,TechnicalContact,SupportContact,AdministrativeContact +``` + +Usage: + +``` +usage: entity_details.py [-h] [-e E] [-f F] [-u U] [ENTITYID] + +Show detailed information about an eduGAIN entity. + +positional arguments: + ENTITYID entityID of the entity to look up + +options: + -h, --help show this help message and exit + -e E load entityIDs from file E + -f F load metadata from file F + -u U download metadata from url U (default to https://mds.edugain.org/edugain-v2.xml) + +Examples + +- Retrieve details about a single entity using eduGAIN metadata: + +./entity_details.py https://idp.dir.garr.it/idp/shibboleth + +- Retrieve details for a list of idps using a local metadata file: + +./entity_details.py -e idp_list -f edugain-v2.xml +``` + + diff --git a/entity_details.py b/entity_details.py index 76bb783826885b6f748f171b7917bfa4721e5bfd..ec5bd47ac751af563fe420e0e3d092678cf1ff53 100755 --- a/entity_details.py +++ b/entity_details.py @@ -21,6 +21,10 @@ adm_contact = '' parser = argparse.ArgumentParser(description='Show detailed information about an eduGAIN entity.', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=''' +Output format CSV: + +entity_id,fed_name,fed_email,reg_auth,org_name,tec_email,sup_email,adm_email + Examples - Retrieve details about a single entity using eduGAIN metadata:\n @@ -42,10 +46,11 @@ args = parser.parse_args() feds_request = requests.get("https://technical.edugain.org/api.php?action=list_feds&format") feds = feds_request.json() -def get_fed_name(registration_authority): +def get_fed_attr(registration_authority, attr): for key in feds: if feds[key]['reg_auth'] == registration_authority: - return feds[key]['name'] + if attr in feds[key].keys(): + return feds[key][attr] return None # MAIN @@ -79,7 +84,7 @@ ns = { 'mdrpi': 'urn:oasis:names:tc:SAML:metadata:rpi', } -print('entityID,FederationName,RegistrationAuthority,OrganizationName,TechnicalContact,SupportContact,AdministrativeContact') +print('entity_id,fed_name,fed_email,reg_auth,org_name,tec_email,sup_email,adm_email') for entity_id in entities: @@ -92,7 +97,8 @@ for entity_id in entities: if registration_info: registration_authority = registration_info.attrib['registrationAuthority'].strip() - fed_name = get_fed_name(registration_authority) + fed_name = get_fed_attr(registration_authority, 'name') + fed_contact = get_fed_attr(registration_authority, 'email') orgname = entity.find('./md:Organization/md:OrganizationDisplayName', ns).text.strip() @@ -106,7 +112,7 @@ for entity_id in entities: if adm_contact_el is not None: adm_contact = adm_contact_el.text.replace('mailto:', '') - print('{},{},{},{},{},{}'.format(entity_id, fed_name, registration_authority, orgname, tec_contact, sup_contact, adm_contact)) + print('{},{},{},{},{},{},{},{}'.format(entity_id, fed_name, fed_contact, registration_authority, orgname, tec_contact, sup_contact, adm_contact)) else: diff --git a/entity_search.py b/entity_search.py new file mode 100755 index 0000000000000000000000000000000000000000..1bc4963a3d59fc0ee6a29a98302b4ee2f6f5e67e --- /dev/null +++ b/entity_search.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 + +import sys +import requests +import argparse +import hashlib +import base64 +from xml.etree import ElementTree as ET + + +# DEFINE SOME VARS + +entity_id = None +metadata_file = None +root = None +tec_contact = '' +sup_contact = '' +adm_contact = '' + + +# ARGPARSE + +parser = argparse.ArgumentParser(description='Search for an entity in a metadata aggregate (default to eduGAIN metadata).', + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=''' +Output: entityID + +Examples + +- Search an entity by the SHA256 hash of its certificate in the eduGAIN metadata:\n +./entity_search.py -s SHA256HASH_STRING \n\n + +- Search an entity by the SHA256 hash of its certificate using local metadata file:\n +./entity_details.py -s SHA256HASH_STRING -f edugain-v2.xml +''') +group = parser.add_mutually_exclusive_group() +group.add_argument('-s', type=str, help='search entity by the sha256 hash S') +parser.add_argument('-f', type=str, help='load metadata from file F') +parser.add_argument('-u', type=str, default='https://mds.edugain.org/edugain-v2.xml', + help='download metadata from url U (default to https://mds.edugain.org/edugain-v2.xml)') +args = parser.parse_args() + +# MAIN + +if not args.s: + parser.parse_args(['-h']) + exit(1) + +if args.f: + tree = ET.parse(args.f) + root = tree.getroot() +else: + xml_req = requests.get(args.u) + root = ET.fromstring(xml_req.content) + +orgs = set() + +ns = { + 'md': 'urn:oasis:names:tc:SAML:2.0:metadata', + 'ds': 'http://www.w3.org/2000/09/xmldsig#', + 'mdui': 'urn:oasis:names:tc:SAML:metadata:ui', + 'shibmd': 'urn:mace:shibboleth:metadata:1.0', + 'remd': 'http://refeds.org/metadata', + 'icmd': 'http://id.incommon.org/metadata', + 'mdrpi': 'urn:oasis:names:tc:SAML:metadata:rpi', +} + +entities = root.findall('./md:EntityDescriptor', ns) + +found_entities = set() + +for entity in entities: + certs = entity.findall(f'.//ds:X509Certificate', ns) + for cert in certs: + if cert.text is not None: + if hashlib.sha256(base64.b64decode(cert.text)).hexdigest().lower() == args.s.replace(':', '').lower(): + entity_id = entity.attrib['entityID'].strip() + found_entities.add(entity_id) + +if len(found_entities) > 0: + print(f'Found {len(found_entities)} entities:') + for fentity in found_entities: + print(fentity) +else: + print('No entities found.')