def operation(self, saml_msg, binding): logger.debug("_operation: %s", saml_msg) if not (saml_msg and "SAMLRequest" in saml_msg): resp = BadRequest("Error parsing request or no request") return resp(self.environ, self.start_response) else: # saml_msg may also contain Signature and SigAlg if "Signature" in saml_msg: try: kwargs = { "signature": saml_msg["Signature"], "sigalg": saml_msg["SigAlg"], } except KeyError: resp = BadRequest("Signature Algorithm specification is missing") return resp(self.environ, self.start_response) else: kwargs = {} try: kwargs["encrypt_cert"] = encrypt_cert_from_item( saml_msg["req_info"].message ) except KeyError: pass try: kwargs["relay_state"] = saml_msg["RelayState"] except KeyError: pass return self.do(saml_msg["SAMLRequest"], binding, **kwargs)
def do(self, query, binding, relay_state=""): req = IDP.parse_name_id_mapping_request(query, binding) request = req.message # Do the necessary stuff try: name_id = IDP.ident.handle_name_id_mapping_request( request.name_id, request.name_id_policy) except Unknown: resp = BadRequest("Unknown entity") return resp(self.environ, self.start_response) except PolicyError: resp = BadRequest("Unknown entity") return resp(self.environ, self.start_response) info = IDP.response_args(request) _resp = IDP.create_name_id_mapping_response(name_id, **info) # Only SOAP hinfo = IDP.apply_binding(BINDING_SOAP, "%s" % _resp, "", "", response=True) resp = Response(hinfo["data"], headers=hinfo["headers"]) return resp(self.environ, self.start_response)
def redirect(self): """ This is the HTTP-redirect endpoint """ logger.info("--- In SSO Redirect ---") saml_msg = self.unpack_redirect() try: _key = saml_msg["key"] saml_msg = IDP.ticket[_key] self.req_info = saml_msg["req_info"] del IDP.ticket[_key] except KeyError: try: self.req_info = IDP.parse_authn_request( saml_msg["SAMLRequest"], BINDING_HTTP_REDIRECT ) except KeyError: resp = BadRequest("Message signature verification failure") return resp(self.environ, self.start_response) if not self.req_info: resp = BadRequest("Message parsing failed") return resp(self.environ, self.start_response) _req = self.req_info.message if "SigAlg" in saml_msg and "Signature" in saml_msg: # Signed request issuer = _req.issuer.text _certs = IDP.metadata.certs(issuer, "any", "signing") verified_ok = False for cert in _certs: if verify_redirect_signature(saml_msg, IDP.sec.sec_backend, cert): verified_ok = True break if not verified_ok: resp = BadRequest("Message signature verification failure") return resp(self.environ, self.start_response) if self.user: saml_msg["req_info"] = self.req_info if _req.force_authn is not None and _req.force_authn.lower() == "true": key = self._store_request(saml_msg) return self.not_authn(key, _req.requested_authn_context) else: return self.operation(saml_msg, BINDING_HTTP_REDIRECT) else: saml_msg["req_info"] = self.req_info key = self._store_request(saml_msg) return self.not_authn(key, _req.requested_authn_context) else: return self.operation(saml_msg, BINDING_HTTP_REDIRECT)
def operation(self, _dict, binding, **kwargs): logger.debug("_operation: %s", _dict) if not _dict or "ID" not in _dict: resp = BadRequest("Error parsing request or no request") return resp(self.environ, self.start_response) return self.do(_dict["ID"], binding, **kwargs)
def artifact_operation(self, _dict): if not _dict: resp = BadRequest("Missing query") return resp(self.environ, self.start_response) else: # exchange artifact for response request = self.sp.artifact2message(_dict["SAMLart"], "spsso") return self.do(request, BINDING_HTTP_ARTIFACT, _dict["RelayState"])
def artifact_operation(self, saml_msg): if not saml_msg: resp = BadRequest("Missing query") return resp(self.environ, self.start_response) else: # exchange artifact for request request = IDP.artifact2message(saml_msg["SAMLart"], "spsso") try: return self.do(request, BINDING_HTTP_ARTIFACT, saml_msg["RelayState"]) except KeyError: return self.do(request, BINDING_HTTP_ARTIFACT)
def operation(self, _dict, binding): logger.debug("_operation: %s", _dict) if not _dict or not 'SAMLRequest' in _dict: resp = BadRequest('Error parsing request or no request') return resp(self.environ, self.start_response) else: try: return self.do(_dict["SAMLRequest"], binding, _dict["RelayState"]) except KeyError: # Can live with no relay state return self.do(_dict["SAMLRequest"], binding)
def operation(self, saml_msg, binding): logger.debug("_operation: %s", saml_msg) if not saml_msg or not 'SAMLRequest' in saml_msg: resp = BadRequest('Error parsing request or no request') return resp(self.environ, self.start_response) else: try: _encrypt_cert = encrypt_cert_from_item( saml_msg["req_info"].message) return self.do(saml_msg["SAMLRequest"], binding, saml_msg["RelayState"], encrypt_cert=_encrypt_cert) except KeyError: # Can live with no relay state return self.do(saml_msg["SAMLRequest"], binding)
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 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 operation(self, _dict, binding): logger.debug("_operation: %s", _dict) if not _dict: resp = BadRequest("Error parsing request or no request") return resp(self.environ, self.start_response) else: try: _relay_state = _dict["RelayState"] except KeyError: _relay_state = "" if "SAMLResponse" in _dict: return self.do(_dict["SAMLResponse"], binding, _relay_state, mtype="response") elif "SAMLRequest" in _dict: return self.do(_dict["SAMLRequest"], binding, _relay_state, mtype="request")
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)