def test_authn_response(self, context, idp_conf, sp_conf): response_binding = BINDING_HTTP_REDIRECT fakesp = FakeSP(SPConfig().load(sp_conf, metadata_construction=False)) fakeidp = FakeIdP(USERS, config=IdPConfig().load(idp_conf, metadata_construction=False)) destination, request_params = fakesp.make_auth_req( idp_conf["entityid"]) url, auth_resp = fakeidp.handle_auth_req( request_params["SAMLRequest"], request_params["RelayState"], BINDING_HTTP_REDIRECT, "testuser1", response_binding=response_binding) context.request = auth_resp context.state[self.samlbackend.name] = { "relay_state": request_params["RelayState"] } self.samlbackend.authn_response(context, response_binding) context, internal_resp = self.samlbackend.auth_callback_func.call_args[ 0] self.assert_authn_response(internal_resp) assert self.samlbackend.name not in context.state
def test_authn_response_no_name_id(self, context, idp_conf, sp_conf): response_binding = BINDING_HTTP_REDIRECT fakesp_conf = SPConfig().load(sp_conf, metadata_construction=False) fakesp = FakeSP(fakesp_conf) fakeidp_conf = IdPConfig().load(idp_conf, metadata_construction=False) fakeidp = FakeIdP(USERS, config=fakeidp_conf) destination, request_params = fakesp.make_auth_req( idp_conf["entityid"]) # Use the fake IdP to mock up an authentication request that has no # <NameID> element. url, auth_resp = fakeidp.handle_auth_req_no_name_id( request_params["SAMLRequest"], request_params["RelayState"], BINDING_HTTP_REDIRECT, "testuser1", response_binding=response_binding) backend = self.samlbackend context.request = auth_resp context.state[backend.name] = { "relay_state": request_params["RelayState"], } backend.authn_response(context, response_binding) context, internal_resp = backend.auth_callback_func.call_args[0] assert_authn_response(internal_resp) assert backend.name not in context.state
def dispatch(self, request, *args, **kwargs): """ Construct IDP server with config from settings dict """ conf = IdPConfig() conf.load(copy.deepcopy(settings.SAML_IDP_CONFIG)) self.IDP = Server(config=conf) return super(IdPHandlerViewMixin, self).dispatch(request, *args, **kwargs)
def test_crypto_backend(): idpc = IdPConfig() idpc.load(IDP_XMLSECURITY) assert idpc.crypto_backend == 'XMLSecurity' sec = security_context(idpc) assert isinstance(sec.crypto, CryptoBackendXMLSecurity)
def idp_configure(self, metadata_construction=False): sys.path.insert(0, self.args.configpath) mod = import_module(self.args.config) self.idp_config = IdPConfig().load(mod.CONFIG, metadata_construction) if not self.args.insecure: self.idp_config.verify_ssl_cert = False else: if self.args.ca_certs: self.idp_config.ca_certs = self.args.ca_certs else: self.idp_config.ca_certs = "../keys/cacert.pem" # hack to change idp cert without config change. TODO: find interface to # change IDP cert after __init__ if self.args.oper == 'sp-04': self.idp_config.cert_file = os.path.join(self.args.keysdir, "non_md_cert.pem") self.idp_config.key_file = os.path.join(self.args.keysdir, "non_md_key.pem") for f in [self.idp_config.cert_file, self.idp_config.key_file]: if not os.path.isfile(f): print "File not found: %s" % os.path.abspath(f) raise self.idp = Server(config=self.idp_config)
def metadata(request): """ Returns an XML with the SAML 2.0 metadata for this Idp. The metadata is constructed on-the-fly based on the config dict in the django settings. """ conf = IdPConfig() conf.load(copy.deepcopy(settings.SAML_IDP_CONFIG)) metadata = entity_descriptor(conf) return HttpResponse(content=text_type(metadata).encode('utf-8'), content_type="text/xml; charset=utf8")
def metadata(cls) -> str: conf = IdPConfig() try: conf.load(cls.construct_metadata()) metadata = entity_descriptor(conf) except: pass return str(metadata)
def test_full_flow(self, satosa_config_dict, oidc_frontend_config, saml_backend_config, idp_conf): subject_id = "testuser1" # proxy config satosa_config_dict["FRONTEND_MODULES"] = [oidc_frontend_config] satosa_config_dict["BACKEND_MODULES"] = [saml_backend_config] satosa_config_dict["INTERNAL_ATTRIBUTES"]["attributes"] = {attr_name: {"openid": [attr_name], "saml": [attr_name]} for attr_name in USERS[subject_id]} _, backend_metadata = create_entity_descriptors(SATOSAConfig(satosa_config_dict)) # application test_client = Client(make_app(SATOSAConfig(satosa_config_dict)), Response) # get frontend OP config info provider_config = json.loads(test_client.get("/.well-known/openid-configuration").data.decode("utf-8")) # create auth req claims_request = ClaimsRequest(id_token=Claims(**{k: None for k in USERS[subject_id]})) req_args = {"scope": "openid", "response_type": "id_token", "client_id": CLIENT_ID, "redirect_uri": REDIRECT_URI, "nonce": "nonce", "claims": claims_request.to_json()} auth_req = urlparse(provider_config["authorization_endpoint"]).path + "?" + urlencode(req_args) # make auth req to proxy proxied_auth_req = test_client.get(auth_req) assert proxied_auth_req.status == "303 See Other" # config test IdP backend_metadata_str = str(backend_metadata[saml_backend_config["name"]][0]) idp_conf["metadata"]["inline"].append(backend_metadata_str) fakeidp = FakeIdP(USERS, config=IdPConfig().load(idp_conf)) # create auth resp req_params = dict(parse_qsl(urlparse(proxied_auth_req.data.decode("utf-8")).query)) url, authn_resp = fakeidp.handle_auth_req( req_params["SAMLRequest"], req_params["RelayState"], BINDING_HTTP_REDIRECT, subject_id, response_binding=BINDING_HTTP_REDIRECT) # make auth resp to proxy authn_resp_req = urlparse(url).path + "?" + urlencode(authn_resp) authn_resp = test_client.get(authn_resp_req) assert authn_resp.status == "303 See Other" # verify auth resp from proxy resp_dict = dict(parse_qsl(urlparse(authn_resp.data.decode("utf-8")).fragment)) signing_key = RSAKey(key=rsa_load(oidc_frontend_config["config"]["signing_key_path"]), use="sig", alg="RS256") id_token_claims = JWS().verify_compact(resp_dict["id_token"], keys=[signing_key]) assert all( (name, values) in id_token_claims.items() for name, values in OIDC_USERS[subject_id].items() )
def test_idp_1(): c = IdPConfig().load(IDP1) c.context = "idp" print c assert c.endpoint("single_sign_on_service")[0] == 'http://localhost:8088/' attribute_restrictions = c.policy.get_attribute_restriction("") assert attribute_restrictions["eduPersonAffiliation"][0].match("staff")
def metadata(cls) -> str: """ Get the IDP metadata as a string. """ conf = IdPConfig() try: conf.load(cls.construct_metadata()) metadata = entity_descriptor(conf) except Exception as e: raise ImproperlyConfigured(_('Could not instantiate IDP metadata based on the SAML_IDP_CONFIG settings and configured ServiceProviders: {}').format(str(e))) return str(metadata)
def load(cls, force_refresh:bool = False) -> Server: if cls._server_instance is None or force_refresh: conf = IdPConfig() # md = cls.construct_metadata() # try: conf.load(md) cls._server_instance = Server(config=conf) except: pass return cls._server_instance
def dispatch(self, request, *args, **kwargs): """ Construct IDP server with config from settings dict """ conf = IdPConfig() try: conf.load(copy.deepcopy(settings.SAML_IDP_CONFIG)) self.IDP = Server(config=conf) except Exception as e: return self.handle_error(request, exception=e) return super().dispatch(request, *args, **kwargs)
def dispatch(self, request, *args, **kwargs): """ Construct IDP server with config from settings dict """ conf = IdPConfig() try: conf.load(copy.deepcopy(get_idp_config())) self.IDP = Server(config=conf) except Exception as e: return self.handle_error(request, exception=e) return super(IdPHandlerViewMixin, self).dispatch(request, *args, **kwargs)
def register_endpoints(self, providers): """ See super class satosa.frontends.base.FrontendModule :type providers: list[str] :rtype: list[(str, ((satosa.context.Context, Any) -> satosa.response.Response, Any))] """ self._validate_providers(providers) self.config = self._build_idp_config_endpoints(self.config, providers) # Create the idp idp_config = IdPConfig().load(copy.deepcopy(self.config), metadata_construction=False) self.idp = Server(config=idp_config) return self._register_endpoints(providers)
def test_assertion_consumer_service(): c = IdPConfig() c.load_file(dotname("idp_conf")) c.context = "idp" c.metadata.load("local", full_path("InCommon-metadata.xml")) entity_id = "https://www.zimride.com/shibboleth" acs = c.metadata.assertion_consumer_service(entity_id) assert len(acs) == 1 assert acs[0][ "location"] == 'https://www.zimride.com/Shibboleth.sso/SAML2/POST'
def register_endpoints(self, backend_names): """ See super class satosa.frontends.base.FrontendModule :type backend_names: list[str] :rtype: list[(str, ((satosa.context.Context, Any) -> satosa.response.Response, Any))] """ self.idp_config = self._build_idp_config_endpoints( self.config[self.KEY_IDP_CONFIG], backend_names) # Create the idp idp_config = IdPConfig().load(copy.deepcopy(self.idp_config)) self.idp = Server(config=idp_config) return self._register_endpoints(backend_names)
def test_idp_2(): c = IdPConfig().load(IDP2) c.context = "idp" print(c) assert c.endpoint("single_logout_service", BINDING_SOAP) == [] assert c.endpoint("single_logout_service", BINDING_HTTP_REDIRECT) == ["http://localhost:8088/"] attribute_restrictions = c.getattr("policy", "idp").get_attribute_restrictions("") assert attribute_restrictions["edupersonaffiliation"][0].match("staff")
def test_do_idp_sso_descriptor(): conf = IdPConfig().load(IDP) idpsso = metadata.do_idpsso_descriptor(conf) assert isinstance(idpsso, md.IDPSSODescriptor) assert _eq(idpsso.keyswv(), [ 'protocol_support_enumeration', 'single_sign_on_service', 'want_authn_requests_signed', "extensions" ]) exts = idpsso.extensions.extension_elements assert len(exts) == 2 print(exts) inst = saml2.extension_element_to_element(exts[0], shibmd.ELEMENT_FROM_STRING, namespace=shibmd.NAMESPACE) assert isinstance(inst, shibmd.Scope) assert inst.text == "example.org" assert inst.regexp == "false" uiinfo = saml2.extension_element_to_element(exts[1], mdui.ELEMENT_FROM_STRING, namespace=mdui.NAMESPACE) assert uiinfo assert _eq(uiinfo.keyswv(), [ 'display_name', 'description', 'information_url', 'privacy_statement_url', 'keywords', 'logo' ]) assert len(uiinfo.privacy_statement_url) == 1 assert uiinfo.privacy_statement_url[ 0].text == "http://example.com/saml2/privacyStatement.html" assert len(uiinfo.description) == 1 assert uiinfo.description[0].text == "Exempel bolag" assert uiinfo.description[0].lang == "se" res = extension_elements_to_elements(exts, [shibmd, mdui]) assert len(res) == 2 # one is a shibmd.Scope instance and the other a mdui.UIInfo instance if isinstance(res[0], shibmd.Scope): assert isinstance(res[1], mdui.UIInfo) elif isinstance(res[1], shibmd.Scope): assert isinstance(res[0], mdui.UIInfo) found = idpsso.extensions.find_extensions(mdui.UIInfo.c_tag, mdui.NAMESPACE) assert len(found) == 1 elem = idpsso.extensions.extensions_as_elements(mdui.UIInfo.c_tag, mdui) assert len(elem) == 1 assert isinstance(elem[0], mdui.UIInfo)
def run(): '''配置IdP,生成证书、元数据文件''' if not os.path.exists(BASEDIR+'/djangosaml2idp/certificates/mycert.pem') or not \ os.path.exists(BASEDIR+'/djangosaml2idp/certificates/mykey.pem'): create_self_signed_cert() conf = IdPConfig() # pylint: disable=invalid-name conf.load(copy.deepcopy(idpsettings.SAML_IDP_CONFIG)) meta_data = entity_descriptor(conf) # pylint: disable=invalid-name content = text_type(meta_data).encode('utf-8') with open(BASEDIR + '/djangosaml2idp/saml2_config/idp_metadata.xml', 'wb') as f: f.write(content)
def __init__(self, config, attribute_map=None): """Initialize SAML Identity Provider. Args: config (dict): Identity Provider config info in dict form attribute_map (dict): Mapping of attribute keys to user data """ self._config = IdPConfig() self._config.load(config) self._server = Server(config=self._config) self.attribute_map = {} if attribute_map is not None: self.attribute_map = attribute_map
def load(cls, force_refresh: bool = False) -> Server: """ Instantiate a IDP Server instance based on the config defined in the SAML_IDP_CONFIG settings. Throws an ImproperlyConfigured exception if it could not do so for any reason. """ if cls._server_instance is None or force_refresh: conf = IdPConfig() md = cls.construct_metadata() try: conf.load(md) cls._server_instance = Server(config=conf) except Exception as e: raise ImproperlyConfigured(_('Could not instantiate an IDP based on the SAML_IDP_CONFIG settings and configured ServiceProviders: {}').format(str(e))) return cls._server_instance
def setup(self): """ Initiates the test. :return: None """ self.sp = FakeSP(None, config=SPConfig().load( TestConfiguration.get_instance().fake_sp_config, metadata_construction=False)) self.idp = FakeIdP( USERS, IdPConfig().load(TestConfiguration.get_instance().fake_idp_config, metadata_construction=False))
def idp_configure(self, metadata_construction=False): sys.path.insert(0, self.args.configpath) mod = import_module(self.args.config) self.idp_config = IdPConfig().load(mod.CONFIG, metadata_construction) if not self.args.insecure: self.idp_config.verify_ssl_cert = False else: if self.args.ca_certs: self.idp_config.ca_certs = self.args.ca_certs else: self.idp_config.ca_certs = "../keys/cacert.pem" self.idp = Server(config=self.idp_config)
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_assertion_consumer_service(): c = IdPConfig() c.load_file("idp_conf") c.context = "idp" xml_src = open("inCommon-metadata.xml").read() # A trick so outdated data is allowed c.metadata.import_metadata(xml_src, "-") print c.metadata.entity.keys() entity_id = "https://www.zimride.com/shibboleth" acs = c.assertion_consumer_services(entity_id) assert len(acs) == 1 assert acs[0].location == 'https://www.zimride.com/Shibboleth.sso/SAML2/POST'
def _load_idp_dynamic_entity_id(self, state): """ Loads an idp server with the entity id saved in state :type state: satosa.state.State :rtype: saml.server.Server :param state: The current state :return: An idp server """ # Change the idp entity id dynamically idp_config_file = copy.deepcopy(self.idp_config) idp_config_file["entityid"] = "{}/{}".format(self.idp_config["entityid"], state[self.name]["target_entity_id"]) idp_config = IdPConfig().load(idp_config_file) return Server(config=idp_config)
def _load_idp_dynamic_endpoints(self, context): """ Loads an idp server that accepts the target backend name in the endpoint url ex: /<backend_name>/sso/redirect :type context: The current context :rtype: saml.server.Server :param context: :return: An idp server """ target_entity_id = context.target_entity_id_from_path() idp_conf_file = self._load_endpoints_to_config(context.target_backend, target_entity_id) idp_config = IdPConfig().load(idp_conf_file) return Server(config=idp_config)
def _load_idp_dynamic_endpoints(self, context): """ Loads an idp server that accepts the target backend name in the endpoint url ex: /<backend_name>/sso/redirect :type context: The current context :rtype: saml.server.Server :param context: :return: An idp server """ target_entity_id = context.path.split("/")[1] context.decorate(Context.KEY_MIRROR_TARGET_ENTITYID, target_entity_id) idp_conf_file = self._load_endpoints_to_config(context.target_backend, target_entity_id) idp_config = IdPConfig().load(idp_conf_file, metadata_construction=False) return Server(config=idp_config)
def send_assertion(): logger.debug("Sending SAML assertion") idp_config = current_app.config['IDP_CONFIG'] idp_config_object = IdPConfig().load(copy.deepcopy(idp_config), metadata_construction=False) idp = Server(config=idp_config_object) name_id_format = 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' name_id_policy = NameIDPolicy(format=name_id_format) args = { 'identity': {}, 'name_id': None, 'authn': { 'class_ref': 'https://refeds.org/profile/mfa', 'authn_auth': idp_config['entityid'] }, 'sign_response': True, 'sign_assertion': False, 'encrypted_advice_attributes': False, 'in_response_to': session['saml_authn_request_id'], 'sp_entity_id': session['saml_sp_entity_id'], 'name_id_policy': name_id_policy, 'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', 'destination': session['saml_sp_acs'], 'sign_alg': 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256', 'digest_alg': 'http://www.w3.org/2001/04/xmlenc#sha256', } saml_response_string = idp.create_authn_response(**args) saml_response_string_encoded = saml_response_string.encode('utf-8') saml_response = b64encode(saml_response_string_encoded) saml_response_encoded = saml_response.decode('ascii') action = session['saml_sp_acs'] saml_relay_state = session['saml_relay_state'] rendered_template = render_template( 'saml_http_post_binding.html', action=action, saml_response_encoded=saml_response_encoded, saml_relay_state=saml_relay_state) # Kill the session session.clear() return rendered_template
def create_authn_response(session_id, identity=dict(), sign=True): config = IdPConfig() config.load(idp_config) idp_server = Server(config=config) idp_server.ident = Identifier(auth.AuthDictCache(dict(), '_ident')) authn_response = str( idp_server.authn_response( identity=identity, in_response_to=session_id, destination='https://foo.example.com/sp/acs', sp_entity_id='https://foo.example.com/sp/metadata', name_id_policy=None, userid='Irrelevent', sign=sign, instance=True)) response = samlp.response_from_string(authn_response) return response.assertion[0].subject.name_id.text, authn_response