276 lines
8.1 KiB
Python
Executable File
276 lines
8.1 KiB
Python
Executable File
#!/usr/bin/env python
|
|
import os
|
|
import getopt
|
|
from saml2 import utils, md, samlp, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT
|
|
from saml2 import BINDING_SOAP, class_name
|
|
from saml2.time_util import in_a_while
|
|
from saml2.utils import parse_attribute_map
|
|
from saml2.saml import NAME_FORMAT_URI
|
|
from saml2.sigver import pre_signature_part, sign_statement_using_xmlsec
|
|
|
|
HELP_MESSAGE = """
|
|
Usage: make_metadata [options] 1*configurationfile
|
|
|
|
Valid options:
|
|
hi:k:sv:x:
|
|
-h : Print this help message
|
|
-i id : The ID of the entities descriptor
|
|
-k keyfile : A file with a key to sign the metadata with
|
|
-s : sign the metadta
|
|
-v : How long, in days, the metadata is valid from the
|
|
time of creation
|
|
-x : xmlsec1 binaries to be used for the signing
|
|
"""
|
|
|
|
class Usage(Exception):
|
|
def __init__(self, msg):
|
|
self.msg = msg
|
|
|
|
DEFAULTS = {
|
|
"want_assertions_signed": "true",
|
|
"authn_requests_signed": "false",
|
|
}
|
|
|
|
ORG_ATTR_TRANSL = {
|
|
"organization_name": "name",
|
|
"organization_display_name": "display_name",
|
|
"organization_url": "url",
|
|
}
|
|
|
|
PERSON_ATTR_TRANSL = {
|
|
"company": "company",
|
|
"given_name": "givenname",
|
|
"sur_name": "surname",
|
|
"email_address": "mail",
|
|
"telephone_number": "phone",
|
|
}
|
|
|
|
def do_organization_info(conf, desc):
|
|
try:
|
|
corg = conf["organization"]
|
|
dorg = desc["organization"] = {}
|
|
|
|
for (dkey, ckey) in ORG_ATTR_TRANSL.items():
|
|
try:
|
|
dorg[dkey] = corg[ckey]
|
|
except KeyError:
|
|
pass
|
|
except KeyError:
|
|
pass
|
|
|
|
def do_contact_person_info(conf, desc):
|
|
try:
|
|
corg = conf["contact_person"]
|
|
dorg = desc["contact_person"] = {}
|
|
|
|
for (dkey, ckey) in PERSON_ATTR_TRANSL.items():
|
|
try:
|
|
dorg[dkey] = corg[ckey]
|
|
except:
|
|
pass
|
|
except KeyError:
|
|
pass
|
|
|
|
def do_sp_sso_descriptor(sp, cert, backward_map):
|
|
desc = {
|
|
"protocol_support_enumeration": samlp.NAMESPACE,
|
|
"assertion_consumer_service": {
|
|
"binding": BINDING_HTTP_POST ,
|
|
"location": sp["url"],
|
|
"index": 0,
|
|
},
|
|
"key_descriptor":{
|
|
"key_info": {
|
|
"x509_data": {
|
|
"x509_certificate": cert
|
|
}
|
|
}
|
|
},
|
|
}
|
|
|
|
for key in ["want_assertions_signed", "authn_requests_signed"]:
|
|
try:
|
|
desc[key] = "%s" % sp[key]
|
|
except KeyError:
|
|
desc[key] = DEFAULTS[key]
|
|
|
|
requested_attribute = []
|
|
if "required_attributes" in sp:
|
|
for attr in sp["required_attributes"]:
|
|
try:
|
|
requested_attribute.append({
|
|
"is_required": "true",
|
|
"friendly_name": attr,
|
|
"name_format": NAME_FORMAT_URI,
|
|
"name": backward_map[attr][0]
|
|
})
|
|
except KeyError:
|
|
requested_attribute.append({
|
|
"is_required": "true",
|
|
"friendly_name": attr,
|
|
"name_format": NAME_FORMAT_URI,
|
|
"name": attr
|
|
})
|
|
|
|
if "optional_attributes" in sp:
|
|
for attr in sp["optional_attributes"]:
|
|
try:
|
|
requested_attribute.append({
|
|
"friendly_name": attr,
|
|
"name_format": NAME_FORMAT_URI,
|
|
"name": backward_map[attr][0]
|
|
})
|
|
except KeyError:
|
|
requested_attribute.append({
|
|
"friendly_name": attr,
|
|
"name_format": NAME_FORMAT_URI,
|
|
"name": attr
|
|
})
|
|
|
|
if requested_attribute:
|
|
desc["attribute_consuming_service"] = {
|
|
"requested_attribute": requested_attribute,
|
|
"service_name": {
|
|
"lang":"en",
|
|
"text":sp["name"],
|
|
}
|
|
}
|
|
|
|
return desc
|
|
|
|
def do_idp_sso_descriptor(idp, cert):
|
|
return {
|
|
"protocol_support_enumeration": samlp.NAMESPACE,
|
|
"want_authn_requests_signed": True,
|
|
"single_sign_on_service": {
|
|
"binding": BINDING_HTTP_REDIRECT ,
|
|
"location": idp["url"],
|
|
},
|
|
"key_descriptor":{
|
|
"key_info": {
|
|
"x509_data": {
|
|
"x509_certificate": cert
|
|
}
|
|
}
|
|
},
|
|
}
|
|
|
|
def do_aa_descriptor(aa, cert):
|
|
return {
|
|
"protocol_support_enumeration": samlp.NAMESPACE,
|
|
"attribute_service": {
|
|
"binding": BINDING_SOAP ,
|
|
"location": aa["url"],
|
|
},
|
|
"key_descriptor":{
|
|
"key_info": {
|
|
"x509_data": {
|
|
"x509_certificate": cert
|
|
}
|
|
}
|
|
},
|
|
}
|
|
|
|
def entity_descriptor(confd, valid_for):
|
|
mycert = "".join(open(confd["cert_file"]).readlines()[1:-1])
|
|
|
|
if "attribute_maps" in confd:
|
|
(forward,backward) = parse_attribute_map(confd["attribute_maps"])
|
|
else:
|
|
backward = {}
|
|
|
|
ed = {
|
|
"entity_id": confd["entityid"],
|
|
}
|
|
if valid_for:
|
|
ed["valid_until"] = in_a_while(hours=valid_for)
|
|
|
|
do_organization_info(confd, ed)
|
|
do_contact_person_info(confd, ed)
|
|
|
|
if "sp" in confd["service"]:
|
|
# The SP
|
|
ed["sp_sso_descriptor"] = do_sp_sso_descriptor(confd["service"]["sp"],
|
|
mycert, backward)
|
|
if "idp" in confd["service"]:
|
|
ed["idp_sso_descriptor"] = do_idp_sso_descriptor(
|
|
confd["service"]["idp"], mycert)
|
|
if "aa" in confd["service"]:
|
|
ed["attribute_authority_descriptor"] = do_aa_descriptor(
|
|
confd["service"]["aa"], mycert)
|
|
|
|
return ed
|
|
|
|
def entities_descriptor(eds, valid_for, name, id, sign, xmlsec, keyfile):
|
|
d = {"entity_descriptor": eds}
|
|
if valid_for:
|
|
d["valid_until"] = in_a_while(hours=valid_for)
|
|
if name:
|
|
d["name"] = name
|
|
if id:
|
|
d["id"] = id
|
|
|
|
if sign:
|
|
d["signature"] = pre_signature_part(d["id"])
|
|
|
|
statement = utils.make_instance(md.EntitiesDescriptor, d)
|
|
if sign:
|
|
statement = sign_statement_using_xmlsec("%s" % statement,
|
|
class_name(statement),
|
|
xmlsec, key_file=keyfile)
|
|
return statement
|
|
|
|
|
|
def main(args):
|
|
try:
|
|
opts, args = getopt.getopt(args, "hi:k:sv:x:",
|
|
["help", "name", "id", "keyfile", "sign",
|
|
"valid", "xmlsec"])
|
|
except getopt.GetoptError, err:
|
|
# print help information and exit:
|
|
raise Usage(err) # will print something like "option -a not recognized"
|
|
sys.exit(2)
|
|
|
|
output = None
|
|
verbose = False
|
|
valid_for = 0
|
|
name = ""
|
|
id = ""
|
|
sign = False
|
|
xmlsec = ""
|
|
keyfile = ""
|
|
|
|
try:
|
|
for o, a in opts:
|
|
if o in ("-v", "--valid"):
|
|
valid_for = int(a) * 24
|
|
elif o in ("-h", "--help"):
|
|
raise Usage(HELP_MESSAGE)
|
|
elif o in ("-n", "--name"):
|
|
name = a
|
|
elif o in ("-i", "--id"):
|
|
id = a
|
|
elif o in ("-s", "--sign"):
|
|
sign = True
|
|
elif o in ("-x", "--xmlsec"):
|
|
xmlsec = a
|
|
elif o in ("-k", "--keyfile"):
|
|
keyfile = a
|
|
else:
|
|
assert False, "unhandled option %s" % o
|
|
except Usage, err:
|
|
print >> sys.stderr, sys.argv[0].split("/")[-1] + ": " + str(err.msg)
|
|
print >> sys.stderr, "\t for help use --help"
|
|
return 2
|
|
|
|
eds = []
|
|
for conf in args:
|
|
confd = eval(open(conf).read())
|
|
eds.append(entity_descriptor(confd, valid_for))
|
|
print entities_descriptor(eds, valid_for, name, id, sign, xmlsec, keyfile)
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
|
|
main(sys.argv[1:])
|