Example #1
0
    def correctly_signed_logout_response(self,
                                         decoded_xml,
                                         must=False,
                                         origdoc=None):
        """ Check if a request is correctly signed, if we have metadata for
        the SP that sent the info use that, if not use the key that are in 
        the message if any.

        :param decoded_xml: The SAML message as a XML string
        :param must: Whether there must be a signature
        :return: None if the signature can not be verified otherwise 
             the response as a samlp.LogoutResponse instance
        """
        response = samlp.logout_response_from_string(decoded_xml)
        if not response:
            raise TypeError("Not a LogoutResponse")

        if not response.signature:
            if must:
                raise SignatureError("Missing must signature")
            else:
                return response

        return self._check_signature(decoded_xml, response,
                                     class_name(response), origdoc)
Example #2
0
 def test_Saml_handle_logout_request(self):
     not_on_or_after = time.time() + 3600
     identity = {
         'id-1': {
             'https://sso.example.com/idp/metadata': (not_on_or_after, {
                 'authn_info': [],
                 'name_id': 'id-1',
                 'not_on_or_after': not_on_or_after,
                 'came_from': '/next',
                 'ava': {
                     'uid': ['123456']
                 }
             })
         }
     }
     state = {
         'entity_ids': ['https://sso.example.com/idp/metadata'],
         'subject_id': 'id-1',
         'return_to': '/next'
     }
     # modifying config in this test, make copy so as not to effect
     # following tests.
     tmp_sp_config = copy.deepcopy(sp_config)
     # create a response to assert upon
     sp = auth.Saml(tmp_sp_config)
     logout_request = create_logout_request(
         'id-1',
         destination='https://foo.example.com/sp/slo',
         issuer_entity_id='https://sso.example.com/idp/metadata',
         req_entity_id='https://sso.example.com/idp/metadata')
     # test SAMLRequest logout
     with self.app.test_request_context(
             '/',
             method='GET',
             query_string=dict(
                 SAMLRequest=deflate_and_base64_encode(str(logout_request)),
                 RelayState=deflate_and_base64_encode(logout_request.id))):
         # first need to be logged in, let's pretend
         session['_saml_identity'] = identity
         session['_saml_subject_id'] = 'id-1'
         session['_saml_state'] = {logout_request.id: state}
         success, resp = sp.handle_logout(request, next_url='/next')
         self.assertTrue(success)
         self.assertEqual(resp.status_code, 302)
         self.assert_("SAMLResponse" in resp.headers['Location'])
         url = urlparse.urlparse(resp.headers['Location'])
         params = urlparse.parse_qs(url.query)
         self.assert_('SAMLResponse' in params)
         logout = samlp.logout_response_from_string(
             decode_base64_and_inflate(params['SAMLResponse'][0]))
         self.assertEqual(logout.status.status_code.value,
                          'urn:oasis:names:tc:SAML:2.0:status:Success')
         self.assertEqual(logout.destination,
                          'https://sso.example.com/idp/slo')
Example #3
0
 def testUsingTestData(self):
     """Test for logout_response_from_string() using test data"""
     new_lr = samlp.logout_response_from_string(
         samlp_data.TEST_LOGOUT_RESPONSE)
     assert new_lr.id == "response id"
     assert new_lr.in_response_to == "request id"
     assert new_lr.version == saml2.VERSION
     assert new_lr.issue_instant == "2007-09-14T01:05:02Z"
     assert new_lr.destination == "http://www.example.com/Destination"
     assert new_lr.consent == saml.CONSENT_UNSPECIFIED
     assert isinstance(new_lr.issuer, saml.Issuer)
     assert isinstance(new_lr.signature, ds.Signature)
     assert isinstance(new_lr.extensions, samlp.Extensions)
     assert isinstance(new_lr.status, samlp.Status)
Example #4
0
 def testUsingTestData(self):
     """Test for logout_response_from_string() using test data"""
     new_lr = samlp.logout_response_from_string(
         samlp_data.TEST_LOGOUT_RESPONSE)
     assert new_lr.id == "response id"
     assert new_lr.in_response_to == "request id"
     assert new_lr.version == saml2.VERSION
     assert new_lr.issue_instant == "2007-09-14T01:05:02Z"
     assert new_lr.destination == "http://www.example.com/Destination"
     assert new_lr.consent == saml.CONSENT_UNSPECIFIED
     assert isinstance(new_lr.issuer, saml.Issuer)
     assert isinstance(new_lr.signature, ds.Signature)
     assert isinstance(new_lr.extensions, samlp.Extensions)
     assert isinstance(new_lr.status, samlp.Status)
