def validate_timestamps(self): """ Verifies that the document is valid according to Conditions Element :returns: True if the condition is valid, False otherwise :rtype: bool """ conditions_nodes = self.__query_assertion('/saml:Conditions') for conditions_node in conditions_nodes: nb_attr = conditions_node.get('NotBefore') nooa_attr = conditions_node.get('NotOnOrAfter') if nb_attr and OneLogin_Saml2_Utils.parse_SAML_to_time( nb_attr) > OneLogin_Saml2_Utils.now( ) + OneLogin_Saml2_Constants.ALLOWED_CLOCK_DRIFT: raise OneLogin_Saml2_ValidationError( 'Could not validate timestamp: not yet valid. Check system clock.', OneLogin_Saml2_ValidationError.ASSERTION_TOO_EARLY) if nooa_attr and OneLogin_Saml2_Utils.parse_SAML_to_time( nooa_attr ) + OneLogin_Saml2_Constants.ALLOWED_CLOCK_DRIFT <= OneLogin_Saml2_Utils.now( ): raise OneLogin_Saml2_ValidationError( 'Could not validate timestamp: expired. Check system clock.', OneLogin_Saml2_ValidationError.ASSERTION_EXPIRED) return True
def __init__(self, settings, response): """ Constructs the response object. :param settings: The setting info :type settings: OneLogin_Saml2_Setting object :param response: The base64 encoded, XML string containing the samlp:Response :type response: string """ self.__settings = settings self.__error = None self.response = OneLogin_Saml2_Utils.b64decode(response) self.document = OneLogin_Saml2_XML.to_etree(self.response) self.decrypted_document = None self.encrypted = None self.valid_scd_not_on_or_after = None # Quick check for the presence of EncryptedAssertion encrypted_assertion_nodes = self.__query( '/samlp:Response/saml:EncryptedAssertion') if encrypted_assertion_nodes: decrypted_document = deepcopy(self.document) self.encrypted = True self.decrypted_document = self.__decrypt_assertion( decrypted_document)
def get_nameid_data(self): """ Gets the NameID Data provided by the SAML Response from the IdP :returns: Name ID Data (Value, Format, NameQualifier, SPNameQualifier) :rtype: dict """ nameid = None nameid_data = {} encrypted_id_data_nodes = self.__query_assertion( '/saml:Subject/saml:EncryptedID/xenc:EncryptedData') if encrypted_id_data_nodes: encrypted_data = encrypted_id_data_nodes[0] key = self.__settings.get_sp_key() nameid = OneLogin_Saml2_Utils.decrypt_element(encrypted_data, key) else: nameid_nodes = self.__query_assertion('/saml:Subject/saml:NameID') if nameid_nodes: nameid = nameid_nodes[0] is_strict = self.__settings.is_strict() want_nameid = self.__settings.get_security_data().get( 'wantNameId', True) if nameid is None: if is_strict and want_nameid: raise OneLogin_Saml2_ValidationError( 'NameID not found in the assertion of the Response', OneLogin_Saml2_ValidationError.NO_NAMEID) else: if is_strict and want_nameid and not OneLogin_Saml2_XML.element_text( nameid): raise OneLogin_Saml2_ValidationError( 'An empty NameID value found', OneLogin_Saml2_ValidationError.EMPTY_NAMEID) nameid_data = {'Value': OneLogin_Saml2_XML.element_text(nameid)} for attr in ['Format', 'SPNameQualifier', 'NameQualifier']: value = nameid.get(attr, None) if value: if is_strict and attr == 'SPNameQualifier': sp_data = self.__settings.get_sp_data() sp_entity_id = sp_data.get('entityId', '') if sp_entity_id != value: raise OneLogin_Saml2_ValidationError( 'The SPNameQualifier value mistmatch the SP entityID value.', OneLogin_Saml2_ValidationError. SP_NAME_QUALIFIER_NAME_MISMATCH) nameid_data[attr] = value return nameid_data
def get_session_not_on_or_after(self): """ Gets the SessionNotOnOrAfter from the AuthnStatement Could be used to set the local session expiration :returns: The SessionNotOnOrAfter value :rtype: time|None """ not_on_or_after = None authn_statement_nodes = self.__query_assertion( '/saml:AuthnStatement[@SessionNotOnOrAfter]') if authn_statement_nodes: not_on_or_after = OneLogin_Saml2_Utils.parse_SAML_to_time( authn_statement_nodes[0].get('SessionNotOnOrAfter')) return not_on_or_after
def check_status(self): """ Check if the status of the response is success or not :raises: Exception. If the status is not success """ status = OneLogin_Saml2_Utils.get_status(self.document) code = status.get('code', None) if code and code != OneLogin_Saml2_Constants.STATUS_SUCCESS: splited_code = code.split(':') printable_code = splited_code.pop() status_exception_msg = 'The status code of the Response was not Success, was %s' % printable_code status_msg = status.get('msg', None) if status_msg: status_exception_msg += ' -> ' + status_msg raise OneLogin_Saml2_ValidationError( status_exception_msg, OneLogin_Saml2_ValidationError.STATUS_CODE_IS_NOT_SUCCESS)