Example #1
0
    def http_redirect_logout_request(self, get, subject_id, log=None):
        """ Deal with a LogoutRequest received through HTTP redirect

        :param get: The request as a dictionary 
        :param subject_id: the id of the current logged user
        :return: a tuple with a list of header tuples (presently only location)
            and a status which will be True in case of success or False 
            otherwise.
        """
        headers = []
        success = False
        if log is None:
            log = self.logger

        try:
            saml_request = get['SAMLRequest']
        except KeyError:
            return None

        if saml_request:
            xml = decode_base64_and_inflate(saml_request)

            request = samlp.logout_request_from_string(xml)
            if log:
                log.debug(request)

            if request.name_id.text == subject_id:
                status = samlp.STATUS_SUCCESS
                success = self.local_logout(subject_id)
            else:
                status = samlp.STATUS_REQUEST_DENIED

            response, destination = self .make_logout_response(
                                                        request.issuer.text,
                                                        request.id,
                                                        status)

            if log:
                log.info("RESPONSE: {0:>s}".format(response))

            if 'RelayState' in get:
                rstate = get['RelayState']
            else:
                rstate = ""
                
            (headers, _body) = http_redirect_message(str(response), 
                                                    destination, 
                                                    rstate, 'SAMLResponse')

        return headers, success
Example #2
0
    def do_authenticate(
        self,
        entityid=None,
        relay_state="",
        binding=saml2.BINDING_HTTP_REDIRECT,
        vorg="",
        nameid_format=NAMEID_FORMAT_PERSISTENT,
        scoping=None,
        consent=None,
        extensions=None,
        sign=None,
    ):
        """ Makes an authentication request.

        :param entityid: The entity ID of the IdP to send the request to
        :param relay_state: To where the user should be returned after
            successfull log in.
        :param binding: Which binding to use for sending the request
        :param vorg: The entity_id of the virtual organization I'm a member of
        :param scoping: For which IdPs this query are aimed.
        :param consent: Whether the principal have given her consent
        :param extensions: Possible extensions
        :param sign: Whether the request should be signed or not.
        :return: AuthnRequest response
        """

        location = self._sso_location(entityid, binding)

        req = self.create_authn_request(location, vorg, scoping, binding, nameid_format, consent, extensions, sign)
        _req_str = "%s" % req

        logger.info("AuthNReq: %s" % _req_str)

        if binding == saml2.BINDING_HTTP_POST:
            # No valid ticket; Send a form to the client
            # THIS IS NOT TO BE USED RIGHT NOW
            logger.info("HTTP POST")
            (head, response) = http_post_message(_req_str, location, relay_state)
        elif binding == saml2.BINDING_HTTP_REDIRECT:
            logger.info("HTTP REDIRECT")
            (head, _body) = http_redirect_message(_req_str, location, relay_state)
            response = head[0]
        else:
            raise Exception("Unknown binding type: %s" % binding)

        return req.id, response
Example #3
0
    def authenticate(
        self,
        entityid=None,
        relay_state="",
        binding=saml2.BINDING_HTTP_REDIRECT,
        log=None,
        vorg="",
        scoping=None,
        sign=None,
    ):
        """ Makes an authentication request.

        :param entityid: The entity ID of the IdP to send the request to
        :param relay_state: To where the user should be returned after
            successfull log in.
        :param binding: Which binding to use for sending the request
        :param log: Where to write log messages
        :param vorg: The entity_id of the virtual organization I'm a member of
        :param scoping: For which IdPs this query are aimed.
        :param sign: Whether the request should be signed or not.
        :return: AuthnRequest response
        """

        location = self._sso_location(entityid)
        session_id = sid()

        _req_str = "%s" % self.authn(location, session_id, vorg, scoping, log, sign)

        if log:
            log.info("AuthNReq: %s" % _req_str)

        if binding == saml2.BINDING_HTTP_POST:
            # No valid ticket; Send a form to the client
            # THIS IS NOT TO BE USED RIGHT NOW
            if log:
                log.info("HTTP POST")
            (head, response) = http_post_message(_req_str, location, relay_state)
        elif binding == saml2.BINDING_HTTP_REDIRECT:
            if log:
                log.info("HTTP REDIRECT")
            (head, _body) = http_redirect_message(_req_str, location, relay_state)
            response = head[0]
        else:
            raise Exception("Unkown binding type: %s" % binding)
        return session_id, response