Example #5
0
 def test_Saml_handle_logout_request(self):
     not_on_or_after = time.time()+3600
     identity = {'id-1': {
         'https://sso.example.com/idp/metadata': (
             not_on_or_after, {
                 'authn_info': [],
                 'name_id': 'id-1',
                 'not_on_or_after': not_on_or_after,
                 'came_from': '/next',
                 'ava': {'uid': ['123456']}
             }
         )
     }}
     state = {
         'entity_ids': ['https://sso.example.com/idp/metadata'],
         'subject_id': 'id-1',
         'return_to': '/next'
     }
     # modifying config in this test, make copy so as not to effect
     # following tests.
     tmp_sp_config = copy.deepcopy(sp_config)
     # create a response to assert upon
     sp = auth.Saml(tmp_sp_config)
     logout_request = create_logout_request('id-1',
         destination='https://foo.example.com/sp/slo',
         issuer_entity_id='https://sso.example.com/idp/metadata',
         req_entity_id='https://sso.example.com/idp/metadata')
     # test SAMLRequest logout
     with self.app.test_request_context('/',
             method='GET',
             query_string=dict(
                 SAMLRequest=deflate_and_base64_encode(str(logout_request)),
                 RelayState=deflate_and_base64_encode(logout_request.id))):
         # first need to be logged in, let's pretend
         session['_saml_identity'] = identity
         session['_saml_subject_id'] = 'id-1'
         session['_saml_state'] = {logout_request.id: state}
         success, resp = sp.handle_logout(request, next_url='/next')
         self.assertTrue(success)
         self.assertEqual(resp.status_code, 302)
         self.assert_("SAMLResponse" in resp.headers['Location'])
         url = urlparse.urlparse(resp.headers['Location'])
         params = urlparse.parse_qs(url.query)
         self.assert_('SAMLResponse' in params)
         logout = samlp.logout_response_from_string(
             decode_base64_and_inflate(params['SAMLResponse'][0]))
         self.assertEqual(logout.status.status_code.value,
             'urn:oasis:names:tc:SAML:2.0:status:Success')
         self.assertEqual(logout.destination, 'https://sso.example.com/idp/slo')
Example #6
0
def create_logout_response(subject_id, destination, issuer_entity_id,
        req_entity_id, sign=True):
    config = IdPConfig()
    config.load(idp_config)
    idp_server = Server(config=config)
    # construct a request
    logout_request = create_logout_request(
        subject_id=subject_id,
        destination=destination,
        issuer_entity_id=issuer_entity_id,
        req_entity_id=req_entity_id)
    #idp_server.ident = Identifier(auth.AuthDictCache(dict(), '_ident'))
    resp, headers, message = idp_server.logout_response(
        request=logout_request,
        bindings=[BINDING_HTTP_REDIRECT],
        sign=sign)
    location = dict(headers).get('Location')
    url = urlparse.urlparse(location)
    params = urlparse.parse_qs(url.query)
    logout_response_xml = decode_base64_and_inflate(params['SAMLResponse'][0])
    response = samlp.logout_response_from_string(logout_response_xml)
    return response.in_response_to, logout_response_xml
Example #7
0
def create_logout_response(subject_id,
                           destination,
                           issuer_entity_id,
                           req_entity_id,
                           sign=True):
    config = IdPConfig()
    config.load(idp_config)
    idp_server = Server(config=config)
    # construct a request
    logout_request = create_logout_request(subject_id=subject_id,
                                           destination=destination,
                                           issuer_entity_id=issuer_entity_id,
                                           req_entity_id=req_entity_id)
    #idp_server.ident = Identifier(auth.AuthDictCache(dict(), '_ident'))
    resp, headers, message = idp_server.logout_response(
        request=logout_request, bindings=[BINDING_HTTP_REDIRECT], sign=sign)
    location = dict(headers).get('Location')
    url = urlparse.urlparse(location)
    params = urlparse.parse_qs(url.query)
    logout_response_xml = decode_base64_and_inflate(params['SAMLResponse'][0])
    response = samlp.logout_response_from_string(logout_response_xml)
    return response.in_response_to, logout_response_xml
Example #8
0
    def correctly_signed_logout_response(self, decoded_xml, must=False,
                                         origdoc=None):
        """ Check if a request is correctly signed, if we have metadata for
        the SP that sent the info use that, if not use the key that are in
        the message if any.

        :param decoded_xml: The SAML message as a XML string
        :param must: Whether there must be a signature
        :return: None if the signature can not be verified otherwise
             the response as a samlp.LogoutResponse instance
        """
        response = samlp.logout_response_from_string(decoded_xml)
        if not response:
            raise TypeError("Not a LogoutResponse")

        if not response.signature:
            if must:
                raise SignatureError("Missing must signature")
            else:
                return response

        return self._check_signature(decoded_xml, response,
                                     class_name(response), origdoc)
