Skip to content

Commit 90474ff

Browse files
committed
Add more tests and documentation related to get_last_message_id and get_last_assertion_id. Add get_last_assertion_not_on_or_after
1 parent f512657 commit 90474ff

File tree

6 files changed

+128
-8
lines changed

6 files changed

+128
-8
lines changed

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,13 @@ The 'x509certMulti' is an array with 2 keys:
854854
- 'encryption' An array with one unique cert that will be used to encrypt data to be sent to the IdP.
855855

856856

857+
### Replay attacks ###
858+
859+
In order to avoid replay attacks, you can store the ID of the SAML messages already processed, to avoid processing them twice. Since the Messages expires and will be invalidated due that fact, you don't need to store those IDs longer than the time frame that you currently accepting.
860+
861+
Get the ID of the last processed message/assertion with the get_last_message_id/get_last_assertion_id method of the Auth object.
862+
863+
857864
### Main classes and methods ###
858865

859866
Described below are the main classes and methods that can be invoked from the SAML2 library.
@@ -885,6 +892,9 @@ Main class of OneLogin Python Toolkit
885892
* ***set_strict*** Set the strict mode active/disable.
886893
* ***get_last_request_xml*** Returns the most recently-constructed/processed XML SAML request (AuthNRequest, LogoutRequest)
887894
* ***get_last_response_xml*** Returns the most recently-constructed/processed XML SAML response (SAMLResponse, LogoutResponse). If the SAMLResponse had an encrypted assertion, decrypts it.
895+
* ***get_last_message_id*** The ID of the last Response SAML message processed.
896+
* ***get_last_assertion_id*** The ID of the last assertion processed.
897+
* ***get_last_assertion_not_on_or_after*** The NotOnOrAfter value of the valid SubjectConfirmationData node (if any) of the last assertion processed (is only calculated with strict = true)
888898

889899
####OneLogin_Saml2_Auth - authn_request.py####
890900

@@ -913,6 +923,9 @@ SAML 2 Authentication Response class
913923
* ***validate_timestamps*** Verifies that the document is valid according to Conditions Element
914924
* ***get_error*** After execute a validation process, if fails this method returns the cause
915925
* ***get_xml_document*** Returns the SAML Response document (If contains an encrypted assertion, decrypts it).
926+
* ***get_id*** the ID of the response
927+
* ***get_assertion_id*** the ID of the assertion in the response
928+
* ***get_assertion_not_on_or_after*** the NotOnOrAfter value of the valid SubjectConfirmationData if any
916929

917930
####OneLogin_Saml2_LogoutRequest - logout_request.py####
918931

src/onelogin/saml2/auth.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ def __init__(self, request_data, old_settings=None, custom_base_path=None):
6565
self.__last_assertion_id = None
6666
self.__last_request = None
6767
self.__last_response = None
68+
self.__last_assertion_not_on_or_after = None
6869

6970
def get_settings(self):
7071
"""
@@ -109,6 +110,7 @@ def process_response(self, request_id=None):
109110
self.__last_message_id = response.get_id()
110111
self.__last_assertion_id = response.get_assertion_id()
111112
self.__authenticated = True
113+
self.__last_assertion_not_on_or_after = response.get_assertion_not_on_or_after()
112114

113115
else:
114116
self.__errors.append('invalid_response')
@@ -255,6 +257,13 @@ def get_session_expiration(self):
255257
"""
256258
return self.__session_expiration
257259

260+
def get_last_assertion_not_on_or_after(self):
261+
"""
262+
The NotOnOrAfter value of the valid SubjectConfirmationData node
263+
(if any) of the last assertion processed
264+
"""
265+
return self.__last_assertion_not_on_or_after
266+
258267
def get_errors(self):
259268
"""
260269
Returns a list with code errors if something went wrong

src/onelogin/saml2/response.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def __init__(self, settings, response):
3939
self.document = OneLogin_Saml2_XML.to_etree(self.response)
4040
self.decrypted_document = None
4141
self.encrypted = None
42+
self.valid_scd_not_on_or_after = None
4243

4344
# Quick check for the presence of EncryptedAssertion
4445
encrypted_assertion_nodes = self.__query('/samlp:Response/saml:EncryptedAssertion')
@@ -245,6 +246,10 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False):
245246
parsed_nb = OneLogin_Saml2_Utils.parse_SAML_to_time(nb)
246247
if parsed_nb > OneLogin_Saml2_Utils.now():
247248
continue
249+
250+
if nooa:
251+
self.valid_scd_not_on_or_after = OneLogin_Saml2_Utils.parse_SAML_to_time(nooa)
252+
248253
any_subject_confirmation = True
249254
break
250255

@@ -475,6 +480,12 @@ def get_session_not_on_or_after(self):
475480
not_on_or_after = OneLogin_Saml2_Utils.parse_SAML_to_time(authn_statement_nodes[0].get('SessionNotOnOrAfter'))
476481
return not_on_or_after
477482

483+
def get_assertion_not_on_or_after(self):
484+
"""
485+
Returns the NotOnOrAfter value of the valid SubjectConfirmationData node if any
486+
"""
487+
return self.valid_scd_not_on_or_after
488+
478489
def get_session_index(self):
479490
"""
480491
Gets the SessionIndex from the AuthnStatement

0 commit comments

Comments
 (0)