Example #4
0
    def http_redirect_logout_request(self, get, subject_id):
        """ Deal with a LogoutRequest received through HTTP redirect

        :param get: The request as a dictionary 
        :param subject_id: the id of the current logged user
        :return: a tuple with a list of header tuples (presently only location)
            and a status which will be True in case of success or False 
            otherwise.
        """
        headers = []
        success = False

        try:
            saml_request = get['SAMLRequest']
        except KeyError:
            return None

        if saml_request:
            xml = decode_base64_and_inflate(saml_request)

            request = samlp.logout_request_from_string(xml)
            logger.debug(request)

            if request.name_id.text == subject_id:
                status = samlp.STATUS_SUCCESS
                success = self.local_logout(subject_id)
            else:
                status = samlp.STATUS_REQUEST_DENIED

            response, destination = self.make_logout_response(
                request.issuer.text, request.id, status)

            logger.info("RESPONSE: {0:>s}".format(response))

            if 'RelayState' in get:
                rstate = get['RelayState']
            else:
                rstate = ""

            (headers, _body) = http_redirect_message(str(response),
                                                     destination, rstate,
                                                     'SAMLResponse')

        return headers, success
Example #5
0
    def authenticate(self,
                     entityid=None,
                     relay_state="",
                     binding=saml2.BINDING_HTTP_REDIRECT,
                     vorg="",
                     scoping=None,
                     sign=None):
        """ Makes an authentication request.

        :param entityid: The entity ID of the IdP to send the request to
        :param relay_state: To where the user should be returned after
            successfull log in.
        :param binding: Which binding to use for sending the request
        :param vorg: The entity_id of the virtual organization I'm a member of
        :param scoping: For which IdPs this query are aimed.
        :param sign: Whether the request should be signed or not.
        :return: AuthnRequest response
        """

        location = self._sso_location(entityid, binding)
        session_id = sid()

        _req_str = "%s" % self.authn(location, session_id, vorg, scoping, sign)

        logger.info("AuthNReq: %s" % _req_str)

        if binding == saml2.BINDING_HTTP_POST:
            # No valid ticket; Send a form to the client
            # THIS IS NOT TO BE USED RIGHT NOW
            logger.info("HTTP POST")
            (head, response) = http_post_message(_req_str, location,
                                                 relay_state)
        elif binding == saml2.BINDING_HTTP_REDIRECT:
            logger.info("HTTP REDIRECT")
            (head, _body) = http_redirect_message(_req_str, location,
                                                  relay_state)
            response = head[0]
        else:
            raise Exception("Unkown binding type: %s" % binding)
        return session_id, response
Example #6
0
    def _logout(self, subject_id, entity_ids, reason, expire, sign=None, log=None, return_to="/"):

        # 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[:]
        response = False
        if log is None:
            log = self.logger

        for entity_id in entity_ids:
            response = False

            for binding in [BINDING_SOAP, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT]:
                destinations = self.config.single_logout_services(entity_id, binding)
                if not destinations:
                    continue

                destination = destinations[0]

                if log:
                    log.info("destination to provider: %s" % destination)
                request = self.construct_logout_request(subject_id, destination, entity_id, reason, expire)

                to_sign = []
                # if sign and binding != BINDING_HTTP_REDIRECT:

                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)]

                if log:
                    log.info("REQUEST: %s" % request)

                request = signed_instance_factory(request, self.sec, to_sign)

                if binding == BINDING_SOAP:
                    response = send_using_soap(
                        request,
                        destination,
                        self.config.key_file,
                        self.config.cert_file,
                        log=log,
                        ca_certs=self.config.ca_certs,
                    )
                    if response:
                        if log:
                            log.info("Verifying response")
                        response = self.logout_response(response, log)

                    if response:
                        not_done.remove(entity_id)
                        if log:
                            log.info("OK response from %s" % destination)
                    else:
                        if log:
                            log.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,
                        "return_to": return_to,
                    }

                    if binding == BINDING_HTTP_POST:
                        (head, body) = http_post_message(request, destination, rstate)
                        code = "200 OK"
                    else:
                        (head, body) = http_redirect_message(request, destination, rstate)
                        code = "302 Found"

                    return session_id, code, head, body

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

        return 0, "", [], response
Example #7
0
    def logout_response(self, request, bindings, status=None, sign=False, issuer=None):
        """ Create a LogoutResponse. What is returned depends on which binding
        is used.
        
        :param request: The request this is a response to
        :param bindings: Which bindings that can be used to send the response
        :param status: The return status of the response operation
        :param issuer: The issuer of the message
        :return: A 3-tuple consisting of HTTP return code, HTTP headers and 
            possibly a message.
        """
        sp_entity_id = request.issuer.text.strip()

        binding = None
        destinations = []
        for binding in bindings:
            destinations = self.conf.single_logout_services(sp_entity_id, binding)
            if destinations:
                break

        if not destinations:
            if self.log:
                self.log.error("Not way to return a response !!!")
            return ("412 Precondition Failed", [("Content-type", "text/html")], ["No return way defined"])

        # Pick the first
        destination = destinations[0]

        if self.log:
            self.log.info("Logout Destination: %s, binding: %s" % (destination, binding))
        if not status:
            status = success_status_factory()

        mid = sid()
        rcode = "200 OK"

        # response and packaging differs depending on binding

        if binding == BINDING_SOAP:
            response = logoutresponse_factory(sign=sign, id=mid, in_response_to=request.id, status=status)
            if sign:
                to_sign = [(class_name(response), mid)]
                response = signed_instance_factory(response, self.sec, to_sign)

            (headers, message) = http_soap_message(response)
        else:
            _issuer = self.issuer(issuer)
            response = logoutresponse_factory(
                sign=sign,
                id=mid,
                in_response_to=request.id,
                status=status,
                issuer=_issuer,
                destination=destination,
                sp_entity_id=sp_entity_id,
                instant=instant(),
            )
            if sign:
                to_sign = [(class_name(response), mid)]
                response = signed_instance_factory(response, self.sec, to_sign)

            if self.log:
                self.log.info("Response: %s" % (response,))
            if binding == BINDING_HTTP_REDIRECT:
                (headers, message) = http_redirect_message(response, destination, typ="SAMLResponse")
                rcode = "302 Found"
            else:
                (headers, message) = http_post_message(response, destination, typ="SAMLResponse")

        return rcode, headers, message
