This commit is contained in:
Roland Hedberg
2010-03-29 11:06:04 +02:00
parent 51fd521492
commit 9345b2b376
3 changed files with 60 additions and 31 deletions

View File

@@ -29,19 +29,23 @@ from saml2.attribute_converter import to_local
def _use_on_or_after(condition, slack): def _use_on_or_after(condition, slack):
now = time.mktime(time.gmtime()) now = time.mktime(time.gmtime())
#print "NOW: %d" % now
not_on_or_after = time.mktime(str_to_time(condition.not_on_or_after)) not_on_or_after = time.mktime(str_to_time(condition.not_on_or_after))
#print "not_on_or_after: %d" % not_on_or_after
# slack is +- # slack is +-
high = not_on_or_after+slack high = not_on_or_after+slack
if now > high: if now > high:
# To old ignore # To old ignore
print "(%d > %d)" % (now,high) #print "(%d > %d)" % (now,high)
raise Exception("To old can't use it!") raise Exception("To old can't use it!")
return not_on_or_after return not_on_or_after
def _use_before(condition, slack): def _use_before(condition, slack):
not_before = time.mktime(str_to_time(condition.not_before))
now = time.mktime(time.gmtime()) now = time.mktime(time.gmtime())
#print "NOW: %s" % now
not_before = time.mktime(str_to_time(condition.not_before))
#print "not_before: %d" % not_before
if not_before > now + slack: if not_before > now + slack:
# Can't use it yet # Can't use it yet
raise Exception("Can't use it yet %s <= %s" % (not_before, now)) raise Exception("Can't use it yet %s <= %s" % (not_before, now))
@@ -54,8 +58,11 @@ def for_me(condition, myself ):
if audience.text.strip() == myself: if audience.text.strip() == myself:
return True return True
else: else:
print "%s != %s" % (audience.text.strip(), myself) #print "Not for me: %s != %s" % (audience.text.strip(), myself)
pass
return False
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
class IncorrectlySigned(Exception): class IncorrectlySigned(Exception):
@@ -64,7 +71,7 @@ class IncorrectlySigned(Exception):
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
def authn_response(conf, requestor, outstanding_queries=None, log=None, def authn_response(conf, requestor, outstanding_queries=None, log=None,
timeslack=0): timeslack=0, debug=0):
sec = security_context(conf) sec = security_context(conf)
if not timeslack: if not timeslack:
try: try:
@@ -73,7 +80,7 @@ def authn_response(conf, requestor, outstanding_queries=None, log=None,
pass pass
return AuthnResponse(sec, conf.attribute_converters, requestor, return AuthnResponse(sec, conf.attribute_converters, requestor,
outstanding_queries, log, timeslack) outstanding_queries, log, timeslack, debug)
class AuthnResponse(object): class AuthnResponse(object):
@@ -95,6 +102,8 @@ class AuthnResponse(object):
self.clear() self.clear()
def loads(self, xmldata, decode=True): def loads(self, xmldata, decode=True):
if self.debug:
self.log.info("--- Loads AuthnResponse ---")
if decode: if decode:
decoded_xml = base64.b64decode(xmldata) decoded_xml = base64.b64decode(xmldata)
else: else:
@@ -102,7 +111,13 @@ class AuthnResponse(object):
# own copy # own copy
self.xmlstr = decoded_xml[:] self.xmlstr = decoded_xml[:]
self.response = self.sc.correctly_signed_response(decoded_xml) if self.debug:
self.log.info("xmlstr: %s" % (self.xmlstr,))
try:
self.response = self.sc.correctly_signed_response(decoded_xml)
except Exception, excp:
self.log.info("EXCEPTION: %s", excp)
raise
if not self.response: if not self.response:
if self.log: if self.log:
@@ -111,7 +126,7 @@ class AuthnResponse(object):
raise IncorrectlySigned() raise IncorrectlySigned()
if self.debug: if self.debug:
self.log.debug("response: %s" % (response,)) self.log.info("response: %s" % (self.response,))
return self return self
@@ -127,6 +142,7 @@ class AuthnResponse(object):
def status_ok(self): def status_ok(self):
if self.response.status: if self.response.status:
status = self.response.status status = self.response.status
self.log.info("status: %s" % (status,))
if status.status_code.value != samlp.STATUS_SUCCESS: if status.status_code.value != samlp.STATUS_SUCCESS:
if self.log: if self.log:
self.log.info("Not successfull operation: %s" % status) self.log.info("Not successfull operation: %s" % status)
@@ -151,7 +167,8 @@ class AuthnResponse(object):
try: try:
self.not_on_or_after = _use_on_or_after(condition, self.timeslack) self.not_on_or_after = _use_on_or_after(condition, self.timeslack)
_use_before(condition, self.timeslack) _use_before(condition, self.timeslack)
except Exception: except Exception,excp:
self.log.error("Exception on condition: %s" % (excp,))
if not lax: if not lax:
raise raise
else: else:
@@ -166,9 +183,14 @@ class AuthnResponse(object):
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: if not self.assertion.attribute_statement:
self.log.error("Missing Attribute Statement")
ava = {} ava = {}
else: else:
assert len(self.assertion.attribute_statement) == 1 assert len(self.assertion.attribute_statement) == 1
self.log.info("Attribute Statement: %s" % (
self.assertion.attribute_statement[0],))
for ac in self.attribute_converters():
self.log.info("Converts name format: %s" % (ac.format,))
ava = to_local(self.attribute_converters(), ava = to_local(self.attribute_converters(),
self.assertion.attribute_statement[0]) self.assertion.attribute_statement[0])
return ava return ava
@@ -179,11 +201,11 @@ class AuthnResponse(object):
subject = self.assertion.subject subject = self.assertion.subject
for subject_confirmation in subject.subject_confirmation: for subject_confirmation in subject.subject_confirmation:
data = subject_confirmation.subject_confirmation_data data = subject_confirmation.subject_confirmation_data
if data.in_response_to in self.outstanding: if data.in_response_to in self.outstanding_queries:
self.came_from = self.outstanding[data.in_response_to] self.came_from = self.outstanding_queries[data.in_response_to]
del self.outstanding[data.in_response_to] del self.outstanding_queries[data.in_response_to]
else: else:
print data.in_response_to, self.outstanding.keys() print data.in_response_to, self.outstanding_queries.keys()
raise Exception( raise Exception(
"Combination of session id and requestURI I don't recall") "Combination of session id and requestURI I don't recall")
@@ -195,9 +217,10 @@ class AuthnResponse(object):
self.assertion = assertion self.assertion = assertion
if self.debug: if self.debug:
self.log.info("assertion context: %s" % (context,)) self.log.info("assertion context: %s" % (self.context,))
self.log.info("assertion keys: %s" % (assertion.keyswv())) self.log.info("assertion keys: %s" % (assertion.keyswv()))
self.log.info("outstanding: %s" % (outstanding)) self.log.info("outstanding_queries: %s" % (
self.outstanding_queries))
if self.context == "AuthNReq": if self.context == "AuthNReq":
self.authn_statement_ok() self.authn_statement_ok()
@@ -205,10 +228,13 @@ class AuthnResponse(object):
if not self.condition_ok(): if not self.condition_ok():
return None return None
if self.debug:
self.log.info("--- Getting Identity ---")
self.ava = self.get_identity() self.ava = self.get_identity()
if self.debug: if self.debug:
self.log.debug("AVA: %s" % (self.ava,)) self.log.info("--- AVA: %s" % (self.ava,))
self.get_subject() self.get_subject()
@@ -218,18 +244,18 @@ class AuthnResponse(object):
decrypt_xml = self.sc.decrypt(self.xmlstr) decrypt_xml = self.sc.decrypt(self.xmlstr)
if self.debug: if self.debug:
self.log.debug("Decryption successfull") self.log.info("Decryption successfull")
self.response = samlp.response_from_string(decrypt_xml) self.response = samlp.response_from_string(decrypt_xml)
if self.debug: if self.debug:
self.log.debug("Parsed decrypted assertion successfull") self.log.info("Parsed decrypted assertion successfull")
enc = self.response.encrypted_assertion[0].extension_elements[0] enc = self.response.encrypted_assertion[0].extension_elements[0]
assertion = extension_element_to_element(enc, assertion = extension_element_to_element(enc,
saml.ELEMENT_FROM_STRING, saml.ELEMENT_FROM_STRING,
namespace=saml.NAMESPACE) namespace=saml.NAMESPACE)
if self.debug: if self.debug:
self.log.debug("Decrypted Assertion: %s" % assertion) self.log.info("Decrypted Assertion: %s" % assertion)
return self._assertion(assertion) return self._assertion(assertion)
def parse_assertion(self): def parse_assertion(self):
@@ -240,11 +266,12 @@ class AuthnResponse(object):
raise Exception("No assertion part") raise Exception("No assertion part")
if self.response.assertion: if self.response.assertion:
self.debug and self.log.debug("***Unencrypted response***") self.debug and self.log.info("***Unencrypted response***")
return self._assertion(self.response.assertion[0]) return self._assertion(self.response.assertion[0])
else: else:
self.debug and self.log.info("***Encrypted response***") self.debug and self.log.info("***Encrypted response***")
return self._encrypted_assertion(outstanding) return self._encrypted_assertion(
self.response.encrypted_assertion[0])
return True return True
@@ -266,7 +293,7 @@ class AuthnResponse(object):
return self.response.id return self.response.id
def session_info(self): def session_info(self):
return { "ava": self.ava, "name_id": name_id, return { "ava": self.ava, "name_id": self.name_id,
"came_from": self.came_from, "issuer": self.issuer(), "came_from": self.came_from, "issuer": self.issuer(),
"not_on_or_after": self.not_on_or_after } "not_on_or_after": self.not_on_or_after }

View File

@@ -51,7 +51,7 @@ LAX = False
class Saml2Client(object): class Saml2Client(object):
""" The basic pySAML2 service provider class """ """ The basic pySAML2 service provider class """
def __init__(self, environ, config=None): def __init__(self, environ, config=None, debug=0):
""" """
:param environ: :param environ:
:param config: A saml2.config.Config instance :param config: A saml2.config.Config instance
@@ -62,7 +62,9 @@ class Saml2Client(object):
if "metadata" in config: if "metadata" in config:
self.metadata = config["metadata"] self.metadata = config["metadata"]
self.sc = security_context(config) self.sc = security_context(config)
self.debug = debug
def _init_request(self, request, destination): def _init_request(self, request, destination):
#request.id = sid() #request.id = sid()
request.version = VERSION request.version = VERSION
@@ -112,7 +114,8 @@ class Saml2Client(object):
if post.has_key("SAMLResponse"): if post.has_key("SAMLResponse"):
saml_response = post['SAMLResponse'].value saml_response = post['SAMLResponse'].value
if saml_response: if saml_response:
ar = authn_response(self.conf, requestor, outstanding, log) ar = authn_response(self.config, requestor, outstanding, log,
debug=self.debug)
ar.loads(saml_response) ar.loads(saml_response)
return ar.verify() return ar.verify()
@@ -324,7 +327,7 @@ class Saml2Client(object):
if response: if response:
log and log.info("Verifying response") log and log.info("Verifying response")
ar = authn_response(self.conf, issuer, {session_id:""}, log) ar = authn_response(self.config, issuer, {session_id:""}, log)
session_info = ar.loads(response).verify().session_info() session_info = ar.loads(response).verify().session_info()
log and log.info("session: %s" % session_info) log and log.info("session: %s" % session_info)

View File

@@ -200,7 +200,7 @@ class Server(object):
try: try:
request = self.sc.correctly_signed_authn_request(request_xml) request = self.sc.correctly_signed_authn_request(request_xml)
if self.log and self.debug: if self.log and self.debug:
self.log.error("Request was correctly signed") self.log.info("Request was correctly signed")
except Exception: except Exception:
if self.log: if self.log:
self.log.error("Request was not correctly signed") self.log.error("Request was not correctly signed")
@@ -238,7 +238,7 @@ class Server(object):
response["consumer_url"] = consumer_url response["consumer_url"] = consumer_url
response["request"] = request response["request"] = request
self.log and self.log.info("AuthNRequest: %s" % request)
return response return response
def wants(self, sp_entity_id): def wants(self, sp_entity_id):
@@ -407,8 +407,7 @@ class Server(object):
in_response_to, # in_response_to in_response_to, # in_response_to
sp_entity_id, # sp_entity_id sp_entity_id, # sp_entity_id
identity, # identity as dictionary identity, # identity as dictionary
name_id, name_id
userid
) )
except MissingValue, exc: except MissingValue, exc:
response = self.error_response(destination, in_response_to, response = self.error_response(destination, in_response_to,