Exemplo n.º 1
0
 def handle_Response(self, rsp, binding, relay_state):
     """handle a positive response."""
     rid, ok, fail = self._resolve_relay_state(relay_state)
     if rid != rsp.InResponseTo:
         raise SamlError("relay state and response to not match: %s" % rid)
     if binding == "redirect":
         raise SamlError(
             "We do not accept responses via redirect (as we do not yet support signatures for redirect"
         )
     context = ResponseContext(
         rsp,
         binding,
         self.REQUEST,
         self._get_authority(),
     )
     # check whether this is a success or failure
     if rsp.Status.StatusCode.Value != rsp.PREFIX + "Success":
         # this is a failure
         logger.error("failed SAML2 request: %s" % rsp.toxml())
         if fail is None:
             raise SystemError("unsolicited SAML failure response")
         return self.REQUEST.response.redirect(fail)
     # success response
     for ass in rsp.Assertion:
         self._process_assertion(ass, context)
     return self.REQUEST.response.redirect(ok)
Exemplo n.º 2
0
    def handle_Response(self, rsp, binding, relay_state):
        """handle a positive response."""
        rid, ok, fail = self._resolve_relay_state(relay_state)
        if rid != rsp.InResponseTo:
            raise SamlError("relay state and response to not match: %s" % rid)
        if binding == "redirect":
            raise SamlError(
                "We do not accept responses via redirect (as we do not yet support signatures for redirect"
            )
        context = ResponseContext(
            rsp,
            binding,
            self.REQUEST,
            self._get_authority(),
        )
        # check whether this is a success or failure
        if rsp.Status.StatusCode.Value != rsp.PREFIX + "Success":
            # this is a failure
            logger.error("failed SAML2 request: %s" % rsp.toxml())
            if fail is None:
                raise SystemError("unsolicited SAML failure response")
            return self.REQUEST.response.redirect(fail)
        # success response
        for ass in rsp.Assertion:
            self._process_assertion(ass, context)

        # Do not send back to the login (infinity loop) and do not send the user
        # out of the portal
        pu_tool = getToolByName(self, "portal_url")
        purl = pu_tool()
        if '/login' in ok or not pu_tool.isURLInPortal(ok):
            ok = purl

        return self.REQUEST.response.redirect(ok)
Exemplo n.º 3
0
 def _process_assertion(self, ass, context):
     # ensure we know the issuer -- an exception results, if not
     eid = ass.Issuer.value()
     auth = self._get_authority()
     auth.metadata_by_id(eid)
     if not ass.verified_signature():
         raise SamlError("assertion %s was not signed by the issuer" %
                         ass.ID)
     if not ass.is_valid(context):
         raise SamlError("assertion %s is not valid" % ass.ID)
     if context.binding == "post":
         # check the `SubjectConfirmation` condition of the `Browser SSO profile`
         ok = False
         for sc in ass.Subject.SubjectConfirmation:
             scm = sc.Method
             if scm != "urn:oasis:names:tc:SAML:2.0:cm:bearer":
                 # we do not understand the method -- hope, we find one we understand
                 continue
             scd = sc.SubjectConfirmationData
             if scd is None: continue  # not valid
             if scd.NotOnOrAfter is None or utcnow() >= as_utc(
                     scd.NotOnOrAfter):
                 continue  # not valid
             if scd.Recipient != context.rsp.Destination:
                 continue  # not valid
             if scd.InResponseTo != context.rsp.InResponseTo:
                 continue  # not valid
             # XXX Jazkarta Note: NYU is on a private network, so we get an address
             # mismatch here. At the point at which we want to remove
             # client-specific customizations, we'll have to monkey patch this
             # method or find some other means of overriding this check:
             # if scd.Address and context.zrequest.getClientAddr() != scd.Address:
             #   continue # not valid
             ok = True
             break
         if not ok:
             raise SamlError(
                 "subject confirmation in assertion %s is invalid" % ass.ID)
     subject = ass.Subject.NameID
     for tag in ("Statement", "AuthnStatement", "AuthzDecisionStatement",
                 "AttributeStatement"):
         for s in getattr(ass, tag):
             # process statement -- "AttributeError", if we do not support its type
             getattr(self, "_process_" + tag)(subject, s)
Exemplo n.º 4
0
    def resolve(self, auth, req, rsp=None):
        """update the instance to have defined *url* and *binding*.

    Note: There is probably no need for *rsp*.
    """
        __traceback_info__ = self.__dict__
        eid = self.eid
        if eid is None and req is not None: eid = self.eid = req.Issuer.value()
        rd = self.get_role_descriptor(auth)
        if self.url:
            # the url has been directly specified.
            # SAML2 demands to verify that the url indeed belongs to the authority
            #  We try to ensure that be enforcing that the host in url
            #  is identical to the host part of one of the service endpoints
            #  used by this role.
            from dm.saml2.util import child_values
            from dm.saml2.pyxb.metadata import EndpointType
            from urlparse import urlsplit
            target_host = urlsplit(self.url)[1]
            ok = False
            for c in child_values(rd):
                if not hasattr(c, "__iter__"):
                    continue  # all endpoints are repeatable
                for ep in c:
                    if not isinstance(ep, EndpointType):
                        break  # if the first value is not endpoint, none will be
                    # should we consider `ResponseLocation` as well?
                    if target_host == urlsplit(ep.Location)[1]:
                        ok = True
                        break
                if ok: break
            if not ok:
                raise SamlError("`%s` does not belong to authority `%s`" %
                                (self.url, eid))
            if self.binding is None:
                # The request has specified a serice url but no
                #  protocol binding. Try post (the only binding
                #  we currently support for authentication responses)
                #  Alternatively, we may try the url to determine
                #  the binding from metadata
                self.binding = HttpPostBinding
        else:
            # `endpoint` must have been specified
            epd = getattr(rd, self.endpoint)
            # we may have more than a single endpoint
            if self.binding:
                # retrict by the requested binding
                epd = [d for d in epd if d.Binding == self.binding]
            epi = self.endpoint_index
            if epi is not None:
                # this requires the endpoints to be indexed endpoints
                epd = [d for d in epd if d.index == epi]
            if len(epd) > 1:
                # we still have more than a single candidate - select on `isDefault`
                epd = [d for d in epd if getattr(d, "isDefault", True)]
            if not epd:
                raise SamlError("no appropriate endpoint found: " +
                                str((eid, self.endpoint, self.binding,
                                     self.endpoint_index)))
            epd = epd[0]
            self.binding = epd.Binding
            self.url = rsp is not None and epd.ResponseLocation or epd.Location
        # determine whether we should sign the assertions
        # Note: SAML requires that assertions are signed in the
        #   HTTP-Post binding.
        if not self.sign_ass and self.sign_ass_attr:
            self.sign_ass = getattr(rd, self.sign_ass_attr)
        self.sign_ass = self.sign_ass or self.binding == HttpPostBinding
        # determine whether we should sign the message
        if not self.sign_msg and self.sign_msg_attr:
            self.sign_msg = getattr(rd, self.sign_msg_attr)