Ejemplo n.º 1
0
    def handle_logout_response(self, response, sign_alg=None, digest_alg=None):
        """ handles a Logout response

        :param response: A response.Response instance
        :return: 4-tuple of (session_id of the last sent logout request,
            response message, response headers and message)
        """

        logger.info("state: %s", self.state)
        status = self.state[response.in_response_to]
        logger.info("status: %s", status)
        issuer = response.issuer()
        logger.info("issuer: %s", issuer)
        del self.state[response.in_response_to]
        if status["entity_ids"] == [issuer]:  # done
            self.local_logout(decode(status["name_id"]))
            return 0, "200 Ok", [("Content-type", "text/html")], []
        else:
            status["entity_ids"].remove(issuer)
            if "sign_alg" in status:
                sign_alg = status["sign_alg"]
            return self.do_logout(decode(status["name_id"]),
                                  status["entity_ids"],
                                  status["reason"],
                                  status["not_on_or_after"],
                                  status["sign"],
                                  sign_alg=sign_alg,
                                  digest_alg=digest_alg)
Ejemplo n.º 2
0
    def handle_logout_response(self, response, sign_alg=None, digest_alg=None):
        """ handles a Logout response

        :param response: A response.Response instance
        :return: 4-tuple of (session_id of the last sent logout request,
            response message, response headers and message)
        """

        logger.info("state: %s", self.state)
        status = self.state[response.in_response_to]
        logger.info("status: %s", status)
        issuer = response.issuer()
        logger.info("issuer: %s", issuer)
        del self.state[response.in_response_to]
        if status["entity_ids"] == [issuer]:  # done
            self.local_logout(decode(status["name_id"]))
            return 0, "200 Ok", [("Content-type", "text/html")], []
        else:
            status["entity_ids"].remove(issuer)
            if "sign_alg" in status:
                sign_alg = status["sign_alg"]
            return self.do_logout(decode(status["name_id"]),
                                  status["entity_ids"],
                                  status["reason"], status["not_on_or_after"],
                                  status["sign"], sign_alg=sign_alg,
                                  digest_alg=digest_alg)
Ejemplo n.º 3
0
    def global_logout(self, name_id, reason="", expire=None, sign=None,
                      sign_alg=None, digest_alg=None):
        """ More or less a layer of indirection :-/
        Bootstrapping the whole thing by finding all the IdPs that should
        be notified.

        :param name_id: The identifier of the subject that wants to be
            logged out.
        :param reason: Why the subject wants to log out
        :param expire: The latest the log out should happen.
            If this time has passed don't bother.
        :param sign: Whether the request should be signed or not.
            This also depends on what binding is used.
        :return: Depends on which binding is used:
            If the HTTP redirect binding then a HTTP redirect,
            if SOAP binding has been used the just the result of that
            conversation.
        """

        if isinstance(name_id, six.string_types):
            name_id = decode(name_id)

        logger.info("logout request for: %s", name_id)

        # find out which IdPs/AAs I should notify
        entity_ids = self.users.issuers_of_info(name_id)
        return self.do_logout(name_id, entity_ids, reason, expire, sign,
                              sign_alg=sign_alg, digest_alg=digest_alg)
Ejemplo n.º 4
0
 def authenticate(self, environ, identity=None):
     if identity:
         if (identity.get("user") and environ.get("s2repoze.sessioninfo")
                 and identity.get("user")
                 == environ.get("s2repoze.sessioninfo").get("ava")):
             return identity.get("login")
         tktuser = identity.get("repoze.who.plugins.auth_tkt.userid", None)
         if tktuser and self.saml_client.is_logged_in(decode(tktuser)):
             return tktuser
         return None
     else:
         return None
Ejemplo n.º 5
0
 def authenticate(self, environ, identity=None):
     if identity:
         if (
             identity.get("user")
             and environ.get("s2repoze.sessioninfo")
             and identity.get("user")
             == environ.get("s2repoze.sessioninfo").get("ava")
         ):
             return identity.get("login")
         tktuser = identity.get("repoze.who.plugins.auth_tkt.userid", None)
         if tktuser and self.saml_client.is_logged_in(decode(tktuser)):
             return tktuser
         return None
     else:
         return None