Example #9
0
    def testAccessors(self):
        """Test for LogoutResponse accessors"""
        self.lr.id = "response id"
        self.lr.in_response_to = "request id"
        self.lr.version = saml2.VERSION
        self.lr.issue_instant = "2007-09-14T01:05:02Z"
        self.lr.destination = "http://www.example.com/Destination"
        self.lr.consent = saml.CONSENT_UNSPECIFIED
        self.lr.issuer = saml.Issuer()
        self.lr.signature = ds.Signature()
        self.lr.extensions = samlp.Extensions()
        self.lr.status = samlp.Status()

        new_lr = samlp.logout_response_from_string(self.lr.to_string())
        assert new_lr.id == "response id"
        assert new_lr.in_response_to == "request id"
        assert new_lr.version == saml2.VERSION
        assert new_lr.issue_instant == "2007-09-14T01:05:02Z"
        assert new_lr.destination == "http://www.example.com/Destination"
        assert new_lr.consent == saml.CONSENT_UNSPECIFIED
        assert isinstance(new_lr.issuer, saml.Issuer)
        assert isinstance(new_lr.signature, ds.Signature)
        assert isinstance(new_lr.extensions, samlp.Extensions)
        assert isinstance(new_lr.status, samlp.Status)
Example #10
0
    def testAccessors(self):
        """Test for LogoutResponse accessors"""
        self.lr.id = "response id"
        self.lr.in_response_to = "request id"
        self.lr.version = saml2.VERSION
        self.lr.issue_instant = "2007-09-14T01:05:02Z"
        self.lr.destination = "http://www.example.com/Destination"
        self.lr.consent = saml.CONSENT_UNSPECIFIED
        self.lr.issuer = saml.Issuer()
        self.lr.signature = ds.Signature()
        self.lr.extensions = samlp.Extensions()
        self.lr.status = samlp.Status()

        new_lr = samlp.logout_response_from_string(self.lr.to_string())
        assert new_lr.id == "response id"
        assert new_lr.in_response_to == "request id"
        assert new_lr.version == saml2.VERSION
        assert new_lr.issue_instant == "2007-09-14T01:05:02Z"
        assert new_lr.destination == "http://www.example.com/Destination"
        assert new_lr.consent == saml.CONSENT_UNSPECIFIED
        assert isinstance(new_lr.issuer, saml.Issuer)
        assert isinstance(new_lr.signature, ds.Signature)
        assert isinstance(new_lr.extensions, samlp.Extensions)
        assert isinstance(new_lr.status, samlp.Status)
Example #11
0
 def _parse_logout_response(self, enc_response):
     xmlstr = decode_base64_and_inflate(urllib.unquote(enc_response))
     return logout_response_from_string(xmlstr)
Example #12
0
    def do_logout(self, subject_id, entity_ids, reason, expire, sign=None):
        """

        :param subject_id: Identifier of the Subject
        :param entity_ids: List of entity ids for the IdPs that have provided
            information concerning the subject
        :param reason: The reason for doing the logout
        :param expire: Try to logout before this time.
        :param sign: Whether to sign the request or not
        :return:
        """
        # check time
        if not not_on_or_after(expire): # I've run out of time
            # Do the local logout anyway
            self.local_logout(subject_id)
            return 0, "504 Gateway Timeout", [], []
            
        # for all where I can use the SOAP binding, do those first
        not_done = entity_ids[:]
        responses = {}

        for entity_id in entity_ids:
            response = False

            for binding in [#BINDING_SOAP,
                            BINDING_HTTP_POST,
                            BINDING_HTTP_REDIRECT]:
                srvs = self.metadata.single_logout_service(entity_id, "idpsso",
                                                           binding=binding)
                if not srvs:
                    continue

                destination = destinations(srvs)[0]

                logger.info("destination to provider: %s" % destination)
                request = self.create_logout_request(destination, entity_id,
                                                     subject_id, reason=reason,
                                                     expire=expire)
                
                to_sign = []
                if binding.startswith("http://"):
                    sign = True

                if sign is None:
                    sign = self.logout_requests_signed_default

                if sign:
                    request.signature = pre_signature_part(request.id,
                                                    self.sec.my_cert, 1)
                    to_sign = [(class_name(request), request.id)]

                logger.info("REQUEST: %s" % request)

                srequest = signed_instance_factory(request, self.sec, to_sign)
        
                if binding == BINDING_SOAP:
                    response = self.send_using_soap(srequest, destination)
                    if response:
                        logger.info("Verifying response")
                        response = self.logout_request_response(response)

                    if response:
                        not_done.remove(entity_id)
                        logger.info("OK response from %s" % destination)
                        responses[entity_id] = logout_response_from_string(response)
                    else:
                        logger.info("NOT OK response from %s" % destination)

                else:
                    session_id = request.id
                    rstate = self._relay_state(session_id)

                    self.state[session_id] = {"entity_id": entity_id,
                                              "operation": "SLO",
                                              "entity_ids": entity_ids,
                                              "subject_id": subject_id,
                                              "reason": reason,
                                              "not_on_of_after": expire,
                                              "sign": sign}
                    

                    if binding == BINDING_HTTP_POST:
                        response = self.use_http_form_post(srequest,
                                                           destination,
                                                           rstate)
                    else:
                        response = self.use_http_get(srequest, destination,
                                                     rstate)

                    responses[entity_id] = response
                    not_done.remove(entity_id)

                # only try one binding
                break

        if not_done:
            # upstream should try later
            raise LogoutError("%s" % (entity_ids,))
        
        return responses