def __init__(self, idp_conf, logger, conf, publicKey, privateKey, metadataList): """ Constructor. Initiates the class. :param logger: Logger to be used when something needs to be logged. :param conf: idp_proxy_conf see IdpProxy/conig/idp_proxy_conf.example.py :param key: A RSA key to be used for encryption. :param metadataList: A list of metadata files. [{"local": ["swamid-1.0.xml"]}, {"local": ["sp.xml"]}] :raise: """ if (logger is None) or (conf is None) or (publicKey is None)or (privateKey is None): raise ValueError( "A new instance must include a value for logger, conf and key.") #Public key to be used for encryption. self.publicKey = publicKey self.privateKey = privateKey #Used for presentation of mako files. self.lookup = TemplateLookup( directories=[MetadataGeneration.CONST_STATIC_MAKO + 'templates', MetadataGeneration.CONST_STATIC_MAKO + 'htdocs'], module_directory='modules', input_encoding='utf-8', output_encoding='utf-8') #The logger. self.logger = logger #A list of all social services used by this IdPproxy. self.socialServiceKeyList = [] #A list of all service providers used by this sp. self.spKeyList = [] for key in conf: self.socialServiceKeyList.append(conf[key]["name"]) try: xmlsec_path = get_xmlsec_binary(["/opt/local/bin"]) except: try: xmlsec_path = get_xmlsec_binary(["/usr/local/bin"]) except: self.logger.info('Xmlsec must be installed! Tries /usr/bin/xmlsec1.') xmlsec_path = '/usr/bin/xmlsec1' self.xmlsec_path = xmlsec_path config = Config() config.disable_ssl_certificate_validation = True config.key_file = idp_conf["key_file"] config.cert_file = idp_conf["cert_file"] config.xmlsec_binary = idp_conf["xmlsec_binary"] config.debug = idp_conf["debug"] for metadata in metadataList: mds = MetadataStore(MetadataGeneration.CONST_ONTS.values(), MetadataGeneration.CONST_ATTRCONV, config) mds.imp(metadata) for entityId in mds.keys(): self.spKeyList.append(entityId)
def test_metadata_file(): sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) mds = MetadataStore(list(ONTS.values()), ATTRCONV, sec_config, disable_ssl_certificate_validation=True) mds.imp(METADATACONF["8"]) print((len(list(mds.keys())))) assert len(list(mds.keys())) == 560
def load(self, cnf, metadata_construction=False): """ The base load method, loads the configuration :param cnf: The configuration as a dictionary :param metadata_construction: Is this only to be able to construct metadata. If so some things can be left out. :return: The Configuration instance """ for arg in COMMON_ARGS: try: self._attr[""][arg] = cnf[arg] except KeyError: pass if "service" in cnf: for typ in ["aa", "idp", "sp", "pdp"]: try: self.load_special(cnf["service"][typ], typ, metadata_construction=metadata_construction) except KeyError: pass if not metadata_construction: if "xmlsec_binary" not in self._attr[""]: self._attr[""]["xmlsec_binary"] = get_xmlsec_binary() # verify that xmlsec is where it's supposed to be if not os.access(self._attr[""]["xmlsec_binary"], os.F_OK): raise Exception("xmlsec binary not in '%s' !" % ( self._attr[""]["xmlsec_binary"])) self.load_complex(cnf, metadata_construction=metadata_construction) self.context = self.def_context return self
def test_metadata_file(): sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) mds = MetadataStore(ONTS.values(), ATTRCONV, sec_config, disable_ssl_certificate_validation=True) mds.imp(METADATACONF["8"]) print(len(mds.keys())) assert len(mds.keys()) == 560
def test_load_local_dir(): sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) mds = MetadataStore(ONTS.values(), ATTRCONV, sec_config, disable_ssl_certificate_validation=True) mds.imp(METADATACONF["9"]) print mds assert len(mds) == 3 # Three sources assert len(mds.keys()) == 4 # number of idps
def xmlsec(): """ xmlsec path """ if get_xmlsec_binary: return get_xmlsec_binary() else: return '/usr/local/bin/xmlsec1'
def test_load_external(): sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) mds = MetadataStore(ATTRCONV, sec_config, disable_ssl_certificate_validation=True) mds.imp(METADATACONF["10"]) print(mds) assert len(mds) == 1 # One source assert len(mds.keys()) > 1 # number of idps
def test_mdx_certs(): sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) http = HTTPBase(verify=False, ca_bundle=None) mdx = MetaDataMDX(quote_plus, ONTS.values(), ATTRCONV, "http://pyff-test.nordu.net", sec_config, None, http) foo = mdx.certs("https://idp.umu.se/saml2/idp/metadata.php", "idpsso") assert len(foo) == 1
def test_load_extern_incommon(): sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) mds = MetadataStore(ONTS.values(), ATTRCONV, sec_config, disable_ssl_certificate_validation=True) mds.imp(METADATACONF["10"]) print(mds) assert mds assert len(mds.keys())
def test_mdx_certs(): sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) http = HTTPBase(verify=False, ca_bundle=None) mdx = MetaDataMDX(ONTS.values(), ATTRCONV, "http://pyff-test.nordu.net", sec_config, None, http) foo = mdx.certs("https://idp.umu.se/saml2/idp/metadata.php", "idpsso") assert len(foo) == 1
def test_load_external(): sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) mds = MetadataStore(ONTS.values(), ATTRCONV, sec_config, disable_ssl_certificate_validation=True) mds.imp(METADATACONF["10"]) print(mds) assert len(mds) == 1 # One source assert len(mds.keys()) > 1 # number of idps
def test_load_local_dir(): sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) mds = MetadataStore(ATTRCONV, sec_config, disable_ssl_certificate_validation=True) mds.imp(METADATACONF["9"]) print(mds) assert len(mds) == 3 # Three sources assert len(mds.keys()) == 4 # number of idps
def test_load_extern_incommon(): sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) mds = MetadataStore(ATTRCONV, sec_config, disable_ssl_certificate_validation=True) mds.imp(METADATACONF["10"]) print(mds) assert mds assert len(mds.keys())
def test_signed_metadata_proper_str_bytes_handling(): sp_conf_2 = sp_conf.copy() sp_conf_2['key_file'] = full_path("test.key") sp_conf_2['cert_file'] = full_path("inc-md-cert.pem") # requires xmlsec binaries per https://pysaml2.readthedocs.io/en/latest/examples/sp.html sp_conf_2['xmlsec_binary'] = sigver.get_xmlsec_binary(["/opt/local/bin"]) cnf = SPConfig().load(sp_conf_2, metadata_construction=True) # This will raise TypeError if string/bytes handling is not correct sp_metadata = create_metadata_string('', config=cnf, sign=True)
def test_mdx_service(): sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) http = HTTPBase(verify=False, ca_bundle=None) mdx = MetaDataMDX(quote_plus, ONTS.values(), ATTRCONV, "http://pyff-test.nordu.net", sec_config, None, http) foo = mdx.service("https://idp.umu.se/saml2/idp/metadata.php", "idpsso_descriptor", "single_sign_on_service") assert len(foo) == 1 assert foo.keys()[0] == BINDING_HTTP_REDIRECT
def test_mdx_service(): sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) http = HTTPBase(verify=False, ca_bundle=None) mdx = MetaDataMDX(ONTS.values(), ATTRCONV, "http://pyff-test.nordu.net", sec_config, None, http) foo = mdx.service("https://idp.umu.se/saml2/idp/metadata.php", "idpsso_descriptor", "single_sign_on_service") assert len(foo) == 1 assert foo.keys()[0] == BINDING_HTTP_REDIRECT
def dispatch(self, request, *args, **kwargs): """ Construct IDP server with config from settings dict """ conf = IdPConfig() try: SAML_IDP_CONFIG = { # pylint: disable=invalid-name 'debug': settings.DEBUG, 'xmlsec_binary': get_xmlsec_binary(['/opt/local/bin', '/usr/bin/xmlsec1']), 'entityid': '%s/saml/metadata/' % settings.BASE_URL, 'description': 'longguikeji IdP setup', 'service': { 'idp': { 'name': 'Django localhost IdP', 'endpoints': { 'single_sign_on_service': [ ('%s/saml/sso/post/' % settings.BASE_URL, BINDING_HTTP_POST), ('%s/saml/sso/redirect/' % settings.BASE_URL, BINDING_HTTP_REDIRECT), ], }, 'name_id_format': [NAMEID_FORMAT_EMAILADDRESS, NAMEID_FORMAT_UNSPECIFIED], 'sign_response': True, 'sign_assertion': True, }, }, 'metadata': { 'local': [os.path.join(os.path.join(os.path.join(BASEDIR, 'djangosaml2idp'), \ 'saml2_config'), f) for f in os.listdir(BASEDIR + '/djangosaml2idp/saml2_config/') \ if f.split('.')[-1] == 'xml'], }, # Signing 'key_file': BASEDIR + '/djangosaml2idp/certificates/mykey.pem', 'cert_file': BASEDIR + '/djangosaml2idp/certificates/mycert.pem', # Encryption 'encryption_keypairs': [{ 'key_file': BASEDIR + '/djangosaml2idp/certificates/mykey.pem', 'cert_file': BASEDIR + '/djangosaml2idp/certificates/mycert.pem', }], 'valid_for': 365 * 24, } conf.load(copy.copy(SAML_IDP_CONFIG)) self.IDP = Server(config=conf) # pylint: disable=invalid-name except Exception as e: # pylint: disable=invalid-name, broad-except return self.handle_error(request, exception=e) return super(IdPHandlerViewMixin, self).dispatch(request, *args, **kwargs)
def test_decrypt(self): attr_stat = saml.attribute_statement_from_string( open("encrypted_attribute_statement.xml").read()) assert len(attr_stat.attribute) == 0 assert len(attr_stat.encrypted_attribute) == 4 xmlsec = get_xmlsec_binary() sec = SecurityContext(xmlsec, key_file="private_key.pem") resp = AuthnResponse(sec, None, "entity_id") resp.decrypt_attributes(attr_stat) assert len(attr_stat.attribute) == 4 assert len(attr_stat.encrypted_attribute) == 4
def setup_class(self): xmlexec = get_xmlsec_binary() self.sec = sigver.SecurityContext(xmlexec, key_file=PRIV_KEY, cert_file=PUB_KEY, debug=1) self._assertion = factory( saml.Assertion, version="2.0", id="11111", issue_instant="2009-10-30T13:20:28Z", signature=sigver.pre_signature_part("11111", self.sec.my_cert, 1), attribute_statement=do_attribute_statement({ ("","","surName"): ("Foo",""), ("","","givenName") :("Bar",""), }) )
def test_load_external(mock_request): filepath = os.path.join(TESTS_DIR, "remote_data/InCommon-metadata-export.xml") with open(filepath) as fd: data = fd.read() mock_request.return_value.ok = True mock_request.return_value.status_code = 200 mock_request.return_value.content = data sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) mds = MetadataStore(ATTRCONV, sec_config, disable_ssl_certificate_validation=True) mds.imp(METADATACONF["10"]) print(mds) assert len(mds) == 1 # One source assert len(mds.keys()) > 1 # number of idps
def test_load_string(): sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) mds = MetadataStore(ONTS.values(), ATTRCONV, sec_config, disable_ssl_certificate_validation=True) mds.imp(METADATACONF["11"]) print(mds) assert len(mds.keys()) == 1 idps = mds.with_descriptor("idpsso") assert list(idps.keys()) == [ 'http://xenosmilus.umdc.umu.se/simplesaml/saml2/idp/metadata.php'] certs = mds.certs( 'http://xenosmilus.umdc.umu.se/simplesaml/saml2/idp/metadata.php', "idpsso", "signing") assert len(certs) == 1
def test_load_string(): sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) mds = MetadataStore(ONTS.values(), ATTRCONV, sec_config, disable_ssl_certificate_validation=True) mds.imp(METADATACONF["11"]) # print(mds) assert len(mds.keys()) == 1 idps = mds.with_descriptor("idpsso") assert list(idps.keys()) == [ 'http://xenosmilus.umdc.umu.se/simplesaml/saml2/idp/metadata.php'] certs = mds.certs( 'http://xenosmilus.umdc.umu.se/simplesaml/saml2/idp/metadata.php', "idpsso", "signing") assert len(certs) == 1
def load(self, cnf, metadata_construction=False): """ The base load method, loads the configuration :param cnf: The configuration as a dictionary :param metadata_construction: Is this only to be able to construct metadata. If so some things can be left out. :return: The Configuration instance """ _uc = self.unicode_convert for arg in COMMON_ARGS: if arg == "virtual_organization": if "virtual_organization" in cnf: for key, val in cnf["virtual_organization"].items(): self.vorg[key] = VirtualOrg(None, key, val) continue try: setattr(self, arg, _uc(cnf[arg])) except KeyError: pass except TypeError: # Something that can't be a string setattr(self, arg, cnf[arg]) if "service" in cnf: for typ in ["aa", "idp", "sp", "pdp", "aq"]: try: self.load_special( cnf["service"][typ], typ, metadata_construction=metadata_construction) self.serves.append(typ) except KeyError: pass if not metadata_construction: if not self.xmlsec_binary: self.xmlsec_binary = get_xmlsec_binary() # verify that xmlsec is where it's supposed to be if not os.path.exists(self.xmlsec_binary): #if not os.access(, os.F_OK): raise Exception( "xmlsec binary not in '%s' !" % self.xmlsec_binary) self.load_complex(cnf, metadata_construction=metadata_construction) self.context = self.def_context return self
def setup_class(self): xmlexec = get_xmlsec_binary() self.sec = sigver.SecurityContext(xmlexec, key_file=PRIV_KEY, cert_file=PUB_KEY, debug=1) self._assertion = factory(saml.Assertion, version="2.0", id="11111", issue_instant="2009-10-30T13:20:28Z", signature=sigver.pre_signature_part( "11111", self.sec.my_cert, 1), attribute_statement=do_attribute_statement({ ("", "", "surName"): ("Foo", ""), ("", "", "givenName"): ("Bar", ""), }))
def load(self, cnf, metadata_construction=False): """ The base load method, loads the configuration :param cnf: The configuration as a dictionary :param metadata_construction: Is this only to be able to construct metadata. If so some things can be left out. :return: The Configuration instance """ for arg in COMMON_ARGS: if arg == "virtual_organization": if "virtual_organization" in cnf: for key, val in cnf["virtual_organization"].items(): self.vorg[key] = VirtualOrg(None, key, val) continue try: setattr(self, arg, cnf[arg]) except KeyError: pass if "service" in cnf: for typ in ["aa", "idp", "sp", "pdp"]: try: self.load_special( cnf["service"][typ], typ, metadata_construction=metadata_construction) self.serves.append(typ) except KeyError: pass if not metadata_construction: if not self.xmlsec_binary: self.xmlsec_binary = get_xmlsec_binary() # verify that xmlsec is where it's supposed to be if not os.path.exists(self.xmlsec_binary): #if not os.access(, os.F_OK): raise Exception("xmlsec binary not in '%s' !" % (self.xmlsec_binary)) self.load_complex(cnf, metadata_construction=metadata_construction) self.context = self.def_context return self
def setup_class(self): xmlexec = get_xmlsec_binary() md = MetadataStore([saml, samlp], None, xmlexec) md.load("local", full_path("metadata_cert.xml")) crypto = get_xmlsec_cryptobackend() self.sec = sigver.SecurityContext(crypto, key_file=PRIV_KEY, cert_file=PUB_KEY, debug=1, metadata=md) self._assertion = factory( saml.Assertion, version="2.0", id="11111", issue_instant="2009-10-30T13:20:28Z", signature=sigver.pre_signature_part("11111", self.sec.my_cert, 1), attribute_statement=do_attribute_statement({ ("","","surName"): ("Foo",""), ("","","givenName") :("Bar",""), }) )
def test_sign_assertion(self): ass = self._assertion print ass sign_ass = self.sec.sign_assertion_using_xmlsec("%s" % ass, nodeid=ass.id) #print sign_ass sass = saml.assertion_from_string(sign_ass) #print sass assert _eq(sass.keyswv(), ['attribute_statement', 'issue_instant', 'version', 'signature', 'id']) assert sass.version == "2.0" assert sass.id == "11111" assert time_util.str_to_time(sass.issue_instant) print xmlsec_version(get_xmlsec_binary()) item = self.sec.check_signature(sass, class_name(sass), sign_ass) assert isinstance(item, saml.Assertion)
def test_multiple_signatures_assertion(self): ass = self._assertion # basic test with two of the same to_sign = [(ass, ass.id, ''), (ass, ass.id, '') ] sign_ass = self.sec.multiple_signatures("%s" % ass, to_sign) sass = saml.assertion_from_string(sign_ass) assert _eq(sass.keyswv(), ['attribute_statement', 'issue_instant', 'version', 'signature', 'id']) assert sass.version == "2.0" assert sass.id == "11111" assert time_util.str_to_time(sass.issue_instant) print xmlsec_version(get_xmlsec_binary()) item = self.sec.check_signature(sass, class_name(sass), sign_ass, must=True) assert isinstance(item, saml.Assertion)
def test_sign_assertion(self): ass = self._assertion print ass sign_ass = self.sec.sign_assertion_using_xmlsec("%s" % ass, nodeid=ass.id) #print sign_ass sass = saml.assertion_from_string(sign_ass) #print sass assert _eq(sass.keyswv(), [ 'attribute_statement', 'issue_instant', 'version', 'signature', 'id' ]) assert sass.version == "2.0" assert sass.id == "11111" assert time_util.str_to_time(sass.issue_instant) print xmlsec_version(get_xmlsec_binary()) item = self.sec.check_signature(sass, class_name(sass), sign_ass) assert isinstance(item, saml.Assertion)
def load(self, cnf, metadata_construction=False): """ The base load method, loads the configuration :param cnf: The configuration as a dictionary :param metadata_construction: Is this only to be able to construct metadata. If so some things can be left out. :return: The Configuration instance """ for arg in COMMON_ARGS: try: self._attr[""][arg] = cnf[arg] except KeyError: pass if "service" in cnf: for typ in ["aa", "idp", "sp", "pdp"]: try: self.load_special( cnf["service"][typ], typ, metadata_construction=metadata_construction) except KeyError: pass if not metadata_construction: if "xmlsec_binary" not in self._attr[""]: self._attr[""]["xmlsec_binary"] = get_xmlsec_binary() # verify that xmlsec is where it's supposed to be if not os.path.exists(self._attr[""]["xmlsec_binary"]): #if not os.access(, os.F_OK): raise Exception("xmlsec binary not in '%s' !" % (self._attr[""]["xmlsec_binary"])) self.load_complex(cnf, metadata_construction=metadata_construction) self.context = self.def_context return self
import idp_metadata as metadata import logging from student.models import State, District, SubjectArea, GradeLevel, YearsInEducation, School from baseinfo.models import Enum from django import db import requests import base64 # *Guess the xmlsec_path try: from saml2.sigver import get_xmlsec_binary except ImportError: get_xmlsec_binary = None if get_xmlsec_binary: xmlsec_path = get_xmlsec_binary() else: xmlsec_path = '/usr/local/bin/xmlsec1' SSO_DIR = settings.PROJECT_HOME + "/sso" BASEDIR = SSO_DIR + "/idp" log = logging.getLogger("tracking") @csrf_exempt def genericsso(request): '''Assertion consume service (acs) of pepper''' log.debug("===== genericsso: receiving a token =====")
### Everything above are default settings made by django-admin startproject ### The following is added for djangosaml2idp IdP configuration. import saml2 from saml2.saml import NAMEID_FORMAT_EMAILADDRESS, NAMEID_FORMAT_UNSPECIFIED from saml2.sigver import get_xmlsec_binary LOGIN_URL = '/login/' BASE_URL = 'http://localhost:9000/idp' SAML_IDP_CONFIG = { 'debug': DEBUG, 'xmlsec_binary': get_xmlsec_binary(['/opt/local/bin', '/usr/bin/xmlsec1']), 'entityid': '%s/metadata' % BASE_URL, 'description': 'Example IdP setup', 'service': { 'idp': { 'name': 'Django localhost IdP', 'endpoints': { 'single_sign_on_service': [ ('%s/sso/post' % BASE_URL, saml2.BINDING_HTTP_POST), ('%s/sso/redirect' % BASE_URL, saml2.BINDING_HTTP_REDIRECT), ], },
dest='wellknown', help="Use wellknown namespace prefixes") parser.add_argument(dest="config", nargs="+") args = parser.parse_args() valid_for = 0 nspair = None paths = [".", "/opt/local/bin"] if args.valid: # translate into hours valid_for = int(args.valid) * 24 if args.xmlsec: xmlsec = args.xmlsec else: xmlsec = get_xmlsec_binary(paths) eds = [] for filespec in args.config: bas, fil = os.path.split(filespec) if bas != "": sys.path.insert(0, bas) if fil.endswith(".py"): fil = fil[:-3] cnf = Config().load_file(fil, metadata_construction=True) eds.append(entity_descriptor(cnf)) secc = SecurityContext(xmlsec, args.keyfile, cert_file=args.cert) if args.id: desc = entities_descriptor(eds, valid_for, args.name, args.id, args.sign, secc)
#!/usr/bin/env python import json from subprocess import Popen, PIPE from saml2.sigver import get_xmlsec_binary # MAKE_METADATA = "make_metadata.py" # or if you have problem with your paths be more specific #MAKE_METADATA = "/usr/bin/make_metadata.py" #MAKE_METADATA = "/Library/Frameworks/Python.framework/Versions/2.7/bin/make_metadata.py" XMLSEC = get_xmlsec_binary(["/opt/local/bin", "/usr/local/bin"]) MDNS = '"urn:oasis:names:tc:SAML:2.0:metadata"' NFORMAT = "xenosmilus.umdc.umu.se-8086%ssp.xml" CNFS = [""] COMBOS = json.loads(open("build.json").read()) CNFS.extend(COMBOS.keys()) for cnf in CNFS: if cnf: name = "conf_%s.py" % cnf fname = "-%s-" % cnf else: name = "conf.py" fname = "-" print 10*"=" + name + 10*"="
# -*- coding: utf-8 -*- __author__ = 'roland' from saml2 import BINDING_PAOS from saml2 import BINDING_HTTP_ARTIFACT from saml2 import BINDING_HTTP_POST from saml2 import BINDING_HTTP_REDIRECT from saml2.sigver import get_xmlsec_binary try: XMLSEC_BINARY = get_xmlsec_binary(["/opt/local/bin"]) except Exception: XMLSEC_BINARY = "" # Base URL for the service BASE = "http://localhost:8087" # Base directory for needed files PATH = "/Users/rolandh/code/saml2test/tests" CONFIG = { "entityid": "%s/sp.xml" % BASE, "name": "SAML2 test tool", "description": "Simplest possible", "service": { "sp": { "allow_unsolicited": True, "endpoints": { "assertion_consumer_service": [ ("%s/acs/post" % BASE, BINDING_HTTP_POST), ("%s/acs/redirect" % BASE, BINDING_HTTP_REDIRECT),
def initAuthApp(self, application, metadataList, conf, secretFile, configfilePath=None): """ Will add authentication middleware to the WSGI application. :param self: :param application: Function for main WSGI application :rtype : PluggableAuthenticationMiddleware :return: """ self.CONST_SETUP = "/setup" self.CONST_SETUPSERVICE = "/setup/service" self.CONST_SETUPSTYLES = "/setup/styles" self.CONST_SAVESECRET = "/setup/SaveSecret" self.CONST_SETUPABOUT = "/setup/about" self.CONST_SETUPLOGUT = "/setup/logout" self.CONST_SESSION_USER = "******" self.CONST_KEY = "key" self.CONST_SECRET = "secret" if configfilePath is None: self.CONST_ROOT = os.path.dirname(os.path.abspath(__file__)) else: self.CONST_ROOT = configfilePath self.CONST_AUTH_FILE = IdpSetupSp._instance.CONST_ROOT + "/auth.json" self.CONST_STATIC_FILE = IdpSetupSp._instance.CONST_ROOT + "/files/static/" self.CONST_STATIC_MAKO = IdpSetupSp._instance.CONST_ROOT + "/files/mako/" self.CONST_LOOKUP = TemplateLookup(directories=[self.CONST_STATIC_MAKO + 'templates', self.CONST_STATIC_MAKO + 'htdocs'], module_directory=self.CONST_STATIC_MAKO + 'modules', input_encoding='utf-8', output_encoding='utf-8') self.CONST_ONTS = { saml.NAMESPACE: saml, mdui.NAMESPACE: mdui, mdattr.NAMESPACE: mdattr, dri.NAMESPACE: dri, ui.NAMESPACE: ui, idpdisc.NAMESPACE: idpdisc, md.NAMESPACE: md, xmldsig.NAMESPACE: xmldsig, xmlenc.NAMESPACE: xmlenc } self.CONST_ATTRCONV = attribute_converter.ac_factory("attributemaps") try: xmlsec_path = get_xmlsec_binary(["/opt/local/bin"]) except: try: xmlsec_path = get_xmlsec_binary(["/usr/local/bin"]) except: xmlsec_path = '/usr/bin/xmlsec1' for metadata in metadataList: mds = MetadataStore(self.CONST_ONTS.values(), self.CONST_ATTRCONV, xmlsec_path, disable_ssl_certificate_validation=True) mds.imp(metadata) for entityId in mds.keys(): self.spKeyList.append(entityId) for key in conf: self.socialServiceKeyList.append(conf[key]["name"]) self.secretFile = secretFile currentPath = os.path.dirname(os.path.abspath(__file__)) lib_path = os.path.abspath(self.CONST_ROOT) sys.path.append(lib_path) DeclarativeAuth.getInstance(self.CONST_AUTH_FILE) app_with_auth = make_middleware_with_config(application, {"here": "."}, self.CONST_ROOT+'/who.ini', log_file=self.CONST_ROOT+"/repoze_who.log") return app_with_auth
from saml2 import BINDING_HTTP_POST from saml2.extension.idpdisc import BINDING_DISCO from saml2.saml import NAME_FORMAT_URI try: from saml2.sigver import get_xmlsec_binary xmlsec_path = get_xmlsec_binary(["/opt/local/bin", "/usr/local/bin", "/usr/bin/"]) except ImportError: xmlsec_path = '/usr/bin/xmlsec1' # Make sure the same port number appear in service_conf.py HOST = "localhost" PORT = 8088 BASE = "https://%s:%s" % (HOST, PORT) CONFIG = { "entityid": "%s/sp.xml" % BASE, "service": { "sp": { "endpoints": { "assertion_consumer_service": [ ("%s/acs/post" % BASE, BINDING_HTTP_POST) ], "single_logout_service": [ ("%s/slo/redirect" % BASE, BINDING_HTTP_POST), ("%s/slo/post" % BASE, BINDING_HTTP_POST) ] }, "authn_requests_signed": True, "logout_requests_signed": True,
# SAML2 IdP settings import saml2 from saml2.saml import NAMEID_FORMAT_EMAILADDRESS, NAMEID_FORMAT_UNSPECIFIED from saml2.sigver import get_xmlsec_binary SESSION_COOKIE_NAME = 'sessionid_idp' LOGIN_REDIRECT_URL = '/' LOGOUT_REDIRECT_URL = '/' SAML_IDP_BASE_URL = 'http://localhost:8000/idp' SAML_IDP_CONFIG = { 'debug' : DEBUG, 'xmlsec_binary': get_xmlsec_binary(['/usr/bin/xmlsec1']), 'entityid': '{}/metadata'.format(SAML_IDP_BASE_URL), 'description': 'Django SAML2 IdP', 'service': { 'idp': { 'name': 'Django SAML2 IdP', 'endpoints': { 'single_sign_on_service': [ ('{}/sso/post'.format(SAML_IDP_BASE_URL), saml2.BINDING_HTTP_POST), ('{}/sso/redirect'.format(SAML_IDP_BASE_URL), saml2.BINDING_HTTP_REDIRECT), ], }, 'name_id_format': [NAMEID_FORMAT_EMAILADDRESS, NAMEID_FORMAT_UNSPECIFIED], 'sign_response': True, 'sign_assertion': True,
parser.add_argument('-w', dest='wellknown', help="Use wellknown namespace prefixes") parser.add_argument(dest="config", nargs="+") args = parser.parse_args() valid_for = 0 nspair = None paths = [".", "/opt/local/bin"] if args.valid: # translate into hours valid_for = int(args.valid) * 24 if args.xmlsec: xmlsec = args.xmlsec else: xmlsec = get_xmlsec_binary(paths) eds = [] for filespec in args.config: bas, fil = os.path.split(filespec) if bas != "": sys.path.insert(0, bas) if fil.endswith(".py"): fil = fil[:-3] cnf = Config().load_file(fil, metadata_construction=True) eds.append(entity_descriptor(cnf)) secc = SecurityContext(xmlsec, args.keyfile, cert_file=args.cert) if args.id: desc = entities_descriptor(eds, valid_for, args.name, args.id, args.sign, secc)
def get_saml_client(org): """ Return SAML configuration. The configuration is a hash for use by saml2.config.Config """ saml_type = org.get_setting("auth_saml_type") entity_id = org.get_setting("auth_saml_entity_id") sso_url = org.get_setting("auth_saml_sso_url") x509_cert = org.get_setting("auth_saml_x509_cert") metadata_url = org.get_setting("auth_saml_metadata_url") sp_settings = org.get_setting("auth_saml_sp_settings") if settings.SAML_SCHEME_OVERRIDE: acs_url = url_for( "saml_auth.idp_initiated", org_slug=org.slug, _external=True, _scheme=settings.SAML_SCHEME_OVERRIDE, ) else: acs_url = url_for("saml_auth.idp_initiated", org_slug=org.slug, _external=True) saml_settings = { "metadata": {"remote": [{"url": metadata_url}]}, "service": { "sp": { "endpoints": { "assertion_consumer_service": [ (acs_url, BINDING_HTTP_REDIRECT), (acs_url, BINDING_HTTP_POST), ] }, # Don't verify that the incoming requests originate from us via # the built-in cache for authn request ids in pysaml2 "allow_unsolicited": True, # Don't sign authn requests, since signed requests only make # sense in a situation where you control both the SP and IdP "authn_requests_signed": False, "logout_requests_signed": True, "want_assertions_signed": True, "want_response_signed": False, } }, } if settings.SAML_ENCRYPTION_ENABLED: encryption_dict = { "xmlsec_binary": get_xmlsec_binary(), "encryption_keypairs": [ { "key_file": settings.SAML_ENCRYPTION_PEM_PATH, "cert_file": settings.SAML_ENCRYPTION_CERT_PATH, } ], } saml_settings.update(encryption_dict) if saml_type is not None and saml_type == "static": metadata_inline = mustache_render( inline_metadata_template, entity_id=entity_id, x509_cert=x509_cert, sso_url=sso_url, ) saml_settings["metadata"] = {"inline": [metadata_inline]} if acs_url is not None and acs_url != "": saml_settings["entityid"] = acs_url if sp_settings: import json saml_settings["service"]["sp"].update(json.loads(sp_settings)) sp_config = Saml2Config() sp_config.load(saml_settings) sp_config.allow_unknown_attributes = True saml_client = Saml2Client(config=sp_config) return saml_client
from common.django.drf.serializer import DynamicFieldsModelSerializer from oneid_meta.models import ( APP, OAuthAPP, OIDCAPP, SAMLAPP, LDAPAPP, HTTPAPP, Dept, User, ) from siteapi.v1.views.utils import gen_uid from siteapi.v1.serializers.perm import PermWithOwnerSerializer if get_xmlsec_binary: xmlsec_path = get_xmlsec_binary(["/opt/local/bin", "/usr/local/bin"]) # pylint: disable=invalid-name else: xmlsec_path = '/usr/local/bin/xmlsec1' # pylint: disable=invalid-name BASEDIR = os.path.dirname( os.path.dirname(os.path.dirname(os.path.dirname( os.path.abspath(__file__))))) class OAuthAPPSerializer(DynamicFieldsModelSerializer): ''' Serializer for OAuthAPP ''' class Meta: # pylint: disable=missing-docstring model = OAuthAPP fields = (
def config_settings_loader(request: Optional[HttpRequest] = None) -> SPConfig: conf = SPConfig() if request is None or not request.path.lstrip('/').startswith( settings.SPID_URLS_PREFIX): # Not a SPID request: load SAML_CONFIG unchanged conf.load(copy.deepcopy(settings.SAML_CONFIG)) return conf # Build a SAML_CONFIG for SPID metadata_url = request.build_absolute_uri( reverse('djangosaml2_spid:spid_metadata')) saml_config = { 'entityid': metadata_url, 'attribute_map_dir': os.path.join(djangosaml2_spid_config.path, 'attribute_maps/'), 'service': { 'sp': { 'name': metadata_url, 'name_qualifier': request.build_absolute_uri('/'), 'name_id_format': [settings.SPID_NAMEID_FORMAT], 'endpoints': { 'assertion_consumer_service': [ (request.build_absolute_uri( reverse('djangosaml2_spid:saml2_acs')), saml2.BINDING_HTTP_POST), ], 'single_logout_service': [ (request.build_absolute_uri( reverse('djangosaml2_spid:saml2_ls_post')), saml2.BINDING_HTTP_POST), ], }, # Mandates that the IdP MUST authenticate the presenter directly # rather than rely on a previous security context. 'force_authn': False, # SPID 'name_id_format_allow_create': False, # attributes that this project need to identify a user 'required_attributes': ['spidCode', 'name', 'familyName', 'fiscalNumber', 'email'], 'requested_attribute_name_format': saml2.saml.NAME_FORMAT_BASIC, 'name_format': saml2.saml.NAME_FORMAT_BASIC, # attributes that may be useful to have but not required 'optional_attributes': [ 'gender', 'companyName', 'registeredOffice', 'ivaCode', 'idCard', 'digitalAddress', 'placeOfBirth', 'countyOfBirth', 'dateOfBirth', 'address', 'mobilePhone', 'expirationDate' ], 'signing_algorithm': settings.SPID_SIG_ALG, 'digest_algorithm': settings.SPID_DIG_ALG, 'authn_requests_signed': True, 'logout_requests_signed': True, # Indicates that Authentication Responses to this SP must # be signed. If set to True, the SP will not consume # any SAML Responses that are not signed. 'want_assertions_signed': True, # When set to true, the SP will consume unsolicited SAML # Responses, i.e. SAML Responses for which it has not sent # a respective SAML Authentication Request. 'allow_unsolicited': False, # Permits to have attributes not configured in attribute-mappings # otherwise...without OID will be rejected 'allow_unknown_attributes': True, }, }, 'metadata': { 'local': [settings.SPID_IDENTITY_PROVIDERS_METADATA_DIR], 'remote': [] }, # Signing 'key_file': settings.SPID_PRIVATE_KEY, 'cert_file': settings.SPID_PUBLIC_CERT, # Encryption 'encryption_keypairs': [{ 'key_file': settings.SPID_PRIVATE_KEY, 'cert_file': settings.SPID_PUBLIC_CERT, }], 'organization': copy.deepcopy(settings.SAML_CONFIG['organization']) } if settings.SAML_CONFIG.get('debug'): saml_config['debug'] = True if 'xmlsec_binary' in settings.SAML_CONFIG: saml_config['xmlsec_binary'] = copy.deepcopy( settings.SAML_CONFIG['xmlsec_binary']) else: saml_config['xmlsec_binary'] = get_xmlsec_binary( ['/opt/local/bin', '/usr/bin/xmlsec1']) if settings.SPID_SAML_CHECK_REMOTE_METADATA_ACTIVE: saml_config['metadata']['remote'].append( {'url': settings.SPID_SAML_CHECK_METADATA_URL}) if settings.SPID_TESTENV2_REMOTE_METADATA_ACTIVE: saml_config['metadata']['remote'].append( {'url': settings.SPID_TESTENV2_METADATA_URL}) logger.debug(f'SAML_CONFIG: {saml_config}') conf.load(saml_config) return conf
for alg in algs: if alg in DIGEST_METHODS: digest.append(alg) elif alg in SIGNING_METHODS: signing.append(alg) return {"digest": digest, "signing": signing} raise SystemError(p_err) def algorithm_support_in_metadata(xmlsec): if xmlsec is None: return [] support = get_algorithm_support(xmlsec) element_list = [] for alg in support["digest"]: element_list.append(DigestMethod(algorithm=DIGEST_METHODS[alg])) for alg in support["signing"]: element_list.append(SigningMethod(algorithm=SIGNING_METHODS[alg])) return element_list if __name__ == '__main__': xmlsec = get_xmlsec_binary() res = get_algorithm_support(xmlsec) print(res) for a in algorithm_support_in_metadata(xmlsec): print(a)
def create_conf(sp_host='sp.example.com', idp_hosts=['idp.example.com'], metadata_file='remote_metadata.xml', authn_requests_signed=None): try: from saml2.sigver import get_xmlsec_binary except ImportError: get_xmlsec_binary = None if get_xmlsec_binary: xmlsec_path = get_xmlsec_binary(["/opt/local/bin"]) else: xmlsec_path = '/usr/bin/xmlsec1' BASEDIR = os.path.dirname(os.path.abspath(__file__)) config = { 'xmlsec_binary': xmlsec_path, 'entityid': 'http://%s/saml2/metadata/' % sp_host, 'attribute_map_dir': os.path.join(BASEDIR, 'attribute-maps'), 'service': { 'sp': { 'name': 'Test SP', 'name_id_format': saml2.saml.NAMEID_FORMAT_PERSISTENT, 'endpoints': { 'assertion_consumer_service': [ ('http://%s/saml2/acs/' % sp_host, saml2.BINDING_HTTP_POST), ], 'single_logout_service': [ ('http://%s/saml2/ls/' % sp_host, saml2.BINDING_HTTP_REDIRECT), ], }, 'required_attributes': ['uid'], 'optional_attributes': ['eduPersonAffiliation'], 'idp': {}, # this is filled later 'want_response_signed': False, }, }, 'metadata': { 'local': [os.path.join(BASEDIR, metadata_file)], }, 'debug': 1, # certificates 'key_file': os.path.join(BASEDIR, 'mycert.key'), 'cert_file': os.path.join(BASEDIR, 'mycert.pem'), # These fields are only used when generating the metadata 'contact_person': [ { 'given_name': 'Technical givenname', 'sur_name': 'Technical surname', 'company': 'Example Inc.', 'email_address': '*****@*****.**', 'contact_type': 'technical' }, { 'given_name': 'Administrative givenname', 'sur_name': 'Administrative surname', 'company': 'Example Inc.', 'email_address': '*****@*****.**', 'contact_type': 'administrative' }, ], 'organization': { 'name': [('Ejemplo S.A.', 'es'), ('Example Inc.', 'en')], 'display_name': [('Ejemplo', 'es'), ('Example', 'en')], 'url': [('http://www.example.es', 'es'), ('http://www.example.com', 'en')], }, 'valid_for': 24, } if authn_requests_signed is not None: config['service']['sp'][ 'authn_requests_signed'] = authn_requests_signed for idp in idp_hosts: entity_id = 'https://%s/simplesaml/saml2/idp/metadata.php' % idp config['service']['sp']['idp'][entity_id] = { 'single_sign_on_service': { saml2.BINDING_HTTP_REDIRECT: 'https://%s/simplesaml/saml2/idp/SSOService.php' % idp, }, 'single_logout_service': { saml2.BINDING_HTTP_REDIRECT: 'https://%s/simplesaml/saml2/idp/SingleLogoutService.php' % idp, }, } return config
# -*- coding: utf-8 -*- from saml2 import BINDING_PAOS from saml2 import BINDING_HTTP_ARTIFACT from saml2 import BINDING_HTTP_POST from saml2 import BINDING_HTTP_REDIRECT from saml2.sigver import get_xmlsec_binary try: XMLSEC_BINARY = get_xmlsec_binary(["/opt/local/bin"]) except Exception: XMLSEC_BINARY = "/usr/bin/xmlsec1" PORT = 8087 # Base URL for the service BASE = "http://localhost:{port}".format(port=PORT) # Base directory for needed files CONFIG = { "entityid": "%s/sp.xml" % BASE, "name": "SAML2 test tool", "description": "Simplest possible", "service": { "sp": { "allow_unsolicited": True, "authn_requests_signed": "true", "endpoints": { "assertion_consumer_service": [ ("%s/acs/post" % BASE, BINDING_HTTP_POST),
# -*- coding: utf-8 -*- from saml2 import BINDING_SOAP, BINDING_URI from saml2 import BINDING_HTTP_REDIRECT from saml2 import BINDING_HTTP_POST from saml2 import BINDING_HTTP_ARTIFACT from saml2.saml import NAMEID_FORMAT_PERSISTENT from saml2.saml import NAME_FORMAT_URI try: from saml2.sigver import get_xmlsec_binary except ImportError: get_xmlsec_binary = None if get_xmlsec_binary: xmlsec_path = get_xmlsec_binary(["/opt/local/bin"]) else: xmlsec_path = '/usr/bin/xmlsec1' BASE = "http://localhost:8088" CONFIG = { "entityid" : "urn:mace:example.com:saml:roland:idp", "name" : "Rolands IdP", "service": { "aa": { "endpoints" : { "attribute_service": [ ("%s/aap" % BASE, BINDING_HTTP_POST), ("%s/aas" % BASE, BINDING_SOAP) ]
retcode = p.poll() #returns None while subprocess is running if (retcode is not None): break p_out = p.stdout.read() p_err = p.stderr.read() return (True, p_out, p_err) except Exception as ex: return (False, None, None) MAKE_METADATA = "make_metadata.py" # or if you have problem with your paths be more specific #MAKE_METADATA = "/usr/bin/make_metadata.py" #MAKE_METADATA = "/Library/Frameworks/Python.framework/Versions/2.7/bin/make_metadata.py" XMLSEC = get_xmlsec_binary(["/opt/local/bin", "/usr/local/bin"]) MDNS = '"urn:oasis:names:tc:SAML:2.0:metadata"' NFORMAT = "{HOST}%ssp.xml".format(HOST=server_conf.HOST) CNFS = [""] COMBOS = json.loads(open("build.json").read()) CNFS.extend(COMBOS.keys()) fname_list = [] for cnf in CNFS: if cnf: name = "conf_%s.py" % cnf fname = "-%s-" % cnf
signing = [] for alg in algs: if alg in DIGEST_METHODS: digest.append(alg) elif alg in SIGNING_METHODS: signing.append(alg) return {"digest": digest, "signing": signing} raise SystemError(p_err) def algorithm_support_in_metadata(xmlsec): if xmlsec is None: return [] support = get_algorithm_support(xmlsec) element_list = [] for alg in support["digest"]: element_list.append(DigestMethod(algorithm=DIGEST_METHODS[alg])) for alg in support["signing"]: element_list.append(SigningMethod(algorithm=SIGNING_METHODS[alg])) return element_list if __name__ == '__main__': xmlsec = get_xmlsec_binary() res = get_algorithm_support(xmlsec) print(res) for a in algorithm_support_in_metadata(xmlsec): print(a)
import os from saml2 import BINDING_HTTP_POST, BINDING_HTTP_REDIRECT, BINDING_SOAP from saml2.saml import NAME_FORMAT_URI, NAMEID_FORMAT_PERSISTENT, NAMEID_FORMAT_TRANSIENT try: from saml2.sigver import get_xmlsec_binary except ImportError: get_xmlsec_binary = None if get_xmlsec_binary: xmlsec_path = get_xmlsec_binary(["/opt/local/bin"]) else: xmlsec_path = '/usr/bin/xmlsec1' _hostname = 'unittest-idp.example.edu' BASE = "https://{!s}".format(_hostname) here = os.path.dirname(__file__) key_path = os.path.join(here, 'idp-public-snakeoil.key') cert_path = os.path.join(here, 'idp-public-snakeoil.pem') attrmaps_path = os.path.join(here, '../../../attributemaps') sp_metadata_path = os.path.join(here, 'sp_metadata.xml') CONFIG = { "entityid": "%s/idp.xml" % BASE, "description": "eduID UNITTEST identity provider", "service": { "idp": {
import os from saml2 import BINDING_HTTP_POST, BINDING_HTTP_REDIRECT from saml2.saml import NAME_FORMAT_URI try: from saml2.sigver import get_xmlsec_binary except ImportError: get_xmlsec_binary = None if get_xmlsec_binary: xmlsec_path = get_xmlsec_binary(['/opt/local/bin', '/usr/local/bin']) else: xmlsec_path = '/usr/local/bin/xmlsec1' # Make sure the same port number appear in service_conf.py BASEDIR = os.path.abspath(os.path.dirname(__file__)) BASE = 'https://sp-url.rackspace.com' SSL_CERT_LOCATION = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'pki') CERT = os.path.join(SSL_CERT_LOCATION, 'sp.crt') IDP_METADATA_LOCATION = os.getenv( 'IDP_METADATA_LOCATION', os.path.join(SSL_CERT_LOCATION, 'idp_metadata.xml')) PRIVATE_KEY = os.path.join(SSL_CERT_LOCATION, 'sp.key') CONFIG = { 'entityid': BASE, 'service': { 'sp': {
ONTS = { saml.NAMESPACE: saml, mdui.NAMESPACE: mdui, mdattr.NAMESPACE: mdattr, dri.NAMESPACE: dri, ui.NAMESPACE: ui, idpdisc.NAMESPACE: idpdisc, md.NAMESPACE: md, xmldsig.NAMESPACE: xmldsig, xmlenc.NAMESPACE: xmlenc } ATTRCONV = ac_factory(full_path("attributemaps")) sec_config = config.Config() sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) __author__ = 'rolandh' MDS = MetadataStore(ONTS.values(), ATTRCONV, sec_config, disable_ssl_certificate_validation=True) MDS.imp([{"class": "saml2.mdstore.MetaDataMD", "metadata": [(full_path("swamid.md"), )]}]) def _eq(l1, l2): return set(l1) == set(l2) def test_filter_ava(): policy = Policy({ "default": {
from saml2 import BINDING_HTTP_POST from saml2.extension.idpdisc import BINDING_DISCO from saml2.saml import NAME_FORMAT_URI try: from saml2.sigver import get_xmlsec_binary xmlsec_path = get_xmlsec_binary( ["/opt/local/bin", "/usr/local/bin", "/usr/bin/"]) except ImportError: xmlsec_path = '/usr/bin/xmlsec1' # Make sure the same port number appear in service_conf.py HOST = "localhost" PORT = 8088 BASE = "https://%s:%s" % (HOST, PORT) CONFIG = { "entityid": "%s/sp.xml" % BASE, "service": { "sp": { "endpoints": { "assertion_consumer_service": [("%s/acs/post" % BASE, BINDING_HTTP_POST)], "single_logout_service": [("%s/slo/redirect" % BASE, BINDING_HTTP_POST), ("%s/slo/post" % BASE, BINDING_HTTP_POST)] }, "authn_requests_signed": True, "logout_requests_signed": True, "want_assertions_signed": True },
def config_settings_loader(request: Optional[HttpRequest] = None) -> SPConfig: conf = SPConfig() if request is None: # Not a SPID request: load SAML_CONFIG unchanged conf.load(copy.deepcopy(settings.SAML_CONFIG)) return conf # Build a SAML_CONFIG for SPID base_url = settings.SPID_BASE_URL or request.build_absolute_uri("/") metadata_url = urljoin(base_url, settings.SPID_METADATA_URL_PATH) if settings.SPID_METADATA_URL_PATH in request.get_full_path(): _REQUIRED_ATTRIBUTES = settings.SPID_REQUIRED_ATTRIBUTES _OPTIONAL_ATTRIBUTES = settings.SPID_OPTIONAL_ATTRIBUTES else: _REQUIRED_ATTRIBUTES = settings.CIE_REQUIRED_ATTRIBUTES _OPTIONAL_ATTRIBUTES = [] saml_config = { "entityid": getattr(settings, 'SAML2_ENTITY_ID', metadata_url), "attribute_map_dir": settings.SPID_ATTR_MAP_DIR, "service": { "sp": { "name": metadata_url, "name_qualifier": base_url, "name_id_format": [settings.SPID_NAMEID_FORMAT], "endpoints": { "assertion_consumer_service": [ ( urljoin(base_url, reverse("djangosaml2_spid:saml2_acs")), saml2.BINDING_HTTP_POST, ), ], "single_logout_service": [ ( urljoin(base_url, reverse("djangosaml2_spid:saml2_ls_post")), saml2.BINDING_HTTP_POST, ), ], }, # Mandates that the IdP MUST authenticate the presenter directly # rather than rely on a previous security context. "force_authn": False, # SPID "name_id_format_allow_create": False, # attributes that this project need to identify a user "required_attributes": _REQUIRED_ATTRIBUTES, "optional_attributes": _OPTIONAL_ATTRIBUTES, "requested_attribute_name_format": saml2.saml.NAME_FORMAT_BASIC, "name_format": saml2.saml.NAME_FORMAT_BASIC, "signing_algorithm": settings.SPID_SIG_ALG, "digest_algorithm": settings.SPID_DIG_ALG, "authn_requests_signed": True, "logout_requests_signed": True, # Indicates that Authentication Responses to this SP must # be signed. If set to True, the SP will not consume # any SAML Responses that are not signed. "want_assertions_signed": True, # When set to true, the SP will consume unsolicited SAML # Responses, i.e. SAML Responses for which it has not sent # a respective SAML Authentication Request. Set to True to # let ACS endpoint work. "allow_unsolicited": settings.SAML_CONFIG.get("allow_unsolicited", False), # Permits to have attributes not configured in attribute-mappings # otherwise...without OID will be rejected "allow_unknown_attributes": True, }, }, "disable_ssl_certificate_validation": settings.SAML_CONFIG.get("disable_ssl_certificate_validation"), "metadata": { "local": [settings.SPID_IDENTITY_PROVIDERS_METADATA_DIR], "remote": [], }, # Signing "key_file": settings.SPID_PRIVATE_KEY, "cert_file": settings.SPID_PUBLIC_CERT, # Encryption "encryption_keypairs": [{ "key_file": settings.SPID_PRIVATE_KEY, "cert_file": settings.SPID_PUBLIC_CERT, }], "organization": copy.deepcopy(settings.SAML_CONFIG["organization"]), } if settings.SAML_CONFIG.get("debug"): saml_config["debug"] = True if "xmlsec_binary" in settings.SAML_CONFIG: saml_config["xmlsec_binary"] = copy.deepcopy( settings.SAML_CONFIG["xmlsec_binary"]) else: saml_config["xmlsec_binary"] = get_xmlsec_binary( ["/opt/local/bin", "/usr/bin/xmlsec1"]) if settings.SPID_SAML_CHECK_IDP_ACTIVE: saml_config["metadata"]["remote"].append( {"url": settings.SPID_SAML_CHECK_METADATA_URL}) if settings.SPID_DEMO_IDP_ACTIVE: saml_config["metadata"]["remote"].append( {"url": settings.SPID_DEMO_METADATA_URL}) if settings.SPID_VALIDATOR_IDP_ACTIVE: saml_config["metadata"]["remote"].append( {"url": settings.SPID_VALIDATOR_METADATA_URL}) logger.debug(f"SAML_CONFIG: {saml_config}") conf.load(saml_config) return conf
def get_saml2_config(self): """ Configures a Saml2Client with the given settings SAML2 based SSO(Single-Sign-On) identity provider with dynamic metadata configuration https://pysaml2.readthedocs.io/en/latest/howto/config.html """ saml_settings = { # "entityid": None, # "description": "Example SP", 'service': { 'sp': { 'endpoints': { 'assertion_consumer_service': [(self.acs_url, BINDING_HTTP_REDIRECT), (self.acs_url, BINDING_HTTP_POST)], # Other Valid endpoints not configured yet #'artifact_resolution_service': [] #"single_logout_service": [] }, # this is overriden if a key is available 'authn_requests_signed': False, 'allow_unsolicited': True, # this is needed for POST # Indicates that either the Authentication Response or the assertions contained # within the response to this SP must be signed. Check DOCS 'want_assertions_signed': False, 'want_response_signed': False, "want_assertions_or_response_signed": True, }, }, } if 'METADATA_LOCAL_FILE_PATH' in self.settings: update(saml_settings, { 'metadata': { 'local': [self.settings['METADATA_LOCAL_FILE_PATH']] } }) elif 'METADATA_AUTO_CONF_URL' in self.settings: update( saml_settings, { 'metadata': { 'remote': [ { "url": self.settings['METADATA_AUTO_CONF_URL'], }, ] } }) else: raise Saml2Error("IdP metadata missing") if 'ENTITY_ID' in self.settings: saml_settings['entityid'] = self.settings['ENTITY_ID'] else: # EntityId: It is recommended that the entityid should point to a real webpage where the metadata # for the entity can be found. TODO: make this true uri = urlparse(self.acs_url) saml_settings['entityid'] = f'{uri.scheme}://{uri.netloc}' if 'NAME_ID_FORMAT' in self.settings: saml_settings['service']['sp']['name_id_format'] = self.settings[ 'NAME_ID_FORMAT'] if 'ACCEPTED_TIME_DIFF' in self.settings: saml_settings['accepted_time_diff'] = self.settings[ 'ACCEPTED_TIME_DIFF'] if 'KEY_FILE' in self.settings and 'CERT_FILE' in self.settings: try: from saml2.sigver import get_xmlsec_binary except ImportError: get_xmlsec_binary = None if get_xmlsec_binary: xmlsec_path = get_xmlsec_binary( ["/opt/local/bin", "/usr/local/bin"]) else: xmlsec_path = '/usr/local/bin/xmlsec1' update( saml_settings, { 'service': { 'sp': { "authn_requests_signed": True } }, "key_file": self.settings['KEY_FILE'], "cert_file": self.settings['CERT_FILE'], "xmlsec_binary": xmlsec_path, }) # TODO: add encryption_keypairs return saml_settings
def create_conf(sp_host='sp.example.com', idp_hosts=['idp.example.com'], metadata_file='remote_metadata.xml'): try: from saml2.sigver import get_xmlsec_binary except ImportError: get_xmlsec_binary = None if get_xmlsec_binary: xmlsec_path = get_xmlsec_binary(["/opt/local/bin"]) else: xmlsec_path = '/usr/bin/xmlsec1' BASEDIR = os.path.dirname(os.path.abspath(__file__)) config = { 'xmlsec_binary': xmlsec_path, 'entityid': 'http://%s/saml2/metadata/' % sp_host, 'attribute_map_dir': os.path.join(BASEDIR, 'attribute-maps'), 'service': { 'sp': { 'name': 'Test SP', 'name_id_format': saml2.saml.NAMEID_FORMAT_PERSISTENT, 'endpoints': { 'assertion_consumer_service': [ ('http://%s/saml2/acs/' % sp_host, saml2.BINDING_HTTP_POST), ], 'single_logout_service': [ ('http://%s/saml2/ls/' % sp_host, saml2.BINDING_HTTP_REDIRECT), ], }, 'required_attributes': ['uid'], 'optional_attributes': ['eduPersonAffiliation'], 'idp': {} # this is filled later }, }, 'metadata': { 'local': [os.path.join(BASEDIR, metadata_file)], }, 'debug': 1, # certificates 'key_file': os.path.join(BASEDIR, 'mycert.key'), 'cert_file': os.path.join(BASEDIR, 'mycert.pem'), # These fields are only used when generating the metadata 'contact_person': [ {'given_name': 'Technical givenname', 'sur_name': 'Technical surname', 'company': 'Example Inc.', 'email_address': '*****@*****.**', 'contact_type': 'technical'}, {'given_name': 'Administrative givenname', 'sur_name': 'Administrative surname', 'company': 'Example Inc.', 'email_address': '*****@*****.**', 'contact_type': 'administrative'}, ], 'organization': { 'name': [('Ejemplo S.A.', 'es'), ('Example Inc.', 'en')], 'display_name': [('Ejemplo', 'es'), ('Example', 'en')], 'url': [('http://www.example.es', 'es'), ('http://www.example.com', 'en')], }, 'valid_for': 24, } for idp in idp_hosts: entity_id = 'https://%s/simplesaml/saml2/idp/metadata.php' % idp config['service']['sp']['idp'][entity_id] = { 'single_sign_on_service': { saml2.BINDING_HTTP_REDIRECT: 'https://%s/simplesaml/saml2/idp/SSOService.php' % idp, }, 'single_logout_service': { saml2.BINDING_HTTP_REDIRECT: 'https://%s/simplesaml/saml2/idp/SingleLogoutService.php' % idp, }, } return config