Ejemplo n.º 6
0
    def add_metadata(self, environ, identity):
        """ Add information to the knowledge I have about the user """
        name_id = identity["repoze.who.userid"]
        if isinstance(name_id, six.string_types):
            try:
                # Make sure that userids authenticated by another plugin
                # don't cause problems here.
                name_id = decode(name_id)
            except:
                pass

        _cli = self.saml_client
        logger.debug("[add_metadata] for %s", name_id)
        try:
            logger.debug("Issuers: %s", _cli.users.sources(name_id))
        except KeyError:
            pass

        if "user" not in identity:
            identity["user"] = {}
        try:
            (ava, _) = _cli.users.get_identity(name_id)
            # now = time.gmtime()
            logger.debug("[add_metadata] adds: %s", ava)
            identity["user"].update(ava)
        except KeyError:
            pass

        if "pysaml2_vo_expanded" not in identity and _cli.vorg:
            # is this a Virtual Organization situation
            for vo in _cli.vorg.values():
                try:
                    if vo.do_aggregation(name_id):
                        # Get the extended identity
                        identity["user"] = _cli.users.get_identity(name_id)[0]
                        # Only do this once, mark that the identity has been
                        # expanded
                        identity["pysaml2_vo_expanded"] = 1
                except KeyError:
                    logger.exception(
                        "Failed to do attribute aggregation, "
                        "missing common attribute"
                    )
        logger.debug("[add_metadata] returns: %s", dict(identity))

        if not identity["user"]:
            # remove cookie and demand re-authentication
            pass
Ejemplo n.º 7
0
    def add_metadata(self, environ, identity):
        """ Add information to the knowledge I have about the user """
        name_id = identity["repoze.who.userid"]
        if isinstance(name_id, six.string_types):
            try:
                # Make sure that userids authenticated by another plugin
                # don't cause problems here.
                name_id = decode(name_id)
            except:
                pass

        _cli = self.saml_client
        logger.debug("[add_metadata] for %s", name_id)
        try:
            logger.debug("Issuers: %s", _cli.users.sources(name_id))
        except KeyError:
            pass

        if "user" not in identity:
            identity["user"] = {}
        try:
            (ava, _) = _cli.users.get_identity(name_id)
            # now = time.gmtime()
            logger.debug("[add_metadata] adds: %s", ava)
            identity["user"].update(ava)
        except KeyError:
            pass

        if "pysaml2_vo_expanded" not in identity and _cli.vorg:
            # is this a Virtual Organization situation
            for vo in _cli.vorg.values():
                try:
                    if vo.do_aggregation(name_id):
                        # Get the extended identity
                        identity["user"] = _cli.users.get_identity(name_id)[0]
                        # Only do this once, mark that the identity has been
                        # expanded
                        identity["pysaml2_vo_expanded"] = 1
                except KeyError:
                    logger.exception("Failed to do attribute aggregation, "
                                     "missing common attribute")
        logger.debug("[add_metadata] returns: %s", dict(identity))

        if not identity["user"]:
            # remove cookie and demand re-authentication
            pass
Ejemplo n.º 8
0
    def get(self, name_id, entity_id, check_not_on_or_after=True):
        """ Get session information about a subject gotten from a
        specified IdP/AA.

        :param name_id: The subject identifier, a NameID instance
        :param entity_id: The identifier of the entity_id
        :param check_not_on_or_after: if True it will check if this
             subject is still valid or if it is too old. Otherwise it
             will not check this. True by default.
        :return: The session information
        """
        cni = code(name_id)
        (timestamp, info) = self._db[cni][entity_id]
        info = info.copy()
        if check_not_on_or_after and time_util.after(timestamp):
            raise ToOld("past %s" % str(timestamp))

        if 'name_id' in info and isinstance(info['name_id'], six.string_types):
            info['name_id'] = decode(info['name_id'])
        return info or None
Ejemplo n.º 9
0
    def global_logout(self,
                      name_id,
                      reason="",
                      expire=None,
                      sign=None,
                      sign_alg=None,
                      digest_alg=None):
        """ More or less a layer of indirection :-/
        Bootstrapping the whole thing by finding all the IdPs that should
        be notified.

        :param name_id: The identifier of the subject that wants to be
            logged out.
        :param reason: Why the subject wants to log out
        :param expire: The latest the log out should happen.
            If this time has passed don't bother.
        :param sign: Whether the request should be signed or not.
            This also depends on what binding is used.
        :return: Depends on which binding is used:
            If the HTTP redirect binding then a HTTP redirect,
            if SOAP binding has been used the just the result of that
            conversation.
        """

        if isinstance(name_id, six.string_types):
            name_id = decode(name_id)

        logger.info("logout request for: %s", name_id)

        # find out which IdPs/AAs I should notify
        entity_ids = self.users.issuers_of_info(name_id)
        return self.do_logout(name_id,
                              entity_ids,
                              reason,
                              expire,
                              sign,
                              sign_alg=sign_alg,
                              digest_alg=digest_alg)
