def validate_response(self, response: ResponseParser): # Check it came from the right place if self.entity_id != response.issuer: raise CannotHandleAssertion( f'Entity ID mismatch {self.entity_id} != {response.issuer}') if response.conditions is not None: # Validate the NotBefore/NotOnOrAfter tags now = utcnow() not_before = response.conditions.get('NotBefore') not_on_or_after = response.conditions.get('NotOnOrAfter') try: if not_before is not None and now < iso8601.parse_date( not_before): raise CannotHandleAssertion( f'NotBefore={not_before} check failed') if not_on_or_after is not None and now >= iso8601.parse_date( not_on_or_after): raise CannotHandleAssertion( f'NotOnOrAfter={not_on_or_after} check failed') except ValueError as err: raise CannotHandleAssertion("Could not parse date") from err # Validate the AudienceRestriction elements, if they exist audiences = response._xpath( response.conditions, './saml:AudienceRestriction/saml:Audience') entity_id = self.sp.get_sp_entity_id() if len(audiences) and not any(el.text == entity_id for el in audiences): raise CannotHandleAssertion( "No valid AudienceRestriction found")
def make_response(self, request) -> XmlTemplate: """ Process the request and make a :class:`~.xml_render.ResponseTemplate`. """ self.validate_request(request) self.validate_user() issue_instant = utcnow() assertion = self.format_assertion(self.build_assertion(request, issue_instant)) response = self.format_response(self.build_response(request, issue_instant), assertion) return response
def get_authn_request( self, template=AuthnRequest, **parameters, ): """ Make a AuthnRequest to send to this IdP. """ return template({ 'REQUEST_ID': get_random_id(), 'ISSUE_INSTANT': self.format_datetime(utcnow()), 'DESTINATION': self.get_idp_sso_url(), 'ISSUER': self.sp.get_sp_entity_id(), 'ACS_URL': self.get_sp_acs_url(), **parameters, })
def test_just_right(self): now = utcnow() self.login(self.user) with freezegun.freeze_time(now) as frozen: authn_request = self._make_authn_request() # step forwards a bit for transmission time frozen.tick(delta=datetime.timedelta(seconds=30)) authn_response = self._process_authn_request(authn_request) # step forwards a bit for transmission times frozen.tick(delta=datetime.timedelta(seconds=30)) auth_data = self._process_authn_response(authn_response) assert auth_data.nameid == self.user.email
def test_too_late(self): now = utcnow() self.login(self.user) with freezegun.freeze_time(now) as frozen: authn_request = self._make_authn_request() # step forwards a bit for transmission time frozen.tick(delta=datetime.timedelta(seconds=30)) authn_response = self._process_authn_request(authn_request) # step backwards a bunch frozen.tick(delta=datetime.timedelta(minutes=25)) with pytest.raises(CannotHandleAssertion, match='NotOnOrAfter'): self._process_authn_response(authn_response)
def get_logout_request( self, auth_data: AuthData, template: XmlTemplate = LogoutRequest, **parameters, ): """ Make a LogoutRequest for the authenticated user to send to this IdP. """ return template({ 'REQUEST_ID': get_random_id(), 'ISSUE_INSTANT': self.format_datetime(utcnow()), 'DESTINATION': self.get_idp_slo_url(), 'ISSUER': self.sp.get_sp_entity_id(), 'SUBJECT': auth_data.nameid, 'SUBJECT_FORMAT': auth_data.nameid_format, **parameters, })