def do(self, response, binding, relay_state="", mtype="response"): """ :param response: The SAML response, transport encoded :param binding: Which binding the query came in over """ # tmp_outstanding_queries = dict(self.outstanding_queries) if not response: logger.info("Missing Response") resp = Unauthorized("Unknown user") return resp(self.environ, self.start_response) try: conv_info = { "remote_addr": self.environ["REMOTE_ADDR"], "request_uri": self.environ["REQUEST_URI"], "entity_id": self.sp.config.entityid, "endpoints": self.sp.config.getattr("endpoints", "sp"), } self.response = self.sp.parse_authn_request_response( response, binding, self.outstanding_queries, self.cache.outstanding_certs, conv_info=conv_info, ) except UnknownPrincipal as excp: logger.error("UnknownPrincipal: %s", excp) resp = ServiceError("UnknownPrincipal: %s" % (excp, )) return resp(self.environ, self.start_response) except UnsupportedBinding as excp: logger.error("UnsupportedBinding: %s", excp) resp = ServiceError("UnsupportedBinding: %s" % (excp, )) return resp(self.environ, self.start_response) except VerificationError as err: resp = ServiceError("Verification error: %s" % (err, )) return resp(self.environ, self.start_response) except SignatureError as err: resp = ServiceError("Signature error: %s" % (err, )) return resp(self.environ, self.start_response) except Exception as err: resp = ServiceError("Other error: %s" % (err, )) return resp(self.environ, self.start_response) logger.info("AVA: %s", self.response.ava) user = User(self.response.name_id, self.response.ava, self.response) cookie = self.cache.set_cookie(user) resp = Redirect("/", headers=[cookie]) return resp(self.environ, self.start_response)
def do(self, request, binding, relay_state=""): logger.info("--- Single Log Out Service ---") try: _, body = request.split("\n") logger.debug("req: '%s'", body) req_info = IDP.parse_logout_request(body, binding) except Exception as exc: logger.error("Bad request: %s", exc) resp = BadRequest("%s" % exc) return resp(self.environ, self.start_response) msg = req_info.message if msg.name_id: lid = IDP.ident.find_local_id(msg.name_id) logger.info("local identifier: %s", lid) if lid in IDP.cache.user2uid: uid = IDP.cache.user2uid[lid] if uid in IDP.cache.uid2user: del IDP.cache.uid2user[uid] del IDP.cache.user2uid[lid] # remove the authentication try: IDP.session_db.remove_authn_statements(msg.name_id) except KeyError as exc: logger.error("ServiceError: %s", exc) resp = ServiceError("%s" % exc) return resp(self.environ, self.start_response) resp = IDP.create_logout_response(msg, [binding]) try: hinfo = IDP.apply_binding(binding, "%s" % resp, "", relay_state) except Exception as exc: logger.error("ServiceError: %s", exc) resp = ServiceError("%s" % exc) return resp(self.environ, self.start_response) #_tlh = dict2list_of_tuples(hinfo["headers"]) delco = delete_cookie(self.environ, "idpauthn") if delco: hinfo["headers"].append(delco) logger.info("Header: %s", hinfo["headers"]) resp = Response(hinfo["data"], headers=hinfo["headers"]) return resp(self.environ, self.start_response)
def do(self, query, binding_in, relay_state=""): try: resp_args, _resp = self.verify_request(query, binding_in) except UnknownPrincipal as excp: logger.error("UnknownPrincipal: %s", excp) resp = ServiceError("UnknownPrincipal: %s" % (excp, )) return resp(self.environ, self.start_response) except UnsupportedBinding as excp: logger.error("UnsupportedBinding: %s", excp) resp = ServiceError("UnsupportedBinding: %s" % (excp, )) return resp(self.environ, self.start_response) if not _resp: identity = USERS[self.user].copy() #identity["eduPersonTargetedID"] = get_eptid(IDP, query, session) logger.info("Identity: %s", identity) if REPOZE_ID_EQUIVALENT: identity[REPOZE_ID_EQUIVALENT] = self.user try: sign_assertion = IDP.config.getattr("sign_assertion", "idp") if sign_assertion is None: sign_assertion = False _resp = IDP.create_authn_response( identity, userid=self.user, authn=AUTHN_BROKER[self.environ["idp.authn_ref"]], sign_assertion=sign_assertion, sign_response=False, **resp_args) except Exception as excp: logging.error(exception_trace(excp)) resp = ServiceError("Exception: %s" % (excp, )) return resp(self.environ, self.start_response) logger.info("AuthNResponse: %s", _resp) http_args = IDP.apply_binding(self.binding_out, "%s" % _resp, self.destination, relay_state, response=True) logger.debug("HTTPargs: %s", http_args) return self.response(self.binding_out, http_args)
def redirect_to_auth(self, _cli, entity_id, came_from, sigalg=""): try: # Picks a binding to use for sending the Request to the IDP _binding, destination = _cli.pick_binding("single_sign_on_service", self.bindings, "idpsso", entity_id=entity_id) logger.debug("binding: %s, destination: %s", _binding, destination) # Binding here is the response binding that is which binding the # IDP should use to return the response. acs = _cli.config.getattr("endpoints", "sp")["assertion_consumer_service"] # just pick one endp, return_binding = acs[0] 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) ]) req_id, req = _cli.create_authn_request( destination, binding=return_binding, extensions=extensions, nameid_format=NAMEID_FORMAT_PERSISTENT, ) _rstate = rndstr() self.cache.relay_state[_rstate] = came_from ht_args = _cli.apply_binding(_binding, "%s" % req, destination, relay_state=_rstate, sigalg=sigalg) _sid = req_id if cert is not None: self.cache.outstanding_certs[_sid] = cert except Exception as exc: logger.exception(exc) resp = ServiceError("Failed to construct the AuthnRequest: %s" % exc) return resp # remember the request self.cache.outstanding_queries[_sid] = came_from return self.response(_binding, ht_args, do_not_start_response=True)
def logout(environ, start_response, sp): user = CACHE.get_user(environ) if user is None: sso = SSO(sp, environ, start_response, cache=CACHE, **ARGS) return sso.do() logger.info("[logout] subject_id: '%s'", user.name_id) # What if more than one data = sp.global_logout(user.name_id) logger.info("[logout] global_logout > %s", data) for entity_id, logout_info in data.items(): if isinstance(logout_info, tuple): binding, http_info = logout_info if binding == BINDING_HTTP_POST: body = "".join(http_info["data"]) resp = Response(body) return resp(environ, start_response) elif binding == BINDING_HTTP_REDIRECT: for key, value in http_info["headers"]: if key.lower() == "location": resp = Redirect(value) return resp(environ, start_response) resp = ServiceError("missing Location header") return resp(environ, start_response) else: resp = ServiceError("unknown logout binding: %s", binding) return resp(environ, start_response) else: # result from logout, should be OK pass return finish_logout(environ, start_response)
def response(self, binding, http_args): resp = None if binding == BINDING_HTTP_ARTIFACT: resp = Redirect() elif http_args["data"]: resp = Response(http_args["data"], headers=http_args["headers"]) else: for header in http_args["headers"]: if header[0] == "Location": resp = Redirect(header[1]) if not resp: resp = ServiceError("Don't know how to return response") return resp(self.environ, self.start_response)
def response(self, binding, http_args, do_not_start_response=False): if binding == BINDING_HTTP_ARTIFACT: resp = Redirect() elif binding == BINDING_HTTP_REDIRECT: for param, value in http_args["headers"]: if param == "Location": resp = SeeOther(str(value)) break else: resp = ServiceError("Parameter error") else: resp = Response(http_args["data"], headers=http_args["headers"]) if do_not_start_response: return resp else: return resp(self.environ, self.start_response)
def application(environ, start_response): """ The main WSGI application. Dispatch the current request to the functions from above. If nothing matches, call the `not_found` function. :param environ: The HTTP application environment :param start_response: The application to run when the handling of the request is done :return: The response as a list of lines """ path = environ.get("PATH_INFO", "").lstrip("/") logger.debug("<application> PATH: '%s'", path) if path == "metadata": return metadata(environ, start_response) logger.debug("Finding callback to run") try: for regex, spec in urls: match = re.search(regex, path) if match is not None: if isinstance(spec, tuple): callback, func_name, _sp = spec cls = callback(_sp, environ, start_response, cache=CACHE) func = getattr(cls, func_name) return func() else: return spec(environ, start_response, SP) if re.match(".*static/.*", path): return handle_static(environ, start_response, path) return not_found(environ, start_response) except StatusError as err: logging.error("StatusError: %s" % err) resp = BadRequest("%s" % err) return resp(environ, start_response) except Exception as err: # _err = exception_trace("RUN", err) # logging.error(exception_trace("RUN", _err)) print(err, file=sys.stderr) resp = ServiceError("%s" % err) return resp(environ, start_response)
def _pick_idp(self, came_from): """ If more than one idp and if none is selected, I have to do wayf or disco """ _cli = self.sp logger.debug("[_pick_idp] %s", self.environ) if "HTTP_PAOS" in self.environ: if self.environ["HTTP_PAOS"] == PAOS_HEADER_INFO: if MIME_PAOS in self.environ["HTTP_ACCEPT"]: # Where should I redirect the user to # entityid -> the IdP to use # relay_state -> when back from authentication logger.debug("- ECP client detected -") _rstate = rndstr() self.cache.relay_state[_rstate] = geturl(self.environ) _entityid = _cli.config.ecp_endpoint( self.environ["REMOTE_ADDR"]) if not _entityid: return -1, ServiceError("No IdP to talk to") logger.debug("IdP to talk to: %s", _entityid) return ecp.ecp_auth_request(_cli, _entityid, _rstate) else: return -1, ServiceError("Faulty Accept header") else: return -1, ServiceError("unknown ECP version") # Find all IdPs idps = self.sp.metadata.with_descriptor("idpsso") idp_entity_id = None kaka = self.environ.get("HTTP_COOKIE", "") if kaka: try: (idp_entity_id, _) = parse_cookie("ve_disco", "SEED_SAW", kaka) except ValueError: pass except TypeError: pass # Any specific IdP specified in a query part query = self.environ.get("QUERY_STRING") if not idp_entity_id and query: try: _idp_entity_id = dict(parse_qs(query))[self.idp_query_param][0] if _idp_entity_id in idps: idp_entity_id = _idp_entity_id except KeyError: logger.debug("No IdP entity ID in query: %s", query) pass if not idp_entity_id: if self.wayf: if query: try: wayf_selected = dict( parse_qs(query))["wayf_selected"][0] except KeyError: return self._wayf_redirect(came_from) idp_entity_id = wayf_selected else: return self._wayf_redirect(came_from) elif self.discosrv: if query: idp_entity_id = _cli.parse_discovery_service_response( query=self.environ.get("QUERY_STRING")) if not idp_entity_id: sid_ = sid() self.cache.outstanding_queries[sid_] = came_from logger.debug("Redirect to Discovery Service function") eid = _cli.config.entityid ret = _cli.config.getattr("endpoints", "sp")["discovery_response"][0][0] ret += "?sid=%s" % sid_ loc = _cli.create_discovery_service_request( self.discosrv, eid, **{"return": ret}) return -1, SeeOther(loc) elif len(idps) == 1: # idps is a dictionary idp_entity_id = list(idps.keys())[0] elif not len(idps): return -1, ServiceError("Misconfiguration") else: return -1, NotImplemented("No WAYF or DS present!") logger.info("Chosen IdP: '%s'", idp_entity_id) return 0, idp_entity_id
def do(self, request, binding, relay_state="", encrypt_cert=None, **kwargs): logger.info("--- Single Log Out Service ---") try: logger.debug("req: '%s'", request) req_info = IDP.parse_logout_request(request, binding) except Exception as exc: logger.error("Bad request: %s", exc) resp = BadRequest("%s" % exc) return resp(self.environ, self.start_response) msg = req_info.message if msg.name_id: lid = IDP.ident.find_local_id(msg.name_id) logger.info("local identifier: %s", lid) if lid in IDP.cache.user2uid: uid = IDP.cache.user2uid[lid] if uid in IDP.cache.uid2user: del IDP.cache.uid2user[uid] del IDP.cache.user2uid[lid] # remove the authentication try: IDP.session_db.remove_authn_statements(msg.name_id) except KeyError as exc: logger.error("Unknown session: %s", exc) resp = ServiceError("Unknown session: %s", exc) return resp(self.environ, self.start_response) resp = IDP.create_logout_response(msg, [binding]) if binding == BINDING_SOAP: destination = "" response = False else: binding, destination = IDP.pick_binding( "single_logout_service", [binding], "spsso", req_info ) response = True try: hinfo = IDP.apply_binding( binding, "%s" % resp, destination, relay_state, response=response ) except Exception as exc: logger.error("ServiceError: %s", exc) resp = ServiceError("%s" % exc) return resp(self.environ, self.start_response) # _tlh = dict2list_of_tuples(hinfo["headers"]) delco = delete_cookie(self.environ, "idpauthn") if delco: hinfo["headers"].append(delco) logger.info("Header: %s", (hinfo["headers"],)) if binding == BINDING_HTTP_REDIRECT: for key, value in hinfo["headers"]: if key.lower() == "location": resp = Redirect(value, headers=hinfo["headers"]) return resp(self.environ, self.start_response) resp = ServiceError("missing Location header") return resp(self.environ, self.start_response) else: resp = Response(hinfo["data"], headers=hinfo["headers"]) return resp(self.environ, self.start_response)
def do(self, query, binding_in, relay_state="", encrypt_cert=None, **kwargs): """ :param query: The request :param binding_in: Which binding was used when receiving the query :param relay_state: The relay state provided by the SP :param encrypt_cert: Cert to use for encryption :return: A response """ try: resp_args, _resp = self.verify_request(query, binding_in) except UnknownPrincipal as excp: logger.error("UnknownPrincipal: %s", excp) resp = ServiceError("UnknownPrincipal: %s" % (excp,)) return resp(self.environ, self.start_response) except UnsupportedBinding as excp: logger.error("UnsupportedBinding: %s", excp) resp = ServiceError("UnsupportedBinding: %s" % (excp,)) return resp(self.environ, self.start_response) if not _resp: identity = USERS[self.user].copy() # identity["eduPersonTargetedID"] = get_eptid(IDP, query, session) logger.info("Identity: %s", identity) if REPOZE_ID_EQUIVALENT: identity[REPOZE_ID_EQUIVALENT] = self.user try: try: metod = self.environ["idp.authn"] except KeyError: pass else: resp_args["authn"] = metod _resp = IDP.create_authn_response( identity, userid=self.user, encrypt_cert_assertion=encrypt_cert, **resp_args ) except Exception as excp: logging.error(exception_trace(excp)) resp = ServiceError("Exception: %s" % (excp,)) return resp(self.environ, self.start_response) logger.info("AuthNResponse: %s", _resp) if self.op_type == "ecp": kwargs = { "soap_headers": [ ecp.Response(assertion_consumer_service_url=self.destination) ] } else: kwargs = {} http_args = IDP.apply_binding( self.binding_out, "%s" % _resp, self.destination, relay_state, response=True, **kwargs ) logger.debug("HTTPargs: %s", http_args) return self.response(self.binding_out, http_args)