def create_logout_request(self, destination, issuer_entity_id, subject_id=None, name_id=None, reason=None, expire=None, message_id=0, consent=None, extensions=None, sign=False, session_indexes=None, sign_alg=None, digest_alg=None): """ Constructs a LogoutRequest :param destination: Destination of the request :param issuer_entity_id: The entity ID of the IdP the request is target at. :param subject_id: The identifier of the subject :param name_id: A NameID instance identifying the subject :param reason: An indication of the reason for the logout, in the form of a URI reference. :param expire: The time at which the request expires, after which the recipient may discard the message. :param message_id: Request identifier :param consent: Whether the principal have given her consent :param extensions: Possible extensions :param sign: Whether the query should be signed or not. :param session_indexes: SessionIndex instances or just values :return: A LogoutRequest instance """ if subject_id: if self.entity_type == "idp": name_id = NameID(text=self.users.get_entityid(subject_id, issuer_entity_id, False)) else: name_id = NameID(text=subject_id) if not name_id: raise SAMLError("Missing subject identification") args = {} if session_indexes: sis = [] for si in session_indexes: if isinstance(si, SessionIndex): sis.append(si) else: sis.append(SessionIndex(text=si)) args["session_index"] = sis return self._message(LogoutRequest, destination, message_id, consent, extensions, sign, name_id=name_id, reason=reason, not_on_or_after=expire, issuer=self._issuer(), sign_alg=sign_alg, digest_alg=digest_alg, **args)
def create_logout_request(self, destination, issuer_entity_id, subject_id=None, name_id=None, reason=None, expire=None, message_id=0, consent=None, extensions=None, sign=False): """ Constructs a LogoutRequest :param destination: Destination of the request :param issuer_entity_id: The entity ID of the IdP the request is target at. :param subject_id: The identifier of the subject :param name_id: A NameID instance identifying the subject :param reason: An indication of the reason for the logout, in the form of a URI reference. :param expire: The time at which the request expires, after which the recipient may discard the message. :param message_id: Request identifier :param consent: Whether the principal have given her consent :param extensions: Possible extensions :param sign: Whether the query should be signed or not. :return: A LogoutRequest instance """ if subject_id: if self.entity_type == "idp": name_id = NameID(text=self.users.get_entityid( subject_id, issuer_entity_id, False)) else: name_id = NameID(text=subject_id) if not name_id: raise Exception("Missing subject identification") return self._message(LogoutRequest, destination, message_id, consent, extensions, sign, name_id=name_id, reason=reason, not_on_or_after=expire)
def test_basic(): sp = Saml2Client(config_file="servera_conf") idp = Server(config_file="idp_all_conf") # -------- @SP ------------ binding, destination = sp.pick_binding("manage_name_id_service", entity_id=idp.config.entityid) nameid = NameID(format=NAMEID_FORMAT_TRANSIENT, text="foobar") newid = NewID(text="Barfoo") mid, mreq = sp.create_manage_name_id_request(destination, name_id=nameid, new_id=newid) print(mreq) rargs = sp.apply_binding(binding, "%s" % mreq, destination, "") # --------- @IDP -------------- _req = idp.parse_manage_name_id_request(rargs["data"], binding) print(_req.message) assert mid == _req.message.id
def test_assertion_with_zero_attributes(): ava = {} ast = Assertion(ava) policy = Policy({ "default": { "lifetime": { "minutes": 240 }, "attribute_restrictions": None, # means all I have "name_form": NAME_FORMAT_URI }, }) name_id = NameID(format=NAMEID_FORMAT_TRANSIENT, text="foobar") issuer = Issuer(text="entityid", format=NAMEID_FORMAT_ENTITY) msg = ast.construct("sp_entity_id", "in_response_to", "consumer_url", name_id, [AttributeConverterNOOP(NAME_FORMAT_URI)], policy, issuer=issuer, authn_decl=ACD, authn_auth="authn_authn") print(msg) assert msg.attribute_statement == []
def get_nameid(self, userid, nformat, sp_name_qualifier, name_qualifier): if nformat == NAMEID_FORMAT_PERSISTENT: nameid = self.match_local_id(userid, sp_name_qualifier, name_qualifier) if nameid: logger.debug( "Found existing persistent NameId {nid} for user {uid}". format(nid=nameid, uid=userid)) return nameid _id = self.create_id(nformat, name_qualifier, sp_name_qualifier) if nformat == NAMEID_FORMAT_EMAILADDRESS: if not self.domain: raise SAMLError("Can't issue email nameids, unknown domain") _id = "%s@%s" % (_id, self.domain) nameid = NameID( format=nformat, sp_name_qualifier=sp_name_qualifier, name_qualifier=name_qualifier, text=_id, ) self.store(userid, nameid) return nameid
def to_(self, attrvals): """ Create a list of Attribute instances. :param attrvals: A dictionary of attributes and values :return: A list of Attribute instances """ attributes = [] for key, value in attrvals.items(): name = self._to.get(key.lower()) if name: if name == "urn:oid:1.3.6.1.4.1.5923.1.1.1.10": # special case for eduPersonTargetedID attr_value = do_ava( NameID(format=NAMEID_FORMAT_PERSISTENT, text=value).to_string()) else: attr_value = do_ava(value) attributes.append( factory(saml.Attribute, name=name, name_format=self.name_format, friendly_name=key, attribute_value=attr_value)) else: attributes.append( factory(saml.Attribute, name=key, attribute_value=do_ava(value))) return attributes
def test_assertion_with_noop_attribute_conv(): ava = {"urn:oid:2.5.4.4": "Roland", "urn:oid:2.5.4.42": "Hedberg" } ast = Assertion(ava) policy = Policy({ "default": { "lifetime": {"minutes": 240}, "attribute_restrictions": None, # means all I have "name_form": NAME_FORMAT_URI }, }) name_id = NameID(format=NAMEID_FORMAT_TRANSIENT, text="foobar") issuer = Issuer(text="entityid", format=NAMEID_FORMAT_ENTITY) msg = ast.construct("sp_entity_id", "in_response_to", "consumer_url", name_id, [AttributeConverterNOOP(NAME_FORMAT_URI)], policy, issuer=issuer, authn_decl=ACD , authn_auth="authn_authn") print(msg) for attr in msg.attribute_statement[0].attribute: assert attr.name_format == NAME_FORMAT_URI assert len(attr.attribute_value) == 1 if attr.name == "urn:oid:2.5.4.42": assert attr.attribute_value[0].text == "Hedberg" elif attr.name == "urn:oid:2.5.4.4": assert attr.attribute_value[0].text == "Roland"
def test_assertion_with_authn_instant(): ava = {} ast = Assertion(ava) policy = Policy({ "default": { "lifetime": { "minutes": 240 }, "attribute_restrictions": None, # means all I have "name_form": NAME_FORMAT_URI }, }) name_id = NameID(format=NAMEID_FORMAT_TRANSIENT, text="foobar") issuer = Issuer(text="entityid", format=NAMEID_FORMAT_ENTITY) farg = add_path( {}, ['subject', 'subject_confirmation', 'method', saml.SCM_BEARER]) add_path(farg['subject']['subject_confirmation'], ['subject_confirmation_data', 'in_response_to', 'in_response_to']) add_path(farg['subject']['subject_confirmation'], ['subject_confirmation_data', 'recipient', 'consumer_url']) msg = ast.construct("sp_entity_id", [AttributeConverterNOOP(NAME_FORMAT_URI)], policy, issuer=issuer, authn_decl=ACD, authn_auth="authn_authn", authn_instant=1234567890, name_id=name_id, farg=farg) print(msg) assert msg.authn_statement[0].authn_instant == "2009-02-13T23:31:30Z"
def get_auth_token(): samlServer = server.Server("idp_conf") nid = NameID(format=NAMEID_FORMAT_UNSPECIFIED, text="ganderson") authn_dict = { "decl": "authn_decl", "class_ref": UNSPECIFIED, "authn_instant": time.time(), } samlResp = samlServer.create_authn_response( identity={}, in_response_to="", userid="ganderson", destination= "https://uat.envestnet.com/openenv/api/auth/login?firm=edelman", sp_entity_id=None, sign_assertion=True, name_id=nid, authn=authn_dict, ) url = "https://uat.envestnet.com/openenv/api/auth/login?firm=edelman" headers = DEFAULT_HEADERS.copy() headers["Content-Type"] = "application/xml; charset=utf-8" resp = webServiceCall(url, headers, data=samlResp) return resp.headers["Token"]
def test_from_local_nest_eduPersonTargetedID_in_NameID(self): ava = {"edupersontargetedid": "test value"} attributes = from_local(self.acs, ava, URI_NF) assert len(attributes) == 1 assert len(attributes[0].attribute_value) == 1 assert attributes[0].attribute_value[0].text == NameID( format=NAMEID_FORMAT_PERSISTENT, text="test value").to_string().decode("utf-8")
def test_request_response(): sp = Saml2Client(config_file="servera_conf") with closing(Server(config_file="idp_all_conf")) as idp: binding, destination = sp.pick_binding("name_id_mapping_service", entity_id=idp.config.entityid) policy = NameIDPolicy(format=NAMEID_FORMAT_TRANSIENT, sp_name_qualifier="urn:mace:swamid:junk", allow_create="true") nameid = NameID(format=NAMEID_FORMAT_TRANSIENT, text="foobar") mid, nmr = sp.create_name_id_mapping_request(policy, nameid, destination) print nmr args = sp.use_soap(nmr, destination) # ------- IDP ------------ req = idp.parse_name_id_mapping_request(args["data"], binding) in_response_to = req.message.id name_id = NameID(format=NAMEID_FORMAT_PERSISTENT, text="foobar") idp_response = idp.create_name_id_mapping_response( name_id, in_response_to=in_response_to) print idp_response ht_args = sp.use_soap(idp_response) # ------- SP ------------ _resp = sp.parse_name_id_mapping_request_response( ht_args["data"], binding) print _resp.response r_name_id = _resp.response.name_id assert r_name_id.format == NAMEID_FORMAT_PERSISTENT assert r_name_id.text == "foobar"
def test_create_authn_request_with_subject(self, context, idp_conf, sp_conf, internal_response): name_id_value = 'somenameid' name_id = NameID(format=NAMEID_FORMAT_UNSPECIFIED, text=name_id_value) subject = Subject(name_id=name_id) samlfrontend = self.setup_for_authn_req( context, idp_conf, sp_conf, subject=subject ) _, internal_req = samlfrontend.handle_authn_request(context, BINDING_HTTP_REDIRECT) assert internal_req.subject_id == name_id_value
def _create_idp_response( self, authn_request_id='2aaaeb7692471eb4ba00d5546877a7fd', cls=Response): issue_instant = datetime.utcnow().isoformat() + 'Z' not_before = (datetime.utcnow() - timedelta(minutes=5)).isoformat() + 'Z' not_on_or_after = (datetime.utcnow() + timedelta(minutes=5)).isoformat() + 'Z' issuer = Issuer(format=NAMEID_FORMAT_ENTITY, text='http://nohost/auth') signature = pre_signature_part( 's2998eb2e03b5006acb0a931d0fb558b0e4ec360c7') status = Status(status_code=StatusCode(value=STATUS_SUCCESS)) subject_confirmation_data = SubjectConfirmationData( not_on_or_after=not_on_or_after, in_response_to=authn_request_id, recipient='http://nohost/') subject_confirmation = SubjectConfirmation( method=SCM_BEARER, subject_confirmation_data=subject_confirmation_data) subject = Subject(name_id=NameID(text='AABVSVesMLYDiHtowyX4MDu6UopU', format=NAMEID_FORMAT_TRANSIENT), subject_confirmation=subject_confirmation) conditions = Conditions(not_before=not_before, not_on_or_after=not_on_or_after, audience_restriction=AudienceRestriction( Audience('http://nohost/')), one_time_use=OneTimeUse()) authn_context = AuthnContext(authn_context_decl_ref=AuthnContextClassRef( 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' )) authn_statement = AuthnStatement(authn_instant=issue_instant, authn_context=authn_context, session_index=self.session_index) attribute_statement = attribute_statement_from_string( self.attribute_xml) assertion = Assertion( id='s2bb879ef893d1b27fb90903e7c7e2779a3e7502c1', version='2.0', issue_instant=issue_instant, issuer=issuer, subject=subject, conditions=conditions, authn_statement=authn_statement, attribute_statement=attribute_statement, ) return cls(id='s2998eb2e03b5006acb0a931d0fb558b0e4ec360c7', in_response_to=authn_request_id, version='2.0', issue_instant=issue_instant, destination='http://nohost/', issuer=issuer, signature=signature, status=status, assertion=assertion)
def decode(txt): _nid = NameID() for part in txt.split(","): if part.find("=") != -1: i, val = part.split("=") try: setattr(_nid, ATTR[int(i)], unquote(val)) except: pass return _nid
def decode(txt): """Turns a coded string by code() into a NameID class instance. :param txt: The coded string """ _nid = NameID() for part in txt.split(","): if part.find("=") != -1: i, val = part.split("=") try: setattr(_nid, ATTR[int(i)], unquote(val)) except: pass return _nid
def get_nameid(self, userid, nformat, sp_name_qualifier, name_qualifier): _id = self.create_id(nformat, name_qualifier, sp_name_qualifier) if nformat == NAMEID_FORMAT_EMAILADDRESS: if not self.domain: raise SAMLError("Can't issue email nameids, unknown domain") _id = "%s@%s" % (_id, self.domain) nameid = NameID(format=nformat, sp_name_qualifier=sp_name_qualifier, name_qualifier=name_qualifier, text=_id) self.store(userid, nameid) return nameid
def test_basic(): sp = Saml2Client(config_file="servera_conf") with closing(Server(config_file="idp_all_conf")) as idp: srvs = sp.metadata.authn_query_service(idp.config.entityid) destination = srvs[0]["location"] authn_context = requested_authn_context(INTERNETPROTOCOLPASSWORD) subject = Subject(text="abc", name_id=NameID(format=NAMEID_FORMAT_TRANSIENT)) _id, aq = sp.create_authn_query(subject, destination, authn_context) print(aq) assert isinstance(aq, AuthnQuery)
def post_auth(authData): for t in authData: if t[0] == 'Stripped-User-Name': userName = t[1][1:-1] elif t[0] == 'User-Password': userPassword = t[1][1:-1] identity = ldap_attributes(userName, userPassword) if identity == None: return radiusd.RLM_MODULE_FAIL indentityFiltered = { k: identity[k] for k in set(ATTRS) & set(identity.keys()) } print {k: identity[k] for k in set(ATTRS) & set(identity.keys())} policy = Policy({ 'default': { 'lifetime': { 'minutes': 60 }, 'attribute_restrictions': None, 'name_form': NAME_FORMAT_URI } }) name_id = NameID(format=NAMEID_FORMAT_TRANSIENT, text='urn:mace:' + LDAP_SERVER) issuer = Issuer(text='moonshot.' + LDAP_SERVER, format=NAMEID_FORMAT_ENTITY) ast = Assertion(indentityFiltered) assertion = ast.construct('', '', '', name_id, [AttributeConverterNOOP(NAME_FORMAT_URI)], policy, issuer=issuer) assertion = str(assertion).replace('\n', '') attr = 'SAML-AAA-Assertion' result = (tuple([(attr, x) for x in eq_len_parts('%s' % assertion)])) return radiusd.RLM_MODULE_UPDATED, result, None
def test_base_request(): sp = Saml2Client(config_file="servera_conf") idp = Server(config_file="idp_all_conf") binding, destination = sp.pick_binding("name_id_mapping_service", entity_id=idp.config.entityid) policy = NameIDPolicy(format=NAMEID_FORMAT_TRANSIENT, sp_name_qualifier="urn:mace:swamid:junk", allow_create="true") nameid = NameID(format=NAMEID_FORMAT_TRANSIENT, text="foobar") nmr = sp.create_name_id_mapping_request(policy, nameid, destination) print nmr assert isinstance(nmr, NameIDMappingRequest)
def test_flow(): sp = Saml2Client(config_file="servera_conf") idp = Server(config_file="idp_all_conf") binding, destination = sp.pick_binding("manage_name_id_service", entity_id=idp.config.entityid) nameid = NameID(format=NAMEID_FORMAT_TRANSIENT, text="foobar") newid = NewID(text="Barfoo") mid, mreq = sp.create_manage_name_id_request(destination, name_id=nameid, new_id=newid) print(mreq) rargs = sp.apply_binding(binding, "%s" % mreq, destination, "") # --------- @IDP -------------- _req = idp.parse_manage_name_id_request(rargs["data"], binding) print((_req.message)) mnir = idp.create_manage_name_id_response(_req.message, None) if binding != BINDING_SOAP: binding, destination = idp.pick_binding("manage_name_id_service", entity_id=sp.config.entityid) else: destination = "" respargs = idp.apply_binding(binding, "%s" % mnir, destination, "") print(respargs) # ---------- @SP --------------- _response = sp.parse_manage_name_id_request_response( respargs["data"], binding) print((_response.response)) assert _response.response.id == mnir.id
def _handle_authn_response(self, context, internal_response, idp): """ See super class satosa.frontends.base.FrontendModule :type context: satosa.context.Context :type internal_response: satosa.internal_data.InternalResponse :type idp: saml.server.Server :param context: The current context :param internal_response: The internal response :param idp: The saml frontend idp server :return: A saml response """ request_state = self.load_state(context.state) resp_args = request_state["resp_args"] ava = self.converter.from_internal(self.attribute_profile, internal_response.get_attributes()) auth_info = {} if self.acr_mapping: auth_info["class_ref"] = self.acr_mapping.get(internal_response.auth_info.issuer, self.acr_mapping[""]) else: auth_info["class_ref"] = internal_response.auth_info.auth_class_ref name_id = NameID(text=internal_response.get_user_id(), format=get_saml_name_id_format(internal_response.user_id_hash_type), sp_name_qualifier=None, name_qualifier=None) # Will signed the response by default resp = self.construct_authn_response(idp, context.state, ava, name_id=name_id, authn=auth_info, resp_args=resp_args, relay_state=request_state["relay_state"], sign_response=True) return resp
def test_assertion_with_noop_attribute_conv(): ava = {"urn:oid:2.5.4.4": "Roland", "urn:oid:2.5.4.42": "Hedberg"} ast = Assertion(ava) policy = Policy({ "default": { "lifetime": { "minutes": 240 }, "attribute_restrictions": None, # means all I have "name_form": NAME_FORMAT_URI }, }) name_id = NameID(format=NAMEID_FORMAT_TRANSIENT, text="foobar") issuer = Issuer(text="entityid", format=NAMEID_FORMAT_ENTITY) farg = add_path( {}, ['subject', 'subject_confirmation', 'method', saml.SCM_BEARER]) add_path(farg['subject']['subject_confirmation'], ['subject_confirmation_data', 'in_response_to', 'in_response_to']) add_path(farg['subject']['subject_confirmation'], ['subject_confirmation_data', 'recipient', 'consumer_url']) msg = ast.construct("sp_entity_id", [AttributeConverterNOOP(NAME_FORMAT_URI)], policy, issuer=issuer, farg=farg, authn_decl=ACD, name_id=name_id, authn_auth="authn_authn") print(msg) for attr in msg.attribute_statement[0].attribute: assert attr.name_format == NAME_FORMAT_URI assert len(attr.attribute_value) == 1 if attr.name == "urn:oid:2.5.4.42": assert attr.attribute_value[0].text == "Hedberg" elif attr.name == "urn:oid:2.5.4.4": assert attr.attribute_value[0].text == "Roland"
def saml_logout(): saml_client = saml_client_for( current_app.config.get('SECURITY_SAML_IDP_METADATA').split(',')[0]) nid = NameID(format=NAMEID_FORMAT_UNSPECIFIED, text="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified") logout_url = LogoutUrl( text=url_for("saml.saml_logout_postback", _external=True)) destination = current_app.config.get('SECURITY_SAML_FA_URL') extensions = Extensions(extension_elements=[logout_url]) req_id, logout_request = saml_client.create_logout_request( name_id=nid, destination=destination, issuer_entity_id=current_app.config.get('SECURITY_SAML_ENTITY_ID'), sign=True, consent="urn:oasis:names:tc:SAML:2.0:logout:user", extensions=extensions) post_message = http_form_post_message(message=logout_request, location=destination) return post_message['data']
from saml2 import extension_elements_to_elements from saml2 import s_utils from saml2 import sigver from saml2 import time_util from saml2.s_utils import OtherError from saml2.s_utils import do_attribute_statement from saml2.s_utils import factory from saml2.soap import make_soap_enveloped_saml_thingy from saml2 import BINDING_HTTP_POST from saml2 import BINDING_HTTP_REDIRECT from py.test import raises from pathutils import full_path nid = NameID(name_qualifier="foo", format=NAMEID_FORMAT_TRANSIENT, text="123456") AUTHN = { "class_ref": INTERNETPROTOCOLPASSWORD, "authn_auth": "http://www.example.com/login" } def _eq(l1, l2): return set(l1) == set(l2) BASEDIR = os.path.abspath(os.path.dirname(__file__))
def _handle_authn_response(self, context, internal_response, idp): """ See super class satosa.frontends.base.FrontendModule :type context: satosa.context.Context :type internal_response: satosa.internal.InternalData :type idp: saml.server.Server :param context: The current context :param internal_response: The internal response :param idp: The saml frontend idp server :return: A saml response """ request_state = self.load_state(context.state) resp_args = request_state["resp_args"] sp_entity_id = resp_args["sp_entity_id"] internal_response.attributes = self._filter_attributes( idp, internal_response, context) ava = self.converter.from_internal( self.attribute_profile, internal_response.attributes) auth_info = {} if self.acr_mapping: auth_info["class_ref"] = self.acr_mapping.get( internal_response.auth_info.issuer, self.acr_mapping[""]) else: auth_info["class_ref"] = internal_response.auth_info.auth_class_ref auth_info["authn_auth"] = internal_response.auth_info.issuer if self.custom_attribute_release: custom_release = util.get_dict_defaults( self.custom_attribute_release, internal_response.auth_info.issuer, sp_entity_id) attributes_to_remove = custom_release.get("exclude", []) for k in attributes_to_remove: ava.pop(k, None) nameid_value = internal_response.subject_id nameid_format = subject_type_to_saml_nameid_format( internal_response.subject_type ) # If the backend did not receive a SAML <NameID> and so # name_id is set to None then do not create a NameID instance. # Instead pass None as the name name_id to the IdP server # instance and it will use its configured policy to construct # a <NameID>, with the default to create a transient <NameID>. name_id = None if not nameid_value else NameID( text=nameid_value, format=nameid_format, sp_name_qualifier=None, name_qualifier=None, ) msg = "returning attributes {}".format(json.dumps(ava)) logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state), message=msg) logger.debug(logline) idp_conf = self.idp_config.get('service', {}).get('idp', {}) policies = idp_conf.get('policy', {}) sp_policy = policies.get('default', {}) sp_policy.update(policies.get(sp_entity_id, {})) sign_assertion = sp_policy.get('sign_assertion', False) sign_response = sp_policy.get('sign_response', True) encrypt_assertion = sp_policy.get('encrypt_assertion', False) encrypted_advice_attributes = sp_policy.get('encrypted_advice_attributes', False) signing_algorithm = idp_conf.get('signing_algorithm') digest_algorithm = idp_conf.get('digest_algorithm') sign_alg_attr = sp_policy.get('sign_alg', 'SIG_RSA_SHA256') digest_alg_attr = sp_policy.get('digest_alg', 'DIGEST_SHA256') # Construct arguments for method create_authn_response # on IdP Server instance args = { # Add the SP details **resp_args, # AuthnResponse data 'identity': ava, 'name_id': name_id, 'authn': auth_info, 'sign_response': sign_response, 'sign_assertion': sign_assertion, 'encrypt_assertion': encrypt_assertion, 'encrypted_advice_attributes': encrypted_advice_attributes, } args['sign_alg'] = signing_algorithm if not args['sign_alg']: try: args['sign_alg'] = getattr(xmldsig, sign_alg_attr) except AttributeError as e: msg = "Unsupported sign algorithm {}".format(sign_alg) logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state), message=msg) logger.error(logline) raise Exception(msg) from e msg = "signing with algorithm {}".format(args['sign_alg']) logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state), message=msg) logger.debug(logline) args['digest_alg'] = digest_algorithm if not args['digest_alg']: try: args['digest_alg'] = getattr(xmldsig, digest_alg_attr) except AttributeError as e: msg = "Unsupported digest algorithm {}".format(digest_alg) logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state), message=msg) logger.error(logline) raise Exception(msg) from e msg = "using digest algorithm {}".format(args['digest_alg']) logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state), message=msg) logger.debug(logline) if sign_alg_attr or digest_alg_attr: msg = ( "sign_alg and digest_alg are deprecated; " "instead, use signing_algorithm and digest_algorithm " "under the service/idp configuration path " "(not under policy/default)." ) logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state), message=msg) logger.warning(msg) resp = idp.create_authn_response(**args) http_args = idp.apply_binding( resp_args["binding"], str(resp), resp_args["destination"], request_state["relay_state"], response=True) # Set the common domain cookie _saml_idp if so configured. if self.config.get('common_domain_cookie'): self._set_common_domain_cookie(internal_response, http_args, context) del context.state[self.name] return make_saml_response(resp_args["binding"], http_args)
def _handle_authn_response(self, context, internal_response, idp): """ See super class satosa.frontends.base.FrontendModule :type context: satosa.context.Context :type internal_response: satosa.internal.InternalData :type idp: saml.server.Server :param context: The current context :param internal_response: The internal response :param idp: The saml frontend idp server :return: A saml response """ request_state = self.load_state(context.state) resp_args = request_state["resp_args"] sp_entity_id = resp_args["sp_entity_id"] internal_response.attributes = self._filter_attributes( idp, internal_response, context) ava = self.converter.from_internal(self.attribute_profile, internal_response.attributes) auth_info = {} if self.acr_mapping: auth_info["class_ref"] = self.acr_mapping.get( internal_response.auth_info.issuer, self.acr_mapping[""]) else: auth_info["class_ref"] = internal_response.auth_info.auth_class_ref auth_info["authn_auth"] = internal_response.auth_info.issuer if self.custom_attribute_release: custom_release = util.get_dict_defaults( self.custom_attribute_release, internal_response.auth_info.issuer, sp_entity_id) attributes_to_remove = custom_release.get("exclude", []) for k in attributes_to_remove: ava.pop(k, None) nameid_value = internal_response.subject_id nameid_format = subject_type_to_saml_nameid_format( internal_response.subject_type) # If the backend did not receive a SAML <NameID> and so # name_id is set to None then do not create a NameID instance. # Instead pass None as the name name_id to the IdP server # instance and it will use its configured policy to construct # a <NameID>, with the default to create a transient <NameID>. name_id = None if not nameid_value else NameID( text=nameid_value, format=nameid_format, sp_name_qualifier=None, name_qualifier=None, ) dbgmsg = "returning attributes %s" % json.dumps(ava) satosa_logging(logger, logging.DEBUG, dbgmsg, context.state) policies = self.idp_config.get('service', {}).get('idp', {}).get('policy', {}) sp_policy = policies.get('default', {}) sp_policy.update(policies.get(sp_entity_id, {})) sign_assertion = sp_policy.get('sign_assertion', False) sign_response = sp_policy.get('sign_response', True) sign_alg = sp_policy.get('sign_alg', 'SIG_RSA_SHA256') digest_alg = sp_policy.get('digest_alg', 'DIGEST_SHA256') # Construct arguments for method create_authn_response # on IdP Server instance args = { 'identity': ava, 'name_id': name_id, 'authn': auth_info, 'sign_response': sign_response, 'sign_assertion': sign_assertion, } # Add the SP details args.update(**resp_args) try: args['sign_alg'] = getattr(xmldsig, sign_alg) except AttributeError as e: errmsg = "Unsupported sign algorithm %s" % sign_alg satosa_logging(logger, logging.ERROR, errmsg, context.state) raise Exception(errmsg) from e else: dbgmsg = "signing with algorithm %s" % args['sign_alg'] satosa_logging(logger, logging.DEBUG, dbgmsg, context.state) try: args['digest_alg'] = getattr(xmldsig, digest_alg) except AttributeError as e: errmsg = "Unsupported digest algorithm %s" % digest_alg satosa_logging(logger, logging.ERROR, errmsg, context.state) raise Exception(errmsg) from e else: dbgmsg = "using digest algorithm %s" % args['digest_alg'] satosa_logging(logger, logging.DEBUG, dbgmsg, context.state) resp = idp.create_authn_response(**args) http_args = idp.apply_binding(resp_args["binding"], str(resp), resp_args["destination"], request_state["relay_state"], response=True) # Set the common domain cookie _saml_idp if so configured. if self.config.get('common_domain_cookie'): self._set_common_domain_cookie(internal_response, http_args, context) del context.state[self.name] return make_saml_response(resp_args["binding"], http_args)
from saml2.saml import NameID from saml2.saml import NAMEID_FORMAT_TRANSIENT __author__ = 'rolandh' from saml2 import config from saml2.client import Saml2Client from saml2.time_util import str_to_time, in_a_while SESSION_INFO_PATTERN = {"ava": {}, "came from": "", "not_on_or_after": 0, "issuer": "", "session_id": -1} nid = NameID(name_qualifier="foo", format=NAMEID_FORMAT_TRANSIENT, text="abcdefgh") nid0 = NameID(name_qualifier="foo", format=NAMEID_FORMAT_TRANSIENT, text="01234567") def add_derek_info(sp): not_on_or_after = str_to_time(in_a_while(days=1)) session_info = SESSION_INFO_PATTERN.copy() session_info["ava"] = {"givenName": ["Derek"], "umuselin": ["deje0001"]} session_info["issuer"] = "urn:mace:example.com:saml:idp" session_info["name_id"] = nid session_info["not_on_or_after"] = not_on_or_after # subject_id, entity_id, info, timestamp sp.users.add_information_about_person(session_info) class TestVirtualOrg(): def setup_class(self):
def decode(txt): _nid = NameID() for part in txt.split(","): i, val = part.split("=") setattr(_nid, ATTR[int(i)], unquote(val)) return _nid
"not_on_or_after": 0, "issuer": "", "session_id": -1 } def _eq(l1, l2): return set(l1) == set(l2) def nid_eq(l1, l2): return _eq([code(c) for c in l1], [code(c) for c in l2]) nid = [ NameID(name_qualifier="foo", format=NAMEID_FORMAT_TRANSIENT, text="1234"), NameID(name_qualifier="foo", format=NAMEID_FORMAT_TRANSIENT, text="9876"), NameID(name_qualifier="foo", format=NAMEID_FORMAT_TRANSIENT, text="1000") ] class TestClass: def setup_class(self): self.cache = Cache() def test_set(self): not_on_or_after = str_to_time(in_a_while(days=1)) session_info = SESSION_INFO_PATTERN.copy() session_info["ava"] = {"givenName": ["Derek"]} self.cache.set(nid[0], "abcd", session_info, not_on_or_after)