Ejemplo n.º 10
0
    def clean_out_user(self, name_id):
        """
        Remove all authentication statements that belongs to a user identified
        by a NameID instance

        :param name_id: NameID instance
        :return: The local identifier for the user
        """

        lid = self.ident.find_local_id(name_id)
        logger.info("Clean out %s", lid)

        # remove the authentications
        try:
            for _nid in [decode(x) for x in self.ident.db[lid].split(" ")]:
                try:
                    self.session_db.remove_authn_statements(_nid)
                except KeyError:
                    pass
        except KeyError:
            pass

        return lid
Ejemplo n.º 11
0
    def clean_out_user(self, name_id):
        """
        Remove all authentication statements that belongs to a user identified
        by a NameID instance

        :param name_id: NameID instance
        :return: The local identifier for the user
        """

        lid = self.ident.find_local_id(name_id)
        logger.info("Clean out %s", lid)

        # remove the authentications
        try:
            for _nid in [decode(x) for x in self.ident.db[lid].split(" ")]:
                try:
                    self.session_db.remove_authn_statements(_nid)
                except KeyError:
                    pass
        except KeyError:
            pass

        return lid
Ejemplo n.º 12
0
    def challenge(self, environ, _status, _app_headers, _forget_headers):

        _cli = self.saml_client

        if "REMOTE_USER" in environ:
            name_id = decode(environ["REMOTE_USER"])

            _cli = self.saml_client
            path_info = environ["PATH_INFO"]

            if "samlsp.logout" in environ:
                responses = _cli.global_logout(name_id)
                return self._handle_logout(responses)

        if "samlsp.pending" in environ:
            response = environ["samlsp.pending"]
            if isinstance(response, HTTPRedirection):
                response.headers += _forget_headers
            return response

        # logger = environ.get('repoze.who.logger','')

        # Which page was accessed to get here
        came_from = construct_came_from(environ)
        environ["myapp.came_from"] = came_from
        logger.debug("[sp.challenge] RelayState >> '%s'", came_from)

        # Am I part of a virtual organization or more than one ?
        try:
            vorg_name = environ["myapp.vo"]
        except KeyError:
            try:
                vorg_name = _cli.vorg._name
            except AttributeError:
                vorg_name = ""

        logger.info("[sp.challenge] VO: %s", vorg_name)

        # If more than one idp and if none is selected, I have to do wayf
        (done, response) = self._pick_idp(environ, came_from)
        # Three cases: -1 something went wrong or Discovery service used
        #               0 I've got an IdP to send a request to
        #               >0 ECP in progress
        logger.debug("_idp_pick returned: %s", done)
        if done == -1:
            return response
        elif done > 0:
            self.outstanding_queries[done] = came_from
            return ECP_response(response)
        else:
            entity_id = response
            logger.info("[sp.challenge] entity_id: %s", entity_id)
            # Do the AuthnRequest
            _binding = BINDING_HTTP_REDIRECT
            try:
                srvs = _cli.metadata.single_sign_on_service(entity_id, _binding)
                logger.debug("srvs: %s", srvs)
                dest = srvs[0]["location"]
                logger.debug("destination: %s", dest)

                extensions = None
                cert = None

                if _cli.config.generate_cert_func is not None:
                    cert_str, req_key_str = _cli.config.generate_cert_func()
                    cert = {"cert": cert_str, "key": req_key_str}
                    spcertenc = SPCertEnc(
                        x509_data=ds.X509Data(
                            x509_certificate=ds.X509Certificate(text=cert_str)
                        )
                    )
                    extensions = Extensions(
                        extension_elements=[element_to_extension_element(spcertenc)]
                    )

                if _cli.authn_requests_signed:
                    _sid = saml2_tophat.s_utils.sid()
                    req_id, msg_str = _cli.create_authn_request(
                        dest,
                        vorg=vorg_name,
                        sign=_cli.authn_requests_signed,
                        message_id=_sid,
                        extensions=extensions,
                    )
                    _sid = req_id
                else:
                    req_id, req = _cli.create_authn_request(
                        dest, vorg=vorg_name, sign=False, extensions=extensions
                    )
                    msg_str = "%s" % req
                    _sid = req_id

                if cert is not None:
                    self.outstanding_certs[_sid] = cert

                ht_args = _cli.apply_binding(
                    _binding,
                    msg_str,
                    destination=dest,
                    relay_state=came_from,
                    sign=_cli.authn_requests_signed,
                )

                logger.debug("ht_args: %s", ht_args)
            except Exception as exc:
                logger.exception(exc)
                raise Exception("Failed to construct the AuthnRequest: %s" % exc)

            try:
                ret = _cli.config.getattr("endpoints", "sp")["discovery_response"][0][0]
                if (environ["PATH_INFO"]) in ret and ret.split(environ["PATH_INFO"])[
                    1
                ] == "":
                    query = parse_qs(environ["QUERY_STRING"])
                    sid = query["sid"][0]
                    came_from = self.outstanding_queries[sid]
            except:
                pass
            # remember the request
            self.outstanding_queries[_sid] = came_from

            if not ht_args["data"] and ht_args["headers"][0][0] == "Location":
                logger.debug("redirect to: %s", ht_args["headers"][0][1])
                return HTTPSeeOther(headers=ht_args["headers"])
            else:
                return ht_args["data"]
