Merge pull request #192 from HaToHo/master
Encryption and signing of authentication
This commit is contained in:
14
.gitignore
vendored
14
.gitignore
vendored
@@ -178,4 +178,16 @@ example/sp-repoze/sp_conf_example.py
|
|||||||
|
|
||||||
example/idp2/idp_conf_example.py
|
example/idp2/idp_conf_example.py
|
||||||
|
|
||||||
example/sp-wsgi/sp_conf.py
|
example/idp2/lidp.xml
|
||||||
|
|
||||||
|
example/idp2/old_idp.xml
|
||||||
|
|
||||||
|
example/sp-repoze/old_sp.xml
|
||||||
|
|
||||||
|
example/sp-repoze/sp_conf_2.Pygmalion
|
||||||
|
|
||||||
|
.gitignore.swp
|
||||||
|
|
||||||
|
example/sp-repoze/sp_conf_2.py
|
||||||
|
|
||||||
|
sp.xml
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ saml_conf = sp_conf
|
|||||||
remember_name = auth_tkt
|
remember_name = auth_tkt
|
||||||
sid_store = outstanding
|
sid_store = outstanding
|
||||||
idp_query_param = IdPEntityId
|
idp_query_param = IdPEntityId
|
||||||
discovery = http://130.239.201.5/role/idp.ds
|
#discovery = http://130.239.201.5/role/idp.ds
|
||||||
|
|
||||||
[general]
|
[general]
|
||||||
request_classifier = s2repoze.plugins.challenge_decider:my_request_classifier
|
request_classifier = s2repoze.plugins.challenge_decider:my_request_classifier
|
||||||
|
|||||||
@@ -337,7 +337,7 @@ class SAML2Plugin(object):
|
|||||||
element_to_extension_element(spcertenc)])
|
element_to_extension_element(spcertenc)])
|
||||||
|
|
||||||
if _cli.authn_requests_signed:
|
if _cli.authn_requests_signed:
|
||||||
_sid = saml2.s_utils.sid(_cli.seed)
|
_sid = saml2.s_utils.sid()
|
||||||
req_id, msg_str = _cli.create_authn_request(
|
req_id, msg_str = _cli.create_authn_request(
|
||||||
dest, vorg=vorg_name, sign=_cli.authn_requests_signed,
|
dest, vorg=vorg_name, sign=_cli.authn_requests_signed,
|
||||||
message_id=_sid, extensions=extensions)
|
message_id=_sid, extensions=extensions)
|
||||||
|
|||||||
@@ -558,6 +558,106 @@ class SamlBase(ExtensionContainer):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def get_ns_map_attribute(self, attributes, uri_set):
|
||||||
|
for attribute in attributes:
|
||||||
|
if attribute[0] == "{":
|
||||||
|
uri, tag = attribute[1:].split("}")
|
||||||
|
uri_set.add(uri)
|
||||||
|
return uri_set
|
||||||
|
|
||||||
|
def tag_get_uri(self, elem):
|
||||||
|
if elem.tag[0] == "{":
|
||||||
|
uri, tag = elem.tag[1:].split("}")
|
||||||
|
return uri
|
||||||
|
return None
|
||||||
|
def get_ns_map(self, elements, uri_set):
|
||||||
|
|
||||||
|
for elem in elements:
|
||||||
|
uri_set = self.get_ns_map_attribute(elem.attrib, uri_set)
|
||||||
|
uri_set = self.get_ns_map(elem._children, uri_set)
|
||||||
|
uri = self.tag_get_uri(elem)
|
||||||
|
if uri is not None:
|
||||||
|
uri_set.add(uri)
|
||||||
|
return uri_set
|
||||||
|
|
||||||
|
def get_prefix_map(self, elements):
|
||||||
|
uri_set = self.get_ns_map(elements, set())
|
||||||
|
prefix_map = {}
|
||||||
|
for uri in sorted(uri_set):
|
||||||
|
prefix_map["encas%d" % len(prefix_map)] = uri
|
||||||
|
return prefix_map
|
||||||
|
|
||||||
|
def get_xml_string_with_self_contained_assertion_within_advice_encrypted_assertion(self, assertion_tag, advice_tag):
|
||||||
|
for tmp_encrypted_assertion in self.assertion.advice.encrypted_assertion:
|
||||||
|
prefix_map = self.get_prefix_map([tmp_encrypted_assertion._to_element_tree().
|
||||||
|
find(assertion_tag)])
|
||||||
|
|
||||||
|
tree = self._to_element_tree()
|
||||||
|
|
||||||
|
self.set_prefixes(tree.find(assertion_tag).find(advice_tag).find(tmp_encrypted_assertion._to_element_tree()
|
||||||
|
.tag).find(assertion_tag), prefix_map)
|
||||||
|
|
||||||
|
return ElementTree.tostring(tree, encoding="UTF-8")
|
||||||
|
|
||||||
|
def get_xml_string_with_self_contained_assertion_within_encrypted_assertion(self, assertion_tag):
|
||||||
|
prefix_map = self.get_prefix_map([self.encrypted_assertion._to_element_tree().find(assertion_tag)])
|
||||||
|
|
||||||
|
tree = self._to_element_tree()
|
||||||
|
|
||||||
|
self.set_prefixes(tree.find(self.encrypted_assertion._to_element_tree().tag).find(assertion_tag), prefix_map)
|
||||||
|
|
||||||
|
return ElementTree.tostring(tree, encoding="UTF-8")
|
||||||
|
|
||||||
|
|
||||||
|
def set_prefixes(self, elem, prefix_map):
|
||||||
|
|
||||||
|
# check if this is a tree wrapper
|
||||||
|
if not ElementTree.iselement(elem):
|
||||||
|
elem = elem.getroot()
|
||||||
|
|
||||||
|
# build uri map and add to root element
|
||||||
|
uri_map = {}
|
||||||
|
for prefix, uri in prefix_map.items():
|
||||||
|
uri_map[uri] = prefix
|
||||||
|
elem.set("xmlns:" + prefix, uri)
|
||||||
|
|
||||||
|
# fixup all elements in the tree
|
||||||
|
memo = {}
|
||||||
|
for elem in elem.getiterator():
|
||||||
|
self.fixup_element_prefixes(elem, uri_map, memo)
|
||||||
|
|
||||||
|
|
||||||
|
def fixup_element_prefixes(self, elem, uri_map, memo):
|
||||||
|
def fixup(name):
|
||||||
|
try:
|
||||||
|
return memo[name]
|
||||||
|
except KeyError:
|
||||||
|
if name[0] != "{":
|
||||||
|
return
|
||||||
|
uri, tag = name[1:].split("}")
|
||||||
|
if uri in uri_map:
|
||||||
|
new_name = uri_map[uri] + ":" + tag
|
||||||
|
memo[name] = new_name
|
||||||
|
return new_name
|
||||||
|
# fix element name
|
||||||
|
name = fixup(elem.tag)
|
||||||
|
if name:
|
||||||
|
elem.tag = name
|
||||||
|
# fix attribute names
|
||||||
|
for key, value in elem.items():
|
||||||
|
name = fixup(key)
|
||||||
|
if name:
|
||||||
|
elem.set(name, value)
|
||||||
|
del elem.attrib[key]
|
||||||
|
|
||||||
|
def to_string_force_namespace(self, nspair):
|
||||||
|
|
||||||
|
elem = self._to_element_tree()
|
||||||
|
|
||||||
|
self.set_prefixes(elem, nspair)
|
||||||
|
|
||||||
|
return ElementTree.tostring(elem, encoding="UTF-8")
|
||||||
|
|
||||||
def to_string(self, nspair=None):
|
def to_string(self, nspair=None):
|
||||||
"""Converts the Saml object to a string containing XML.
|
"""Converts the Saml object to a string containing XML.
|
||||||
|
|
||||||
|
|||||||
@@ -666,7 +666,7 @@ class Assertion(dict):
|
|||||||
name_id, attrconvs, policy, issuer, authn_class=None,
|
name_id, attrconvs, policy, issuer, authn_class=None,
|
||||||
authn_auth=None, authn_decl=None, encrypt=None,
|
authn_auth=None, authn_decl=None, encrypt=None,
|
||||||
sec_context=None, authn_decl_ref=None, authn_instant="",
|
sec_context=None, authn_decl_ref=None, authn_instant="",
|
||||||
subject_locality="", authn_statem=None):
|
subject_locality="", authn_statem=None, add_subject=True):
|
||||||
""" Construct the Assertion
|
""" Construct the Assertion
|
||||||
|
|
||||||
:param sp_entity_id: The entityid of the SP
|
:param sp_entity_id: The entityid of the SP
|
||||||
@@ -722,22 +722,29 @@ class Assertion(dict):
|
|||||||
else:
|
else:
|
||||||
_authn_statement = None
|
_authn_statement = None
|
||||||
|
|
||||||
_ass = assertion_factory(
|
if not add_subject:
|
||||||
issuer=issuer,
|
_ass = assertion_factory(
|
||||||
conditions=conds,
|
issuer=issuer,
|
||||||
subject=factory(
|
conditions=conds,
|
||||||
saml.Subject,
|
subject=None
|
||||||
name_id=name_id,
|
)
|
||||||
subject_confirmation=[factory(
|
else:
|
||||||
saml.SubjectConfirmation,
|
_ass = assertion_factory(
|
||||||
method=saml.SCM_BEARER,
|
issuer=issuer,
|
||||||
subject_confirmation_data=factory(
|
conditions=conds,
|
||||||
saml.SubjectConfirmationData,
|
subject=factory(
|
||||||
in_response_to=in_response_to,
|
saml.Subject,
|
||||||
recipient=consumer_url,
|
name_id=name_id,
|
||||||
not_on_or_after=policy.not_on_or_after(sp_entity_id)))]
|
subject_confirmation=[factory(
|
||||||
),
|
saml.SubjectConfirmation,
|
||||||
)
|
method=saml.SCM_BEARER,
|
||||||
|
subject_confirmation_data=factory(
|
||||||
|
saml.SubjectConfirmationData,
|
||||||
|
in_response_to=in_response_to,
|
||||||
|
recipient=consumer_url,
|
||||||
|
not_on_or_after=policy.not_on_or_after(sp_entity_id)))]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
if _authn_statement:
|
if _authn_statement:
|
||||||
_ass.authn_statement = [_authn_statement]
|
_ass.authn_statement = [_authn_statement]
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ from saml2.soap import make_soap_enveloped_saml_thingy
|
|||||||
|
|
||||||
from urlparse import parse_qs
|
from urlparse import parse_qs
|
||||||
|
|
||||||
from saml2.s_utils import signature, UnravelError
|
from saml2.s_utils import signature, UnravelError, exception_trace
|
||||||
from saml2.s_utils import do_attributes
|
from saml2.s_utils import do_attributes
|
||||||
|
|
||||||
from saml2 import samlp, BINDING_SOAP, SAMLError
|
from saml2 import samlp, BINDING_SOAP, SAMLError
|
||||||
@@ -537,7 +537,7 @@ class Base(Entity):
|
|||||||
# ======== response handling ===========
|
# ======== response handling ===========
|
||||||
|
|
||||||
def parse_authn_request_response(self, xmlstr, binding, outstanding=None,
|
def parse_authn_request_response(self, xmlstr, binding, outstanding=None,
|
||||||
outstanding_certs=None):
|
outstanding_certs=None, decrypt=True):
|
||||||
""" Deal with an AuthnResponse
|
""" Deal with an AuthnResponse
|
||||||
|
|
||||||
:param xmlstr: The reply as a xml string
|
:param xmlstr: The reply as a xml string
|
||||||
@@ -545,6 +545,8 @@ class Base(Entity):
|
|||||||
:param outstanding: A dictionary with session IDs as keys and
|
:param outstanding: A dictionary with session IDs as keys and
|
||||||
the original web request from the user before redirection
|
the original web request from the user before redirection
|
||||||
as values.
|
as values.
|
||||||
|
:param only_identity_in_encrypted_assertion: Must exist an assertion that is not encrypted that contains all
|
||||||
|
other information like subject and authentication statement.
|
||||||
:return: An response.AuthnResponse or None
|
:return: An response.AuthnResponse or None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -566,6 +568,7 @@ class Base(Entity):
|
|||||||
"attribute_converters": self.config.attribute_converters,
|
"attribute_converters": self.config.attribute_converters,
|
||||||
"allow_unknown_attributes":
|
"allow_unknown_attributes":
|
||||||
self.config.allow_unknown_attributes,
|
self.config.allow_unknown_attributes,
|
||||||
|
"decrypt": decrypt
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
resp = self._parse_response(xmlstr, AuthnResponse,
|
resp = self._parse_response(xmlstr, AuthnResponse,
|
||||||
@@ -580,13 +583,12 @@ class Base(Entity):
|
|||||||
logger.error("XML parse error: %s" % err)
|
logger.error("XML parse error: %s" % err)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
#logger.debug(">> %s", resp)
|
|
||||||
|
|
||||||
if resp is None:
|
if resp is None:
|
||||||
return None
|
return None
|
||||||
elif isinstance(resp, AuthnResponse):
|
elif isinstance(resp, AuthnResponse):
|
||||||
self.users.add_information_about_person(resp.session_info())
|
if resp.assertion is not None and len(resp.response.encrypted_assertion) == 0:
|
||||||
logger.info("--- ADDED person info ----")
|
self.users.add_information_about_person(resp.session_info())
|
||||||
|
logger.info("--- ADDED person info ----")
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
logger.error("Response type not supported: %s" % (
|
logger.error("Response type not supported: %s" % (
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ from saml2 import soap
|
|||||||
from saml2 import element_to_extension_element
|
from saml2 import element_to_extension_element
|
||||||
from saml2 import extension_elements_to_elements
|
from saml2 import extension_elements_to_elements
|
||||||
|
|
||||||
from saml2.saml import NameID
|
from saml2.saml import NameID, EncryptedAssertion
|
||||||
from saml2.saml import Issuer
|
from saml2.saml import Issuer
|
||||||
from saml2.saml import NAMEID_FORMAT_ENTITY
|
from saml2.saml import NAMEID_FORMAT_ENTITY
|
||||||
from saml2.response import LogoutResponse
|
from saml2.response import LogoutResponse
|
||||||
@@ -63,6 +63,7 @@ from saml2.sigver import CryptoBackendXmlSec1
|
|||||||
from saml2.sigver import make_temp
|
from saml2.sigver import make_temp
|
||||||
from saml2.sigver import pre_encryption_part
|
from saml2.sigver import pre_encryption_part
|
||||||
from saml2.sigver import pre_signature_part
|
from saml2.sigver import pre_signature_part
|
||||||
|
from saml2.sigver import pre_encrypt_assertion
|
||||||
from saml2.sigver import signed_instance_factory
|
from saml2.sigver import signed_instance_factory
|
||||||
from saml2.virtual_org import VirtualOrg
|
from saml2.virtual_org import VirtualOrg
|
||||||
|
|
||||||
@@ -502,8 +503,16 @@ class Entity(HTTPBase):
|
|||||||
|
|
||||||
def _response(self, in_response_to, consumer_url=None, status=None,
|
def _response(self, in_response_to, consumer_url=None, status=None,
|
||||||
issuer=None, sign=False, to_sign=None,
|
issuer=None, sign=False, to_sign=None,
|
||||||
encrypt_assertion=False, encrypt_cert=None, **kwargs):
|
encrypt_assertion=False, encrypt_assertion_self_contained=False, encrypted_advice_attributes=False,
|
||||||
|
encrypt_cert=None, **kwargs):
|
||||||
""" Create a Response.
|
""" Create a Response.
|
||||||
|
Encryption:
|
||||||
|
encrypt_assertion must be true for encryption to be performed. If encrypted_advice_attributes also is
|
||||||
|
true, then will the function try to encrypt the assertion in the the advice element of the main
|
||||||
|
assertion. Only one assertion element is allowed in the advice element, if multiple assertions exists
|
||||||
|
in the advice element the main assertion will be encrypted instead, since it's no point to encrypt
|
||||||
|
If encrypted_advice_attributes is
|
||||||
|
false the main assertion will be encrypted. Since the same key
|
||||||
|
|
||||||
:param in_response_to: The session identifier of the request
|
:param in_response_to: The session identifier of the request
|
||||||
:param consumer_url: The URL which should receive the response
|
:param consumer_url: The URL which should receive the response
|
||||||
@@ -533,22 +542,44 @@ class Entity(HTTPBase):
|
|||||||
return signed_instance_factory(response, self.sec, to_sign)
|
return signed_instance_factory(response, self.sec, to_sign)
|
||||||
|
|
||||||
if encrypt_assertion:
|
if encrypt_assertion:
|
||||||
|
node_xpath = None
|
||||||
if sign:
|
if sign:
|
||||||
response.signature = pre_signature_part(response.id,
|
response.signature = pre_signature_part(response.id,
|
||||||
self.sec.my_cert, 1)
|
self.sec.my_cert, 1)
|
||||||
|
sign_class = [(class_name(response), response.id)]
|
||||||
cbxs = CryptoBackendXmlSec1(self.config.xmlsec_binary)
|
cbxs = CryptoBackendXmlSec1(self.config.xmlsec_binary)
|
||||||
|
if encrypted_advice_attributes and response.assertion.advice is not None \
|
||||||
|
and len(response.assertion.advice.assertion) == 1:
|
||||||
|
tmp_assertion = response.assertion.advice.assertion[0]
|
||||||
|
response.assertion.advice.encrypted_assertion = []
|
||||||
|
response.assertion.advice.encrypted_assertion.append(EncryptedAssertion())
|
||||||
|
if isinstance(tmp_assertion, list):
|
||||||
|
response.assertion.advice.encrypted_assertion[0].add_extension_elements(tmp_assertion)
|
||||||
|
else:
|
||||||
|
response.assertion.advice.encrypted_assertion[0].add_extension_element(tmp_assertion)
|
||||||
|
response.assertion.advice.assertion = []
|
||||||
|
if encrypt_assertion_self_contained:
|
||||||
|
advice_tag = response.assertion.advice._to_element_tree().tag
|
||||||
|
assertion_tag = tmp_assertion._to_element_tree().tag
|
||||||
|
response = response.get_xml_string_with_self_contained_assertion_within_advice_encrypted_assertion(
|
||||||
|
assertion_tag, advice_tag)
|
||||||
|
node_xpath = ''.join(["/*[local-name()=\"%s\"]" % v for v in
|
||||||
|
["Response", "Assertion", "Advice", "EncryptedAssertion", "Assertion"]])
|
||||||
|
elif encrypt_assertion_self_contained:
|
||||||
|
assertion_tag = response.assertion._to_element_tree().tag
|
||||||
|
response = pre_encrypt_assertion(response)
|
||||||
|
response = response.get_xml_string_with_self_contained_assertion_within_encrypted_assertion(
|
||||||
|
assertion_tag)
|
||||||
|
else:
|
||||||
|
response = pre_encrypt_assertion(response)
|
||||||
|
if to_sign:
|
||||||
|
response = signed_instance_factory(response, self.sec, to_sign)
|
||||||
_, cert_file = make_temp("%s" % encrypt_cert, decode=False)
|
_, cert_file = make_temp("%s" % encrypt_cert, decode=False)
|
||||||
response = cbxs.encrypt_assertion(response, cert_file,
|
response = cbxs.encrypt_assertion(response, cert_file,
|
||||||
pre_encryption_part())
|
pre_encryption_part(), node_xpath=node_xpath)
|
||||||
# template(response.assertion.id))
|
# template(response.assertion.id))
|
||||||
if sign:
|
if sign:
|
||||||
if to_sign:
|
return signed_instance_factory(response, self.sec, sign_class)
|
||||||
signed_instance_factory(response, self.sec, to_sign)
|
|
||||||
else:
|
|
||||||
# default is to sign the whole response if anything
|
|
||||||
sign_class = [(class_name(response), response.id)]
|
|
||||||
return signed_instance_factory(response, self.sec,
|
|
||||||
sign_class)
|
|
||||||
else:
|
else:
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@@ -946,7 +977,13 @@ class Entity(HTTPBase):
|
|||||||
decode=False)
|
decode=False)
|
||||||
else:
|
else:
|
||||||
key_file = ""
|
key_file = ""
|
||||||
response = response.verify(key_file)
|
only_identity_in_encrypted_assertion = False
|
||||||
|
if "only_identity_in_encrypted_assertion" in kwargs:
|
||||||
|
only_identity_in_encrypted_assertion = kwargs["only_identity_in_encrypted_assertion"]
|
||||||
|
decrypt = True
|
||||||
|
if "decrypt" in kwargs:
|
||||||
|
decrypt = kwargs["decrypt"]
|
||||||
|
response = response.verify(key_file, decrypt=decrypt)
|
||||||
|
|
||||||
if not response:
|
if not response:
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ __author__ = 'rhoerbe' #2013-09-05
|
|||||||
|
|
||||||
|
|
||||||
EGOVTOKEN = ["PVP-VERSION",
|
EGOVTOKEN = ["PVP-VERSION",
|
||||||
"PVP-PRINCIPALNAME",
|
"PVP-PRINCIPAL-NAME",
|
||||||
"PVP-GIVENNAME",
|
"PVP-GIVENNAME",
|
||||||
"PVP-BIRTHDATE",
|
"PVP-BIRTHDATE",
|
||||||
"PVP-USERID",
|
"PVP-USERID",
|
||||||
|
|||||||
@@ -395,7 +395,7 @@ class StatusResponse(object):
|
|||||||
def loads(self, xmldata, decode=True, origxml=None):
|
def loads(self, xmldata, decode=True, origxml=None):
|
||||||
return self._loads(xmldata, decode, origxml)
|
return self._loads(xmldata, decode, origxml)
|
||||||
|
|
||||||
def verify(self, key_file=""):
|
def verify(self, key_file="", decrypt=True):
|
||||||
try:
|
try:
|
||||||
return self._verify()
|
return self._verify()
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
@@ -622,24 +622,32 @@ class AuthnResponse(StatusResponse):
|
|||||||
attrlist = enc_attr.extensions_as_elements("Attribute", saml)
|
attrlist = enc_attr.extensions_as_elements("Attribute", saml)
|
||||||
attribute_statement.attribute.extend(attrlist)
|
attribute_statement.attribute.extend(attrlist)
|
||||||
|
|
||||||
|
def read_attribute_statement(self, attr_statem):
|
||||||
|
logger.debug("Attribute Statement: %s" % (attr_statem,))
|
||||||
|
for aconv in self.attribute_converters:
|
||||||
|
logger.debug("Converts name format: %s" % (aconv.name_format,))
|
||||||
|
|
||||||
|
self.decrypt_attributes(attr_statem)
|
||||||
|
return to_local(self.attribute_converters, attr_statem,
|
||||||
|
self.allow_unknown_attributes)
|
||||||
|
|
||||||
def get_identity(self):
|
def get_identity(self):
|
||||||
""" The assertion can contain zero or one attributeStatements
|
""" The assertion can contain zero or one attributeStatements
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not self.assertion.attribute_statement:
|
ava = {}
|
||||||
logger.error("Missing Attribute Statement")
|
if self.assertion.advice:
|
||||||
ava = {}
|
if self.assertion.advice.assertion:
|
||||||
else:
|
for tmp_assertion in self.assertion.advice.assertion:
|
||||||
|
if tmp_assertion.attribute_statement:
|
||||||
|
assert len(tmp_assertion.attribute_statement) == 1
|
||||||
|
ava.update(self.read_attribute_statement(tmp_assertion.attribute_statement[0]))
|
||||||
|
if self.assertion.attribute_statement:
|
||||||
assert len(self.assertion.attribute_statement) == 1
|
assert len(self.assertion.attribute_statement) == 1
|
||||||
_attr_statem = self.assertion.attribute_statement[0]
|
_attr_statem = self.assertion.attribute_statement[0]
|
||||||
|
ava.update(self.read_attribute_statement(_attr_statem))
|
||||||
logger.debug("Attribute Statement: %s" % (_attr_statem,))
|
if not ava:
|
||||||
for aconv in self.attribute_converters:
|
logger.error("Missing Attribute Statement")
|
||||||
logger.debug("Converts name format: %s" % (aconv.name_format,))
|
|
||||||
|
|
||||||
self.decrypt_attributes(_attr_statem)
|
|
||||||
ava = to_local(self.attribute_converters, _attr_statem,
|
|
||||||
self.allow_unknown_attributes)
|
|
||||||
return ava
|
return ava
|
||||||
|
|
||||||
def _bearer_confirmed(self, data):
|
def _bearer_confirmed(self, data):
|
||||||
@@ -749,11 +757,9 @@ class AuthnResponse(StatusResponse):
|
|||||||
raise SignatureError("Signature missing for assertion")
|
raise SignatureError("Signature missing for assertion")
|
||||||
else:
|
else:
|
||||||
logger.debug("signed")
|
logger.debug("signed")
|
||||||
|
|
||||||
if not verified:
|
if not verified:
|
||||||
try:
|
try:
|
||||||
self.sec.check_signature(assertion, class_name(assertion),
|
self.sec.check_signature(assertion, class_name(assertion),self.xmlstr)
|
||||||
self.xmlstr)
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error("correctly_signed_response: %s" % exc)
|
logger.error("correctly_signed_response: %s" % exc)
|
||||||
raise
|
raise
|
||||||
@@ -808,7 +814,7 @@ class AuthnResponse(StatusResponse):
|
|||||||
res.append(assertion)
|
res.append(assertion)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def parse_assertion(self, key_file=""):
|
def parse_assertion(self, key_file="", decrypt=True):
|
||||||
if self.context == "AuthnQuery":
|
if self.context == "AuthnQuery":
|
||||||
# can contain one or more assertions
|
# can contain one or more assertions
|
||||||
pass
|
pass
|
||||||
@@ -820,17 +826,35 @@ class AuthnResponse(StatusResponse):
|
|||||||
raise Exception("No assertion part")
|
raise Exception("No assertion part")
|
||||||
|
|
||||||
res = []
|
res = []
|
||||||
if self.response.encrypted_assertion:
|
has_encrypted_assertions = self.response.encrypted_assertion
|
||||||
|
if not has_encrypted_assertions and self.response.assertion:
|
||||||
|
for tmp_assertion in self.response.assertion:
|
||||||
|
if tmp_assertion.advice:
|
||||||
|
if tmp_assertion.advice.encrypted_assertion:
|
||||||
|
has_encrypted_assertions = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if has_encrypted_assertions and decrypt:
|
||||||
logger.debug("***Encrypted assertion/-s***")
|
logger.debug("***Encrypted assertion/-s***")
|
||||||
decr_text = self.sec.decrypt(self.xmlstr, key_file)
|
decr_text = self.sec.decrypt(self.xmlstr, key_file)
|
||||||
resp = samlp.response_from_string(decr_text)
|
resp = samlp.response_from_string(decr_text)
|
||||||
res = self.decrypt_assertions(resp.encrypted_assertion, decr_text)
|
res = self.decrypt_assertions(resp.encrypted_assertion, decr_text)
|
||||||
|
if resp.assertion:
|
||||||
|
for tmp_ass in resp.assertion:
|
||||||
|
if tmp_ass.advice and tmp_ass.advice.encrypted_assertion:
|
||||||
|
advice_res = self.decrypt_assertions(tmp_ass.advice.encrypted_assertion, decr_text)
|
||||||
|
if tmp_ass.advice.assertion:
|
||||||
|
tmp_ass.advice.assertion.extend(advice_res)
|
||||||
|
else:
|
||||||
|
tmp_ass.advice.assertion = advice_res
|
||||||
|
tmp_ass.advice.encrypted_assertion = []
|
||||||
|
self.response.assertion = resp.assertion
|
||||||
if self.response.assertion:
|
if self.response.assertion:
|
||||||
self.response.assertion.extend(res)
|
self.response.assertion.extend(res)
|
||||||
else:
|
else:
|
||||||
self.response.assertion = res
|
self.response.assertion = res
|
||||||
self.response.encrypted_assertion = []
|
|
||||||
self.xmlstr = decr_text
|
self.xmlstr = decr_text
|
||||||
|
self.response.encrypted_assertion = []
|
||||||
|
|
||||||
if self.response.assertion:
|
if self.response.assertion:
|
||||||
logger.debug("***Unencrypted assertion***")
|
logger.debug("***Unencrypted assertion***")
|
||||||
@@ -840,10 +864,9 @@ class AuthnResponse(StatusResponse):
|
|||||||
else:
|
else:
|
||||||
self.assertions.append(assertion)
|
self.assertions.append(assertion)
|
||||||
self.assertion = self.assertions[0]
|
self.assertion = self.assertions[0]
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def verify(self, key_file=""):
|
def verify(self, key_file="", decrypt=True):
|
||||||
""" Verify that the assertion is syntactically correct and
|
""" Verify that the assertion is syntactically correct and
|
||||||
the signature is correct if present.
|
the signature is correct if present.
|
||||||
:param key_file: If not the default key file should be used this is it.
|
:param key_file: If not the default key file should be used this is it.
|
||||||
@@ -861,7 +884,7 @@ class AuthnResponse(StatusResponse):
|
|||||||
if not isinstance(self.response, samlp.Response):
|
if not isinstance(self.response, samlp.Response):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
if self.parse_assertion(key_file):
|
if self.parse_assertion(key_file, decrypt=decrypt):
|
||||||
return self
|
return self
|
||||||
else:
|
else:
|
||||||
logger.error("Could not parse the assertion")
|
logger.error("Could not parse the assertion")
|
||||||
@@ -1089,7 +1112,7 @@ class AssertionIDResponse(object):
|
|||||||
|
|
||||||
return self._postamble()
|
return self._postamble()
|
||||||
|
|
||||||
def verify(self, key_file=""):
|
def verify(self, key_file="", decrypt=True):
|
||||||
try:
|
try:
|
||||||
valid_instance(self.response)
|
valid_instance(self.response)
|
||||||
except NotValid as exc:
|
except NotValid as exc:
|
||||||
|
|||||||
@@ -280,12 +280,50 @@ class Server(Entity):
|
|||||||
|
|
||||||
# ------------------------------------------------------------------------
|
# ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def setup_assertion(self, authn, sp_entity_id, in_response_to, consumer_url, name_id, policy, _issuer,
|
||||||
|
authn_statement, identity, best_effort, sign_response, add_subject=True):
|
||||||
|
ast = Assertion(identity)
|
||||||
|
ast.acs = self.config.getattr("attribute_converters", "idp")
|
||||||
|
if policy is None:
|
||||||
|
policy = Policy()
|
||||||
|
try:
|
||||||
|
ast.apply_policy(sp_entity_id, policy, self.metadata)
|
||||||
|
except MissingValue, exc:
|
||||||
|
if not best_effort:
|
||||||
|
return self.create_error_response(in_response_to, consumer_url,
|
||||||
|
exc, sign_response)
|
||||||
|
|
||||||
|
if authn: # expected to be a dictionary
|
||||||
|
# Would like to use dict comprehension but ...
|
||||||
|
authn_args = dict([
|
||||||
|
(AUTHN_DICT_MAP[k], v) for k, v in authn.items()
|
||||||
|
if k in AUTHN_DICT_MAP])
|
||||||
|
|
||||||
|
assertion = ast.construct(sp_entity_id, in_response_to,
|
||||||
|
consumer_url, name_id,
|
||||||
|
self.config.attribute_converters,
|
||||||
|
policy, issuer=_issuer, add_subject=add_subject,
|
||||||
|
**authn_args)
|
||||||
|
elif authn_statement: # Got a complete AuthnStatement
|
||||||
|
assertion = ast.construct(sp_entity_id, in_response_to,
|
||||||
|
consumer_url, name_id,
|
||||||
|
self.config.attribute_converters,
|
||||||
|
policy, issuer=_issuer,
|
||||||
|
authn_statem=authn_statement, add_subject=add_subject)
|
||||||
|
else:
|
||||||
|
assertion = ast.construct(sp_entity_id, in_response_to,
|
||||||
|
consumer_url, name_id,
|
||||||
|
self.config.attribute_converters,
|
||||||
|
policy, issuer=_issuer, add_subject=add_subject)
|
||||||
|
return assertion
|
||||||
|
|
||||||
def _authn_response(self, in_response_to, consumer_url,
|
def _authn_response(self, in_response_to, consumer_url,
|
||||||
sp_entity_id, identity=None, name_id=None,
|
sp_entity_id, identity=None, name_id=None,
|
||||||
status=None, authn=None, issuer=None, policy=None,
|
status=None, authn=None, issuer=None, policy=None,
|
||||||
sign_assertion=False, sign_response=False,
|
sign_assertion=False, sign_response=False,
|
||||||
best_effort=False, encrypt_assertion=False,
|
best_effort=False, encrypt_assertion=False,
|
||||||
encrypt_cert=None, authn_statement=None):
|
encrypt_cert=None, authn_statement=None,
|
||||||
|
encrypt_assertion_self_contained=False, encrypted_advice_attributes=False):
|
||||||
""" Create a response. A layer of indirection.
|
""" Create a response. A layer of indirection.
|
||||||
|
|
||||||
:param in_response_to: The session identifier of the request
|
:param in_response_to: The session identifier of the request
|
||||||
@@ -309,45 +347,43 @@ class Server(Entity):
|
|||||||
args = {}
|
args = {}
|
||||||
#if identity:
|
#if identity:
|
||||||
_issuer = self._issuer(issuer)
|
_issuer = self._issuer(issuer)
|
||||||
ast = Assertion(identity)
|
|
||||||
ast.acs = self.config.getattr("attribute_converters", "idp")
|
|
||||||
if policy is None:
|
|
||||||
policy = Policy()
|
|
||||||
try:
|
|
||||||
ast.apply_policy(sp_entity_id, policy, self.metadata)
|
|
||||||
except MissingValue, exc:
|
|
||||||
if not best_effort:
|
|
||||||
return self.create_error_response(in_response_to, consumer_url,
|
|
||||||
exc, sign_response)
|
|
||||||
|
|
||||||
if authn: # expected to be a dictionary
|
#if encrypt_assertion and show_nameid:
|
||||||
# Would like to use dict comprehension but ...
|
# tmp_name_id = name_id
|
||||||
authn_args = dict([
|
# name_id = None
|
||||||
(AUTHN_DICT_MAP[k], v) for k, v in authn.items()
|
# name_id = None
|
||||||
if k in AUTHN_DICT_MAP])
|
# tmp_authn = authn
|
||||||
|
# authn = None
|
||||||
|
# tmp_authn_statement = authn_statement
|
||||||
|
# authn_statement = None
|
||||||
|
|
||||||
assertion = ast.construct(sp_entity_id, in_response_to,
|
if encrypt_assertion and encrypted_advice_attributes:
|
||||||
consumer_url, name_id,
|
assertion_attributes = self.setup_assertion(None, sp_entity_id, None, None, None, policy,
|
||||||
self.config.attribute_converters,
|
None, None, identity, best_effort, sign_response, False)
|
||||||
policy, issuer=_issuer,
|
assertion = self.setup_assertion(authn, sp_entity_id, in_response_to, consumer_url,
|
||||||
**authn_args)
|
name_id, policy, _issuer, authn_statement, [], True,
|
||||||
elif authn_statement: # Got a complete AuthnStatement
|
sign_response)
|
||||||
assertion = ast.construct(sp_entity_id, in_response_to,
|
assertion.advice = saml.Advice()
|
||||||
consumer_url, name_id,
|
|
||||||
self.config.attribute_converters,
|
#assertion.advice.assertion_id_ref.append(saml.AssertionIDRef())
|
||||||
policy, issuer=_issuer,
|
#assertion.advice.assertion_uri_ref.append(saml.AssertionURIRef())
|
||||||
authn_statem=authn_statement)
|
assertion.advice.assertion.append(assertion_attributes)
|
||||||
else:
|
else:
|
||||||
assertion = ast.construct(sp_entity_id, in_response_to,
|
assertion = self.setup_assertion(authn, sp_entity_id, in_response_to, consumer_url,
|
||||||
consumer_url, name_id,
|
name_id, policy, _issuer, authn_statement, identity, True,
|
||||||
self.config.attribute_converters,
|
sign_response)
|
||||||
policy, issuer=_issuer)
|
|
||||||
|
|
||||||
|
to_sign = []
|
||||||
if sign_assertion is not None and sign_assertion:
|
if sign_assertion is not None and sign_assertion:
|
||||||
|
if assertion.advice and assertion.advice.assertion:
|
||||||
|
for tmp_assertion in assertion.advice.assertion:
|
||||||
|
tmp_assertion.signature = pre_signature_part(tmp_assertion.id, self.sec.my_cert, 1)
|
||||||
|
to_sign.append((class_name(tmp_assertion), tmp_assertion.id))
|
||||||
assertion.signature = pre_signature_part(assertion.id,
|
assertion.signature = pre_signature_part(assertion.id,
|
||||||
self.sec.my_cert, 1)
|
self.sec.my_cert, 1)
|
||||||
# Just the assertion or the response and the assertion ?
|
# Just the assertion or the response and the assertion ?
|
||||||
to_sign = [(class_name(assertion), assertion.id)]
|
to_sign.append((class_name(assertion), assertion.id))
|
||||||
|
|
||||||
|
|
||||||
# Store which assertion that has been sent to which SP about which
|
# Store which assertion that has been sent to which SP about which
|
||||||
# subject.
|
# subject.
|
||||||
@@ -358,12 +394,14 @@ class Server(Entity):
|
|||||||
|
|
||||||
args["assertion"] = assertion
|
args["assertion"] = assertion
|
||||||
|
|
||||||
if self.support_AssertionIDRequest() or self.support_AuthnQuery():
|
if (self.support_AssertionIDRequest() or self.support_AuthnQuery()):
|
||||||
self.session_db.store_assertion(assertion, to_sign)
|
self.session_db.store_assertion(assertion, to_sign)
|
||||||
|
|
||||||
return self._response(in_response_to, consumer_url, status, issuer,
|
return self._response(in_response_to, consumer_url, status, issuer,
|
||||||
sign_response, to_sign, encrypt_assertion=encrypt_assertion,
|
sign_response, to_sign, encrypt_assertion=encrypt_assertion,
|
||||||
encrypt_cert=encrypt_cert, **args)
|
encrypt_cert=encrypt_cert,
|
||||||
|
encrypt_assertion_self_contained=encrypt_assertion_self_contained,
|
||||||
|
encrypted_advice_attributes=encrypted_advice_attributes, **args)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
# ------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -438,6 +476,8 @@ class Server(Entity):
|
|||||||
name_id=None, authn=None, issuer=None,
|
name_id=None, authn=None, issuer=None,
|
||||||
sign_response=None, sign_assertion=None,
|
sign_response=None, sign_assertion=None,
|
||||||
encrypt_cert=None, encrypt_assertion=None,
|
encrypt_cert=None, encrypt_assertion=None,
|
||||||
|
encrypt_assertion_self_contained=False,
|
||||||
|
encrypted_advice_attributes=False,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
""" Constructs an AuthenticationResponse
|
""" Constructs an AuthenticationResponse
|
||||||
|
|
||||||
@@ -549,6 +589,8 @@ class Server(Entity):
|
|||||||
sign_response=sign_response,
|
sign_response=sign_response,
|
||||||
best_effort=best_effort,
|
best_effort=best_effort,
|
||||||
encrypt_assertion=encrypt_assertion,
|
encrypt_assertion=encrypt_assertion,
|
||||||
|
encrypt_assertion_self_contained=encrypt_assertion_self_contained,
|
||||||
|
encrypted_advice_attributes=encrypted_advice_attributes,
|
||||||
encrypt_cert=encrypt_cert)
|
encrypt_cert=encrypt_cert)
|
||||||
return self._authn_response(in_response_to, # in_response_to
|
return self._authn_response(in_response_to, # in_response_to
|
||||||
destination, # consumer_url
|
destination, # consumer_url
|
||||||
@@ -562,6 +604,8 @@ class Server(Entity):
|
|||||||
sign_response=sign_response,
|
sign_response=sign_response,
|
||||||
best_effort=best_effort,
|
best_effort=best_effort,
|
||||||
encrypt_assertion=encrypt_assertion,
|
encrypt_assertion=encrypt_assertion,
|
||||||
|
encrypt_assertion_self_contained=encrypt_assertion_self_contained,
|
||||||
|
encrypted_advice_attributes=encrypted_advice_attributes,
|
||||||
encrypt_cert=encrypt_cert)
|
encrypt_cert=encrypt_cert)
|
||||||
|
|
||||||
except MissingValue, exc:
|
except MissingValue, exc:
|
||||||
|
|||||||
15
tests/root_cert/localhost.ca.crt
Normal file
15
tests/root_cert/localhost.ca.crt
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICSTCCAbICAQEwDQYJKoZIhvcNAQELBQAwbTELMAkGA1UEBhMCc2UxCzAJBgNV
|
||||||
|
BAgTAmFjMQ0wCwYDVQQHEwR1bWVhMRwwGgYDVQQKExNJVFMgVW1lYSBVbml2ZXJz
|
||||||
|
aXR5MQ0wCwYDVQQLEwRESVJHMRUwEwYDVQQDEwxsb2NhbGhvc3QuY2EwHhcNMTQw
|
||||||
|
MzE0MDczNDIwWhcNMjQwMzExMDczNDIwWjBtMQswCQYDVQQGEwJzZTELMAkGA1UE
|
||||||
|
CBMCYWMxDTALBgNVBAcTBHVtZWExHDAaBgNVBAoTE0lUUyBVbWVhIFVuaXZlcnNp
|
||||||
|
dHkxDTALBgNVBAsTBERJUkcxFTATBgNVBAMTDGxvY2FsaG9zdC5jYTCBnzANBgkq
|
||||||
|
hkiG9w0BAQEFAAOBjQAwgYkCgYEAzJseOYg+hKGsnGWilv9FrfH2csQ9UZGwgwHz
|
||||||
|
zR2IquQg/+nONxd4MIwjOnQrLOJuhpu55ZTSpeT901GNDLj4xPQnFrWWyET8NxZg
|
||||||
|
w7Ilra55iQNaoUWpdi0JQSXI/9CY8t+Y170+7DfBJ6zo4y6+HKaOLNxZy4IbB0SE
|
||||||
|
aZBGsrUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQAEOQJkD+5fb2mTtxwaZeRyQN9c
|
||||||
|
If0Xd2E7Z6BstGCUMWa/q4FrNee324kINVsFYg0GBTBwfyYPwR7I70LGjS3gXEzd
|
||||||
|
RjGx/Z8yvHJyavJj8iRCOflQ0fUlwasFYtrJesPOfM+aJ05Jb8GelUxpKh6BtPlE
|
||||||
|
IU0FLm//i9ucLQ9zBg==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
15
tests/root_cert/localhost.ca.key
Normal file
15
tests/root_cert/localhost.ca.key
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIICXQIBAAKBgQDMmx45iD6EoaycZaKW/0Wt8fZyxD1RkbCDAfPNHYiq5CD/6c43
|
||||||
|
F3gwjCM6dCss4m6Gm7nllNKl5P3TUY0MuPjE9CcWtZbIRPw3FmDDsiWtrnmJA1qh
|
||||||
|
Ral2LQlBJcj/0Jjy35jXvT7sN8EnrOjjLr4cpo4s3FnLghsHRIRpkEaytQIDAQAB
|
||||||
|
AoGAMw6aUjz/bNVzX2u1UPzOhIOWvjjeHFbAt1BraEnwasSWv4W2oeTHZ0XxHIsU
|
||||||
|
oxS2A/0kPHgQwLkN5ge5rO0TlpAI5X9ZqlJ0SXF5zjJOBtyK6TWoUbwnyzS7lbFC
|
||||||
|
q9AVrHwMX9uNCboccqzjrzHyNE+4/QT7z2G5AMzjfq+5EwECQQDvOJuUl2pbUUIK
|
||||||
|
nMCmwkARFEZzZYV2oIBDsagTG8gX7glj5stoYXuez8EnYtNHRDBConyKqruuzqJk
|
||||||
|
qSKlha7hAkEA2vT4CpAzHSCknwQKXmFwBD5hVBrv+JZSur6XpqEdwXkX2osScAaW
|
||||||
|
xj3vQEQorJC2CryvUVOTeuFoog0f+6HiVQJBAMs0dMQ2ErxbPBQzr1p4K1/Wrzmb
|
||||||
|
BVINaKcYJEOHF+Nr6kIYbLTQCeiPZe4E/p/NBomz6MMJ4L/O+xcyrSGZe0ECQQCZ
|
||||||
|
ejELpnxNpH4AAKML+Ry9vMQYYjFnfGdNAx/l6vWikjEIPYeVAulY2D0GPUCNhXo1
|
||||||
|
GIGDbiPodGwVe0G57oVpAkBjckA1LEE1Kzkq5sR9U9t9m+3WSBvMZxLUwJYdVmOY
|
||||||
|
Y6xKVtJfe9XuQt9tzoWQW8iieWyKOw7yLYCBcjns2iZn
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
@@ -55,6 +55,10 @@ NAME_ID_POLICY_2 = """<?xml version="1.0" encoding="utf-8"?>
|
|||||||
|
|
||||||
class TestIdentifier():
|
class TestIdentifier():
|
||||||
def setup_class(self):
|
def setup_class(self):
|
||||||
|
try:
|
||||||
|
os.remove("subject.db.db")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
self.id = IdentDB("subject.db", "example.com", "example")
|
self.id = IdentDB("subject.db", "example.com", "example")
|
||||||
|
|
||||||
def test_persistent_1(self):
|
def test_persistent_1(self):
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import base64
|
import base64
|
||||||
|
import os
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
from urlparse import parse_qs
|
from urlparse import parse_qs
|
||||||
from saml2.sigver import pre_encryption_part
|
import uuid
|
||||||
|
|
||||||
|
from saml2.cert import OpenSSLWrapper
|
||||||
|
from saml2.sigver import pre_encryption_part, make_temp
|
||||||
from saml2.assertion import Policy
|
from saml2.assertion import Policy
|
||||||
from saml2.authn_context import INTERNETPROTOCOLPASSWORD
|
from saml2.authn_context import INTERNETPROTOCOLPASSWORD
|
||||||
from saml2.saml import NameID, NAMEID_FORMAT_TRANSIENT
|
from saml2.saml import NameID, NAMEID_FORMAT_TRANSIENT
|
||||||
@@ -41,6 +45,40 @@ def _eq(l1, l2):
|
|||||||
return set(l1) == set(l2)
|
return set(l1) == set(l2)
|
||||||
|
|
||||||
|
|
||||||
|
BASEDIR = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
def get_ava(assertion):
|
||||||
|
ava = {}
|
||||||
|
for statement in assertion.attribute_statement:
|
||||||
|
for attr in statement.attribute:
|
||||||
|
value = []
|
||||||
|
for tmp_val in attr.attribute_value:
|
||||||
|
value.append(tmp_val.text)
|
||||||
|
key = attr.friendly_name
|
||||||
|
if key is None or len(key) == 0:
|
||||||
|
key = attr.text
|
||||||
|
ava[key] = value
|
||||||
|
return ava
|
||||||
|
|
||||||
|
|
||||||
|
def generate_cert():
|
||||||
|
sn = uuid.uuid4().urn
|
||||||
|
cert_info = {
|
||||||
|
"cn": "localhost",
|
||||||
|
"country_code": "se",
|
||||||
|
"state": "ac",
|
||||||
|
"city": "Umea",
|
||||||
|
"organization": "ITS",
|
||||||
|
"organization_unit": "DIRG"
|
||||||
|
}
|
||||||
|
osw = OpenSSLWrapper()
|
||||||
|
ca_cert_str = osw.read_str_from_file("/Users/haho0032/Develop/root_cert/localhost.ca.crt")
|
||||||
|
ca_key_str = osw.read_str_from_file("/Users/haho0032/Develop/root_cert/localhost.ca.key")
|
||||||
|
req_cert_str, req_key_str = osw.create_certificate(cert_info, request=True, sn=sn, key_length=2048)
|
||||||
|
cert_str = osw.create_cert_signed_certificate(ca_cert_str, ca_key_str, req_cert_str)
|
||||||
|
return cert_str, req_key_str
|
||||||
|
|
||||||
class TestServer1():
|
class TestServer1():
|
||||||
def setup_class(self):
|
def setup_class(self):
|
||||||
self.server = Server("idp_conf")
|
self.server = Server("idp_conf")
|
||||||
@@ -372,6 +410,354 @@ class TestServer1():
|
|||||||
# value. Just that there should be one
|
# value. Just that there should be one
|
||||||
assert assertion.signature.signature_value.text != ""
|
assert assertion.signature.signature_value.text != ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_encrypted_signed_response_1(self):
|
||||||
|
name_id = self.server.ident.transient_nameid(
|
||||||
|
"urn:mace:example.com:saml:roland:sp", "id12")
|
||||||
|
ava = {"givenName": ["Derek"], "surName": ["Jeter"],
|
||||||
|
"mail": ["derek@nyy.mlb.com"], "title": "The man"}
|
||||||
|
|
||||||
|
cert_str, cert_key_str = generate_cert()
|
||||||
|
|
||||||
|
signed_resp = self.server.create_authn_response(
|
||||||
|
ava,
|
||||||
|
"id12", # in_response_to
|
||||||
|
"http://lingon.catalogix.se:8087/", # consumer_url
|
||||||
|
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||||
|
name_id=name_id,
|
||||||
|
sign_response=True,
|
||||||
|
sign_assertion=True,
|
||||||
|
encrypt_assertion=True,
|
||||||
|
encrypt_assertion_self_contained=True,
|
||||||
|
encrypted_advice_attributes=True,
|
||||||
|
encrypt_cert=cert_str,
|
||||||
|
)
|
||||||
|
|
||||||
|
sresponse = response_from_string(signed_resp)
|
||||||
|
|
||||||
|
valid = self.server.sec.verify_signature(signed_resp,
|
||||||
|
self.server.config.cert_file,
|
||||||
|
node_name='urn:oasis:names:tc:SAML:2.0:protocol:Response',
|
||||||
|
node_id=sresponse.id,
|
||||||
|
id_attr="")
|
||||||
|
assert valid
|
||||||
|
|
||||||
|
_, key_file = make_temp("%s" % cert_key_str, decode=False)
|
||||||
|
|
||||||
|
decr_text = self.server.sec.decrypt(signed_resp, key_file)
|
||||||
|
|
||||||
|
resp = samlp.response_from_string(decr_text)
|
||||||
|
|
||||||
|
valid = self.server.sec.verify_signature(decr_text,
|
||||||
|
self.server.config.cert_file,
|
||||||
|
node_name='urn:oasis:names:tc:SAML:2.0:assertion:Assertion',
|
||||||
|
node_id=resp.assertion[0].id,
|
||||||
|
id_attr="")
|
||||||
|
assert valid
|
||||||
|
|
||||||
|
assert resp.assertion[0].advice.encrypted_assertion[0].extension_elements
|
||||||
|
|
||||||
|
assertion = extension_elements_to_elements(resp.assertion[0].advice.encrypted_assertion[0].extension_elements,
|
||||||
|
[saml, samlp])
|
||||||
|
assert assertion
|
||||||
|
assert assertion[0].attribute_statement
|
||||||
|
|
||||||
|
ava = ava = get_ava(assertion[0])
|
||||||
|
|
||||||
|
assert ava ==\
|
||||||
|
{'mail': ['derek@nyy.mlb.com'], 'givenname': ['Derek'], 'surname': ['Jeter'], 'title': ['The man']}
|
||||||
|
|
||||||
|
assert 'EncryptedAssertion><encas2:Assertion xmlns:encas0="http://www.w3.org/2000/09/xmldsig#" ' \
|
||||||
|
'xmlns:encas1="http://www.w3.org/2001/XMLSchema-instance" ' \
|
||||||
|
'xmlns:encas2="urn:oasis:names:tc:SAML:2.0:assertion"' in decr_text
|
||||||
|
|
||||||
|
valid = self.server.sec.verify_signature(decr_text,
|
||||||
|
self.server.config.cert_file,
|
||||||
|
node_name='urn:oasis:names:tc:SAML:2.0:assertion:Assertion',
|
||||||
|
node_id=assertion[0].id,
|
||||||
|
id_attr="")
|
||||||
|
assert valid
|
||||||
|
|
||||||
|
def test_encrypted_signed_response_2(self):
|
||||||
|
name_id = self.server.ident.transient_nameid(
|
||||||
|
"urn:mace:example.com:saml:roland:sp", "id12")
|
||||||
|
ava = {"givenName": ["Derek"], "surName": ["Jeter"],
|
||||||
|
"mail": ["derek@nyy.mlb.com"], "title": "The man"}
|
||||||
|
|
||||||
|
cert_str, cert_key_str = generate_cert()
|
||||||
|
|
||||||
|
signed_resp = self.server.create_authn_response(
|
||||||
|
ava,
|
||||||
|
"id12", # in_response_to
|
||||||
|
"http://lingon.catalogix.se:8087/", # consumer_url
|
||||||
|
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||||
|
name_id=name_id,
|
||||||
|
sign_response=True,
|
||||||
|
sign_assertion=True,
|
||||||
|
encrypt_assertion=True,
|
||||||
|
encrypt_assertion_self_contained=True,
|
||||||
|
encrypt_cert=cert_str,
|
||||||
|
)
|
||||||
|
|
||||||
|
sresponse = response_from_string(signed_resp)
|
||||||
|
|
||||||
|
valid = self.server.sec.verify_signature(signed_resp,
|
||||||
|
self.server.config.cert_file,
|
||||||
|
node_name='urn:oasis:names:tc:SAML:2.0:protocol:Response',
|
||||||
|
node_id=sresponse.id,
|
||||||
|
id_attr="")
|
||||||
|
assert valid
|
||||||
|
|
||||||
|
_, key_file = make_temp("%s" % cert_key_str, decode=False)
|
||||||
|
|
||||||
|
decr_text = self.server.sec.decrypt(signed_resp, key_file)
|
||||||
|
|
||||||
|
resp = samlp.response_from_string(decr_text)
|
||||||
|
|
||||||
|
assert resp.encrypted_assertion[0].extension_elements
|
||||||
|
|
||||||
|
assertion = extension_elements_to_elements(resp.encrypted_assertion[0].extension_elements, [saml, samlp])
|
||||||
|
assert assertion
|
||||||
|
assert assertion[0].attribute_statement
|
||||||
|
|
||||||
|
ava = get_ava(assertion[0])
|
||||||
|
|
||||||
|
assert ava ==\
|
||||||
|
{'mail': ['derek@nyy.mlb.com'], 'givenname': ['Derek'], 'surname': ['Jeter'], 'title': ['The man']}
|
||||||
|
|
||||||
|
assert 'EncryptedAssertion><encas2:Assertion xmlns:encas0="http://www.w3.org/2000/09/xmldsig#" ' \
|
||||||
|
'xmlns:encas1="http://www.w3.org/2001/XMLSchema-instance" ' \
|
||||||
|
'xmlns:encas2="urn:oasis:names:tc:SAML:2.0:assertion"' in decr_text
|
||||||
|
|
||||||
|
valid = self.server.sec.verify_signature(decr_text,
|
||||||
|
self.server.config.cert_file,
|
||||||
|
node_name='urn:oasis:names:tc:SAML:2.0:assertion:Assertion',
|
||||||
|
node_id=assertion[0].id,
|
||||||
|
id_attr="")
|
||||||
|
assert valid
|
||||||
|
|
||||||
|
def test_encrypted_signed_response_3(self):
|
||||||
|
name_id = self.server.ident.transient_nameid(
|
||||||
|
"urn:mace:example.com:saml:roland:sp", "id12")
|
||||||
|
ava = {"givenName": ["Derek"], "surName": ["Jeter"],
|
||||||
|
"mail": ["derek@nyy.mlb.com"], "title": "The man"}
|
||||||
|
|
||||||
|
cert_str, cert_key_str = generate_cert()
|
||||||
|
|
||||||
|
signed_resp = self.server.create_authn_response(
|
||||||
|
ava,
|
||||||
|
"id12", # in_response_to
|
||||||
|
"http://lingon.catalogix.se:8087/", # consumer_url
|
||||||
|
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||||
|
name_id=name_id,
|
||||||
|
sign_response=True,
|
||||||
|
sign_assertion=True,
|
||||||
|
encrypt_assertion=True,
|
||||||
|
encrypt_cert=cert_str,
|
||||||
|
)
|
||||||
|
|
||||||
|
sresponse = response_from_string(signed_resp)
|
||||||
|
|
||||||
|
valid = self.server.sec.verify_signature(signed_resp,
|
||||||
|
self.server.config.cert_file,
|
||||||
|
node_name='urn:oasis:names:tc:SAML:2.0:protocol:Response',
|
||||||
|
node_id=sresponse.id,
|
||||||
|
id_attr="")
|
||||||
|
assert valid
|
||||||
|
|
||||||
|
_, key_file = make_temp("%s" % cert_key_str, decode=False)
|
||||||
|
|
||||||
|
decr_text = self.server.sec.decrypt(signed_resp, key_file)
|
||||||
|
|
||||||
|
resp = samlp.response_from_string(decr_text)
|
||||||
|
|
||||||
|
assert resp.encrypted_assertion[0].extension_elements
|
||||||
|
|
||||||
|
assertion = extension_elements_to_elements(resp.encrypted_assertion[0].extension_elements, [saml, samlp])
|
||||||
|
assert assertion
|
||||||
|
assert assertion[0].attribute_statement
|
||||||
|
|
||||||
|
ava = get_ava(assertion[0])
|
||||||
|
|
||||||
|
assert ava ==\
|
||||||
|
{'mail': ['derek@nyy.mlb.com'], 'givenname': ['Derek'], 'surname': ['Jeter'], 'title': ['The man']}
|
||||||
|
|
||||||
|
assert 'xmlns:encas' not in decr_text
|
||||||
|
|
||||||
|
valid = self.server.sec.verify_signature(decr_text,
|
||||||
|
self.server.config.cert_file,
|
||||||
|
node_name='urn:oasis:names:tc:SAML:2.0:assertion:Assertion',
|
||||||
|
node_id=assertion[0].id,
|
||||||
|
id_attr="")
|
||||||
|
assert valid
|
||||||
|
|
||||||
|
def test_encrypted_signed_response_4(self):
|
||||||
|
name_id = self.server.ident.transient_nameid(
|
||||||
|
"urn:mace:example.com:saml:roland:sp", "id12")
|
||||||
|
ava = {"givenName": ["Derek"], "surName": ["Jeter"],
|
||||||
|
"mail": ["derek@nyy.mlb.com"], "title": "The man"}
|
||||||
|
|
||||||
|
cert_str, cert_key_str = generate_cert()
|
||||||
|
|
||||||
|
signed_resp = self.server.create_authn_response(
|
||||||
|
ava,
|
||||||
|
"id12", # in_response_to
|
||||||
|
"http://lingon.catalogix.se:8087/", # consumer_url
|
||||||
|
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||||
|
name_id=name_id,
|
||||||
|
sign_response=True,
|
||||||
|
sign_assertion=True,
|
||||||
|
encrypt_assertion=True,
|
||||||
|
encrypted_advice_attributes=True,
|
||||||
|
encrypt_cert=cert_str,
|
||||||
|
)
|
||||||
|
|
||||||
|
sresponse = response_from_string(signed_resp)
|
||||||
|
|
||||||
|
valid = self.server.sec.verify_signature(signed_resp,
|
||||||
|
self.server.config.cert_file,
|
||||||
|
node_name='urn:oasis:names:tc:SAML:2.0:protocol:Response',
|
||||||
|
node_id=sresponse.id,
|
||||||
|
id_attr="")
|
||||||
|
assert valid
|
||||||
|
|
||||||
|
_, key_file = make_temp("%s" % cert_key_str, decode=False)
|
||||||
|
|
||||||
|
decr_text = self.server.sec.decrypt(signed_resp, key_file)
|
||||||
|
|
||||||
|
resp = samlp.response_from_string(decr_text)
|
||||||
|
|
||||||
|
valid = self.server.sec.verify_signature(decr_text,
|
||||||
|
self.server.config.cert_file,
|
||||||
|
node_name='urn:oasis:names:tc:SAML:2.0:assertion:Assertion',
|
||||||
|
node_id=resp.assertion[0].id,
|
||||||
|
id_attr="")
|
||||||
|
assert valid
|
||||||
|
|
||||||
|
assert resp.assertion[0].advice.encrypted_assertion[0].extension_elements
|
||||||
|
|
||||||
|
assertion = extension_elements_to_elements(resp.assertion[0].advice.encrypted_assertion[0].extension_elements,
|
||||||
|
[saml, samlp])
|
||||||
|
assert assertion
|
||||||
|
assert assertion[0].attribute_statement
|
||||||
|
|
||||||
|
ava = ava = get_ava(assertion[0])
|
||||||
|
|
||||||
|
assert ava ==\
|
||||||
|
{'mail': ['derek@nyy.mlb.com'], 'givenname': ['Derek'], 'surname': ['Jeter'], 'title': ['The man']}
|
||||||
|
|
||||||
|
#Should work, but I suspect that xmlsec manipulates the xml to much while encrypting that the signature
|
||||||
|
#is no longer working. :(
|
||||||
|
|
||||||
|
assert 'xmlns:encas' not in decr_text
|
||||||
|
|
||||||
|
valid = self.server.sec.verify_signature(decr_text,
|
||||||
|
self.server.config.cert_file,
|
||||||
|
node_name='urn:oasis:names:tc:SAML:2.0:assertion:Assertion',
|
||||||
|
node_id=assertion[0].id,
|
||||||
|
id_attr="")
|
||||||
|
assert valid
|
||||||
|
|
||||||
|
def test_encrypted_response_1(self):
|
||||||
|
name_id = self.server.ident.transient_nameid(
|
||||||
|
"urn:mace:example.com:saml:roland:sp", "id12")
|
||||||
|
ava = {"givenName": ["Derek"], "surName": ["Jeter"],
|
||||||
|
"mail": ["derek@nyy.mlb.com"], "title": "The man"}
|
||||||
|
|
||||||
|
cert_str, cert_key_str = generate_cert()
|
||||||
|
|
||||||
|
signed_resp = self.server.create_authn_response(
|
||||||
|
ava,
|
||||||
|
"id12", # in_response_to
|
||||||
|
"http://lingon.catalogix.se:8087/", # consumer_url
|
||||||
|
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||||
|
name_id=name_id,
|
||||||
|
sign_response=False,
|
||||||
|
sign_assertion=False,
|
||||||
|
encrypt_assertion=True,
|
||||||
|
encrypt_assertion_self_contained=True,
|
||||||
|
encrypted_advice_attributes=True,
|
||||||
|
encrypt_cert=cert_str,
|
||||||
|
)
|
||||||
|
|
||||||
|
sresponse = response_from_string(signed_resp)
|
||||||
|
|
||||||
|
assert sresponse.signature is None
|
||||||
|
|
||||||
|
_, key_file = make_temp("%s" % cert_key_str, decode=False)
|
||||||
|
|
||||||
|
decr_text = self.server.sec.decrypt(signed_resp, key_file)
|
||||||
|
|
||||||
|
resp = samlp.response_from_string(decr_text)
|
||||||
|
|
||||||
|
assert resp.assertion[0].signature is None
|
||||||
|
|
||||||
|
assert resp.assertion[0].advice.encrypted_assertion[0].extension_elements
|
||||||
|
|
||||||
|
assertion = extension_elements_to_elements(resp.assertion[0].advice.encrypted_assertion[0].extension_elements,
|
||||||
|
[saml, samlp])
|
||||||
|
assert assertion
|
||||||
|
assert assertion[0].attribute_statement
|
||||||
|
|
||||||
|
ava = ava = get_ava(assertion[0])
|
||||||
|
|
||||||
|
assert ava ==\
|
||||||
|
{'mail': ['derek@nyy.mlb.com'], 'givenname': ['Derek'], 'surname': ['Jeter'], 'title': ['The man']}
|
||||||
|
|
||||||
|
assert 'EncryptedAssertion><encas1:Assertion xmlns:encas0="http://www.w3.org/2001/XMLSchema-instance" ' \
|
||||||
|
'xmlns:encas1="urn:oasis:names:tc:SAML:2.0:assertion"' in decr_text
|
||||||
|
|
||||||
|
assert assertion[0].signature is None
|
||||||
|
|
||||||
|
def test_encrypted_response_2(self):
|
||||||
|
name_id = self.server.ident.transient_nameid(
|
||||||
|
"urn:mace:example.com:saml:roland:sp", "id12")
|
||||||
|
ava = {"givenName": ["Derek"], "surName": ["Jeter"],
|
||||||
|
"mail": ["derek@nyy.mlb.com"], "title": "The man"}
|
||||||
|
|
||||||
|
cert_str, cert_key_str = generate_cert()
|
||||||
|
|
||||||
|
signed_resp = self.server.create_authn_response(
|
||||||
|
ava,
|
||||||
|
"id12", # in_response_to
|
||||||
|
"http://lingon.catalogix.se:8087/", # consumer_url
|
||||||
|
"urn:mace:example.com:saml:roland:sp", # sp_entity_id
|
||||||
|
name_id=name_id,
|
||||||
|
sign_response=False,
|
||||||
|
sign_assertion=False,
|
||||||
|
encrypt_assertion=True,
|
||||||
|
encrypt_assertion_self_contained=True,
|
||||||
|
encrypted_advice_attributes=False,
|
||||||
|
encrypt_cert=cert_str,
|
||||||
|
)
|
||||||
|
|
||||||
|
sresponse = response_from_string(signed_resp)
|
||||||
|
|
||||||
|
assert sresponse.signature is None
|
||||||
|
|
||||||
|
_, key_file = make_temp("%s" % cert_key_str, decode=False)
|
||||||
|
|
||||||
|
decr_text = self.server.sec.decrypt(signed_resp, key_file)
|
||||||
|
|
||||||
|
resp = samlp.response_from_string(decr_text)
|
||||||
|
|
||||||
|
assert resp.encrypted_assertion[0].extension_elements
|
||||||
|
|
||||||
|
assertion = extension_elements_to_elements(resp.encrypted_assertion[0].extension_elements, [saml, samlp])
|
||||||
|
assert assertion
|
||||||
|
assert assertion[0].attribute_statement
|
||||||
|
|
||||||
|
ava = ava = get_ava(assertion[0])
|
||||||
|
|
||||||
|
assert ava ==\
|
||||||
|
{'mail': ['derek@nyy.mlb.com'], 'givenname': ['Derek'], 'surname': ['Jeter'], 'title': ['The man']}
|
||||||
|
|
||||||
|
assert 'EncryptedAssertion><encas1:Assertion xmlns:encas0="http://www.w3.org/2001/XMLSchema-instance" ' \
|
||||||
|
'xmlns:encas1="urn:oasis:names:tc:SAML:2.0:assertion"' in decr_text
|
||||||
|
|
||||||
|
assert assertion[0].signature is None
|
||||||
|
|
||||||
|
|
||||||
def test_slo_http_post(self):
|
def test_slo_http_post(self):
|
||||||
soon = time_util.in_a_while(days=1)
|
soon = time_util.in_a_while(days=1)
|
||||||
sinfo = {
|
sinfo = {
|
||||||
@@ -509,3 +895,4 @@ class TestServerLogout():
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
ts = TestServer1()
|
ts = TestServer1()
|
||||||
ts.setup_class()
|
ts.setup_class()
|
||||||
|
ts.test_encrypted_response_1()
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from saml2.authn_context import INTERNETPROTOCOLPASSWORD
|
|||||||
from saml2.client import Saml2Client
|
from saml2.client import Saml2Client
|
||||||
from saml2.config import SPConfig
|
from saml2.config import SPConfig
|
||||||
from saml2.response import LogoutResponse
|
from saml2.response import LogoutResponse
|
||||||
from saml2.saml import NAMEID_FORMAT_PERSISTENT, EncryptedAssertion
|
from saml2.saml import NAMEID_FORMAT_PERSISTENT, EncryptedAssertion, Advice
|
||||||
from saml2.saml import NAMEID_FORMAT_TRANSIENT
|
from saml2.saml import NAMEID_FORMAT_TRANSIENT
|
||||||
from saml2.saml import NameID
|
from saml2.saml import NameID
|
||||||
from saml2.server import Server
|
from saml2.server import Server
|
||||||
@@ -499,6 +499,86 @@ class TestClient:
|
|||||||
assert resp.assertion
|
assert resp.assertion
|
||||||
assert resp.ava == {'givenName': ['Derek'], 'sn': ['Jeter']}
|
assert resp.ava == {'givenName': ['Derek'], 'sn': ['Jeter']}
|
||||||
|
|
||||||
|
def test_sign_then_encrypt_assertion_advice(self):
|
||||||
|
# Begin with the IdPs side
|
||||||
|
_sec = self.server.sec
|
||||||
|
|
||||||
|
nameid_policy = samlp.NameIDPolicy(allow_create="false",
|
||||||
|
format=saml.NAMEID_FORMAT_PERSISTENT)
|
||||||
|
|
||||||
|
asser = Assertion({"givenName": "Derek", "surName": "Jeter"})
|
||||||
|
assertion = asser.construct(
|
||||||
|
self.client.config.entityid, "_012345",
|
||||||
|
"http://lingon.catalogix.se:8087/",
|
||||||
|
factory(saml.NameID, format=saml.NAMEID_FORMAT_TRANSIENT),
|
||||||
|
policy=self.server.config.getattr("policy", "idp"),
|
||||||
|
issuer=self.server._issuer(),
|
||||||
|
attrconvs=self.server.config.attribute_converters,
|
||||||
|
authn_class=INTERNETPROTOCOLPASSWORD,
|
||||||
|
authn_auth="http://www.example.com/login")
|
||||||
|
|
||||||
|
a_asser = Assertion({"uid": "test01", "email": "test.testsson@test.se"})
|
||||||
|
a_assertion = a_asser.construct(
|
||||||
|
self.client.config.entityid, "_012345",
|
||||||
|
"http://lingon.catalogix.se:8087/",
|
||||||
|
factory(saml.NameID, format=saml.NAMEID_FORMAT_TRANSIENT),
|
||||||
|
policy=self.server.config.getattr("policy", "idp"),
|
||||||
|
issuer=self.server._issuer(),
|
||||||
|
attrconvs=self.server.config.attribute_converters,
|
||||||
|
authn_class=INTERNETPROTOCOLPASSWORD,
|
||||||
|
authn_auth="http://www.example.com/login")
|
||||||
|
|
||||||
|
a_assertion.signature = sigver.pre_signature_part(
|
||||||
|
a_assertion.id, _sec.my_cert, 1)
|
||||||
|
|
||||||
|
assertion.advice = Advice()
|
||||||
|
|
||||||
|
assertion.advice.encrypted_assertion = []
|
||||||
|
assertion.advice.encrypted_assertion.append(EncryptedAssertion())
|
||||||
|
|
||||||
|
assertion.advice.encrypted_assertion[0].add_extension_element(a_assertion)
|
||||||
|
|
||||||
|
response = sigver.response_factory(
|
||||||
|
in_response_to="_012345",
|
||||||
|
destination="http://lingon.catalogix.se:8087/",
|
||||||
|
status=s_utils.success_status_factory(),
|
||||||
|
issuer=self.server._issuer()
|
||||||
|
)
|
||||||
|
|
||||||
|
response.assertion.append(assertion)
|
||||||
|
|
||||||
|
response = _sec.sign_statement("%s" % response, class_name(a_assertion),
|
||||||
|
key_file=self.client.sec.key_file,
|
||||||
|
node_id=a_assertion.id)
|
||||||
|
|
||||||
|
#xmldoc = "%s" % response
|
||||||
|
# strangely enough I get different tags if I run this test separately
|
||||||
|
# or as part of a bunch of tests.
|
||||||
|
#xmldoc = add_subelement(xmldoc, "EncryptedAssertion", sigass)
|
||||||
|
|
||||||
|
node_xpath = ''.join(["/*[local-name()=\"%s\"]" % v for v in
|
||||||
|
["Response", "Assertion", "Advice", "EncryptedAssertion", "Assertion"]])
|
||||||
|
|
||||||
|
enctext = _sec.crypto.encrypt_assertion(response, _sec.cert_file,
|
||||||
|
pre_encryption_part(), node_xpath=node_xpath)
|
||||||
|
|
||||||
|
#seresp = samlp.response_from_string(enctext)
|
||||||
|
|
||||||
|
resp_str = base64.encodestring(enctext)
|
||||||
|
# Now over to the client side
|
||||||
|
resp = self.client.parse_authn_request_response(
|
||||||
|
resp_str, BINDING_HTTP_POST,
|
||||||
|
{"_012345": "http://foo.example.com/service"})
|
||||||
|
|
||||||
|
#assert resp.encrypted_assertion == []
|
||||||
|
assert resp.assertion
|
||||||
|
assert resp.assertion.advice
|
||||||
|
assert resp.assertion.advice.assertion
|
||||||
|
assert resp.ava == \
|
||||||
|
{'sn': ['Jeter'], 'givenName': ['Derek'], 'uid': ['test01'], 'email': ['test.testsson@test.se']}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_signed_redirect(self):
|
def test_signed_redirect(self):
|
||||||
|
|
||||||
msg_str = "%s" % self.client.create_authn_request(
|
msg_str = "%s" % self.client.create_authn_request(
|
||||||
@@ -628,4 +708,4 @@ class TestClientWithDummy():
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
tc = TestClient()
|
tc = TestClient()
|
||||||
tc.setup_class()
|
tc.setup_class()
|
||||||
tc.test_sign_then_encrypt_assertion2()
|
tc.test_sign_then_encrypt_assertion_advice()
|
||||||
Reference in New Issue
Block a user