Example #8
0
    def logout_response(self, request, bindings, status=None, sign=False,
                        issuer=None):
        """ Create a LogoutResponse. What is returned depends on which binding
        is used.
        
        :param request: The request this is a response to
        :param bindings: Which bindings that can be used to send the response
        :param status: The return status of the response operation
        :param issuer: The issuer of the message
        :return: A 3-tuple consisting of HTTP return code, HTTP headers and 
            possibly a message.
        """
        sp_entity_id = request.issuer.text.strip()
        
        binding = None
        destinations = []
        for binding in bindings:
            destinations = self.conf.single_logout_services(sp_entity_id,
                                                           binding)
            if destinations:
                break
                

        if not destinations:
            logger.error("Not way to return a response !!!")
            return ("412 Precondition Failed",
                    [("Content-type", "text/html")],
                    ["No return way defined"])
        
        # Pick the first
        destination = destinations[0]

        logger.info("Logout Destination: %s, binding: %s" % (destination,
                                                                    binding))
        if not status: 
            status = success_status_factory()

        mid = sid()
        rcode = "200 OK"
        
        # response and packaging differs depending on binding
        
        if binding == BINDING_SOAP:
            response = logoutresponse_factory(
                                sign=sign,
                                id = mid,
                                in_response_to = request.id,
                                status = status,
                                )
            if sign:
                to_sign = [(class_name(response), mid)]
                response = signed_instance_factory(response, self.sec, to_sign)
                
            (headers, message) = http_soap_message(response)
        else:
            _issuer = self.issuer(issuer)
            response = logoutresponse_factory(
                                sign=sign,
                                id = mid,
                                in_response_to = request.id,
                                status = status,
                                issuer = _issuer,
                                destination = destination,
                                sp_entity_id = sp_entity_id,
                                instant=instant(),
                                )
            if sign:
                to_sign = [(class_name(response), mid)]
                response = signed_instance_factory(response, self.sec, to_sign)

            logger.info("Response: %s" % (response,))
            if binding == BINDING_HTTP_REDIRECT:
                (headers, message) = http_redirect_message(response, 
                                                            destination, 
                                                            typ="SAMLResponse")
                rcode = "302 Found"
            else:
                (headers, message) = http_post_message(response, destination,
                                                        typ="SAMLResponse")
                
        return rcode, headers, message
Example #9
0
    def _logout(self,
                subject_id,
                entity_ids,
                reason,
                expire,
                sign=None,
                return_to="/"):

        # 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[:]
        response = False

        for entity_id in entity_ids:
            response = False

            for binding in [
                    BINDING_SOAP, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT
            ]:
                destinations = self.config.single_logout_services(
                    entity_id, binding)
                if not destinations:
                    continue

                destination = destinations[0]

                logger.info("destination to provider: %s" % destination)
                request = self.construct_logout_request(
                    subject_id, destination, entity_id, reason, expire)

                to_sign = []
                #if sign and binding != BINDING_HTTP_REDIRECT:

                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)

                request = signed_instance_factory(request, self.sec, to_sign)

                if binding == BINDING_SOAP:
                    response = send_using_soap(request,
                                               destination,
                                               self.config.key_file,
                                               self.config.cert_file,
                                               ca_certs=self.config.ca_certs)
                    if response:
                        logger.info("Verifying response")
                        response = self.logout_response(response)

                    if response:
                        not_done.remove(entity_id)
                        logger.info("OK response from %s" % destination)
                    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,
                        "return_to": return_to
                    }

                    if binding == BINDING_HTTP_POST:
                        (head,
                         body) = http_post_message(request, destination,
                                                   rstate)
                        code = "200 OK"
                    else:
                        (head,
                         body) = http_redirect_message(request, destination,
                                                       rstate)
                        code = "302 Found"

                    return session_id, code, head, body

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

        return 0, "", [], response