Ejemplo n.º 13
0
    def subjects(self):
        """ Return identifiers for all the subjects that are in the cache.

        :return: list of subject identifiers
        """
        return [decode(c) for c in self._db.keys()]
Ejemplo n.º 14
0
    def challenge(self, environ, _status, _app_headers, _forget_headers):

        _cli = self.saml_client

        if "REMOTE_USER" in environ:
            name_id = decode(environ["REMOTE_USER"])

            _cli = self.saml_client
            path_info = environ["PATH_INFO"]

            if "samlsp.logout" in environ:
                responses = _cli.global_logout(name_id)
                return self._handle_logout(responses)

        if "samlsp.pending" in environ:
            response = environ["samlsp.pending"]
            if isinstance(response, HTTPRedirection):
                response.headers += _forget_headers
            return response

        # logger = environ.get('repoze.who.logger','')

        # Which page was accessed to get here
        came_from = construct_came_from(environ)
        environ["myapp.came_from"] = came_from
        logger.debug("[sp.challenge] RelayState >> '%s'", came_from)

        # Am I part of a virtual organization or more than one ?
        try:
            vorg_name = environ["myapp.vo"]
        except KeyError:
            try:
                vorg_name = _cli.vorg._name
            except AttributeError:
                vorg_name = ""

        logger.info("[sp.challenge] VO: %s", vorg_name)

        # If more than one idp and if none is selected, I have to do wayf
        (done, response) = self._pick_idp(environ, came_from)
        # Three cases: -1 something went wrong or Discovery service used
        #               0 I've got an IdP to send a request to
        #               >0 ECP in progress
        logger.debug("_idp_pick returned: %s", done)
        if done == -1:
            return response
        elif done > 0:
            self.outstanding_queries[done] = came_from
            return ECP_response(response)
        else:
            entity_id = response
            logger.info("[sp.challenge] entity_id: %s", entity_id)
            # Do the AuthnRequest
            _binding = BINDING_HTTP_REDIRECT
            try:
                srvs = _cli.metadata.single_sign_on_service(
                    entity_id, _binding)
                logger.debug("srvs: %s", srvs)
                dest = srvs[0]["location"]
                logger.debug("destination: %s", dest)

                extensions = None
                cert = None

                if _cli.config.generate_cert_func is not None:
                    cert_str, req_key_str = _cli.config.generate_cert_func()
                    cert = {"cert": cert_str, "key": req_key_str}
                    spcertenc = SPCertEnc(x509_data=ds.X509Data(
                        x509_certificate=ds.X509Certificate(text=cert_str)))
                    extensions = Extensions(extension_elements=[
                        element_to_extension_element(spcertenc)
                    ])

                if _cli.authn_requests_signed:
                    _sid = saml2_tophat.s_utils.sid()
                    req_id, msg_str = _cli.create_authn_request(
                        dest,
                        vorg=vorg_name,
                        sign=_cli.authn_requests_signed,
                        message_id=_sid,
                        extensions=extensions,
                    )
                    _sid = req_id
                else:
                    req_id, req = _cli.create_authn_request(
                        dest,
                        vorg=vorg_name,
                        sign=False,
                        extensions=extensions)
                    msg_str = "%s" % req
                    _sid = req_id

                if cert is not None:
                    self.outstanding_certs[_sid] = cert

                ht_args = _cli.apply_binding(
                    _binding,
                    msg_str,
                    destination=dest,
                    relay_state=came_from,
                    sign=_cli.authn_requests_signed,
                )

                logger.debug("ht_args: %s", ht_args)
            except Exception as exc:
                logger.exception(exc)
                raise Exception("Failed to construct the AuthnRequest: %s" %
                                exc)

            try:
                ret = _cli.config.getattr("endpoints",
                                          "sp")["discovery_response"][0][0]
                if (environ["PATH_INFO"]) in ret and ret.split(
                        environ["PATH_INFO"])[1] == "":
                    query = parse_qs(environ["QUERY_STRING"])
                    sid = query["sid"][0]
                    came_from = self.outstanding_queries[sid]
            except:
                pass
            # remember the request
            self.outstanding_queries[_sid] = came_from

            if not ht_args["data"] and ht_args["headers"][0][0] == "Location":
                logger.debug("redirect to: %s", ht_args["headers"][0][1])
                return HTTPSeeOther(headers=ht_args["headers"])
            else:
                return ht_args["data"]